438c24673a23499bf91091d9f1040bf4bdc23bf5
[claws.git] / src / gtk / gtksctree.c
1 /*
2  * This program is based on gtkflist.c
3  */
4
5 #include <stdlib.h>
6
7 #include "gtksctree.h"
8 #include "sylpheed-marshal.h"
9 #include "stock_pixmap.h"
10
11 #define CLIST_UNFROZEN(clist)     (((GtkCList*) (clist))->freeze_count == 0)
12 #define CLIST_REFRESH(clist)    G_STMT_START { \
13   if (CLIST_UNFROZEN (clist)) \
14     GTK_CLIST_GET_CLASS (clist)->refresh ((GtkCList*) (clist)); \
15 } G_STMT_END
16 #define CELL_SPACING               1
17 #define CLIST_OPTIMUM_SIZE         64
18 #define COLUMN_INSET               3
19
20 enum {
21         ROW_POPUP_MENU,
22         EMPTY_POPUP_MENU,
23         OPEN_ROW,
24         START_DRAG,
25         LAST_SIGNAL
26 };
27
28 static GdkPixmap *emptyxpm = NULL;
29 static GdkBitmap *emptyxpmmask = NULL;
30
31 static void gtk_sctree_class_init (GtkSCTreeClass *class);
32 static void gtk_sctree_init (GtkSCTree *sctree);
33
34 static gint gtk_sctree_button_press (GtkWidget *widget, GdkEventButton *event);
35 static gint gtk_sctree_button_release (GtkWidget *widget, GdkEventButton *event);
36 static gint gtk_sctree_motion (GtkWidget *widget, GdkEventMotion *event);
37 static void gtk_sctree_drag_begin (GtkWidget *widget, GdkDragContext *context);
38 static void gtk_sctree_drag_end (GtkWidget *widget, GdkDragContext *context);
39 static void gtk_sctree_drag_data_get (GtkWidget *widget, GdkDragContext *context,
40                                      GtkSelectionData *data, guint info, guint time);
41 static void gtk_sctree_drag_leave (GtkWidget *widget, GdkDragContext *context, guint time);
42 static gboolean gtk_sctree_drag_motion (GtkWidget *widget, GdkDragContext *context,
43                                        gint x, gint y, guint time);
44 static gboolean gtk_sctree_drag_drop (GtkWidget *widget, GdkDragContext *context,
45                                      gint x, gint y, guint time);
46 static void gtk_sctree_drag_data_received (GtkWidget *widget, GdkDragContext *context,
47                                           gint x, gint y, GtkSelectionData *data,
48                                           guint info, guint time);
49
50 static void gtk_sctree_clear (GtkCList *clist);
51 static void gtk_sctree_collapse (GtkCTree *ctree, GtkCTreeNode *node);
52        
53 static void stree_sort (GtkCTree *ctree, GtkCTreeNode  *node, gpointer data);
54 void gtk_sctree_sort_node (GtkCTree *ctree, GtkCTreeNode *node);
55 void gtk_sctree_sort_recursive (GtkCTree *ctree, GtkCTreeNode *node);
56
57 static void gtk_sctree_link (GtkCTree *ctree,
58                         GtkCTreeNode  *node,
59                         GtkCTreeNode  *parent,
60                         GtkCTreeNode  *sibling,
61                         gboolean       update_focus_row);
62
63 static void gtk_sctree_unlink (GtkCTree      *ctree, 
64                         GtkCTreeNode  *node,
65                         gboolean       update_focus_row);
66
67 static void stree_update_level (GtkCTree      *ctree, 
68                         GtkCTreeNode  *node, 
69                         gpointer       data);
70
71 static GtkCTreeNode * gtk_sctree_last_visible (GtkCTree     *ctree,
72                                               GtkCTreeNode *node);
73 static void gtk_sctree_real_tree_expand            (GtkCTree      *ctree,
74                                                  GtkCTreeNode  *node);
75 static void
76 sreal_tree_move (GtkCTree     *ctree,
77                 GtkCTreeNode *node,
78                 GtkCTreeNode *new_parent, 
79                 GtkCTreeNode *new_sibling);
80
81 static GtkCTreeClass *parent_class;
82
83 static guint sctree_signals[LAST_SIGNAL];
84
85
86 /**
87  * gtk_sctree_get_type:
88  * @void: 
89  * 
90  * Creates the GtkSCTree class and its type information
91  * 
92  * Return value: The type ID for GtkSCTreeClass
93  **/
94 GType
95 gtk_sctree_get_type (void)
96 {
97         static GType sctree_type = 0;
98
99         if (!sctree_type) {
100                 GTypeInfo sctree_info = {
101                         sizeof (GtkSCTreeClass),
102
103                         (GBaseInitFunc) NULL,
104                         (GBaseFinalizeFunc) NULL,
105
106                         (GClassInitFunc) gtk_sctree_class_init,
107                         (GClassFinalizeFunc) NULL,
108                         NULL,   /* class_data */
109
110                         sizeof (GtkSCTree),
111                         0,      /* n_preallocs */
112                         (GInstanceInitFunc) gtk_sctree_init,
113                 };
114
115                 sctree_type = g_type_register_static (GTK_TYPE_CTREE, "GtkSCTree", &sctree_info, (GTypeFlags)0);
116         }
117
118         return sctree_type;
119 }
120
121 /* Standard class initialization function */
122 static void
123 gtk_sctree_class_init (GtkSCTreeClass *klass)
124 {
125         GtkObjectClass *object_class;
126         GtkWidgetClass *widget_class;
127         GtkCListClass *clist_class;
128         GtkCTreeClass *ctree_class;
129
130         object_class = (GtkObjectClass *) klass;
131         widget_class = (GtkWidgetClass *) klass;
132         clist_class = (GtkCListClass *) klass;
133         ctree_class = (GtkCTreeClass *) klass;
134
135         parent_class = gtk_type_class (gtk_ctree_get_type ());
136
137         sctree_signals[ROW_POPUP_MENU] =
138                 g_signal_new ("row_popup_menu",
139                               G_TYPE_FROM_CLASS (klass),
140                               G_SIGNAL_RUN_FIRST,
141                               G_STRUCT_OFFSET (GtkSCTreeClass, row_popup_menu),
142                               NULL, NULL,
143                               sylpheed_marshal_VOID__POINTER,
144                               G_TYPE_NONE, 1,
145                               GDK_TYPE_EVENT);
146         sctree_signals[EMPTY_POPUP_MENU] =
147                 g_signal_new ("empty_popup_menu",
148                               G_TYPE_FROM_CLASS (klass),
149                               G_SIGNAL_RUN_FIRST,
150                               G_STRUCT_OFFSET (GtkSCTreeClass, empty_popup_menu),
151                               NULL, NULL,
152                               sylpheed_marshal_VOID__POINTER,
153                               G_TYPE_NONE, 1,
154                               GDK_TYPE_EVENT);
155         sctree_signals[OPEN_ROW] =
156                 g_signal_new ("open_row",
157                               G_TYPE_FROM_CLASS (klass),
158                               G_SIGNAL_RUN_FIRST,
159                               G_STRUCT_OFFSET (GtkSCTreeClass, open_row),
160                               NULL, NULL,
161                               g_cclosure_marshal_VOID__VOID,
162                               G_TYPE_NONE, 0);
163         sctree_signals[START_DRAG] =
164                 g_signal_new ("start_drag",
165                               G_TYPE_FROM_CLASS (klass),
166                               G_SIGNAL_RUN_FIRST,
167                               G_STRUCT_OFFSET (GtkSCTreeClass, start_drag),
168                               NULL, NULL,
169                               sylpheed_marshal_VOID__INT_POINTER,
170                               G_TYPE_NONE, 2,
171                               G_TYPE_INT,
172                               GDK_TYPE_EVENT);
173
174         /* gtk_object_class_add_signals (object_class, sctree_signals, LAST_SIGNAL); */
175
176         clist_class->clear = gtk_sctree_clear;
177         ctree_class->tree_collapse = gtk_sctree_collapse;
178         ctree_class->tree_expand = gtk_sctree_real_tree_expand;
179         ctree_class->tree_move = sreal_tree_move;
180         
181         widget_class->button_press_event = gtk_sctree_button_press;
182         widget_class->button_release_event = gtk_sctree_button_release;
183         widget_class->motion_notify_event = gtk_sctree_motion;
184         widget_class->drag_begin = gtk_sctree_drag_begin;
185         widget_class->drag_end = gtk_sctree_drag_end;
186         widget_class->drag_data_get = gtk_sctree_drag_data_get;
187         widget_class->drag_leave = gtk_sctree_drag_leave;
188         widget_class->drag_motion = gtk_sctree_drag_motion;
189         widget_class->drag_drop = gtk_sctree_drag_drop;
190         widget_class->drag_data_received = gtk_sctree_drag_data_received;
191 }
192
193 /* Standard object initialization function */
194 static void
195 gtk_sctree_init (GtkSCTree *sctree)
196 {
197         sctree->anchor_row = NULL;
198
199         /* GtkCTree does not specify pointer motion by default */
200         gtk_widget_add_events (GTK_WIDGET (sctree), GDK_POINTER_MOTION_MASK);
201         gtk_widget_add_events (GTK_WIDGET (sctree), GDK_POINTER_MOTION_MASK);
202 }
203
204 /* Get information the specified row is selected. */
205
206 static gboolean
207 row_is_selected(GtkSCTree *sctree, gint row)
208 {
209         GtkCListRow *clist_row;
210         clist_row =  g_list_nth (GTK_CLIST(sctree)->row_list, row)->data;
211         return clist_row ? clist_row->state == GTK_STATE_SELECTED : FALSE;
212 }
213
214 /* Selects the rows between the anchor to the specified row, inclusive.  */
215 static void
216 select_range (GtkSCTree *sctree, gint row)
217 {
218         gint prev_row;
219         gint min, max;
220         gint i;
221         GList *node;
222         if (sctree->anchor_row == NULL) {
223                 prev_row = row;
224                 sctree->anchor_row = gtk_ctree_node_nth(GTK_CTREE(sctree), row);
225         } else
226                 prev_row = g_list_position(GTK_CLIST(sctree)->row_list,
227                                            (GList *)sctree->anchor_row);
228
229         if (row < prev_row) {
230                 min = row;
231                 max = prev_row;
232                 GTK_CLIST(sctree)->focus_row = max;
233         } else {
234                 min = prev_row;
235                 max = row;
236         }
237         sctree->selecting_range = TRUE;
238         
239         if (max < min) {
240                 int t = min;
241                 min = max;
242                 max = t;
243         }
244         
245         if (max - min > 10)
246                 gtk_clist_freeze(GTK_CLIST(sctree));
247
248         node = g_list_nth((GTK_CLIST(sctree))->row_list, min);
249         for (i = min; i < max; i++) {
250                 if (node && GTK_CTREE_ROW (node)->row.selectable) {
251                         g_signal_emit_by_name(G_OBJECT(sctree), "tree_select_row",
252                                 node, -1);
253                 }
254                 node = node->next;
255         }
256         if (max - min > 10)
257                 gtk_clist_thaw(GTK_CLIST(sctree));
258
259
260         sctree->selecting_range = FALSE;
261         gtk_clist_select_row (GTK_CLIST (sctree), max, -1);
262 }
263
264 static gboolean sc_g_list_bigger(GList *list, gint max)
265 {
266         GList *cur = list;
267         int i = 0;
268         while (cur && i <= max+1) {
269                 i++;
270                 cur = cur->next;
271         }
272         return (i > max);
273 }
274
275 /* Handles row selection according to the specified modifier state */
276 /* in certain cases, we arrive here from a function knowing the GtkCTreeNode, and having
277  * already slowly found row using g_list_position. In which case, _node will be non-NULL
278  * to avoid this function having to slowly find it with g_list_nth. */
279 static void
280 select_row (GtkSCTree *sctree, gint row, gint col, guint state, GtkCTreeNode *_node)
281 {
282         gboolean range, additive;
283         g_return_if_fail (sctree != NULL);
284         g_return_if_fail (GTK_IS_SCTREE (sctree));
285     
286         range = ((state & GDK_SHIFT_MASK) != 0) &&
287                 (GTK_CLIST(sctree)->selection_mode != GTK_SELECTION_SINGLE) &&
288                 (GTK_CLIST(sctree)->selection_mode != GTK_SELECTION_BROWSE);
289         additive = ((state & GDK_CONTROL_MASK) != 0) &&
290                    (GTK_CLIST(sctree)->selection_mode != GTK_SELECTION_SINGLE) &&
291                    (GTK_CLIST(sctree)->selection_mode != GTK_SELECTION_BROWSE);
292
293         GTK_CLIST(sctree)->focus_row = row;
294
295         if (!additive) {
296                 /* if this selection isn't additive, we have to unselect what
297                  * is selected. Here, heavy GUI updates can occur if we have 
298                  * a big selection. See if more than one line is selected, in
299                  * which case, freeze, else don't. */
300
301                 gboolean should_freeze = FALSE;
302                 if (sc_g_list_bigger(GTK_CLIST(sctree)->selection, 10)) {
303                         should_freeze = TRUE;
304                         sctree->selecting_range = TRUE;
305                         gtk_clist_freeze (GTK_CLIST (sctree));
306                 }
307
308                 gtk_clist_unselect_all (GTK_CLIST (sctree));
309
310                 if (should_freeze) {
311                         gtk_clist_thaw (GTK_CLIST (sctree));
312                         sctree->selecting_range = FALSE;
313                 }
314         }
315
316         if (!range) {
317                 GtkCTreeNode *node;
318
319                 node = _node ? _node : gtk_ctree_node_nth (GTK_CTREE(sctree), row);
320
321                 /*No need to manage overlapped list*/
322                 if (additive) {
323                         if (row_is_selected(sctree, row))
324                                 gtk_clist_unselect_row (GTK_CLIST (sctree), row, col);
325                         else
326                                 g_signal_emit_by_name
327                                         (G_OBJECT (sctree),
328                                          "tree_select_row", node, col);
329                 } else {
330                         g_signal_emit_by_name
331                                 (G_OBJECT (sctree),
332                                  "tree_select_row", node, col);
333                 }
334                 sctree->anchor_row = node;
335         } else
336                 select_range (sctree, row);
337 }
338
339 /* Our handler for button_press events.  We override all of GtkCList's broken
340  * behavior.
341  */
342 static gint
343 gtk_sctree_button_press (GtkWidget *widget, GdkEventButton *event)
344 {
345         GtkSCTree *sctree;
346         GtkCList *clist;
347         gboolean on_row;
348         gint row;
349         gint col;
350         gint retval;
351
352         g_return_val_if_fail (widget != NULL, FALSE);
353         g_return_val_if_fail (GTK_IS_SCTREE (widget), FALSE);
354         g_return_val_if_fail (event != NULL, FALSE);
355
356         sctree = GTK_SCTREE (widget);
357         clist = GTK_CLIST (widget);
358         retval = FALSE;
359
360         if (event->window != clist->clist_window)
361                 return (* GTK_WIDGET_CLASS (parent_class)->button_press_event) (widget, event);
362
363         on_row = gtk_clist_get_selection_info (clist, event->x, event->y, &row, &col);
364
365         if (on_row && !GTK_WIDGET_HAS_FOCUS(widget))
366                 gtk_widget_grab_focus (widget);
367
368         if (gtk_ctree_is_hot_spot (GTK_CTREE(sctree), event->x, event->y)) {
369                 gtk_ctree_toggle_expansion
370                         (GTK_CTREE(sctree), 
371                          gtk_ctree_node_nth(GTK_CTREE(sctree), row));
372                 return TRUE;
373         }
374
375         switch (event->type) {
376         case GDK_BUTTON_PRESS:
377                 if (event->button == 1 || event->button == 2) {
378                         if (event->button == 2)
379                                 event->state &= ~(GDK_SHIFT_MASK | GDK_CONTROL_MASK);
380                         if (on_row) {
381                                 /* Save the mouse info for DnD */
382                                 sctree->dnd_press_button = event->button;
383                                 sctree->dnd_press_x = event->x;
384                                 sctree->dnd_press_y = event->y;
385
386                                 /* Handle selection */
387                                 if ((row_is_selected (sctree, row)
388                                      && !(event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK)))
389                                     || ((event->state & GDK_CONTROL_MASK)
390                                         && !(event->state & GDK_SHIFT_MASK))) {
391                                         sctree->dnd_select_pending = TRUE;
392                                         sctree->dnd_select_pending_state = event->state;
393                                         sctree->dnd_select_pending_row = row;
394                                 } else {
395                                         select_row (sctree, row, col, event->state, NULL);
396                                 }
397                         } else {
398                                 sctree->selecting_range = TRUE;
399                                 gtk_clist_unselect_all (clist);
400                                 sctree->selecting_range = FALSE;
401                         }
402
403                         retval = TRUE;
404                 } else if (event->button == 3) {
405                         /* Emit *_popup_menu signal*/
406                         if (on_row) {
407                                 if (!row_is_selected(sctree,row))
408                                         select_row (sctree, row, col, 0, NULL);
409                                 g_signal_emit (G_OBJECT (sctree),
410                                                  sctree_signals[ROW_POPUP_MENU],
411                                                  0, event);
412                         } else {
413                                 sctree->selecting_range = TRUE;
414                                 gtk_clist_unselect_all(clist);
415                                 sctree->selecting_range = FALSE;
416                                 g_signal_emit (G_OBJECT (sctree),
417                                                  sctree_signals[EMPTY_POPUP_MENU],
418                                                  0, event);
419                         }
420                         retval = TRUE;
421                 }
422
423                 break;
424
425         case GDK_2BUTTON_PRESS:
426                 if (event->button != 1)
427                         break;
428
429                 sctree->dnd_select_pending = FALSE;
430                 sctree->dnd_select_pending_state = 0;
431
432                 if (on_row)
433                         g_signal_emit (G_OBJECT (sctree),
434                                        sctree_signals[OPEN_ROW], 0);
435
436                 retval = TRUE;
437                 break;
438
439         default:
440                 break;
441         }
442
443         return retval;
444 }
445
446 /* Our handler for button_release events.  We override all of GtkCList's broken
447  * behavior.
448  */
449 static gint
450 gtk_sctree_button_release (GtkWidget *widget, GdkEventButton *event)
451 {
452         GtkSCTree *sctree;
453         GtkCList *clist;
454         gint on_row;
455         gint row, col;
456         gint retval;
457
458         g_return_val_if_fail (widget != NULL, FALSE);
459         g_return_val_if_fail (GTK_IS_SCTREE (widget), FALSE);
460         g_return_val_if_fail (event != NULL, FALSE);
461
462         sctree = GTK_SCTREE (widget);
463         clist = GTK_CLIST (widget);
464         retval = FALSE;
465
466         if (event->window != clist->clist_window)
467                 return (* GTK_WIDGET_CLASS (parent_class)->button_release_event) (widget, event);
468
469         on_row = gtk_clist_get_selection_info (clist, event->x, event->y, &row, &col);
470
471         if (!(event->button == 1 || event->button == 2))
472                 return FALSE;
473
474         sctree->dnd_press_button = 0;
475         sctree->dnd_press_x = 0;
476         sctree->dnd_press_y = 0;
477
478         if (on_row) {
479                 if (sctree->dnd_select_pending) {
480                         select_row (sctree, row, col, sctree->dnd_select_pending_state, NULL);
481                         sctree->dnd_select_pending = FALSE;
482                         sctree->dnd_select_pending_state = 0;
483                 }
484
485                 retval = TRUE;
486         }
487
488         return retval;
489 }
490
491 /* Our handler for motion_notify events.  We override all of GtkCList's broken
492  * behavior.
493  */
494 static gint
495 gtk_sctree_motion (GtkWidget *widget, GdkEventMotion *event)
496 {
497         GtkSCTree *sctree;
498         GtkCList *clist;
499
500         g_return_val_if_fail (widget != NULL, FALSE);
501         g_return_val_if_fail (GTK_IS_SCTREE (widget), FALSE);
502         g_return_val_if_fail (event != NULL, FALSE);
503
504         sctree = GTK_SCTREE (widget);
505         clist = GTK_CLIST (widget);
506
507         if (event->window != clist->clist_window)
508                 return (* GTK_WIDGET_CLASS (parent_class)->motion_notify_event) (widget, event);
509
510         if (!((sctree->dnd_press_button == 1 && (event->state & GDK_BUTTON1_MASK))
511               || (sctree->dnd_press_button == 2 && (event->state & GDK_BUTTON2_MASK))))
512                 return FALSE;
513
514         /* This is the same threshold value that is used in gtkdnd.c */
515
516         if (MAX (ABS (sctree->dnd_press_x - event->x),
517                  ABS (sctree->dnd_press_y - event->y)) <= 3)
518                 return FALSE;
519
520         /* Handle any pending selections */
521
522         if (sctree->dnd_select_pending) {
523                 if (!row_is_selected(sctree,sctree->dnd_select_pending_row))
524                         select_row (sctree,
525                                     sctree->dnd_select_pending_row,
526                                     -1,
527                                     sctree->dnd_select_pending_state,
528                                     NULL);
529
530                 sctree->dnd_select_pending = FALSE;
531                 sctree->dnd_select_pending_state = 0;
532         }
533
534         g_signal_emit (G_OBJECT (sctree),
535                        sctree_signals[START_DRAG],
536                        0,
537                        sctree->dnd_press_button,
538                        event);
539         return TRUE;
540 }
541
542 /* We override the drag_begin signal to do nothing */
543 static void
544 gtk_sctree_drag_begin (GtkWidget *widget, GdkDragContext *context)
545 {
546         /* nothing */
547 }
548
549 /* We override the drag_end signal to do nothing */
550 static void
551 gtk_sctree_drag_end (GtkWidget *widget, GdkDragContext *context)
552 {
553         /* nothing */
554 }
555
556 /* We override the drag_data_get signal to do nothing */
557 static void
558 gtk_sctree_drag_data_get (GtkWidget *widget, GdkDragContext *context,
559                                      GtkSelectionData *data, guint info, guint time)
560 {
561         /* nothing */
562 }
563
564 /* We override the drag_leave signal to do nothing */
565 static void
566 gtk_sctree_drag_leave (GtkWidget *widget, GdkDragContext *context, guint time)
567 {
568         /* nothing */
569 }
570
571 /* We override the drag_motion signal to do nothing */
572 static gboolean
573 gtk_sctree_drag_motion (GtkWidget *widget, GdkDragContext *context,
574                                    gint x, gint y, guint time)
575 {
576         return FALSE;
577 }
578
579 /* We override the drag_drop signal to do nothing */
580 static gboolean
581 gtk_sctree_drag_drop (GtkWidget *widget, GdkDragContext *context,
582                                  gint x, gint y, guint time)
583 {
584         return FALSE;
585 }
586
587 /* We override the drag_data_received signal to do nothing */
588 static void
589 gtk_sctree_drag_data_received (GtkWidget *widget, GdkDragContext *context,
590                                           gint x, gint y, GtkSelectionData *data,
591                                           guint info, guint time)
592 {
593         /* nothing */
594 }
595
596 /* Our handler for the clear signal of the clist.  We have to reset the anchor
597  * to null.
598  */
599 static void
600 gtk_sctree_clear (GtkCList *clist)
601 {
602         GtkSCTree *sctree;
603
604         g_return_if_fail (clist != NULL);
605         g_return_if_fail (GTK_IS_SCTREE (clist));
606
607         sctree = GTK_SCTREE (clist);
608         sctree->anchor_row = NULL;
609
610         if (((GtkCListClass *)parent_class)->clear)
611                 (* ((GtkCListClass *)parent_class)->clear) (clist);
612 }
613
614 /* Our handler for the change_focus_row_expansion signal of the ctree.  
615  We have to set the anchor to parent visible node.
616  */
617 static void 
618 gtk_sctree_collapse (GtkCTree *ctree, GtkCTreeNode *node)
619 {
620         g_return_if_fail (ctree != NULL);
621         g_return_if_fail (GTK_IS_SCTREE (ctree));
622
623         (* parent_class->tree_collapse) (ctree, node);
624         GTK_SCTREE(ctree)->anchor_row =
625                 gtk_ctree_node_nth(ctree, GTK_CLIST(ctree)->focus_row);
626 }
627
628 GtkWidget *gtk_sctree_new_with_titles (gint columns, gint tree_column, 
629                                        gchar *titles[])
630 {
631         GtkWidget *widget;
632                                                                                                             
633         g_return_val_if_fail (columns > 0, NULL);
634         g_return_val_if_fail (tree_column >= 0, NULL);
635                                                                                                             
636         widget = gtk_widget_new (TYPE_GTK_SCTREE,
637                                  "n_columns", columns,
638                                  "tree_column", tree_column,
639                                  NULL);
640         if (titles) {
641                 GtkCList *clist = GTK_CLIST (widget);
642                 guint i;
643
644                 for (i = 0; i < columns; i++)
645                         gtk_clist_set_column_title (clist, i, titles[i]);
646                 gtk_clist_column_titles_show (clist);
647         }
648
649         return widget;
650 }
651
652 void gtk_sctree_select (GtkSCTree *sctree, GtkCTreeNode *node)
653 {
654         select_row(sctree, 
655                    g_list_position(GTK_CLIST(sctree)->row_list, (GList *)node),
656                    -1, 0, node);
657 }
658
659 void gtk_sctree_select_with_state (GtkSCTree *sctree, GtkCTreeNode *node, int state)
660 {
661         select_row(sctree, 
662                    g_list_position(GTK_CLIST(sctree)->row_list, (GList *)node),
663                    -1, state, node);
664 }
665
666 void gtk_sctree_unselect_all (GtkSCTree *sctree)
667 {
668         sctree->selecting_range = TRUE;
669         gtk_clist_freeze(GTK_CLIST(sctree));
670         gtk_clist_unselect_all(GTK_CLIST(sctree));
671         gtk_clist_thaw(GTK_CLIST(sctree));
672         sctree->selecting_range = FALSE;
673         sctree->anchor_row = NULL;
674 }
675
676 void gtk_sctree_set_anchor_row (GtkSCTree *sctree, GtkCTreeNode *node)
677 {
678         sctree->anchor_row = node;
679 }
680
681 void gtk_sctree_remove_node (GtkSCTree *sctree, GtkCTreeNode *node)
682 {
683         if (sctree->anchor_row == node)
684                 sctree->anchor_row = NULL;
685         gtk_ctree_remove_node(GTK_CTREE(sctree), node);
686 }
687
688 /***********************************************************
689  *             Tree sorting functions                      *
690  ***********************************************************/
691
692 static void sink(GtkCList *clist, GPtrArray *numbers, gint root, gint bottom)
693 {
694         gint j, k ;
695         GtkCTreeNode *temp;
696
697         j = 2 * root;
698         k = j + 1;
699
700         /* find the maximum element of numbers[root],
701            numbers[2*root] and numbers[2*root+1] */
702         if (j <= bottom) {
703                 if (clist->compare( clist, GTK_CTREE_ROW (g_ptr_array_index(numbers, root)),
704                                     GTK_CTREE_ROW(g_ptr_array_index( numbers, j))) >= 0)
705                         j = root;
706                 if (k <= bottom)
707                         if (clist->compare( clist, GTK_CTREE_ROW (g_ptr_array_index(numbers, k)),
708                                             GTK_CTREE_ROW (g_ptr_array_index( numbers, j))) > 0)
709                                 j = k;
710                 /* if numbers[root] wasn't the maximum element then
711                    sink again */
712                 if (root != j) {
713                         temp = g_ptr_array_index( numbers,root);
714                         g_ptr_array_index( numbers, root) = g_ptr_array_index( numbers, j);
715                         g_ptr_array_index( numbers, j) = temp;
716                         sink( clist, numbers, j, bottom);
717                 }
718         }
719 }
720
721 static void heap_sort(GtkCList *clist, GPtrArray *numbers, gint array_size)
722 {
723         gint i;
724         GtkCTreeNode *temp;
725         
726         /* build the Heap */
727         for (i = (array_size / 2); i >= 1; i--)
728                 sink( clist, numbers, i, array_size);
729         /* output the Heap */
730         for (i = array_size; i >= 2; i--) {
731                 temp = g_ptr_array_index( numbers, 1);
732                 g_ptr_array_index( numbers, 1) = g_ptr_array_index( numbers, i);
733                 g_ptr_array_index( numbers, i) = temp;
734                 sink( clist, numbers, 1, i-1);
735         }
736 }
737
738 static void
739 stree_sort (GtkCTree    *ctree,
740            GtkCTreeNode *node,
741            gpointer      data)
742 {
743         GtkCTreeNode *list_start, *work, *next;
744         GPtrArray *row_array, *viewable_array;
745         GtkCList *clist;
746         gint i;
747
748         clist = GTK_CLIST (ctree);
749
750         if (node)
751                 work = GTK_CTREE_ROW (node)->children;
752         else
753                 work = GTK_CTREE_NODE (clist->row_list);
754
755         row_array = g_ptr_array_new();
756         viewable_array = g_ptr_array_new();
757
758         if (work) {
759                 g_ptr_array_add( row_array, NULL);
760                 while (work) {
761                         /* add all rows to row_array */
762                         g_ptr_array_add( row_array, work);
763                         if (GTK_CTREE_ROW (work)->parent && gtk_ctree_is_viewable( ctree, work))
764                                 g_ptr_array_add( viewable_array, GTK_CTREE_ROW (work)->parent);
765                         next = GTK_CTREE_ROW (work)->sibling;
766                         gtk_sctree_unlink( ctree, work, FALSE);
767                         work = next;
768                 }
769
770                 heap_sort( clist, row_array, (row_array->len)-1);
771
772                 if (node)
773                         list_start = GTK_CTREE_ROW (node)->children;
774                 else
775                         list_start = GTK_CTREE_NODE (clist->row_list);
776
777                 if (clist->sort_type == GTK_SORT_ASCENDING) {
778                         for (i=(row_array->len)-1; i>=1; i--) {
779                                 work = g_ptr_array_index( row_array, i);
780                                 gtk_sctree_link( ctree, work, node, list_start, FALSE);
781                                 list_start = work;
782                                 /* insert work at the beginning of the list */
783                         }
784                 } else {
785                         for (i=1; i<row_array->len; i++) {
786                                 work = g_ptr_array_index( row_array, i);
787                                 gtk_sctree_link( ctree, work, node, list_start, FALSE);
788                                 list_start = work;
789                                 /* insert work at the beginning of the list */
790                         }
791                 }
792
793                 for (i=0; i<viewable_array->len; i++) {
794                         gtk_ctree_expand( ctree, g_ptr_array_index( viewable_array, i));
795                 }
796                 
797         }
798         g_ptr_array_free( row_array, TRUE);
799         g_ptr_array_free( viewable_array, TRUE);
800 }
801
802 void
803 gtk_sctree_sort_recursive (GtkCTree     *ctree, 
804                           GtkCTreeNode *node)
805 {
806         GtkCList *clist;
807         GtkCTreeNode *focus_node = NULL;
808
809         g_return_if_fail (ctree != NULL);
810         g_return_if_fail (GTK_IS_CTREE (ctree));
811
812         clist = GTK_CLIST (ctree);
813
814         gtk_clist_freeze (clist);
815
816         if (clist->selection_mode == GTK_SELECTION_EXTENDED) {
817                 GTK_CLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
818       
819                 g_list_free (clist->undo_selection);
820                 g_list_free (clist->undo_unselection);
821                 clist->undo_selection = NULL;
822                 clist->undo_unselection = NULL;
823         }
824
825         if (!node || (node && gtk_ctree_is_viewable (ctree, node)))
826                 focus_node = GTK_CTREE_NODE (g_list_nth (clist->row_list, clist->focus_row));
827       
828         GTK_SCTREE(ctree)->sorting = TRUE;
829
830         gtk_ctree_post_recursive (ctree, node, GTK_CTREE_FUNC (stree_sort), NULL);
831
832         if (!node)
833                 stree_sort (ctree, NULL, NULL);
834
835         GTK_SCTREE(ctree)->sorting = FALSE;
836
837         if (focus_node) {
838                 clist->focus_row = g_list_position (clist->row_list,(GList *)focus_node);
839                 clist->undo_anchor = clist->focus_row;
840         }
841
842         gtk_clist_thaw (clist);
843 }
844
845 void
846 gtk_sctree_sort_node (GtkCTree     *ctree, 
847                      GtkCTreeNode *node)
848 {
849         GtkCList *clist;
850         GtkCTreeNode *focus_node = NULL;
851
852         g_return_if_fail (ctree != NULL);
853         g_return_if_fail (GTK_IS_CTREE (ctree));
854
855         clist = GTK_CLIST (ctree);
856
857         gtk_clist_freeze (clist);
858
859         if (clist->selection_mode == GTK_SELECTION_EXTENDED) {
860                 GTK_CLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
861
862                 g_list_free (clist->undo_selection);
863                 g_list_free (clist->undo_unselection);
864                 clist->undo_selection = NULL;
865                 clist->undo_unselection = NULL;
866         }
867
868         if (!node || (node && gtk_ctree_is_viewable (ctree, node)))
869                 focus_node = GTK_CTREE_NODE (g_list_nth (clist->row_list, clist->focus_row));
870
871         GTK_SCTREE(ctree)->sorting = TRUE;
872
873         stree_sort (ctree, node, NULL);
874
875         GTK_SCTREE(ctree)->sorting = FALSE;
876
877         if (focus_node) {
878                 clist->focus_row = g_list_position (clist->row_list,(GList *)focus_node);
879                 clist->undo_anchor = clist->focus_row;
880         }
881
882         gtk_clist_thaw (clist);
883 }
884
885 /************************************************************************/
886
887 static void
888 gtk_sctree_unlink (GtkCTree     *ctree, 
889                   GtkCTreeNode *node,
890                   gboolean      update_focus_row)
891 {
892         GtkCList *clist;
893         gint rows;
894         gint level;
895         gint visible;
896         GtkCTreeNode *work;
897         GtkCTreeNode *parent;
898         GList *list;
899
900         g_return_if_fail (ctree != NULL);
901         g_return_if_fail (GTK_IS_CTREE (ctree));
902         g_return_if_fail (node != NULL);
903
904         clist = GTK_CLIST (ctree);
905   
906         if (update_focus_row && clist->selection_mode == GTK_SELECTION_EXTENDED) {
907                 GTK_CLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
908
909                 g_list_free (clist->undo_selection);
910                 g_list_free (clist->undo_unselection);
911                 clist->undo_selection = NULL;
912                 clist->undo_unselection = NULL;
913         }
914
915         visible = gtk_ctree_is_viewable (ctree, node);
916
917         /* clist->row_list_end unlinked ? */
918         if (visible && (GTK_CTREE_NODE_NEXT (node) == NULL ||
919            (GTK_CTREE_ROW (node)->children && gtk_ctree_is_ancestor (ctree, node,
920             GTK_CTREE_NODE (clist->row_list_end)))))
921                 clist->row_list_end = (GList *) (GTK_CTREE_NODE_PREV (node));
922
923         /* update list */
924         rows = 0;
925         level = GTK_CTREE_ROW (node)->level;
926         work = GTK_CTREE_NODE_NEXT (node);
927         while (work && GTK_CTREE_ROW (work)->level > level) {
928                 work = GTK_CTREE_NODE_NEXT (work);
929                 rows++;
930         }
931
932         if (visible) {
933                 clist->rows -= (rows + 1);
934
935                 if (update_focus_row) {
936                         gint pos;
937                         pos = g_list_position (clist->row_list, (GList *)node);
938                         if (pos + rows < clist->focus_row)
939                                 clist->focus_row -= (rows + 1);
940                         else if (pos <= clist->focus_row) {
941                                 if (!GTK_CTREE_ROW (node)->sibling)
942                                         clist->focus_row = MAX (pos - 1, 0);
943                                 else
944                                         clist->focus_row = pos;
945               
946                                 clist->focus_row = MIN (clist->focus_row, clist->rows - 1);
947                         }
948                         clist->undo_anchor = clist->focus_row;
949                 }
950         }
951
952         if (work) {
953                 list = (GList *)GTK_CTREE_NODE_PREV (work);
954                 list->next = NULL;
955                 list = (GList *)work;
956                 list->prev = (GList *)GTK_CTREE_NODE_PREV (node);
957         }
958
959         if (GTK_CTREE_NODE_PREV (node) &&
960             GTK_CTREE_NODE_NEXT (GTK_CTREE_NODE_PREV (node)) == node) {
961                 list = (GList *)GTK_CTREE_NODE_PREV (node);
962                 list->next = (GList *)work;
963         }
964
965         /* update tree */
966         parent = GTK_CTREE_ROW (node)->parent;
967         if (parent) {
968                 if (GTK_CTREE_ROW (parent)->children == node) {
969                         GTK_CTREE_ROW (parent)->children = GTK_CTREE_ROW (node)->sibling;
970                 }
971                 else {
972                         GtkCTreeNode *sibling;
973
974                         sibling = GTK_CTREE_ROW (parent)->children;
975                         while (GTK_CTREE_ROW (sibling)->sibling != node)
976                                 sibling = GTK_CTREE_ROW (sibling)->sibling;
977                         GTK_CTREE_ROW (sibling)->sibling = GTK_CTREE_ROW (node)->sibling;
978                 }
979         }
980         else {
981                 if (clist->row_list == (GList *)node)
982                         clist->row_list = (GList *) (GTK_CTREE_ROW (node)->sibling);
983                 else {
984                         GtkCTreeNode *sibling;
985
986                         sibling = GTK_CTREE_NODE (clist->row_list);
987                         while (GTK_CTREE_ROW (sibling)->sibling != node)
988                                 sibling = GTK_CTREE_ROW (sibling)->sibling;
989                         GTK_CTREE_ROW (sibling)->sibling = GTK_CTREE_ROW (node)->sibling;
990                 }
991         }
992 }
993
994 static void
995 gtk_sctree_link (GtkCTree     *ctree,
996                 GtkCTreeNode *node,
997                 GtkCTreeNode *parent,
998                 GtkCTreeNode *sibling,
999                 gboolean      update_focus_row)
1000 {
1001         GtkCList *clist;
1002         GList *list_end;
1003         GList *list;
1004         GList *work;
1005         gboolean visible = FALSE;
1006         gint rows = 0;
1007   
1008         if (sibling)
1009                 g_return_if_fail (GTK_CTREE_ROW (sibling)->parent == parent);
1010         g_return_if_fail (node != NULL);
1011         g_return_if_fail (node != sibling);
1012         g_return_if_fail (node != parent);
1013
1014         clist = GTK_CLIST (ctree);
1015
1016         if (update_focus_row && clist->selection_mode == GTK_SELECTION_EXTENDED) {
1017                 GTK_CLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
1018
1019                 g_list_free (clist->undo_selection);
1020                 g_list_free (clist->undo_unselection);
1021                 clist->undo_selection = NULL;
1022                 clist->undo_unselection = NULL;
1023         }
1024
1025         for (rows = 1, list_end = (GList *)node; list_end->next;
1026              list_end = list_end->next)
1027                 rows++;
1028
1029         GTK_CTREE_ROW (node)->parent = parent;
1030         GTK_CTREE_ROW (node)->sibling = sibling;
1031
1032         if (!parent || (parent && (gtk_ctree_is_viewable (ctree, parent) &&
1033             GTK_CTREE_ROW (parent)->expanded))) {
1034                 visible = TRUE;
1035                 clist->rows += rows;
1036         }
1037
1038         if (parent)
1039                 work = (GList *)(GTK_CTREE_ROW (parent)->children);
1040         else
1041                 work = clist->row_list;
1042
1043         if (sibling) {
1044                 if (work != (GList *)sibling) {
1045                         while (GTK_CTREE_ROW (work)->sibling != sibling)
1046                                 work = (GList *)(GTK_CTREE_ROW (work)->sibling);
1047                         GTK_CTREE_ROW (work)->sibling = node;
1048                 }
1049
1050                 if (sibling == GTK_CTREE_NODE (clist->row_list))
1051                 clist->row_list = (GList *) node;
1052                 if (GTK_CTREE_NODE_PREV (sibling) &&
1053                     GTK_CTREE_NODE_NEXT (GTK_CTREE_NODE_PREV (sibling)) == sibling) {
1054                         list = (GList *)GTK_CTREE_NODE_PREV (sibling);
1055                         list->next = (GList *)node;
1056                 }
1057
1058                 list = (GList *)node;
1059                 list->prev = (GList *)GTK_CTREE_NODE_PREV (sibling);
1060                 list_end->next = (GList *)sibling;
1061                 list = (GList *)sibling;
1062                 list->prev = list_end;
1063                 if (parent && GTK_CTREE_ROW (parent)->children == sibling)
1064                         GTK_CTREE_ROW (parent)->children = node;
1065         }
1066         else {
1067                 if (work) {
1068                         /* find sibling */
1069                         while (GTK_CTREE_ROW (work)->sibling)
1070                         work = (GList *)(GTK_CTREE_ROW (work)->sibling);
1071                         GTK_CTREE_ROW (work)->sibling = node;
1072
1073                         /* find last visible child of sibling */
1074                         work = (GList *) gtk_sctree_last_visible (ctree,
1075                                GTK_CTREE_NODE (work));
1076
1077                         list_end->next = work->next;
1078                         if (work->next)
1079                                 list = work->next->prev = list_end;
1080                         work->next = (GList *)node;
1081                         list = (GList *)node;
1082                         list->prev = work;
1083                 }
1084                 else {
1085                         if (parent) {
1086                                 GTK_CTREE_ROW (parent)->children = node;
1087                                 list = (GList *)node;
1088                                 list->prev = (GList *)parent;
1089                                 if (GTK_CTREE_ROW (parent)->expanded) {
1090                                         list_end->next = (GList *)GTK_CTREE_NODE_NEXT (parent);
1091                                         if (GTK_CTREE_NODE_NEXT(parent)) {
1092                                                 list = (GList *)GTK_CTREE_NODE_NEXT (parent);
1093                                                 list->prev = list_end;
1094                                         }
1095                                         list = (GList *)parent;
1096                                         list->next = (GList *)node;
1097                                 }
1098                                 else
1099                                         list_end->next = NULL;
1100                         }
1101                         else {
1102                                 clist->row_list = (GList *)node;
1103                                 list = (GList *)node;
1104                                 list->prev = NULL;
1105                                 list_end->next = NULL;
1106                         }
1107                 }
1108         }
1109
1110         gtk_ctree_pre_recursive (ctree, node, stree_update_level, NULL); 
1111
1112         if (clist->row_list_end == NULL ||
1113             clist->row_list_end->next == (GList *)node)
1114                 clist->row_list_end = list_end;
1115
1116         if (visible && update_focus_row) {
1117                 gint pos;
1118                 pos = g_list_position (clist->row_list, (GList *)node);
1119   
1120                 if (pos <= clist->focus_row) {
1121                         clist->focus_row += rows;
1122                         clist->undo_anchor = clist->focus_row;
1123                 }
1124         }
1125 }
1126
1127 static void
1128 stree_update_level (GtkCTree     *ctree, 
1129                    GtkCTreeNode *node, 
1130                    gpointer      data)
1131 {
1132         if (!node)
1133                 return;
1134
1135         if (GTK_CTREE_ROW (node)->parent)
1136                 GTK_CTREE_ROW (node)->level = 
1137                 GTK_CTREE_ROW (GTK_CTREE_ROW (node)->parent)->level + 1;
1138         else
1139                 GTK_CTREE_ROW (node)->level = 1;
1140 }
1141
1142 static GtkCTreeNode *
1143 gtk_sctree_last_visible (GtkCTree     *ctree,
1144                         GtkCTreeNode *node)
1145 {
1146         GtkCTreeNode *work;
1147   
1148         if (!node)
1149                 return NULL;
1150
1151         work = GTK_CTREE_ROW (node)->children;
1152
1153         if (!work || !GTK_CTREE_ROW (node)->expanded)
1154                 return node;
1155
1156         while (GTK_CTREE_ROW (work)->sibling)
1157                 work = GTK_CTREE_ROW (work)->sibling;
1158
1159         return gtk_sctree_last_visible (ctree, work);
1160 }
1161
1162 /* this wrapper simply replaces NULL pixmaps 
1163  * with a transparent, 1x1 pixmap. This works
1164  * around a memory problem deep inside gtk, 
1165  * revealed by valgrind. 
1166  */
1167 /*GtkCTreeNode* gtk_sctree_insert_node        (GtkCTree *ctree,
1168                                              GtkCTreeNode *parent,
1169                                              GtkCTreeNode *sibling,
1170                                              gchar *text[],
1171                                              guint8 spacing,
1172                                              GdkPixmap *pixmap_closed,
1173                                              GdkBitmap *mask_closed,
1174                                              GdkPixmap *pixmap_opened,
1175                                              GdkBitmap *mask_opened,
1176                                              gboolean is_leaf,
1177                                              gboolean expanded)
1178 {
1179         if (!emptyxpm) {
1180                 stock_pixmap_gdk(GTK_WIDGET(ctree), STOCK_PIXMAP_EMPTY,
1181                          &emptyxpm, &emptyxpmmask);
1182         }
1183         if (!pixmap_closed) {
1184                 pixmap_closed = emptyxpm;
1185                 mask_closed = emptyxpmmask;
1186         }
1187         if (!pixmap_opened) {
1188                 pixmap_opened = emptyxpm;
1189                 mask_opened = emptyxpmmask;
1190         }
1191         return gtk_ctree_insert_node(ctree, parent, sibling, text,spacing,
1192                 pixmap_closed, mask_closed, pixmap_opened, mask_opened,
1193                 is_leaf, expanded);
1194 }*/
1195
1196 static void 
1197 sset_node_info (GtkCTree     *ctree,
1198                GtkCTreeNode *node,
1199                const gchar  *text,
1200                guint8        spacing,
1201                GdkPixmap    *pixmap_closed,
1202                GdkBitmap    *mask_closed,
1203                GdkPixmap    *pixmap_opened,
1204                GdkBitmap    *mask_opened,
1205                gboolean      is_leaf,
1206                gboolean      expanded)
1207 {
1208   if (GTK_CTREE_ROW (node)->pixmap_opened)
1209     {
1210       gdk_pixmap_unref (GTK_CTREE_ROW (node)->pixmap_opened);
1211       if (GTK_CTREE_ROW (node)->mask_opened) 
1212         gdk_bitmap_unref (GTK_CTREE_ROW (node)->mask_opened);
1213     }
1214   if (GTK_CTREE_ROW (node)->pixmap_closed)
1215     {
1216       gdk_pixmap_unref (GTK_CTREE_ROW (node)->pixmap_closed);
1217       if (GTK_CTREE_ROW (node)->mask_closed) 
1218         gdk_bitmap_unref (GTK_CTREE_ROW (node)->mask_closed);
1219     }
1220
1221   GTK_CTREE_ROW (node)->pixmap_opened = NULL;
1222   GTK_CTREE_ROW (node)->mask_opened   = NULL;
1223   GTK_CTREE_ROW (node)->pixmap_closed = NULL;
1224   GTK_CTREE_ROW (node)->mask_closed   = NULL;
1225
1226   if (pixmap_closed)
1227     {
1228       GTK_CTREE_ROW (node)->pixmap_closed = gdk_pixmap_ref (pixmap_closed);
1229       if (mask_closed) 
1230         GTK_CTREE_ROW (node)->mask_closed = gdk_bitmap_ref (mask_closed);
1231     }
1232   if (pixmap_opened)
1233     {
1234       GTK_CTREE_ROW (node)->pixmap_opened = gdk_pixmap_ref (pixmap_opened);
1235       if (mask_opened) 
1236         GTK_CTREE_ROW (node)->mask_opened = gdk_bitmap_ref (mask_opened);
1237     }
1238
1239   GTK_CTREE_ROW (node)->is_leaf  = is_leaf;
1240   GTK_CTREE_ROW (node)->expanded = (is_leaf) ? FALSE : expanded;
1241
1242   if (GTK_CTREE_ROW (node)->expanded)
1243     gtk_ctree_node_set_pixtext (ctree, node, ctree->tree_column,
1244                                 text, spacing, pixmap_opened, mask_opened);
1245   else 
1246     gtk_ctree_node_set_pixtext (ctree, node, ctree->tree_column,
1247                                 text, spacing, pixmap_closed, mask_closed);
1248 }
1249
1250 static void
1251 stree_draw_node (GtkCTree     *ctree, 
1252                 GtkCTreeNode *node)
1253 {
1254   GtkCList *clist;
1255   
1256   clist = GTK_CLIST (ctree);
1257
1258   if (CLIST_UNFROZEN (clist) && gtk_ctree_is_viewable (ctree, node))
1259     {
1260       GtkCTreeNode *work;
1261       gint num = 0;
1262       
1263       work = GTK_CTREE_NODE (clist->row_list);
1264       while (work && work != node)
1265         {
1266           work = GTK_CTREE_NODE_NEXT (work);
1267           num++;
1268         }
1269       if (work && gtk_clist_row_is_visible (clist, num) != GTK_VISIBILITY_NONE)
1270         GTK_CLIST_GET_CLASS (clist)->draw_row
1271           (clist, NULL, num, GTK_CLIST_ROW ((GList *) node));
1272     }
1273 }
1274
1275 /* this wrapper simply replaces NULL pixmaps 
1276  * with a transparent, 1x1 pixmap. This works
1277  * around a memory problem deep inside gtk, 
1278  * revealed by valgrind. 
1279  */
1280 void        gtk_sctree_set_node_info        (GtkCTree *ctree,
1281                                              GtkCTreeNode *node,
1282                                              const gchar *text,
1283                                              guint8 spacing,
1284                                              GdkPixmap *pixmap_closed,
1285                                              GdkBitmap *mask_closed,
1286                                              GdkPixmap *pixmap_opened,
1287                                              GdkBitmap *mask_opened,
1288                                              gboolean is_leaf,
1289                                              gboolean expanded)
1290 {
1291   gboolean old_leaf;
1292   gboolean old_expanded;
1293   GtkCTreeNode *work;
1294  
1295   if (!emptyxpm) {
1296           stock_pixmap_gdk(GTK_WIDGET(ctree), STOCK_PIXMAP_EMPTY,
1297                    &emptyxpm, &emptyxpmmask);
1298   }
1299   if (!pixmap_closed) {
1300           pixmap_closed = emptyxpm;
1301           mask_closed = emptyxpmmask;
1302   }
1303   if (!pixmap_opened) {
1304           pixmap_opened = emptyxpm;
1305           mask_opened = emptyxpmmask;
1306   }
1307
1308   if (!GTK_IS_CTREE (ctree) || !node) return;
1309
1310   old_leaf = GTK_CTREE_ROW (node)->is_leaf;
1311   old_expanded = GTK_CTREE_ROW (node)->expanded;
1312
1313   if (is_leaf && (work = GTK_CTREE_ROW (node)->children) != NULL)
1314     {
1315       GtkCTreeNode *ptr;
1316       
1317       while (work)
1318         {
1319           ptr = work;
1320           work = GTK_CTREE_ROW (work)->sibling;
1321           gtk_ctree_remove_node (ctree, ptr);
1322         }
1323     }
1324
1325   sset_node_info (ctree, node, text, spacing, pixmap_closed, mask_closed,
1326                  pixmap_opened, mask_opened, is_leaf, expanded);
1327
1328   if (!is_leaf && !old_leaf)
1329     {
1330       GTK_CTREE_ROW (node)->expanded = old_expanded;
1331       if (expanded && !old_expanded)
1332         gtk_ctree_expand (ctree, node);
1333       else if (!expanded && old_expanded)
1334         gtk_ctree_collapse (ctree, node);
1335     }
1336
1337   GTK_CTREE_ROW (node)->expanded = (is_leaf) ? FALSE : expanded;
1338   
1339   stree_draw_node (ctree, node);
1340 }
1341
1342 static GtkCTreeRow *
1343 srow_new (GtkCTree *ctree)
1344 {
1345   GtkCList *clist;
1346   GtkCTreeRow *ctree_row;
1347   int i;
1348
1349   clist = GTK_CLIST (ctree);
1350 #if GTK_CHECK_VERSION(2,9,0)
1351   ctree_row = g_slice_new (GtkCTreeRow);
1352   ctree_row->row.cell = g_slice_alloc (sizeof (GtkCell) * clist->columns);
1353 #else
1354   ctree_row = g_chunk_new (GtkCTreeRow, (GMemChunk *)clist->row_mem_chunk);
1355   ctree_row->row.cell = g_chunk_new (GtkCell, (GMemChunk *)clist->cell_mem_chunk);
1356 #endif
1357   for (i = 0; i < clist->columns; i++)
1358     {
1359       ctree_row->row.cell[i].type = GTK_CELL_EMPTY;
1360       ctree_row->row.cell[i].vertical = 0;
1361       ctree_row->row.cell[i].horizontal = 0;
1362       ctree_row->row.cell[i].style = NULL;
1363     }
1364
1365   GTK_CELL_PIXTEXT (ctree_row->row.cell[ctree->tree_column])->text = NULL;
1366
1367   ctree_row->row.fg_set     = FALSE;
1368   ctree_row->row.bg_set     = FALSE;
1369   ctree_row->row.style      = NULL;
1370   ctree_row->row.selectable = TRUE;
1371   ctree_row->row.state      = GTK_STATE_NORMAL;
1372   ctree_row->row.data       = NULL;
1373   ctree_row->row.destroy    = NULL;
1374
1375   ctree_row->level         = 0;
1376   ctree_row->expanded      = FALSE;
1377   ctree_row->parent        = NULL;
1378   ctree_row->sibling       = NULL;
1379   ctree_row->children      = NULL;
1380   ctree_row->pixmap_closed = NULL;
1381   ctree_row->mask_closed   = NULL;
1382   ctree_row->pixmap_opened = NULL;
1383   ctree_row->mask_opened   = NULL;
1384   
1385   return ctree_row;
1386 }
1387
1388 static void
1389 srow_delete (GtkCTree    *ctree,
1390             GtkCTreeRow *ctree_row)
1391 {
1392   GtkCList *clist;
1393   gint i;
1394
1395   clist = GTK_CLIST (ctree);
1396
1397   for (i = 0; i < clist->columns; i++)
1398     {
1399       GTK_CLIST_GET_CLASS (clist)->set_cell_contents
1400         (clist, &(ctree_row->row), i, GTK_CELL_EMPTY, NULL, 0, NULL, NULL);
1401       if (ctree_row->row.cell[i].style)
1402         {
1403           if (GTK_WIDGET_REALIZED (ctree))
1404             gtk_style_detach (ctree_row->row.cell[i].style);
1405           g_object_unref (ctree_row->row.cell[i].style);
1406         }
1407     }
1408
1409   if (ctree_row->row.style)
1410     {
1411       if (GTK_WIDGET_REALIZED (ctree))
1412         gtk_style_detach (ctree_row->row.style);
1413       g_object_unref (ctree_row->row.style);
1414     }
1415
1416   if (ctree_row->pixmap_closed)
1417     {
1418       gdk_pixmap_unref (ctree_row->pixmap_closed);
1419       if (ctree_row->mask_closed)
1420         gdk_bitmap_unref (ctree_row->mask_closed);
1421     }
1422
1423   if (ctree_row->pixmap_opened)
1424     {
1425       gdk_pixmap_unref (ctree_row->pixmap_opened);
1426       if (ctree_row->mask_opened)
1427         gdk_bitmap_unref (ctree_row->mask_opened);
1428     }
1429
1430   if (ctree_row->row.destroy)
1431     {
1432       GtkDestroyNotify dnotify = ctree_row->row.destroy;
1433       gpointer ddata = ctree_row->row.data;
1434
1435       ctree_row->row.destroy = NULL;
1436       ctree_row->row.data = NULL;
1437
1438       dnotify (ddata);
1439     }
1440
1441 #if GTK_CHECK_VERSION(2,9,0)  
1442   g_slice_free1 (sizeof (GtkCell) * clist->columns, ctree_row->row.cell);
1443   g_slice_free (GtkCTreeRow, ctree_row);
1444 #else
1445   g_mem_chunk_free ((GMemChunk *)clist->cell_mem_chunk, ctree_row->row.cell);
1446   g_mem_chunk_free ((GMemChunk *)clist->row_mem_chunk, ctree_row);
1447 #endif
1448 }
1449
1450 static void
1451 stree_delete_row (GtkCTree     *ctree, 
1452                  GtkCTreeNode *node, 
1453                  gpointer      data)
1454 {
1455   srow_delete (ctree, GTK_CTREE_ROW (node));
1456   g_list_free_1 ((GList *)node);
1457 }
1458
1459 static void
1460 gtk_sctree_column_auto_resize (GtkCList    *clist,
1461                     GtkCListRow *clist_row,
1462                     gint         column,
1463                     gint         old_width)
1464 {
1465   /* resize column if needed for auto_resize */
1466   GtkRequisition requisition;
1467
1468   if (!clist->column[column].auto_resize ||
1469       GTK_CLIST_AUTO_RESIZE_BLOCKED (clist))
1470     return;
1471
1472   if (clist_row)
1473     GTK_CLIST_GET_CLASS (clist)->cell_size_request (clist, clist_row,
1474                                                    column, &requisition);
1475   else
1476     requisition.width = 0;
1477
1478   if (requisition.width > clist->column[column].width)
1479     gtk_clist_set_column_width (clist, column, requisition.width);
1480   else if (requisition.width < old_width &&
1481            old_width == clist->column[column].width)
1482     {
1483       GList *list;
1484       gint new_width;
1485
1486       /* run a "gtk_clist_optimal_column_width" but break, if
1487        * the column doesn't shrink */
1488       if (GTK_CLIST_SHOW_TITLES (clist) && clist->column[column].button)
1489         new_width = (clist->column[column].button->requisition.width -
1490                      (CELL_SPACING + (2 * COLUMN_INSET)));
1491       else
1492         new_width = 0;
1493
1494       for (list = clist->row_list; list; list = list->next)
1495         {
1496           GTK_CLIST_GET_CLASS (clist)->cell_size_request
1497             (clist, GTK_CLIST_ROW (list), column, &requisition);
1498           new_width = MAX (new_width, requisition.width);
1499           if (new_width == clist->column[column].width)
1500             break;
1501         }
1502       if (new_width < clist->column[column].width)
1503         gtk_clist_set_column_width (clist, column, new_width);
1504     }
1505 }
1506
1507
1508 static void 
1509 gtk_sctree_real_tree_expand (GtkCTree     *ctree,
1510                   GtkCTreeNode *node)
1511 {
1512   GtkCList *clist;
1513   GtkCTreeNode *work;
1514   GtkRequisition requisition;
1515   gboolean visible;
1516   gint level;
1517
1518   g_return_if_fail (GTK_IS_CTREE (ctree));
1519
1520   if (!node || GTK_CTREE_ROW (node)->expanded || GTK_CTREE_ROW (node)->is_leaf)
1521     return;
1522
1523   clist = GTK_CLIST (ctree);
1524   
1525   GTK_CLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
1526
1527   GTK_CTREE_ROW (node)->expanded = TRUE;
1528   level = GTK_CTREE_ROW (node)->level;
1529
1530   visible = gtk_ctree_is_viewable (ctree, node);
1531   /* get cell width if tree_column is auto resized */
1532   if (visible && clist->column[ctree->tree_column].auto_resize &&
1533       !GTK_CLIST_AUTO_RESIZE_BLOCKED (clist))
1534     GTK_CLIST_GET_CLASS (clist)->cell_size_request
1535       (clist, &GTK_CTREE_ROW (node)->row, ctree->tree_column, &requisition);
1536
1537   /* unref/unset closed pixmap */
1538   if (GTK_CELL_PIXTEXT 
1539       (GTK_CTREE_ROW (node)->row.cell[ctree->tree_column])->pixmap)
1540     {
1541       gdk_pixmap_unref
1542         (GTK_CELL_PIXTEXT
1543          (GTK_CTREE_ROW (node)->row.cell[ctree->tree_column])->pixmap);
1544       
1545       GTK_CELL_PIXTEXT
1546         (GTK_CTREE_ROW (node)->row.cell[ctree->tree_column])->pixmap = NULL;
1547       
1548       if (GTK_CELL_PIXTEXT 
1549           (GTK_CTREE_ROW (node)->row.cell[ctree->tree_column])->mask)
1550         {
1551           gdk_pixmap_unref
1552             (GTK_CELL_PIXTEXT 
1553              (GTK_CTREE_ROW (node)->row.cell[ctree->tree_column])->mask);
1554           GTK_CELL_PIXTEXT 
1555             (GTK_CTREE_ROW (node)->row.cell[ctree->tree_column])->mask = NULL;
1556         }
1557     }
1558
1559   /* set/ref opened pixmap */
1560   if (GTK_CTREE_ROW (node)->pixmap_opened)
1561     {
1562       GTK_CELL_PIXTEXT 
1563         (GTK_CTREE_ROW (node)->row.cell[ctree->tree_column])->pixmap = 
1564         gdk_pixmap_ref (GTK_CTREE_ROW (node)->pixmap_opened);
1565
1566       if (GTK_CTREE_ROW (node)->mask_opened) 
1567         GTK_CELL_PIXTEXT 
1568           (GTK_CTREE_ROW (node)->row.cell[ctree->tree_column])->mask = 
1569           gdk_pixmap_ref (GTK_CTREE_ROW (node)->mask_opened);
1570     }
1571
1572
1573   work = GTK_CTREE_ROW (node)->children;
1574   if (work)
1575     {
1576       GList *list = (GList *)work;
1577       gint *cell_width = NULL;
1578       gint tmp = 0;
1579       gint row;
1580       gint i;
1581       
1582       if (visible && !GTK_CLIST_AUTO_RESIZE_BLOCKED (clist))
1583         {
1584           cell_width = g_new0 (gint, clist->columns);
1585           if (clist->column[ctree->tree_column].auto_resize)
1586               cell_width[ctree->tree_column] = requisition.width;
1587
1588           while (work)
1589             {
1590               /* search maximum cell widths of auto_resize columns */
1591               for (i = 0; i < clist->columns; i++)
1592                 if (clist->column[i].auto_resize)
1593                   {
1594                     GTK_CLIST_GET_CLASS (clist)->cell_size_request
1595                       (clist, &GTK_CTREE_ROW (work)->row, i, &requisition);
1596                     cell_width[i] = MAX (requisition.width, cell_width[i]);
1597                   }
1598
1599               list = (GList *)work;
1600               work = GTK_CTREE_NODE_NEXT (work);
1601               tmp++;
1602             }
1603         }
1604       else
1605         while (work)
1606           {
1607             list = (GList *)work;
1608             work = GTK_CTREE_NODE_NEXT (work);
1609             tmp++;
1610           }
1611
1612       list->next = (GList *)GTK_CTREE_NODE_NEXT (node);
1613
1614       if (GTK_CTREE_NODE_NEXT (node))
1615         {
1616           GList *tmp_list;
1617
1618           tmp_list = (GList *)GTK_CTREE_NODE_NEXT (node);
1619           tmp_list->prev = list;
1620         }
1621       else
1622         clist->row_list_end = list;
1623
1624       list = (GList *)node;
1625       list->next = (GList *)(GTK_CTREE_ROW (node)->children);
1626
1627       if (visible)
1628         {
1629           /* resize auto_resize columns if needed */
1630           for (i = 0; i < clist->columns; i++)
1631             if (clist->column[i].auto_resize &&
1632                 cell_width[i] > clist->column[i].width)
1633               gtk_clist_set_column_width (clist, i, cell_width[i]);
1634           g_free (cell_width);
1635         
1636           if (!GTK_SCTREE(ctree)->sorting) {
1637                   /* update focus_row position */
1638                   row = g_list_position (clist->row_list, (GList *)node);
1639                   if (row < clist->focus_row)
1640                     clist->focus_row += tmp;
1641           }
1642           clist->rows += tmp;
1643           CLIST_REFRESH (clist);
1644         }
1645     }
1646   else if (visible && clist->column[ctree->tree_column].auto_resize)
1647     /* resize tree_column if needed */
1648     gtk_sctree_column_auto_resize (clist, &GTK_CTREE_ROW (node)->row, ctree->tree_column,
1649                         requisition.width);
1650 }
1651
1652 GtkCTreeNode * 
1653 gtk_sctree_insert_node (GtkCTree     *ctree,
1654                        GtkCTreeNode *parent, 
1655                        GtkCTreeNode *sibling,
1656                        gchar        *text[],
1657                        guint8        spacing,
1658                        GdkPixmap    *pixmap_closed,
1659                        GdkBitmap    *mask_closed,
1660                        GdkPixmap    *pixmap_opened,
1661                        GdkBitmap    *mask_opened,
1662                        gboolean      is_leaf,
1663                        gboolean      expanded)
1664 {
1665   GtkCList *clist;
1666   GtkCTreeRow *new_row;
1667   GtkCTreeNode *node;
1668   GList *list;
1669   gint i;
1670
1671   if (!emptyxpm) {
1672           stock_pixmap_gdk(GTK_WIDGET(ctree), STOCK_PIXMAP_EMPTY,
1673                    &emptyxpm, &emptyxpmmask);
1674   }
1675   if (!pixmap_closed) {
1676           pixmap_closed = emptyxpm;
1677           mask_closed = emptyxpmmask;
1678   }
1679   if (!pixmap_opened) {
1680           pixmap_opened = emptyxpm;
1681           mask_opened = emptyxpmmask;
1682   }
1683   g_return_val_if_fail (GTK_IS_CTREE (ctree), NULL);
1684   if (sibling)
1685     g_return_val_if_fail (GTK_CTREE_ROW (sibling)->parent == parent, NULL);
1686
1687   if (parent && GTK_CTREE_ROW (parent)->is_leaf)
1688     return NULL;
1689
1690   clist = GTK_CLIST (ctree);
1691
1692   /* create the row */
1693   new_row = srow_new (ctree);
1694   list = g_list_alloc ();
1695   list->data = new_row;
1696   node = GTK_CTREE_NODE (list);
1697
1698   if (text)
1699     for (i = 0; i < clist->columns; i++)
1700       if (text[i] && i != ctree->tree_column)
1701         GTK_CLIST_GET_CLASS (clist)->set_cell_contents
1702           (clist, &(new_row->row), i, GTK_CELL_TEXT, text[i], 0, NULL, NULL);
1703
1704   sset_node_info (ctree, node, text ?
1705                  text[ctree->tree_column] : NULL, spacing, pixmap_closed,
1706                  mask_closed, pixmap_opened, mask_opened, is_leaf, expanded);
1707
1708   /* sorted insertion */
1709   if (GTK_CLIST_AUTO_SORT (clist))
1710     {
1711       if (parent)
1712         sibling = GTK_CTREE_ROW (parent)->children;
1713       else
1714         sibling = GTK_CTREE_NODE (clist->row_list);
1715
1716       while (sibling && clist->compare
1717              (clist, GTK_CTREE_ROW (node), GTK_CTREE_ROW (sibling)) > 0)
1718         sibling = GTK_CTREE_ROW (sibling)->sibling;
1719     }
1720
1721   gtk_sctree_link (ctree, node, parent, sibling, FALSE);
1722
1723   if (text && !GTK_CLIST_AUTO_RESIZE_BLOCKED (clist) &&
1724       gtk_ctree_is_viewable (ctree, node))
1725     {
1726       for (i = 0; i < clist->columns; i++)
1727         if (clist->column[i].auto_resize)
1728           gtk_sctree_column_auto_resize (clist, &(new_row->row), i, 0);
1729     }
1730
1731   if (clist->rows == 1)
1732     {
1733       clist->focus_row = 0;
1734       if (clist->selection_mode == GTK_SELECTION_BROWSE)
1735         gtk_sctree_select (GTK_SCTREE(ctree), node);
1736     }
1737
1738
1739   CLIST_REFRESH (clist);
1740
1741   return node;
1742 }
1743
1744 GtkCTreeNode *
1745 gtk_sctree_insert_gnode (GtkCTree          *ctree,
1746                         GtkCTreeNode      *parent,
1747                         GtkCTreeNode      *sibling,
1748                         GNode             *gnode,
1749                         GtkCTreeGNodeFunc  func,
1750                         gpointer           data)
1751 {
1752   GtkCList *clist;
1753   GtkCTreeNode *cnode = NULL;
1754   GtkCTreeNode *child = NULL;
1755   GtkCTreeNode *new_child;
1756   GList *list;
1757   GNode *work;
1758   guint depth = 1;
1759
1760   g_return_val_if_fail (GTK_IS_CTREE (ctree), NULL);
1761   g_return_val_if_fail (gnode != NULL, NULL);
1762   g_return_val_if_fail (func != NULL, NULL);
1763   if (sibling)
1764     g_return_val_if_fail (GTK_CTREE_ROW (sibling)->parent == parent, NULL);
1765   
1766   clist = GTK_CLIST (ctree);
1767
1768   if (parent)
1769     depth = GTK_CTREE_ROW (parent)->level + 1;
1770
1771   list = g_list_alloc ();
1772   list->data = srow_new (ctree);
1773   cnode = GTK_CTREE_NODE (list);
1774
1775   gtk_clist_freeze (clist);
1776
1777   sset_node_info (ctree, cnode, "", 0, NULL, NULL, NULL, NULL, TRUE, FALSE);
1778
1779   if (!func (ctree, depth, gnode, cnode, data))
1780     {
1781       stree_delete_row (ctree, cnode, NULL);
1782       gtk_clist_thaw (clist);
1783       return NULL;
1784     }
1785
1786   if (GTK_CLIST_AUTO_SORT (clist))
1787     {
1788       if (parent)
1789         sibling = GTK_CTREE_ROW (parent)->children;
1790       else
1791         sibling = GTK_CTREE_NODE (clist->row_list);
1792
1793       while (sibling && clist->compare
1794              (clist, GTK_CTREE_ROW (cnode), GTK_CTREE_ROW (sibling)) > 0)
1795         sibling = GTK_CTREE_ROW (sibling)->sibling;
1796     }
1797
1798   gtk_sctree_link (ctree, cnode, parent, sibling, FALSE);
1799
1800   for (work = g_node_last_child (gnode); work; work = work->prev)
1801     {
1802       new_child = gtk_sctree_insert_gnode (ctree, cnode, child,
1803                                           work, func, data);
1804       if (new_child)
1805         child = new_child;
1806     }   
1807   
1808   gtk_clist_thaw (clist);
1809
1810   return cnode;
1811 }
1812
1813 static void
1814 sreal_tree_move (GtkCTree     *ctree,
1815                 GtkCTreeNode *node,
1816                 GtkCTreeNode *new_parent, 
1817                 GtkCTreeNode *new_sibling)
1818 {
1819   GtkCList *clist;
1820   GtkCTreeNode *work;
1821   gboolean visible = FALSE;
1822
1823   g_return_if_fail (ctree != NULL);
1824   g_return_if_fail (node != NULL);
1825   g_return_if_fail (!new_sibling || 
1826                     GTK_CTREE_ROW (new_sibling)->parent == new_parent);
1827
1828   if (new_parent && GTK_CTREE_ROW (new_parent)->is_leaf)
1829     return;
1830
1831   /* new_parent != child of child */
1832   for (work = new_parent; work; work = GTK_CTREE_ROW (work)->parent)
1833     if (work == node)
1834       return;
1835
1836   clist = GTK_CLIST (ctree);
1837
1838   visible = gtk_ctree_is_viewable (ctree, node);
1839
1840   if (clist->selection_mode == GTK_SELECTION_MULTIPLE)
1841     {
1842       GTK_CLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
1843       
1844       g_list_free (clist->undo_selection);
1845       g_list_free (clist->undo_unselection);
1846       clist->undo_selection = NULL;
1847       clist->undo_unselection = NULL;
1848     }
1849
1850   if (GTK_CLIST_AUTO_SORT (clist))
1851     {
1852       if (new_parent == GTK_CTREE_ROW (node)->parent)
1853         return;
1854       
1855       if (new_parent)
1856         new_sibling = GTK_CTREE_ROW (new_parent)->children;
1857       else
1858         new_sibling = GTK_CTREE_NODE (clist->row_list);
1859
1860       while (new_sibling && clist->compare
1861              (clist, GTK_CTREE_ROW (node), GTK_CTREE_ROW (new_sibling)) > 0)
1862         new_sibling = GTK_CTREE_ROW (new_sibling)->sibling;
1863     }
1864
1865   if (new_parent == GTK_CTREE_ROW (node)->parent && 
1866       new_sibling == GTK_CTREE_ROW (node)->sibling)
1867     return;
1868
1869   gtk_clist_freeze (clist);
1870
1871   work = NULL;
1872
1873   if (!GTK_SCTREE(ctree)->sorting && gtk_ctree_is_viewable (ctree, node))
1874     work = GTK_CTREE_NODE (g_list_nth (clist->row_list, clist->focus_row));
1875       
1876   gtk_sctree_unlink (ctree, node, FALSE);
1877   gtk_sctree_link (ctree, node, new_parent, new_sibling, FALSE);
1878   
1879   if (!GTK_SCTREE(ctree)->sorting && work)
1880     {
1881       while (work &&  !gtk_ctree_is_viewable (ctree, work))
1882         work = GTK_CTREE_ROW (work)->parent;
1883       clist->focus_row = g_list_position (clist->row_list, (GList *)work);
1884       clist->undo_anchor = clist->focus_row;
1885     }
1886
1887   if (clist->column[ctree->tree_column].auto_resize &&
1888       !GTK_CLIST_AUTO_RESIZE_BLOCKED (clist) &&
1889       (visible || gtk_ctree_is_viewable (ctree, node)))
1890     gtk_clist_set_column_width
1891       (clist, ctree->tree_column,
1892        gtk_clist_optimal_column_width (clist, ctree->tree_column));
1893
1894   gtk_clist_thaw (clist);
1895 }
1896