2005-12-05 [paul] 1.9.100cvs61
[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         GTK_CLIST_GET_CLASS(sctree)->refresh(GTK_CLIST(sctree));
247
248         if (!additive) {
249                 sctree->selecting_range = TRUE;
250                 gtk_clist_unselect_all (GTK_CLIST (sctree));
251                 sctree->selecting_range = FALSE;
252         }
253
254         if (!range) {
255                 GtkCTreeNode *node;
256
257                 node = gtk_ctree_node_nth (GTK_CTREE(sctree), row);
258
259                 /*No need to manage overlapped list*/
260                 if (additive) {
261                         if (row_is_selected(sctree, row))
262                                 gtk_clist_unselect_row (GTK_CLIST (sctree), row, col);
263                         else
264                                 g_signal_emit_by_name
265                                         (G_OBJECT (sctree),
266                                          "tree_select_row", node, col);
267                 } else {
268                         g_signal_emit_by_name
269                                 (G_OBJECT (sctree),
270                                  "tree_select_row", node, col);
271                 }
272                 sctree->anchor_row = node;
273         } else
274                 select_range (sctree, row);
275         
276         gtk_clist_thaw (GTK_CLIST (sctree));
277 }
278
279 /* Our handler for button_press events.  We override all of GtkCList's broken
280  * behavior.
281  */
282 static gint
283 gtk_sctree_button_press (GtkWidget *widget, GdkEventButton *event)
284 {
285         GtkSCTree *sctree;
286         GtkCList *clist;
287         gboolean on_row;
288         gint row;
289         gint col;
290         gint retval;
291
292         g_return_val_if_fail (widget != NULL, FALSE);
293         g_return_val_if_fail (GTK_IS_SCTREE (widget), FALSE);
294         g_return_val_if_fail (event != NULL, FALSE);
295
296         sctree = GTK_SCTREE (widget);
297         clist = GTK_CLIST (widget);
298         retval = FALSE;
299
300         if (event->window != clist->clist_window)
301                 return (* GTK_WIDGET_CLASS (parent_class)->button_press_event) (widget, event);
302
303         on_row = gtk_clist_get_selection_info (clist, event->x, event->y, &row, &col);
304
305         if (on_row && !GTK_WIDGET_HAS_FOCUS(widget))
306                 gtk_widget_grab_focus (widget);
307
308         if (gtk_ctree_is_hot_spot (GTK_CTREE(sctree), event->x, event->y)) {
309                 gtk_ctree_toggle_expansion
310                         (GTK_CTREE(sctree), 
311                          gtk_ctree_node_nth(GTK_CTREE(sctree), row));
312                 return TRUE;
313         }
314
315         switch (event->type) {
316         case GDK_BUTTON_PRESS:
317                 if (event->button == 1 || event->button == 2) {
318                         if (event->button == 2)
319                                 event->state &= ~(GDK_SHIFT_MASK | GDK_CONTROL_MASK);
320                         if (on_row) {
321                                 /* Save the mouse info for DnD */
322                                 sctree->dnd_press_button = event->button;
323                                 sctree->dnd_press_x = event->x;
324                                 sctree->dnd_press_y = event->y;
325
326                                 /* Handle selection */
327                                 if ((row_is_selected (sctree, row)
328                                      && !(event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK)))
329                                     || ((event->state & GDK_CONTROL_MASK)
330                                         && !(event->state & GDK_SHIFT_MASK))) {
331                                         sctree->dnd_select_pending = TRUE;
332                                         sctree->dnd_select_pending_state = event->state;
333                                         sctree->dnd_select_pending_row = row;
334                                 } else
335                                         select_row (sctree, row, col, event->state);
336                         } else {
337                                 sctree->selecting_range = TRUE;
338                                 gtk_clist_unselect_all (clist);
339                                 sctree->selecting_range = FALSE;
340                         }
341
342                         retval = TRUE;
343                 } else if (event->button == 3) {
344                         /* Emit *_popup_menu signal*/
345                         if (on_row) {
346                                 if (!row_is_selected(sctree,row))
347                                         select_row (sctree, row, col, 0);
348                                 g_signal_emit (G_OBJECT (sctree),
349                                                  sctree_signals[ROW_POPUP_MENU],
350                                                  0, event);
351                         } else {
352                                 sctree->selecting_range = TRUE;
353                                 gtk_clist_unselect_all(clist);
354                                 sctree->selecting_range = FALSE;
355                                 g_signal_emit (G_OBJECT (sctree),
356                                                  sctree_signals[EMPTY_POPUP_MENU],
357                                                  0, event);
358                         }
359                         retval = TRUE;
360                 }
361
362                 break;
363
364         case GDK_2BUTTON_PRESS:
365                 if (event->button != 1)
366                         break;
367
368                 sctree->dnd_select_pending = FALSE;
369                 sctree->dnd_select_pending_state = 0;
370
371                 if (on_row)
372                         g_signal_emit (G_OBJECT (sctree),
373                                        sctree_signals[OPEN_ROW], 0);
374
375                 retval = TRUE;
376                 break;
377
378         default:
379                 break;
380         }
381
382         return retval;
383 }
384
385 /* Our handler for button_release events.  We override all of GtkCList's broken
386  * behavior.
387  */
388 static gint
389 gtk_sctree_button_release (GtkWidget *widget, GdkEventButton *event)
390 {
391         GtkSCTree *sctree;
392         GtkCList *clist;
393         gint on_row;
394         gint row, col;
395         gint retval;
396
397         g_return_val_if_fail (widget != NULL, FALSE);
398         g_return_val_if_fail (GTK_IS_SCTREE (widget), FALSE);
399         g_return_val_if_fail (event != NULL, FALSE);
400
401         sctree = GTK_SCTREE (widget);
402         clist = GTK_CLIST (widget);
403         retval = FALSE;
404
405         if (event->window != clist->clist_window)
406                 return (* GTK_WIDGET_CLASS (parent_class)->button_release_event) (widget, event);
407
408         on_row = gtk_clist_get_selection_info (clist, event->x, event->y, &row, &col);
409
410         if (!(event->button == 1 || event->button == 2))
411                 return FALSE;
412
413         sctree->dnd_press_button = 0;
414         sctree->dnd_press_x = 0;
415         sctree->dnd_press_y = 0;
416
417         if (on_row) {
418                 if (sctree->dnd_select_pending) {
419                         select_row (sctree, row, col, sctree->dnd_select_pending_state);
420                         sctree->dnd_select_pending = FALSE;
421                         sctree->dnd_select_pending_state = 0;
422                 }
423
424                 retval = TRUE;
425         }
426
427         return retval;
428 }
429
430 /* Our handler for motion_notify events.  We override all of GtkCList's broken
431  * behavior.
432  */
433 static gint
434 gtk_sctree_motion (GtkWidget *widget, GdkEventMotion *event)
435 {
436         GtkSCTree *sctree;
437         GtkCList *clist;
438
439         g_return_val_if_fail (widget != NULL, FALSE);
440         g_return_val_if_fail (GTK_IS_SCTREE (widget), FALSE);
441         g_return_val_if_fail (event != NULL, FALSE);
442
443         sctree = GTK_SCTREE (widget);
444         clist = GTK_CLIST (widget);
445
446         if (event->window != clist->clist_window)
447                 return (* GTK_WIDGET_CLASS (parent_class)->motion_notify_event) (widget, event);
448
449         if (!((sctree->dnd_press_button == 1 && (event->state & GDK_BUTTON1_MASK))
450               || (sctree->dnd_press_button == 2 && (event->state & GDK_BUTTON2_MASK))))
451                 return FALSE;
452
453         /* This is the same threshold value that is used in gtkdnd.c */
454
455         if (MAX (ABS (sctree->dnd_press_x - event->x),
456                  ABS (sctree->dnd_press_y - event->y)) <= 3)
457                 return FALSE;
458
459         /* Handle any pending selections */
460
461         if (sctree->dnd_select_pending) {
462                 if (!row_is_selected(sctree,sctree->dnd_select_pending_row))
463                         select_row (sctree,
464                                     sctree->dnd_select_pending_row,
465                                     -1,
466                                     sctree->dnd_select_pending_state);
467
468                 sctree->dnd_select_pending = FALSE;
469                 sctree->dnd_select_pending_state = 0;
470         }
471
472         g_signal_emit (G_OBJECT (sctree),
473                        sctree_signals[START_DRAG],
474                        0,
475                        sctree->dnd_press_button,
476                        event);
477         return TRUE;
478 }
479
480 /* We override the drag_begin signal to do nothing */
481 static void
482 gtk_sctree_drag_begin (GtkWidget *widget, GdkDragContext *context)
483 {
484         /* nothing */
485 }
486
487 /* We override the drag_end signal to do nothing */
488 static void
489 gtk_sctree_drag_end (GtkWidget *widget, GdkDragContext *context)
490 {
491         /* nothing */
492 }
493
494 /* We override the drag_data_get signal to do nothing */
495 static void
496 gtk_sctree_drag_data_get (GtkWidget *widget, GdkDragContext *context,
497                                      GtkSelectionData *data, guint info, guint time)
498 {
499         /* nothing */
500 }
501
502 /* We override the drag_leave signal to do nothing */
503 static void
504 gtk_sctree_drag_leave (GtkWidget *widget, GdkDragContext *context, guint time)
505 {
506         /* nothing */
507 }
508
509 /* We override the drag_motion signal to do nothing */
510 static gboolean
511 gtk_sctree_drag_motion (GtkWidget *widget, GdkDragContext *context,
512                                    gint x, gint y, guint time)
513 {
514         return FALSE;
515 }
516
517 /* We override the drag_drop signal to do nothing */
518 static gboolean
519 gtk_sctree_drag_drop (GtkWidget *widget, GdkDragContext *context,
520                                  gint x, gint y, guint time)
521 {
522         return FALSE;
523 }
524
525 /* We override the drag_data_received signal to do nothing */
526 static void
527 gtk_sctree_drag_data_received (GtkWidget *widget, GdkDragContext *context,
528                                           gint x, gint y, GtkSelectionData *data,
529                                           guint info, guint time)
530 {
531         /* nothing */
532 }
533
534 /* Our handler for the clear signal of the clist.  We have to reset the anchor
535  * to null.
536  */
537 static void
538 gtk_sctree_clear (GtkCList *clist)
539 {
540         GtkSCTree *sctree;
541
542         g_return_if_fail (clist != NULL);
543         g_return_if_fail (GTK_IS_SCTREE (clist));
544
545         sctree = GTK_SCTREE (clist);
546         sctree->anchor_row = NULL;
547
548         if (((GtkCListClass *)parent_class)->clear)
549                 (* ((GtkCListClass *)parent_class)->clear) (clist);
550 }
551
552 /* Our handler for the change_focus_row_expansion signal of the ctree.  
553  We have to set the anchor to parent visible node.
554  */
555 static void 
556 gtk_sctree_collapse (GtkCTree *ctree, GtkCTreeNode *node)
557 {
558         g_return_if_fail (ctree != NULL);
559         g_return_if_fail (GTK_IS_SCTREE (ctree));
560
561         (* parent_class->tree_collapse) (ctree, node);
562         GTK_SCTREE(ctree)->anchor_row =
563                 gtk_ctree_node_nth(ctree, GTK_CLIST(ctree)->focus_row);
564 }
565
566 GtkWidget *gtk_sctree_new_with_titles (gint columns, gint tree_column, 
567                                        gchar *titles[])
568 {
569 #if 0
570         GtkSCTree* sctree;
571
572         sctree = gtk_type_new (gtk_sctree_get_type ());
573         gtk_ctree_construct (GTK_CTREE (sctree), columns, tree_column, titles);
574         
575         gtk_clist_set_selection_mode(GTK_CLIST(sctree), GTK_SELECTION_EXTENDED);
576
577         return GTK_WIDGET (sctree);
578 #else
579         GtkWidget *widget;
580                                                                                                             
581         g_return_val_if_fail (columns > 0, NULL);
582         g_return_val_if_fail (tree_column >= 0, NULL);
583                                                                                                             
584         widget = gtk_widget_new (TYPE_GTK_SCTREE,
585                                  "n_columns", columns,
586                                  "tree_column", tree_column,
587                                  NULL);
588         if (titles) {
589                 GtkCList *clist = GTK_CLIST (widget);
590                 guint i;
591
592                 for (i = 0; i < columns; i++)
593                         gtk_clist_set_column_title (clist, i, titles[i]);
594                 gtk_clist_column_titles_show (clist);
595         }
596
597         return widget;
598 #endif
599 }
600
601 void gtk_sctree_select (GtkSCTree *sctree, GtkCTreeNode *node)
602 {
603         select_row(sctree, 
604                    g_list_position(GTK_CLIST(sctree)->row_list, (GList *)node),
605                    -1, 0);
606 }
607
608 void gtk_sctree_select_with_state (GtkSCTree *sctree, GtkCTreeNode *node, int state)
609 {
610         select_row(sctree, 
611                    g_list_position(GTK_CLIST(sctree)->row_list, (GList *)node),
612                    -1, state);
613 }
614
615 void gtk_sctree_unselect_all (GtkSCTree *sctree)
616 {
617         sctree->selecting_range = TRUE;
618         gtk_clist_unselect_all(GTK_CLIST(sctree));
619         sctree->selecting_range = FALSE;
620         sctree->anchor_row = NULL;
621 }
622
623 void gtk_sctree_set_anchor_row (GtkSCTree *sctree, GtkCTreeNode *node)
624 {
625         sctree->anchor_row = node;
626 }
627
628 /***********************************************************
629  *             Tree sorting functions                      *
630  ***********************************************************/
631
632 static void sink(GtkCList *clist, GPtrArray *numbers, gint root, gint bottom)
633 {
634         gint j, k ;
635         GtkCTreeNode *temp;
636
637         j = 2 * root;
638         k = j + 1;
639
640         /* find the maximum element of numbers[root],
641            numbers[2*root] and numbers[2*root+1] */
642         if (j <= bottom) {
643                 if (clist->compare( clist, GTK_CTREE_ROW (g_ptr_array_index(numbers, root)),
644                                     GTK_CTREE_ROW(g_ptr_array_index( numbers, j))) >= 0)
645                         j = root;
646                 if (k <= bottom)
647                         if (clist->compare( clist, GTK_CTREE_ROW (g_ptr_array_index(numbers, k)),
648                                             GTK_CTREE_ROW (g_ptr_array_index( numbers, j))) > 0)
649                                 j = k;
650                 /* if numbers[root] wasn't the maximum element then
651                    sink again */
652                 if (root != j) {
653                         temp = g_ptr_array_index( numbers,root);
654                         g_ptr_array_index( numbers, root) = g_ptr_array_index( numbers, j);
655                         g_ptr_array_index( numbers, j) = temp;
656                         sink( clist, numbers, j, bottom);
657                 }
658         }
659 }
660
661 static void heap_sort(GtkCList *clist, GPtrArray *numbers, gint array_size)
662 {
663         gint i;
664         GtkCTreeNode *temp;
665         
666         /* build the Heap */
667         for (i = (array_size / 2); i >= 1; i--)
668                 sink( clist, numbers, i, array_size);
669         /* output the Heap */
670         for (i = array_size; i >= 2; i--) {
671                 temp = g_ptr_array_index( numbers, 1);
672                 g_ptr_array_index( numbers, 1) = g_ptr_array_index( numbers, i);
673                 g_ptr_array_index( numbers, i) = temp;
674                 sink( clist, numbers, 1, i-1);
675         }
676 }
677
678 static void
679 tree_sort (GtkCTree    *ctree,
680            GtkCTreeNode *node,
681            gpointer      data)
682 {
683         GtkCTreeNode *list_start, *work, *next;
684         GPtrArray *row_array, *viewable_array;
685         GtkCList *clist;
686         gint i;
687
688
689         clist = GTK_CLIST (ctree);
690
691         if (node)
692                 work = GTK_CTREE_ROW (node)->children;
693         else
694                 work = GTK_CTREE_NODE (clist->row_list);
695
696         row_array = g_ptr_array_new();
697         viewable_array = g_ptr_array_new();
698
699         if (work) {
700                 g_ptr_array_add( row_array, NULL);
701                 while (work) {
702                         /* add all rows to row_array */
703                         g_ptr_array_add( row_array, work);
704                         if (GTK_CTREE_ROW (work)->parent && gtk_ctree_is_viewable( ctree, work))
705                                 g_ptr_array_add( viewable_array, GTK_CTREE_ROW (work)->parent);
706                         next = GTK_CTREE_ROW (work)->sibling;
707                         gtk_ctree_unlink( ctree, work, FALSE);
708                         work = next;
709                 }
710
711                 heap_sort( clist, row_array, (row_array->len)-1);
712
713                 if (node)
714                         list_start = GTK_CTREE_ROW (node)->children;
715                 else
716                         list_start = GTK_CTREE_NODE (clist->row_list);
717
718                 if (clist->sort_type == GTK_SORT_ASCENDING) {
719                         for (i=(row_array->len)-1; i>=1; i--) {
720                                 work = g_ptr_array_index( row_array, i);
721                                 gtk_ctree_link( ctree, work, node, list_start, FALSE);
722                                 list_start = work;
723                                 /* insert work at the beginning of the list */
724                         }
725                 } else {
726                         for (i=1; i<row_array->len; i++) {
727                                 work = g_ptr_array_index( row_array, i);
728                                 gtk_ctree_link( ctree, work, node, list_start, FALSE);
729                                 list_start = work;
730                                 /* insert work at the beginning of the list */
731                         }
732                 }
733
734                 for (i=0; i<viewable_array->len; i++) {
735                         gtk_ctree_expand( ctree, g_ptr_array_index( viewable_array, i));
736                 }
737                 
738         }
739
740         g_ptr_array_free( row_array, TRUE);
741         g_ptr_array_free( viewable_array, TRUE);
742 }
743
744 void
745 gtk_sctree_sort_recursive (GtkCTree     *ctree, 
746                           GtkCTreeNode *node)
747 {
748         GtkCList *clist;
749         GtkCTreeNode *focus_node = NULL;
750
751         g_return_if_fail (ctree != NULL);
752         g_return_if_fail (GTK_IS_CTREE (ctree));
753
754         clist = GTK_CLIST (ctree);
755
756         gtk_clist_freeze (clist);
757
758         if (clist->selection_mode == GTK_SELECTION_EXTENDED) {
759                 GTK_CLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
760       
761                 g_list_free (clist->undo_selection);
762                 g_list_free (clist->undo_unselection);
763                 clist->undo_selection = NULL;
764                 clist->undo_unselection = NULL;
765         }
766
767         if (!node || (node && gtk_ctree_is_viewable (ctree, node)))
768                 focus_node = GTK_CTREE_NODE (g_list_nth (clist->row_list, clist->focus_row));
769       
770         gtk_ctree_post_recursive (ctree, node, GTK_CTREE_FUNC (tree_sort), NULL);
771
772         if (!node)
773                 tree_sort (ctree, NULL, NULL);
774
775         if (focus_node) {
776                 clist->focus_row = g_list_position (clist->row_list,(GList *)focus_node);
777                 clist->undo_anchor = clist->focus_row;
778         }
779
780         gtk_clist_thaw (clist);
781 }
782
783 void
784 gtk_sctree_sort_node (GtkCTree     *ctree, 
785                      GtkCTreeNode *node)
786 {
787         GtkCList *clist;
788         GtkCTreeNode *focus_node = NULL;
789
790         g_return_if_fail (ctree != NULL);
791         g_return_if_fail (GTK_IS_CTREE (ctree));
792
793         clist = GTK_CLIST (ctree);
794
795         gtk_clist_freeze (clist);
796
797         if (clist->selection_mode == GTK_SELECTION_EXTENDED) {
798                 GTK_CLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
799
800                 g_list_free (clist->undo_selection);
801                 g_list_free (clist->undo_unselection);
802                 clist->undo_selection = NULL;
803                 clist->undo_unselection = NULL;
804         }
805
806         if (!node || (node && gtk_ctree_is_viewable (ctree, node)))
807                 focus_node = GTK_CTREE_NODE (g_list_nth (clist->row_list, clist->focus_row));
808
809         tree_sort (ctree, node, NULL);
810
811         if (focus_node) {
812                 clist->focus_row = g_list_position (clist->row_list,(GList *)focus_node);
813                 clist->undo_anchor = clist->focus_row;
814         }
815
816         gtk_clist_thaw (clist);
817 }
818
819 /************************************************************************/
820
821 static void
822 gtk_ctree_unlink (GtkCTree     *ctree, 
823                   GtkCTreeNode *node,
824                   gboolean      update_focus_row)
825 {
826         GtkCList *clist;
827         gint rows;
828         gint level;
829         gint visible;
830         GtkCTreeNode *work;
831         GtkCTreeNode *parent;
832         GList *list;
833
834         g_return_if_fail (ctree != NULL);
835         g_return_if_fail (GTK_IS_CTREE (ctree));
836         g_return_if_fail (node != NULL);
837
838         clist = GTK_CLIST (ctree);
839   
840         if (update_focus_row && clist->selection_mode == GTK_SELECTION_EXTENDED) {
841                 GTK_CLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
842
843                 g_list_free (clist->undo_selection);
844                 g_list_free (clist->undo_unselection);
845                 clist->undo_selection = NULL;
846                 clist->undo_unselection = NULL;
847         }
848
849         visible = gtk_ctree_is_viewable (ctree, node);
850
851         /* clist->row_list_end unlinked ? */
852         if (visible && (GTK_CTREE_NODE_NEXT (node) == NULL ||
853            (GTK_CTREE_ROW (node)->children && gtk_ctree_is_ancestor (ctree, node,
854             GTK_CTREE_NODE (clist->row_list_end)))))
855                 clist->row_list_end = (GList *) (GTK_CTREE_NODE_PREV (node));
856
857         /* update list */
858         rows = 0;
859         level = GTK_CTREE_ROW (node)->level;
860         work = GTK_CTREE_NODE_NEXT (node);
861         while (work && GTK_CTREE_ROW (work)->level > level) {
862                 work = GTK_CTREE_NODE_NEXT (work);
863                 rows++;
864         }
865
866         if (visible) {
867                 clist->rows -= (rows + 1);
868
869                 if (update_focus_row) {
870                         gint pos;
871
872                         pos = g_list_position (clist->row_list, (GList *)node);
873                         if (pos + rows < clist->focus_row)
874                                 clist->focus_row -= (rows + 1);
875                         else if (pos <= clist->focus_row) {
876                                 if (!GTK_CTREE_ROW (node)->sibling)
877                                         clist->focus_row = MAX (pos - 1, 0);
878                                 else
879                                         clist->focus_row = pos;
880               
881                                 clist->focus_row = MIN (clist->focus_row, clist->rows - 1);
882                         }
883                         clist->undo_anchor = clist->focus_row;
884                 }
885         }
886
887         if (work) {
888                 list = (GList *)GTK_CTREE_NODE_PREV (work);
889                 list->next = NULL;
890                 list = (GList *)work;
891                 list->prev = (GList *)GTK_CTREE_NODE_PREV (node);
892         }
893
894         if (GTK_CTREE_NODE_PREV (node) &&
895             GTK_CTREE_NODE_NEXT (GTK_CTREE_NODE_PREV (node)) == node) {
896                 list = (GList *)GTK_CTREE_NODE_PREV (node);
897                 list->next = (GList *)work;
898         }
899
900         /* update tree */
901         parent = GTK_CTREE_ROW (node)->parent;
902         if (parent) {
903                 if (GTK_CTREE_ROW (parent)->children == node) {
904                         GTK_CTREE_ROW (parent)->children = GTK_CTREE_ROW (node)->sibling;
905                         if (!GTK_CTREE_ROW (parent)->children)
906                                 gtk_ctree_collapse (ctree, parent);
907                 }
908                 else {
909                         GtkCTreeNode *sibling;
910
911                         sibling = GTK_CTREE_ROW (parent)->children;
912                         while (GTK_CTREE_ROW (sibling)->sibling != node)
913                                 sibling = GTK_CTREE_ROW (sibling)->sibling;
914                         GTK_CTREE_ROW (sibling)->sibling = GTK_CTREE_ROW (node)->sibling;
915                 }
916         }
917         else {
918                 if (clist->row_list == (GList *)node)
919                         clist->row_list = (GList *) (GTK_CTREE_ROW (node)->sibling);
920                 else {
921                         GtkCTreeNode *sibling;
922
923                         sibling = GTK_CTREE_NODE (clist->row_list);
924                         while (GTK_CTREE_ROW (sibling)->sibling != node)
925                                 sibling = GTK_CTREE_ROW (sibling)->sibling;
926                         GTK_CTREE_ROW (sibling)->sibling = GTK_CTREE_ROW (node)->sibling;
927                 }
928         }
929 }
930
931 static void
932 gtk_ctree_link (GtkCTree     *ctree,
933                 GtkCTreeNode *node,
934                 GtkCTreeNode *parent,
935                 GtkCTreeNode *sibling,
936                 gboolean      update_focus_row)
937 {
938         GtkCList *clist;
939         GList *list_end;
940         GList *list;
941         GList *work;
942         gboolean visible = FALSE;
943         gint rows = 0;
944   
945         if (sibling)
946                 g_return_if_fail (GTK_CTREE_ROW (sibling)->parent == parent);
947         g_return_if_fail (node != NULL);
948         g_return_if_fail (node != sibling);
949         g_return_if_fail (node != parent);
950
951         clist = GTK_CLIST (ctree);
952
953         if (update_focus_row && clist->selection_mode == GTK_SELECTION_EXTENDED) {
954                 GTK_CLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
955
956                 g_list_free (clist->undo_selection);
957                 g_list_free (clist->undo_unselection);
958                 clist->undo_selection = NULL;
959                 clist->undo_unselection = NULL;
960         }
961
962         for (rows = 1, list_end = (GList *)node; list_end->next;
963              list_end = list_end->next)
964                 rows++;
965
966         GTK_CTREE_ROW (node)->parent = parent;
967         GTK_CTREE_ROW (node)->sibling = sibling;
968
969         if (!parent || (parent && (gtk_ctree_is_viewable (ctree, parent) &&
970             GTK_CTREE_ROW (parent)->expanded))) {
971                 visible = TRUE;
972                 clist->rows += rows;
973         }
974
975         if (parent)
976                 work = (GList *)(GTK_CTREE_ROW (parent)->children);
977         else
978                 work = clist->row_list;
979
980         if (sibling) {
981                 if (work != (GList *)sibling) {
982                         while (GTK_CTREE_ROW (work)->sibling != sibling)
983                                 work = (GList *)(GTK_CTREE_ROW (work)->sibling);
984                         GTK_CTREE_ROW (work)->sibling = node;
985                 }
986
987                 if (sibling == GTK_CTREE_NODE (clist->row_list))
988                 clist->row_list = (GList *) node;
989                 if (GTK_CTREE_NODE_PREV (sibling) &&
990                     GTK_CTREE_NODE_NEXT (GTK_CTREE_NODE_PREV (sibling)) == sibling) {
991                         list = (GList *)GTK_CTREE_NODE_PREV (sibling);
992                         list->next = (GList *)node;
993                 }
994
995                 list = (GList *)node;
996                 list->prev = (GList *)GTK_CTREE_NODE_PREV (sibling);
997                 list_end->next = (GList *)sibling;
998                 list = (GList *)sibling;
999                 list->prev = list_end;
1000                 if (parent && GTK_CTREE_ROW (parent)->children == sibling)
1001                         GTK_CTREE_ROW (parent)->children = node;
1002         }
1003         else {
1004                 if (work) {
1005                         /* find sibling */
1006                         while (GTK_CTREE_ROW (work)->sibling)
1007                         work = (GList *)(GTK_CTREE_ROW (work)->sibling);
1008                         GTK_CTREE_ROW (work)->sibling = node;
1009
1010                         /* find last visible child of sibling */
1011                         work = (GList *) gtk_ctree_last_visible (ctree,
1012                                GTK_CTREE_NODE (work));
1013
1014                         list_end->next = work->next;
1015                         if (work->next)
1016                                 list = work->next->prev = list_end;
1017                         work->next = (GList *)node;
1018                         list = (GList *)node;
1019                         list->prev = work;
1020                 }
1021                 else {
1022                         if (parent) {
1023                                 GTK_CTREE_ROW (parent)->children = node;
1024                                 list = (GList *)node;
1025                                 list->prev = (GList *)parent;
1026                                 if (GTK_CTREE_ROW (parent)->expanded) {
1027                                         list_end->next = (GList *)GTK_CTREE_NODE_NEXT (parent);
1028                                         if (GTK_CTREE_NODE_NEXT(parent)) {
1029                                                 list = (GList *)GTK_CTREE_NODE_NEXT (parent);
1030                                                 list->prev = list_end;
1031                                         }
1032                                         list = (GList *)parent;
1033                                         list->next = (GList *)node;
1034                                 }
1035                                 else
1036                                         list_end->next = NULL;
1037                         }
1038                         else {
1039                                 clist->row_list = (GList *)node;
1040                                 list = (GList *)node;
1041                                 list->prev = NULL;
1042                                 list_end->next = NULL;
1043                         }
1044                 }
1045         }
1046
1047         gtk_ctree_pre_recursive (ctree, node, tree_update_level, NULL); 
1048
1049         if (clist->row_list_end == NULL ||
1050             clist->row_list_end->next == (GList *)node)
1051                 clist->row_list_end = list_end;
1052
1053         if (visible && update_focus_row) {
1054                 gint pos;
1055           
1056                 pos = g_list_position (clist->row_list, (GList *)node);
1057   
1058                 if (pos <= clist->focus_row) {
1059                         clist->focus_row += rows;
1060                         clist->undo_anchor = clist->focus_row;
1061                 }
1062         }
1063 }
1064
1065 static void
1066 tree_update_level (GtkCTree     *ctree, 
1067                    GtkCTreeNode *node, 
1068                    gpointer      data)
1069 {
1070         if (!node)
1071                 return;
1072
1073         if (GTK_CTREE_ROW (node)->parent)
1074                 GTK_CTREE_ROW (node)->level = 
1075                 GTK_CTREE_ROW (GTK_CTREE_ROW (node)->parent)->level + 1;
1076         else
1077                 GTK_CTREE_ROW (node)->level = 1;
1078 }
1079
1080 static GtkCTreeNode *
1081 gtk_ctree_last_visible (GtkCTree     *ctree,
1082                         GtkCTreeNode *node)
1083 {
1084         GtkCTreeNode *work;
1085   
1086         if (!node)
1087                 return NULL;
1088
1089         work = GTK_CTREE_ROW (node)->children;
1090
1091         if (!work || !GTK_CTREE_ROW (node)->expanded)
1092                 return node;
1093
1094         while (GTK_CTREE_ROW (work)->sibling)
1095                 work = GTK_CTREE_ROW (work)->sibling;
1096
1097         return gtk_ctree_last_visible (ctree, work);
1098 }
1099
1100 /* this wrapper simply replaces NULL pixmaps 
1101  * with a transparent, 1x1 pixmap. This works
1102  * around a memory problem deep inside gtk, 
1103  * revealed by valgrind. 
1104  */
1105 GtkCTreeNode* gtk_sctree_insert_node        (GtkCTree *ctree,
1106                                              GtkCTreeNode *parent,
1107                                              GtkCTreeNode *sibling,
1108                                              gchar *text[],
1109                                              guint8 spacing,
1110                                              GdkPixmap *pixmap_closed,
1111                                              GdkBitmap *mask_closed,
1112                                              GdkPixmap *pixmap_opened,
1113                                              GdkBitmap *mask_opened,
1114                                              gboolean is_leaf,
1115                                              gboolean expanded)
1116 {
1117         if (!emptyxpm) {
1118                 stock_pixmap_gdk((GtkWidget*)ctree, STOCK_PIXMAP_EMPTY,
1119                          &emptyxpm, &emptyxpmmask);
1120         }
1121         if (!pixmap_closed) {
1122                 pixmap_closed = emptyxpm;
1123                 mask_closed = emptyxpmmask;
1124         }
1125         if (!pixmap_opened) {
1126                 pixmap_opened = emptyxpm;
1127                 mask_opened = emptyxpmmask;
1128         }
1129         return gtk_ctree_insert_node(ctree, parent, sibling, text,spacing,
1130                 pixmap_closed, mask_closed, pixmap_opened, mask_opened,
1131                 is_leaf, expanded);
1132 }
1133
1134 /* this wrapper simply replaces NULL pixmaps 
1135  * with a transparent, 1x1 pixmap. This works
1136  * around a memory problem deep inside gtk, 
1137  * revealed by valgrind. 
1138  */
1139 void        gtk_sctree_set_node_info        (GtkCTree *ctree,
1140                                              GtkCTreeNode *node,
1141                                              const gchar *text,
1142                                              guint8 spacing,
1143                                              GdkPixmap *pixmap_closed,
1144                                              GdkBitmap *mask_closed,
1145                                              GdkPixmap *pixmap_opened,
1146                                              GdkBitmap *mask_opened,
1147                                              gboolean is_leaf,
1148                                              gboolean expanded)
1149 {
1150         if (!emptyxpm) {
1151                 stock_pixmap_gdk((GtkWidget*)ctree, STOCK_PIXMAP_EMPTY,
1152                          &emptyxpm, &emptyxpmmask);
1153         }
1154         if (!pixmap_closed) {
1155                 pixmap_closed = emptyxpm;
1156                 mask_closed = emptyxpmmask;
1157         }
1158         if (!pixmap_opened) {
1159                 pixmap_opened = emptyxpm;
1160                 mask_opened = emptyxpmmask;
1161         }
1162         gtk_ctree_set_node_info(ctree, node, text, spacing,
1163                 pixmap_closed, mask_closed, pixmap_opened, mask_opened,
1164                 is_leaf, expanded);
1165
1166 }