2010-03-24 [pawel] 3.7.5cvs38
[claws.git] / src / gtk / gtkcmctree.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball, Josh MacDonald, 
3  * Copyright (C) 1997-1998 Jay Painter <jpaint@serv.net><jpaint@gimp.org>  
4  *
5  * GtkCMCTree widget for GTK+
6  * Copyright (C) 1998 Lars Hamann and Stefan Jeske
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the
20  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21  * Boston, MA 02111-1307, USA.
22  */
23
24 /*
25  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
26  * file for a list of people on the GTK+ Team.  See the ChangeLog
27  * files for a list of changes.  These files are distributed with
28  * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
29  */
30
31 #include <config.h>
32 #include <stdlib.h>
33 #include <gtk/gtk.h>
34 #include <gdk/gdkkeysyms.h>
35 #include "gtkcmctree.h"
36 #include "claws-marshal.h"
37 #include "utils.h"
38 #include "gtkutils.c"
39
40 #define PM_SIZE                    8
41 #define TAB_SIZE                   (PM_SIZE + 6)
42 #define CELL_SPACING               1
43 #define CLIST_OPTIMUM_SIZE         64
44 #define COLUMN_INSET               3
45 #define DRAG_WIDTH                 6
46
47 #define ROW_TOP_YPIXEL(clist, row) (((clist)->row_height * (row)) + \
48                                     (((row) + 1) * CELL_SPACING) + \
49                                     (clist)->voffset)
50 #define ROW_FROM_YPIXEL(clist, y)  (((y) - (clist)->voffset) / \
51                                     ((clist)->row_height + CELL_SPACING))
52 #define COLUMN_LEFT_XPIXEL(clist, col)  ((clist)->column[(col)].area.x \
53                                     + (clist)->hoffset)
54 #define COLUMN_LEFT(clist, column) ((clist)->column[(column)].area.x)
55
56 GType
57 gtk_cmctree_pos_get_type (void)
58 {
59   static GType etype = 0;
60   if (etype == 0) {
61     static const GEnumValue values[] = {
62       { GTK_CMCTREE_POS_BEFORE, "GTK_CMCTREE_POS_BEFORE", "before" },
63       { GTK_CMCTREE_POS_AS_CHILD, "GTK_CMCTREE_POS_AS_CHILD", "as-child" },
64       { GTK_CMCTREE_POS_AFTER, "GTK_CMCTREE_POS_AFTER", "after" },
65       { 0, NULL, NULL }
66     };
67 #if GLIB_CHECK_VERSION(2,10,0)
68     etype = g_enum_register_static (g_intern_static_string ("GtkCMCTreePos"), values);
69 #else
70     etype = g_enum_register_static ("GtkCMCTreePos", values);
71 #endif
72   }
73   return etype;
74 }
75 GType
76 gtk_cmctree_line_style_get_type (void)
77 {
78   static GType etype = 0;
79   if (etype == 0) {
80     static const GEnumValue values[] = {
81       { GTK_CMCTREE_LINES_NONE, "GTK_CMCTREE_LINES_NONE", "none" },
82       { GTK_CMCTREE_LINES_SOLID, "GTK_CMCTREE_LINES_SOLID", "solid" },
83       { GTK_CMCTREE_LINES_DOTTED, "GTK_CMCTREE_LINES_DOTTED", "dotted" },
84       { GTK_CMCTREE_LINES_TABBED, "GTK_CMCTREE_LINES_TABBED", "tabbed" },
85       { 0, NULL, NULL }
86     };
87 #if GLIB_CHECK_VERSION(2,10,0)
88     etype = g_enum_register_static (g_intern_static_string ("GtkCMCTreeLineStyle"), values);
89 #else
90     etype = g_enum_register_static ("GtkCMCTreeLineStyle", values);
91 #endif
92   }
93   return etype;
94 }
95 GType
96 gtk_cmctree_expander_style_get_type (void)
97 {
98   static GType etype = 0;
99   if (etype == 0) {
100     static const GEnumValue values[] = {
101       { GTK_CMCTREE_EXPANDER_NONE, "GTK_CMCTREE_EXPANDER_NONE", "none" },
102       { GTK_CMCTREE_EXPANDER_SQUARE, "GTK_CMCTREE_EXPANDER_SQUARE", "square" },
103       { GTK_CMCTREE_EXPANDER_TRIANGLE, "GTK_CMCTREE_EXPANDER_TRIANGLE", "triangle" },
104       { GTK_CMCTREE_EXPANDER_CIRCULAR, "GTK_CMCTREE_EXPANDER_CIRCULAR", "circular" },
105       { 0, NULL, NULL }
106     };
107 #if GLIB_CHECK_VERSION(2,10,0)
108     etype = g_enum_register_static (g_intern_static_string ("GtkCMCTreeExpanderStyle"), values);
109 #else
110     etype = g_enum_register_static ("GtkCMCTreeExpanderStyle", values);
111 #endif
112   }
113   return etype;
114 }
115 GType
116 gtk_cmctree_expansion_type_get_type (void)
117 {
118   static GType etype = 0;
119   if (etype == 0) {
120     static const GEnumValue values[] = {
121       { GTK_CMCTREE_EXPANSION_EXPAND, "GTK_CMCTREE_EXPANSION_EXPAND", "expand" },
122       { GTK_CMCTREE_EXPANSION_EXPAND_RECURSIVE, "GTK_CMCTREE_EXPANSION_EXPAND_RECURSIVE", "expand-recursive" },
123       { GTK_CMCTREE_EXPANSION_COLLAPSE, "GTK_CMCTREE_EXPANSION_COLLAPSE", "collapse" },
124       { GTK_CMCTREE_EXPANSION_COLLAPSE_RECURSIVE, "GTK_CMCTREE_EXPANSION_COLLAPSE_RECURSIVE", "collapse-recursive" },
125       { GTK_CMCTREE_EXPANSION_TOGGLE, "GTK_CMCTREE_EXPANSION_TOGGLE", "toggle" },
126       { GTK_CMCTREE_EXPANSION_TOGGLE_RECURSIVE, "GTK_CMCTREE_EXPANSION_TOGGLE_RECURSIVE", "toggle-recursive" },
127       { 0, NULL, NULL }
128     };
129 #if GLIB_CHECK_VERSION(2,10,0)
130     etype = g_enum_register_static (g_intern_static_string ("GtkCMCTreeExpansionType"), values);
131 #else
132     etype = g_enum_register_static ("GtkCMCTreeExpansionType", values);
133 #endif
134   }
135   return etype;
136 }
137
138
139 static inline gint
140 COLUMN_FROM_XPIXEL (GtkCMCList * clist,
141                     gint x)
142 {
143   gint i, cx;
144
145   for (i = 0; i < clist->columns; i++)
146     if (clist->column[i].visible)
147       {
148         cx = clist->column[i].area.x + clist->hoffset;
149
150         if (x >= (cx - (COLUMN_INSET + CELL_SPACING)) &&
151             x <= (cx + clist->column[i].area.width + COLUMN_INSET))
152           return i;
153       }
154
155   /* no match */
156   return -1;
157 }
158
159 #define CLIST_UNFROZEN(clist)     (((GtkCMCList*) (clist))->freeze_count == 0)
160 #define CLIST_REFRESH(clist)    G_STMT_START { \
161   if (CLIST_UNFROZEN (clist)) \
162     GTK_CMCLIST_GET_CLASS (clist)->refresh ((GtkCMCList*) (clist)); \
163 } G_STMT_END
164
165
166 enum {
167   ARG_0,
168   ARG_N_COLUMNS,
169   ARG_TREE_COLUMN,
170   ARG_INDENT,
171   ARG_SPACING,
172   ARG_SHOW_STUB,
173   ARG_LINE_STYLE,
174   ARG_EXPANDER_STYLE
175 };
176
177
178 static void     gtk_cmctree_class_init    (GtkCMCTreeClass         *klass);
179 static void     gtk_cmctree_init          (GtkCMCTree              *ctree);
180 static GObject* gtk_cmctree_constructor   (GType                  type,
181                                          guint                  n_construct_properties,
182                                          GObjectConstructParam *construct_params);
183 static void gtk_cmctree_set_arg         (GObject *object,
184                                 guint      arg_id,
185                                 const GValue *value,
186                                 GParamSpec *spec);
187 static void gtk_cmctree_get_arg         (GObject *object,
188                                 guint      arg_id,
189                                 GValue *value,
190                                 GParamSpec *spec);
191 static void gtk_cmctree_realize           (GtkWidget      *widget);
192 static void gtk_cmctree_unrealize         (GtkWidget      *widget);
193 static gint gtk_cmctree_button_press      (GtkWidget      *widget,
194                                          GdkEventButton *event);
195 static void ctree_attach_styles         (GtkCMCTree       *ctree,
196                                          GtkCMCTreeNode   *node,
197                                          gpointer        data);
198 static void ctree_detach_styles         (GtkCMCTree       *ctree,
199                                          GtkCMCTreeNode   *node, 
200                                          gpointer        data);
201 static gint draw_cell_pixbuf            (GdkWindow      *window,
202                                          GdkRectangle   *clip_rectangle,
203                                          GdkGC          *fg_gc,
204                                          GdkPixbuf      *pixbuf,
205                                          gint            x,
206                                          gint            y,
207                                          gint            width,
208                                          gint            height);
209 static void get_cell_style              (GtkCMCList       *clist,
210                                          GtkCMCListRow    *clist_row,
211                                          gint            state,
212                                          gint            column,
213                                          GtkStyle      **style,
214                                          GdkGC         **fg_gc,
215                                          GdkGC         **bg_gc);
216 static gint gtk_cmctree_draw_expander     (GtkCMCTree       *ctree,
217                                          GtkCMCTreeRow    *ctree_row,
218                                          GtkStyle       *style,
219                                          GdkRectangle   *clip_rectangle,
220                                          gint            x);
221 static gint gtk_cmctree_draw_lines        (GtkCMCTree       *ctree,
222                                          GtkCMCTreeRow    *ctree_row,
223                                          gint            row,
224                                          gint            column,
225                                          gint            state,
226                                          GdkRectangle   *clip_rectangle,
227                                          GdkRectangle   *cell_rectangle,
228                                          GdkRectangle   *crect,
229                                          GdkRectangle   *area,
230                                          GtkStyle       *style);
231 static void draw_row                    (GtkCMCList       *clist,
232                                          GdkRectangle   *area,
233                                          gint            row,
234                                          GtkCMCListRow    *clist_row);
235 static void draw_drag_highlight         (GtkCMCList        *clist,
236                                          GtkCMCListRow     *dest_row,
237                                          gint             dest_row_number,
238                                          GtkCMCListDragPos  drag_pos);
239 static void tree_draw_node              (GtkCMCTree      *ctree,
240                                          GtkCMCTreeNode  *node);
241 static void set_cell_contents           (GtkCMCList      *clist,
242                                          GtkCMCListRow   *clist_row,
243                                          gint           column,
244                                          GtkCMCellType    type,
245                                          const gchar   *text,
246                                          guint8         spacing,
247                                          GdkPixbuf     *pixbuf);
248 static void set_node_info               (GtkCMCTree      *ctree,
249                                          GtkCMCTreeNode  *node,
250                                          const gchar   *text,
251                                          guint8         spacing,
252                                          GdkPixbuf     *pixbuf_closed,
253                                          GdkPixbuf     *pixbuf_opened,
254                                          gboolean       is_leaf,
255                                          gboolean       expanded);
256 static GtkCMCTreeRow *row_new             (GtkCMCTree      *ctree);
257 static void row_delete                  (GtkCMCTree      *ctree,
258                                          GtkCMCTreeRow   *ctree_row);
259 static void tree_delete                 (GtkCMCTree      *ctree, 
260                                          GtkCMCTreeNode  *node, 
261                                          gpointer       data);
262 static void tree_delete_row             (GtkCMCTree      *ctree, 
263                                          GtkCMCTreeNode  *node, 
264                                          gpointer       data);
265 static void real_clear                  (GtkCMCList      *clist);
266 static void tree_update_level           (GtkCMCTree      *ctree, 
267                                          GtkCMCTreeNode  *node, 
268                                          gpointer       data);
269 static void tree_select                 (GtkCMCTree      *ctree, 
270                                          GtkCMCTreeNode  *node, 
271                                          gpointer       data);
272 static void tree_unselect               (GtkCMCTree      *ctree, 
273                                          GtkCMCTreeNode  *node, 
274                                          gpointer       data);
275 static void real_select_all             (GtkCMCList      *clist);
276 static void real_unselect_all           (GtkCMCList      *clist);
277 static void tree_expand                 (GtkCMCTree      *ctree, 
278                                          GtkCMCTreeNode  *node,
279                                          gpointer       data);
280 static void tree_collapse               (GtkCMCTree      *ctree, 
281                                          GtkCMCTreeNode  *node,
282                                          gpointer       data);
283 static void tree_collapse_to_depth      (GtkCMCTree      *ctree, 
284                                          GtkCMCTreeNode  *node, 
285                                          gint           depth);
286 static void tree_toggle_expansion       (GtkCMCTree      *ctree,
287                                          GtkCMCTreeNode  *node,
288                                          gpointer       data);
289 static void change_focus_row_expansion  (GtkCMCTree      *ctree,
290                                          GtkCMCTreeExpansionType expansion);
291 static void real_select_row             (GtkCMCList      *clist,
292                                          gint           row,
293                                          gint           column,
294                                          GdkEvent      *event);
295 static void real_unselect_row           (GtkCMCList      *clist,
296                                          gint           row,
297                                          gint           column,
298                                          GdkEvent      *event);
299 static void real_tree_select            (GtkCMCTree      *ctree,
300                                          GtkCMCTreeNode  *node,
301                                          gint           column);
302 static void real_tree_unselect          (GtkCMCTree      *ctree,
303                                          GtkCMCTreeNode  *node,
304                                          gint           column);
305 static void real_tree_expand            (GtkCMCTree      *ctree,
306                                          GtkCMCTreeNode  *node);
307 static void real_tree_collapse          (GtkCMCTree      *ctree,
308                                          GtkCMCTreeNode  *node);
309 static void real_tree_move              (GtkCMCTree      *ctree,
310                                          GtkCMCTreeNode  *node,
311                                          GtkCMCTreeNode  *new_parent, 
312                                          GtkCMCTreeNode  *new_sibling);
313 static void real_row_move               (GtkCMCList      *clist,
314                                          gint           source_row,
315                                          gint           dest_row);
316 static void gtk_cmctree_link              (GtkCMCTree      *ctree,
317                                          GtkCMCTreeNode  *node,
318                                          GtkCMCTreeNode  *parent,
319                                          GtkCMCTreeNode  *sibling,
320                                          gboolean       update_focus_row);
321 static void gtk_cmctree_unlink            (GtkCMCTree      *ctree, 
322                                          GtkCMCTreeNode  *node,
323                                          gboolean       update_focus_row);
324 static GtkCMCTreeNode * gtk_cmctree_last_visible (GtkCMCTree     *ctree,
325                                               GtkCMCTreeNode *node);
326 static gboolean ctree_is_hot_spot       (GtkCMCTree      *ctree, 
327                                          GtkCMCTreeNode  *node,
328                                          gint           row, 
329                                          gint           x, 
330                                          gint           y);
331 static void tree_sort                   (GtkCMCTree      *ctree,
332                                          GtkCMCTreeNode  *node,
333                                          gpointer       data);
334 static void fake_unselect_all           (GtkCMCList      *clist,
335                                          gint           row);
336 static GList * selection_find           (GtkCMCList      *clist,
337                                          gint           row_number,
338                                          GList         *row_list_element);
339 static void resync_selection            (GtkCMCList      *clist,
340                                          GdkEvent      *event);
341 static void real_undo_selection         (GtkCMCList      *clist);
342 static void select_row_recursive        (GtkCMCTree      *ctree, 
343                                          GtkCMCTreeNode  *node, 
344                                          gpointer       data);
345 static gint real_insert_row             (GtkCMCList      *clist,
346                                          gint           row,
347                                          gchar         *text[]);
348 static void real_remove_row             (GtkCMCList      *clist,
349                                          gint           row);
350 static void real_sort_list              (GtkCMCList      *clist);
351 static void cell_size_request           (GtkCMCList       *clist,
352                                          GtkCMCListRow    *clist_row,
353                                          gint            column,
354                                          GtkRequisition *requisition);
355 static void column_auto_resize          (GtkCMCList       *clist,
356                                          GtkCMCListRow    *clist_row,
357                                          gint            column,
358                                          gint            old_width);
359 static void auto_resize_columns         (GtkCMCList       *clist);
360
361
362 static gboolean check_drag               (GtkCMCTree         *ctree,
363                                           GtkCMCTreeNode     *drag_source,
364                                           GtkCMCTreeNode     *drag_target,
365                                           GtkCMCListDragPos   insert_pos);
366 static void gtk_cmctree_drag_begin         (GtkWidget        *widget,
367                                           GdkDragContext   *context);
368 static gint gtk_cmctree_drag_motion        (GtkWidget        *widget,
369                                           GdkDragContext   *context,
370                                           gint              x,
371                                           gint              y,
372                                           guint             time);
373 static void gtk_cmctree_drag_data_received (GtkWidget        *widget,
374                                           GdkDragContext   *context,
375                                           gint              x,
376                                           gint              y,
377                                           GtkSelectionData *selection_data,
378                                           guint             info,
379                                           guint32           time);
380 static void remove_grab                  (GtkCMCList         *clist);
381 static void drag_dest_cell               (GtkCMCList         *clist,
382                                           gint              x,
383                                           gint              y,
384                                           GtkCMCListDestInfo *dest_info);
385
386
387 enum
388 {
389   TREE_SELECT_ROW,
390   TREE_UNSELECT_ROW,
391   TREE_EXPAND,
392   TREE_COLLAPSE,
393   TREE_MOVE,
394   CHANGE_FOCUS_ROW_EXPANSION,
395   LAST_SIGNAL
396 };
397
398 static GtkCMCListClass *parent_class = NULL;
399 static GtkContainerClass *container_class = NULL;
400 static guint ctree_signals[LAST_SIGNAL] = {0};
401
402
403 GType
404 gtk_cmctree_get_type (void)
405 {
406   static GType ctree_type = 0;
407
408   if (!ctree_type)
409     {
410       static const GTypeInfo ctree_info =
411       {
412                         sizeof (GtkCMCTreeClass),
413
414                         (GBaseInitFunc) NULL,
415                         (GBaseFinalizeFunc) NULL,
416
417                         (GClassInitFunc) gtk_cmctree_class_init,
418                         (GClassFinalizeFunc) NULL,
419                         NULL,   /* class_data */
420
421                         sizeof (GtkCMCTree),
422                         0,      /* n_preallocs */
423                         (GInstanceInitFunc) gtk_cmctree_init,
424       };
425
426         ctree_type = g_type_register_static (GTK_TYPE_CMCLIST, "GtkCMCTree", &ctree_info, (GTypeFlags)0);
427     }
428
429   return ctree_type;
430 }
431
432 static void
433 gtk_cmctree_class_init (GtkCMCTreeClass *klass)
434 {
435   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
436   GtkObjectClass *object_class;
437   GtkWidgetClass *widget_class;
438   GtkCMCListClass *clist_class;
439   GtkBindingSet *binding_set;
440
441   gobject_class->constructor = gtk_cmctree_constructor;
442
443   object_class = (GtkObjectClass *) klass;
444   widget_class = (GtkWidgetClass *) klass;
445   container_class = (GtkContainerClass *) klass;
446   clist_class = (GtkCMCListClass *) klass;
447
448   parent_class = g_type_class_peek (GTK_TYPE_CMCLIST);
449   container_class = g_type_class_peek (GTK_TYPE_CONTAINER);
450
451   gobject_class->set_property = gtk_cmctree_set_arg;
452   gobject_class->get_property = gtk_cmctree_get_arg;
453
454   widget_class->realize = gtk_cmctree_realize;
455   widget_class->unrealize = gtk_cmctree_unrealize;
456   widget_class->button_press_event = gtk_cmctree_button_press;
457
458   widget_class->drag_begin = gtk_cmctree_drag_begin;
459   widget_class->drag_motion = gtk_cmctree_drag_motion;
460   widget_class->drag_data_received = gtk_cmctree_drag_data_received;
461
462   clist_class->select_row = real_select_row;
463   clist_class->unselect_row = real_unselect_row;
464   clist_class->row_move = real_row_move;
465   clist_class->undo_selection = real_undo_selection;
466   clist_class->resync_selection = resync_selection;
467   clist_class->selection_find = selection_find;
468   clist_class->click_column = NULL;
469   clist_class->draw_row = draw_row;
470   clist_class->draw_drag_highlight = draw_drag_highlight;
471   clist_class->clear = real_clear;
472   clist_class->select_all = real_select_all;
473   clist_class->unselect_all = real_unselect_all;
474   clist_class->fake_unselect_all = fake_unselect_all;
475   clist_class->insert_row = real_insert_row;
476   clist_class->remove_row = real_remove_row;
477   clist_class->sort_list = real_sort_list;
478   clist_class->set_cell_contents = set_cell_contents;
479   clist_class->cell_size_request = cell_size_request;
480
481   klass->tree_select_row = real_tree_select;
482   klass->tree_unselect_row = real_tree_unselect;
483   klass->tree_expand = real_tree_expand;
484   klass->tree_collapse = real_tree_collapse;
485   klass->tree_move = real_tree_move;
486   klass->change_focus_row_expansion = change_focus_row_expansion;
487
488   g_object_class_install_property (gobject_class,
489                                 ARG_N_COLUMNS,
490                                 g_param_spec_uint ("n-columns",
491                                 "N-Columns",
492                                 "N-Columns",
493                                 1,
494                                 G_MAXINT,
495                                 1,
496                                 G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
497   g_object_class_install_property (gobject_class,
498                                 ARG_TREE_COLUMN,
499                                 g_param_spec_uint ("tree-column",
500                                 "tree-column",
501                                 "tree-column",
502                                 0,
503                                 G_MAXINT,
504                                 0,
505                                 G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
506   g_object_class_install_property (gobject_class,
507                                 ARG_INDENT,
508                                 g_param_spec_uint ("indent",
509                                 "indent",
510                                 "indent",
511                                 1,
512                                 G_MAXINT,
513                                 1,
514                                 G_PARAM_READWRITE));
515   g_object_class_install_property (gobject_class,
516                                 ARG_SPACING,
517                                 g_param_spec_uint ("spacing",
518                                 "spacing",
519                                 "spacing",
520                                 1,
521                                 G_MAXINT,
522                                 1,
523                                 G_PARAM_READWRITE));
524   g_object_class_install_property (gobject_class,
525                                 ARG_SHOW_STUB,
526                                 g_param_spec_boolean ("show-stub",
527                                 "show-stub",
528                                 "show-stub",
529                                 TRUE,
530                                 G_PARAM_READWRITE));
531   g_object_class_install_property (gobject_class,
532                                 ARG_LINE_STYLE,
533                                 g_param_spec_enum ("line-style",
534                                 "line-style",
535                                 "line-style",
536                                 GTK_TYPE_CMCTREE_LINE_STYLE, 0,
537                                 G_PARAM_READWRITE));
538   g_object_class_install_property (gobject_class,
539                                 ARG_EXPANDER_STYLE,
540                                 g_param_spec_enum ("expander-style",
541                                 "expander-style",
542                                 "expander-style",
543                                 GTK_TYPE_CMCTREE_EXPANDER_STYLE, 0,
544                                 G_PARAM_READWRITE));
545
546   ctree_signals[TREE_SELECT_ROW] =
547                 g_signal_new ("tree_select_row",
548                               G_TYPE_FROM_CLASS (object_class),
549                               G_SIGNAL_RUN_FIRST,
550                               G_STRUCT_OFFSET (GtkCMCTreeClass, tree_select_row),
551                               NULL, NULL,
552                               claws_marshal_VOID__POINTER_INT,
553                               G_TYPE_NONE, 2,
554                               GTK_TYPE_CMCTREE_NODE,
555                               G_TYPE_INT);
556   ctree_signals[TREE_UNSELECT_ROW] =
557                 g_signal_new ("tree_unselect_row",
558                               G_TYPE_FROM_CLASS (object_class),
559                               G_SIGNAL_RUN_FIRST,
560                               G_STRUCT_OFFSET (GtkCMCTreeClass, tree_unselect_row),
561                               NULL, NULL,
562                               claws_marshal_VOID__POINTER_INT,
563                               G_TYPE_NONE, 2,
564                               GTK_TYPE_CMCTREE_NODE,
565                               G_TYPE_INT);
566   ctree_signals[TREE_EXPAND] =
567                 g_signal_new ("tree_expand",
568                               G_TYPE_FROM_CLASS (object_class),
569                               G_SIGNAL_RUN_LAST,
570                               G_STRUCT_OFFSET (GtkCMCTreeClass, tree_expand),
571                               NULL, NULL,
572                               claws_marshal_VOID__POINTER,
573                               G_TYPE_NONE, 1,
574                               GTK_TYPE_CMCTREE_NODE);
575   ctree_signals[TREE_COLLAPSE] =
576                 g_signal_new ("tree_collapse",
577                               G_TYPE_FROM_CLASS (object_class),
578                               G_SIGNAL_RUN_LAST,
579                               G_STRUCT_OFFSET (GtkCMCTreeClass, tree_collapse),
580                               NULL, NULL,
581                               claws_marshal_VOID__POINTER,
582                               G_TYPE_NONE, 1,
583                               GTK_TYPE_CMCTREE_NODE);
584   ctree_signals[TREE_MOVE] =
585                 g_signal_new ("tree_move",
586                               G_TYPE_FROM_CLASS (object_class),
587                               G_SIGNAL_RUN_LAST,
588                               G_STRUCT_OFFSET (GtkCMCTreeClass, tree_move),
589                               NULL, NULL,
590                               claws_marshal_VOID__POINTER_POINTER_POINTER,
591                               G_TYPE_NONE, 3,
592                               GTK_TYPE_CMCTREE_NODE,GTK_TYPE_CMCTREE_NODE,GTK_TYPE_CMCTREE_NODE);
593   ctree_signals[CHANGE_FOCUS_ROW_EXPANSION] =
594                 g_signal_new ("change_focus_row_expansion",
595                               G_TYPE_FROM_CLASS (object_class),
596                               G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
597                               G_STRUCT_OFFSET (GtkCMCTreeClass, change_focus_row_expansion),
598                               NULL, NULL,
599                               claws_marshal_VOID__ENUM,
600                               G_TYPE_NONE, 1, GTK_TYPE_CMCTREE_EXPANSION_TYPE);
601
602   binding_set = gtk_binding_set_by_class (klass);
603   gtk_binding_entry_add_signal (binding_set,
604                                 GDK_plus, 0,
605                                 "change_focus_row_expansion", 1,
606                                 G_TYPE_ENUM, GTK_CMCTREE_EXPANSION_EXPAND);
607   gtk_binding_entry_add_signal (binding_set,
608                                 GDK_plus, GDK_CONTROL_MASK,
609                                 "change_focus_row_expansion", 1,
610                                 G_TYPE_ENUM, GTK_CMCTREE_EXPANSION_EXPAND_RECURSIVE);
611
612   gtk_binding_entry_add_signal (binding_set,
613                                 GDK_KP_Add, 0,
614                                 "change_focus_row_expansion", 1,
615                                 G_TYPE_ENUM, GTK_CMCTREE_EXPANSION_EXPAND);
616   gtk_binding_entry_add_signal (binding_set,
617                                 GDK_KP_Add, GDK_CONTROL_MASK,
618                                 "change_focus_row_expansion", 1,
619                                 G_TYPE_ENUM, GTK_CMCTREE_EXPANSION_EXPAND_RECURSIVE);
620   
621   gtk_binding_entry_add_signal (binding_set,
622                                 GDK_minus, 0,
623                                 "change_focus_row_expansion", 1,
624                                 G_TYPE_ENUM, GTK_CMCTREE_EXPANSION_COLLAPSE);
625   gtk_binding_entry_add_signal (binding_set,
626                                 GDK_minus, GDK_CONTROL_MASK,
627                                 "change_focus_row_expansion", 1,
628                                 G_TYPE_ENUM,
629                                 GTK_CMCTREE_EXPANSION_COLLAPSE_RECURSIVE);
630   gtk_binding_entry_add_signal (binding_set,
631                                 GDK_KP_Subtract, 0,
632                                 "change_focus_row_expansion", 1,
633                                 G_TYPE_ENUM, GTK_CMCTREE_EXPANSION_COLLAPSE);
634   gtk_binding_entry_add_signal (binding_set,
635                                 GDK_KP_Subtract, GDK_CONTROL_MASK,
636                                 "change_focus_row_expansion", 1,
637                                 G_TYPE_ENUM,
638                                 GTK_CMCTREE_EXPANSION_COLLAPSE_RECURSIVE);
639   gtk_binding_entry_add_signal (binding_set,
640                                 GDK_equal, 0,
641                                 "change_focus_row_expansion", 1,
642                                 G_TYPE_ENUM, GTK_CMCTREE_EXPANSION_TOGGLE);
643   gtk_binding_entry_add_signal (binding_set,
644                                 GDK_KP_Equal, 0,
645                                 "change_focus_row_expansion", 1,
646                                 G_TYPE_ENUM, GTK_CMCTREE_EXPANSION_TOGGLE);
647   gtk_binding_entry_add_signal (binding_set,
648                                 GDK_KP_Multiply, 0,
649                                 "change_focus_row_expansion", 1,
650                                 G_TYPE_ENUM, GTK_CMCTREE_EXPANSION_TOGGLE);
651   gtk_binding_entry_add_signal (binding_set,
652                                 GDK_asterisk, 0,
653                                 "change_focus_row_expansion", 1,
654                                 G_TYPE_ENUM, GTK_CMCTREE_EXPANSION_TOGGLE);
655   gtk_binding_entry_add_signal (binding_set,
656                                 GDK_KP_Multiply, GDK_CONTROL_MASK,
657                                 "change_focus_row_expansion", 1,
658                                 G_TYPE_ENUM,
659                                 GTK_CMCTREE_EXPANSION_TOGGLE_RECURSIVE);
660   gtk_binding_entry_add_signal (binding_set,
661                                 GDK_asterisk, GDK_CONTROL_MASK,
662                                 "change_focus_row_expansion", 1,
663                                 G_TYPE_ENUM,
664                                 GTK_CMCTREE_EXPANSION_TOGGLE_RECURSIVE);  
665 }
666
667 static void
668 gtk_cmctree_set_arg (GObject *object,
669                                 guint      arg_id,
670                                 const GValue *value,
671                                 GParamSpec *spec)
672 {
673   GtkCMCTree *ctree;
674   GtkCMCList *clist;
675
676   ctree = GTK_CMCTREE (object);
677   clist = GTK_CMCLIST (ctree);
678
679   switch (arg_id)
680     {
681     case ARG_N_COLUMNS: /* construct-only arg, only set at construction time */
682 #if !GLIB_CHECK_VERSION(2,10,0)
683       cm_return_if_fail (clist->row_mem_chunk == NULL);
684 #endif
685       clist->columns = MAX (1, g_value_get_uint (value));
686 #if !GLIB_CHECK_VERSION(2,10,0)
687       clist->row_mem_chunk = g_mem_chunk_new ("ctree row mem chunk",
688                                               sizeof (GtkCMCTreeRow),
689                                               sizeof (GtkCMCTreeRow)
690                                               * CLIST_OPTIMUM_SIZE,
691                                               G_ALLOC_AND_FREE);
692       clist->cell_mem_chunk = g_mem_chunk_new ("ctree cell mem chunk",
693                                                sizeof (GtkCMCell) * clist->columns,
694                                                sizeof (GtkCMCell) * clist->columns
695                                                * CLIST_OPTIMUM_SIZE,
696                                                G_ALLOC_AND_FREE);
697 #endif
698       ctree->tree_column = CLAMP (ctree->tree_column, 0, clist->columns);
699       break;
700     case ARG_TREE_COLUMN: /* construct-only arg, only set at construction time */
701       ctree->tree_column = g_value_get_uint (value);
702 #if !GLIB_CHECK_VERSION(2,10,0)
703       if (clist->row_mem_chunk)
704 #endif
705         ctree->tree_column = CLAMP (ctree->tree_column, 0, clist->columns);
706       break;
707     case ARG_INDENT:
708       gtk_cmctree_set_indent (ctree, g_value_get_uint (value));
709       break;
710     case ARG_SPACING:
711       gtk_cmctree_set_spacing (ctree, g_value_get_uint (value));
712       break;
713     case ARG_SHOW_STUB:
714       gtk_cmctree_set_show_stub (ctree, g_value_get_boolean (value));
715       break;
716     case ARG_LINE_STYLE:
717       gtk_cmctree_set_line_style (ctree, g_value_get_enum (value));
718       break;
719     case ARG_EXPANDER_STYLE:
720       gtk_cmctree_set_expander_style (ctree, g_value_get_enum (value));
721       break;
722     default:
723       break;
724     }
725 }
726
727 static void
728 gtk_cmctree_get_arg (GObject *object,
729                                 guint      arg_id,
730                                 GValue *value,
731                                 GParamSpec *spec)
732 {
733   GtkCMCTree *ctree;
734
735   ctree = GTK_CMCTREE (object);
736
737   switch (arg_id)
738     {
739     case ARG_N_COLUMNS:
740       g_value_set_uint(value, GTK_CMCLIST (ctree)->columns);
741       break;
742     case ARG_TREE_COLUMN:
743       g_value_set_uint(value, ctree->tree_column);
744       break;
745     case ARG_INDENT:
746       g_value_set_uint(value, ctree->tree_indent);
747       break;
748     case ARG_SPACING:
749       g_value_set_uint(value, ctree->tree_spacing);
750       break;
751     case ARG_SHOW_STUB:
752       g_value_set_boolean(value, ctree->show_stub);
753       break;
754     case ARG_LINE_STYLE:
755       g_value_set_enum(value, ctree->line_style);
756       break;
757     case ARG_EXPANDER_STYLE:
758       g_value_set_enum(value, ctree->expander_style);
759       break;
760     default:
761       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, arg_id, spec);
762       break;
763     }
764 }
765
766 static void
767 gtk_cmctree_init (GtkCMCTree *ctree)
768 {
769   GtkCMCList *clist;
770
771   GTK_CMCLIST_SET_FLAG (ctree, CMCLIST_DRAW_DRAG_RECT);
772   GTK_CMCLIST_SET_FLAG (ctree, CMCLIST_DRAW_DRAG_LINE);
773
774   clist = GTK_CMCLIST (ctree);
775
776   ctree->tree_indent    = 20;
777   ctree->tree_spacing   = 5;
778   ctree->tree_column    = 0;
779   ctree->line_style     = GTK_CMCTREE_LINES_SOLID;
780   ctree->expander_style = GTK_CMCTREE_EXPANDER_SQUARE;
781   ctree->drag_compare   = NULL;
782   ctree->show_stub      = TRUE;
783
784   clist->button_actions[0] |= GTK_CMBUTTON_EXPANDS;
785 }
786
787 static void
788 ctree_attach_styles (GtkCMCTree     *ctree,
789                      GtkCMCTreeNode *node,
790                      gpointer      data)
791 {
792   GtkCMCList *clist;
793   gint i;
794
795   clist = GTK_CMCLIST (ctree);
796
797   if (GTK_CMCTREE_ROW (node)->row.style)
798     GTK_CMCTREE_ROW (node)->row.style =
799       gtk_style_attach (GTK_CMCTREE_ROW (node)->row.style, clist->clist_window);
800
801   if (GTK_CMCTREE_ROW (node)->row.fg_set || GTK_CMCTREE_ROW (node)->row.bg_set)
802     {
803       GdkColormap *colormap;
804
805       colormap = gtk_widget_get_colormap (GTK_WIDGET (ctree));
806       if (GTK_CMCTREE_ROW (node)->row.fg_set)
807         gdk_colormap_alloc_color (colormap, &(GTK_CMCTREE_ROW (node)->row.foreground), TRUE, TRUE);
808       if (GTK_CMCTREE_ROW (node)->row.bg_set)
809         gdk_colormap_alloc_color (colormap, &(GTK_CMCTREE_ROW (node)->row.background), TRUE, TRUE);
810     }
811
812   for (i = 0; i < clist->columns; i++)
813     if  (GTK_CMCTREE_ROW (node)->row.cell[i].style)
814       GTK_CMCTREE_ROW (node)->row.cell[i].style =
815         gtk_style_attach (GTK_CMCTREE_ROW (node)->row.cell[i].style,
816                           clist->clist_window);
817 }
818
819 static void
820 ctree_detach_styles (GtkCMCTree     *ctree,
821                      GtkCMCTreeNode *node,
822                      gpointer      data)
823 {
824   GtkCMCList *clist;
825   gint i;
826
827   clist = GTK_CMCLIST (ctree);
828
829   if (GTK_CMCTREE_ROW (node)->row.style)
830     gtk_style_detach (GTK_CMCTREE_ROW (node)->row.style);
831   for (i = 0; i < clist->columns; i++)
832     if  (GTK_CMCTREE_ROW (node)->row.cell[i].style)
833       gtk_style_detach (GTK_CMCTREE_ROW (node)->row.cell[i].style);
834 }
835
836 static void
837 gtk_cmctree_realize (GtkWidget *widget)
838 {
839   GtkCMCTree *ctree;
840   GtkCMCList *clist;
841   GdkGCValues values;
842   GtkCMCTreeNode *node;
843   GtkCMCTreeNode *child;
844   gint i;
845
846   cm_return_if_fail (GTK_IS_CMCTREE (widget));
847
848   GTK_WIDGET_CLASS (parent_class)->realize (widget);
849
850   ctree = GTK_CMCTREE (widget);
851   clist = GTK_CMCLIST (widget);
852
853   node = GTK_CMCTREE_NODE (clist->row_list);
854   for (i = 0; i < clist->rows; i++)
855     {
856       if (GTK_CMCTREE_ROW (node)->children && !GTK_CMCTREE_ROW (node)->expanded)
857         for (child = GTK_CMCTREE_ROW (node)->children; child;
858              child = GTK_CMCTREE_ROW (child)->sibling)
859           gtk_cmctree_pre_recursive (ctree, child, ctree_attach_styles, NULL);
860       node = GTK_CMCTREE_NODE_NEXT (node);
861     }
862
863   values.foreground = widget->style->fg[GTK_STATE_NORMAL];
864   values.background = widget->style->base[GTK_STATE_NORMAL];
865   values.subwindow_mode = GDK_INCLUDE_INFERIORS;
866   values.line_style = GDK_LINE_SOLID;
867   ctree->lines_gc = gdk_gc_new_with_values (GTK_CMCLIST(widget)->clist_window, 
868                                             &values,
869                                             GDK_GC_FOREGROUND |
870                                             GDK_GC_BACKGROUND |
871                                             GDK_GC_SUBWINDOW |
872                                             GDK_GC_LINE_STYLE);
873
874   if (ctree->line_style == GTK_CMCTREE_LINES_DOTTED)
875     {
876       gdk_gc_set_line_attributes (ctree->lines_gc, 1, 
877                                   GDK_LINE_ON_OFF_DASH, GDK_CAP_BUTT, GDK_JOIN_MITER);
878       gdk_gc_set_dashes (ctree->lines_gc, 0, "\1\1", 2);
879     }
880 }
881
882 static void
883 gtk_cmctree_unrealize (GtkWidget *widget)
884 {
885   GtkCMCTree *ctree;
886   GtkCMCList *clist;
887
888   cm_return_if_fail (GTK_IS_CMCTREE (widget));
889
890   GTK_WIDGET_CLASS (parent_class)->unrealize (widget);
891
892   ctree = GTK_CMCTREE (widget);
893   clist = GTK_CMCLIST (widget);
894
895   if (gtkut_widget_get_realized (widget))
896     {
897       GtkCMCTreeNode *node;
898       GtkCMCTreeNode *child;
899       gint i;
900
901       node = GTK_CMCTREE_NODE (clist->row_list);
902       for (i = 0; i < clist->rows; i++)
903         {
904           if (GTK_CMCTREE_ROW (node)->children &&
905               !GTK_CMCTREE_ROW (node)->expanded)
906             for (child = GTK_CMCTREE_ROW (node)->children; child;
907                  child = GTK_CMCTREE_ROW (child)->sibling)
908               gtk_cmctree_pre_recursive(ctree, child, ctree_detach_styles, NULL);
909           node = GTK_CMCTREE_NODE_NEXT (node);
910         }
911     }
912
913   g_object_unref (ctree->lines_gc);
914 }
915
916 static gint
917 gtk_cmctree_button_press (GtkWidget      *widget,
918                         GdkEventButton *event)
919 {
920   GtkCMCTree *ctree;
921   GtkCMCList *clist;
922   gint button_actions;
923
924   cm_return_val_if_fail (GTK_IS_CMCTREE (widget), FALSE);
925   cm_return_val_if_fail (event != NULL, FALSE);
926
927   ctree = GTK_CMCTREE (widget);
928   clist = GTK_CMCLIST (widget);
929
930   button_actions = clist->button_actions[event->button - 1];
931
932   if (button_actions == GTK_CMBUTTON_IGNORED)
933     return FALSE;
934
935   if (event->window == clist->clist_window)
936     {
937       GtkCMCTreeNode *work;
938       gint x;
939       gint y;
940       gint row;
941       gint column;
942
943       x = event->x;
944       y = event->y;
945
946       if (!gtk_cmclist_get_selection_info (clist, x, y, &row, &column))
947         return FALSE;
948
949       work = GTK_CMCTREE_NODE (g_list_nth (clist->row_list, row));
950           
951       if (button_actions & GTK_CMBUTTON_EXPANDS &&
952           (GTK_CMCTREE_ROW (work)->children && !GTK_CMCTREE_ROW (work)->is_leaf  &&
953            (event->type == GDK_2BUTTON_PRESS ||
954             ctree_is_hot_spot (ctree, work, row, x, y))))
955         {
956           if (GTK_CMCTREE_ROW (work)->expanded)
957             gtk_cmctree_collapse (ctree, work);
958           else
959             gtk_cmctree_expand (ctree, work);
960
961           return TRUE;
962         }
963     }
964   
965   return GTK_WIDGET_CLASS (parent_class)->button_press_event (widget, event);
966 }
967
968 static void
969 draw_drag_highlight (GtkCMCList        *clist,
970                      GtkCMCListRow     *dest_row,
971                      gint             dest_row_number,
972                      GtkCMCListDragPos  drag_pos)
973 {
974   GtkCMCTree *ctree;
975   GdkPoint points[4];
976   gint level;
977   gint i;
978   gint y = 0;
979
980   cm_return_if_fail (GTK_IS_CMCTREE (clist));
981
982   ctree = GTK_CMCTREE (clist);
983
984   level = ((GtkCMCTreeRow *)(dest_row))->level;
985
986   y = ROW_TOP_YPIXEL (clist, dest_row_number) - 1;
987
988   switch (drag_pos)
989     {
990     case GTK_CMCLIST_DRAG_NONE:
991       break;
992     case GTK_CMCLIST_DRAG_AFTER:
993       y += clist->row_height + 1;
994     case GTK_CMCLIST_DRAG_BEFORE:
995       
996       if (clist->column[ctree->tree_column].visible)
997         switch (clist->column[ctree->tree_column].justification)
998           {
999           case GTK_JUSTIFY_CENTER:
1000           case GTK_JUSTIFY_FILL:
1001           case GTK_JUSTIFY_LEFT:
1002             if (ctree->tree_column > 0)
1003               gdk_draw_line (clist->clist_window, clist->xor_gc, 
1004                              COLUMN_LEFT_XPIXEL(clist, 0), y,
1005                              COLUMN_LEFT_XPIXEL(clist, ctree->tree_column - 1)+
1006                              clist->column[ctree->tree_column - 1].area.width,
1007                              y);
1008
1009             gdk_draw_line (clist->clist_window, clist->xor_gc, 
1010                            COLUMN_LEFT_XPIXEL(clist, ctree->tree_column) + 
1011                            ctree->tree_indent * level -
1012                            (ctree->tree_indent - PM_SIZE) / 2, y,
1013                            GTK_WIDGET (ctree)->allocation.width, y);
1014             break;
1015           case GTK_JUSTIFY_RIGHT:
1016             if (ctree->tree_column < clist->columns - 1)
1017               gdk_draw_line (clist->clist_window, clist->xor_gc, 
1018                              COLUMN_LEFT_XPIXEL(clist, ctree->tree_column + 1),
1019                              y,
1020                              COLUMN_LEFT_XPIXEL(clist, clist->columns - 1) +
1021                              clist->column[clist->columns - 1].area.width, y);
1022       
1023             gdk_draw_line (clist->clist_window, clist->xor_gc, 
1024                            0, y, COLUMN_LEFT_XPIXEL(clist, ctree->tree_column)
1025                            + clist->column[ctree->tree_column].area.width -
1026                            ctree->tree_indent * level +
1027                            (ctree->tree_indent - PM_SIZE) / 2, y);
1028             break;
1029           }
1030       else
1031         gdk_draw_line (clist->clist_window, clist->xor_gc, 
1032                        0, y, clist->clist_window_width, y);
1033       break;
1034     case GTK_CMCLIST_DRAG_INTO:
1035       y = ROW_TOP_YPIXEL (clist, dest_row_number) + clist->row_height;
1036
1037       if (clist->column[ctree->tree_column].visible)
1038         switch (clist->column[ctree->tree_column].justification)
1039           {
1040           case GTK_JUSTIFY_CENTER:
1041           case GTK_JUSTIFY_FILL:
1042           case GTK_JUSTIFY_LEFT:
1043             points[0].x =  COLUMN_LEFT_XPIXEL(clist, ctree->tree_column) + 
1044               ctree->tree_indent * level - (ctree->tree_indent - PM_SIZE) / 2;
1045             points[0].y = y;
1046             points[3].x = points[0].x;
1047             points[3].y = y - clist->row_height - 1;
1048             points[1].x = clist->clist_window_width - 1;
1049             points[1].y = points[0].y;
1050             points[2].x = points[1].x;
1051             points[2].y = points[3].y;
1052
1053             for (i = 0; i < 3; i++)
1054               gdk_draw_line (clist->clist_window, clist->xor_gc,
1055                              points[i].x, points[i].y,
1056                              points[i+1].x, points[i+1].y);
1057
1058             if (ctree->tree_column > 0)
1059               {
1060                 points[0].x = COLUMN_LEFT_XPIXEL(clist,
1061                                                  ctree->tree_column - 1) +
1062                   clist->column[ctree->tree_column - 1].area.width ;
1063                 points[0].y = y;
1064                 points[3].x = points[0].x;
1065                 points[3].y = y - clist->row_height - 1;
1066                 points[1].x = 0;
1067                 points[1].y = points[0].y;
1068                 points[2].x = 0;
1069                 points[2].y = points[3].y;
1070
1071                 for (i = 0; i < 3; i++)
1072                   gdk_draw_line (clist->clist_window, clist->xor_gc,
1073                                  points[i].x, points[i].y, points[i+1].x, 
1074                                  points[i+1].y);
1075               }
1076             break;
1077           case GTK_JUSTIFY_RIGHT:
1078             points[0].x =  COLUMN_LEFT_XPIXEL(clist, ctree->tree_column) - 
1079               ctree->tree_indent * level + (ctree->tree_indent - PM_SIZE) / 2 +
1080               clist->column[ctree->tree_column].area.width;
1081             points[0].y = y;
1082             points[3].x = points[0].x;
1083             points[3].y = y - clist->row_height - 1;
1084             points[1].x = 0;
1085             points[1].y = points[0].y;
1086             points[2].x = 0;
1087             points[2].y = points[3].y;
1088
1089             for (i = 0; i < 3; i++)
1090               gdk_draw_line (clist->clist_window, clist->xor_gc,
1091                              points[i].x, points[i].y,
1092                              points[i+1].x, points[i+1].y);
1093
1094             if (ctree->tree_column < clist->columns - 1)
1095               {
1096                 points[0].x = COLUMN_LEFT_XPIXEL(clist, ctree->tree_column +1);
1097                 points[0].y = y;
1098                 points[3].x = points[0].x;
1099                 points[3].y = y - clist->row_height - 1;
1100                 points[1].x = clist->clist_window_width - 1;
1101                 points[1].y = points[0].y;
1102                 points[2].x = points[1].x;
1103                 points[2].y = points[3].y;
1104
1105                 for (i = 0; i < 3; i++)
1106                   gdk_draw_line (clist->clist_window, clist->xor_gc,
1107                                  points[i].x, points[i].y,
1108                                  points[i+1].x, points[i+1].y);
1109               }
1110             break;
1111           }
1112       else
1113         gdk_draw_rectangle (clist->clist_window, clist->xor_gc, FALSE,
1114                             0, y - clist->row_height,
1115                             clist->clist_window_width - 1, clist->row_height);
1116       break;
1117     }
1118 }
1119
1120 static gint
1121 draw_cell_pixbuf (GdkWindow    *window,
1122                   GdkRectangle *clip_rectangle,
1123                   GdkGC        *fg_gc,
1124                   GdkPixbuf    *pixbuf,
1125                   gint          x,
1126                   gint          y,
1127                   gint          width,
1128                   gint          height)
1129 {
1130   gint xsrc = 0;
1131   gint ysrc = 0;
1132
1133   gdk_gc_set_clip_origin (fg_gc, x, y);
1134   if (x < clip_rectangle->x)
1135     {
1136       xsrc = clip_rectangle->x - x;
1137       width -= xsrc;
1138       x = clip_rectangle->x;
1139     }
1140   if (x + width > clip_rectangle->x + clip_rectangle->width)
1141     width = clip_rectangle->x + clip_rectangle->width - x;
1142
1143   if (y < clip_rectangle->y)
1144     {
1145       ysrc = clip_rectangle->y - y;
1146       height -= ysrc;
1147       y = clip_rectangle->y;
1148     }
1149   if (y + height > clip_rectangle->y + clip_rectangle->height)
1150     height = clip_rectangle->y + clip_rectangle->height - y;
1151
1152   if (width > 0 && height > 0)
1153     gdk_draw_pixbuf (window, fg_gc, pixbuf, xsrc, ysrc, x, y, width, height, GDK_RGB_DITHER_NONE, 0, 0);
1154
1155   gdk_gc_set_clip_origin (fg_gc, 0, 0);
1156
1157   return x + MAX (width, 0);
1158 }
1159
1160 static void
1161 get_cell_style (GtkCMCList     *clist,
1162                 GtkCMCListRow  *clist_row,
1163                 gint          state,
1164                 gint          column,
1165                 GtkStyle    **style,
1166                 GdkGC       **fg_gc,
1167                 GdkGC       **bg_gc)
1168 {
1169   gint fg_state;
1170
1171   if ((state == GTK_STATE_NORMAL) &&
1172       (GTK_WIDGET (clist)->state == GTK_STATE_INSENSITIVE))
1173     fg_state = GTK_STATE_INSENSITIVE;
1174   else
1175     fg_state = state;
1176
1177   if (clist_row->cell[column].style)
1178     {
1179       if (style)
1180         *style = clist_row->cell[column].style;
1181       if (fg_gc)
1182         *fg_gc = clist_row->cell[column].style->fg_gc[fg_state];
1183       if (bg_gc) {
1184         if (state == GTK_STATE_SELECTED)
1185           *bg_gc = clist_row->cell[column].style->bg_gc[state];
1186         else
1187           *bg_gc = clist_row->cell[column].style->base_gc[state];
1188       }
1189     }
1190   else if (clist_row->style)
1191     {
1192       if (style)
1193         *style = clist_row->style;
1194       if (fg_gc)
1195         *fg_gc = clist_row->style->fg_gc[fg_state];
1196       if (bg_gc) {
1197         if (state == GTK_STATE_SELECTED)
1198           *bg_gc = clist_row->style->bg_gc[state];
1199         else
1200           *bg_gc = clist_row->style->base_gc[state];
1201       }
1202     }
1203   else
1204     {
1205       if (style)
1206         *style = GTK_WIDGET (clist)->style;
1207       if (fg_gc)
1208         *fg_gc = GTK_WIDGET (clist)->style->fg_gc[fg_state];
1209       if (bg_gc) {
1210         if (state == GTK_STATE_SELECTED)
1211           *bg_gc = GTK_WIDGET (clist)->style->bg_gc[state];
1212         else
1213           *bg_gc = GTK_WIDGET (clist)->style->base_gc[state];
1214       }
1215
1216       if (state != GTK_STATE_SELECTED)
1217         {
1218           if (fg_gc && clist_row->fg_set)
1219             *fg_gc = clist->fg_gc;
1220           if (bg_gc && clist_row->bg_set)
1221             *bg_gc = clist->bg_gc;
1222         }
1223     }
1224 }
1225
1226 static gint
1227 gtk_cmctree_draw_expander (GtkCMCTree     *ctree,
1228                          GtkCMCTreeRow  *ctree_row,
1229                          GtkStyle     *style,
1230                          GdkRectangle *clip_rectangle,
1231                          gint          x)
1232 {
1233   GtkCMCList *clist;
1234   GdkPoint points[3];
1235   gint justification_factor;
1236   gint y;
1237
1238  if (ctree->expander_style == GTK_CMCTREE_EXPANDER_NONE)
1239    return x;
1240
1241   clist = GTK_CMCLIST (ctree);
1242   if (clist->column[ctree->tree_column].justification == GTK_JUSTIFY_RIGHT)
1243     justification_factor = -1;
1244   else
1245     justification_factor = 1;
1246   y = (clip_rectangle->y + (clip_rectangle->height - PM_SIZE) / 2 -
1247        (clip_rectangle->height + 1) % 2);
1248
1249   if (!ctree_row->children)
1250     {
1251       switch (ctree->expander_style)
1252         {
1253         case GTK_CMCTREE_EXPANDER_NONE:
1254           return x;
1255         case GTK_CMCTREE_EXPANDER_TRIANGLE:
1256           return x + justification_factor * (PM_SIZE + 3);
1257         case GTK_CMCTREE_EXPANDER_SQUARE:
1258         case GTK_CMCTREE_EXPANDER_CIRCULAR:
1259           return x + justification_factor * (PM_SIZE + 1);
1260         }
1261     }
1262
1263   gdk_gc_set_clip_rectangle (style->fg_gc[GTK_STATE_NORMAL], clip_rectangle);
1264   gdk_gc_set_clip_rectangle (style->base_gc[GTK_STATE_NORMAL], clip_rectangle);
1265
1266   switch (ctree->expander_style)
1267     {
1268     case GTK_CMCTREE_EXPANDER_NONE:
1269       break;
1270     case GTK_CMCTREE_EXPANDER_TRIANGLE:
1271       if (ctree_row->expanded)
1272         {
1273           points[0].x = x;
1274           points[0].y = y + (PM_SIZE + 2) / 6;
1275           points[1].x = points[0].x + justification_factor * (PM_SIZE + 2);
1276           points[1].y = points[0].y;
1277           points[2].x = (points[0].x +
1278                          justification_factor * (PM_SIZE + 2) / 2);
1279           points[2].y = y + 2 * (PM_SIZE + 2) / 3;
1280         }
1281       else
1282         {
1283           points[0].x = x + justification_factor * ((PM_SIZE + 2) / 6 + 2);
1284           points[0].y = y - 1;
1285           points[1].x = points[0].x;
1286           points[1].y = points[0].y + (PM_SIZE + 2);
1287           points[2].x = (points[0].x +
1288                          justification_factor * (2 * (PM_SIZE + 2) / 3 - 1));
1289           points[2].y = points[0].y + (PM_SIZE + 2) / 2;
1290         }
1291
1292       gdk_draw_polygon (clist->clist_window, style->base_gc[GTK_STATE_NORMAL],
1293                         TRUE, points, 3);
1294       gdk_draw_polygon (clist->clist_window, style->fg_gc[GTK_STATE_NORMAL],
1295                         FALSE, points, 3);
1296
1297       x += justification_factor * (PM_SIZE + 3);
1298       break;
1299     case GTK_CMCTREE_EXPANDER_SQUARE:
1300     case GTK_CMCTREE_EXPANDER_CIRCULAR:
1301       if (justification_factor == -1)
1302         x += justification_factor * (PM_SIZE + 1);
1303
1304       if (ctree->expander_style == GTK_CMCTREE_EXPANDER_CIRCULAR)
1305         {
1306           gdk_draw_arc (clist->clist_window, style->base_gc[GTK_STATE_NORMAL],
1307                         TRUE, x, y, PM_SIZE, PM_SIZE, 0, 360 * 64);
1308           gdk_draw_arc (clist->clist_window, style->fg_gc[GTK_STATE_NORMAL],
1309                         FALSE, x, y, PM_SIZE, PM_SIZE, 0, 360 * 64);
1310         }
1311       else
1312         {
1313           gdk_draw_rectangle (clist->clist_window,
1314                               style->base_gc[GTK_STATE_NORMAL], TRUE,
1315                               x, y, PM_SIZE, PM_SIZE);
1316           gdk_draw_rectangle (clist->clist_window,
1317                               style->fg_gc[GTK_STATE_NORMAL], FALSE,
1318                               x, y, PM_SIZE, PM_SIZE);
1319         }
1320
1321       gdk_draw_line (clist->clist_window, style->fg_gc[GTK_STATE_NORMAL], 
1322                      x + 2, y + PM_SIZE / 2, x + PM_SIZE - 2, y + PM_SIZE / 2);
1323
1324       if (!ctree_row->expanded)
1325         gdk_draw_line (clist->clist_window, style->fg_gc[GTK_STATE_NORMAL],
1326                        x + PM_SIZE / 2, y + 2,
1327                        x + PM_SIZE / 2, y + PM_SIZE - 2);
1328
1329       if (justification_factor == 1)
1330         x += justification_factor * (PM_SIZE + 1);
1331       break;
1332     }
1333
1334   gdk_gc_set_clip_rectangle (style->fg_gc[GTK_STATE_NORMAL], NULL);
1335   gdk_gc_set_clip_rectangle (style->base_gc[GTK_STATE_NORMAL], NULL);
1336
1337   return x;
1338 }
1339
1340
1341 static gint
1342 gtk_cmctree_draw_lines (GtkCMCTree     *ctree,
1343                       GtkCMCTreeRow  *ctree_row,
1344                       gint          row,
1345                       gint          column,
1346                       gint          state,
1347                       GdkRectangle *clip_rectangle,
1348                       GdkRectangle *cell_rectangle,
1349                       GdkRectangle *crect,
1350                       GdkRectangle *area,
1351                       GtkStyle     *style)
1352 {
1353   GtkCMCList *clist;
1354   GtkCMCTreeNode *node;
1355   GtkCMCTreeNode *parent;
1356   GdkRectangle tree_rectangle;
1357   GdkRectangle tc_rectangle;
1358   GdkGC *bg_gc;
1359   gint offset;
1360   gint offset_x;
1361   gint offset_y;
1362   gint xcenter;
1363   gint ycenter;
1364   gint next_level;
1365   gint column_right;
1366   gint column_left;
1367   gint justify_right;
1368   gint justification_factor;
1369   
1370   clist = GTK_CMCLIST (ctree);
1371   ycenter = clip_rectangle->y + (clip_rectangle->height / 2);
1372   justify_right = (clist->column[column].justification == GTK_JUSTIFY_RIGHT);
1373
1374   if (justify_right)
1375     {
1376       offset = (clip_rectangle->x + clip_rectangle->width - 1 -
1377                 ctree->tree_indent * (ctree_row->level - 1));
1378       justification_factor = -1;
1379     }
1380   else
1381     {
1382       offset = clip_rectangle->x + ctree->tree_indent * (ctree_row->level - 1);
1383       justification_factor = 1;
1384     }
1385
1386   switch (ctree->line_style)
1387     {
1388     case GTK_CMCTREE_LINES_NONE:
1389       break;
1390     case GTK_CMCTREE_LINES_TABBED:
1391       xcenter = offset + justification_factor * TAB_SIZE;
1392
1393       column_right = (COLUMN_LEFT_XPIXEL (clist, ctree->tree_column) +
1394                       clist->column[ctree->tree_column].area.width +
1395                       COLUMN_INSET);
1396       column_left = (COLUMN_LEFT_XPIXEL (clist, ctree->tree_column) -
1397                      COLUMN_INSET - CELL_SPACING);
1398
1399       if (area)
1400         {
1401           tree_rectangle.y = crect->y;
1402           tree_rectangle.height = crect->height;
1403
1404           if (justify_right)
1405             {
1406               tree_rectangle.x = xcenter;
1407               tree_rectangle.width = column_right - xcenter;
1408             }
1409           else
1410             {
1411               tree_rectangle.x = column_left;
1412               tree_rectangle.width = xcenter - column_left;
1413             }
1414
1415           if (!gdk_rectangle_intersect (area, &tree_rectangle, &tc_rectangle))
1416             {
1417               offset += justification_factor * 3;
1418               break;
1419             }
1420         }
1421
1422       gdk_gc_set_clip_rectangle (ctree->lines_gc, crect);
1423
1424       next_level = ctree_row->level;
1425
1426       if (!ctree_row->sibling || (ctree_row->children && ctree_row->expanded))
1427         {
1428           node = gtk_cmctree_find_node_ptr (ctree, ctree_row);
1429           if (GTK_CMCTREE_NODE_NEXT (node))
1430             next_level = GTK_CMCTREE_ROW (GTK_CMCTREE_NODE_NEXT (node))->level;
1431           else
1432             next_level = 0;
1433         }
1434
1435       if (ctree->tree_indent > 0)
1436         {
1437           node = ctree_row->parent;
1438           while (node)
1439             {
1440               xcenter -= (justification_factor * ctree->tree_indent);
1441
1442               if ((justify_right && xcenter < column_left) ||
1443                   (!justify_right && xcenter > column_right))
1444                 {
1445                   node = GTK_CMCTREE_ROW (node)->parent;
1446                   continue;
1447                 }
1448
1449               tree_rectangle.y = cell_rectangle->y;
1450               tree_rectangle.height = cell_rectangle->height;
1451               if (justify_right)
1452                 {
1453                   tree_rectangle.x = MAX (xcenter - ctree->tree_indent + 1,
1454                                           column_left);
1455                   tree_rectangle.width = MIN (xcenter - column_left,
1456                                               ctree->tree_indent);
1457                 }
1458               else
1459                 {
1460                   tree_rectangle.x = xcenter;
1461                   tree_rectangle.width = MIN (column_right - xcenter,
1462                                               ctree->tree_indent);
1463                 }
1464
1465               if (!area || gdk_rectangle_intersect (area, &tree_rectangle,
1466                                                     &tc_rectangle))
1467                 {
1468                   get_cell_style (clist, &GTK_CMCTREE_ROW (node)->row,
1469                                   state, column, NULL, NULL, &bg_gc);
1470
1471                   if (bg_gc == clist->bg_gc)
1472                     gdk_gc_set_foreground
1473                       (clist->bg_gc, &GTK_CMCTREE_ROW (node)->row.background);
1474
1475                   if (!area)
1476                     gdk_draw_rectangle (clist->clist_window, bg_gc, TRUE,
1477                                         tree_rectangle.x,
1478                                         tree_rectangle.y,
1479                                         tree_rectangle.width,
1480                                         tree_rectangle.height);
1481                   else 
1482                     gdk_draw_rectangle (clist->clist_window, bg_gc, TRUE,
1483                                         tc_rectangle.x,
1484                                         tc_rectangle.y,
1485                                         tc_rectangle.width,
1486                                         tc_rectangle.height);
1487                 }
1488               if (next_level > GTK_CMCTREE_ROW (node)->level)
1489                 gdk_draw_line (clist->clist_window, ctree->lines_gc,
1490                                xcenter, crect->y,
1491                                xcenter, crect->y + crect->height);
1492               else
1493                 {
1494                   gint width;
1495
1496                   offset_x = MIN (ctree->tree_indent, 2 * TAB_SIZE);
1497                   width = offset_x / 2 + offset_x % 2;
1498
1499                   parent = GTK_CMCTREE_ROW (node)->parent;
1500
1501                   tree_rectangle.y = ycenter;
1502                   tree_rectangle.height = (cell_rectangle->y - ycenter +
1503                                            cell_rectangle->height);
1504
1505                   if (justify_right)
1506                     {
1507                       tree_rectangle.x = MAX(xcenter + 1 - width, column_left);
1508                       tree_rectangle.width = MIN (xcenter + 1 - column_left,
1509                                                   width);
1510                     }
1511                   else
1512                     {
1513                       tree_rectangle.x = xcenter;
1514                       tree_rectangle.width = MIN (column_right - xcenter,
1515                                                   width);
1516                     }
1517
1518                   if (!area ||
1519                       gdk_rectangle_intersect (area, &tree_rectangle,
1520                                                &tc_rectangle))
1521                     {
1522                       if (parent)
1523                         {
1524                           get_cell_style (clist, &GTK_CMCTREE_ROW (parent)->row,
1525                                           state, column, NULL, NULL, &bg_gc);
1526                           if (bg_gc == clist->bg_gc)
1527                             gdk_gc_set_foreground
1528                               (clist->bg_gc,
1529                                &GTK_CMCTREE_ROW (parent)->row.background);
1530                         }
1531                       else if (state == GTK_STATE_SELECTED)
1532                         bg_gc = style->base_gc[state];
1533                       else
1534                         bg_gc = GTK_WIDGET (clist)->style->base_gc[state];
1535
1536                       if (!area)
1537                         gdk_draw_rectangle (clist->clist_window, bg_gc, TRUE,
1538                                             tree_rectangle.x,
1539                                             tree_rectangle.y,
1540                                             tree_rectangle.width,
1541                                             tree_rectangle.height);
1542                       else
1543                         gdk_draw_rectangle (clist->clist_window,
1544                                             bg_gc, TRUE,
1545                                             tc_rectangle.x,
1546                                             tc_rectangle.y,
1547                                             tc_rectangle.width,
1548                                             tc_rectangle.height);
1549                     }
1550
1551                   get_cell_style (clist, &GTK_CMCTREE_ROW (node)->row,
1552                                   state, column, NULL, NULL, &bg_gc);
1553                   if (bg_gc == clist->bg_gc)
1554                     gdk_gc_set_foreground
1555                       (clist->bg_gc, &GTK_CMCTREE_ROW (node)->row.background);
1556
1557                   gdk_gc_set_clip_rectangle (bg_gc, crect);
1558                   gdk_draw_arc (clist->clist_window, bg_gc, TRUE,
1559                                 xcenter - (justify_right * offset_x),
1560                                 cell_rectangle->y,
1561                                 offset_x, clist->row_height,
1562                                 (180 + (justify_right * 90)) * 64, 90 * 64);
1563                   gdk_gc_set_clip_rectangle (bg_gc, NULL);
1564
1565                   gdk_draw_line (clist->clist_window, ctree->lines_gc, 
1566                                  xcenter, cell_rectangle->y, xcenter, ycenter);
1567
1568                   if (justify_right)
1569                     gdk_draw_arc (clist->clist_window, ctree->lines_gc, FALSE,
1570                                   xcenter - offset_x, cell_rectangle->y,
1571                                   offset_x, clist->row_height,
1572                                   270 * 64, 90 * 64);
1573                   else
1574                     gdk_draw_arc (clist->clist_window, ctree->lines_gc, FALSE,
1575                                   xcenter, cell_rectangle->y,
1576                                   offset_x, clist->row_height,
1577                                   180 * 64, 90 * 64);
1578                 }
1579               node = GTK_CMCTREE_ROW (node)->parent;
1580             }
1581         }
1582
1583       if (state != GTK_STATE_SELECTED)
1584         {
1585           tree_rectangle.y = clip_rectangle->y;
1586           tree_rectangle.height = clip_rectangle->height;
1587           tree_rectangle.width = COLUMN_INSET + CELL_SPACING +
1588             MIN (clist->column[ctree->tree_column].area.width + COLUMN_INSET,
1589                  TAB_SIZE);
1590
1591           if (justify_right)
1592             tree_rectangle.x = MAX (xcenter + 1, column_left);
1593           else
1594             tree_rectangle.x = column_left;
1595
1596           if (!area)
1597             gdk_draw_rectangle (clist->clist_window,
1598                                 GTK_WIDGET
1599                                 (ctree)->style->base_gc[GTK_STATE_NORMAL],
1600                                 TRUE,
1601                                 tree_rectangle.x,
1602                                 tree_rectangle.y,
1603                                 tree_rectangle.width,
1604                                 tree_rectangle.height);
1605           else if (gdk_rectangle_intersect (area, &tree_rectangle,
1606                                             &tc_rectangle))
1607             gdk_draw_rectangle (clist->clist_window,
1608                                 GTK_WIDGET
1609                                 (ctree)->style->base_gc[GTK_STATE_NORMAL],
1610                                 TRUE,
1611                                 tc_rectangle.x,
1612                                 tc_rectangle.y,
1613                                 tc_rectangle.width,
1614                                 tc_rectangle.height);
1615         }
1616
1617       xcenter = offset + (justification_factor * ctree->tree_indent / 2);
1618
1619       get_cell_style (clist, &ctree_row->row, state, column, NULL, NULL,
1620                       &bg_gc);
1621       if (bg_gc == clist->bg_gc)
1622         gdk_gc_set_foreground (clist->bg_gc, &ctree_row->row.background);
1623
1624       gdk_gc_set_clip_rectangle (bg_gc, crect);
1625       if (ctree_row->is_leaf)
1626         {
1627           GdkPoint points[6];
1628
1629           points[0].x = offset + justification_factor * TAB_SIZE;
1630           points[0].y = cell_rectangle->y;
1631
1632           points[1].x = points[0].x - justification_factor * 4;
1633           points[1].y = points[0].y;
1634
1635           points[2].x = points[1].x - justification_factor * 2;
1636           points[2].y = points[1].y + 3;
1637
1638           points[3].x = points[2].x;
1639           points[3].y = points[2].y + clist->row_height - 5;
1640
1641           points[4].x = points[3].x + justification_factor * 2;
1642           points[4].y = points[3].y + 3;
1643
1644           points[5].x = points[4].x + justification_factor * 4;
1645           points[5].y = points[4].y;
1646
1647           gdk_draw_polygon (clist->clist_window, bg_gc, TRUE, points, 6);
1648           gdk_draw_lines (clist->clist_window, ctree->lines_gc, points, 6);
1649         }
1650       else 
1651         {
1652           gdk_draw_arc (clist->clist_window, bg_gc, TRUE,
1653                         offset - (justify_right * 2 * TAB_SIZE),
1654                         cell_rectangle->y,
1655                         2 * TAB_SIZE, clist->row_height,
1656                         (90 + (180 * justify_right)) * 64, 180 * 64);
1657           gdk_draw_arc (clist->clist_window, ctree->lines_gc, FALSE,
1658                         offset - (justify_right * 2 * TAB_SIZE),
1659                         cell_rectangle->y,
1660                         2 * TAB_SIZE, clist->row_height,
1661                         (90 + (180 * justify_right)) * 64, 180 * 64);
1662         }
1663       gdk_gc_set_clip_rectangle (bg_gc, NULL);
1664       gdk_gc_set_clip_rectangle (ctree->lines_gc, NULL);
1665
1666       offset += justification_factor * 3;
1667       break;
1668     default:
1669       xcenter = offset + justification_factor * PM_SIZE / 2;
1670
1671       if (area)
1672         {
1673           tree_rectangle.y = crect->y;
1674           tree_rectangle.height = crect->height;
1675
1676           if (justify_right)
1677             {
1678               tree_rectangle.x = xcenter - PM_SIZE / 2 - 2;
1679               tree_rectangle.width = (clip_rectangle->x +
1680                                       clip_rectangle->width -tree_rectangle.x);
1681             }
1682           else
1683             {
1684               tree_rectangle.x = clip_rectangle->x + PM_SIZE / 2;
1685               tree_rectangle.width = (xcenter + PM_SIZE / 2 + 2 -
1686                                       clip_rectangle->x);
1687             }
1688
1689           if (!gdk_rectangle_intersect (area, &tree_rectangle, &tc_rectangle))
1690             break;
1691         }
1692
1693       offset_x = 1;
1694       offset_y = 0;
1695       if (ctree->line_style == GTK_CMCTREE_LINES_DOTTED)
1696         {
1697           offset_x += abs((clip_rectangle->x + clist->hoffset) % 2);
1698           offset_y  = abs((cell_rectangle->y + clist->voffset) % 2);
1699         }
1700
1701       clip_rectangle->y--;
1702       clip_rectangle->height++;
1703       gdk_gc_set_clip_rectangle (ctree->lines_gc, clip_rectangle);
1704       gdk_draw_line (clist->clist_window, ctree->lines_gc,
1705                      xcenter,
1706                      (ctree->show_stub || clist->row_list->data != ctree_row) ?
1707                      cell_rectangle->y + offset_y : ycenter,
1708                      xcenter,
1709                      (ctree_row->sibling) ? crect->y +crect->height : ycenter);
1710
1711       gdk_draw_line (clist->clist_window, ctree->lines_gc,
1712                      xcenter + (justification_factor * offset_x), ycenter,
1713                      xcenter + (justification_factor * (PM_SIZE / 2 + 2)),
1714                      ycenter);
1715
1716       node = ctree_row->parent;
1717       while (node)
1718         {
1719           xcenter -= (justification_factor * ctree->tree_indent);
1720
1721           if (GTK_CMCTREE_ROW (node)->sibling)
1722             gdk_draw_line (clist->clist_window, ctree->lines_gc, 
1723                            xcenter, cell_rectangle->y + offset_y,
1724                            xcenter, crect->y + crect->height);
1725           node = GTK_CMCTREE_ROW (node)->parent;
1726         }
1727       gdk_gc_set_clip_rectangle (ctree->lines_gc, NULL);
1728       clip_rectangle->y++;
1729       clip_rectangle->height--;
1730       break;
1731     }
1732   return offset;
1733 }
1734
1735 static void
1736 draw_row (GtkCMCList     *clist,
1737           GdkRectangle *area,
1738           gint          row,
1739           GtkCMCListRow  *clist_row)
1740 {
1741   GtkWidget *widget;
1742   GtkCMCTree  *ctree;
1743   GdkRectangle *crect;
1744   GdkRectangle row_rectangle;
1745   GdkRectangle cell_rectangle; 
1746   GdkRectangle clip_rectangle;
1747   GdkRectangle intersect_rectangle;
1748   gint last_column;
1749   gint column_left = 0;
1750   gint column_right = 0;
1751   gint offset = 0;
1752   gint state;
1753   gint i;
1754
1755   cm_return_if_fail (clist != NULL);
1756
1757   /* bail now if we arn't drawable yet */
1758   if (!gtkut_widget_is_drawable (GTK_WIDGET(clist)) || row < 0 || row >= clist->rows)
1759     return;
1760
1761   widget = GTK_WIDGET (clist);
1762   ctree  = GTK_CMCTREE  (clist);
1763
1764   /* if the function is passed the pointer to the row instead of null,
1765    * it avoids this expensive lookup */
1766   if (!clist_row)
1767     clist_row = (g_list_nth (clist->row_list, row))->data;
1768
1769   /* rectangle of the entire row */
1770   row_rectangle.x = 0;
1771   row_rectangle.y = ROW_TOP_YPIXEL (clist, row);
1772   row_rectangle.width = clist->clist_window_width;
1773   row_rectangle.height = clist->row_height;
1774
1775   /* rectangle of the cell spacing above the row */
1776   cell_rectangle.x = 0;
1777   cell_rectangle.y = row_rectangle.y - CELL_SPACING;
1778   cell_rectangle.width = row_rectangle.width;
1779   cell_rectangle.height = CELL_SPACING;
1780
1781   /* rectangle used to clip drawing operations, its y and height
1782    * positions only need to be set once, so we set them once here. 
1783    * the x and width are set withing the drawing loop below once per
1784    * column */
1785   clip_rectangle.y = row_rectangle.y;
1786   clip_rectangle.height = row_rectangle.height;
1787
1788   if (clist_row->state == GTK_STATE_NORMAL)
1789     {
1790       if (clist_row->fg_set)
1791         gdk_gc_set_foreground (clist->fg_gc, &clist_row->foreground);
1792       if (clist_row->bg_set)
1793         gdk_gc_set_foreground (clist->bg_gc, &clist_row->background);
1794     }
1795   
1796   state = clist_row->state;
1797
1798   gdk_gc_set_foreground (ctree->lines_gc,
1799                          &widget->style->fg[clist_row->state]);
1800
1801   /* draw the cell borders */
1802   if (area)
1803     {
1804       crect = &intersect_rectangle;
1805
1806       if (gdk_rectangle_intersect (area, &cell_rectangle, crect))
1807         gdk_draw_rectangle (clist->clist_window,
1808                             widget->style->base_gc[GTK_STATE_NORMAL], TRUE,
1809                             crect->x, crect->y, crect->width, crect->height);
1810     }
1811   else
1812     {
1813       crect = &cell_rectangle;
1814
1815       gdk_draw_rectangle (clist->clist_window,
1816                           widget->style->base_gc[GTK_STATE_NORMAL], TRUE,
1817                           crect->x, crect->y, crect->width, crect->height);
1818     }
1819
1820   /* horizontal black lines */
1821   if (ctree->line_style == GTK_CMCTREE_LINES_TABBED)
1822     { 
1823
1824       column_right = (COLUMN_LEFT_XPIXEL (clist, ctree->tree_column) +
1825                       clist->column[ctree->tree_column].area.width +
1826                       COLUMN_INSET);
1827       column_left = (COLUMN_LEFT_XPIXEL (clist, ctree->tree_column) -
1828                      COLUMN_INSET - (ctree->tree_column != 0) * CELL_SPACING);
1829
1830       switch (clist->column[ctree->tree_column].justification)
1831         {
1832         case GTK_JUSTIFY_CENTER:
1833         case GTK_JUSTIFY_FILL:
1834         case GTK_JUSTIFY_LEFT:
1835           offset = (column_left + ctree->tree_indent *
1836                     (((GtkCMCTreeRow *)clist_row)->level - 1));
1837
1838           gdk_draw_line (clist->clist_window, ctree->lines_gc, 
1839                          MIN (offset + TAB_SIZE, column_right),
1840                          cell_rectangle.y,
1841                          clist->clist_window_width, cell_rectangle.y);
1842           break;
1843         case GTK_JUSTIFY_RIGHT:
1844           offset = (column_right - 1 - ctree->tree_indent *
1845                     (((GtkCMCTreeRow *)clist_row)->level - 1));
1846
1847           gdk_draw_line (clist->clist_window, ctree->lines_gc,
1848                          -1, cell_rectangle.y,
1849                          MAX (offset - TAB_SIZE, column_left),
1850                          cell_rectangle.y);
1851           break;
1852         }
1853     }
1854
1855   /* the last row has to clear its bottom cell spacing too */
1856   if (clist_row == clist->row_list_end->data)
1857     {
1858       cell_rectangle.y += clist->row_height + CELL_SPACING;
1859
1860       if (!area || gdk_rectangle_intersect (area, &cell_rectangle, crect))
1861         {
1862           gdk_draw_rectangle (clist->clist_window,
1863                               widget->style->base_gc[GTK_STATE_NORMAL], TRUE,
1864                               crect->x, crect->y, crect->width, crect->height);
1865
1866           /* horizontal black lines */
1867           if (ctree->line_style == GTK_CMCTREE_LINES_TABBED)
1868             { 
1869               switch (clist->column[ctree->tree_column].justification)
1870                 {
1871                 case GTK_JUSTIFY_CENTER:
1872                 case GTK_JUSTIFY_FILL:
1873                 case GTK_JUSTIFY_LEFT:
1874                   gdk_draw_line (clist->clist_window, ctree->lines_gc, 
1875                                  MIN (column_left + TAB_SIZE + COLUMN_INSET +
1876                                       (((GtkCMCTreeRow *)clist_row)->level > 1) *
1877                                       MIN (ctree->tree_indent / 2, TAB_SIZE),
1878                                       column_right),
1879                                  cell_rectangle.y,
1880                                  clist->clist_window_width, cell_rectangle.y);
1881                   break;
1882                 case GTK_JUSTIFY_RIGHT:
1883                   gdk_draw_line (clist->clist_window, ctree->lines_gc, 
1884                                  -1, cell_rectangle.y,
1885                                  MAX (column_right - TAB_SIZE - 1 -
1886                                       COLUMN_INSET -
1887                                       (((GtkCMCTreeRow *)clist_row)->level > 1) *
1888                                       MIN (ctree->tree_indent / 2, TAB_SIZE),
1889                                       column_left - 1), cell_rectangle.y);
1890                   break;
1891                 }
1892             }
1893         }
1894     }     
1895
1896   for (last_column = clist->columns - 1;
1897        last_column >= 0 && !clist->column[last_column].visible; last_column--)
1898     ;
1899
1900   /* iterate and draw all the columns (row cells) and draw their contents */
1901   for (i = 0; i < clist->columns; i++)
1902     {
1903       GtkStyle *style;
1904       GdkGC *fg_gc; 
1905       GdkGC *bg_gc;
1906       PangoLayout *layout = NULL;
1907       PangoRectangle logical_rect;
1908
1909       gint width;
1910       gint height;
1911       gint pixbuf_width;
1912       gint string_width;
1913       gint old_offset;
1914
1915       if (!clist->column[i].visible)
1916         continue;
1917
1918       get_cell_style (clist, clist_row, state, i, &style, &fg_gc, &bg_gc);
1919
1920       /* calculate clipping region */
1921       clip_rectangle.x = clist->column[i].area.x + clist->hoffset;
1922       clip_rectangle.width = clist->column[i].area.width;
1923
1924       cell_rectangle.x = clip_rectangle.x - COLUMN_INSET - CELL_SPACING;
1925       cell_rectangle.width = (clip_rectangle.width + 2 * COLUMN_INSET +
1926                               (1 + (i == last_column)) * CELL_SPACING);
1927       cell_rectangle.y = clip_rectangle.y;
1928       cell_rectangle.height = clip_rectangle.height;
1929
1930       string_width = 0;
1931       pixbuf_width = 0;
1932       height = 0;
1933
1934       if (area && !gdk_rectangle_intersect (area, &cell_rectangle,
1935                                             &intersect_rectangle))
1936         {
1937           if (i != ctree->tree_column)
1938             continue;
1939         }
1940       else
1941         {
1942           gdk_draw_rectangle (clist->clist_window, bg_gc, TRUE,
1943                               crect->x, crect->y, crect->width, crect->height);
1944
1945
1946           layout = _gtk_cmclist_create_cell_layout (clist, clist_row, i);
1947           if (layout)
1948             {
1949               pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
1950               width = logical_rect.width;
1951             }
1952           else
1953             width = 0;
1954
1955           switch (clist_row->cell[i].type)
1956             {
1957             case GTK_CMCELL_PIXBUF:
1958               pixbuf_width = gdk_pixbuf_get_width(GTK_CMCELL_PIXBUF (clist_row->cell[i])->pixbuf);
1959               height = gdk_pixbuf_get_height(GTK_CMCELL_PIXBUF (clist_row->cell[i])->pixbuf);
1960               width += pixbuf_width;
1961               break;
1962             case GTK_CMCELL_PIXTEXT:
1963               if (GTK_CMCELL_PIXTEXT (clist_row->cell[i])->pixbuf)
1964                 {
1965                   pixbuf_width = gdk_pixbuf_get_width(GTK_CMCELL_PIXTEXT (clist_row->cell[i])->pixbuf);
1966                   height = gdk_pixbuf_get_height(GTK_CMCELL_PIXTEXT (clist_row->cell[i])->pixbuf);
1967                   width += pixbuf_width;
1968                 }
1969
1970               if (GTK_CMCELL_PIXTEXT (clist_row->cell[i])->text &&
1971                   GTK_CMCELL_PIXTEXT (clist_row->cell[i])->pixbuf)
1972                 width +=  GTK_CMCELL_PIXTEXT (clist_row->cell[i])->spacing;
1973
1974               if (i == ctree->tree_column)
1975                 width += (ctree->tree_indent *
1976                           ((GtkCMCTreeRow *)clist_row)->level);
1977               break;
1978             default:
1979               break;
1980             }
1981
1982           switch (clist->column[i].justification)
1983             {
1984             case GTK_JUSTIFY_LEFT:
1985               offset = clip_rectangle.x + clist_row->cell[i].horizontal;
1986               break;
1987             case GTK_JUSTIFY_RIGHT:
1988               offset = (clip_rectangle.x + clist_row->cell[i].horizontal +
1989                         clip_rectangle.width - width);
1990               break;
1991             case GTK_JUSTIFY_CENTER:
1992             case GTK_JUSTIFY_FILL:
1993               offset = (clip_rectangle.x + clist_row->cell[i].horizontal +
1994                         (clip_rectangle.width / 2) - (width / 2));
1995               break;
1996             };
1997
1998           if (i != ctree->tree_column)
1999             {
2000               offset += clist_row->cell[i].horizontal;
2001               switch (clist_row->cell[i].type)
2002                 {
2003                 case GTK_CMCELL_PIXBUF:
2004                   draw_cell_pixbuf
2005                     (clist->clist_window, &clip_rectangle, fg_gc,
2006                      GTK_CMCELL_PIXBUF (clist_row->cell[i])->pixbuf,
2007                      offset,
2008                      clip_rectangle.y + clist_row->cell[i].vertical +
2009                      (clip_rectangle.height - height) / 2,
2010                      pixbuf_width, height);
2011                   break;
2012                 case GTK_CMCELL_PIXTEXT:
2013                   offset = draw_cell_pixbuf
2014                     (clist->clist_window, &clip_rectangle, fg_gc,
2015                      GTK_CMCELL_PIXTEXT (clist_row->cell[i])->pixbuf,
2016                      offset,
2017                      clip_rectangle.y + clist_row->cell[i].vertical +
2018                      (clip_rectangle.height - height) / 2,
2019                      pixbuf_width, height);
2020                   offset += GTK_CMCELL_PIXTEXT (clist_row->cell[i])->spacing;
2021
2022                   /* Fall through */
2023                 case GTK_CMCELL_TEXT:
2024                   if (layout)
2025                     {
2026                       gint row_center_offset = (clist->row_height - logical_rect.height) / 2;
2027
2028                       gdk_gc_set_clip_rectangle (fg_gc, &clip_rectangle);
2029                       gdk_draw_layout (clist->clist_window, fg_gc,
2030                                        offset,
2031                                        row_rectangle.y + row_center_offset + clist_row->cell[i].vertical,
2032                                        layout);
2033                       gdk_gc_set_clip_rectangle (fg_gc, NULL);
2034                       g_object_unref (G_OBJECT (layout));
2035                     }
2036                   break;
2037                 default:
2038                   break;
2039                 }
2040               continue;
2041             }
2042         }
2043
2044       if (bg_gc == clist->bg_gc)
2045         gdk_gc_set_background (ctree->lines_gc, &clist_row->background);
2046
2047       /* draw ctree->tree_column */
2048       cell_rectangle.y -= CELL_SPACING;
2049       cell_rectangle.height += CELL_SPACING;
2050
2051       if (area && !gdk_rectangle_intersect (area, &cell_rectangle,
2052                                             &intersect_rectangle))
2053         {
2054           if (layout)
2055             g_object_unref (G_OBJECT (layout));
2056           continue;
2057         }
2058
2059       /* draw lines */
2060       offset = gtk_cmctree_draw_lines (ctree, (GtkCMCTreeRow *)clist_row, row, i,
2061                                      state, &clip_rectangle, &cell_rectangle,
2062                                      crect, area, style);
2063
2064       /* draw expander */
2065       offset = gtk_cmctree_draw_expander (ctree, (GtkCMCTreeRow *)clist_row,
2066                                         style, &clip_rectangle, offset);
2067
2068       if (clist->column[i].justification == GTK_JUSTIFY_RIGHT)
2069         offset -= ctree->tree_spacing;
2070       else
2071         offset += ctree->tree_spacing;
2072
2073       if (clist->column[i].justification == GTK_JUSTIFY_RIGHT)
2074         offset -= (pixbuf_width + clist_row->cell[i].horizontal);
2075       else
2076         offset += clist_row->cell[i].horizontal;
2077
2078       old_offset = offset;
2079       offset = draw_cell_pixbuf (clist->clist_window, &clip_rectangle, fg_gc,
2080                                  GTK_CMCELL_PIXTEXT (clist_row->cell[i])->pixbuf,
2081                                  offset, 
2082                                  clip_rectangle.y + clist_row->cell[i].vertical
2083                                  + (clip_rectangle.height - height) / 2,
2084                                  pixbuf_width, height);
2085
2086       if (layout)
2087         {
2088           gint row_center_offset = (clist->row_height - logical_rect.height) / 2;
2089           
2090           if (clist->column[i].justification == GTK_JUSTIFY_RIGHT)
2091             {
2092               offset = (old_offset - string_width);
2093               if (GTK_CMCELL_PIXTEXT (clist_row->cell[i])->pixbuf)
2094                 offset -= GTK_CMCELL_PIXTEXT (clist_row->cell[i])->spacing;
2095             }
2096           else
2097             {
2098               if (GTK_CMCELL_PIXTEXT (clist_row->cell[i])->pixbuf)
2099                 offset += GTK_CMCELL_PIXTEXT (clist_row->cell[i])->spacing;
2100             }
2101           
2102           gdk_gc_set_clip_rectangle (fg_gc, &clip_rectangle);
2103           gdk_draw_layout (clist->clist_window, fg_gc,
2104                            offset,
2105                            row_rectangle.y + row_center_offset + clist_row->cell[i].vertical,
2106                            layout);
2107
2108           g_object_unref (G_OBJECT (layout));
2109         }
2110       gdk_gc_set_clip_rectangle (fg_gc, NULL);
2111     }
2112
2113   /* draw focus rectangle */
2114   if (clist->focus_row == row &&
2115       gtkut_widget_get_can_focus (widget) && gtkut_widget_has_focus (widget))
2116     {
2117       if (!area)
2118         gdk_draw_rectangle (clist->clist_window, clist->xor_gc, FALSE,
2119                             row_rectangle.x, row_rectangle.y,
2120                             row_rectangle.width - 1, row_rectangle.height - 1);
2121       else if (gdk_rectangle_intersect (area, &row_rectangle,
2122                                         &intersect_rectangle))
2123         {
2124           gdk_gc_set_clip_rectangle (clist->xor_gc, &intersect_rectangle);
2125           gdk_draw_rectangle (clist->clist_window, clist->xor_gc, FALSE,
2126                               row_rectangle.x, row_rectangle.y,
2127                               row_rectangle.width - 1,
2128                               row_rectangle.height - 1);
2129           gdk_gc_set_clip_rectangle (clist->xor_gc, NULL);
2130         }
2131     }
2132 }
2133
2134 static void
2135 tree_draw_node (GtkCMCTree     *ctree, 
2136                 GtkCMCTreeNode *node)
2137 {
2138   GtkCMCList *clist;
2139   
2140   clist = GTK_CMCLIST (ctree);
2141
2142   if (CLIST_UNFROZEN (clist) && gtk_cmctree_is_viewable (ctree, node))
2143     {
2144       GtkCMCTreeNode *work;
2145       gint num = 0;
2146       
2147       work = GTK_CMCTREE_NODE (clist->row_list);
2148       while (work && work != node)
2149         {
2150           work = GTK_CMCTREE_NODE_NEXT (work);
2151           num++;
2152         }
2153       if (work && gtk_cmclist_row_is_visible (clist, num) != GTK_VISIBILITY_NONE)
2154         GTK_CMCLIST_GET_CLASS (clist)->draw_row
2155           (clist, NULL, num, GTK_CMCLIST_ROW ((GList *) node));
2156     }
2157 }
2158
2159 static GtkCMCTreeNode *
2160 gtk_cmctree_last_visible (GtkCMCTree     *ctree,
2161                         GtkCMCTreeNode *node)
2162 {
2163   GtkCMCTreeNode *work;
2164   
2165   if (!node)
2166     return NULL;
2167
2168   work = GTK_CMCTREE_ROW (node)->children;
2169
2170   if (!work || !GTK_CMCTREE_ROW (node)->expanded)
2171     return node;
2172
2173   while (GTK_CMCTREE_ROW (work)->sibling)
2174     work = GTK_CMCTREE_ROW (work)->sibling;
2175
2176   return gtk_cmctree_last_visible (ctree, work);
2177 }
2178
2179 static void
2180 gtk_cmctree_link (GtkCMCTree     *ctree,
2181                 GtkCMCTreeNode *node,
2182                 GtkCMCTreeNode *parent,
2183                 GtkCMCTreeNode *sibling,
2184                 gboolean      update_focus_row)
2185 {
2186   GtkCMCList *clist;
2187   GList *list_end;
2188   GList *list;
2189   GList *work;
2190   gboolean visible = FALSE;
2191   gint rows = 0;
2192   
2193   if (sibling)
2194     cm_return_if_fail (GTK_CMCTREE_ROW (sibling)->parent == parent);
2195   cm_return_if_fail (node != NULL);
2196   cm_return_if_fail (node != sibling);
2197   cm_return_if_fail (node != parent);
2198
2199   clist = GTK_CMCLIST (ctree);
2200
2201   if (update_focus_row && clist->selection_mode == GTK_SELECTION_MULTIPLE)
2202     {
2203       GTK_CMCLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
2204       
2205       g_list_free (clist->undo_selection);
2206       g_list_free (clist->undo_unselection);
2207       clist->undo_selection = NULL;
2208       clist->undo_unselection = NULL;
2209     }
2210
2211   for (rows = 1, list_end = (GList *)node; list_end->next;
2212        list_end = list_end->next)
2213     rows++;
2214
2215   GTK_CMCTREE_ROW (node)->parent = parent;
2216   GTK_CMCTREE_ROW (node)->sibling = sibling;
2217
2218   if (!parent || (parent && (gtk_cmctree_is_viewable (ctree, parent) &&
2219                              GTK_CMCTREE_ROW (parent)->expanded)))
2220     {
2221       visible = TRUE;
2222       clist->rows += rows;
2223     }
2224
2225   if (parent)
2226     work = (GList *)(GTK_CMCTREE_ROW (parent)->children);
2227   else
2228     work = clist->row_list;
2229
2230   if (sibling)
2231     {
2232       if (work != (GList *)sibling)
2233         {
2234           while (GTK_CMCTREE_ROW (work)->sibling != sibling)
2235             work = (GList *)(GTK_CMCTREE_ROW (work)->sibling);
2236           GTK_CMCTREE_ROW (work)->sibling = node;
2237         }
2238
2239       if (sibling == GTK_CMCTREE_NODE (clist->row_list))
2240         clist->row_list = (GList *) node;
2241       if (GTK_CMCTREE_NODE_PREV (sibling) &&
2242           GTK_CMCTREE_NODE_NEXT (GTK_CMCTREE_NODE_PREV (sibling)) == sibling)
2243         {
2244           list = (GList *)GTK_CMCTREE_NODE_PREV (sibling);
2245           list->next = (GList *)node;
2246         }
2247       
2248       list = (GList *)node;
2249       list->prev = (GList *)GTK_CMCTREE_NODE_PREV (sibling);
2250       list_end->next = (GList *)sibling;
2251       list = (GList *)sibling;
2252       list->prev = list_end;
2253       if (parent && GTK_CMCTREE_ROW (parent)->children == sibling)
2254         GTK_CMCTREE_ROW (parent)->children = node;
2255     }
2256   else
2257     {
2258       if (work)
2259         {
2260           /* find sibling */
2261           while (GTK_CMCTREE_ROW (work)->sibling)
2262             work = (GList *)(GTK_CMCTREE_ROW (work)->sibling);
2263           GTK_CMCTREE_ROW (work)->sibling = node;
2264           
2265           /* find last visible child of sibling */
2266           work = (GList *) gtk_cmctree_last_visible (ctree,
2267                                                    GTK_CMCTREE_NODE (work));
2268           
2269           list_end->next = work->next;
2270           if (work->next)
2271             list = work->next->prev = list_end;
2272           work->next = (GList *)node;
2273           list = (GList *)node;
2274           list->prev = work;
2275         }
2276       else
2277         {
2278           if (parent)
2279             {
2280               GTK_CMCTREE_ROW (parent)->children = node;
2281               list = (GList *)node;
2282               list->prev = (GList *)parent;
2283               if (GTK_CMCTREE_ROW (parent)->expanded)
2284                 {
2285                   list_end->next = (GList *)GTK_CMCTREE_NODE_NEXT (parent);
2286                   if (GTK_CMCTREE_NODE_NEXT(parent))
2287                     {
2288                       list = (GList *)GTK_CMCTREE_NODE_NEXT (parent);
2289                       list->prev = list_end;
2290                     }
2291                   list = (GList *)parent;
2292                   list->next = (GList *)node;
2293                 }
2294               else
2295                 list_end->next = NULL;
2296             }
2297           else
2298             {
2299               clist->row_list = (GList *)node;
2300               list = (GList *)node;
2301               list->prev = NULL;
2302               list_end->next = NULL;
2303             }
2304         }
2305     }
2306
2307   gtk_cmctree_pre_recursive (ctree, node, tree_update_level, NULL); 
2308
2309   if (clist->row_list_end == NULL ||
2310       clist->row_list_end->next == (GList *)node)
2311     clist->row_list_end = list_end;
2312
2313   if (visible && update_focus_row)
2314     {
2315       gint pos;
2316           
2317       pos = g_list_position (clist->row_list, (GList *)node);
2318   
2319       if (pos <= clist->focus_row)
2320         {
2321           clist->focus_row += rows;
2322           clist->undo_anchor = clist->focus_row;
2323         }
2324     }
2325 }
2326
2327 static void
2328 gtk_cmctree_unlink (GtkCMCTree     *ctree, 
2329                   GtkCMCTreeNode *node,
2330                   gboolean      update_focus_row)
2331 {
2332   GtkCMCList *clist;
2333   gint rows;
2334   gint level;
2335   gint visible;
2336   GtkCMCTreeNode *work;
2337   GtkCMCTreeNode *parent;
2338   GList *list;
2339
2340   cm_return_if_fail (GTK_IS_CMCTREE (ctree));
2341   cm_return_if_fail (node != NULL);
2342
2343   clist = GTK_CMCLIST (ctree);
2344   
2345   if (update_focus_row && clist->selection_mode == GTK_SELECTION_MULTIPLE)
2346     {
2347       GTK_CMCLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
2348       
2349       g_list_free (clist->undo_selection);
2350       g_list_free (clist->undo_unselection);
2351       clist->undo_selection = NULL;
2352       clist->undo_unselection = NULL;
2353     }
2354
2355   visible = gtk_cmctree_is_viewable (ctree, node);
2356
2357   /* clist->row_list_end unlinked ? */
2358   if (visible &&
2359       (GTK_CMCTREE_NODE_NEXT (node) == NULL ||
2360        (GTK_CMCTREE_ROW (node)->children &&
2361         gtk_cmctree_is_ancestor (ctree, node,
2362                                GTK_CMCTREE_NODE (clist->row_list_end)))))
2363     clist->row_list_end = (GList *) (GTK_CMCTREE_NODE_PREV (node));
2364
2365   /* update list */
2366   rows = 0;
2367   level = GTK_CMCTREE_ROW (node)->level;
2368   work = GTK_CMCTREE_NODE_NEXT (node);
2369   while (work && GTK_CMCTREE_ROW (work)->level > level)
2370     {
2371       work = GTK_CMCTREE_NODE_NEXT (work);
2372       rows++;
2373     }
2374
2375   if (visible)
2376     {
2377       clist->rows -= (rows + 1);
2378
2379       if (update_focus_row)
2380         {
2381           gint pos;
2382           
2383           pos = g_list_position (clist->row_list, (GList *)node);
2384           if (pos + rows < clist->focus_row)
2385             clist->focus_row -= (rows + 1);
2386           else if (pos <= clist->focus_row)
2387             {
2388               if (!GTK_CMCTREE_ROW (node)->sibling)
2389                 clist->focus_row = MAX (pos - 1, 0);
2390               else
2391                 clist->focus_row = pos;
2392               
2393               clist->focus_row = MIN (clist->focus_row, clist->rows - 1);
2394             }
2395           clist->undo_anchor = clist->focus_row;
2396         }
2397     }
2398
2399   if (work)
2400     {
2401       list = (GList *)GTK_CMCTREE_NODE_PREV (work);
2402       list->next = NULL;
2403       list = (GList *)work;
2404       list->prev = (GList *)GTK_CMCTREE_NODE_PREV (node);
2405     }
2406
2407   if (GTK_CMCTREE_NODE_PREV (node) &&
2408       GTK_CMCTREE_NODE_NEXT (GTK_CMCTREE_NODE_PREV (node)) == node)
2409     {
2410       list = (GList *)GTK_CMCTREE_NODE_PREV (node);
2411       list->next = (GList *)work;
2412     }
2413
2414   /* update tree */
2415   parent = GTK_CMCTREE_ROW (node)->parent;
2416   if (parent)
2417     {
2418       if (GTK_CMCTREE_ROW (parent)->children == node)
2419         {
2420           GTK_CMCTREE_ROW (parent)->children = GTK_CMCTREE_ROW (node)->sibling;
2421           if (!GTK_CMCTREE_ROW (parent)->children)
2422             gtk_cmctree_collapse (ctree, parent);
2423         }
2424       else
2425         {
2426           GtkCMCTreeNode *sibling;
2427
2428           sibling = GTK_CMCTREE_ROW (parent)->children;
2429           while (GTK_CMCTREE_ROW (sibling)->sibling != node)
2430             sibling = GTK_CMCTREE_ROW (sibling)->sibling;
2431           GTK_CMCTREE_ROW (sibling)->sibling = GTK_CMCTREE_ROW (node)->sibling;
2432         }
2433     }
2434   else
2435     {
2436       if (clist->row_list == (GList *)node)
2437         clist->row_list = (GList *) (GTK_CMCTREE_ROW (node)->sibling);
2438       else
2439         {
2440           GtkCMCTreeNode *sibling;
2441
2442           sibling = GTK_CMCTREE_NODE (clist->row_list);
2443           while (GTK_CMCTREE_ROW (sibling)->sibling != node)
2444             sibling = GTK_CMCTREE_ROW (sibling)->sibling;
2445           GTK_CMCTREE_ROW (sibling)->sibling = GTK_CMCTREE_ROW (node)->sibling;
2446         }
2447     }
2448 }
2449
2450 static void
2451 real_row_move (GtkCMCList *clist,
2452                gint      source_row,
2453                gint      dest_row)
2454 {
2455   GtkCMCTree *ctree;
2456   GtkCMCTreeNode *node;
2457
2458   cm_return_if_fail (GTK_IS_CMCTREE (clist));
2459
2460   if (GTK_CMCLIST_AUTO_SORT (clist))
2461     return;
2462
2463   if (source_row < 0 || source_row >= clist->rows ||
2464       dest_row   < 0 || dest_row   >= clist->rows ||
2465       source_row == dest_row)
2466     return;
2467
2468   ctree = GTK_CMCTREE (clist);
2469   node = GTK_CMCTREE_NODE (g_list_nth (clist->row_list, source_row));
2470
2471   if (source_row < dest_row)
2472     {
2473       GtkCMCTreeNode *work; 
2474
2475       dest_row++;
2476       work = GTK_CMCTREE_ROW (node)->children;
2477
2478       while (work && GTK_CMCTREE_ROW (work)->level > GTK_CMCTREE_ROW (node)->level)
2479         {
2480           work = GTK_CMCTREE_NODE_NEXT (work);
2481           dest_row++;
2482         }
2483
2484       if (dest_row > clist->rows)
2485         dest_row = clist->rows;
2486     }
2487
2488   if (dest_row < clist->rows)
2489     {
2490       GtkCMCTreeNode *sibling;
2491
2492       sibling = GTK_CMCTREE_NODE (g_list_nth (clist->row_list, dest_row));
2493       gtk_cmctree_move (ctree, node, GTK_CMCTREE_ROW (sibling)->parent, sibling);
2494     }
2495   else
2496     gtk_cmctree_move (ctree, node, NULL, NULL);
2497 }
2498
2499 static void
2500 real_tree_move (GtkCMCTree     *ctree,
2501                 GtkCMCTreeNode *node,
2502                 GtkCMCTreeNode *new_parent, 
2503                 GtkCMCTreeNode *new_sibling)
2504 {
2505   GtkCMCList *clist;
2506   GtkCMCTreeNode *work;
2507   gboolean visible = FALSE;
2508
2509   cm_return_if_fail (ctree != NULL);
2510   cm_return_if_fail (node != NULL);
2511   cm_return_if_fail (!new_sibling || 
2512                     GTK_CMCTREE_ROW (new_sibling)->parent == new_parent);
2513
2514   if (new_parent && GTK_CMCTREE_ROW (new_parent)->is_leaf)
2515     return;
2516
2517   /* new_parent != child of child */
2518   for (work = new_parent; work; work = GTK_CMCTREE_ROW (work)->parent)
2519     if (work == node)
2520       return;
2521
2522   clist = GTK_CMCLIST (ctree);
2523
2524   visible = gtk_cmctree_is_viewable (ctree, node);
2525
2526   if (clist->selection_mode == GTK_SELECTION_MULTIPLE)
2527     {
2528       GTK_CMCLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
2529       
2530       g_list_free (clist->undo_selection);
2531       g_list_free (clist->undo_unselection);
2532       clist->undo_selection = NULL;
2533       clist->undo_unselection = NULL;
2534     }
2535
2536   if (GTK_CMCLIST_AUTO_SORT (clist))
2537     {
2538       if (new_parent == GTK_CMCTREE_ROW (node)->parent)
2539         return;
2540       
2541       if (new_parent)
2542         new_sibling = GTK_CMCTREE_ROW (new_parent)->children;
2543       else
2544         new_sibling = GTK_CMCTREE_NODE (clist->row_list);
2545
2546       while (new_sibling && clist->compare
2547              (clist, GTK_CMCTREE_ROW (node), GTK_CMCTREE_ROW (new_sibling)) > 0)
2548         new_sibling = GTK_CMCTREE_ROW (new_sibling)->sibling;
2549     }
2550
2551   if (new_parent == GTK_CMCTREE_ROW (node)->parent && 
2552       new_sibling == GTK_CMCTREE_ROW (node)->sibling)
2553     return;
2554
2555   gtk_cmclist_freeze (clist);
2556
2557   work = NULL;
2558   if (gtk_cmctree_is_viewable (ctree, node))
2559     work = GTK_CMCTREE_NODE (g_list_nth (clist->row_list, clist->focus_row));
2560       
2561   gtk_cmctree_unlink (ctree, node, FALSE);
2562   gtk_cmctree_link (ctree, node, new_parent, new_sibling, FALSE);
2563   
2564   if (work)
2565     {
2566       while (work &&  !gtk_cmctree_is_viewable (ctree, work))
2567         work = GTK_CMCTREE_ROW (work)->parent;
2568       clist->focus_row = g_list_position (clist->row_list, (GList *)work);
2569       clist->undo_anchor = clist->focus_row;
2570     }
2571
2572   if (clist->column[ctree->tree_column].auto_resize &&
2573       !GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist) &&
2574       (visible || gtk_cmctree_is_viewable (ctree, node)))
2575     gtk_cmclist_set_column_width
2576       (clist, ctree->tree_column,
2577        gtk_cmclist_optimal_column_width (clist, ctree->tree_column));
2578
2579   gtk_cmclist_thaw (clist);
2580 }
2581
2582 static void
2583 change_focus_row_expansion (GtkCMCTree          *ctree,
2584                             GtkCMCTreeExpansionType action)
2585 {
2586   GtkCMCList *clist;
2587   GtkCMCTreeNode *node;
2588
2589   cm_return_if_fail (GTK_IS_CMCTREE (ctree));
2590
2591   clist = GTK_CMCLIST (ctree);
2592
2593   if (gdk_display_pointer_is_grabbed (gtk_widget_get_display (GTK_WIDGET (ctree))) && 
2594       gtkut_widget_has_grab (GTK_WIDGET(ctree)))
2595     return;
2596   
2597   if (!(node =
2598         GTK_CMCTREE_NODE (g_list_nth (clist->row_list, clist->focus_row))) ||
2599       GTK_CMCTREE_ROW (node)->is_leaf || !(GTK_CMCTREE_ROW (node)->children))
2600     return;
2601
2602   switch (action)
2603     {
2604     case GTK_CMCTREE_EXPANSION_EXPAND:
2605       gtk_cmctree_expand (ctree, node);
2606       break;
2607     case GTK_CMCTREE_EXPANSION_EXPAND_RECURSIVE:
2608       gtk_cmctree_expand_recursive (ctree, node);
2609       break;
2610     case GTK_CMCTREE_EXPANSION_COLLAPSE:
2611       gtk_cmctree_collapse (ctree, node);
2612       break;
2613     case GTK_CMCTREE_EXPANSION_COLLAPSE_RECURSIVE:
2614       gtk_cmctree_collapse_recursive (ctree, node);
2615       break;
2616     case GTK_CMCTREE_EXPANSION_TOGGLE:
2617       gtk_cmctree_toggle_expansion (ctree, node);
2618       break;
2619     case GTK_CMCTREE_EXPANSION_TOGGLE_RECURSIVE:
2620       gtk_cmctree_toggle_expansion_recursive (ctree, node);
2621       break;
2622     }
2623 }
2624
2625 static void 
2626 real_tree_expand (GtkCMCTree     *ctree,
2627                   GtkCMCTreeNode *node)
2628 {
2629   GtkCMCList *clist;
2630   GtkCMCTreeNode *work;
2631   GtkRequisition requisition;
2632   gboolean visible;
2633
2634   cm_return_if_fail (GTK_IS_CMCTREE (ctree));
2635
2636   if (!node || GTK_CMCTREE_ROW (node)->expanded || GTK_CMCTREE_ROW (node)->is_leaf)
2637     return;
2638
2639   clist = GTK_CMCLIST (ctree);
2640   
2641   GTK_CMCLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
2642
2643   GTK_CMCTREE_ROW (node)->expanded = TRUE;
2644
2645   visible = gtk_cmctree_is_viewable (ctree, node);
2646   /* get cell width if tree_column is auto resized */
2647   if (visible && clist->column[ctree->tree_column].auto_resize &&
2648       !GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist))
2649     GTK_CMCLIST_GET_CLASS (clist)->cell_size_request
2650       (clist, &GTK_CMCTREE_ROW (node)->row, ctree->tree_column, &requisition);
2651
2652   /* unref/unset closed pixbuf */
2653   if (GTK_CMCELL_PIXTEXT 
2654       (GTK_CMCTREE_ROW (node)->row.cell[ctree->tree_column])->pixbuf)
2655     {
2656       g_object_unref
2657         (GTK_CMCELL_PIXTEXT
2658          (GTK_CMCTREE_ROW (node)->row.cell[ctree->tree_column])->pixbuf);
2659       
2660       GTK_CMCELL_PIXTEXT
2661         (GTK_CMCTREE_ROW (node)->row.cell[ctree->tree_column])->pixbuf = NULL;
2662     }
2663
2664   /* set/ref opened pixbuf */
2665   if (GTK_CMCTREE_ROW (node)->pixbuf_opened)
2666     {
2667       GTK_CMCELL_PIXTEXT 
2668         (GTK_CMCTREE_ROW (node)->row.cell[ctree->tree_column])->pixbuf = 
2669         g_object_ref (GTK_CMCTREE_ROW (node)->pixbuf_opened);
2670     }
2671
2672
2673   work = GTK_CMCTREE_ROW (node)->children;
2674   if (work)
2675     {
2676       GList *list = (GList *)work;
2677       gint *cell_width = NULL;
2678       gint tmp = 0;
2679       gint row;
2680       gint i;
2681       
2682       if (visible && !GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist))
2683         {
2684           cell_width = g_new0 (gint, clist->columns);
2685           if (clist->column[ctree->tree_column].auto_resize)
2686               cell_width[ctree->tree_column] = requisition.width;
2687
2688           while (work)
2689             {
2690               /* search maximum cell widths of auto_resize columns */
2691               for (i = 0; i < clist->columns; i++)
2692                 if (clist->column[i].auto_resize)
2693                   {
2694                     GTK_CMCLIST_GET_CLASS (clist)->cell_size_request
2695                       (clist, &GTK_CMCTREE_ROW (work)->row, i, &requisition);
2696                     cell_width[i] = MAX (requisition.width, cell_width[i]);
2697                   }
2698
2699               list = (GList *)work;
2700               work = GTK_CMCTREE_NODE_NEXT (work);
2701               tmp++;
2702             }
2703         }
2704       else
2705         while (work)
2706           {
2707             list = (GList *)work;
2708             work = GTK_CMCTREE_NODE_NEXT (work);
2709             tmp++;
2710           }
2711
2712       list->next = (GList *)GTK_CMCTREE_NODE_NEXT (node);
2713
2714       if (GTK_CMCTREE_NODE_NEXT (node))
2715         {
2716           GList *tmp_list;
2717
2718           tmp_list = (GList *)GTK_CMCTREE_NODE_NEXT (node);
2719           tmp_list->prev = list;
2720         }
2721       else
2722         clist->row_list_end = list;
2723
2724       list = (GList *)node;
2725       list->next = (GList *)(GTK_CMCTREE_ROW (node)->children);
2726
2727       if (visible && !GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist))
2728         {
2729           /* resize auto_resize columns if needed */
2730           for (i = 0; i < clist->columns; i++)
2731             if (clist->column[i].auto_resize &&
2732                 cell_width[i] > clist->column[i].width)
2733               gtk_cmclist_set_column_width (clist, i, cell_width[i]);
2734           g_free (cell_width);
2735
2736           /* update focus_row position */
2737           row = g_list_position (clist->row_list, (GList *)node);
2738           if (row < clist->focus_row)
2739             clist->focus_row += tmp;
2740
2741           clist->rows += tmp;
2742           CLIST_REFRESH (clist);
2743         }
2744     }
2745   else if (visible && clist->column[ctree->tree_column].auto_resize)
2746     /* resize tree_column if needed */
2747     column_auto_resize (clist, &GTK_CMCTREE_ROW (node)->row, ctree->tree_column,
2748                         requisition.width);
2749 }
2750
2751 static void 
2752 real_tree_collapse (GtkCMCTree     *ctree,
2753                     GtkCMCTreeNode *node)
2754 {
2755   GtkCMCList *clist;
2756   GtkCMCTreeNode *work;
2757   GtkRequisition requisition;
2758   gboolean visible;
2759   gint level;
2760
2761   cm_return_if_fail (GTK_IS_CMCTREE (ctree));
2762
2763   if (!node || !GTK_CMCTREE_ROW (node)->expanded ||
2764       GTK_CMCTREE_ROW (node)->is_leaf)
2765     return;
2766
2767   clist = GTK_CMCLIST (ctree);
2768
2769   GTK_CMCLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
2770   
2771   GTK_CMCTREE_ROW (node)->expanded = FALSE;
2772   level = GTK_CMCTREE_ROW (node)->level;
2773
2774   visible = gtk_cmctree_is_viewable (ctree, node);
2775   /* get cell width if tree_column is auto resized */
2776   if (visible && clist->column[ctree->tree_column].auto_resize &&
2777       !GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist))
2778     GTK_CMCLIST_GET_CLASS (clist)->cell_size_request
2779       (clist, &GTK_CMCTREE_ROW (node)->row, ctree->tree_column, &requisition);
2780
2781   /* unref/unset opened pixbuf */
2782   if (GTK_CMCELL_PIXTEXT 
2783       (GTK_CMCTREE_ROW (node)->row.cell[ctree->tree_column])->pixbuf)
2784     {
2785       g_object_unref
2786         (GTK_CMCELL_PIXTEXT
2787          (GTK_CMCTREE_ROW (node)->row.cell[ctree->tree_column])->pixbuf);
2788       
2789       GTK_CMCELL_PIXTEXT
2790         (GTK_CMCTREE_ROW (node)->row.cell[ctree->tree_column])->pixbuf = NULL;
2791     }
2792
2793   /* set/ref closed pixbuf */
2794   if (GTK_CMCTREE_ROW (node)->pixbuf_closed)
2795     {
2796       GTK_CMCELL_PIXTEXT 
2797         (GTK_CMCTREE_ROW (node)->row.cell[ctree->tree_column])->pixbuf = 
2798         g_object_ref (GTK_CMCTREE_ROW (node)->pixbuf_closed);
2799     }
2800
2801   work = GTK_CMCTREE_ROW (node)->children;
2802   if (work)
2803     {
2804       gint tmp = 0;
2805       gint row;
2806       GList *list;
2807
2808       while (work && GTK_CMCTREE_ROW (work)->level > level)
2809         {
2810           work = GTK_CMCTREE_NODE_NEXT (work);
2811           tmp++;
2812         }
2813
2814       if (work)
2815         {
2816           list = (GList *)node;
2817           list->next = (GList *)work;
2818           list = (GList *)GTK_CMCTREE_NODE_PREV (work);
2819           list->next = NULL;
2820           list = (GList *)work;
2821           list->prev = (GList *)node;
2822         }
2823       else
2824         {
2825           list = (GList *)node;
2826           list->next = NULL;
2827           clist->row_list_end = (GList *)node;
2828         }
2829
2830       if (visible)
2831         {
2832           /* resize auto_resize columns if needed */
2833           auto_resize_columns (clist);
2834
2835           row = g_list_position (clist->row_list, (GList *)node);
2836           if (row < clist->focus_row)
2837             clist->focus_row -= tmp;
2838           clist->rows -= tmp;
2839           CLIST_REFRESH (clist);
2840         }
2841     }
2842   else if (visible && clist->column[ctree->tree_column].auto_resize &&
2843            !GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist))
2844     /* resize tree_column if needed */
2845     column_auto_resize (clist, &GTK_CMCTREE_ROW (node)->row, ctree->tree_column,
2846                         requisition.width);
2847     
2848 }
2849
2850 static void
2851 column_auto_resize (GtkCMCList    *clist,
2852                     GtkCMCListRow *clist_row,
2853                     gint         column,
2854                     gint         old_width)
2855 {
2856   /* resize column if needed for auto_resize */
2857   GtkRequisition requisition;
2858
2859   if (!clist->column[column].auto_resize ||
2860       GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist))
2861     return;
2862
2863   if (clist_row)
2864     GTK_CMCLIST_GET_CLASS (clist)->cell_size_request (clist, clist_row,
2865                                                    column, &requisition);
2866   else
2867     requisition.width = 0;
2868
2869   if (requisition.width > clist->column[column].width)
2870     gtk_cmclist_set_column_width (clist, column, requisition.width);
2871   else if (requisition.width < old_width &&
2872            old_width == clist->column[column].width)
2873     {
2874       GList *list;
2875       gint new_width;
2876
2877       /* run a "gtk_cmclist_optimal_column_width" but break, if
2878        * the column doesn't shrink */
2879       if (GTK_CMCLIST_SHOW_TITLES (clist) && clist->column[column].button)
2880         new_width = (clist->column[column].button->requisition.width -
2881                      (CELL_SPACING + (2 * COLUMN_INSET)));
2882       else
2883         new_width = 0;
2884
2885       for (list = clist->row_list; list; list = list->next)
2886         {
2887           GTK_CMCLIST_GET_CLASS (clist)->cell_size_request
2888             (clist, GTK_CMCLIST_ROW (list), column, &requisition);
2889           new_width = MAX (new_width, requisition.width);
2890           if (new_width == clist->column[column].width)
2891             break;
2892         }
2893       if (new_width < clist->column[column].width)
2894         gtk_cmclist_set_column_width (clist, column, new_width);
2895     }
2896 }
2897
2898 static void
2899 auto_resize_columns (GtkCMCList *clist)
2900 {
2901   gint i;
2902
2903   if (GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist))
2904     return;
2905
2906   for (i = 0; i < clist->columns; i++)
2907     column_auto_resize (clist, NULL, i, clist->column[i].width);
2908 }
2909
2910 static void
2911 cell_size_request (GtkCMCList       *clist,
2912                    GtkCMCListRow    *clist_row,
2913                    gint            column,
2914                    GtkRequisition *requisition)
2915 {
2916   GtkCMCTree *ctree;
2917   gint width;
2918   gint height;
2919   PangoLayout *layout;
2920   PangoRectangle logical_rect;
2921
2922   cm_return_if_fail (GTK_IS_CMCTREE (clist));
2923   cm_return_if_fail (requisition != NULL);
2924
2925   ctree = GTK_CMCTREE (clist);
2926
2927   layout = _gtk_cmclist_create_cell_layout (clist, clist_row, column);
2928   if (layout)
2929     {
2930       pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
2931
2932       requisition->width = logical_rect.width;
2933       requisition->height = logical_rect.height;
2934       
2935       g_object_unref (G_OBJECT (layout));
2936     }
2937   else
2938     {
2939       requisition->width  = 0;
2940       requisition->height = 0;
2941     }
2942
2943   switch (clist_row->cell[column].type)
2944     {
2945     case GTK_CMCELL_PIXTEXT:
2946       if (GTK_CMCELL_PIXTEXT (clist_row->cell[column])->pixbuf)
2947         {
2948           width = gdk_pixbuf_get_width(GTK_CMCELL_PIXTEXT (clist_row->cell[column])->pixbuf);
2949           height = gdk_pixbuf_get_height(GTK_CMCELL_PIXTEXT (clist_row->cell[column])->pixbuf);
2950           width += GTK_CMCELL_PIXTEXT (clist_row->cell[column])->spacing;
2951         }
2952       else
2953         width = height = 0;
2954           
2955       requisition->width += width;
2956       requisition->height = MAX (requisition->height, height);
2957       
2958       if (column == ctree->tree_column)
2959         {
2960           requisition->width += (ctree->tree_spacing + ctree->tree_indent *
2961                                  (((GtkCMCTreeRow *) clist_row)->level - 1));
2962           switch (ctree->expander_style)
2963             {
2964             case GTK_CMCTREE_EXPANDER_NONE:
2965               break;
2966             case GTK_CMCTREE_EXPANDER_TRIANGLE:
2967               requisition->width += PM_SIZE + 3;
2968               break;
2969             case GTK_CMCTREE_EXPANDER_SQUARE:
2970             case GTK_CMCTREE_EXPANDER_CIRCULAR:
2971               requisition->width += PM_SIZE + 1;
2972               break;
2973             }
2974           if (ctree->line_style == GTK_CMCTREE_LINES_TABBED)
2975             requisition->width += 3;
2976         }
2977       break;
2978     case GTK_CMCELL_PIXBUF:
2979       width = gdk_pixbuf_get_width(GTK_CMCELL_PIXBUF (clist_row->cell[column])->pixbuf);
2980       height = gdk_pixbuf_get_height(GTK_CMCELL_PIXBUF (clist_row->cell[column])->pixbuf);
2981       requisition->width += width;
2982       requisition->height = MAX (requisition->height, height);
2983       break;
2984     default:
2985       break;
2986     }
2987
2988   requisition->width  += clist_row->cell[column].horizontal;
2989   requisition->height += clist_row->cell[column].vertical;
2990 }
2991
2992 static void
2993 set_cell_contents (GtkCMCList    *clist,
2994                    GtkCMCListRow *clist_row,
2995                    gint         column,
2996                    GtkCMCellType  type,
2997                    const gchar *text,
2998                    guint8       spacing,
2999                    GdkPixbuf   *pixbuf)
3000 {
3001   gboolean visible = FALSE;
3002   GtkCMCTree *ctree;
3003   GtkRequisition requisition;
3004   gchar *old_text = NULL;
3005   GdkPixbuf *old_pixbuf = NULL;
3006
3007   cm_return_if_fail (GTK_IS_CMCTREE (clist));
3008   cm_return_if_fail (clist_row != NULL);
3009
3010   ctree = GTK_CMCTREE (clist);
3011
3012   if (clist->column[column].auto_resize &&
3013       !GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist))
3014     {
3015       GtkCMCTreeNode *parent;
3016
3017       parent = ((GtkCMCTreeRow *)clist_row)->parent;
3018       if ((parent && GTK_CMCTREE_ROW (parent)->expanded &&
3019                       gtk_cmctree_is_viewable (ctree, parent)))
3020         {
3021           visible = TRUE;
3022           GTK_CMCLIST_GET_CLASS (clist)->cell_size_request (clist, clist_row,
3023                                                          column, &requisition);
3024         }
3025     }
3026
3027   switch (clist_row->cell[column].type)
3028     {
3029     case GTK_CMCELL_EMPTY:
3030       break;
3031     case GTK_CMCELL_TEXT:
3032       old_text = GTK_CMCELL_TEXT (clist_row->cell[column])->text;
3033       break;
3034     case GTK_CMCELL_PIXBUF:
3035       old_pixbuf = GTK_CMCELL_PIXBUF (clist_row->cell[column])->pixbuf;
3036       break;
3037     case GTK_CMCELL_PIXTEXT:
3038       old_text = GTK_CMCELL_PIXTEXT (clist_row->cell[column])->text;
3039       old_pixbuf = GTK_CMCELL_PIXTEXT (clist_row->cell[column])->pixbuf;
3040       break;
3041     case GTK_CMCELL_WIDGET:
3042       /* unimplemented */
3043       break;
3044       
3045     default:
3046       break;
3047     }
3048
3049   clist_row->cell[column].type = GTK_CMCELL_EMPTY;
3050   if (column == ctree->tree_column && type != GTK_CMCELL_EMPTY)
3051     type = GTK_CMCELL_PIXTEXT;
3052
3053   /* Note that pixbuf and mask were already ref'ed by the caller
3054    */
3055   switch (type)
3056     {
3057     case GTK_CMCELL_TEXT:
3058       if (text)
3059         {
3060           clist_row->cell[column].type = GTK_CMCELL_TEXT;
3061           GTK_CMCELL_TEXT (clist_row->cell[column])->text = g_strdup (text);
3062         }
3063       break;
3064     case GTK_CMCELL_PIXBUF:
3065       if (pixbuf)
3066         {
3067           clist_row->cell[column].type = GTK_CMCELL_PIXBUF;
3068           GTK_CMCELL_PIXBUF (clist_row->cell[column])->pixbuf = pixbuf;
3069         }
3070       break;
3071     case GTK_CMCELL_PIXTEXT:
3072       if (column == ctree->tree_column)
3073         {
3074           clist_row->cell[column].type = GTK_CMCELL_PIXTEXT;
3075           GTK_CMCELL_PIXTEXT (clist_row->cell[column])->spacing = spacing;
3076           if (text)
3077             GTK_CMCELL_PIXTEXT (clist_row->cell[column])->text = g_strdup (text);
3078           else
3079             GTK_CMCELL_PIXTEXT (clist_row->cell[column])->text = NULL;
3080           if (pixbuf)
3081             {
3082               GTK_CMCELL_PIXTEXT (clist_row->cell[column])->pixbuf = pixbuf;
3083             }
3084           else
3085             {
3086               GTK_CMCELL_PIXTEXT (clist_row->cell[column])->pixbuf = NULL;
3087             }
3088         }
3089       else if (text && pixbuf)
3090         {
3091           clist_row->cell[column].type = GTK_CMCELL_PIXTEXT;
3092           GTK_CMCELL_PIXTEXT (clist_row->cell[column])->text = g_strdup (text);
3093           GTK_CMCELL_PIXTEXT (clist_row->cell[column])->spacing = spacing;
3094           GTK_CMCELL_PIXTEXT (clist_row->cell[column])->pixbuf = pixbuf;
3095         }
3096       break;
3097     default:
3098       break;
3099     }
3100   
3101   if (visible && clist->column[column].auto_resize &&
3102       !GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist))
3103     column_auto_resize (clist, clist_row, column, requisition.width);
3104
3105   g_free (old_text);
3106   if (old_pixbuf)
3107     g_object_unref (old_pixbuf);
3108 }
3109
3110 static void 
3111 set_node_info (GtkCMCTree     *ctree,
3112                GtkCMCTreeNode *node,
3113                const gchar  *text,
3114                guint8        spacing,
3115                GdkPixbuf    *pixbuf_closed,
3116                GdkPixbuf    *pixbuf_opened,
3117                gboolean      is_leaf,
3118                gboolean      expanded)
3119 {
3120   if (GTK_CMCTREE_ROW (node)->pixbuf_opened)
3121     {
3122       g_object_unref (GTK_CMCTREE_ROW (node)->pixbuf_opened);
3123     }
3124   if (GTK_CMCTREE_ROW (node)->pixbuf_closed)
3125     {
3126       g_object_unref (GTK_CMCTREE_ROW (node)->pixbuf_closed);
3127     }
3128
3129   GTK_CMCTREE_ROW (node)->pixbuf_opened = NULL;
3130   GTK_CMCTREE_ROW (node)->pixbuf_closed = NULL;
3131
3132   if (pixbuf_closed)
3133     {
3134       GTK_CMCTREE_ROW (node)->pixbuf_closed = g_object_ref (pixbuf_closed);
3135     }
3136   if (pixbuf_opened)
3137     {
3138       GTK_CMCTREE_ROW (node)->pixbuf_opened = g_object_ref (pixbuf_opened);
3139     }
3140
3141   GTK_CMCTREE_ROW (node)->is_leaf  = is_leaf;
3142   GTK_CMCTREE_ROW (node)->expanded = (is_leaf) ? FALSE : expanded;
3143
3144   if (GTK_CMCTREE_ROW (node)->expanded)
3145     gtk_cmctree_node_set_pixtext (ctree, node, ctree->tree_column,
3146                                 text, spacing, pixbuf_opened);
3147   else 
3148     gtk_cmctree_node_set_pixtext (ctree, node, ctree->tree_column,
3149                                 text, spacing, pixbuf_closed);
3150 }
3151
3152 static void
3153 tree_delete (GtkCMCTree     *ctree, 
3154              GtkCMCTreeNode *node, 
3155              gpointer      data)
3156 {
3157   tree_unselect (ctree,  node, NULL);
3158   row_delete (ctree, GTK_CMCTREE_ROW (node));
3159   g_list_free_1 ((GList *)node);
3160 }
3161
3162 static void
3163 tree_delete_row (GtkCMCTree     *ctree, 
3164                  GtkCMCTreeNode *node, 
3165                  gpointer      data)
3166 {
3167   row_delete (ctree, GTK_CMCTREE_ROW (node));
3168   g_list_free_1 ((GList *)node);
3169 }
3170
3171 static void
3172 tree_update_level (GtkCMCTree     *ctree, 
3173                    GtkCMCTreeNode *node, 
3174                    gpointer      data)
3175 {
3176   if (!node)
3177     return;
3178
3179   if (GTK_CMCTREE_ROW (node)->parent)
3180       GTK_CMCTREE_ROW (node)->level = 
3181         GTK_CMCTREE_ROW (GTK_CMCTREE_ROW (node)->parent)->level + 1;
3182   else
3183       GTK_CMCTREE_ROW (node)->level = 1;
3184 }
3185
3186 static void
3187 tree_select (GtkCMCTree     *ctree, 
3188              GtkCMCTreeNode *node, 
3189              gpointer      data)
3190 {
3191   if (node && GTK_CMCTREE_ROW (node)->row.state != GTK_STATE_SELECTED &&
3192       GTK_CMCTREE_ROW (node)->row.selectable)
3193     g_signal_emit (G_OBJECT (ctree), ctree_signals[TREE_SELECT_ROW], 0,
3194                      node, -1);
3195 }
3196
3197 static void
3198 tree_unselect (GtkCMCTree     *ctree, 
3199                GtkCMCTreeNode *node, 
3200                gpointer      data)
3201 {
3202   if (node && GTK_CMCTREE_ROW (node)->row.state == GTK_STATE_SELECTED)
3203     g_signal_emit (G_OBJECT (ctree), ctree_signals[TREE_UNSELECT_ROW], 0,
3204                      node, -1);
3205 }
3206
3207 static void
3208 tree_expand (GtkCMCTree     *ctree, 
3209              GtkCMCTreeNode *node, 
3210              gpointer      data)
3211 {
3212   if (node && !GTK_CMCTREE_ROW (node)->expanded)
3213     g_signal_emit (G_OBJECT (ctree), ctree_signals[TREE_EXPAND], 0,node);
3214 }
3215
3216 static void
3217 tree_collapse (GtkCMCTree     *ctree, 
3218                GtkCMCTreeNode *node, 
3219                gpointer      data)
3220 {
3221   if (node && GTK_CMCTREE_ROW (node)->expanded)
3222     g_signal_emit (G_OBJECT (ctree), ctree_signals[TREE_COLLAPSE], 0,node);
3223 }
3224
3225 static void
3226 tree_collapse_to_depth (GtkCMCTree     *ctree, 
3227                         GtkCMCTreeNode *node, 
3228                         gint          depth)
3229 {
3230   if (node && GTK_CMCTREE_ROW (node)->level == depth)
3231     gtk_cmctree_collapse_recursive (ctree, node);
3232 }
3233
3234 static void
3235 tree_toggle_expansion (GtkCMCTree     *ctree,
3236                        GtkCMCTreeNode *node,
3237                        gpointer      data)
3238 {
3239   if (!node)
3240     return;
3241
3242   if (GTK_CMCTREE_ROW (node)->expanded)
3243     g_signal_emit (G_OBJECT (ctree), ctree_signals[TREE_COLLAPSE], 0,node);
3244   else
3245     g_signal_emit (G_OBJECT (ctree), ctree_signals[TREE_EXPAND], 0,node);
3246 }
3247
3248 static GtkCMCTreeRow *
3249 row_new (GtkCMCTree *ctree)
3250 {
3251   GtkCMCList *clist;
3252   GtkCMCTreeRow *ctree_row;
3253   int i;
3254
3255   clist = GTK_CMCLIST (ctree);
3256 #if GLIB_CHECK_VERSION(2,10,0)
3257   ctree_row = g_slice_new (GtkCMCTreeRow);
3258   ctree_row->row.cell = g_slice_alloc (sizeof (GtkCMCell) * clist->columns);
3259 #else
3260   ctree_row = g_chunk_new (GtkCMCTreeRow, (GMemChunk *)clist->row_mem_chunk);
3261   ctree_row->row.cell = g_chunk_new (GtkCMCell, (GMemChunk *)clist->cell_mem_chunk);
3262 #endif
3263
3264   for (i = 0; i < clist->columns; i++)
3265     {
3266       ctree_row->row.cell[i].type = GTK_CMCELL_EMPTY;
3267       ctree_row->row.cell[i].vertical = 0;
3268       ctree_row->row.cell[i].horizontal = 0;
3269       ctree_row->row.cell[i].style = NULL;
3270     }
3271   GTK_CMCELL_PIXTEXT (ctree_row->row.cell[ctree->tree_column])->text = NULL;
3272
3273   ctree_row->row.fg_set     = FALSE;
3274   ctree_row->row.bg_set     = FALSE;
3275   ctree_row->row.style      = NULL;
3276   ctree_row->row.selectable = TRUE;
3277   ctree_row->row.state      = GTK_STATE_NORMAL;
3278   ctree_row->row.data       = NULL;
3279   ctree_row->row.destroy    = NULL;
3280
3281   ctree_row->level         = 0;
3282   ctree_row->expanded      = FALSE;
3283   ctree_row->parent        = NULL;
3284   ctree_row->sibling       = NULL;
3285   ctree_row->children      = NULL;
3286   ctree_row->pixbuf_closed = NULL;
3287   ctree_row->pixbuf_opened = NULL;
3288   
3289   return ctree_row;
3290 }
3291
3292 static void
3293 row_delete (GtkCMCTree    *ctree,
3294             GtkCMCTreeRow *ctree_row)
3295 {
3296   GtkCMCList *clist;
3297   gint i;
3298
3299   clist = GTK_CMCLIST (ctree);
3300
3301   for (i = 0; i < clist->columns; i++)
3302     {
3303       GTK_CMCLIST_GET_CLASS (clist)->set_cell_contents
3304         (clist, &(ctree_row->row), i, GTK_CMCELL_EMPTY, NULL, 0, NULL);
3305       if (ctree_row->row.cell[i].style)
3306         {
3307           if (gtkut_widget_get_realized (GTK_WIDGET(ctree)))
3308             gtk_style_detach (ctree_row->row.cell[i].style);
3309           g_object_unref (ctree_row->row.cell[i].style);
3310         }
3311     }
3312
3313   if (ctree_row->row.style)
3314     {
3315       if (gtkut_widget_get_realized (GTK_WIDGET(ctree)))
3316         gtk_style_detach (ctree_row->row.style);
3317       g_object_unref (ctree_row->row.style);
3318     }
3319
3320   if (ctree_row->pixbuf_closed)
3321     {
3322       g_object_unref (ctree_row->pixbuf_closed);
3323     }
3324
3325   if (ctree_row->pixbuf_opened)
3326     {
3327       g_object_unref (ctree_row->pixbuf_opened);
3328     }
3329
3330   if (ctree_row->row.destroy)
3331     {
3332       GDestroyNotify dnotify = ctree_row->row.destroy;
3333       gpointer ddata = ctree_row->row.data;
3334
3335       ctree_row->row.destroy = NULL;
3336       ctree_row->row.data = NULL;
3337
3338       dnotify (ddata);
3339     }
3340
3341 #if GLIB_CHECK_VERSION(2,10,0)  
3342   g_slice_free1 (sizeof (GtkCMCell) * clist->columns, ctree_row->row.cell);
3343   g_slice_free (GtkCMCTreeRow, ctree_row);
3344 #else
3345   g_mem_chunk_free ((GMemChunk *)clist->cell_mem_chunk, ctree_row->row.cell);
3346   g_mem_chunk_free ((GMemChunk *)clist->row_mem_chunk, ctree_row);
3347 #endif
3348 }
3349
3350 static void
3351 real_select_row (GtkCMCList *clist,
3352                  gint      row,
3353                  gint      column,
3354                  GdkEvent *event)
3355 {
3356   GList *node;
3357
3358   cm_return_if_fail (GTK_IS_CMCTREE (clist));
3359   
3360   if ((node = g_list_nth (clist->row_list, row)) &&
3361       GTK_CMCTREE_ROW (node)->row.selectable)
3362     g_signal_emit (G_OBJECT (clist), ctree_signals[TREE_SELECT_ROW],0,
3363                      node, column);
3364 }
3365
3366 static void
3367 real_unselect_row (GtkCMCList *clist,
3368                    gint      row,
3369                    gint      column,
3370                    GdkEvent *event)
3371 {
3372   GList *node;
3373
3374   cm_return_if_fail (GTK_IS_CMCTREE (clist));
3375
3376   if ((node = g_list_nth (clist->row_list, row)))
3377     g_signal_emit (G_OBJECT (clist), ctree_signals[TREE_UNSELECT_ROW],0,
3378                      node, column);
3379 }
3380
3381 static void
3382 real_tree_select (GtkCMCTree     *ctree,
3383                   GtkCMCTreeNode *node,
3384                   gint          column)
3385 {
3386   GtkCMCList *clist;
3387   GList *list;
3388   GtkCMCTreeNode *sel_row;
3389   gboolean node_selected;
3390
3391   cm_return_if_fail (GTK_IS_CMCTREE (ctree));
3392
3393   if (!node || GTK_CMCTREE_ROW (node)->row.state == GTK_STATE_SELECTED ||
3394       !GTK_CMCTREE_ROW (node)->row.selectable)
3395     return;
3396
3397   clist = GTK_CMCLIST (ctree);
3398
3399   switch (clist->selection_mode)
3400     {
3401     case GTK_SELECTION_SINGLE:
3402     case GTK_SELECTION_BROWSE:
3403
3404       node_selected = FALSE;
3405       list = clist->selection;
3406
3407       while (list)
3408         {
3409           sel_row = list->data;
3410           list = list->next;
3411           
3412           if (node == sel_row)
3413             node_selected = TRUE;
3414           else
3415             g_signal_emit (G_OBJECT (ctree),
3416                              ctree_signals[TREE_UNSELECT_ROW], 0, sel_row, column);
3417         }
3418
3419       if (node_selected)
3420         return;
3421
3422     default:
3423       break;
3424     }
3425
3426   GTK_CMCTREE_ROW (node)->row.state = GTK_STATE_SELECTED;
3427
3428   if (!clist->selection)
3429     {
3430       clist->selection = g_list_append (clist->selection, node);
3431       clist->selection_end = clist->selection;
3432     }
3433   else
3434     clist->selection_end = g_list_append (clist->selection_end, node)->next;
3435
3436   tree_draw_node (ctree, node);
3437 }
3438
3439 static void
3440 real_tree_unselect (GtkCMCTree     *ctree,
3441                     GtkCMCTreeNode *node,
3442                     gint          column)
3443 {
3444   GtkCMCList *clist;
3445
3446   cm_return_if_fail (GTK_IS_CMCTREE (ctree));
3447
3448   if (!node || GTK_CMCTREE_ROW (node)->row.state != GTK_STATE_SELECTED)
3449     return;
3450
3451   clist = GTK_CMCLIST (ctree);
3452
3453   if (clist->selection_end && clist->selection_end->data == node)
3454     clist->selection_end = clist->selection_end->prev;
3455
3456   clist->selection = g_list_remove (clist->selection, node);
3457   
3458   GTK_CMCTREE_ROW (node)->row.state = GTK_STATE_NORMAL;
3459
3460   tree_draw_node (ctree, node);
3461 }
3462
3463 static void
3464 select_row_recursive (GtkCMCTree     *ctree, 
3465                       GtkCMCTreeNode *node, 
3466                       gpointer      data)
3467 {
3468   if (!node || GTK_CMCTREE_ROW (node)->row.state == GTK_STATE_SELECTED ||
3469       !GTK_CMCTREE_ROW (node)->row.selectable)
3470     return;
3471
3472   GTK_CMCLIST (ctree)->undo_unselection = 
3473     g_list_prepend (GTK_CMCLIST (ctree)->undo_unselection, node);
3474   gtk_cmctree_select (ctree, node);
3475 }
3476
3477 static void
3478 real_select_all (GtkCMCList *clist)
3479 {
3480   GtkCMCTree *ctree;
3481   GtkCMCTreeNode *node;
3482   
3483   cm_return_if_fail (GTK_IS_CMCTREE (clist));
3484
3485   ctree = GTK_CMCTREE (clist);
3486
3487   switch (clist->selection_mode)
3488     {
3489     case GTK_SELECTION_SINGLE:
3490     case GTK_SELECTION_BROWSE:
3491       return;
3492
3493     case GTK_SELECTION_MULTIPLE:
3494
3495       gtk_cmclist_freeze (clist);
3496
3497       g_list_free (clist->undo_selection);
3498       g_list_free (clist->undo_unselection);
3499       clist->undo_selection = NULL;
3500       clist->undo_unselection = NULL;
3501           
3502       clist->anchor_state = GTK_STATE_SELECTED;
3503       clist->anchor = -1;
3504       clist->drag_pos = -1;
3505       clist->undo_anchor = clist->focus_row;
3506
3507       for (node = GTK_CMCTREE_NODE (clist->row_list); node;
3508            node = GTK_CMCTREE_NODE_NEXT (node))
3509         gtk_cmctree_pre_recursive (ctree, node, select_row_recursive, NULL);
3510
3511       gtk_cmclist_thaw (clist);
3512       break;
3513
3514     default:
3515       /* do nothing */
3516       break;
3517     }
3518 }
3519
3520 static void
3521 real_unselect_all (GtkCMCList *clist)
3522 {
3523   GtkCMCTree *ctree;
3524   GtkCMCTreeNode *node;
3525   GList *list;
3526  
3527   cm_return_if_fail (GTK_IS_CMCTREE (clist));
3528   
3529   ctree = GTK_CMCTREE (clist);
3530
3531   switch (clist->selection_mode)
3532     {
3533     case GTK_SELECTION_BROWSE:
3534       if (clist->focus_row >= 0)
3535         {
3536           gtk_cmctree_select
3537             (ctree,
3538              GTK_CMCTREE_NODE (g_list_nth (clist->row_list, clist->focus_row)));
3539           return;
3540         }
3541       break;
3542
3543     case GTK_SELECTION_MULTIPLE:
3544       g_list_free (clist->undo_selection);
3545       g_list_free (clist->undo_unselection);
3546       clist->undo_selection = NULL;
3547       clist->undo_unselection = NULL;
3548
3549       clist->anchor = -1;
3550       clist->drag_pos = -1;
3551       clist->undo_anchor = clist->focus_row;
3552       break;
3553
3554     default:
3555       break;
3556     }
3557
3558   list = clist->selection;
3559
3560   while (list)
3561     {
3562       node = list->data;
3563       list = list->next;
3564       gtk_cmctree_unselect (ctree, node);
3565     }
3566 }
3567
3568 static gboolean
3569 ctree_is_hot_spot (GtkCMCTree     *ctree, 
3570                    GtkCMCTreeNode *node,
3571                    gint          row, 
3572                    gint          x, 
3573                    gint          y)
3574 {
3575   GtkCMCTreeRow *tree_row;
3576   GtkCMCList *clist;
3577   gint xl;
3578   gint yu;
3579   
3580   cm_return_val_if_fail (GTK_IS_CMCTREE (ctree), FALSE);
3581   cm_return_val_if_fail (node != NULL, FALSE);
3582
3583   clist = GTK_CMCLIST (ctree);
3584
3585   if (!clist->column[ctree->tree_column].visible ||
3586       ctree->expander_style == GTK_CMCTREE_EXPANDER_NONE)
3587     return FALSE;
3588
3589   tree_row = GTK_CMCTREE_ROW (node);
3590
3591   yu = (ROW_TOP_YPIXEL (clist, row) + (clist->row_height - PM_SIZE) / 2 -
3592         (clist->row_height - 1) % 2);
3593
3594   if (clist->column[ctree->tree_column].justification == GTK_JUSTIFY_RIGHT)
3595     xl = (clist->column[ctree->tree_column].area.x + 
3596           clist->column[ctree->tree_column].area.width - 1 + clist->hoffset -
3597           (tree_row->level - 1) * ctree->tree_indent - PM_SIZE -
3598           (ctree->line_style == GTK_CMCTREE_LINES_TABBED) * 3);
3599   else
3600     xl = (clist->column[ctree->tree_column].area.x + clist->hoffset +
3601           (tree_row->level - 1) * ctree->tree_indent +
3602           (ctree->line_style == GTK_CMCTREE_LINES_TABBED) * 3);
3603
3604   return (x >= xl && x <= xl + PM_SIZE && y >= yu && y <= yu + PM_SIZE);
3605 }
3606
3607 /***********************************************************
3608  ***********************************************************
3609  ***                  Public interface                   ***
3610  ***********************************************************
3611  ***********************************************************/
3612
3613
3614 /***********************************************************
3615  *           Creation, insertion, deletion                 *
3616  ***********************************************************/
3617
3618 static GObject*
3619 gtk_cmctree_constructor (GType                  type,
3620                        guint                  n_construct_properties,
3621                        GObjectConstructParam *construct_properties)
3622 {
3623   GObject *object = G_OBJECT_CLASS (parent_class)->constructor (type,
3624                                                                 n_construct_properties,
3625                                                                 construct_properties);
3626
3627   return object;
3628 }
3629
3630 GtkWidget*
3631 gtk_cmctree_new_with_titles (gint         columns, 
3632                            gint         tree_column,
3633                            gchar       *titles[])
3634 {
3635   GtkWidget *widget;
3636
3637   cm_return_val_if_fail (columns > 0, NULL);
3638   cm_return_val_if_fail (tree_column >= 0 && tree_column < columns, NULL);
3639
3640   widget = gtk_widget_new (GTK_TYPE_CMCTREE,
3641                            "n_columns", columns,
3642                            "tree_column", tree_column,
3643                            NULL);
3644   if (titles)
3645     {
3646       GtkCMCList *clist = GTK_CMCLIST (widget);
3647       guint i;
3648
3649       for (i = 0; i < columns; i++)
3650         gtk_cmclist_set_column_title (clist, i, titles[i]);
3651       gtk_cmclist_column_titles_show (clist);
3652     }
3653
3654   return widget;
3655 }
3656
3657 GtkWidget *
3658 gtk_cmctree_new (gint columns, 
3659                gint tree_column)
3660 {
3661   return gtk_cmctree_new_with_titles (columns, tree_column, NULL);
3662 }
3663
3664 static gint
3665 real_insert_row (GtkCMCList *clist,
3666                  gint      row,
3667                  gchar    *text[])
3668 {
3669   GtkCMCTreeNode *parent = NULL;
3670   GtkCMCTreeNode *sibling;
3671   GtkCMCTreeNode *node;
3672
3673   cm_return_val_if_fail (GTK_IS_CMCTREE (clist), -1);
3674
3675   sibling = GTK_CMCTREE_NODE (g_list_nth (clist->row_list, row));
3676   if (sibling)
3677     parent = GTK_CMCTREE_ROW (sibling)->parent;
3678
3679   node = gtk_cmctree_insert_node (GTK_CMCTREE (clist), parent, sibling, text, 5,
3680                                 NULL, NULL, TRUE, FALSE);
3681
3682   if (GTK_CMCLIST_AUTO_SORT (clist) || !sibling)
3683     return g_list_position (clist->row_list, (GList *) node);
3684   
3685   return row;
3686 }
3687
3688 GtkCMCTreeNode * 
3689 gtk_cmctree_insert_node (GtkCMCTree     *ctree,
3690                        GtkCMCTreeNode *parent, 
3691                        GtkCMCTreeNode *sibling,
3692                        gchar        *text[],
3693                        guint8        spacing,
3694                        GdkPixbuf    *pixbuf_closed,
3695                        GdkPixbuf    *pixbuf_opened,
3696                        gboolean      is_leaf,
3697                        gboolean      expanded)
3698 {
3699   GtkCMCList *clist;
3700   GtkCMCTreeRow *new_row;
3701   GtkCMCTreeNode *node;
3702   GList *list;
3703   gint i;
3704
3705   cm_return_val_if_fail (GTK_IS_CMCTREE (ctree), NULL);
3706   if (sibling)
3707     cm_return_val_if_fail (GTK_CMCTREE_ROW (sibling)->parent == parent, NULL);
3708
3709   if (parent && GTK_CMCTREE_ROW (parent)->is_leaf)
3710     return NULL;
3711
3712   clist = GTK_CMCLIST (ctree);
3713
3714   /* create the row */
3715   new_row = row_new (ctree);
3716   list = g_list_alloc ();
3717   list->data = new_row;
3718   node = GTK_CMCTREE_NODE (list);
3719
3720   if (text)
3721     for (i = 0; i < clist->columns; i++)
3722       if (text[i] && i != ctree->tree_column)
3723         GTK_CMCLIST_GET_CLASS (clist)->set_cell_contents
3724           (clist, &(new_row->row), i, GTK_CMCELL_TEXT, text[i], 0, NULL);
3725
3726   set_node_info (ctree, node, text ?
3727                  text[ctree->tree_column] : NULL, spacing, pixbuf_closed,
3728                  pixbuf_opened, is_leaf, expanded);
3729
3730   /* sorted insertion */
3731   if (GTK_CMCLIST_AUTO_SORT (clist))
3732     {
3733       if (parent)
3734         sibling = GTK_CMCTREE_ROW (parent)->children;
3735       else
3736         sibling = GTK_CMCTREE_NODE (clist->row_list);
3737
3738       while (sibling && clist->compare
3739              (clist, GTK_CMCTREE_ROW (node), GTK_CMCTREE_ROW (sibling)) > 0)
3740         sibling = GTK_CMCTREE_ROW (sibling)->sibling;
3741     }
3742
3743   gtk_cmctree_link (ctree, node, parent, sibling, TRUE);
3744
3745   if (text && !GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist) &&
3746       gtk_cmctree_is_viewable (ctree, node))
3747     {
3748       for (i = 0; i < clist->columns; i++)
3749         if (clist->column[i].auto_resize)
3750           column_auto_resize (clist, &(new_row->row), i, 0);
3751     }
3752
3753   if (clist->rows == 1)
3754     {
3755       clist->focus_row = 0;
3756       if (clist->selection_mode == GTK_SELECTION_BROWSE)
3757         gtk_cmctree_select (ctree, node);
3758     }
3759
3760
3761   CLIST_REFRESH (clist);
3762
3763   return node;
3764 }
3765
3766 GtkCMCTreeNode *
3767 gtk_cmctree_insert_gnode (GtkCMCTree          *ctree,
3768                         GtkCMCTreeNode      *parent,
3769                         GtkCMCTreeNode      *sibling,
3770                         GNode             *gnode,
3771                         GtkCMCTreeGNodeFunc  func,
3772                         gpointer           data)
3773 {
3774   GtkCMCList *clist;
3775   GtkCMCTreeNode *cnode = NULL;
3776   GtkCMCTreeNode *child = NULL;
3777   GtkCMCTreeNode *new_child;
3778   GList *list;
3779   GNode *work;
3780   guint depth = 1;
3781
3782   cm_return_val_if_fail (GTK_IS_CMCTREE (ctree), NULL);
3783   cm_return_val_if_fail (gnode != NULL, NULL);
3784   cm_return_val_if_fail (func != NULL, NULL);
3785   if (sibling)
3786     cm_return_val_if_fail (GTK_CMCTREE_ROW (sibling)->parent == parent, NULL);
3787   
3788   clist = GTK_CMCLIST (ctree);
3789
3790   if (parent)
3791     depth = GTK_CMCTREE_ROW (parent)->level + 1;
3792
3793   list = g_list_alloc ();
3794   list->data = row_new (ctree);
3795   cnode = GTK_CMCTREE_NODE (list);
3796
3797   gtk_cmclist_freeze (clist);
3798
3799   set_node_info (ctree, cnode, "", 0, NULL, NULL, TRUE, FALSE);
3800
3801   if (!func (ctree, depth, gnode, cnode, data))
3802     {
3803       tree_delete_row (ctree, cnode, NULL);
3804       gtk_cmclist_thaw (clist);
3805       return NULL;
3806     }
3807
3808   if (GTK_CMCLIST_AUTO_SORT (clist))
3809     {
3810       if (parent)
3811         sibling = GTK_CMCTREE_ROW (parent)->children;
3812       else
3813         sibling = GTK_CMCTREE_NODE (clist->row_list);
3814
3815       while (sibling && clist->compare
3816              (clist, GTK_CMCTREE_ROW (cnode), GTK_CMCTREE_ROW (sibling)) > 0)
3817         sibling = GTK_CMCTREE_ROW (sibling)->sibling;
3818     }
3819
3820   gtk_cmctree_link (ctree, cnode, parent, sibling, TRUE);
3821
3822   for (work = g_node_last_child (gnode); work; work = work->prev)
3823     {
3824       new_child = gtk_cmctree_insert_gnode (ctree, cnode, child,
3825                                           work, func, data);
3826       if (new_child)
3827         child = new_child;
3828     }   
3829   
3830   gtk_cmclist_thaw (clist);
3831
3832   return cnode;
3833 }
3834
3835 GNode *
3836 gtk_cmctree_export_to_gnode (GtkCMCTree          *ctree,
3837                            GNode             *parent,
3838                            GNode             *sibling,
3839                            GtkCMCTreeNode      *node,
3840                            GtkCMCTreeGNodeFunc  func,
3841                            gpointer           data)
3842 {
3843   GtkCMCTreeNode *work;
3844   GNode *gnode;
3845   gint depth;
3846
3847   cm_return_val_if_fail (GTK_IS_CMCTREE (ctree), NULL);
3848   cm_return_val_if_fail (node != NULL, NULL);
3849   cm_return_val_if_fail (func != NULL, NULL);
3850   if (sibling)
3851     {
3852       cm_return_val_if_fail (parent != NULL, NULL);
3853       cm_return_val_if_fail (sibling->parent == parent, NULL);
3854     }
3855
3856   gnode = g_node_new (NULL);
3857   depth = g_node_depth (parent) + 1;
3858   
3859   if (!func (ctree, depth, gnode, node, data))
3860     {
3861       g_node_destroy (gnode);
3862       return NULL;
3863     }
3864
3865   if (parent)
3866     g_node_insert_before (parent, sibling, gnode);
3867
3868   if (!GTK_CMCTREE_ROW (node)->is_leaf)
3869     {
3870       GNode *new_sibling = NULL;
3871
3872       for (work = GTK_CMCTREE_ROW (node)->children; work;
3873            work = GTK_CMCTREE_ROW (work)->sibling)
3874         new_sibling = gtk_cmctree_export_to_gnode (ctree, gnode, new_sibling,
3875                                                  work, func, data);
3876
3877       g_node_reverse_children (gnode);
3878     }
3879
3880   return gnode;
3881 }
3882   
3883 static void
3884 real_remove_row (GtkCMCList *clist,
3885                  gint      row)
3886 {
3887   GtkCMCTreeNode *node;
3888
3889   cm_return_if_fail (GTK_IS_CMCTREE (clist));
3890
3891   node = GTK_CMCTREE_NODE (g_list_nth (clist->row_list, row));
3892
3893   if (node)
3894     gtk_cmctree_remove_node (GTK_CMCTREE (clist), node);
3895 }
3896
3897 void
3898 gtk_cmctree_remove_node (GtkCMCTree     *ctree, 
3899                        GtkCMCTreeNode *node)
3900 {
3901   GtkCMCList *clist;
3902
3903   cm_return_if_fail (GTK_IS_CMCTREE (ctree));
3904
3905   clist = GTK_CMCLIST (ctree);
3906
3907   gtk_cmclist_freeze (clist);
3908
3909   if (node)
3910     {
3911       gtk_cmctree_unlink (ctree, node, TRUE);
3912       gtk_cmctree_post_recursive (ctree, node, GTK_CMCTREE_FUNC (tree_delete),
3913                                 NULL);
3914       if (clist->selection_mode == GTK_SELECTION_BROWSE && !clist->selection &&
3915           clist->focus_row >= 0)
3916         gtk_cmclist_select_row (clist, clist->focus_row, -1);
3917
3918       auto_resize_columns (clist);
3919     }
3920   else
3921     gtk_cmclist_clear (clist);
3922
3923   gtk_cmclist_thaw (clist);
3924 }
3925
3926 static void
3927 real_clear (GtkCMCList *clist)
3928 {
3929   GtkCMCTree *ctree;
3930   GtkCMCTreeNode *work;
3931   GtkCMCTreeNode *ptr;
3932
3933   cm_return_if_fail (GTK_IS_CMCTREE (clist));
3934
3935   ctree = GTK_CMCTREE (clist);
3936
3937   /* remove all rows */
3938   work = GTK_CMCTREE_NODE (clist->row_list);
3939   clist->row_list = NULL;
3940   clist->row_list_end = NULL;
3941
3942   GTK_CMCLIST_SET_FLAG (clist, CMCLIST_AUTO_RESIZE_BLOCKED);
3943   while (work)
3944     {
3945       ptr = work;
3946       work = GTK_CMCTREE_ROW (work)->sibling;
3947       gtk_cmctree_post_recursive (ctree, ptr, GTK_CMCTREE_FUNC (tree_delete_row), 
3948                                 NULL);
3949     }
3950   GTK_CMCLIST_UNSET_FLAG (clist, CMCLIST_AUTO_RESIZE_BLOCKED);
3951
3952   parent_class->clear (clist);
3953 }
3954
3955
3956 /***********************************************************
3957  *  Generic recursive functions, querying / finding tree   *
3958  *  information                                            *
3959  ***********************************************************/
3960
3961
3962 void
3963 gtk_cmctree_post_recursive (GtkCMCTree     *ctree, 
3964                           GtkCMCTreeNode *node,
3965                           GtkCMCTreeFunc  func,
3966                           gpointer      data)
3967 {
3968   GtkCMCTreeNode *work;
3969   GtkCMCTreeNode *tmp;
3970
3971   cm_return_if_fail (GTK_IS_CMCTREE (ctree));
3972   cm_return_if_fail (func != NULL);
3973
3974   if (node)
3975     work = GTK_CMCTREE_ROW (node)->children;
3976   else
3977     work = GTK_CMCTREE_NODE (GTK_CMCLIST (ctree)->row_list);
3978
3979   while (work)
3980     {
3981       tmp = GTK_CMCTREE_ROW (work)->sibling;
3982       gtk_cmctree_post_recursive (ctree, work, func, data);
3983       work = tmp;
3984     }
3985
3986   if (node)
3987     func (ctree, node, data);
3988 }
3989
3990 void
3991 gtk_cmctree_post_recursive_to_depth (GtkCMCTree     *ctree, 
3992                                    GtkCMCTreeNode *node,
3993                                    gint          depth,
3994                                    GtkCMCTreeFunc  func,
3995                                    gpointer      data)
3996 {
3997   GtkCMCTreeNode *work;
3998   GtkCMCTreeNode *tmp;
3999
4000   cm_return_if_fail (GTK_IS_CMCTREE (ctree));
4001   cm_return_if_fail (func != NULL);
4002
4003   if (depth < 0)
4004     {
4005       gtk_cmctree_post_recursive (ctree, node, func, data);
4006       return;
4007     }
4008
4009   if (node)
4010     work = GTK_CMCTREE_ROW (node)->children;
4011   else
4012     work = GTK_CMCTREE_NODE (GTK_CMCLIST (ctree)->row_list);
4013
4014   if (work && GTK_CMCTREE_ROW (work)->level <= depth)
4015     {
4016       while (work)
4017         {
4018           tmp = GTK_CMCTREE_ROW (work)->sibling;
4019           gtk_cmctree_post_recursive_to_depth (ctree, work, depth, func, data);
4020           work = tmp;
4021         }
4022     }
4023
4024   if (node && GTK_CMCTREE_ROW (node)->level <= depth)
4025     func (ctree, node, data);
4026 }
4027
4028 void
4029 gtk_cmctree_pre_recursive (GtkCMCTree     *ctree, 
4030                          GtkCMCTreeNode *node,
4031                          GtkCMCTreeFunc  func,
4032                          gpointer      data)
4033 {
4034   GtkCMCTreeNode *work;
4035   GtkCMCTreeNode *tmp;
4036
4037   cm_return_if_fail (GTK_IS_CMCTREE (ctree));
4038   cm_return_if_fail (func != NULL);
4039
4040   if (node)
4041     {
4042       work = GTK_CMCTREE_ROW (node)->children;
4043       func (ctree, node, data);
4044     }
4045   else
4046     work = GTK_CMCTREE_NODE (GTK_CMCLIST (ctree)->row_list);
4047
4048   while (work)
4049     {
4050       tmp = GTK_CMCTREE_ROW (work)->sibling;
4051       gtk_cmctree_pre_recursive (ctree, work, func, data);
4052       work = tmp;
4053     }
4054 }
4055
4056 void
4057 gtk_cmctree_pre_recursive_to_depth (GtkCMCTree     *ctree, 
4058                                   GtkCMCTreeNode *node,
4059                                   gint          depth, 
4060                                   GtkCMCTreeFunc  func,
4061                                   gpointer      data)
4062 {
4063   GtkCMCTreeNode *work;
4064   GtkCMCTreeNode *tmp;
4065
4066   cm_return_if_fail (GTK_IS_CMCTREE (ctree));
4067   cm_return_if_fail (func != NULL);
4068
4069   if (depth < 0)
4070     {
4071       gtk_cmctree_pre_recursive (ctree, node, func, data);
4072       return;
4073     }
4074
4075   if (node)
4076     {
4077       work = GTK_CMCTREE_ROW (node)->children;
4078       if (GTK_CMCTREE_ROW (node)->level <= depth)
4079         func (ctree, node, data);
4080     }
4081   else
4082     work = GTK_CMCTREE_NODE (GTK_CMCLIST (ctree)->row_list);
4083
4084   if (work && GTK_CMCTREE_ROW (work)->level <= depth)
4085     {
4086       while (work)
4087         {
4088           tmp = GTK_CMCTREE_ROW (work)->sibling;
4089           gtk_cmctree_pre_recursive_to_depth (ctree, work, depth, func, data);
4090           work = tmp;
4091         }
4092     }
4093 }
4094
4095 gboolean
4096 gtk_cmctree_is_viewable (GtkCMCTree     *ctree, 
4097                        GtkCMCTreeNode *node)
4098
4099   GtkCMCTreeRow *work;
4100
4101   cm_return_val_if_fail (GTK_IS_CMCTREE (ctree), FALSE);
4102   cm_return_val_if_fail (node != NULL, FALSE);
4103
4104   work = GTK_CMCTREE_ROW (node);
4105
4106   while (work && work->parent && GTK_CMCTREE_ROW (work->parent)->expanded)
4107     work = GTK_CMCTREE_ROW (work->parent);
4108
4109   if (!work->parent)
4110     return TRUE;
4111
4112   return FALSE;
4113 }
4114
4115 GtkCMCTreeNode * 
4116 gtk_cmctree_last (GtkCMCTree     *ctree,
4117                 GtkCMCTreeNode *node)
4118 {
4119   cm_return_val_if_fail (GTK_IS_CMCTREE (ctree), NULL);
4120
4121   if (!node) 
4122     return NULL;
4123
4124   while (GTK_CMCTREE_ROW (node)->sibling)
4125     node = GTK_CMCTREE_ROW (node)->sibling;
4126   
4127   if (GTK_CMCTREE_ROW (node)->children)
4128     return gtk_cmctree_last (ctree, GTK_CMCTREE_ROW (node)->children);
4129   
4130   return node;
4131 }
4132
4133 GtkCMCTreeNode *
4134 gtk_cmctree_find_node_ptr (GtkCMCTree    *ctree,
4135                          GtkCMCTreeRow *ctree_row)
4136 {
4137   GtkCMCTreeNode *node;
4138   
4139   cm_return_val_if_fail (GTK_IS_CMCTREE (ctree), NULL);
4140   cm_return_val_if_fail (ctree_row != NULL, NULL);
4141   
4142   if (ctree_row->parent)
4143     node = GTK_CMCTREE_ROW (ctree_row->parent)->children;
4144   else
4145     node = GTK_CMCTREE_NODE (GTK_CMCLIST (ctree)->row_list);
4146
4147   while (GTK_CMCTREE_ROW (node) != ctree_row)
4148     node = GTK_CMCTREE_ROW (node)->sibling;
4149   
4150   return node;
4151 }
4152
4153 GtkCMCTreeNode *
4154 gtk_cmctree_node_nth (GtkCMCTree *ctree,
4155                     guint     row)
4156 {
4157   cm_return_val_if_fail (GTK_IS_CMCTREE (ctree), NULL);
4158
4159   if ((row >= GTK_CMCLIST(ctree)->rows))
4160     return NULL;
4161  
4162   return GTK_CMCTREE_NODE (g_list_nth (GTK_CMCLIST (ctree)->row_list, row));
4163 }
4164
4165 gboolean
4166 gtk_cmctree_find (GtkCMCTree     *ctree,
4167                 GtkCMCTreeNode *node,
4168                 GtkCMCTreeNode *child)
4169 {
4170   if (!child)
4171     return FALSE;
4172
4173   if (!node)
4174     node = GTK_CMCTREE_NODE (GTK_CMCLIST (ctree)->row_list);
4175
4176   while (node)
4177     {
4178       if (node == child) 
4179         return TRUE;
4180       if (GTK_CMCTREE_ROW (node)->children)
4181         {
4182           if (gtk_cmctree_find (ctree, GTK_CMCTREE_ROW (node)->children, child))
4183             return TRUE;
4184         }
4185       node = GTK_CMCTREE_ROW (node)->sibling;
4186     }
4187   return FALSE;
4188 }
4189
4190 gboolean
4191 gtk_cmctree_is_ancestor (GtkCMCTree     *ctree,
4192                        GtkCMCTreeNode *node,
4193                        GtkCMCTreeNode *child)
4194 {
4195   cm_return_val_if_fail (GTK_IS_CMCTREE (ctree), FALSE);
4196   cm_return_val_if_fail (node != NULL, FALSE);
4197
4198   if (GTK_CMCTREE_ROW (node)->children)
4199     return gtk_cmctree_find (ctree, GTK_CMCTREE_ROW (node)->children, child);
4200
4201   return FALSE;
4202 }
4203
4204 GtkCMCTreeNode *
4205 gtk_cmctree_find_by_row_data (GtkCMCTree     *ctree,
4206                             GtkCMCTreeNode *node,
4207                             gpointer      data)
4208 {
4209   GtkCMCTreeNode *work;
4210   
4211   if (!node)
4212     node = GTK_CMCTREE_NODE (GTK_CMCLIST (ctree)->row_list);
4213   
4214   while (node)
4215     {
4216       if (GTK_CMCTREE_ROW (node)->row.data == data) 
4217         return node;
4218       if (GTK_CMCTREE_ROW (node)->children &&
4219           (work = gtk_cmctree_find_by_row_data 
4220            (ctree, GTK_CMCTREE_ROW (node)->children, data)))
4221         return work;
4222       node = GTK_CMCTREE_ROW (node)->sibling;
4223     }
4224   return NULL;
4225 }
4226
4227 GList *
4228 gtk_cmctree_find_all_by_row_data (GtkCMCTree     *ctree,
4229                                 GtkCMCTreeNode *node,
4230                                 gpointer      data)
4231 {
4232   GList *list = NULL;
4233
4234   cm_return_val_if_fail (GTK_IS_CMCTREE (ctree), NULL);
4235
4236   /* if node == NULL then look in the whole tree */
4237   if (!node)
4238     node = GTK_CMCTREE_NODE (GTK_CMCLIST (ctree)->row_list);
4239
4240   while (node)
4241     {
4242       if (GTK_CMCTREE_ROW (node)->row.data == data)
4243         list = g_list_append (list, node);
4244
4245       if (GTK_CMCTREE_ROW (node)->children)
4246         {
4247           GList *sub_list;
4248
4249           sub_list = gtk_cmctree_find_all_by_row_data (ctree,
4250                                                      GTK_CMCTREE_ROW
4251                                                      (node)->children,
4252                                                      data);
4253           list = g_list_concat (list, sub_list);
4254         }
4255       node = GTK_CMCTREE_ROW (node)->sibling;
4256     }
4257   return list;
4258 }
4259
4260 GtkCMCTreeNode *
4261 gtk_cmctree_find_by_row_data_custom (GtkCMCTree     *ctree,
4262                                    GtkCMCTreeNode *node,
4263                                    gpointer      data,
4264                                    GCompareFunc  func)
4265 {
4266   GtkCMCTreeNode *work;
4267
4268   cm_return_val_if_fail (func != NULL, NULL);
4269
4270   if (!node)
4271     node = GTK_CMCTREE_NODE (GTK_CMCLIST (ctree)->row_list);
4272
4273   while (node)
4274     {
4275       if (!func (GTK_CMCTREE_ROW (node)->row.data, data))
4276         return node;
4277       if (GTK_CMCTREE_ROW (node)->children &&
4278           (work = gtk_cmctree_find_by_row_data_custom
4279            (ctree, GTK_CMCTREE_ROW (node)->children, data, func)))
4280         return work;
4281       node = GTK_CMCTREE_ROW (node)->sibling;
4282     }
4283   return NULL;
4284 }
4285
4286 GList *
4287 gtk_cmctree_find_all_by_row_data_custom (GtkCMCTree     *ctree,
4288                                        GtkCMCTreeNode *node,
4289                                        gpointer      data,
4290                                        GCompareFunc  func)
4291 {
4292   GList *list = NULL;
4293
4294   cm_return_val_if_fail (GTK_IS_CMCTREE (ctree), NULL);
4295   cm_return_val_if_fail (func != NULL, NULL);
4296
4297   /* if node == NULL then look in the whole tree */
4298   if (!node)
4299     node = GTK_CMCTREE_NODE (GTK_CMCLIST (ctree)->row_list);
4300
4301   while (node)
4302     {
4303       if (!func (GTK_CMCTREE_ROW (node)->row.data, data))
4304         list = g_list_append (list, node);
4305
4306       if (GTK_CMCTREE_ROW (node)->children)
4307         {
4308           GList *sub_list;
4309
4310           sub_list = gtk_cmctree_find_all_by_row_data_custom (ctree,
4311                                                             GTK_CMCTREE_ROW
4312                                                             (node)->children,
4313                                                             data,
4314                                                             func);
4315           list = g_list_concat (list, sub_list);
4316         }
4317       node = GTK_CMCTREE_ROW (node)->sibling;
4318     }
4319   return list;
4320 }
4321
4322 gboolean
4323 gtk_cmctree_is_hot_spot (GtkCMCTree *ctree, 
4324                        gint      x, 
4325                        gint      y)
4326 {
4327   GtkCMCTreeNode *node;
4328   gint column;
4329   gint row;
4330   
4331   cm_return_val_if_fail (GTK_IS_CMCTREE (ctree), FALSE);
4332
4333   if (gtk_cmclist_get_selection_info (GTK_CMCLIST (ctree), x, y, &row, &column))
4334     if ((node = GTK_CMCTREE_NODE(g_list_nth (GTK_CMCLIST (ctree)->row_list, row))))
4335       return ctree_is_hot_spot (ctree, node, row, x, y);
4336
4337   return FALSE;
4338 }
4339
4340
4341 /***********************************************************
4342  *   Tree signals : move, expand, collapse, (un)select     *
4343  ***********************************************************/
4344
4345
4346 void
4347 gtk_cmctree_move (GtkCMCTree     *ctree,
4348                 GtkCMCTreeNode *node,
4349                 GtkCMCTreeNode *new_parent, 
4350                 GtkCMCTreeNode *new_sibling)
4351 {
4352   cm_return_if_fail (GTK_IS_CMCTREE (ctree));
4353   cm_return_if_fail (node != NULL);
4354   
4355   g_signal_emit (G_OBJECT (ctree), ctree_signals[TREE_MOVE], 0, node,
4356                    new_parent, new_sibling);
4357 }
4358
4359 void
4360 gtk_cmctree_expand (GtkCMCTree     *ctree,
4361                   GtkCMCTreeNode *node)
4362 {
4363   cm_return_if_fail (GTK_IS_CMCTREE (ctree));
4364   cm_return_if_fail (node != NULL);
4365   
4366   if (GTK_CMCTREE_ROW (node)->is_leaf)
4367     return;
4368
4369   g_signal_emit (G_OBJECT (ctree), ctree_signals[TREE_EXPAND], 0, node);
4370 }
4371
4372 void 
4373 gtk_cmctree_expand_recursive (GtkCMCTree     *ctree,
4374                             GtkCMCTreeNode *node)
4375 {
4376   GtkCMCList *clist;
4377   gboolean thaw = FALSE;
4378
4379   cm_return_if_fail (GTK_IS_CMCTREE (ctree));
4380
4381   clist = GTK_CMCLIST (ctree);
4382
4383   if (node && GTK_CMCTREE_ROW (node)->is_leaf)
4384     return;
4385
4386   if (CLIST_UNFROZEN (clist) && (!node || gtk_cmctree_is_viewable (ctree, node)))
4387     {
4388       gtk_cmclist_freeze (clist);
4389       thaw = TRUE;
4390     }
4391
4392   gtk_cmctree_post_recursive (ctree, node, GTK_CMCTREE_FUNC (tree_expand), NULL);
4393
4394   if (thaw)
4395     gtk_cmclist_thaw (clist);
4396 }
4397
4398 void 
4399 gtk_cmctree_expand_to_depth (GtkCMCTree     *ctree,
4400                            GtkCMCTreeNode *node,
4401                            gint          depth)
4402 {
4403   GtkCMCList *clist;
4404   gboolean thaw = FALSE;
4405
4406   cm_return_if_fail (GTK_IS_CMCTREE (ctree));
4407
4408   clist = GTK_CMCLIST (ctree);
4409
4410   if (node && GTK_CMCTREE_ROW (node)->is_leaf)
4411     return;
4412
4413   if (CLIST_UNFROZEN (clist) && (!node || gtk_cmctree_is_viewable (ctree, node)))
4414     {
4415       gtk_cmclist_freeze (clist);
4416       thaw = TRUE;
4417     }
4418
4419   gtk_cmctree_post_recursive_to_depth (ctree, node, depth,
4420                                      GTK_CMCTREE_FUNC (tree_expand), NULL);
4421
4422   if (thaw)
4423     gtk_cmclist_thaw (clist);
4424 }
4425
4426 void
4427 gtk_cmctree_collapse (GtkCMCTree     *ctree,
4428                     GtkCMCTreeNode *node)
4429 {
4430   cm_return_if_fail (GTK_IS_CMCTREE (ctree));
4431   cm_return_if_fail (node != NULL);
4432   
4433   if (GTK_CMCTREE_ROW (node)->is_leaf)
4434     return;
4435
4436   g_signal_emit (G_OBJECT (ctree), ctree_signals[TREE_COLLAPSE], 0, node);
4437 }
4438
4439 void 
4440 gtk_cmctree_collapse_recursive (GtkCMCTree     *ctree,
4441                               GtkCMCTreeNode *node)
4442 {
4443   GtkCMCList *clist;
4444   gboolean thaw = FALSE;
4445   gint i;
4446
4447   cm_return_if_fail (GTK_IS_CMCTREE (ctree));
4448
4449   if (node && GTK_CMCTREE_ROW (node)->is_leaf)
4450     return;
4451
4452   clist = GTK_CMCLIST (ctree);
4453
4454   if (CLIST_UNFROZEN (clist) && (!node || gtk_cmctree_is_viewable (ctree, node)))
4455     {
4456       gtk_cmclist_freeze (clist);
4457       thaw = TRUE;
4458     }
4459
4460   GTK_CMCLIST_SET_FLAG (clist, CMCLIST_AUTO_RESIZE_BLOCKED);
4461   gtk_cmctree_post_recursive (ctree, node, GTK_CMCTREE_FUNC (tree_collapse), NULL);
4462   GTK_CMCLIST_UNSET_FLAG (clist, CMCLIST_AUTO_RESIZE_BLOCKED);
4463   for (i = 0; i < clist->columns; i++)
4464     if (clist->column[i].auto_resize)
4465       gtk_cmclist_set_column_width (clist, i,
4466                                   gtk_cmclist_optimal_column_width (clist, i));
4467
4468   if (thaw)
4469     gtk_cmclist_thaw (clist);
4470 }
4471
4472 void 
4473 gtk_cmctree_collapse_to_depth (GtkCMCTree     *ctree,
4474                              GtkCMCTreeNode *node,
4475                              gint          depth)
4476 {
4477   GtkCMCList *clist;
4478   gboolean thaw = FALSE;
4479   gint i;
4480
4481   cm_return_if_fail (GTK_IS_CMCTREE (ctree));
4482
4483   if (node && GTK_CMCTREE_ROW (node)->is_leaf)
4484     return;
4485
4486   clist = GTK_CMCLIST (ctree);
4487
4488   if (CLIST_UNFROZEN (clist) && (!node || gtk_cmctree_is_viewable (ctree, node)))
4489     {
4490       gtk_cmclist_freeze (clist);
4491       thaw = TRUE;
4492     }
4493
4494   GTK_CMCLIST_SET_FLAG (clist, CMCLIST_AUTO_RESIZE_BLOCKED);
4495   gtk_cmctree_post_recursive_to_depth (ctree, node, depth,
4496                                      GTK_CMCTREE_FUNC (tree_collapse_to_depth),
4497                                      GINT_TO_POINTER (depth));
4498   GTK_CMCLIST_UNSET_FLAG (clist, CMCLIST_AUTO_RESIZE_BLOCKED);
4499   for (i = 0; i < clist->columns; i++)
4500     if (clist->column[i].auto_resize)
4501       gtk_cmclist_set_column_width (clist, i,
4502                                   gtk_cmclist_optimal_column_width (clist, i));
4503
4504   if (thaw)
4505     gtk_cmclist_thaw (clist);
4506 }
4507
4508 void
4509 gtk_cmctree_toggle_expansion (GtkCMCTree     *ctree,
4510                             GtkCMCTreeNode *node)
4511 {
4512   cm_return_if_fail (GTK_IS_CMCTREE (ctree));
4513   cm_return_if_fail (node != NULL);
4514   
4515   if (GTK_CMCTREE_ROW (node)->is_leaf)
4516     return;
4517
4518   tree_toggle_expansion (ctree, node, NULL);
4519 }
4520
4521 void 
4522 gtk_cmctree_toggle_expansion_recursive (GtkCMCTree     *ctree,
4523                                       GtkCMCTreeNode *node)
4524 {
4525   GtkCMCList *clist;
4526   gboolean thaw = FALSE;
4527
4528   cm_return_if_fail (GTK_IS_CMCTREE (ctree));
4529   
4530   if (node && GTK_CMCTREE_ROW (node)->is_leaf)
4531     return;
4532
4533   clist = GTK_CMCLIST (ctree);
4534
4535   if (CLIST_UNFROZEN (clist) && (!node || gtk_cmctree_is_viewable (ctree, node)))
4536     {
4537       gtk_cmclist_freeze (clist);
4538       thaw = TRUE;
4539     }
4540   
4541   gtk_cmctree_post_recursive (ctree, node,
4542                             GTK_CMCTREE_FUNC (tree_toggle_expansion), NULL);
4543
4544   if (thaw)
4545     gtk_cmclist_thaw (clist);
4546 }
4547
4548 void
4549 gtk_cmctree_select (GtkCMCTree     *ctree, 
4550                   GtkCMCTreeNode *node)
4551 {
4552   cm_return_if_fail (GTK_IS_CMCTREE (ctree));
4553   cm_return_if_fail (node != NULL);
4554
4555   if (GTK_CMCTREE_ROW (node)->row.selectable)
4556     g_signal_emit (G_OBJECT (ctree), ctree_signals[TREE_SELECT_ROW], 0,
4557                      node, -1);
4558 }
4559
4560 void
4561 gtk_cmctree_unselect (GtkCMCTree     *ctree, 
4562                     GtkCMCTreeNode *node)
4563 {
4564   cm_return_if_fail (GTK_IS_CMCTREE (ctree));
4565   cm_return_if_fail (node != NULL);
4566
4567   g_signal_emit (G_OBJECT (ctree), ctree_signals[TREE_UNSELECT_ROW], 0,
4568                    node, -1);
4569 }
4570
4571 void
4572 gtk_cmctree_select_recursive (GtkCMCTree     *ctree, 
4573                             GtkCMCTreeNode *node)
4574 {
4575   gtk_cmctree_real_select_recursive (ctree, node, TRUE);
4576 }
4577
4578 void
4579 gtk_cmctree_unselect_recursive (GtkCMCTree     *ctree, 
4580                               GtkCMCTreeNode *node)
4581 {
4582   gtk_cmctree_real_select_recursive (ctree, node, FALSE);
4583 }
4584
4585 void
4586 gtk_cmctree_real_select_recursive (GtkCMCTree     *ctree, 
4587                                  GtkCMCTreeNode *node, 
4588                                  gint          state)
4589 {
4590   GtkCMCList *clist;
4591   gboolean thaw = FALSE;
4592
4593   cm_return_if_fail (GTK_IS_CMCTREE (ctree));
4594
4595   clist = GTK_CMCLIST (ctree);
4596
4597   if ((state && 
4598        (clist->selection_mode ==  GTK_SELECTION_BROWSE ||
4599         clist->selection_mode == GTK_SELECTION_SINGLE)) ||
4600       (!state && clist->selection_mode ==  GTK_SELECTION_BROWSE))
4601     return;
4602
4603   if (CLIST_UNFROZEN (clist) && (!node || gtk_cmctree_is_viewable (ctree, node)))
4604     {
4605       gtk_cmclist_freeze (clist);
4606       thaw = TRUE;
4607     }
4608
4609   if (clist->selection_mode == GTK_SELECTION_MULTIPLE)
4610     {
4611       GTK_CMCLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
4612       
4613       g_list_free (clist->undo_selection);
4614       g_list_free (clist->undo_unselection);
4615       clist->undo_selection = NULL;
4616       clist->undo_unselection = NULL;
4617     }
4618
4619   if (state)
4620     gtk_cmctree_post_recursive (ctree, node,
4621                               GTK_CMCTREE_FUNC (tree_select), NULL);
4622   else 
4623     gtk_cmctree_post_recursive (ctree, node,
4624                               GTK_CMCTREE_FUNC (tree_unselect), NULL);
4625   
4626   if (thaw)
4627     gtk_cmclist_thaw (clist);
4628 }
4629
4630
4631 /***********************************************************
4632  *           Analogons of GtkCMCList functions               *
4633  ***********************************************************/
4634
4635
4636 void 
4637 gtk_cmctree_node_set_text (GtkCMCTree     *ctree,
4638                          GtkCMCTreeNode *node,
4639                          gint          column,
4640                          const gchar  *text)
4641 {
4642   GtkCMCList *clist;
4643
4644   cm_return_if_fail (GTK_IS_CMCTREE (ctree));
4645   cm_return_if_fail (node != NULL);
4646
4647   if (column < 0 || column >= GTK_CMCLIST (ctree)->columns)
4648     return;
4649   
4650   clist = GTK_CMCLIST (ctree);
4651
4652   GTK_CMCLIST_GET_CLASS (clist)->set_cell_contents
4653     (clist, &(GTK_CMCTREE_ROW (node)->row), column, GTK_CMCELL_TEXT,
4654      text, 0, NULL);
4655
4656   tree_draw_node (ctree, node);
4657 }
4658
4659 void 
4660 gtk_cmctree_node_set_pixbuf (GtkCMCTree     *ctree,
4661                            GtkCMCTreeNode *node,
4662                            gint          column,
4663                            GdkPixbuf    *pixbuf)
4664 {
4665   GtkCMCList *clist;
4666
4667   cm_return_if_fail (GTK_IS_CMCTREE (ctree));
4668   cm_return_if_fail (node != NULL);
4669   cm_return_if_fail (pixbuf != NULL);
4670
4671   if (column < 0 || column >= GTK_CMCLIST (ctree)->columns)
4672     return;
4673
4674   g_object_ref (pixbuf);
4675
4676   clist = GTK_CMCLIST (ctree);
4677
4678   GTK_CMCLIST_GET_CLASS (clist)->set_cell_contents
4679     (clist, &(GTK_CMCTREE_ROW (node)->row), column, GTK_CMCELL_PIXBUF,
4680      NULL, 0, pixbuf);
4681
4682   tree_draw_node (ctree, node);
4683 }
4684
4685 void 
4686 gtk_cmctree_node_set_pixtext (GtkCMCTree     *ctree,
4687                             GtkCMCTreeNode *node,
4688                             gint          column,
4689                             const gchar  *text,
4690                             guint8        spacing,
4691                             GdkPixbuf    *pixbuf)
4692 {
4693   GtkCMCList *clist;
4694
4695   cm_return_if_fail (GTK_IS_CMCTREE (ctree));
4696   cm_return_if_fail (node != NULL);
4697   if (column != ctree->tree_column)
4698     cm_return_if_fail (pixbuf != NULL);
4699   if (column < 0 || column >= GTK_CMCLIST (ctree)->columns)
4700     return;
4701
4702   clist = GTK_CMCLIST (ctree);
4703
4704   if (pixbuf)
4705     {
4706       g_object_ref (pixbuf);
4707     }
4708
4709   GTK_CMCLIST_GET_CLASS (clist)->set_cell_contents
4710     (clist, &(GTK_CMCTREE_ROW (node)->row), column, GTK_CMCELL_PIXTEXT,
4711      text, spacing, pixbuf);
4712
4713   tree_draw_node (ctree, node);
4714 }
4715
4716 void 
4717 gtk_cmctree_set_node_info (GtkCMCTree     *ctree,
4718                          GtkCMCTreeNode *node,
4719                          const gchar  *text,
4720                          guint8        spacing,
4721                          GdkPixbuf    *pixbuf_closed,
4722                          GdkPixbuf    *pixbuf_opened,
4723                          gboolean      is_leaf,
4724                          gboolean      expanded)
4725 {
4726   gboolean old_leaf;
4727   gboolean old_expanded;
4728  
4729   cm_return_if_fail (GTK_IS_CMCTREE (ctree));
4730   cm_return_if_fail (node != NULL);
4731
4732   old_leaf = GTK_CMCTREE_ROW (node)->is_leaf;
4733   old_expanded = GTK_CMCTREE_ROW (node)->expanded;
4734
4735   if (is_leaf && GTK_CMCTREE_ROW (node)->children)
4736     {
4737       GtkCMCTreeNode *work;
4738       GtkCMCTreeNode *ptr;
4739       
4740       work = GTK_CMCTREE_ROW (node)->children;
4741       while (work)
4742         {
4743           ptr = work;
4744           work = GTK_CMCTREE_ROW (work)->sibling;
4745           gtk_cmctree_remove_node (ctree, ptr);
4746         }
4747     }
4748
4749   set_node_info (ctree, node, text, spacing, pixbuf_closed,
4750                  pixbuf_opened, is_leaf, expanded);
4751
4752   if (!is_leaf && !old_leaf)
4753     {
4754       GTK_CMCTREE_ROW (node)->expanded = old_expanded;
4755       if (expanded && !old_expanded)
4756         gtk_cmctree_expand (ctree, node);
4757       else if (!expanded && old_expanded)
4758         gtk_cmctree_collapse (ctree, node);
4759     }
4760
4761   GTK_CMCTREE_ROW (node)->expanded = (is_leaf) ? FALSE : expanded;
4762   
4763   tree_draw_node (ctree, node);
4764 }
4765
4766 void
4767 gtk_cmctree_node_set_shift (GtkCMCTree     *ctree,
4768                           GtkCMCTreeNode *node,
4769                           gint          column,
4770                           gint          vertical,
4771                           gint          horizontal)
4772 {
4773   GtkCMCList *clist;
4774   GtkRequisition requisition;
4775   gboolean visible = FALSE;
4776
4777   cm_return_if_fail (GTK_IS_CMCTREE (ctree));
4778   cm_return_if_fail (node != NULL);
4779
4780   if (column < 0 || column >= GTK_CMCLIST (ctree)->columns)
4781     return;
4782
4783   clist = GTK_CMCLIST (ctree);
4784
4785   if (clist->column[column].auto_resize &&
4786       !GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist))
4787     {
4788       visible = gtk_cmctree_is_viewable (ctree, node);
4789       if (visible)
4790         GTK_CMCLIST_GET_CLASS (clist)->cell_size_request
4791           (clist, &GTK_CMCTREE_ROW (node)->row, column, &requisition);
4792     }
4793
4794   GTK_CMCTREE_ROW (node)->row.cell[column].vertical   = vertical;
4795   GTK_CMCTREE_ROW (node)->row.cell[column].horizontal = horizontal;
4796
4797   if (visible)
4798     column_auto_resize (clist, &GTK_CMCTREE_ROW (node)->row,
4799                         column, requisition.width);
4800
4801   tree_draw_node (ctree, node);
4802 }
4803
4804 static void
4805 remove_grab (GtkCMCList *clist)
4806 {
4807   if (gdk_display_pointer_is_grabbed (gtk_widget_get_display (GTK_WIDGET (clist))) && 
4808       gtkut_widget_has_grab (GTK_WIDGET(clist)))
4809     {
4810       gtk_grab_remove (GTK_WIDGET (clist));
4811       gdk_display_pointer_ungrab (gtk_widget_get_display (GTK_WIDGET (clist)),
4812                                   GDK_CURRENT_TIME);
4813     }
4814
4815   if (clist->htimer)
4816     {
4817       g_source_remove (clist->htimer);
4818       clist->htimer = 0;
4819     }
4820
4821   if (clist->vtimer)
4822     {
4823       g_source_remove (clist->vtimer);
4824       clist->vtimer = 0;
4825     }
4826 }
4827
4828 void
4829 gtk_cmctree_node_set_selectable (GtkCMCTree     *ctree,
4830                                GtkCMCTreeNode *node,
4831                                gboolean      selectable)
4832 {
4833   cm_return_if_fail (GTK_IS_CMCTREE (ctree));
4834   cm_return_if_fail (node != NULL);
4835
4836   if (selectable == GTK_CMCTREE_ROW (node)->row.selectable)
4837     return;
4838
4839   GTK_CMCTREE_ROW (node)->row.selectable = selectable;
4840
4841   if (!selectable && GTK_CMCTREE_ROW (node)->row.state == GTK_STATE_SELECTED)
4842     {
4843       GtkCMCList *clist;
4844
4845       clist = GTK_CMCLIST (ctree);
4846
4847       if (clist->anchor >= 0 &&
4848           clist->selection_mode == GTK_SELECTION_MULTIPLE)
4849         {
4850           clist->drag_button = 0;
4851           remove_grab (clist);
4852
4853           GTK_CMCLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
4854         }
4855       gtk_cmctree_unselect (ctree, node);
4856     }      
4857 }
4858
4859 gboolean
4860 gtk_cmctree_node_get_selectable (GtkCMCTree     *ctree,
4861                                GtkCMCTreeNode *node)
4862 {
4863   cm_return_val_if_fail (node != NULL, FALSE);
4864
4865   return GTK_CMCTREE_ROW (node)->row.selectable;
4866 }
4867
4868 GtkCMCellType 
4869 gtk_cmctree_node_get_cell_type (GtkCMCTree     *ctree,
4870                               GtkCMCTreeNode *node,
4871                               gint          column)
4872 {
4873   cm_return_val_if_fail (GTK_IS_CMCTREE (ctree), -1);
4874   cm_return_val_if_fail (node != NULL, -1);
4875
4876   if (column < 0 || column >= GTK_CMCLIST (ctree)->columns)
4877     return -1;
4878
4879   return GTK_CMCTREE_ROW (node)->row.cell[column].type;
4880 }
4881
4882 gboolean
4883 gtk_cmctree_node_get_text (GtkCMCTree      *ctree,
4884                          GtkCMCTreeNode  *node,
4885                          gint           column,
4886                          gchar        **text)
4887 {
4888   cm_return_val_if_fail (GTK_IS_CMCTREE (ctree), FALSE);
4889   cm_return_val_if_fail (node != NULL, FALSE);
4890
4891   if (column < 0 || column >= GTK_CMCLIST (ctree)->columns)
4892     return FALSE;
4893
4894   if (GTK_CMCTREE_ROW (node)->row.cell[column].type != GTK_CMCELL_TEXT)
4895     return FALSE;
4896
4897   if (text)
4898     *text = GTK_CMCELL_TEXT (GTK_CMCTREE_ROW (node)->row.cell[column])->text;
4899
4900   return TRUE;
4901 }
4902
4903 gboolean
4904 gtk_cmctree_node_get_pixbuf (GtkCMCTree     *ctree,
4905                            GtkCMCTreeNode *node,
4906                            gint          column,
4907                            GdkPixbuf   **pixbuf)
4908 {
4909   cm_return_val_if_fail (GTK_IS_CMCTREE (ctree), FALSE);
4910   cm_return_val_if_fail (node != NULL, FALSE);
4911
4912   if (column < 0 || column >= GTK_CMCLIST (ctree)->columns)
4913     return FALSE;
4914
4915   if (GTK_CMCTREE_ROW (node)->row.cell[column].type != GTK_CMCELL_PIXBUF)
4916     return FALSE;
4917
4918   if (pixbuf)
4919     *pixbuf = GTK_CMCELL_PIXBUF (GTK_CMCTREE_ROW (node)->row.cell[column])->pixbuf;
4920
4921   return TRUE;
4922 }
4923
4924 gboolean
4925 gtk_cmctree_node_get_pixtext (GtkCMCTree      *ctree,
4926                             GtkCMCTreeNode  *node,
4927                             gint           column,
4928                             gchar        **text,
4929                             guint8        *spacing,
4930                             GdkPixbuf    **pixbuf)
4931 {
4932   cm_return_val_if_fail (GTK_IS_CMCTREE (ctree), FALSE);
4933   cm_return_val_if_fail (node != NULL, FALSE);
4934   
4935   if (column < 0 || column >= GTK_CMCLIST (ctree)->columns)
4936     return FALSE;
4937   
4938   if (GTK_CMCTREE_ROW (node)->row.cell[column].type != GTK_CMCELL_PIXTEXT)
4939     return FALSE;
4940   
4941   if (text)
4942     *text = GTK_CMCELL_PIXTEXT (GTK_CMCTREE_ROW (node)->row.cell[column])->text;
4943   if (spacing)
4944     *spacing = GTK_CMCELL_PIXTEXT (GTK_CMCTREE_ROW 
4945                                  (node)->row.cell[column])->spacing;
4946   if (pixbuf)
4947     *pixbuf = GTK_CMCELL_PIXTEXT (GTK_CMCTREE_ROW 
4948                                 (node)->row.cell[column])->pixbuf;
4949   
4950   return TRUE;
4951 }
4952
4953 gboolean
4954 gtk_cmctree_get_node_info (GtkCMCTree      *ctree,
4955                          GtkCMCTreeNode  *node,
4956                          gchar        **text,
4957                          guint8        *spacing,
4958                          GdkPixbuf    **pixbuf_closed,
4959                          GdkPixbuf    **pixbuf_opened,
4960                          gboolean      *is_leaf,
4961                          gboolean      *expanded)
4962 {
4963   cm_return_val_if_fail (GTK_IS_CMCTREE (ctree), FALSE);
4964   cm_return_val_if_fail (node != NULL, FALSE);
4965   
4966   if (text)
4967     *text = GTK_CMCELL_PIXTEXT 
4968       (GTK_CMCTREE_ROW (node)->row.cell[ctree->tree_column])->text;
4969   if (spacing)
4970     *spacing = GTK_CMCELL_PIXTEXT 
4971       (GTK_CMCTREE_ROW (node)->row.cell[ctree->tree_column])->spacing;
4972   if (pixbuf_closed)
4973     *pixbuf_closed = GTK_CMCTREE_ROW (node)->pixbuf_closed;
4974   if (pixbuf_opened)
4975     *pixbuf_opened = GTK_CMCTREE_ROW (node)->pixbuf_opened;
4976   if (is_leaf)
4977     *is_leaf = GTK_CMCTREE_ROW (node)->is_leaf;
4978   if (expanded)
4979     *expanded = GTK_CMCTREE_ROW (node)->expanded;
4980   
4981   return TRUE;
4982 }
4983
4984 void
4985 gtk_cmctree_node_set_cell_style (GtkCMCTree     *ctree,
4986                                GtkCMCTreeNode *node,
4987                                gint          column,
4988                                GtkStyle     *style)
4989 {
4990   GtkCMCList *clist;
4991   GtkRequisition requisition;
4992   gboolean visible = FALSE;
4993
4994   cm_return_if_fail (GTK_IS_CMCTREE (ctree));
4995   cm_return_if_fail (node != NULL);
4996
4997   clist = GTK_CMCLIST (ctree);
4998
4999   if (column < 0 || column >= clist->columns)
5000     return;
5001
5002   if (GTK_CMCTREE_ROW (node)->row.cell[column].style == style)
5003     return;
5004
5005   if (clist->column[column].auto_resize &&
5006       !GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist))
5007     {
5008       visible = gtk_cmctree_is_viewable (ctree, node);
5009       if (visible)
5010         GTK_CMCLIST_GET_CLASS (clist)->cell_size_request
5011           (clist, &GTK_CMCTREE_ROW (node)->row, column, &requisition);
5012     }
5013
5014   if (GTK_CMCTREE_ROW (node)->row.cell[column].style)
5015     {
5016       if (gtkut_widget_get_realized (GTK_WIDGET(ctree)))
5017         gtk_style_detach (GTK_CMCTREE_ROW (node)->row.cell[column].style);
5018       g_object_unref (GTK_CMCTREE_ROW (node)->row.cell[column].style);
5019     }
5020
5021   GTK_CMCTREE_ROW (node)->row.cell[column].style = style;
5022
5023   if (GTK_CMCTREE_ROW (node)->row.cell[column].style)
5024     {
5025       g_object_ref (GTK_CMCTREE_ROW (node)->row.cell[column].style);
5026       
5027       if (gtkut_widget_get_realized (GTK_WIDGET(ctree)))
5028         GTK_CMCTREE_ROW (node)->row.cell[column].style =
5029           gtk_style_attach (GTK_CMCTREE_ROW (node)->row.cell[column].style,
5030                             clist->clist_window);
5031     }
5032
5033   if (visible)
5034     column_auto_resize (clist, &GTK_CMCTREE_ROW (node)->row, column,
5035                         requisition.width);
5036
5037   tree_draw_node (ctree, node);
5038 }
5039
5040 GtkStyle *
5041 gtk_cmctree_node_get_cell_style (GtkCMCTree     *ctree,
5042                                GtkCMCTreeNode *node,
5043                                gint          column)
5044 {
5045   cm_return_val_if_fail (GTK_IS_CMCTREE (ctree), NULL);
5046   cm_return_val_if_fail (node != NULL, NULL);
5047
5048   if (column < 0 || column >= GTK_CMCLIST (ctree)->columns)
5049     return NULL;
5050
5051   return GTK_CMCTREE_ROW (node)->row.cell[column].style;
5052 }
5053
5054 void
5055 gtk_cmctree_node_set_row_style (GtkCMCTree     *ctree,
5056                               GtkCMCTreeNode *node,
5057                               GtkStyle     *style)
5058 {
5059   GtkCMCList *clist;
5060   GtkRequisition requisition;
5061   gboolean visible;
5062   gint *old_width = NULL;
5063   gint i;
5064
5065   cm_return_if_fail (GTK_IS_CMCTREE (ctree));
5066   cm_return_if_fail (node != NULL);
5067
5068   clist = GTK_CMCLIST (ctree);
5069
5070   if (GTK_CMCTREE_ROW (node)->row.style == style)
5071     return;
5072   
5073   visible = gtk_cmctree_is_viewable (ctree, node);
5074   if (visible && !GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist))
5075     {
5076       old_width = g_new (gint, clist->columns);
5077       for (i = 0; i < clist->columns; i++)
5078         if (clist->column[i].auto_resize)
5079           {
5080             GTK_CMCLIST_GET_CLASS (clist)->cell_size_request
5081               (clist, &GTK_CMCTREE_ROW (node)->row, i, &requisition);
5082             old_width[i] = requisition.width;
5083           }
5084     }
5085
5086   if (GTK_CMCTREE_ROW (node)->row.style)
5087     {
5088       if (gtkut_widget_get_realized (GTK_WIDGET(ctree)))
5089         gtk_style_detach (GTK_CMCTREE_ROW (node)->row.style);
5090       g_object_unref (GTK_CMCTREE_ROW (node)->row.style);
5091     }
5092
5093   GTK_CMCTREE_ROW (node)->row.style = style;
5094
5095   if (GTK_CMCTREE_ROW (node)->row.style)
5096     {
5097       g_object_ref (GTK_CMCTREE_ROW (node)->row.style);
5098       
5099       if (gtkut_widget_get_realized (GTK_WIDGET(ctree)))
5100         GTK_CMCTREE_ROW (node)->row.style =
5101           gtk_style_attach (GTK_CMCTREE_ROW (node)->row.style,
5102                             clist->clist_window);
5103     }
5104
5105   if (visible && !GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist))
5106     {
5107       for (i = 0; i < clist->columns; i++)
5108         if (clist->column[i].auto_resize)
5109           column_auto_resize (clist, &GTK_CMCTREE_ROW (node)->row, i,
5110                               old_width[i]);
5111       g_free (old_width);
5112     }
5113   tree_draw_node (ctree, node);
5114 }
5115
5116 GtkStyle *
5117 gtk_cmctree_node_get_row_style (GtkCMCTree     *ctree,
5118                               GtkCMCTreeNode *node)
5119 {
5120   cm_return_val_if_fail (GTK_IS_CMCTREE (ctree), NULL);
5121   cm_return_val_if_fail (node != NULL, NULL);
5122
5123   return GTK_CMCTREE_ROW (node)->row.style;
5124 }
5125
5126 void
5127 gtk_cmctree_node_set_foreground (GtkCMCTree       *ctree,
5128                                GtkCMCTreeNode   *node,
5129                                const GdkColor *color)
5130 {
5131   cm_return_if_fail (GTK_IS_CMCTREE (ctree));
5132   cm_return_if_fail (node != NULL);
5133
5134   if (color)
5135     {
5136       GTK_CMCTREE_ROW (node)->row.foreground = *color;
5137       GTK_CMCTREE_ROW (node)->row.fg_set = TRUE;
5138       if (gtkut_widget_get_realized (GTK_WIDGET(ctree)))
5139         gdk_colormap_alloc_color (gtk_widget_get_colormap (GTK_WIDGET (ctree)),
5140                          &GTK_CMCTREE_ROW (node)->row.foreground, TRUE, TRUE);
5141     }
5142   else
5143     GTK_CMCTREE_ROW (node)->row.fg_set = FALSE;
5144
5145   tree_draw_node (ctree, node);
5146 }
5147
5148 void
5149 gtk_cmctree_node_set_background (GtkCMCTree       *ctree,
5150                                GtkCMCTreeNode   *node,
5151                                const GdkColor *color)
5152 {
5153   cm_return_if_fail (GTK_IS_CMCTREE (ctree));
5154   cm_return_if_fail (node != NULL);
5155
5156   if (color)
5157     {
5158       GTK_CMCTREE_ROW (node)->row.background = *color;
5159       GTK_CMCTREE_ROW (node)->row.bg_set = TRUE;
5160       if (gtkut_widget_get_realized (GTK_WIDGET(ctree)))
5161         gdk_colormap_alloc_color (gtk_widget_get_colormap (GTK_WIDGET (ctree)),
5162                          &GTK_CMCTREE_ROW (node)->row.background, TRUE, TRUE);
5163     }
5164   else
5165     GTK_CMCTREE_ROW (node)->row.bg_set = FALSE;
5166
5167   tree_draw_node (ctree, node);
5168 }
5169
5170 void
5171 gtk_cmctree_node_set_row_data (GtkCMCTree     *ctree,
5172                              GtkCMCTreeNode *node,
5173                              gpointer      data)
5174 {
5175   gtk_cmctree_node_set_row_data_full (ctree, node, data, NULL);
5176 }
5177
5178 void
5179 gtk_cmctree_node_set_row_data_full (GtkCMCTree         *ctree,
5180                                   GtkCMCTreeNode     *node,
5181                                   gpointer          data,
5182                                   GDestroyNotify  destroy)
5183 {
5184   GDestroyNotify dnotify;
5185   gpointer ddata;
5186   
5187   cm_return_if_fail (GTK_IS_CMCTREE (ctree));
5188   cm_return_if_fail (node != NULL);
5189
5190   dnotify = GTK_CMCTREE_ROW (node)->row.destroy;
5191   ddata = GTK_CMCTREE_ROW (node)->row.data;
5192   
5193   GTK_CMCTREE_ROW (node)->row.data = data;
5194   GTK_CMCTREE_ROW (node)->row.destroy = destroy;
5195
5196   if (dnotify)
5197     dnotify (ddata);
5198 }
5199
5200 gpointer
5201 gtk_cmctree_node_get_row_data (GtkCMCTree     *ctree,
5202                              GtkCMCTreeNode *node)
5203 {
5204   cm_return_val_if_fail (GTK_IS_CMCTREE (ctree), NULL);
5205
5206   return node ? GTK_CMCTREE_ROW (node)->row.data : NULL;
5207 }
5208
5209 void
5210 gtk_cmctree_node_moveto (GtkCMCTree     *ctree,
5211                        GtkCMCTreeNode *node,
5212                        gint          column,
5213                        gfloat        row_align,
5214                        gfloat        col_align)
5215 {
5216   gint row = -1;
5217   GtkCMCList *clist;
5218
5219   cm_return_if_fail (GTK_IS_CMCTREE (ctree));
5220
5221   clist = GTK_CMCLIST (ctree);
5222
5223   while (node && !gtk_cmctree_is_viewable (ctree, node))
5224     node = GTK_CMCTREE_ROW (node)->parent;
5225
5226   if (node)
5227     row = g_list_position (clist->row_list, (GList *)node);
5228   
5229   gtk_cmclist_moveto (clist, row, column, row_align, col_align);
5230 }
5231
5232 GtkVisibility 
5233 gtk_cmctree_node_is_visible (GtkCMCTree     *ctree,
5234                            GtkCMCTreeNode *node)
5235 {
5236   gint row;
5237   
5238   cm_return_val_if_fail (ctree != NULL, 0);
5239   cm_return_val_if_fail (node != NULL, 0);
5240   
5241   row = g_list_position (GTK_CMCLIST (ctree)->row_list, (GList*) node);
5242   return gtk_cmclist_row_is_visible (GTK_CMCLIST (ctree), row);
5243 }
5244
5245
5246 /***********************************************************
5247  *             GtkCMCTree specific functions                 *
5248  ***********************************************************/
5249
5250 void
5251 gtk_cmctree_set_indent (GtkCMCTree *ctree, 
5252                       gint      indent)
5253 {
5254   GtkCMCList *clist;
5255
5256   cm_return_if_fail (GTK_IS_CMCTREE (ctree));
5257   cm_return_if_fail (indent >= 0);
5258
5259   if (indent == ctree->tree_indent)
5260     return;
5261
5262   clist = GTK_CMCLIST (ctree);
5263   ctree->tree_indent = indent;
5264
5265   if (clist->column[ctree->tree_column].auto_resize &&
5266       !GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist))
5267     gtk_cmclist_set_column_width
5268       (clist, ctree->tree_column,
5269        gtk_cmclist_optimal_column_width (clist, ctree->tree_column));
5270   else
5271     CLIST_REFRESH (ctree);
5272 }
5273
5274 void
5275 gtk_cmctree_set_spacing (GtkCMCTree *ctree, 
5276                        gint      spacing)
5277 {
5278   GtkCMCList *clist;
5279   gint old_spacing;
5280
5281   cm_return_if_fail (GTK_IS_CMCTREE (ctree));
5282   cm_return_if_fail (spacing >= 0);
5283
5284   if (spacing == ctree->tree_spacing)
5285     return;
5286
5287   clist = GTK_CMCLIST (ctree);
5288
5289   old_spacing = ctree->tree_spacing;
5290   ctree->tree_spacing = spacing;
5291
5292   if (clist->column[ctree->tree_column].auto_resize &&
5293       !GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist))
5294     gtk_cmclist_set_column_width (clist, ctree->tree_column,
5295                                 clist->column[ctree->tree_column].width +
5296                                 spacing - old_spacing);
5297   else
5298     CLIST_REFRESH (ctree);
5299 }
5300
5301 void
5302 gtk_cmctree_set_show_stub (GtkCMCTree *ctree, 
5303                          gboolean  show_stub)
5304 {
5305   cm_return_if_fail (GTK_IS_CMCTREE (ctree));
5306
5307   show_stub = show_stub != FALSE;
5308
5309   if (show_stub != ctree->show_stub)
5310     {
5311       GtkCMCList *clist;
5312
5313       clist = GTK_CMCLIST (ctree);
5314       ctree->show_stub = show_stub;
5315
5316       if (CLIST_UNFROZEN (clist) && clist->rows &&
5317           gtk_cmclist_row_is_visible (clist, 0) != GTK_VISIBILITY_NONE)
5318         GTK_CMCLIST_GET_CLASS (clist)->draw_row
5319           (clist, NULL, 0, GTK_CMCLIST_ROW (clist->row_list));
5320     }
5321 }
5322
5323 void 
5324 gtk_cmctree_set_line_style (GtkCMCTree          *ctree, 
5325                           GtkCMCTreeLineStyle  line_style)
5326 {
5327   GtkCMCList *clist;
5328   GtkCMCTreeLineStyle old_style;
5329
5330   cm_return_if_fail (GTK_IS_CMCTREE (ctree));
5331
5332   if (line_style == ctree->line_style)
5333     return;
5334
5335   clist = GTK_CMCLIST (ctree);
5336
5337   old_style = ctree->line_style;
5338   ctree->line_style = line_style;
5339
5340   if (clist->column[ctree->tree_column].auto_resize &&
5341       !GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist))
5342     {
5343       if (old_style == GTK_CMCTREE_LINES_TABBED)
5344         gtk_cmclist_set_column_width
5345           (clist, ctree->tree_column,
5346            clist->column[ctree->tree_column].width - 3);
5347       else if (line_style == GTK_CMCTREE_LINES_TABBED)
5348         gtk_cmclist_set_column_width
5349           (clist, ctree->tree_column,
5350            clist->column[ctree->tree_column].width + 3);
5351     }
5352
5353   if (gtkut_widget_get_realized (GTK_WIDGET(ctree)))
5354     {
5355       switch (line_style)
5356         {
5357         case GTK_CMCTREE_LINES_SOLID:
5358           if (gtkut_widget_get_realized (GTK_WIDGET(ctree)))
5359             gdk_gc_set_line_attributes (ctree->lines_gc, 1, GDK_LINE_SOLID, 
5360                                         GDK_CAP_BUTT, GDK_JOIN_MITER);
5361           break;
5362         case GTK_CMCTREE_LINES_DOTTED:
5363           if (gtkut_widget_get_realized (GTK_WIDGET(ctree)))
5364             gdk_gc_set_line_attributes (ctree->lines_gc, 1, 
5365                                         GDK_LINE_ON_OFF_DASH, GDK_CAP_BUTT, GDK_JOIN_MITER);
5366           gdk_gc_set_dashes (ctree->lines_gc, 0, "\1\1", 2);
5367           break;
5368         case GTK_CMCTREE_LINES_TABBED:
5369           if (gtkut_widget_get_realized (GTK_WIDGET(ctree)))
5370             gdk_gc_set_line_attributes (ctree->lines_gc, 1, GDK_LINE_SOLID, 
5371                                         GDK_CAP_BUTT, GDK_JOIN_MITER);
5372           break;
5373         case GTK_CMCTREE_LINES_NONE:
5374           break;
5375         default:
5376           return;
5377         }
5378       CLIST_REFRESH (ctree);
5379     }
5380 }
5381
5382 void 
5383 gtk_cmctree_set_expander_style (GtkCMCTree              *ctree, 
5384                               GtkCMCTreeExpanderStyle  expander_style)
5385 {
5386   GtkCMCList *clist;
5387   GtkCMCTreeExpanderStyle old_style;
5388
5389   cm_return_if_fail (GTK_IS_CMCTREE (ctree));
5390
5391   if (expander_style == ctree->expander_style)
5392     return;
5393
5394   clist = GTK_CMCLIST (ctree);
5395
5396   old_style = ctree->expander_style;
5397   ctree->expander_style = expander_style;
5398
5399   if (clist->column[ctree->tree_column].auto_resize &&
5400       !GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist))
5401     {
5402       gint new_width;
5403
5404       new_width = clist->column[ctree->tree_column].width;
5405       switch (old_style)
5406         {
5407         case GTK_CMCTREE_EXPANDER_NONE:
5408           break;
5409         case GTK_CMCTREE_EXPANDER_TRIANGLE:
5410           new_width -= PM_SIZE + 3;
5411           break;
5412         case GTK_CMCTREE_EXPANDER_SQUARE:
5413         case GTK_CMCTREE_EXPANDER_CIRCULAR:
5414           new_width -= PM_SIZE + 1;
5415           break;
5416         }
5417
5418       switch (expander_style)
5419         {
5420         case GTK_CMCTREE_EXPANDER_NONE:
5421           break;
5422         case GTK_CMCTREE_EXPANDER_TRIANGLE:
5423           new_width += PM_SIZE + 3;
5424           break;
5425         case GTK_CMCTREE_EXPANDER_SQUARE:
5426         case GTK_CMCTREE_EXPANDER_CIRCULAR:
5427           new_width += PM_SIZE + 1;
5428           break;
5429         }
5430
5431       gtk_cmclist_set_column_width (clist, ctree->tree_column, new_width);
5432     }
5433
5434   if (gtkut_widget_is_drawable (GTK_WIDGET(clist)))
5435     CLIST_REFRESH (clist);
5436 }
5437
5438
5439 /***********************************************************
5440  *             Tree sorting functions                      *
5441  ***********************************************************/
5442
5443
5444 static void
5445 tree_sort (GtkCMCTree     *ctree,
5446            GtkCMCTreeNode *node,
5447            gpointer      data)
5448 {
5449   GtkCMCTreeNode *list_start;
5450   GtkCMCTreeNode *cmp;
5451   GtkCMCTreeNode *work;
5452   GtkCMCList *clist;
5453
5454   clist = GTK_CMCLIST (ctree);
5455
5456   if (node)
5457     list_start = GTK_CMCTREE_ROW (node)->children;
5458   else
5459     list_start = GTK_CMCTREE_NODE (clist->row_list);
5460
5461   while (list_start)
5462     {
5463       cmp = list_start;
5464       work = GTK_CMCTREE_ROW (cmp)->sibling;
5465       while (work)
5466         {
5467           if (clist->sort_type == GTK_SORT_ASCENDING)
5468             {
5469               if (clist->compare 
5470                   (clist, GTK_CMCTREE_ROW (work), GTK_CMCTREE_ROW (cmp)) < 0)
5471                 cmp = work;
5472             }
5473           else
5474             {
5475               if (clist->compare 
5476                   (clist, GTK_CMCTREE_ROW (work), GTK_CMCTREE_ROW (cmp)) > 0)
5477                 cmp = work;
5478             }
5479           work = GTK_CMCTREE_ROW (work)->sibling;
5480         }
5481       if (cmp == list_start)
5482         list_start = GTK_CMCTREE_ROW (cmp)->sibling;
5483       else
5484         {
5485           gtk_cmctree_unlink (ctree, cmp, FALSE);
5486           gtk_cmctree_link (ctree, cmp, node, list_start, FALSE);
5487         }
5488     }
5489 }
5490
5491 void
5492 gtk_cmctree_sort_recursive (GtkCMCTree     *ctree, 
5493                           GtkCMCTreeNode *node)
5494 {
5495   GtkCMCList *clist;
5496   GtkCMCTreeNode *focus_node = NULL;
5497
5498   cm_return_if_fail (GTK_IS_CMCTREE (ctree));
5499
5500   clist = GTK_CMCLIST (ctree);
5501
5502   gtk_cmclist_freeze (clist);
5503
5504   if (clist->selection_mode == GTK_SELECTION_MULTIPLE)
5505     {
5506       GTK_CMCLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
5507       
5508       g_list_free (clist->undo_selection);
5509       g_list_free (clist->undo_unselection);
5510       clist->undo_selection = NULL;
5511       clist->undo_unselection = NULL;
5512     }
5513
5514   if (!node || (node && gtk_cmctree_is_viewable (ctree, node)))
5515     focus_node =
5516       GTK_CMCTREE_NODE (g_list_nth (clist->row_list, clist->focus_row));
5517       
5518   gtk_cmctree_post_recursive (ctree, node, GTK_CMCTREE_FUNC (tree_sort), NULL);
5519
5520   if (!node)
5521     tree_sort (ctree, NULL, NULL);
5522
5523   if (focus_node)
5524     {
5525       clist->focus_row = g_list_position (clist->row_list,(GList *)focus_node);
5526       clist->undo_anchor = clist->focus_row;
5527     }
5528
5529   gtk_cmclist_thaw (clist);
5530 }
5531
5532 static void
5533 real_sort_list (GtkCMCList *clist)
5534 {
5535   gtk_cmctree_sort_recursive (GTK_CMCTREE (clist), NULL);
5536 }
5537
5538 void
5539 gtk_cmctree_sort_node (GtkCMCTree     *ctree, 
5540                      GtkCMCTreeNode *node)
5541 {
5542   GtkCMCList *clist;
5543   GtkCMCTreeNode *focus_node = NULL;
5544
5545   cm_return_if_fail (GTK_IS_CMCTREE (ctree));
5546
5547   clist = GTK_CMCLIST (ctree);
5548
5549   gtk_cmclist_freeze (clist);
5550
5551   if (clist->selection_mode == GTK_SELECTION_MULTIPLE)
5552     {
5553       GTK_CMCLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
5554       
5555       g_list_free (clist->undo_selection);
5556       g_list_free (clist->undo_unselection);
5557       clist->undo_selection = NULL;
5558       clist->undo_unselection = NULL;
5559     }
5560
5561   if (!node || (node && gtk_cmctree_is_viewable (ctree, node)))
5562     focus_node = GTK_CMCTREE_NODE
5563       (g_list_nth (clist->row_list, clist->focus_row));
5564
5565   tree_sort (ctree, node, NULL);
5566
5567   if (focus_node)
5568     {
5569       clist->focus_row = g_list_position (clist->row_list,(GList *)focus_node);
5570       clist->undo_anchor = clist->focus_row;
5571     }
5572
5573   gtk_cmclist_thaw (clist);
5574 }
5575
5576 /************************************************************************/
5577
5578 static void
5579 fake_unselect_all (GtkCMCList *clist,
5580                    gint      row)
5581 {
5582   GList *list;
5583   GList *focus_node = NULL;
5584
5585   if (row >= 0 && (focus_node = g_list_nth (clist->row_list, row)))
5586     {
5587       if (GTK_CMCTREE_ROW (focus_node)->row.state == GTK_STATE_NORMAL &&
5588           GTK_CMCTREE_ROW (focus_node)->row.selectable)
5589         {
5590           GTK_CMCTREE_ROW (focus_node)->row.state = GTK_STATE_SELECTED;
5591           
5592           if (CLIST_UNFROZEN (clist) &&
5593               gtk_cmclist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE)
5594             GTK_CMCLIST_GET_CLASS (clist)->draw_row (clist, NULL, row,
5595                                                   GTK_CMCLIST_ROW (focus_node));
5596         }  
5597     }
5598
5599   clist->undo_selection = clist->selection;
5600   clist->selection = NULL;
5601   clist->selection_end = NULL;
5602   
5603   for (list = clist->undo_selection; list; list = list->next)
5604     {
5605       if (list->data == focus_node)
5606         continue;
5607
5608       GTK_CMCTREE_ROW ((GList *)(list->data))->row.state = GTK_STATE_NORMAL;
5609       tree_draw_node (GTK_CMCTREE (clist), GTK_CMCTREE_NODE (list->data));
5610     }
5611 }
5612
5613 static GList *
5614 selection_find (GtkCMCList *clist,
5615                 gint      row_number,
5616                 GList    *row_list_element)
5617 {
5618   return g_list_find (clist->selection, row_list_element);
5619 }
5620
5621 static void
5622 resync_selection (GtkCMCList *clist, GdkEvent *event)
5623 {
5624   GtkCMCTree *ctree;
5625   GList *list;
5626   GtkCMCTreeNode *node;
5627   gint i;
5628   gint e;
5629   gint row;
5630   gboolean unselect;
5631
5632   cm_return_if_fail (GTK_IS_CMCTREE (clist));
5633
5634   if (clist->selection_mode != GTK_SELECTION_MULTIPLE)
5635     return;
5636
5637   if (clist->anchor < 0 || clist->drag_pos < 0)
5638     return;
5639
5640   ctree = GTK_CMCTREE (clist);
5641   
5642   clist->freeze_count++;
5643
5644   i = MIN (clist->anchor, clist->drag_pos);
5645   e = MAX (clist->anchor, clist->drag_pos);
5646
5647   if (clist->undo_selection)
5648     {
5649       list = clist->selection;
5650       clist->selection = clist->undo_selection;
5651       clist->selection_end = g_list_last (clist->selection);
5652       clist->undo_selection = list;
5653       list = clist->selection;
5654
5655       while (list)
5656         {
5657           node = list->data;
5658           list = list->next;
5659           
5660           unselect = TRUE;
5661
5662           if (gtk_cmctree_is_viewable (ctree, node))
5663             {
5664               row = g_list_position (clist->row_list, (GList *)node);
5665               if (row >= i && row <= e)
5666                 unselect = FALSE;
5667             }
5668           if (unselect && GTK_CMCTREE_ROW (node)->row.selectable)
5669             {
5670               GTK_CMCTREE_ROW (node)->row.state = GTK_STATE_SELECTED;
5671               gtk_cmctree_unselect (ctree, node);
5672               clist->undo_selection = g_list_prepend (clist->undo_selection,
5673                                                       node);
5674             }
5675         }
5676     }    
5677
5678   if (clist->anchor < clist->drag_pos)
5679     {
5680       for (node = GTK_CMCTREE_NODE (g_list_nth (clist->row_list, i)); i <= e;
5681            i++, node = GTK_CMCTREE_NODE_NEXT (node))
5682         if (GTK_CMCTREE_ROW (node)->row.selectable)
5683           {
5684             if (g_list_find (clist->selection, node))
5685               {
5686                 if (GTK_CMCTREE_ROW (node)->row.state == GTK_STATE_NORMAL)
5687                   {
5688                     GTK_CMCTREE_ROW (node)->row.state = GTK_STATE_SELECTED;
5689                     gtk_cmctree_unselect (ctree, node);
5690                     clist->undo_selection =
5691                       g_list_prepend (clist->undo_selection, node);
5692                   }
5693               }
5694             else if (GTK_CMCTREE_ROW (node)->row.state == GTK_STATE_SELECTED)
5695               {
5696                 GTK_CMCTREE_ROW (node)->row.state = GTK_STATE_NORMAL;
5697                 clist->undo_unselection =
5698                   g_list_prepend (clist->undo_unselection, node);
5699               }
5700           }
5701     }
5702   else
5703     {
5704       for (node = GTK_CMCTREE_NODE (g_list_nth (clist->row_list, e)); i <= e;
5705            e--, node = GTK_CMCTREE_NODE_PREV (node))
5706         if (GTK_CMCTREE_ROW (node)->row.selectable)
5707           {
5708             if (g_list_find (clist->selection, node))
5709               {
5710                 if (GTK_CMCTREE_ROW (node)->row.state == GTK_STATE_NORMAL)
5711                   {
5712                     GTK_CMCTREE_ROW (node)->row.state = GTK_STATE_SELECTED;
5713                     gtk_cmctree_unselect (ctree, node);
5714                     clist->undo_selection =
5715                       g_list_prepend (clist->undo_selection, node);
5716                   }
5717               }
5718             else if (GTK_CMCTREE_ROW (node)->row.state == GTK_STATE_SELECTED)
5719               {
5720                 GTK_CMCTREE_ROW (node)->row.state = GTK_STATE_NORMAL;
5721                 clist->undo_unselection =
5722                   g_list_prepend (clist->undo_unselection, node);
5723               }
5724           }
5725     }
5726
5727   clist->undo_unselection = g_list_reverse (clist->undo_unselection);
5728   for (list = clist->undo_unselection; list; list = list->next)
5729     gtk_cmctree_select (ctree, list->data);
5730
5731   clist->anchor = -1;
5732   clist->drag_pos = -1;
5733
5734   if (!CLIST_UNFROZEN (clist))
5735     clist->freeze_count--;
5736 }
5737
5738 static void
5739 real_undo_selection (GtkCMCList *clist)
5740 {
5741   GtkCMCTree *ctree;
5742   GList *work;
5743
5744   cm_return_if_fail (GTK_IS_CMCTREE (clist));
5745
5746   if (clist->selection_mode != GTK_SELECTION_MULTIPLE)
5747     return;
5748
5749   if (!(clist->undo_selection || clist->undo_unselection))
5750     {
5751       gtk_cmclist_unselect_all (clist);
5752       return;
5753     }
5754
5755   ctree = GTK_CMCTREE (clist);
5756
5757   for (work = clist->undo_selection; work; work = work->next)
5758     if (GTK_CMCTREE_ROW (work->data)->row.selectable)
5759       gtk_cmctree_select (ctree, GTK_CMCTREE_NODE (work->data));
5760
5761   for (work = clist->undo_unselection; work; work = work->next)
5762     if (GTK_CMCTREE_ROW (work->data)->row.selectable)
5763       gtk_cmctree_unselect (ctree, GTK_CMCTREE_NODE (work->data));
5764
5765   if (gtkut_widget_has_focus (GTK_WIDGET(clist)) &&
5766       clist->focus_row != clist->undo_anchor)
5767     {
5768       clist->focus_row = clist->undo_anchor;
5769       gtk_widget_queue_draw (GTK_WIDGET (clist));
5770     }
5771   else
5772     clist->focus_row = clist->undo_anchor;
5773   
5774   clist->undo_anchor = -1;
5775  
5776   g_list_free (clist->undo_selection);
5777   g_list_free (clist->undo_unselection);
5778   clist->undo_selection = NULL;
5779   clist->undo_unselection = NULL;
5780
5781   if (ROW_TOP_YPIXEL (clist, clist->focus_row) + clist->row_height >
5782       clist->clist_window_height)
5783     gtk_cmclist_moveto (clist, clist->focus_row, -1, 1, 0);
5784   else if (ROW_TOP_YPIXEL (clist, clist->focus_row) < 0)
5785     gtk_cmclist_moveto (clist, clist->focus_row, -1, 0, 0);
5786
5787 }
5788
5789 void
5790 gtk_cmctree_set_drag_compare_func (GtkCMCTree                *ctree,
5791                                  GtkCMCTreeCompareDragFunc  cmp_func)
5792 {
5793   cm_return_if_fail (GTK_IS_CMCTREE (ctree));
5794
5795   ctree->drag_compare = cmp_func;
5796 }
5797
5798 static gboolean
5799 check_drag (GtkCMCTree        *ctree,
5800             GtkCMCTreeNode    *drag_source,
5801             GtkCMCTreeNode    *drag_target,
5802             GtkCMCListDragPos  insert_pos)
5803 {
5804   cm_return_val_if_fail (GTK_IS_CMCTREE (ctree), FALSE);
5805
5806   if (drag_source && drag_source != drag_target &&
5807       (!GTK_CMCTREE_ROW (drag_source)->children ||
5808        !gtk_cmctree_is_ancestor (ctree, drag_source, drag_target)))
5809     {
5810       switch (insert_pos)
5811         {
5812         case GTK_CMCLIST_DRAG_NONE:
5813           return FALSE;
5814         case GTK_CMCLIST_DRAG_AFTER:
5815           if (GTK_CMCTREE_ROW (drag_target)->sibling != drag_source)
5816             return (!ctree->drag_compare ||
5817                     ctree->drag_compare (ctree,
5818                                          drag_source,
5819                                          GTK_CMCTREE_ROW (drag_target)->parent,
5820                                          GTK_CMCTREE_ROW (drag_target)->sibling));
5821           break;
5822         case GTK_CMCLIST_DRAG_BEFORE:
5823           if (GTK_CMCTREE_ROW (drag_source)->sibling != drag_target)
5824             return (!ctree->drag_compare ||
5825                     ctree->drag_compare (ctree,
5826                                          drag_source,
5827                                          GTK_CMCTREE_ROW (drag_target)->parent,
5828                                          drag_target));
5829           break;
5830         case GTK_CMCLIST_DRAG_INTO:
5831           if (!GTK_CMCTREE_ROW (drag_target)->is_leaf &&
5832               GTK_CMCTREE_ROW (drag_target)->children != drag_source)
5833             return (!ctree->drag_compare ||
5834                     ctree->drag_compare (ctree,
5835                                          drag_source,
5836                                          drag_target,
5837                                          GTK_CMCTREE_ROW (drag_target)->children));
5838           break;
5839         }
5840     }
5841   return FALSE;
5842 }
5843
5844
5845
5846 /************************************/
5847 static void
5848 drag_dest_info_destroy (gpointer data)
5849 {
5850   GtkCMCListDestInfo *info = data;
5851
5852   g_free (info);
5853 }
5854
5855 static void
5856 drag_dest_cell (GtkCMCList         *clist,
5857                 gint              x,
5858                 gint              y,
5859                 GtkCMCListDestInfo *dest_info)
5860 {
5861   GtkWidget *widget;
5862
5863   widget = GTK_WIDGET (clist);
5864
5865   dest_info->insert_pos = GTK_CMCLIST_DRAG_NONE;
5866
5867   y -= (GTK_CONTAINER (widget)->border_width +
5868         widget->style->ythickness + clist->column_title_area.height);
5869   dest_info->cell.row = ROW_FROM_YPIXEL (clist, y);
5870
5871   if (dest_info->cell.row >= clist->rows)
5872     {
5873       dest_info->cell.row = clist->rows - 1;
5874       y = ROW_TOP_YPIXEL (clist, dest_info->cell.row) + clist->row_height;
5875     }
5876   if (dest_info->cell.row < -1)
5877     dest_info->cell.row = -1;
5878   
5879   x -= GTK_CONTAINER (widget)->border_width + widget->style->xthickness;
5880
5881   dest_info->cell.column = COLUMN_FROM_XPIXEL (clist, x);
5882
5883   if (dest_info->cell.row >= 0)
5884     {
5885       gint y_delta;
5886       gint h = 0;
5887
5888       y_delta = y - ROW_TOP_YPIXEL (clist, dest_info->cell.row);
5889       
5890       if (GTK_CMCLIST_DRAW_DRAG_RECT(clist) &&
5891           !GTK_CMCTREE_ROW (g_list_nth (clist->row_list,
5892                                       dest_info->cell.row))->is_leaf)
5893         {
5894           dest_info->insert_pos = GTK_CMCLIST_DRAG_INTO;
5895           h = clist->row_height / 4;
5896         }
5897       else if (GTK_CMCLIST_DRAW_DRAG_LINE(clist))
5898         {
5899           dest_info->insert_pos = GTK_CMCLIST_DRAG_BEFORE;
5900           h = clist->row_height / 2;
5901         }
5902
5903       if (GTK_CMCLIST_DRAW_DRAG_LINE(clist))
5904         {
5905           if (y_delta < h)
5906             dest_info->insert_pos = GTK_CMCLIST_DRAG_BEFORE;
5907           else if (clist->row_height - y_delta < h)
5908             dest_info->insert_pos = GTK_CMCLIST_DRAG_AFTER;
5909         }
5910     }
5911 }
5912
5913 static void
5914 gtk_cmctree_drag_begin (GtkWidget            *widget,
5915                       GdkDragContext *context)
5916 {
5917   GtkCMCList *clist;
5918   GtkCMCTree *ctree;
5919   gboolean use_icons;
5920
5921   cm_return_if_fail (GTK_IS_CMCTREE (widget));
5922   cm_return_if_fail (context != NULL);
5923
5924   clist = GTK_CMCLIST (widget);
5925   ctree = GTK_CMCTREE (widget);
5926
5927   use_icons = GTK_CMCLIST_USE_DRAG_ICONS (clist);
5928   GTK_CMCLIST_UNSET_FLAG (clist, CMCLIST_USE_DRAG_ICONS);
5929   GTK_WIDGET_CLASS (parent_class)->drag_begin (widget, context);
5930
5931   if (use_icons)
5932     {
5933       GtkCMCTreeNode *node;
5934
5935       GTK_CMCLIST_SET_FLAG (clist, CMCLIST_USE_DRAG_ICONS);
5936       node = GTK_CMCTREE_NODE (g_list_nth (clist->row_list,
5937                                          clist->click_cell.row));
5938       gtk_drag_set_icon_default (context);
5939     }
5940 }
5941
5942 static gint
5943 gtk_cmctree_drag_motion (GtkWidget      *widget,
5944                        GdkDragContext *context,
5945                        gint            x,
5946                        gint            y,
5947                        guint           time)
5948 {
5949   GtkCMCList *clist;
5950   GtkCMCTree *ctree;
5951   GtkCMCListDestInfo new_info;
5952   GtkCMCListDestInfo *dest_info;
5953
5954   cm_return_val_if_fail (GTK_IS_CMCTREE (widget), FALSE);
5955
5956   clist = GTK_CMCLIST (widget);
5957   ctree = GTK_CMCTREE (widget);
5958
5959   dest_info = g_dataset_get_data (context, "gtk-clist-drag-dest");
5960
5961   if (!dest_info)
5962     {
5963       dest_info = g_new (GtkCMCListDestInfo, 1);
5964           
5965       dest_info->cell.row    = -1;
5966       dest_info->cell.column = -1;
5967       dest_info->insert_pos  = GTK_CMCLIST_DRAG_NONE;
5968
5969       g_dataset_set_data_full (context, "gtk-clist-drag-dest", dest_info,
5970                                drag_dest_info_destroy);
5971     }
5972
5973   drag_dest_cell (clist, x, y, &new_info);
5974
5975   if (GTK_CMCLIST_REORDERABLE (clist))
5976     {
5977       GList *list;
5978       GdkAtom atom = gdk_atom_intern_static_string ("gtk-clist-drag-reorder");
5979
5980       list = context->targets;
5981       while (list)
5982         {
5983           if (atom == GDK_POINTER_TO_ATOM (list->data))
5984             break;
5985           list = list->next;
5986         }
5987
5988       if (list)
5989         {
5990           GtkCMCTreeNode *drag_source;
5991           GtkCMCTreeNode *drag_target;
5992
5993           drag_source = GTK_CMCTREE_NODE (g_list_nth (clist->row_list,
5994                                                     clist->click_cell.row));
5995           drag_target = GTK_CMCTREE_NODE (g_list_nth (clist->row_list,
5996                                                     new_info.cell.row));
5997
5998           if (gtk_drag_get_source_widget (context) != widget ||
5999               !check_drag (ctree, drag_source, drag_target,
6000                            new_info.insert_pos))
6001             {
6002               if (dest_info->cell.row < 0)
6003                 {
6004                   gdk_drag_status (context, GDK_ACTION_DEFAULT, time);
6005                   return FALSE;
6006                 }
6007               return TRUE;
6008             }
6009
6010           if (new_info.cell.row != dest_info->cell.row ||
6011               (new_info.cell.row == dest_info->cell.row &&
6012                dest_info->insert_pos != new_info.insert_pos))
6013             {
6014               if (dest_info->cell.row >= 0)
6015                 GTK_CMCLIST_GET_CLASS (clist)->draw_drag_highlight
6016                   (clist,
6017                    g_list_nth (clist->row_list, dest_info->cell.row)->data,
6018                    dest_info->cell.row, dest_info->insert_pos);
6019
6020               dest_info->insert_pos  = new_info.insert_pos;
6021               dest_info->cell.row    = new_info.cell.row;
6022               dest_info->cell.column = new_info.cell.column;
6023
6024               GTK_CMCLIST_GET_CLASS (clist)->draw_drag_highlight
6025                 (clist,
6026                  g_list_nth (clist->row_list, dest_info->cell.row)->data,
6027                  dest_info->cell.row, dest_info->insert_pos);
6028
6029               clist->drag_highlight_row = dest_info->cell.row;
6030               clist->drag_highlight_pos = dest_info->insert_pos;
6031
6032               gdk_drag_status (context, context->suggested_action, time);
6033             }
6034           return TRUE;
6035         }
6036     }
6037
6038   dest_info->insert_pos  = new_info.insert_pos;
6039   dest_info->cell.row    = new_info.cell.row;
6040   dest_info->cell.column = new_info.cell.column;
6041   return TRUE;
6042 }
6043
6044 static void
6045 gtk_cmctree_drag_data_received (GtkWidget        *widget,
6046                               GdkDragContext   *context,
6047                               gint              x,
6048                               gint              y,
6049                               GtkSelectionData *selection_data,
6050                               guint             info,
6051                               guint32           time)
6052 {
6053   GtkCMCTree *ctree;
6054   GtkCMCList *clist;
6055
6056   cm_return_if_fail (GTK_IS_CMCTREE (widget));
6057   cm_return_if_fail (context != NULL);
6058   cm_return_if_fail (selection_data != NULL);
6059
6060   ctree = GTK_CMCTREE (widget);
6061   clist = GTK_CMCLIST (widget);
6062
6063   if (GTK_CMCLIST_REORDERABLE (clist) &&
6064       gtk_drag_get_source_widget (context) == widget &&
6065       selection_data->target ==
6066       gdk_atom_intern_static_string ("gtk-clist-drag-reorder") &&
6067       selection_data->format == 8 &&
6068       selection_data->length == sizeof (GtkCMCListCellInfo))
6069     {
6070       GtkCMCListCellInfo *source_info;
6071
6072       source_info = (GtkCMCListCellInfo *)(selection_data->data);
6073       if (source_info)
6074         {
6075           GtkCMCListDestInfo dest_info;
6076           GtkCMCTreeNode *source_node;
6077           GtkCMCTreeNode *dest_node;
6078
6079           drag_dest_cell (clist, x, y, &dest_info);
6080           
6081           source_node = GTK_CMCTREE_NODE (g_list_nth (clist->row_list,
6082                                                     source_info->row));
6083           dest_node = GTK_CMCTREE_NODE (g_list_nth (clist->row_list,
6084                                                   dest_info.cell.row));
6085
6086           if (!source_node || !dest_node)
6087             return;
6088
6089           switch (dest_info.insert_pos)
6090             {
6091             case GTK_CMCLIST_DRAG_NONE:
6092               break;
6093             case GTK_CMCLIST_DRAG_INTO:
6094               if (check_drag (ctree, source_node, dest_node,
6095                               dest_info.insert_pos))
6096                 gtk_cmctree_move (ctree, source_node, dest_node,
6097                                 GTK_CMCTREE_ROW (dest_node)->children);
6098               g_dataset_remove_data (context, "gtk-clist-drag-dest");
6099               break;
6100             case GTK_CMCLIST_DRAG_BEFORE:
6101               if (check_drag (ctree, source_node, dest_node,
6102                               dest_info.insert_pos))
6103                 gtk_cmctree_move (ctree, source_node,
6104                                 GTK_CMCTREE_ROW (dest_node)->parent, dest_node);
6105               g_dataset_remove_data (context, "gtk-clist-drag-dest");
6106               break;
6107             case GTK_CMCLIST_DRAG_AFTER:
6108               if (check_drag (ctree, source_node, dest_node,
6109                               dest_info.insert_pos))
6110                 gtk_cmctree_move (ctree, source_node,
6111                                 GTK_CMCTREE_ROW (dest_node)->parent, 
6112                                 GTK_CMCTREE_ROW (dest_node)->sibling);
6113               g_dataset_remove_data (context, "gtk-clist-drag-dest");
6114               break;
6115             }
6116         }
6117     }
6118 }
6119
6120 GType
6121 gtk_cmctree_node_get_type (void)
6122 {
6123   static GType our_type = 0;
6124   
6125   if (our_type == 0)
6126     our_type = g_pointer_type_register_static ("GtkCMCTreeNode");
6127
6128   return our_type;
6129 }