2006-04-18 [colin] 2.1.1cvs13
[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 enum {
12         ROW_POPUP_MENU,
13         EMPTY_POPUP_MENU,
14         OPEN_ROW,
15         START_DRAG,
16         LAST_SIGNAL
17 };
18
19 static GdkPixmap *emptyxpm = NULL;
20 static GdkBitmap *emptyxpmmask = NULL;
21
22 static void gtk_sctree_class_init (GtkSCTreeClass *class);
23 static void gtk_sctree_init (GtkSCTree *sctree);
24
25 static gint gtk_sctree_button_press (GtkWidget *widget, GdkEventButton *event);
26 static gint gtk_sctree_button_release (GtkWidget *widget, GdkEventButton *event);
27 static gint gtk_sctree_motion (GtkWidget *widget, GdkEventMotion *event);
28 static void gtk_sctree_drag_begin (GtkWidget *widget, GdkDragContext *context);
29 static void gtk_sctree_drag_end (GtkWidget *widget, GdkDragContext *context);
30 static void gtk_sctree_drag_data_get (GtkWidget *widget, GdkDragContext *context,
31                                      GtkSelectionData *data, guint info, guint time);
32 static void gtk_sctree_drag_leave (GtkWidget *widget, GdkDragContext *context, guint time);
33 static gboolean gtk_sctree_drag_motion (GtkWidget *widget, GdkDragContext *context,
34                                        gint x, gint y, guint time);
35 static gboolean gtk_sctree_drag_drop (GtkWidget *widget, GdkDragContext *context,
36                                      gint x, gint y, guint time);
37 static void gtk_sctree_drag_data_received (GtkWidget *widget, GdkDragContext *context,
38                                           gint x, gint y, GtkSelectionData *data,
39                                           guint info, guint time);
40
41 static void gtk_sctree_clear (GtkCList *clist);
42 static void gtk_sctree_collapse (GtkCTree *ctree, GtkCTreeNode *node);
43        
44 static void tree_sort (GtkCTree *ctree, GtkCTreeNode  *node, gpointer data);
45 void gtk_sctree_sort_node (GtkCTree *ctree, GtkCTreeNode *node);
46 void gtk_sctree_sort_recursive (GtkCTree *ctree, GtkCTreeNode *node);
47
48 static void gtk_ctree_link (GtkCTree *ctree,
49                         GtkCTreeNode  *node,
50                         GtkCTreeNode  *parent,
51                         GtkCTreeNode  *sibling,
52                         gboolean       update_focus_row);
53
54 static void gtk_ctree_unlink (GtkCTree      *ctree, 
55                         GtkCTreeNode  *node,
56                         gboolean       update_focus_row);
57
58 static void tree_update_level (GtkCTree      *ctree, 
59                         GtkCTreeNode  *node, 
60                         gpointer       data);
61
62 static GtkCTreeNode * gtk_ctree_last_visible (GtkCTree     *ctree,
63                                               GtkCTreeNode *node);
64
65 static GtkCTreeClass *parent_class;
66
67 static guint sctree_signals[LAST_SIGNAL];
68
69
70 /**
71  * gtk_sctree_get_type:
72  * @void: 
73  * 
74  * Creates the GtkSCTree class and its type information
75  * 
76  * Return value: The type ID for GtkSCTreeClass
77  **/
78 GType
79 gtk_sctree_get_type (void)
80 {
81         static GType sctree_type = 0;
82
83         if (!sctree_type) {
84                 GTypeInfo sctree_info = {
85                         sizeof (GtkSCTreeClass),
86
87                         (GBaseInitFunc) NULL,
88                         (GBaseFinalizeFunc) NULL,
89
90                         (GClassInitFunc) gtk_sctree_class_init,
91                         (GClassFinalizeFunc) NULL,
92                         NULL,   /* class_data */
93
94                         sizeof (GtkSCTree),
95                         0,      /* n_preallocs */
96                         (GInstanceInitFunc) gtk_sctree_init,
97                 };
98
99                 sctree_type = g_type_register_static (GTK_TYPE_CTREE, "GtkSCTree", &sctree_info, (GTypeFlags)0);
100         }
101
102         return sctree_type;
103 }
104
105 /* Standard class initialization function */
106 static void
107 gtk_sctree_class_init (GtkSCTreeClass *klass)
108 {
109         GtkObjectClass *object_class;
110         GtkWidgetClass *widget_class;
111         GtkCListClass *clist_class;
112         GtkCTreeClass *ctree_class;
113
114         object_class = (GtkObjectClass *) klass;
115         widget_class = (GtkWidgetClass *) klass;
116         clist_class = (GtkCListClass *) klass;
117         ctree_class = (GtkCTreeClass *) klass;
118
119         parent_class = gtk_type_class (gtk_ctree_get_type ());
120
121         sctree_signals[ROW_POPUP_MENU] =
122                 g_signal_new ("row_popup_menu",
123                               G_TYPE_FROM_CLASS (klass),
124                               G_SIGNAL_RUN_FIRST,
125                               G_STRUCT_OFFSET (GtkSCTreeClass, row_popup_menu),
126                               NULL, NULL,
127                               sylpheed_marshal_VOID__POINTER,
128                               G_TYPE_NONE, 1,
129                               GDK_TYPE_EVENT);
130         sctree_signals[EMPTY_POPUP_MENU] =
131                 g_signal_new ("empty_popup_menu",
132                               G_TYPE_FROM_CLASS (klass),
133                               G_SIGNAL_RUN_FIRST,
134                               G_STRUCT_OFFSET (GtkSCTreeClass, empty_popup_menu),
135                               NULL, NULL,
136                               sylpheed_marshal_VOID__POINTER,
137                               G_TYPE_NONE, 1,
138                               GDK_TYPE_EVENT);
139         sctree_signals[OPEN_ROW] =
140                 g_signal_new ("open_row",
141                               G_TYPE_FROM_CLASS (klass),
142                               G_SIGNAL_RUN_FIRST,
143                               G_STRUCT_OFFSET (GtkSCTreeClass, open_row),
144                               NULL, NULL,
145                               g_cclosure_marshal_VOID__VOID,
146                               G_TYPE_NONE, 0);
147         sctree_signals[START_DRAG] =
148                 g_signal_new ("start_drag",
149                               G_TYPE_FROM_CLASS (klass),
150                               G_SIGNAL_RUN_FIRST,
151                               G_STRUCT_OFFSET (GtkSCTreeClass, start_drag),
152                               NULL, NULL,
153                               sylpheed_marshal_VOID__INT_POINTER,
154                               G_TYPE_NONE, 2,
155                               G_TYPE_INT,
156                               GDK_TYPE_EVENT);
157
158         /* gtk_object_class_add_signals (object_class, sctree_signals, LAST_SIGNAL); */
159
160         clist_class->clear = gtk_sctree_clear;
161         ctree_class->tree_collapse = gtk_sctree_collapse;
162         
163         widget_class->button_press_event = gtk_sctree_button_press;
164         widget_class->button_release_event = gtk_sctree_button_release;
165         widget_class->motion_notify_event = gtk_sctree_motion;
166         widget_class->drag_begin = gtk_sctree_drag_begin;
167         widget_class->drag_end = gtk_sctree_drag_end;
168         widget_class->drag_data_get = gtk_sctree_drag_data_get;
169         widget_class->drag_leave = gtk_sctree_drag_leave;
170         widget_class->drag_motion = gtk_sctree_drag_motion;
171         widget_class->drag_drop = gtk_sctree_drag_drop;
172         widget_class->drag_data_received = gtk_sctree_drag_data_received;
173 }
174
175 /* Standard object initialization function */
176 static void
177 gtk_sctree_init (GtkSCTree *sctree)
178 {
179         sctree->anchor_row = NULL;
180
181         /* GtkCTree does not specify pointer motion by default */
182         gtk_widget_add_events (GTK_WIDGET (sctree), GDK_POINTER_MOTION_MASK);
183         gtk_widget_add_events (GTK_WIDGET (sctree), GDK_POINTER_MOTION_MASK);
184 }
185
186 /* Get information the specified row is selected. */
187
188 static gboolean
189 row_is_selected(GtkSCTree *sctree, gint row)
190 {
191         GtkCListRow *clist_row;
192         clist_row =  g_list_nth (GTK_CLIST(sctree)->row_list, row)->data;
193         return clist_row ? clist_row->state == GTK_STATE_SELECTED : FALSE;
194 }
195
196 /* Selects the rows between the anchor to the specified row, inclusive.  */
197 static void
198 select_range (GtkSCTree *sctree, gint row)
199 {
200         gint prev_row;
201         gint min, max;
202         gint i;
203
204         if (sctree->anchor_row == NULL) {
205                 prev_row = row;
206                 sctree->anchor_row = gtk_ctree_node_nth(GTK_CTREE(sctree), row);
207         } else
208                 prev_row = g_list_position(GTK_CLIST(sctree)->row_list,
209                                            (GList *)sctree->anchor_row);
210
211         if (row < prev_row) {
212                 min = row;
213                 max = prev_row;
214                 GTK_CLIST(sctree)->focus_row = max;
215         } else {
216                 min = prev_row;
217                 max = row;
218         }
219         sctree->selecting_range = TRUE;
220         for (i = min; i < max; i++)
221                 gtk_clist_select_row (GTK_CLIST (sctree), i, -1);
222
223         sctree->selecting_range = FALSE;
224         gtk_clist_select_row (GTK_CLIST (sctree), max, -1);
225 }
226
227 /* Handles row selection according to the specified modifier state */
228 static void
229 select_row (GtkSCTree *sctree, gint row, gint col, guint state)
230 {
231         gboolean range, additive;
232         g_return_if_fail (sctree != NULL);
233         g_return_if_fail (GTK_IS_SCTREE (sctree));
234     
235         range = ((state & GDK_SHIFT_MASK) != 0) &&
236                 (GTK_CLIST(sctree)->selection_mode != GTK_SELECTION_SINGLE) &&
237                 (GTK_CLIST(sctree)->selection_mode != GTK_SELECTION_BROWSE);
238         additive = ((state & GDK_CONTROL_MASK) != 0) &&
239                    (GTK_CLIST(sctree)->selection_mode != GTK_SELECTION_SINGLE) &&
240                    (GTK_CLIST(sctree)->selection_mode != GTK_SELECTION_BROWSE);
241
242         gtk_clist_freeze (GTK_CLIST (sctree));
243
244         GTK_CLIST(sctree)->focus_row = row;
245
246         if (!additive) {
247                 sctree->selecting_range = TRUE;
248                 gtk_clist_unselect_all (GTK_CLIST (sctree));
249                 sctree->selecting_range = FALSE;
250         }
251
252         if (!range) {
253                 GtkCTreeNode *node;
254
255                 node = gtk_ctree_node_nth (GTK_CTREE(sctree), row);
256
257                 /*No need to manage overlapped list*/
258                 if (additive) {
259                         if (row_is_selected(sctree, row))
260                                 gtk_clist_unselect_row (GTK_CLIST (sctree), row, col);
261                         else
262                                 g_signal_emit_by_name
263                                         (G_OBJECT (sctree),
264                                          "tree_select_row", node, col);
265                 } else {
266                         g_signal_emit_by_name
267                                 (G_OBJECT (sctree),
268                                  "tree_select_row", node, col);
269                 }
270                 sctree->anchor_row = node;
271         } else
272                 select_range (sctree, row);
273         
274         gtk_clist_thaw (GTK_CLIST (sctree));
275 }
276
277 /* Our handler for button_press events.  We override all of GtkCList's broken
278  * behavior.
279  */
280 static gint
281 gtk_sctree_button_press (GtkWidget *widget, GdkEventButton *event)
282 {
283         GtkSCTree *sctree;
284         GtkCList *clist;
285         gboolean on_row;
286         gint row;
287         gint col;
288         gint retval;
289
290         g_return_val_if_fail (widget != NULL, FALSE);
291         g_return_val_if_fail (GTK_IS_SCTREE (widget), FALSE);
292         g_return_val_if_fail (event != NULL, FALSE);
293
294         sctree = GTK_SCTREE (widget);
295         clist = GTK_CLIST (widget);
296         retval = FALSE;
297
298         if (event->window != clist->clist_window)
299                 return (* GTK_WIDGET_CLASS (parent_class)->button_press_event) (widget, event);
300
301         on_row = gtk_clist_get_selection_info (clist, event->x, event->y, &row, &col);
302
303         if (on_row && !GTK_WIDGET_HAS_FOCUS(widget))
304                 gtk_widget_grab_focus (widget);
305
306         if (gtk_ctree_is_hot_spot (GTK_CTREE(sctree), event->x, event->y)) {
307                 gtk_ctree_toggle_expansion
308                         (GTK_CTREE(sctree), 
309                          gtk_ctree_node_nth(GTK_CTREE(sctree), row));
310                 return TRUE;
311         }
312
313         switch (event->type) {
314         case GDK_BUTTON_PRESS:
315                 if (event->button == 1 || event->button == 2) {
316                         if (event->button == 2)
317                                 event->state &= ~(GDK_SHIFT_MASK | GDK_CONTROL_MASK);
318                         if (on_row) {
319                                 /* Save the mouse info for DnD */
320                                 sctree->dnd_press_button = event->button;
321                                 sctree->dnd_press_x = event->x;
322                                 sctree->dnd_press_y = event->y;
323
324                                 /* Handle selection */
325                                 if ((row_is_selected (sctree, row)
326                                      && !(event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK)))
327                                     || ((event->state & GDK_CONTROL_MASK)
328                                         && !(event->state & GDK_SHIFT_MASK))) {
329                                         sctree->dnd_select_pending = TRUE;
330                                         sctree->dnd_select_pending_state = event->state;
331                                         sctree->dnd_select_pending_row = row;
332                                 } else
333                                         select_row (sctree, row, col, event->state);
334                         } else {
335                                 sctree->selecting_range = TRUE;
336                                 gtk_clist_unselect_all (clist);
337                                 sctree->selecting_range = FALSE;
338                         }
339
340                         retval = TRUE;
341                 } else if (event->button == 3) {
342                         /* Emit *_popup_menu signal*/
343                         if (on_row) {
344                                 if (!row_is_selected(sctree,row))
345                                         select_row (sctree, row, col, 0);
346                                 g_signal_emit (G_OBJECT (sctree),
347                                                  sctree_signals[ROW_POPUP_MENU],
348                                                  0, event);
349                         } else {
350                                 sctree->selecting_range = TRUE;
351                                 gtk_clist_unselect_all(clist);
352                                 sctree->selecting_range = FALSE;
353                                 g_signal_emit (G_OBJECT (sctree),
354                                                  sctree_signals[EMPTY_POPUP_MENU],
355                                                  0, event);
356                         }
357                         retval = TRUE;
358                 }
359
360                 break;
361
362         case GDK_2BUTTON_PRESS:
363                 if (event->button != 1)
364                         break;
365
366                 sctree->dnd_select_pending = FALSE;
367                 sctree->dnd_select_pending_state = 0;
368
369                 if (on_row)
370                         g_signal_emit (G_OBJECT (sctree),
371                                        sctree_signals[OPEN_ROW], 0);
372
373                 retval = TRUE;
374                 break;
375
376         default:
377                 break;
378         }
379
380         return retval;
381 }
382
383 /* Our handler for button_release events.  We override all of GtkCList's broken
384  * behavior.
385  */
386 static gint
387 gtk_sctree_button_release (GtkWidget *widget, GdkEventButton *event)
388 {
389         GtkSCTree *sctree;
390         GtkCList *clist;
391         gint on_row;
392         gint row, col;
393         gint retval;
394
395         g_return_val_if_fail (widget != NULL, FALSE);
396         g_return_val_if_fail (GTK_IS_SCTREE (widget), FALSE);
397         g_return_val_if_fail (event != NULL, FALSE);
398
399         sctree = GTK_SCTREE (widget);
400         clist = GTK_CLIST (widget);
401         retval = FALSE;
402
403         if (event->window != clist->clist_window)
404                 return (* GTK_WIDGET_CLASS (parent_class)->button_release_event) (widget, event);
405
406         on_row = gtk_clist_get_selection_info (clist, event->x, event->y, &row, &col);
407
408         if (!(event->button == 1 || event->button == 2))
409                 return FALSE;
410
411         sctree->dnd_press_button = 0;
412         sctree->dnd_press_x = 0;
413         sctree->dnd_press_y = 0;
414
415         if (on_row) {
416                 if (sctree->dnd_select_pending) {
417                         select_row (sctree, row, col, sctree->dnd_select_pending_state);
418                         sctree->dnd_select_pending = FALSE;
419                         sctree->dnd_select_pending_state = 0;
420                 }
421
422                 retval = TRUE;
423         }
424
425         return retval;
426 }
427
428 /* Our handler for motion_notify events.  We override all of GtkCList's broken
429  * behavior.
430  */
431 static gint
432 gtk_sctree_motion (GtkWidget *widget, GdkEventMotion *event)
433 {
434         GtkSCTree *sctree;
435         GtkCList *clist;
436
437         g_return_val_if_fail (widget != NULL, FALSE);
438         g_return_val_if_fail (GTK_IS_SCTREE (widget), FALSE);
439         g_return_val_if_fail (event != NULL, FALSE);
440
441         sctree = GTK_SCTREE (widget);
442         clist = GTK_CLIST (widget);
443
444         if (event->window != clist->clist_window)
445                 return (* GTK_WIDGET_CLASS (parent_class)->motion_notify_event) (widget, event);
446
447         if (!((sctree->dnd_press_button == 1 && (event->state & GDK_BUTTON1_MASK))
448               || (sctree->dnd_press_button == 2 && (event->state & GDK_BUTTON2_MASK))))
449                 return FALSE;
450
451         /* This is the same threshold value that is used in gtkdnd.c */
452
453         if (MAX (ABS (sctree->dnd_press_x - event->x),
454                  ABS (sctree->dnd_press_y - event->y)) <= 3)
455                 return FALSE;
456
457         /* Handle any pending selections */
458
459         if (sctree->dnd_select_pending) {
460                 if (!row_is_selected(sctree,sctree->dnd_select_pending_row))
461                         select_row (sctree,
462                                     sctree->dnd_select_pending_row,
463                                     -1,
464                                     sctree->dnd_select_pending_state);
465
466                 sctree->dnd_select_pending = FALSE;
467                 sctree->dnd_select_pending_state = 0;
468         }
469
470         g_signal_emit (G_OBJECT (sctree),
471                        sctree_signals[START_DRAG],
472                        0,
473                        sctree->dnd_press_button,
474                        event);
475         return TRUE;
476 }
477
478 /* We override the drag_begin signal to do nothing */
479 static void
480 gtk_sctree_drag_begin (GtkWidget *widget, GdkDragContext *context)
481 {
482         /* nothing */
483 }
484
485 /* We override the drag_end signal to do nothing */
486 static void
487 gtk_sctree_drag_end (GtkWidget *widget, GdkDragContext *context)
488 {
489         /* nothing */
490 }
491
492 /* We override the drag_data_get signal to do nothing */
493 static void
494 gtk_sctree_drag_data_get (GtkWidget *widget, GdkDragContext *context,
495                                      GtkSelectionData *data, guint info, guint time)
496 {
497         /* nothing */
498 }
499
500 /* We override the drag_leave signal to do nothing */
501 static void
502 gtk_sctree_drag_leave (GtkWidget *widget, GdkDragContext *context, guint time)
503 {
504         /* nothing */
505 }
506
507 /* We override the drag_motion signal to do nothing */
508 static gboolean
509 gtk_sctree_drag_motion (GtkWidget *widget, GdkDragContext *context,
510                                    gint x, gint y, guint time)
511 {
512         return FALSE;
513 }
514
515 /* We override the drag_drop signal to do nothing */
516 static gboolean
517 gtk_sctree_drag_drop (GtkWidget *widget, GdkDragContext *context,
518                                  gint x, gint y, guint time)
519 {
520         return FALSE;
521 }
522
523 /* We override the drag_data_received signal to do nothing */
524 static void
525 gtk_sctree_drag_data_received (GtkWidget *widget, GdkDragContext *context,
526                                           gint x, gint y, GtkSelectionData *data,
527                                           guint info, guint time)
528 {
529         /* nothing */
530 }
531
532 /* Our handler for the clear signal of the clist.  We have to reset the anchor
533  * to null.
534  */
535 static void
536 gtk_sctree_clear (GtkCList *clist)
537 {
538         GtkSCTree *sctree;
539
540         g_return_if_fail (clist != NULL);
541         g_return_if_fail (GTK_IS_SCTREE (clist));
542
543         sctree = GTK_SCTREE (clist);
544         sctree->anchor_row = NULL;
545
546         if (((GtkCListClass *)parent_class)->clear)
547                 (* ((GtkCListClass *)parent_class)->clear) (clist);
548 }
549
550 /* Our handler for the change_focus_row_expansion signal of the ctree.  
551  We have to set the anchor to parent visible node.
552  */
553 static void 
554 gtk_sctree_collapse (GtkCTree *ctree, GtkCTreeNode *node)
555 {
556         g_return_if_fail (ctree != NULL);
557         g_return_if_fail (GTK_IS_SCTREE (ctree));
558
559         (* parent_class->tree_collapse) (ctree, node);
560         GTK_SCTREE(ctree)->anchor_row =
561                 gtk_ctree_node_nth(ctree, GTK_CLIST(ctree)->focus_row);
562 }
563
564 GtkWidget *gtk_sctree_new_with_titles (gint columns, gint tree_column, 
565                                        gchar *titles[])
566 {
567         GtkWidget *widget;
568                                                                                                             
569         g_return_val_if_fail (columns > 0, NULL);
570         g_return_val_if_fail (tree_column >= 0, NULL);
571                                                                                                             
572         widget = gtk_widget_new (TYPE_GTK_SCTREE,
573                                  "n_columns", columns,
574                                  "tree_column", tree_column,
575                                  NULL);
576         if (titles) {
577                 GtkCList *clist = GTK_CLIST (widget);
578                 guint i;
579
580                 for (i = 0; i < columns; i++)
581                         gtk_clist_set_column_title (clist, i, titles[i]);
582                 gtk_clist_column_titles_show (clist);
583         }
584
585         return widget;
586 }
587
588 void gtk_sctree_select (GtkSCTree *sctree, GtkCTreeNode *node)
589 {
590         select_row(sctree, 
591                    g_list_position(GTK_CLIST(sctree)->row_list, (GList *)node),
592                    -1, 0);
593 }
594
595 void gtk_sctree_select_with_state (GtkSCTree *sctree, GtkCTreeNode *node, int state)
596 {
597         select_row(sctree, 
598                    g_list_position(GTK_CLIST(sctree)->row_list, (GList *)node),
599                    -1, state);
600 }
601
602 void gtk_sctree_unselect_all (GtkSCTree *sctree)
603 {
604         sctree->selecting_range = TRUE;
605         gtk_clist_unselect_all(GTK_CLIST(sctree));
606         sctree->selecting_range = FALSE;
607         sctree->anchor_row = NULL;
608 }
609
610 void gtk_sctree_set_anchor_row (GtkSCTree *sctree, GtkCTreeNode *node)
611 {
612         sctree->anchor_row = node;
613 }
614
615 void gtk_sctree_remove_node (GtkSCTree *sctree, GtkCTreeNode *node)
616 {
617         if (sctree->anchor_row == node)
618                 sctree->anchor_row = NULL;
619         gtk_ctree_remove_node(GTK_CTREE(sctree), node);
620 }
621
622 /***********************************************************
623  *             Tree sorting functions                      *
624  ***********************************************************/
625
626 static void sink(GtkCList *clist, GPtrArray *numbers, gint root, gint bottom)
627 {
628         gint j, k ;
629         GtkCTreeNode *temp;
630
631         j = 2 * root;
632         k = j + 1;
633
634         /* find the maximum element of numbers[root],
635            numbers[2*root] and numbers[2*root+1] */
636         if (j <= bottom) {
637                 if (clist->compare( clist, GTK_CTREE_ROW (g_ptr_array_index(numbers, root)),
638                                     GTK_CTREE_ROW(g_ptr_array_index( numbers, j))) >= 0)
639                         j = root;
640                 if (k <= bottom)
641                         if (clist->compare( clist, GTK_CTREE_ROW (g_ptr_array_index(numbers, k)),
642                                             GTK_CTREE_ROW (g_ptr_array_index( numbers, j))) > 0)
643                                 j = k;
644                 /* if numbers[root] wasn't the maximum element then
645                    sink again */
646                 if (root != j) {
647                         temp = g_ptr_array_index( numbers,root);
648                         g_ptr_array_index( numbers, root) = g_ptr_array_index( numbers, j);
649                         g_ptr_array_index( numbers, j) = temp;
650                         sink( clist, numbers, j, bottom);
651                 }
652         }
653 }
654
655 static void heap_sort(GtkCList *clist, GPtrArray *numbers, gint array_size)
656 {
657         gint i;
658         GtkCTreeNode *temp;
659         
660         /* build the Heap */
661         for (i = (array_size / 2); i >= 1; i--)
662                 sink( clist, numbers, i, array_size);
663         /* output the Heap */
664         for (i = array_size; i >= 2; i--) {
665                 temp = g_ptr_array_index( numbers, 1);
666                 g_ptr_array_index( numbers, 1) = g_ptr_array_index( numbers, i);
667                 g_ptr_array_index( numbers, i) = temp;
668                 sink( clist, numbers, 1, i-1);
669         }
670 }
671
672 static void
673 tree_sort (GtkCTree    *ctree,
674            GtkCTreeNode *node,
675            gpointer      data)
676 {
677         GtkCTreeNode *list_start, *work, *next;
678         GPtrArray *row_array, *viewable_array;
679         GtkCList *clist;
680         gint i;
681
682
683         clist = GTK_CLIST (ctree);
684
685         if (node)
686                 work = GTK_CTREE_ROW (node)->children;
687         else
688                 work = GTK_CTREE_NODE (clist->row_list);
689
690         row_array = g_ptr_array_new();
691         viewable_array = g_ptr_array_new();
692
693         if (work) {
694                 g_ptr_array_add( row_array, NULL);
695                 while (work) {
696                         /* add all rows to row_array */
697                         g_ptr_array_add( row_array, work);
698                         if (GTK_CTREE_ROW (work)->parent && gtk_ctree_is_viewable( ctree, work))
699                                 g_ptr_array_add( viewable_array, GTK_CTREE_ROW (work)->parent);
700                         next = GTK_CTREE_ROW (work)->sibling;
701                         gtk_ctree_unlink( ctree, work, FALSE);
702                         work = next;
703                 }
704
705                 heap_sort( clist, row_array, (row_array->len)-1);
706
707                 if (node)
708                         list_start = GTK_CTREE_ROW (node)->children;
709                 else
710                         list_start = GTK_CTREE_NODE (clist->row_list);
711
712                 if (clist->sort_type == GTK_SORT_ASCENDING) {
713                         for (i=(row_array->len)-1; i>=1; i--) {
714                                 work = g_ptr_array_index( row_array, i);
715                                 gtk_ctree_link( ctree, work, node, list_start, FALSE);
716                                 list_start = work;
717                                 /* insert work at the beginning of the list */
718                         }
719                 } else {
720                         for (i=1; i<row_array->len; i++) {
721                                 work = g_ptr_array_index( row_array, i);
722                                 gtk_ctree_link( ctree, work, node, list_start, FALSE);
723                                 list_start = work;
724                                 /* insert work at the beginning of the list */
725                         }
726                 }
727
728                 for (i=0; i<viewable_array->len; i++) {
729                         gtk_ctree_expand( ctree, g_ptr_array_index( viewable_array, i));
730                 }
731                 
732         }
733
734         g_ptr_array_free( row_array, TRUE);
735         g_ptr_array_free( viewable_array, TRUE);
736 }
737
738 void
739 gtk_sctree_sort_recursive (GtkCTree     *ctree, 
740                           GtkCTreeNode *node)
741 {
742         GtkCList *clist;
743         GtkCTreeNode *focus_node = NULL;
744
745         g_return_if_fail (ctree != NULL);
746         g_return_if_fail (GTK_IS_CTREE (ctree));
747
748         clist = GTK_CLIST (ctree);
749
750         gtk_clist_freeze (clist);
751
752         if (clist->selection_mode == GTK_SELECTION_EXTENDED) {
753                 GTK_CLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
754       
755                 g_list_free (clist->undo_selection);
756                 g_list_free (clist->undo_unselection);
757                 clist->undo_selection = NULL;
758                 clist->undo_unselection = NULL;
759         }
760
761         if (!node || (node && gtk_ctree_is_viewable (ctree, node)))
762                 focus_node = GTK_CTREE_NODE (g_list_nth (clist->row_list, clist->focus_row));
763       
764         gtk_ctree_post_recursive (ctree, node, GTK_CTREE_FUNC (tree_sort), NULL);
765
766         if (!node)
767                 tree_sort (ctree, NULL, NULL);
768
769         if (focus_node) {
770                 clist->focus_row = g_list_position (clist->row_list,(GList *)focus_node);
771                 clist->undo_anchor = clist->focus_row;
772         }
773
774         gtk_clist_thaw (clist);
775 }
776
777 void
778 gtk_sctree_sort_node (GtkCTree     *ctree, 
779                      GtkCTreeNode *node)
780 {
781         GtkCList *clist;
782         GtkCTreeNode *focus_node = NULL;
783
784         g_return_if_fail (ctree != NULL);
785         g_return_if_fail (GTK_IS_CTREE (ctree));
786
787         clist = GTK_CLIST (ctree);
788
789         gtk_clist_freeze (clist);
790
791         if (clist->selection_mode == GTK_SELECTION_EXTENDED) {
792                 GTK_CLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
793
794                 g_list_free (clist->undo_selection);
795                 g_list_free (clist->undo_unselection);
796                 clist->undo_selection = NULL;
797                 clist->undo_unselection = NULL;
798         }
799
800         if (!node || (node && gtk_ctree_is_viewable (ctree, node)))
801                 focus_node = GTK_CTREE_NODE (g_list_nth (clist->row_list, clist->focus_row));
802
803         tree_sort (ctree, node, NULL);
804
805         if (focus_node) {
806                 clist->focus_row = g_list_position (clist->row_list,(GList *)focus_node);
807                 clist->undo_anchor = clist->focus_row;
808         }
809
810         gtk_clist_thaw (clist);
811 }
812
813 /************************************************************************/
814
815 static void
816 gtk_ctree_unlink (GtkCTree     *ctree, 
817                   GtkCTreeNode *node,
818                   gboolean      update_focus_row)
819 {
820         GtkCList *clist;
821         gint rows;
822         gint level;
823         gint visible;
824         GtkCTreeNode *work;
825         GtkCTreeNode *parent;
826         GList *list;
827
828         g_return_if_fail (ctree != NULL);
829         g_return_if_fail (GTK_IS_CTREE (ctree));
830         g_return_if_fail (node != NULL);
831
832         clist = GTK_CLIST (ctree);
833   
834         if (update_focus_row && clist->selection_mode == GTK_SELECTION_EXTENDED) {
835                 GTK_CLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
836
837                 g_list_free (clist->undo_selection);
838                 g_list_free (clist->undo_unselection);
839                 clist->undo_selection = NULL;
840                 clist->undo_unselection = NULL;
841         }
842
843         visible = gtk_ctree_is_viewable (ctree, node);
844
845         /* clist->row_list_end unlinked ? */
846         if (visible && (GTK_CTREE_NODE_NEXT (node) == NULL ||
847            (GTK_CTREE_ROW (node)->children && gtk_ctree_is_ancestor (ctree, node,
848             GTK_CTREE_NODE (clist->row_list_end)))))
849                 clist->row_list_end = (GList *) (GTK_CTREE_NODE_PREV (node));
850
851         /* update list */
852         rows = 0;
853         level = GTK_CTREE_ROW (node)->level;
854         work = GTK_CTREE_NODE_NEXT (node);
855         while (work && GTK_CTREE_ROW (work)->level > level) {
856                 work = GTK_CTREE_NODE_NEXT (work);
857                 rows++;
858         }
859
860         if (visible) {
861                 clist->rows -= (rows + 1);
862
863                 if (update_focus_row) {
864                         gint pos;
865
866                         pos = g_list_position (clist->row_list, (GList *)node);
867                         if (pos + rows < clist->focus_row)
868                                 clist->focus_row -= (rows + 1);
869                         else if (pos <= clist->focus_row) {
870                                 if (!GTK_CTREE_ROW (node)->sibling)
871                                         clist->focus_row = MAX (pos - 1, 0);
872                                 else
873                                         clist->focus_row = pos;
874               
875                                 clist->focus_row = MIN (clist->focus_row, clist->rows - 1);
876                         }
877                         clist->undo_anchor = clist->focus_row;
878                 }
879         }
880
881         if (work) {
882                 list = (GList *)GTK_CTREE_NODE_PREV (work);
883                 list->next = NULL;
884                 list = (GList *)work;
885                 list->prev = (GList *)GTK_CTREE_NODE_PREV (node);
886         }
887
888         if (GTK_CTREE_NODE_PREV (node) &&
889             GTK_CTREE_NODE_NEXT (GTK_CTREE_NODE_PREV (node)) == node) {
890                 list = (GList *)GTK_CTREE_NODE_PREV (node);
891                 list->next = (GList *)work;
892         }
893
894         /* update tree */
895         parent = GTK_CTREE_ROW (node)->parent;
896         if (parent) {
897                 if (GTK_CTREE_ROW (parent)->children == node) {
898                         GTK_CTREE_ROW (parent)->children = GTK_CTREE_ROW (node)->sibling;
899                         if (!GTK_CTREE_ROW (parent)->children)
900                                 gtk_ctree_collapse (ctree, parent);
901                 }
902                 else {
903                         GtkCTreeNode *sibling;
904
905                         sibling = GTK_CTREE_ROW (parent)->children;
906                         while (GTK_CTREE_ROW (sibling)->sibling != node)
907                                 sibling = GTK_CTREE_ROW (sibling)->sibling;
908                         GTK_CTREE_ROW (sibling)->sibling = GTK_CTREE_ROW (node)->sibling;
909                 }
910         }
911         else {
912                 if (clist->row_list == (GList *)node)
913                         clist->row_list = (GList *) (GTK_CTREE_ROW (node)->sibling);
914                 else {
915                         GtkCTreeNode *sibling;
916
917                         sibling = GTK_CTREE_NODE (clist->row_list);
918                         while (GTK_CTREE_ROW (sibling)->sibling != node)
919                                 sibling = GTK_CTREE_ROW (sibling)->sibling;
920                         GTK_CTREE_ROW (sibling)->sibling = GTK_CTREE_ROW (node)->sibling;
921                 }
922         }
923 }
924
925 static void
926 gtk_ctree_link (GtkCTree     *ctree,
927                 GtkCTreeNode *node,
928                 GtkCTreeNode *parent,
929                 GtkCTreeNode *sibling,
930                 gboolean      update_focus_row)
931 {
932         GtkCList *clist;
933         GList *list_end;
934         GList *list;
935         GList *work;
936         gboolean visible = FALSE;
937         gint rows = 0;
938   
939         if (sibling)
940                 g_return_if_fail (GTK_CTREE_ROW (sibling)->parent == parent);
941         g_return_if_fail (node != NULL);
942         g_return_if_fail (node != sibling);
943         g_return_if_fail (node != parent);
944
945         clist = GTK_CLIST (ctree);
946
947         if (update_focus_row && clist->selection_mode == GTK_SELECTION_EXTENDED) {
948                 GTK_CLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
949
950                 g_list_free (clist->undo_selection);
951                 g_list_free (clist->undo_unselection);
952                 clist->undo_selection = NULL;
953                 clist->undo_unselection = NULL;
954         }
955
956         for (rows = 1, list_end = (GList *)node; list_end->next;
957              list_end = list_end->next)
958                 rows++;
959
960         GTK_CTREE_ROW (node)->parent = parent;
961         GTK_CTREE_ROW (node)->sibling = sibling;
962
963         if (!parent || (parent && (gtk_ctree_is_viewable (ctree, parent) &&
964             GTK_CTREE_ROW (parent)->expanded))) {
965                 visible = TRUE;
966                 clist->rows += rows;
967         }
968
969         if (parent)
970                 work = (GList *)(GTK_CTREE_ROW (parent)->children);
971         else
972                 work = clist->row_list;
973
974         if (sibling) {
975                 if (work != (GList *)sibling) {
976                         while (GTK_CTREE_ROW (work)->sibling != sibling)
977                                 work = (GList *)(GTK_CTREE_ROW (work)->sibling);
978                         GTK_CTREE_ROW (work)->sibling = node;
979                 }
980
981                 if (sibling == GTK_CTREE_NODE (clist->row_list))
982                 clist->row_list = (GList *) node;
983                 if (GTK_CTREE_NODE_PREV (sibling) &&
984                     GTK_CTREE_NODE_NEXT (GTK_CTREE_NODE_PREV (sibling)) == sibling) {
985                         list = (GList *)GTK_CTREE_NODE_PREV (sibling);
986                         list->next = (GList *)node;
987                 }
988
989                 list = (GList *)node;
990                 list->prev = (GList *)GTK_CTREE_NODE_PREV (sibling);
991                 list_end->next = (GList *)sibling;
992                 list = (GList *)sibling;
993                 list->prev = list_end;
994                 if (parent && GTK_CTREE_ROW (parent)->children == sibling)
995                         GTK_CTREE_ROW (parent)->children = node;
996         }
997         else {
998                 if (work) {
999                         /* find sibling */
1000                         while (GTK_CTREE_ROW (work)->sibling)
1001                         work = (GList *)(GTK_CTREE_ROW (work)->sibling);
1002                         GTK_CTREE_ROW (work)->sibling = node;
1003
1004                         /* find last visible child of sibling */
1005                         work = (GList *) gtk_ctree_last_visible (ctree,
1006                                GTK_CTREE_NODE (work));
1007
1008                         list_end->next = work->next;
1009                         if (work->next)
1010                                 list = work->next->prev = list_end;
1011                         work->next = (GList *)node;
1012                         list = (GList *)node;
1013                         list->prev = work;
1014                 }
1015                 else {
1016                         if (parent) {
1017                                 GTK_CTREE_ROW (parent)->children = node;
1018                                 list = (GList *)node;
1019                                 list->prev = (GList *)parent;
1020                                 if (GTK_CTREE_ROW (parent)->expanded) {
1021                                         list_end->next = (GList *)GTK_CTREE_NODE_NEXT (parent);
1022                                         if (GTK_CTREE_NODE_NEXT(parent)) {
1023                                                 list = (GList *)GTK_CTREE_NODE_NEXT (parent);
1024                                                 list->prev = list_end;
1025                                         }
1026                                         list = (GList *)parent;
1027                                         list->next = (GList *)node;
1028                                 }
1029                                 else
1030                                         list_end->next = NULL;
1031                         }
1032                         else {
1033                                 clist->row_list = (GList *)node;
1034                                 list = (GList *)node;
1035                                 list->prev = NULL;
1036                                 list_end->next = NULL;
1037                         }
1038                 }
1039         }
1040
1041         gtk_ctree_pre_recursive (ctree, node, tree_update_level, NULL); 
1042
1043         if (clist->row_list_end == NULL ||
1044             clist->row_list_end->next == (GList *)node)
1045                 clist->row_list_end = list_end;
1046
1047         if (visible && update_focus_row) {
1048                 gint pos;
1049           
1050                 pos = g_list_position (clist->row_list, (GList *)node);
1051   
1052                 if (pos <= clist->focus_row) {
1053                         clist->focus_row += rows;
1054                         clist->undo_anchor = clist->focus_row;
1055                 }
1056         }
1057 }
1058
1059 static void
1060 tree_update_level (GtkCTree     *ctree, 
1061                    GtkCTreeNode *node, 
1062                    gpointer      data)
1063 {
1064         if (!node)
1065                 return;
1066
1067         if (GTK_CTREE_ROW (node)->parent)
1068                 GTK_CTREE_ROW (node)->level = 
1069                 GTK_CTREE_ROW (GTK_CTREE_ROW (node)->parent)->level + 1;
1070         else
1071                 GTK_CTREE_ROW (node)->level = 1;
1072 }
1073
1074 static GtkCTreeNode *
1075 gtk_ctree_last_visible (GtkCTree     *ctree,
1076                         GtkCTreeNode *node)
1077 {
1078         GtkCTreeNode *work;
1079   
1080         if (!node)
1081                 return NULL;
1082
1083         work = GTK_CTREE_ROW (node)->children;
1084
1085         if (!work || !GTK_CTREE_ROW (node)->expanded)
1086                 return node;
1087
1088         while (GTK_CTREE_ROW (work)->sibling)
1089                 work = GTK_CTREE_ROW (work)->sibling;
1090
1091         return gtk_ctree_last_visible (ctree, work);
1092 }
1093
1094 /* this wrapper simply replaces NULL pixmaps 
1095  * with a transparent, 1x1 pixmap. This works
1096  * around a memory problem deep inside gtk, 
1097  * revealed by valgrind. 
1098  */
1099 GtkCTreeNode* gtk_sctree_insert_node        (GtkCTree *ctree,
1100                                              GtkCTreeNode *parent,
1101                                              GtkCTreeNode *sibling,
1102                                              gchar *text[],
1103                                              guint8 spacing,
1104                                              GdkPixmap *pixmap_closed,
1105                                              GdkBitmap *mask_closed,
1106                                              GdkPixmap *pixmap_opened,
1107                                              GdkBitmap *mask_opened,
1108                                              gboolean is_leaf,
1109                                              gboolean expanded)
1110 {
1111         if (!emptyxpm) {
1112                 stock_pixmap_gdk(GTK_WIDGET(ctree), STOCK_PIXMAP_EMPTY,
1113                          &emptyxpm, &emptyxpmmask);
1114         }
1115         if (!pixmap_closed) {
1116                 pixmap_closed = emptyxpm;
1117                 mask_closed = emptyxpmmask;
1118         }
1119         if (!pixmap_opened) {
1120                 pixmap_opened = emptyxpm;
1121                 mask_opened = emptyxpmmask;
1122         }
1123         return gtk_ctree_insert_node(ctree, parent, sibling, text,spacing,
1124                 pixmap_closed, mask_closed, pixmap_opened, mask_opened,
1125                 is_leaf, expanded);
1126 }
1127
1128 /* this wrapper simply replaces NULL pixmaps 
1129  * with a transparent, 1x1 pixmap. This works
1130  * around a memory problem deep inside gtk, 
1131  * revealed by valgrind. 
1132  */
1133 void        gtk_sctree_set_node_info        (GtkCTree *ctree,
1134                                              GtkCTreeNode *node,
1135                                              const gchar *text,
1136                                              guint8 spacing,
1137                                              GdkPixmap *pixmap_closed,
1138                                              GdkBitmap *mask_closed,
1139                                              GdkPixmap *pixmap_opened,
1140                                              GdkBitmap *mask_opened,
1141                                              gboolean is_leaf,
1142                                              gboolean expanded)
1143 {
1144         if (!emptyxpm) {
1145                 stock_pixmap_gdk(GTK_WIDGET(ctree), STOCK_PIXMAP_EMPTY,
1146                          &emptyxpm, &emptyxpmmask);
1147         }
1148         if (!pixmap_closed) {
1149                 pixmap_closed = emptyxpm;
1150                 mask_closed = emptyxpmmask;
1151         }
1152         if (!pixmap_opened) {
1153                 pixmap_opened = emptyxpm;
1154                 mask_opened = emptyxpmmask;
1155         }
1156         gtk_ctree_set_node_info(ctree, node, text, spacing,
1157                 pixmap_closed, mask_closed, pixmap_opened, mask_opened,
1158                 is_leaf, expanded);
1159
1160 }