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