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