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