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