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