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