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