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);