13dbdb5e4decc3f077b9530f90f9a95721e1280d
[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     etype = g_enum_register_static (g_intern_static_string ("GtkCMCTreePos"), values);
69   }
70   return etype;
71 }
72 GType
73 gtk_cmctree_line_style_get_type (void)
74 {
75   static GType etype = 0;
76   if (etype == 0) {
77     static const GEnumValue values[] = {
78       { GTK_CMCTREE_LINES_NONE, "GTK_CMCTREE_LINES_NONE", "none" },
79       { 0, NULL, NULL }
80     };
81     etype = g_enum_register_static (g_intern_static_string ("GtkCMCTreeLineStyle"), values);
82   }
83   return etype;
84 }
85 GType
86 gtk_cmctree_expander_style_get_type (void)
87 {
88   static GType etype = 0;
89   if (etype == 0) {
90     static const GEnumValue values[] = {
91       { GTK_CMCTREE_EXPANDER_NONE, "GTK_CMCTREE_EXPANDER_NONE", "none" },
92       { GTK_CMCTREE_EXPANDER_TRIANGLE, "GTK_CMCTREE_EXPANDER_TRIANGLE", "triangle" },
93       { 0, NULL, NULL }
94     };
95     etype = g_enum_register_static (g_intern_static_string ("GtkCMCTreeExpanderStyle"), values);
96   }
97   return etype;
98 }
99 GType
100 gtk_cmctree_expansion_type_get_type (void)
101 {
102   static GType etype = 0;
103   if (etype == 0) {
104     static const GEnumValue values[] = {
105       { GTK_CMCTREE_EXPANSION_EXPAND, "GTK_CMCTREE_EXPANSION_EXPAND", "expand" },
106       { GTK_CMCTREE_EXPANSION_EXPAND_RECURSIVE, "GTK_CMCTREE_EXPANSION_EXPAND_RECURSIVE", "expand-recursive" },
107       { GTK_CMCTREE_EXPANSION_COLLAPSE, "GTK_CMCTREE_EXPANSION_COLLAPSE", "collapse" },
108       { GTK_CMCTREE_EXPANSION_COLLAPSE_RECURSIVE, "GTK_CMCTREE_EXPANSION_COLLAPSE_RECURSIVE", "collapse-recursive" },
109       { GTK_CMCTREE_EXPANSION_TOGGLE, "GTK_CMCTREE_EXPANSION_TOGGLE", "toggle" },
110       { GTK_CMCTREE_EXPANSION_TOGGLE_RECURSIVE, "GTK_CMCTREE_EXPANSION_TOGGLE_RECURSIVE", "toggle-recursive" },
111       { 0, NULL, NULL }
112     };
113    etype = g_enum_register_static (g_intern_static_string ("GtkCMCTreeExpansionType"), values);
114   }
115   return etype;
116 }
117
118
119 static inline gint
120 COLUMN_FROM_XPIXEL (GtkCMCList * clist,
121                     gint x)
122 {
123   gint i, cx;
124
125   for (i = 0; i < clist->columns; i++)
126     if (clist->column[i].visible)
127       {
128         cx = clist->column[i].area.x + clist->hoffset;
129
130         if (x >= (cx - (COLUMN_INSET + CELL_SPACING)) &&
131             x <= (cx + clist->column[i].area.width + COLUMN_INSET))
132           return i;
133       }
134
135   /* no match */
136   return -1;
137 }
138
139 #define CLIST_UNFROZEN(clist)     (((GtkCMCList*) (clist))->freeze_count == 0)
140 #define CLIST_REFRESH(clist)    G_STMT_START { \
141   if (CLIST_UNFROZEN (clist)) \
142     GTK_CMCLIST_GET_CLASS (clist)->refresh ((GtkCMCList*) (clist)); \
143 } G_STMT_END
144
145
146 enum {
147   ARG_0,
148   ARG_N_COLUMNS,
149   ARG_TREE_COLUMN,
150   ARG_INDENT,
151   ARG_SPACING,
152   ARG_SHOW_STUB,
153   ARG_LINE_STYLE,
154   ARG_EXPANDER_STYLE
155 };
156
157
158 static void     gtk_cmctree_class_init    (GtkCMCTreeClass         *klass);
159 static void     gtk_cmctree_init          (GtkCMCTree              *ctree);
160 static GObject* gtk_cmctree_constructor   (GType                  type,
161                                          guint                  n_construct_properties,
162                                          GObjectConstructParam *construct_params);
163 static void gtk_cmctree_set_arg         (GObject *object,
164                                 guint      arg_id,
165                                 const GValue *value,
166                                 GParamSpec *spec);
167 static void gtk_cmctree_get_arg         (GObject *object,
168                                 guint      arg_id,
169                                 GValue *value,
170                                 GParamSpec *spec);
171 static void gtk_cmctree_realize           (GtkWidget      *widget);
172 static void gtk_cmctree_unrealize         (GtkWidget      *widget);
173 static gint gtk_cmctree_button_press      (GtkWidget      *widget,
174                                          GdkEventButton *event);
175 static void ctree_attach_styles         (GtkCMCTree       *ctree,
176                                          GtkCMCTreeNode   *node,
177                                          gpointer        data);
178 static void ctree_detach_styles         (GtkCMCTree       *ctree,
179                                          GtkCMCTreeNode   *node, 
180                                          gpointer        data);
181 static void set_cell_contents           (GtkCMCList      *clist,
182                                          GtkCMCListRow   *clist_row,
183                                          gint           column,
184                                          GtkCMCellType    type,
185                                          const gchar   *text,
186                                          guint8         spacing,
187                                          GdkPixbuf     *pixbuf);
188 static void set_node_info               (GtkCMCTree      *ctree,
189                                          GtkCMCTreeNode  *node,
190                                          const gchar   *text,
191                                          guint8         spacing,
192                                          GdkPixbuf     *pixbuf_closed,
193                                          GdkPixbuf     *pixbuf_opened,
194                                          gboolean       is_leaf,
195                                          gboolean       expanded);
196 static GtkCMCTreeRow *row_new             (GtkCMCTree      *ctree);
197 static void row_delete                  (GtkCMCTree      *ctree,
198                                          GtkCMCTreeRow   *ctree_row);
199 static void tree_delete                 (GtkCMCTree      *ctree, 
200                                          GtkCMCTreeNode  *node, 
201                                          gpointer       data);
202 static void tree_delete_row             (GtkCMCTree      *ctree, 
203                                          GtkCMCTreeNode  *node, 
204                                          gpointer       data);
205 static void real_clear                  (GtkCMCList      *clist);
206 static void tree_update_level           (GtkCMCTree      *ctree, 
207                                          GtkCMCTreeNode  *node, 
208                                          gpointer       data);
209 static void tree_select                 (GtkCMCTree      *ctree, 
210                                          GtkCMCTreeNode  *node, 
211                                          gpointer       data);
212 static void tree_unselect               (GtkCMCTree      *ctree, 
213                                          GtkCMCTreeNode  *node, 
214                                          gpointer       data);
215 static void real_select_all             (GtkCMCList      *clist);
216 static void real_unselect_all           (GtkCMCList      *clist);
217 static void tree_expand                 (GtkCMCTree      *ctree, 
218                                          GtkCMCTreeNode  *node,
219                                          gpointer       data);
220 static void tree_collapse               (GtkCMCTree      *ctree, 
221                                          GtkCMCTreeNode  *node,
222                                          gpointer       data);
223 static void tree_collapse_to_depth      (GtkCMCTree      *ctree, 
224                                          GtkCMCTreeNode  *node, 
225                                          gint           depth);
226 static void tree_toggle_expansion       (GtkCMCTree      *ctree,
227                                          GtkCMCTreeNode  *node,
228                                          gpointer       data);
229 static void change_focus_row_expansion  (GtkCMCTree      *ctree,
230                                          GtkCMCTreeExpansionType expansion);
231 static void real_select_row             (GtkCMCList      *clist,
232                                          gint           row,
233                                          gint           column,
234                                          GdkEvent      *event);
235 static void real_unselect_row           (GtkCMCList      *clist,
236                                          gint           row,
237                                          gint           column,
238                                          GdkEvent      *event);
239 static void real_tree_select            (GtkCMCTree      *ctree,
240                                          GtkCMCTreeNode  *node,
241                                          gint           column);
242 static void real_tree_unselect          (GtkCMCTree      *ctree,
243                                          GtkCMCTreeNode  *node,
244                                          gint           column);
245 static void real_tree_expand            (GtkCMCTree      *ctree,
246                                          GtkCMCTreeNode  *node);
247 static void real_tree_collapse          (GtkCMCTree      *ctree,
248                                          GtkCMCTreeNode  *node);
249 static void real_tree_move              (GtkCMCTree      *ctree,
250                                          GtkCMCTreeNode  *node,
251                                          GtkCMCTreeNode  *new_parent, 
252                                          GtkCMCTreeNode  *new_sibling);
253 static void real_row_move               (GtkCMCList      *clist,
254                                          gint           source_row,
255                                          gint           dest_row);
256 static void gtk_cmctree_link              (GtkCMCTree      *ctree,
257                                          GtkCMCTreeNode  *node,
258                                          GtkCMCTreeNode  *parent,
259                                          GtkCMCTreeNode  *sibling,
260                                          gboolean       update_focus_row);
261 static void gtk_cmctree_unlink            (GtkCMCTree      *ctree, 
262                                          GtkCMCTreeNode  *node,
263                                          gboolean       update_focus_row);
264 static GtkCMCTreeNode * gtk_cmctree_last_visible (GtkCMCTree     *ctree,
265                                               GtkCMCTreeNode *node);
266 static gboolean ctree_is_hot_spot       (GtkCMCTree      *ctree, 
267                                          GtkCMCTreeNode  *node,
268                                          gint           row, 
269                                          gint           x, 
270                                          gint           y);
271 static void tree_sort                   (GtkCMCTree      *ctree,
272                                          GtkCMCTreeNode  *node,
273                                          gpointer       data);
274 static void fake_unselect_all           (GtkCMCList      *clist,
275                                          gint           row);
276 static GList * selection_find           (GtkCMCList      *clist,
277                                          gint           row_number,
278                                          GList         *row_list_element);
279 static void resync_selection            (GtkCMCList      *clist,
280                                          GdkEvent      *event);
281 static void real_undo_selection         (GtkCMCList      *clist);
282 static void select_row_recursive        (GtkCMCTree      *ctree, 
283                                          GtkCMCTreeNode  *node, 
284                                          gpointer       data);
285 static gint real_insert_row             (GtkCMCList      *clist,
286                                          gint           row,
287                                          gchar         *text[]);
288 static void real_remove_row             (GtkCMCList      *clist,
289                                          gint           row);
290 static void real_sort_list              (GtkCMCList      *clist);
291 static void cell_size_request           (GtkCMCList       *clist,
292                                          GtkCMCListRow    *clist_row,
293                                          gint            column,
294                                          GtkRequisition *requisition);
295 static void column_auto_resize          (GtkCMCList       *clist,
296                                          GtkCMCListRow    *clist_row,
297                                          gint            column,
298                                          gint            old_width);
299 static void auto_resize_columns         (GtkCMCList       *clist);
300
301
302 static gboolean check_drag               (GtkCMCTree         *ctree,
303                                           GtkCMCTreeNode     *drag_source,
304                                           GtkCMCTreeNode     *drag_target,
305                                           GtkCMCListDragPos   insert_pos);
306 static void gtk_cmctree_drag_begin         (GtkWidget        *widget,
307                                           GdkDragContext   *context);
308 static gint gtk_cmctree_drag_motion        (GtkWidget        *widget,
309                                           GdkDragContext   *context,
310                                           gint              x,
311                                           gint              y,
312                                           guint             time);
313 static void gtk_cmctree_drag_data_received (GtkWidget        *widget,
314                                           GdkDragContext   *context,
315                                           gint              x,
316                                           gint              y,
317                                           GtkSelectionData *selection_data,
318                                           guint             info,
319                                           guint32           time);
320 static void remove_grab                  (GtkCMCList         *clist);
321 static void drag_dest_cell               (GtkCMCList         *clist,
322                                           gint              x,
323                                           gint              y,
324                                           GtkCMCListDestInfo *dest_info);
325
326
327 enum
328 {
329   TREE_SELECT_ROW,
330   TREE_UNSELECT_ROW,
331   TREE_EXPAND,
332   TREE_COLLAPSE,
333   TREE_MOVE,
334   CHANGE_FOCUS_ROW_EXPANSION,
335   LAST_SIGNAL
336 };
337
338 static GtkCMCListClass *parent_class = NULL;
339 static GtkContainerClass *container_class = NULL;
340 static guint ctree_signals[LAST_SIGNAL] = {0};
341
342
343 GType
344 gtk_cmctree_get_type (void)
345 {
346   static GType ctree_type = 0;
347
348   if (!ctree_type)
349     {
350       static const GTypeInfo ctree_info =
351       {
352                         sizeof (GtkCMCTreeClass),
353
354                         (GBaseInitFunc) NULL,
355                         (GBaseFinalizeFunc) NULL,
356
357                         (GClassInitFunc) gtk_cmctree_class_init,
358                         (GClassFinalizeFunc) NULL,
359                         NULL,   /* class_data */
360
361                         sizeof (GtkCMCTree),
362                         0,      /* n_preallocs */
363                         (GInstanceInitFunc) gtk_cmctree_init,
364
365                         (const GTypeValueTable *) NULL  /* value table */
366       };
367
368         ctree_type = g_type_register_static (GTK_TYPE_CMCLIST, "GtkCMCTree", &ctree_info, (GTypeFlags)0);
369     }
370
371   return ctree_type;
372 }
373
374 static gint
375 draw_cell_pixbuf (GdkWindow    *window,
376                   GdkRectangle *clip_rectangle,
377                   cairo_t      *cr,
378                   GdkPixbuf    *pixbuf,
379                   gint          x,
380                   gint          y,
381                   gint          width,
382                   gint          height)
383 {
384   gint xsrc = 0;
385   gint ysrc = 0;
386
387   if (!pixbuf || (width == 0 && height == 0))
388         return x;
389
390   if (x < clip_rectangle->x)
391     {
392       xsrc = clip_rectangle->x - x;
393       width -= xsrc;
394       x = clip_rectangle->x;
395     }
396   if (x + width > clip_rectangle->x + clip_rectangle->width)
397     width = clip_rectangle->x + clip_rectangle->width - x;
398
399   if (y < clip_rectangle->y)
400     {
401       ysrc = clip_rectangle->y - y;
402       height -= ysrc;
403       y = clip_rectangle->y;
404     }
405
406   if (y + height > clip_rectangle->y + clip_rectangle->height)
407     height = clip_rectangle->y + clip_rectangle->height - y;
408
409   gdk_cairo_set_source_pixbuf(cr, pixbuf, x, y);
410   cairo_paint(cr);
411
412   return x + MAX (width, 0);
413 }
414
415 static gint
416 draw_expander (GtkCMCTree     *ctree,
417                GtkCMCTreeRow  *ctree_row,
418                GtkStyle     *style,
419                GdkRectangle *clip_rectangle,
420                cairo_t      *cr,
421                gint          x)
422 {
423   GtkCMCList *clist;
424   gint justification_factor;
425   gint y;
426
427  if (ctree->expander_style == GTK_CMCTREE_EXPANDER_NONE)
428    return x;
429
430   clist = GTK_CMCLIST (ctree);
431   if (clist->column[ctree->tree_column].justification == GTK_JUSTIFY_RIGHT)
432     justification_factor = -1;
433   else
434     justification_factor = 1;
435   if (!GTK_CMCLIST_ROW_HEIGHT_SET(GTK_CMCLIST(clist)))
436       y = (clip_rectangle->y + (clip_rectangle->height - PM_SIZE) / 2 -
437           (clip_rectangle->height + 1) % 2) + 1;
438   else
439       y = (clip_rectangle->y + (clip_rectangle->height/2 - PM_SIZE) / 2 -
440           (clip_rectangle->height/2 + 1) % 2) + 1;
441
442   if (!ctree_row->children)
443     {
444           return x + justification_factor * (PM_SIZE + 3);
445     }
446
447   /* pixel offsets +/- 1 or +/- justification_factor here and there ..
448    * to fill correctly, somewhat ... what do I do wrong?
449    */
450   gdk_cairo_set_source_color(cr, &gtk_widget_get_style(GTK_WIDGET(ctree))->text[GTK_STATE_NORMAL]);
451   if (ctree_row->expanded)
452   {
453     gint tmp3 = PM_SIZE / 2;
454     gint tmp6 = PM_SIZE / 6;
455     cairo_move_to(cr, x + justification_factor * (tmp3 + tmp6) + (PM_SIZE / 2), y + 1);
456     cairo_rel_line_to(cr, 0, tmp3 + tmp6 + 1);
457     cairo_rel_line_to(cr, -justification_factor * (tmp3 + tmp6) - justification_factor, -1);
458   }
459   else
460   {
461     gint tmp3 = PM_SIZE / 2;
462     gint tmp6 = PM_SIZE / 6;
463     cairo_move_to(cr, x + tmp6 - justification_factor + (PM_SIZE / 2), y + tmp6 - 1);
464     cairo_rel_line_to(cr, justification_factor * tmp3, tmp3);
465     cairo_rel_line_to(cr, -justification_factor * tmp3, tmp3);
466   }
467   cairo_fill(cr);
468
469   x += justification_factor * (PM_SIZE + 3);
470
471   return x;
472 }
473
474 static gint
475 get_offset(GtkCMCTree     *ctree,
476                       GtkCMCTreeRow  *ctree_row,
477                       gint            column,
478                       GdkRectangle   *clip_rectangle)
479 {
480   gint justify_right;
481   justify_right = (GTK_CMCLIST (ctree)->column[column].justification == GTK_JUSTIFY_RIGHT);
482
483   if (justify_right)
484       return (clip_rectangle->x + clip_rectangle->width - 1 -
485                 ctree->tree_indent * (ctree_row->level - 1));
486
487   return clip_rectangle->x + ctree->tree_indent * (ctree_row->level - 1);
488 }
489
490  static void
491 get_cell_style (GtkCMCList     *clist,
492                 GtkCMCListRow  *clist_row,
493                 gint          state,
494                 gint          column,
495                 GtkStyle    **style)
496 {
497   GtkStyle *gtkstyle;
498
499   gtkstyle = gtk_widget_get_style (GTK_WIDGET (clist));
500
501   if (clist_row->cell[column].style)
502     {
503       if (style)
504         *style = clist_row->cell[column].style;
505     }
506   else if (clist_row->style)
507     {
508       if (style)
509         *style = clist_row->style;
510     }
511   else
512     {
513       if (style)
514         *style = gtkstyle;
515     }
516 }
517
518 static gboolean filter_fg (PangoAttribute *attribute, gpointer data)
519 {
520         const PangoAttrClass *klass = attribute->klass;
521         if (klass->type == PANGO_ATTR_FOREGROUND)
522                 return TRUE;
523
524         return FALSE;   
525 }
526
527 static PangoLayout *
528 create_cell_layout (GtkCMCList       *clist,
529                                GtkCMCListRow    *clist_row,
530                                gint            column)
531 {
532   PangoLayout *layout;
533   GtkStyle *style;
534   GtkCMCell *cell;
535   gchar *text;
536
537   get_cell_style (clist, clist_row, GTK_STATE_NORMAL, column, &style);
538
539
540   cell = &clist_row->cell[column];
541   switch (cell->type)
542     {
543     case GTK_CMCELL_TEXT:
544     case GTK_CMCELL_PIXTEXT:
545       text = ((cell->type == GTK_CMCELL_PIXTEXT) ?
546               GTK_CMCELL_PIXTEXT (*cell)->text :
547               GTK_CMCELL_TEXT (*cell)->text);
548
549       if (!text)
550         return NULL;
551       
552       if (!GTK_SCTREE(clist)->use_markup[column]) {
553               layout = gtk_widget_create_pango_layout (GTK_WIDGET (clist),
554                                                        ((cell->type == GTK_CMCELL_PIXTEXT) ?
555                                                         GTK_CMCELL_PIXTEXT (*cell)->text :
556                                                         GTK_CMCELL_TEXT (*cell)->text));
557               pango_layout_set_font_description (layout, style->font_desc);
558       } else {
559               PangoContext *context = gtk_widget_get_pango_context (GTK_WIDGET(clist));
560               layout = pango_layout_new (context);
561               pango_layout_set_markup (layout, text, -1);
562               pango_layout_set_font_description (layout, style->font_desc);
563               if (clist_row->state == GTK_STATE_SELECTED) {
564                       /* for selected row, we should remove any forced foreground color
565                        * or it looks like shit */
566                       PangoAttrList *list = pango_layout_get_attributes(layout);
567                       PangoAttrList *rem = pango_attr_list_filter(list, filter_fg, NULL);
568                       if (rem)
569                               pango_attr_list_unref(rem);
570               }
571       }
572       
573       return layout;
574       
575     default:
576       return NULL;
577     }
578 }
579
580
581 static void
582 draw_row (GtkCMCList     *clist,
583           GdkRectangle *area,
584           gint          row,
585           GtkCMCListRow  *clist_row)
586 {
587   GtkWidget *widget;
588   GtkStyle *style;
589   GtkCMCTree  *ctree;
590   GdkRectangle *crect;
591   GdkRectangle row_rectangle;
592   GdkRectangle cell_rectangle; 
593   GdkRectangle clip_rectangle;
594   GdkRectangle intersect_rectangle;
595   gint last_column;
596   gint offset = 0;
597   gint state;
598   gint i;
599   static GdkColor greybg={0, 0, 0, 0};
600   static gboolean color_change = TRUE;
601   cairo_t *cr;
602   GdkColor *fgcolor, *bgcolor;
603
604   cm_return_if_fail (clist != NULL);
605   widget = GTK_WIDGET (clist);
606
607   /* if the function is passed the pointer to the row instead of null,
608    * it avoids this expensive lookup */
609   if (!clist_row)
610     clist_row = (g_list_nth (clist->row_list, row))->data;
611
612   style = clist_row->style ? clist_row->style : gtk_widget_get_style (widget);
613
614   if (greybg.pixel == 0 &&
615       greybg.red == 0 &&
616       greybg.green == 0 &&
617       greybg.blue == 0) {
618         GdkColor normalbg = {0, 0xffff, 0xffff, 0xffff};
619         if (style) {
620                 normalbg = style->base[GTK_STATE_NORMAL];
621         }
622         if (normalbg.red > 0x8888 && normalbg.green > 0x8888 && normalbg.blue > 0x8888) {
623                 greybg.pixel = normalbg.pixel;
624                 greybg.red = normalbg.red - prefs_common.stripes_color_offset;
625                 greybg.green = normalbg.green - prefs_common.stripes_color_offset;
626                 greybg.blue = normalbg.blue - prefs_common.stripes_color_offset;
627         } else if (normalbg.red < 0x8888 && normalbg.green < 0x8888 && normalbg.blue < 0x8888) {
628                 greybg.pixel = normalbg.pixel;
629                 greybg.red = normalbg.red + prefs_common.stripes_color_offset;
630                 greybg.green = normalbg.green + prefs_common.stripes_color_offset;
631                 greybg.blue = normalbg.blue + prefs_common.stripes_color_offset;
632         } else {
633                 color_change = FALSE;
634         }
635   }
636
637   /* bail now if we arn't drawable yet */
638   if (!gtk_widget_is_drawable (GTK_WIDGET(clist)) || row < 0 || row >= clist->rows)
639     return;
640
641   ctree  = GTK_CMCTREE  (clist);
642
643   /* rectangle of the entire row */
644   row_rectangle.x = 0;
645   row_rectangle.y = ROW_TOP_YPIXEL (clist, row);
646   row_rectangle.width = clist->clist_window_width;
647   row_rectangle.height = clist->row_height;
648
649   /* rectangle of the cell spacing above the row */
650   cell_rectangle.x = 0;
651   cell_rectangle.y = row_rectangle.y - CELL_SPACING;
652   cell_rectangle.width = row_rectangle.width;
653   cell_rectangle.height = CELL_SPACING;
654
655   /* rectangle used to clip drawing operations, its y and height
656    * positions only need to be set once, so we set them once here. 
657    * the x and width are set withing the drawing loop below once per
658    * column */
659   clip_rectangle.y = row_rectangle.y;
660   clip_rectangle.height = row_rectangle.height;
661
662   if (prefs_common.use_stripes_everywhere && GTK_SCTREE(ctree)->show_stripes
663       && color_change && row % 2) {
664     bgcolor = &greybg;
665   } else {
666     bgcolor = &style->base[GTK_STATE_NORMAL];
667   }
668   state = clist_row->state;
669
670   cr = gdk_cairo_create(clist->clist_window);
671   
672   if (clist_row->fg_set && state != GTK_STATE_SELECTED)
673         fgcolor = &clist_row->foreground;
674   else
675         fgcolor = &style->text[clist_row->state];
676   /* draw the cell borders */
677   if (area)
678     {
679       crect = &intersect_rectangle;
680
681       if (gdk_rectangle_intersect (area, &cell_rectangle, crect)) {
682         gdk_cairo_rectangle(cr, &cell_rectangle);
683         gdk_cairo_set_source_color(cr, &style->base[GTK_STATE_NORMAL]);
684         cairo_fill(cr);
685         cairo_rectangle(cr, cell_rectangle.x, cell_rectangle.y + row_rectangle.height + 1,cell_rectangle.width,cell_rectangle.height);
686         cairo_fill(cr);
687       }
688     }
689   else
690     {
691       crect = &cell_rectangle;
692
693       gdk_cairo_rectangle(cr, &cell_rectangle);
694       gdk_cairo_set_source_color(cr, &style->base[GTK_STATE_NORMAL]);
695       cairo_fill(cr);
696       cairo_rectangle(cr, cell_rectangle.x, cell_rectangle.y + row_rectangle.height + 1,cell_rectangle.width,cell_rectangle.height);
697       cairo_fill(cr);
698     }
699
700   /* the last row has to clear its bottom cell spacing too */
701   if (clist_row == clist->row_list_end->data)
702     {
703       cell_rectangle.y += clist->row_height + CELL_SPACING;
704
705       if (!area || gdk_rectangle_intersect (area, &cell_rectangle, crect))
706         {
707           gdk_cairo_rectangle(cr, crect);
708           gdk_cairo_set_source_color(cr, &style->base[GTK_STATE_NORMAL]);
709           cairo_fill(cr);
710         }
711     }     
712
713   for (last_column = clist->columns - 1;
714        last_column >= 0 && !clist->column[last_column].visible; last_column--)
715     ;
716
717   /* iterate and draw all the columns (row cells) and draw their contents */
718   for (i = 0; i < clist->columns; i++)
719     {
720       GtkStyle *style;
721       PangoLayout *layout = NULL;
722       PangoRectangle logical_rect;
723
724       gint width;
725       gint height;
726       gint pixbuf_width;
727       gint string_width;
728       gint old_offset;
729
730       if (!clist->column[i].visible)
731         continue;
732
733       get_cell_style (clist, clist_row, state, i, &style);
734
735       /* calculate clipping region */
736       clip_rectangle.x = clist->column[i].area.x + clist->hoffset;
737       clip_rectangle.width = clist->column[i].area.width;
738
739       cell_rectangle.x = clip_rectangle.x - COLUMN_INSET - CELL_SPACING;
740       cell_rectangle.width = (clip_rectangle.width + 2 * COLUMN_INSET +
741                               (1 + (i == last_column)) * CELL_SPACING);
742       cell_rectangle.y = clip_rectangle.y;
743       cell_rectangle.height = clip_rectangle.height;
744
745       string_width = 0;
746       pixbuf_width = 0;
747       height = 0;
748
749       if (area && !gdk_rectangle_intersect (area, &cell_rectangle,
750                                             &intersect_rectangle))
751         {
752           if (i != ctree->tree_column)
753             continue;
754         }
755       else
756         {
757           gdk_cairo_rectangle(cr, &cell_rectangle);
758           if (state == GTK_STATE_NORMAL)
759                 gdk_cairo_set_source_color(cr, bgcolor);
760           else
761                 gdk_cairo_set_source_color(cr, &style->base[state]);
762           cairo_fill(cr);
763           layout = create_cell_layout (clist, clist_row, i);
764           if (layout)
765             {
766               pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
767               width = logical_rect.width;
768             }
769           else
770             width = 0;
771
772           switch (clist_row->cell[i].type)
773             {
774             case GTK_CMCELL_PIXBUF:
775               pixbuf_width = gdk_pixbuf_get_width(GTK_CMCELL_PIXBUF (clist_row->cell[i])->pixbuf);
776               height = gdk_pixbuf_get_height(GTK_CMCELL_PIXBUF (clist_row->cell[i])->pixbuf);
777               width += pixbuf_width;
778               break;
779             case GTK_CMCELL_PIXTEXT:
780               if (GTK_CMCELL_PIXTEXT (clist_row->cell[i])->pixbuf)
781                 {
782                   pixbuf_width = gdk_pixbuf_get_width(GTK_CMCELL_PIXTEXT (clist_row->cell[i])->pixbuf);
783                   height = gdk_pixbuf_get_height(GTK_CMCELL_PIXTEXT (clist_row->cell[i])->pixbuf);
784                   width += pixbuf_width;
785                 }
786
787               if (GTK_CMCELL_PIXTEXT (clist_row->cell[i])->text &&
788                   GTK_CMCELL_PIXTEXT (clist_row->cell[i])->pixbuf)
789                 width +=  GTK_CMCELL_PIXTEXT (clist_row->cell[i])->spacing;
790
791               if (i == ctree->tree_column)
792                 width += (ctree->tree_indent *
793                           ((GtkCMCTreeRow *)clist_row)->level);
794               break;
795             default:
796               break;
797             }
798
799           switch (clist->column[i].justification)
800             {
801             case GTK_JUSTIFY_LEFT:
802               offset = clip_rectangle.x + clist_row->cell[i].horizontal;
803               break;
804             case GTK_JUSTIFY_RIGHT:
805               offset = (clip_rectangle.x + clist_row->cell[i].horizontal +
806                         clip_rectangle.width - width);
807               break;
808             case GTK_JUSTIFY_CENTER:
809             case GTK_JUSTIFY_FILL:
810               offset = (clip_rectangle.x + clist_row->cell[i].horizontal +
811                         (clip_rectangle.width / 2) - (width / 2));
812               break;
813             };
814
815           if (i != ctree->tree_column)
816             {
817               int start_y = (clip_rectangle.height - height) / 2;
818               if (GTK_CMCLIST_ROW_HEIGHT_SET(GTK_CMCLIST(clist)))
819                       start_y = (clip_rectangle.height/2 - height) / 2;
820
821               offset += clist_row->cell[i].horizontal;
822               switch (clist_row->cell[i].type)
823                 {
824                 case GTK_CMCELL_PIXBUF:
825                   draw_cell_pixbuf
826                     (clist->clist_window, &clip_rectangle, cr,
827                      GTK_CMCELL_PIXBUF (clist_row->cell[i])->pixbuf,
828                      offset,
829                      clip_rectangle.y + clist_row->cell[i].vertical +
830                      start_y,
831                      pixbuf_width, height);
832                   break;
833                 case GTK_CMCELL_PIXTEXT:
834                   offset = draw_cell_pixbuf
835                     (clist->clist_window, &clip_rectangle, cr,
836                      GTK_CMCELL_PIXTEXT (clist_row->cell[i])->pixbuf,
837                      offset,
838                      clip_rectangle.y + clist_row->cell[i].vertical +
839                      start_y,
840                      pixbuf_width, height);
841                   offset += GTK_CMCELL_PIXTEXT (clist_row->cell[i])->spacing;
842
843                   /* Fall through */
844                 case GTK_CMCELL_TEXT:
845                   if (layout)
846                     {
847                       gint row_center_offset = (clist->row_height - logical_rect.height) / 2;
848                       gdk_cairo_set_source_color(cr, fgcolor);
849                       cairo_move_to(cr, offset, row_rectangle.y + row_center_offset + clist_row->cell[i].vertical);
850                       pango_cairo_show_layout(cr, layout);
851                       g_object_unref (G_OBJECT (layout));
852                     }
853                   break;
854                 default:
855                   break;
856                 }
857               continue;
858             }
859         }
860
861       /* draw ctree->tree_column */
862       cell_rectangle.y -= CELL_SPACING;
863       cell_rectangle.height += CELL_SPACING;
864
865       if (area && !gdk_rectangle_intersect (area, &cell_rectangle,
866                                             &intersect_rectangle))
867         {
868           if (layout)
869             g_object_unref (G_OBJECT (layout));
870           continue;
871         }
872
873
874       /* draw lines */
875       offset = get_offset (ctree, (GtkCMCTreeRow *)clist_row, i,
876                                       &clip_rectangle);
877
878       /* draw expander */
879       offset = draw_expander (ctree, (GtkCMCTreeRow *)clist_row,
880                                         style, &clip_rectangle, cr, offset);
881
882       if (clist->column[i].justification == GTK_JUSTIFY_RIGHT)
883         offset -= ctree->tree_spacing;
884       else
885         offset += ctree->tree_spacing;
886
887       if (clist->column[i].justification == GTK_JUSTIFY_RIGHT)
888         offset -= (pixbuf_width + clist_row->cell[i].horizontal);
889       else
890         offset += clist_row->cell[i].horizontal;
891
892       old_offset = offset;
893       offset = draw_cell_pixbuf (clist->clist_window, &clip_rectangle, cr,
894                                  GTK_CMCELL_PIXTEXT (clist_row->cell[i])->pixbuf,
895                                  offset, 
896                                  clip_rectangle.y + clist_row->cell[i].vertical
897                                  + (clip_rectangle.height - height) / 2,
898                                  pixbuf_width, height);
899
900       if (layout)
901         {
902           gint row_center_offset = (clist->row_height - logical_rect.height) / 2;
903           
904           if (clist->column[i].justification == GTK_JUSTIFY_RIGHT)
905             {
906               offset = (old_offset - string_width);
907               if (GTK_CMCELL_PIXTEXT (clist_row->cell[i])->pixbuf)
908                 offset -= GTK_CMCELL_PIXTEXT (clist_row->cell[i])->spacing;
909             }
910           else
911             {
912               if (GTK_CMCELL_PIXTEXT (clist_row->cell[i])->pixbuf)
913                 offset += GTK_CMCELL_PIXTEXT (clist_row->cell[i])->spacing;
914             }
915           
916           cairo_move_to(cr, offset, row_rectangle.y + row_center_offset + clist_row->cell[i].vertical);
917           gdk_cairo_set_source_color(cr, fgcolor);
918           pango_cairo_show_layout(cr, layout);
919           g_object_unref (G_OBJECT (layout));
920         }
921     }
922    /* draw focus rectangle */
923   if (clist->focus_row == row &&
924       gtk_widget_get_can_focus (widget) && gtk_widget_has_focus (widget)
925        && state == GTK_STATE_SELECTED)
926     {
927       if (!area || gdk_rectangle_intersect (area, &row_rectangle,
928                                         &intersect_rectangle))
929         {
930             cairo_set_line_width(cr, 1.0);
931             cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE);
932             gdk_cairo_set_source_color(cr, &style->text[GTK_STATE_NORMAL]);
933             cairo_move_to (cr, row_rectangle.x, row_rectangle.y + 0.5);
934             cairo_line_to (cr, row_rectangle.x + row_rectangle.width, row_rectangle.y + 0.5);
935             cairo_move_to (cr, row_rectangle.x, row_rectangle.y + row_rectangle.height - 0.5);
936             cairo_line_to (cr, row_rectangle.x + row_rectangle.width, row_rectangle.y + row_rectangle.height - 0.5);
937             cairo_stroke(cr);
938         }
939      }
940     cairo_destroy(cr);
941 }
942
943 static void
944 gtk_cmctree_class_init (GtkCMCTreeClass *klass)
945 {
946   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
947 #if !GTK_CHECK_VERSION(3, 0, 0)
948   GtkObjectClass *object_class;
949 #else /* for simplicity */
950   GtkWidgetClass *object_class;
951 #endif
952   GtkWidgetClass *widget_class;
953   GtkCMCListClass *clist_class;
954   GtkBindingSet *binding_set;
955
956   gobject_class->constructor = gtk_cmctree_constructor;
957
958 #if !GTK_CHECK_VERSION(3, 0, 0)
959   object_class = (GtkObjectClass *) klass;
960 #else /* for simplicity */
961   object_class = (GtkWidgetClass *) klass;
962 #endif
963   widget_class = (GtkWidgetClass *) klass;
964   container_class = (GtkContainerClass *) klass;
965   clist_class = (GtkCMCListClass *) klass;
966
967   parent_class = g_type_class_peek (GTK_TYPE_CMCLIST);
968   container_class = g_type_class_peek (GTK_TYPE_CONTAINER);
969
970   gobject_class->set_property = gtk_cmctree_set_arg;
971   gobject_class->get_property = gtk_cmctree_get_arg;
972
973   widget_class->realize = gtk_cmctree_realize;
974   widget_class->unrealize = gtk_cmctree_unrealize;
975   widget_class->button_press_event = gtk_cmctree_button_press;
976
977   widget_class->drag_begin = gtk_cmctree_drag_begin;
978   widget_class->drag_motion = gtk_cmctree_drag_motion;
979   widget_class->drag_data_received = gtk_cmctree_drag_data_received;
980
981   clist_class->select_row = real_select_row;
982   clist_class->unselect_row = real_unselect_row;
983   clist_class->row_move = real_row_move;
984   clist_class->undo_selection = real_undo_selection;
985   clist_class->resync_selection = resync_selection;
986   clist_class->selection_find = selection_find;
987   clist_class->click_column = NULL;
988   clist_class->draw_row = draw_row;
989   clist_class->clear = real_clear;
990   clist_class->select_all = real_select_all;
991   clist_class->unselect_all = real_unselect_all;
992   clist_class->fake_unselect_all = fake_unselect_all;
993   clist_class->insert_row = real_insert_row;
994   clist_class->remove_row = real_remove_row;
995   clist_class->sort_list = real_sort_list;
996   clist_class->set_cell_contents = set_cell_contents;
997   clist_class->cell_size_request = cell_size_request;
998
999   klass->tree_select_row = real_tree_select;
1000   klass->tree_unselect_row = real_tree_unselect;
1001   klass->tree_expand = real_tree_expand;
1002   klass->tree_collapse = real_tree_collapse;
1003   klass->tree_move = real_tree_move;
1004   klass->change_focus_row_expansion = change_focus_row_expansion;
1005
1006   g_object_class_install_property (gobject_class,
1007                                 ARG_N_COLUMNS,
1008                                 g_param_spec_uint ("n-columns",
1009                                 "N-Columns",
1010                                 "N-Columns",
1011                                 1,
1012                                 G_MAXINT,
1013                                 1,
1014                                 G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
1015   g_object_class_install_property (gobject_class,
1016                                 ARG_TREE_COLUMN,
1017                                 g_param_spec_uint ("tree-column",
1018                                 "tree-column",
1019                                 "tree-column",
1020                                 0,
1021                                 G_MAXINT,
1022                                 0,
1023                                 G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
1024   g_object_class_install_property (gobject_class,
1025                                 ARG_INDENT,
1026                                 g_param_spec_uint ("indent",
1027                                 "indent",
1028                                 "indent",
1029                                 1,
1030                                 G_MAXINT,
1031                                 1,
1032                                 G_PARAM_READWRITE));
1033   g_object_class_install_property (gobject_class,
1034                                 ARG_SPACING,
1035                                 g_param_spec_uint ("spacing",
1036                                 "spacing",
1037                                 "spacing",
1038                                 1,
1039                                 G_MAXINT,
1040                                 1,
1041                                 G_PARAM_READWRITE));
1042   g_object_class_install_property (gobject_class,
1043                                 ARG_SHOW_STUB,
1044                                 g_param_spec_boolean ("show-stub",
1045                                 "show-stub",
1046                                 "show-stub",
1047                                 TRUE,
1048                                 G_PARAM_READWRITE));
1049   g_object_class_install_property (gobject_class,
1050                                 ARG_LINE_STYLE,
1051                                 g_param_spec_enum ("line-style",
1052                                 "line-style",
1053                                 "line-style",
1054                                 GTK_TYPE_CMCTREE_LINE_STYLE, 0,
1055                                 G_PARAM_READWRITE));
1056   g_object_class_install_property (gobject_class,
1057                                 ARG_EXPANDER_STYLE,
1058                                 g_param_spec_enum ("expander-style",
1059                                 "expander-style",
1060                                 "expander-style",
1061                                 GTK_TYPE_CMCTREE_EXPANDER_STYLE, 0,
1062                                 G_PARAM_READWRITE));
1063
1064   ctree_signals[TREE_SELECT_ROW] =
1065                 g_signal_new ("tree_select_row",
1066                               G_TYPE_FROM_CLASS (object_class),
1067                               G_SIGNAL_RUN_FIRST,
1068                               G_STRUCT_OFFSET (GtkCMCTreeClass, tree_select_row),
1069                               NULL, NULL,
1070                               claws_marshal_VOID__POINTER_INT,
1071                               G_TYPE_NONE, 2,
1072                               GTK_TYPE_CMCTREE_NODE,
1073                               G_TYPE_INT);
1074   ctree_signals[TREE_UNSELECT_ROW] =
1075                 g_signal_new ("tree_unselect_row",
1076                               G_TYPE_FROM_CLASS (object_class),
1077                               G_SIGNAL_RUN_FIRST,
1078                               G_STRUCT_OFFSET (GtkCMCTreeClass, tree_unselect_row),
1079                               NULL, NULL,
1080                               claws_marshal_VOID__POINTER_INT,
1081                               G_TYPE_NONE, 2,
1082                               GTK_TYPE_CMCTREE_NODE,
1083                               G_TYPE_INT);
1084   ctree_signals[TREE_EXPAND] =
1085                 g_signal_new ("tree_expand",
1086                               G_TYPE_FROM_CLASS (object_class),
1087                               G_SIGNAL_RUN_LAST,
1088                               G_STRUCT_OFFSET (GtkCMCTreeClass, tree_expand),
1089                               NULL, NULL,
1090                               claws_marshal_VOID__POINTER,
1091                               G_TYPE_NONE, 1,
1092                               GTK_TYPE_CMCTREE_NODE);
1093   ctree_signals[TREE_COLLAPSE] =
1094                 g_signal_new ("tree_collapse",
1095                               G_TYPE_FROM_CLASS (object_class),
1096                               G_SIGNAL_RUN_LAST,
1097                               G_STRUCT_OFFSET (GtkCMCTreeClass, tree_collapse),
1098                               NULL, NULL,
1099                               claws_marshal_VOID__POINTER,
1100                               G_TYPE_NONE, 1,
1101                               GTK_TYPE_CMCTREE_NODE);
1102   ctree_signals[TREE_MOVE] =
1103                 g_signal_new ("tree_move",
1104                               G_TYPE_FROM_CLASS (object_class),
1105                               G_SIGNAL_RUN_LAST,
1106                               G_STRUCT_OFFSET (GtkCMCTreeClass, tree_move),
1107                               NULL, NULL,
1108                               claws_marshal_VOID__POINTER_POINTER_POINTER,
1109                               G_TYPE_NONE, 3,
1110                               GTK_TYPE_CMCTREE_NODE,GTK_TYPE_CMCTREE_NODE,GTK_TYPE_CMCTREE_NODE);
1111   ctree_signals[CHANGE_FOCUS_ROW_EXPANSION] =
1112                 g_signal_new ("change_focus_row_expansion",
1113                               G_TYPE_FROM_CLASS (object_class),
1114                               G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1115                               G_STRUCT_OFFSET (GtkCMCTreeClass, change_focus_row_expansion),
1116                               NULL, NULL,
1117                               claws_marshal_VOID__ENUM,
1118                               G_TYPE_NONE, 1, GTK_TYPE_CMCTREE_EXPANSION_TYPE);
1119
1120   binding_set = gtk_binding_set_by_class (klass);
1121   gtk_binding_entry_add_signal (binding_set,
1122                                 GDK_KEY_plus, 0,
1123                                 "change_focus_row_expansion", 1,
1124                                 G_TYPE_ENUM, GTK_CMCTREE_EXPANSION_EXPAND);
1125   gtk_binding_entry_add_signal (binding_set,
1126                                 GDK_KEY_plus, GDK_CONTROL_MASK,
1127                                 "change_focus_row_expansion", 1,
1128                                 G_TYPE_ENUM, GTK_CMCTREE_EXPANSION_EXPAND_RECURSIVE);
1129
1130   gtk_binding_entry_add_signal (binding_set,
1131                                 GDK_KEY_KP_Add, 0,
1132                                 "change_focus_row_expansion", 1,
1133                                 G_TYPE_ENUM, GTK_CMCTREE_EXPANSION_EXPAND);
1134   gtk_binding_entry_add_signal (binding_set,
1135                                 GDK_KEY_KP_Add, GDK_CONTROL_MASK,
1136                                 "change_focus_row_expansion", 1,
1137                                 G_TYPE_ENUM, GTK_CMCTREE_EXPANSION_EXPAND_RECURSIVE);
1138   
1139   gtk_binding_entry_add_signal (binding_set,
1140                                 GDK_KEY_minus, 0,
1141                                 "change_focus_row_expansion", 1,
1142                                 G_TYPE_ENUM, GTK_CMCTREE_EXPANSION_COLLAPSE);
1143   gtk_binding_entry_add_signal (binding_set,
1144                                 GDK_KEY_minus, GDK_CONTROL_MASK,
1145                                 "change_focus_row_expansion", 1,
1146                                 G_TYPE_ENUM,
1147                                 GTK_CMCTREE_EXPANSION_COLLAPSE_RECURSIVE);
1148   gtk_binding_entry_add_signal (binding_set,
1149                                 GDK_KEY_KP_Subtract, 0,
1150                                 "change_focus_row_expansion", 1,
1151                                 G_TYPE_ENUM, GTK_CMCTREE_EXPANSION_COLLAPSE);
1152   gtk_binding_entry_add_signal (binding_set,
1153                                 GDK_KEY_KP_Subtract, GDK_CONTROL_MASK,
1154                                 "change_focus_row_expansion", 1,
1155                                 G_TYPE_ENUM,
1156                                 GTK_CMCTREE_EXPANSION_COLLAPSE_RECURSIVE);
1157   gtk_binding_entry_add_signal (binding_set,
1158                                 GDK_KEY_equal, 0,
1159                                 "change_focus_row_expansion", 1,
1160                                 G_TYPE_ENUM, GTK_CMCTREE_EXPANSION_TOGGLE);
1161   gtk_binding_entry_add_signal (binding_set,
1162                                 GDK_KEY_KP_Equal, 0,
1163                                 "change_focus_row_expansion", 1,
1164                                 G_TYPE_ENUM, GTK_CMCTREE_EXPANSION_TOGGLE);
1165   gtk_binding_entry_add_signal (binding_set,
1166                                 GDK_KEY_KP_Multiply, 0,
1167                                 "change_focus_row_expansion", 1,
1168                                 G_TYPE_ENUM, GTK_CMCTREE_EXPANSION_TOGGLE);
1169   gtk_binding_entry_add_signal (binding_set,
1170                                 GDK_KEY_asterisk, 0,
1171                                 "change_focus_row_expansion", 1,
1172                                 G_TYPE_ENUM, GTK_CMCTREE_EXPANSION_TOGGLE);
1173   gtk_binding_entry_add_signal (binding_set,
1174                                 GDK_KEY_KP_Multiply, GDK_CONTROL_MASK,
1175                                 "change_focus_row_expansion", 1,
1176                                 G_TYPE_ENUM,
1177                                 GTK_CMCTREE_EXPANSION_TOGGLE_RECURSIVE);
1178   gtk_binding_entry_add_signal (binding_set,
1179                                 GDK_KEY_asterisk, GDK_CONTROL_MASK,
1180                                 "change_focus_row_expansion", 1,
1181                                 G_TYPE_ENUM,
1182                                 GTK_CMCTREE_EXPANSION_TOGGLE_RECURSIVE);  
1183 }
1184
1185 static void
1186 gtk_cmctree_set_arg (GObject *object,
1187                                 guint      arg_id,
1188                                 const GValue *value,
1189                                 GParamSpec *spec)
1190 {
1191   GtkCMCTree *ctree;
1192   GtkCMCList *clist;
1193
1194   ctree = GTK_CMCTREE (object);
1195   clist = GTK_CMCLIST (ctree);
1196
1197   switch (arg_id)
1198     {
1199     case ARG_N_COLUMNS: /* construct-only arg, only set at construction time */
1200       clist->columns = MAX (1, g_value_get_uint (value));
1201       ctree->tree_column = CLAMP (ctree->tree_column, 0, clist->columns);
1202       break;
1203     case ARG_TREE_COLUMN: /* construct-only arg, only set at construction time */
1204       ctree->tree_column = g_value_get_uint (value);
1205         ctree->tree_column = CLAMP (ctree->tree_column, 0, clist->columns);
1206       break;
1207     case ARG_INDENT:
1208       gtk_cmctree_set_indent (ctree, g_value_get_uint (value));
1209       break;
1210     case ARG_SPACING:
1211       gtk_cmctree_set_spacing (ctree, g_value_get_uint (value));
1212       break;
1213     case ARG_SHOW_STUB:
1214       gtk_cmctree_set_show_stub (ctree, g_value_get_boolean (value));
1215       break;
1216     case ARG_LINE_STYLE:
1217       gtk_cmctree_set_line_style (ctree, g_value_get_enum (value));
1218       break;
1219     case ARG_EXPANDER_STYLE:
1220       gtk_cmctree_set_expander_style (ctree, g_value_get_enum (value));
1221       break;
1222     default:
1223       break;
1224     }
1225 }
1226
1227 static void
1228 gtk_cmctree_get_arg (GObject *object,
1229                                 guint      arg_id,
1230                                 GValue *value,
1231                                 GParamSpec *spec)
1232 {
1233   GtkCMCTree *ctree;
1234
1235   ctree = GTK_CMCTREE (object);
1236
1237   switch (arg_id)
1238     {
1239     case ARG_N_COLUMNS:
1240       g_value_set_uint(value, GTK_CMCLIST (ctree)->columns);
1241       break;
1242     case ARG_TREE_COLUMN:
1243       g_value_set_uint(value, ctree->tree_column);
1244       break;
1245     case ARG_INDENT:
1246       g_value_set_uint(value, ctree->tree_indent);
1247       break;
1248     case ARG_SPACING:
1249       g_value_set_uint(value, ctree->tree_spacing);
1250       break;
1251     case ARG_SHOW_STUB:
1252       g_value_set_boolean(value, ctree->show_stub);
1253       break;
1254     case ARG_LINE_STYLE:
1255       g_value_set_enum(value, ctree->line_style);
1256       break;
1257     case ARG_EXPANDER_STYLE:
1258       g_value_set_enum(value, ctree->expander_style);
1259       break;
1260     default:
1261       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, arg_id, spec);
1262       break;
1263     }
1264 }
1265
1266 static void
1267 gtk_cmctree_init (GtkCMCTree *ctree)
1268 {
1269   GtkCMCList *clist;
1270
1271   GTK_CMCLIST_SET_FLAG (ctree, CMCLIST_DRAW_DRAG_RECT);
1272   GTK_CMCLIST_SET_FLAG (ctree, CMCLIST_DRAW_DRAG_LINE);
1273
1274   clist = GTK_CMCLIST (ctree);
1275
1276   ctree->tree_indent    = 20;
1277   ctree->tree_spacing   = 5;
1278   ctree->tree_column    = 0;
1279   ctree->line_style     = GTK_CMCTREE_LINES_NONE;
1280   ctree->expander_style = GTK_CMCTREE_EXPANDER_TRIANGLE;
1281   ctree->drag_compare   = NULL;
1282   ctree->show_stub      = TRUE;
1283
1284   clist->button_actions[0] |= GTK_CMBUTTON_EXPANDS;
1285 }
1286
1287 static void
1288 ctree_attach_styles (GtkCMCTree     *ctree,
1289                      GtkCMCTreeNode *node,
1290                      gpointer      data)
1291 {
1292   GtkCMCList *clist;
1293   gint i;
1294
1295   clist = GTK_CMCLIST (ctree);
1296
1297   if (GTK_CMCTREE_ROW (node)->row.style)
1298     GTK_CMCTREE_ROW (node)->row.style =
1299       gtk_style_attach (GTK_CMCTREE_ROW (node)->row.style, clist->clist_window);
1300
1301 #if !GTK_CHECK_VERSION(3, 0, 0)
1302   if (GTK_CMCTREE_ROW (node)->row.fg_set || GTK_CMCTREE_ROW (node)->row.bg_set)
1303     {
1304       GdkColormap *colormap;
1305
1306       colormap = gtk_widget_get_colormap (GTK_WIDGET (ctree));
1307       if (GTK_CMCTREE_ROW (node)->row.fg_set)
1308         gdk_colormap_alloc_color (colormap, &(GTK_CMCTREE_ROW (node)->row.foreground), TRUE, TRUE);
1309       if (GTK_CMCTREE_ROW (node)->row.bg_set)
1310         gdk_colormap_alloc_color (colormap, &(GTK_CMCTREE_ROW (node)->row.background), TRUE, TRUE);
1311     }
1312 #endif
1313
1314   for (i = 0; i < clist->columns; i++)
1315     if  (GTK_CMCTREE_ROW (node)->row.cell[i].style)
1316       GTK_CMCTREE_ROW (node)->row.cell[i].style =
1317         gtk_style_attach (GTK_CMCTREE_ROW (node)->row.cell[i].style,
1318                           clist->clist_window);
1319 }
1320
1321 static void
1322 ctree_detach_styles (GtkCMCTree     *ctree,
1323                      GtkCMCTreeNode *node,
1324                      gpointer      data)
1325 {
1326   GtkCMCList *clist;
1327   gint i;
1328
1329   clist = GTK_CMCLIST (ctree);
1330
1331   if (GTK_CMCTREE_ROW (node)->row.style)
1332     gtk_style_detach (GTK_CMCTREE_ROW (node)->row.style);
1333   for (i = 0; i < clist->columns; i++)
1334     if  (GTK_CMCTREE_ROW (node)->row.cell[i].style)
1335       gtk_style_detach (GTK_CMCTREE_ROW (node)->row.cell[i].style);
1336 }
1337
1338 static void
1339 gtk_cmctree_realize (GtkWidget *widget)
1340 {
1341   GtkCMCTree *ctree;
1342   GtkCMCList *clist;
1343   GtkCMCTreeNode *node;
1344   GtkCMCTreeNode *child;
1345   gint i;
1346
1347   cm_return_if_fail (GTK_IS_CMCTREE (widget));
1348
1349   GTK_WIDGET_CLASS (parent_class)->realize (widget);
1350
1351   ctree = GTK_CMCTREE (widget);
1352   clist = GTK_CMCLIST (widget);
1353
1354   node = GTK_CMCTREE_NODE (clist->row_list);
1355   for (i = 0; i < clist->rows; i++)
1356     {
1357       if (GTK_CMCTREE_ROW (node)->children && !GTK_CMCTREE_ROW (node)->expanded)
1358         for (child = GTK_CMCTREE_ROW (node)->children; child;
1359              child = GTK_CMCTREE_ROW (child)->sibling)
1360           gtk_cmctree_pre_recursive (ctree, child, ctree_attach_styles, NULL);
1361       node = GTK_CMCTREE_NODE_NEXT (node);
1362     }
1363 }
1364
1365 static void
1366 gtk_cmctree_unrealize (GtkWidget *widget)
1367 {
1368   GtkCMCTree *ctree;
1369   GtkCMCList *clist;
1370
1371   cm_return_if_fail (GTK_IS_CMCTREE (widget));
1372
1373   GTK_WIDGET_CLASS (parent_class)->unrealize (widget);
1374
1375   ctree = GTK_CMCTREE (widget);
1376   clist = GTK_CMCLIST (widget);
1377
1378   if (gtk_widget_get_realized (widget))
1379     {
1380       GtkCMCTreeNode *node;
1381       GtkCMCTreeNode *child;
1382       gint i;
1383
1384       node = GTK_CMCTREE_NODE (clist->row_list);
1385       for (i = 0; i < clist->rows; i++)
1386         {
1387           if (GTK_CMCTREE_ROW (node)->children &&
1388               !GTK_CMCTREE_ROW (node)->expanded)
1389             for (child = GTK_CMCTREE_ROW (node)->children; child;
1390                  child = GTK_CMCTREE_ROW (child)->sibling)
1391               gtk_cmctree_pre_recursive(ctree, child, ctree_detach_styles, NULL);
1392           node = GTK_CMCTREE_NODE_NEXT (node);
1393         }
1394     }
1395 }
1396
1397 static gint
1398 gtk_cmctree_button_press (GtkWidget      *widget,
1399                         GdkEventButton *event)
1400 {
1401   GtkCMCTree *ctree;
1402   GtkCMCList *clist;
1403   gint button_actions;
1404
1405   cm_return_val_if_fail (GTK_IS_CMCTREE (widget), FALSE);
1406   cm_return_val_if_fail (event != NULL, FALSE);
1407
1408   ctree = GTK_CMCTREE (widget);
1409   clist = GTK_CMCLIST (widget);
1410
1411   button_actions = clist->button_actions[event->button - 1];
1412
1413   if (button_actions == GTK_CMBUTTON_IGNORED)
1414     return FALSE;
1415
1416   if (event->window == clist->clist_window)
1417     {
1418       GtkCMCTreeNode *work;
1419       gint x;
1420       gint y;
1421       gint row;
1422       gint column;
1423
1424       x = event->x;
1425       y = event->y;
1426
1427       if (!gtk_cmclist_get_selection_info (clist, x, y, &row, &column))
1428         return FALSE;
1429
1430       work = GTK_CMCTREE_NODE (g_list_nth (clist->row_list, row));
1431           
1432       if (button_actions & GTK_CMBUTTON_EXPANDS &&
1433           (GTK_CMCTREE_ROW (work)->children && !GTK_CMCTREE_ROW (work)->is_leaf  &&
1434            (event->type == GDK_2BUTTON_PRESS ||
1435             ctree_is_hot_spot (ctree, work, row, x, y))))
1436         {
1437           if (GTK_CMCTREE_ROW (work)->expanded)
1438             gtk_cmctree_collapse (ctree, work);
1439           else
1440             gtk_cmctree_expand (ctree, work);
1441
1442           return TRUE;
1443         }
1444     }
1445   
1446   return GTK_WIDGET_CLASS (parent_class)->button_press_event (widget, event);
1447 }
1448
1449 static GtkCMCTreeNode *
1450 gtk_cmctree_last_visible (GtkCMCTree     *ctree,
1451                         GtkCMCTreeNode *node)
1452 {
1453   GtkCMCTreeNode *work;
1454   
1455   if (!node)
1456     return NULL;
1457
1458   work = GTK_CMCTREE_ROW (node)->children;
1459
1460   if (!work || !GTK_CMCTREE_ROW (node)->expanded)
1461     return node;
1462
1463   while (GTK_CMCTREE_ROW (work)->sibling)
1464     work = GTK_CMCTREE_ROW (work)->sibling;
1465
1466   return gtk_cmctree_last_visible (ctree, work);
1467 }
1468
1469 static void
1470 gtk_cmctree_link (GtkCMCTree     *ctree,
1471                 GtkCMCTreeNode *node,
1472                 GtkCMCTreeNode *parent,
1473                 GtkCMCTreeNode *sibling,
1474                 gboolean      update_focus_row)
1475 {
1476   GtkCMCList *clist;
1477   GList *list_end;
1478   GList *list;
1479   GList *work;
1480   gboolean visible = FALSE;
1481   gint rows = 0;
1482   
1483   if (sibling)
1484     cm_return_if_fail (GTK_CMCTREE_ROW (sibling)->parent == parent);
1485   cm_return_if_fail (node != NULL);
1486   cm_return_if_fail (node != sibling);
1487   cm_return_if_fail (node != parent);
1488
1489   clist = GTK_CMCLIST (ctree);
1490
1491   if (update_focus_row && clist->selection_mode == GTK_SELECTION_MULTIPLE)
1492     {
1493       GTK_CMCLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
1494       
1495       g_list_free (clist->undo_selection);
1496       g_list_free (clist->undo_unselection);
1497       clist->undo_selection = NULL;
1498       clist->undo_unselection = NULL;
1499     }
1500
1501   for (rows = 1, list_end = (GList *)node; list_end->next;
1502        list_end = list_end->next)
1503     rows++;
1504
1505   GTK_CMCTREE_ROW (node)->parent = parent;
1506   GTK_CMCTREE_ROW (node)->sibling = sibling;
1507
1508   if (!parent || (parent && (gtk_cmctree_is_viewable (ctree, parent) &&
1509                              GTK_CMCTREE_ROW (parent)->expanded)))
1510     {
1511       visible = TRUE;
1512       clist->rows += rows;
1513     }
1514
1515   if (parent)
1516     work = (GList *)(GTK_CMCTREE_ROW (parent)->children);
1517   else
1518     work = clist->row_list;
1519
1520   if (sibling)
1521     {
1522       if (work != (GList *)sibling)
1523         {
1524           while (GTK_CMCTREE_ROW (work)->sibling != sibling)
1525             work = (GList *)(GTK_CMCTREE_ROW (work)->sibling);
1526           GTK_CMCTREE_ROW (work)->sibling = node;
1527         }
1528
1529       if (sibling == GTK_CMCTREE_NODE (clist->row_list))
1530         clist->row_list = (GList *) node;
1531       if (GTK_CMCTREE_NODE_PREV (sibling) &&
1532           GTK_CMCTREE_NODE_NEXT (GTK_CMCTREE_NODE_PREV (sibling)) == sibling)
1533         {
1534           list = (GList *)GTK_CMCTREE_NODE_PREV (sibling);
1535           list->next = (GList *)node;
1536         }
1537       
1538       list = (GList *)node;
1539       list->prev = (GList *)GTK_CMCTREE_NODE_PREV (sibling);
1540       list_end->next = (GList *)sibling;
1541       list = (GList *)sibling;
1542       list->prev = list_end;
1543       if (parent && GTK_CMCTREE_ROW (parent)->children == sibling)
1544         GTK_CMCTREE_ROW (parent)->children = node;
1545     }
1546   else
1547     {
1548       if (work)
1549         {
1550           /* find sibling */
1551           while (GTK_CMCTREE_ROW (work)->sibling)
1552             work = (GList *)(GTK_CMCTREE_ROW (work)->sibling);
1553           GTK_CMCTREE_ROW (work)->sibling = node;
1554           
1555           /* find last visible child of sibling */
1556           work = (GList *) gtk_cmctree_last_visible (ctree,
1557                                                    GTK_CMCTREE_NODE (work));
1558           
1559           list_end->next = work->next;
1560           if (work->next)
1561             work->next->prev = list_end;
1562           work->next = (GList *)node;
1563           list = (GList *)node;
1564           list->prev = work;
1565         }
1566       else
1567         {
1568           if (parent)
1569             {
1570               GTK_CMCTREE_ROW (parent)->children = node;
1571               list = (GList *)node;
1572               list->prev = (GList *)parent;
1573               if (GTK_CMCTREE_ROW (parent)->expanded)
1574                 {
1575                   list_end->next = (GList *)GTK_CMCTREE_NODE_NEXT (parent);
1576                   if (GTK_CMCTREE_NODE_NEXT(parent))
1577                     {
1578                       list = (GList *)GTK_CMCTREE_NODE_NEXT (parent);
1579                       list->prev = list_end;
1580                     }
1581                   list = (GList *)parent;
1582                   list->next = (GList *)node;
1583                 }
1584               else
1585                 list_end->next = NULL;
1586             }
1587           else
1588             {
1589               clist->row_list = (GList *)node;
1590               list = (GList *)node;
1591               list->prev = NULL;
1592               list_end->next = NULL;
1593             }
1594         }
1595     }
1596
1597   gtk_cmctree_pre_recursive (ctree, node, tree_update_level, NULL); 
1598
1599   if (clist->row_list_end == NULL ||
1600       clist->row_list_end->next == (GList *)node)
1601     clist->row_list_end = list_end;
1602
1603   if (visible && update_focus_row)
1604     {
1605       gint pos;
1606           
1607       pos = g_list_position (clist->row_list, (GList *)node);
1608   
1609       if (pos <= clist->focus_row)
1610         {
1611           clist->focus_row += rows;
1612           clist->undo_anchor = clist->focus_row;
1613         }
1614     }
1615 }
1616
1617 static void
1618 gtk_cmctree_unlink (GtkCMCTree     *ctree, 
1619                   GtkCMCTreeNode *node,
1620                   gboolean      update_focus_row)
1621 {
1622   GtkCMCList *clist;
1623   gint rows;
1624   gint level;
1625   gint visible;
1626   GtkCMCTreeNode *work;
1627   GtkCMCTreeNode *parent;
1628   GList *list;
1629
1630   cm_return_if_fail (GTK_IS_CMCTREE (ctree));
1631   cm_return_if_fail (node != NULL);
1632
1633   clist = GTK_CMCLIST (ctree);
1634   
1635   if (update_focus_row && clist->selection_mode == GTK_SELECTION_MULTIPLE)
1636     {
1637       GTK_CMCLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
1638       
1639       g_list_free (clist->undo_selection);
1640       g_list_free (clist->undo_unselection);
1641       clist->undo_selection = NULL;
1642       clist->undo_unselection = NULL;
1643     }
1644
1645   visible = gtk_cmctree_is_viewable (ctree, node);
1646
1647   /* clist->row_list_end unlinked ? */
1648   if (visible &&
1649       (GTK_CMCTREE_NODE_NEXT (node) == NULL ||
1650        (GTK_CMCTREE_ROW (node)->children &&
1651         gtk_cmctree_is_ancestor (ctree, node,
1652                                GTK_CMCTREE_NODE (clist->row_list_end)))))
1653     clist->row_list_end = (GList *) (GTK_CMCTREE_NODE_PREV (node));
1654
1655   /* update list */
1656   rows = 0;
1657   level = GTK_CMCTREE_ROW (node)->level;
1658   work = GTK_CMCTREE_NODE_NEXT (node);
1659   while (work && GTK_CMCTREE_ROW (work)->level > level)
1660     {
1661       work = GTK_CMCTREE_NODE_NEXT (work);
1662       rows++;
1663     }
1664
1665   if (visible)
1666     {
1667       clist->rows -= (rows + 1);
1668
1669       if (update_focus_row)
1670         {
1671           gint pos;
1672           
1673           pos = g_list_position (clist->row_list, (GList *)node);
1674           if (pos + rows < clist->focus_row)
1675             clist->focus_row -= (rows + 1);
1676           else if (pos <= clist->focus_row)
1677             {
1678               if (!GTK_CMCTREE_ROW (node)->sibling)
1679                 clist->focus_row = MAX (pos - 1, 0);
1680               else
1681                 clist->focus_row = pos;
1682               
1683               clist->focus_row = MIN (clist->focus_row, clist->rows - 1);
1684             }
1685           clist->undo_anchor = clist->focus_row;
1686         }
1687     }
1688
1689   if (work)
1690     {
1691       list = (GList *)GTK_CMCTREE_NODE_PREV (work);
1692       list->next = NULL;
1693       list = (GList *)work;
1694       list->prev = (GList *)GTK_CMCTREE_NODE_PREV (node);
1695     }
1696
1697   if (GTK_CMCTREE_NODE_PREV (node) &&
1698       GTK_CMCTREE_NODE_NEXT (GTK_CMCTREE_NODE_PREV (node)) == node)
1699     {
1700       list = (GList *)GTK_CMCTREE_NODE_PREV (node);
1701       list->next = (GList *)work;
1702     }
1703
1704   /* update tree */
1705   parent = GTK_CMCTREE_ROW (node)->parent;
1706   if (parent)
1707     {
1708       if (GTK_CMCTREE_ROW (parent)->children == node)
1709         {
1710           GTK_CMCTREE_ROW (parent)->children = GTK_CMCTREE_ROW (node)->sibling;
1711           if (!GTK_CMCTREE_ROW (parent)->children)
1712             gtk_cmctree_collapse (ctree, parent);
1713         }
1714       else
1715         {
1716           GtkCMCTreeNode *sibling;
1717
1718           sibling = GTK_CMCTREE_ROW (parent)->children;
1719           while (GTK_CMCTREE_ROW (sibling)->sibling != node)
1720             sibling = GTK_CMCTREE_ROW (sibling)->sibling;
1721           GTK_CMCTREE_ROW (sibling)->sibling = GTK_CMCTREE_ROW (node)->sibling;
1722         }
1723     }
1724   else
1725     {
1726       if (clist->row_list == (GList *)node)
1727         clist->row_list = (GList *) (GTK_CMCTREE_ROW (node)->sibling);
1728       else
1729         {
1730           GtkCMCTreeNode *sibling;
1731
1732           sibling = GTK_CMCTREE_NODE (clist->row_list);
1733           while (GTK_CMCTREE_ROW (sibling)->sibling != node)
1734             sibling = GTK_CMCTREE_ROW (sibling)->sibling;
1735           GTK_CMCTREE_ROW (sibling)->sibling = GTK_CMCTREE_ROW (node)->sibling;
1736         }
1737     }
1738 }
1739
1740 static void
1741 real_row_move (GtkCMCList *clist,
1742                gint      source_row,
1743                gint      dest_row)
1744 {
1745   GtkCMCTree *ctree;
1746   GtkCMCTreeNode *node;
1747
1748   cm_return_if_fail (GTK_IS_CMCTREE (clist));
1749
1750   if (GTK_CMCLIST_AUTO_SORT (clist))
1751     return;
1752
1753   if (source_row < 0 || source_row >= clist->rows ||
1754       dest_row   < 0 || dest_row   >= clist->rows ||
1755       source_row == dest_row)
1756     return;
1757
1758   ctree = GTK_CMCTREE (clist);
1759   node = GTK_CMCTREE_NODE (g_list_nth (clist->row_list, source_row));
1760
1761   if (source_row < dest_row)
1762     {
1763       GtkCMCTreeNode *work; 
1764
1765       dest_row++;
1766       work = GTK_CMCTREE_ROW (node)->children;
1767
1768       while (work && GTK_CMCTREE_ROW (work)->level > GTK_CMCTREE_ROW (node)->level)
1769         {
1770           work = GTK_CMCTREE_NODE_NEXT (work);
1771           dest_row++;
1772         }
1773
1774       if (dest_row > clist->rows)
1775         dest_row = clist->rows;
1776     }
1777
1778   if (dest_row < clist->rows)
1779     {
1780       GtkCMCTreeNode *sibling;
1781
1782       sibling = GTK_CMCTREE_NODE (g_list_nth (clist->row_list, dest_row));
1783       gtk_cmctree_move (ctree, node, GTK_CMCTREE_ROW (sibling)->parent, sibling);
1784     }
1785   else
1786     gtk_cmctree_move (ctree, node, NULL, NULL);
1787 }
1788
1789 static void
1790 real_tree_move (GtkCMCTree     *ctree,
1791                 GtkCMCTreeNode *node,
1792                 GtkCMCTreeNode *new_parent, 
1793                 GtkCMCTreeNode *new_sibling)
1794 {
1795   GtkCMCList *clist;
1796   GtkCMCTreeNode *work;
1797   gboolean visible = FALSE;
1798
1799   cm_return_if_fail (ctree != NULL);
1800   cm_return_if_fail (node != NULL);
1801   cm_return_if_fail (!new_sibling || 
1802                     GTK_CMCTREE_ROW (new_sibling)->parent == new_parent);
1803
1804   if (new_parent && GTK_CMCTREE_ROW (new_parent)->is_leaf)
1805     return;
1806
1807   /* new_parent != child of child */
1808   for (work = new_parent; work; work = GTK_CMCTREE_ROW (work)->parent)
1809     if (work == node)
1810       return;
1811
1812   clist = GTK_CMCLIST (ctree);
1813
1814   visible = gtk_cmctree_is_viewable (ctree, node);
1815
1816   if (clist->selection_mode == GTK_SELECTION_MULTIPLE)
1817     {
1818       GTK_CMCLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
1819       
1820       g_list_free (clist->undo_selection);
1821       g_list_free (clist->undo_unselection);
1822       clist->undo_selection = NULL;
1823       clist->undo_unselection = NULL;
1824     }
1825
1826   if (GTK_CMCLIST_AUTO_SORT (clist))
1827     {
1828       if (new_parent == GTK_CMCTREE_ROW (node)->parent)
1829         return;
1830       
1831       if (new_parent)
1832         new_sibling = GTK_CMCTREE_ROW (new_parent)->children;
1833       else
1834         new_sibling = GTK_CMCTREE_NODE (clist->row_list);
1835
1836       while (new_sibling && clist->compare
1837              (clist, GTK_CMCTREE_ROW (node), GTK_CMCTREE_ROW (new_sibling)) > 0)
1838         new_sibling = GTK_CMCTREE_ROW (new_sibling)->sibling;
1839     }
1840
1841   if (new_parent == GTK_CMCTREE_ROW (node)->parent && 
1842       new_sibling == GTK_CMCTREE_ROW (node)->sibling)
1843     return;
1844
1845   gtk_cmclist_freeze (clist);
1846
1847   work = NULL;
1848   if (gtk_cmctree_is_viewable (ctree, node))
1849     work = GTK_CMCTREE_NODE (g_list_nth (clist->row_list, clist->focus_row));
1850       
1851   gtk_cmctree_unlink (ctree, node, FALSE);
1852   gtk_cmctree_link (ctree, node, new_parent, new_sibling, FALSE);
1853   
1854   if (work)
1855     {
1856       while (work &&  !gtk_cmctree_is_viewable (ctree, work))
1857         work = GTK_CMCTREE_ROW (work)->parent;
1858       clist->focus_row = g_list_position (clist->row_list, (GList *)work);
1859       clist->undo_anchor = clist->focus_row;
1860     }
1861
1862   if (clist->column[ctree->tree_column].auto_resize &&
1863       !GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist) &&
1864       (visible || gtk_cmctree_is_viewable (ctree, node)))
1865     gtk_cmclist_set_column_width
1866       (clist, ctree->tree_column,
1867        gtk_cmclist_optimal_column_width (clist, ctree->tree_column));
1868
1869   gtk_cmclist_thaw (clist);
1870 }
1871
1872 static void
1873 change_focus_row_expansion (GtkCMCTree          *ctree,
1874                             GtkCMCTreeExpansionType action)
1875 {
1876   GtkCMCList *clist;
1877   GtkCMCTreeNode *node;
1878
1879   cm_return_if_fail (GTK_IS_CMCTREE (ctree));
1880
1881   clist = GTK_CMCLIST (ctree);
1882
1883   if (gdk_display_pointer_is_grabbed (gtk_widget_get_display (GTK_WIDGET (ctree))) && 
1884       gtk_widget_has_grab (GTK_WIDGET(ctree)))
1885     return;
1886   
1887   if (!(node =
1888         GTK_CMCTREE_NODE (g_list_nth (clist->row_list, clist->focus_row))) ||
1889       GTK_CMCTREE_ROW (node)->is_leaf || !(GTK_CMCTREE_ROW (node)->children))
1890     return;
1891
1892   switch (action)
1893     {
1894     case GTK_CMCTREE_EXPANSION_EXPAND:
1895       gtk_cmctree_expand (ctree, node);
1896       break;
1897     case GTK_CMCTREE_EXPANSION_EXPAND_RECURSIVE:
1898       gtk_cmctree_expand_recursive (ctree, node);
1899       break;
1900     case GTK_CMCTREE_EXPANSION_COLLAPSE:
1901       gtk_cmctree_collapse (ctree, node);
1902       break;
1903     case GTK_CMCTREE_EXPANSION_COLLAPSE_RECURSIVE:
1904       gtk_cmctree_collapse_recursive (ctree, node);
1905       break;
1906     case GTK_CMCTREE_EXPANSION_TOGGLE:
1907       gtk_cmctree_toggle_expansion (ctree, node);
1908       break;
1909     case GTK_CMCTREE_EXPANSION_TOGGLE_RECURSIVE:
1910       gtk_cmctree_toggle_expansion_recursive (ctree, node);
1911       break;
1912     }
1913 }
1914
1915 static void 
1916 real_tree_expand (GtkCMCTree     *ctree,
1917                   GtkCMCTreeNode *node)
1918 {
1919   GtkCMCList *clist;
1920   GtkCMCTreeNode *work;
1921   GtkRequisition requisition;
1922   gboolean visible;
1923
1924   cm_return_if_fail (GTK_IS_CMCTREE (ctree));
1925
1926   if (!node || GTK_CMCTREE_ROW (node)->expanded || GTK_CMCTREE_ROW (node)->is_leaf)
1927     return;
1928
1929   clist = GTK_CMCLIST (ctree);
1930   
1931   GTK_CMCLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
1932
1933   GTK_CMCTREE_ROW (node)->expanded = TRUE;
1934
1935   visible = gtk_cmctree_is_viewable (ctree, node);
1936   /* get cell width if tree_column is auto resized */
1937   if (visible && clist->column[ctree->tree_column].auto_resize &&
1938       !GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist))
1939     GTK_CMCLIST_GET_CLASS (clist)->cell_size_request
1940       (clist, &GTK_CMCTREE_ROW (node)->row, ctree->tree_column, &requisition);
1941
1942   /* unref/unset closed pixbuf */
1943   if (GTK_CMCELL_PIXTEXT 
1944       (GTK_CMCTREE_ROW (node)->row.cell[ctree->tree_column])->pixbuf)
1945     {
1946       g_object_unref
1947         (GTK_CMCELL_PIXTEXT
1948          (GTK_CMCTREE_ROW (node)->row.cell[ctree->tree_column])->pixbuf);
1949       
1950       GTK_CMCELL_PIXTEXT
1951         (GTK_CMCTREE_ROW (node)->row.cell[ctree->tree_column])->pixbuf = NULL;
1952     }
1953
1954   /* set/ref opened pixbuf */
1955   if (GTK_CMCTREE_ROW (node)->pixbuf_opened)
1956     {
1957       GTK_CMCELL_PIXTEXT 
1958         (GTK_CMCTREE_ROW (node)->row.cell[ctree->tree_column])->pixbuf = 
1959         g_object_ref (GTK_CMCTREE_ROW (node)->pixbuf_opened);
1960     }
1961
1962
1963   work = GTK_CMCTREE_ROW (node)->children;
1964   if (work)
1965     {
1966       GList *list = (GList *)work;
1967       gint *cell_width = NULL;
1968       gint tmp = 0;
1969       gint row;
1970       gint i;
1971       
1972       if (visible && !GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist))
1973         {
1974           cell_width = g_new0 (gint, clist->columns);
1975           if (clist->column[ctree->tree_column].auto_resize)
1976               cell_width[ctree->tree_column] = requisition.width;
1977
1978           while (work)
1979             {
1980               /* search maximum cell widths of auto_resize columns */
1981               for (i = 0; i < clist->columns; i++)
1982                 if (clist->column[i].auto_resize)
1983                   {
1984                     GTK_CMCLIST_GET_CLASS (clist)->cell_size_request
1985                       (clist, &GTK_CMCTREE_ROW (work)->row, i, &requisition);
1986                     cell_width[i] = MAX (requisition.width, cell_width[i]);
1987                   }
1988
1989               list = (GList *)work;
1990               work = GTK_CMCTREE_NODE_NEXT (work);
1991               tmp++;
1992             }
1993         }
1994       else
1995         while (work)
1996           {
1997             list = (GList *)work;
1998             work = GTK_CMCTREE_NODE_NEXT (work);
1999             tmp++;
2000           }
2001
2002       list->next = (GList *)GTK_CMCTREE_NODE_NEXT (node);
2003
2004       if (GTK_CMCTREE_NODE_NEXT (node))
2005         {
2006           GList *tmp_list;
2007
2008           tmp_list = (GList *)GTK_CMCTREE_NODE_NEXT (node);
2009           tmp_list->prev = list;
2010         }
2011       else
2012         clist->row_list_end = list;
2013
2014       list = (GList *)node;
2015       list->next = (GList *)(GTK_CMCTREE_ROW (node)->children);
2016
2017       if (visible && !GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist))
2018         {
2019           /* resize auto_resize columns if needed */
2020           for (i = 0; i < clist->columns; i++)
2021             if (clist->column[i].auto_resize &&
2022                 cell_width[i] > clist->column[i].width)
2023               gtk_cmclist_set_column_width (clist, i, cell_width[i]);
2024           g_free (cell_width);
2025
2026           /* update focus_row position */
2027           row = g_list_position (clist->row_list, (GList *)node);
2028           if (row < clist->focus_row)
2029             clist->focus_row += tmp;
2030
2031           clist->rows += tmp;
2032           CLIST_REFRESH (clist);
2033         }
2034     }
2035   else if (visible && clist->column[ctree->tree_column].auto_resize)
2036     /* resize tree_column if needed */
2037     column_auto_resize (clist, &GTK_CMCTREE_ROW (node)->row, ctree->tree_column,
2038                         requisition.width);
2039 }
2040
2041 static void 
2042 real_tree_collapse (GtkCMCTree     *ctree,
2043                     GtkCMCTreeNode *node)
2044 {
2045   GtkCMCList *clist;
2046   GtkCMCTreeNode *work;
2047   GtkRequisition requisition;
2048   gboolean visible;
2049   gint level;
2050
2051   cm_return_if_fail (GTK_IS_CMCTREE (ctree));
2052
2053   if (!node || !GTK_CMCTREE_ROW (node)->expanded ||
2054       GTK_CMCTREE_ROW (node)->is_leaf)
2055     return;
2056
2057   clist = GTK_CMCLIST (ctree);
2058
2059   GTK_CMCLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
2060   
2061   GTK_CMCTREE_ROW (node)->expanded = FALSE;
2062   level = GTK_CMCTREE_ROW (node)->level;
2063
2064   visible = gtk_cmctree_is_viewable (ctree, node);
2065   /* get cell width if tree_column is auto resized */
2066   if (visible && clist->column[ctree->tree_column].auto_resize &&
2067       !GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist))
2068     GTK_CMCLIST_GET_CLASS (clist)->cell_size_request
2069       (clist, &GTK_CMCTREE_ROW (node)->row, ctree->tree_column, &requisition);
2070
2071   /* unref/unset opened pixbuf */
2072   if (GTK_CMCELL_PIXTEXT 
2073       (GTK_CMCTREE_ROW (node)->row.cell[ctree->tree_column])->pixbuf)
2074     {
2075       g_object_unref
2076         (GTK_CMCELL_PIXTEXT
2077          (GTK_CMCTREE_ROW (node)->row.cell[ctree->tree_column])->pixbuf);
2078       
2079       GTK_CMCELL_PIXTEXT
2080         (GTK_CMCTREE_ROW (node)->row.cell[ctree->tree_column])->pixbuf = NULL;
2081     }
2082
2083   /* set/ref closed pixbuf */
2084   if (GTK_CMCTREE_ROW (node)->pixbuf_closed)
2085     {
2086       GTK_CMCELL_PIXTEXT 
2087         (GTK_CMCTREE_ROW (node)->row.cell[ctree->tree_column])->pixbuf = 
2088         g_object_ref (GTK_CMCTREE_ROW (node)->pixbuf_closed);
2089     }
2090
2091   work = GTK_CMCTREE_ROW (node)->children;
2092   if (work)
2093     {
2094       gint tmp = 0;
2095       gint row;
2096       GList *list;
2097
2098       while (work && GTK_CMCTREE_ROW (work)->level > level)
2099         {
2100           work = GTK_CMCTREE_NODE_NEXT (work);
2101           tmp++;
2102         }
2103
2104       if (work)
2105         {
2106           list = (GList *)node;
2107           list->next = (GList *)work;
2108           list = (GList *)GTK_CMCTREE_NODE_PREV (work);
2109           list->next = NULL;
2110           list = (GList *)work;
2111           list->prev = (GList *)node;
2112         }
2113       else
2114         {
2115           list = (GList *)node;
2116           list->next = NULL;
2117           clist->row_list_end = (GList *)node;
2118         }
2119
2120       if (visible)
2121         {
2122           /* resize auto_resize columns if needed */
2123           auto_resize_columns (clist);
2124
2125           row = g_list_position (clist->row_list, (GList *)node);
2126           if (row < clist->focus_row)
2127             clist->focus_row -= tmp;
2128           clist->rows -= tmp;
2129           CLIST_REFRESH (clist);
2130         }
2131     }
2132   else if (visible && clist->column[ctree->tree_column].auto_resize &&
2133            !GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist))
2134     /* resize tree_column if needed */
2135     column_auto_resize (clist, &GTK_CMCTREE_ROW (node)->row, ctree->tree_column,
2136                         requisition.width);
2137     
2138 }
2139
2140 static void
2141 column_auto_resize (GtkCMCList    *clist,
2142                     GtkCMCListRow *clist_row,
2143                     gint         column,
2144                     gint         old_width)
2145 {
2146   /* resize column if needed for auto_resize */
2147   GtkRequisition requisition;
2148
2149   if (!clist->column[column].auto_resize ||
2150       GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist))
2151     return;
2152
2153   if (clist_row)
2154     GTK_CMCLIST_GET_CLASS (clist)->cell_size_request (clist, clist_row,
2155                                                    column, &requisition);
2156   else
2157     requisition.width = 0;
2158
2159   if (requisition.width > clist->column[column].width)
2160     gtk_cmclist_set_column_width (clist, column, requisition.width);
2161   else if (requisition.width < old_width &&
2162            old_width == clist->column[column].width)
2163     {
2164       GList *list;
2165       gint new_width;
2166
2167       /* run a "gtk_cmclist_optimal_column_width" but break, if
2168        * the column doesn't shrink */
2169       if (GTK_CMCLIST_SHOW_TITLES (clist) && clist->column[column].button)
2170         {
2171         GtkRequisition req;
2172         gtk_widget_get_requisition (clist->column[column].button, &req);
2173         new_width = (req.width -
2174                      (CELL_SPACING + (2 * COLUMN_INSET)));
2175         }
2176       else
2177         new_width = 0;
2178
2179       for (list = clist->row_list; list; list = list->next)
2180         {
2181           GTK_CMCLIST_GET_CLASS (clist)->cell_size_request
2182             (clist, GTK_CMCLIST_ROW (list), column, &requisition);
2183           new_width = MAX (new_width, requisition.width);
2184           if (new_width == clist->column[column].width)
2185             break;
2186         }
2187       if (new_width < clist->column[column].width)
2188         gtk_cmclist_set_column_width (clist, column, new_width);
2189     }
2190 }
2191
2192 static void
2193 auto_resize_columns (GtkCMCList *clist)
2194 {
2195   gint i;
2196
2197   if (GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist))
2198     return;
2199
2200   for (i = 0; i < clist->columns; i++)
2201     column_auto_resize (clist, NULL, i, clist->column[i].width);
2202 }
2203
2204 static void
2205 cell_size_request (GtkCMCList       *clist,
2206                    GtkCMCListRow    *clist_row,
2207                    gint            column,
2208                    GtkRequisition *requisition)
2209 {
2210   GtkCMCTree *ctree;
2211   gint width;
2212   gint height;
2213   PangoLayout *layout;
2214   PangoRectangle logical_rect;
2215
2216   cm_return_if_fail (GTK_IS_CMCTREE (clist));
2217   cm_return_if_fail (requisition != NULL);
2218
2219   ctree = GTK_CMCTREE (clist);
2220
2221   layout = create_cell_layout (clist, clist_row, column);
2222   if (layout)
2223     {
2224       pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
2225
2226       requisition->width = logical_rect.width;
2227       requisition->height = logical_rect.height;
2228       
2229       g_object_unref (G_OBJECT (layout));
2230     }
2231   else
2232     {
2233       requisition->width  = 0;
2234       requisition->height = 0;
2235     }
2236
2237   switch (clist_row->cell[column].type)
2238     {
2239     case GTK_CMCELL_PIXTEXT:
2240       if (GTK_CMCELL_PIXTEXT (clist_row->cell[column])->pixbuf)
2241         {
2242           width = gdk_pixbuf_get_width(GTK_CMCELL_PIXTEXT (clist_row->cell[column])->pixbuf);
2243           height = gdk_pixbuf_get_height(GTK_CMCELL_PIXTEXT (clist_row->cell[column])->pixbuf);
2244           width += GTK_CMCELL_PIXTEXT (clist_row->cell[column])->spacing;
2245         }
2246       else
2247         width = height = 0;
2248           
2249       requisition->width += width;
2250       requisition->height = MAX (requisition->height, height);
2251       
2252       if (column == ctree->tree_column)
2253         {
2254           requisition->width += (ctree->tree_spacing + ctree->tree_indent *
2255                                  (((GtkCMCTreeRow *) clist_row)->level - 1));
2256           switch (ctree->expander_style)
2257             {
2258             case GTK_CMCTREE_EXPANDER_NONE:
2259               break;
2260             case GTK_CMCTREE_EXPANDER_TRIANGLE:
2261               requisition->width += PM_SIZE + 3;
2262               break;
2263             }
2264         }
2265       break;
2266     case GTK_CMCELL_PIXBUF:
2267       width = gdk_pixbuf_get_width(GTK_CMCELL_PIXBUF (clist_row->cell[column])->pixbuf);
2268       height = gdk_pixbuf_get_height(GTK_CMCELL_PIXBUF (clist_row->cell[column])->pixbuf);
2269       requisition->width += width;
2270       requisition->height = MAX (requisition->height, height);
2271       break;
2272     default:
2273       break;
2274     }
2275
2276   requisition->width  += clist_row->cell[column].horizontal;
2277   requisition->height += clist_row->cell[column].vertical;
2278 }
2279
2280 static void
2281 set_cell_contents (GtkCMCList    *clist,
2282                    GtkCMCListRow *clist_row,
2283                    gint         column,
2284                    GtkCMCellType  type,
2285                    const gchar *text,
2286                    guint8       spacing,
2287                    GdkPixbuf   *pixbuf)
2288 {
2289   gboolean visible = FALSE;
2290   GtkCMCTree *ctree;
2291   GtkRequisition requisition;
2292   gchar *old_text = NULL;
2293   GdkPixbuf *old_pixbuf = NULL;
2294
2295   cm_return_if_fail (GTK_IS_CMCTREE (clist));
2296   cm_return_if_fail (clist_row != NULL);
2297
2298   ctree = GTK_CMCTREE (clist);
2299
2300   if (clist->column[column].auto_resize &&
2301       !GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist))
2302     {
2303       GtkCMCTreeNode *parent;
2304
2305       parent = ((GtkCMCTreeRow *)clist_row)->parent;
2306       if ((parent && GTK_CMCTREE_ROW (parent)->expanded &&
2307                       gtk_cmctree_is_viewable (ctree, parent)))
2308         {
2309           visible = TRUE;
2310           GTK_CMCLIST_GET_CLASS (clist)->cell_size_request (clist, clist_row,
2311                                                          column, &requisition);
2312         }
2313     }
2314
2315   switch (clist_row->cell[column].type)
2316     {
2317     case GTK_CMCELL_EMPTY:
2318       break;
2319     case GTK_CMCELL_TEXT:
2320       old_text = GTK_CMCELL_TEXT (clist_row->cell[column])->text;
2321       break;
2322     case GTK_CMCELL_PIXBUF:
2323       old_pixbuf = GTK_CMCELL_PIXBUF (clist_row->cell[column])->pixbuf;
2324       break;
2325     case GTK_CMCELL_PIXTEXT:
2326       old_text = GTK_CMCELL_PIXTEXT (clist_row->cell[column])->text;
2327       old_pixbuf = GTK_CMCELL_PIXTEXT (clist_row->cell[column])->pixbuf;
2328       break;
2329     case GTK_CMCELL_WIDGET:
2330       /* unimplemented */
2331       break;
2332       
2333     default:
2334       break;
2335     }
2336
2337   clist_row->cell[column].type = GTK_CMCELL_EMPTY;
2338   if (column == ctree->tree_column && type != GTK_CMCELL_EMPTY)
2339     type = GTK_CMCELL_PIXTEXT;
2340
2341   /* Note that pixbuf and mask were already ref'ed by the caller
2342    */
2343   switch (type)
2344     {
2345     case GTK_CMCELL_TEXT:
2346       if (text)
2347         {
2348           clist_row->cell[column].type = GTK_CMCELL_TEXT;
2349           GTK_CMCELL_TEXT (clist_row->cell[column])->text = g_strdup (text);
2350         }
2351       break;
2352     case GTK_CMCELL_PIXBUF:
2353       if (pixbuf)
2354         {
2355           clist_row->cell[column].type = GTK_CMCELL_PIXBUF;
2356           GTK_CMCELL_PIXBUF (clist_row->cell[column])->pixbuf = pixbuf;
2357         }
2358       break;
2359     case GTK_CMCELL_PIXTEXT:
2360       if (column == ctree->tree_column)
2361         {
2362           clist_row->cell[column].type = GTK_CMCELL_PIXTEXT;
2363           GTK_CMCELL_PIXTEXT (clist_row->cell[column])->spacing = spacing;
2364           if (text)
2365             GTK_CMCELL_PIXTEXT (clist_row->cell[column])->text = g_strdup (text);
2366           else
2367             GTK_CMCELL_PIXTEXT (clist_row->cell[column])->text = NULL;
2368           if (pixbuf)
2369             {
2370               GTK_CMCELL_PIXTEXT (clist_row->cell[column])->pixbuf = pixbuf;
2371             }
2372           else
2373             {
2374               GTK_CMCELL_PIXTEXT (clist_row->cell[column])->pixbuf = NULL;
2375             }
2376         }
2377       else if (text && pixbuf)
2378         {
2379           clist_row->cell[column].type = GTK_CMCELL_PIXTEXT;
2380           GTK_CMCELL_PIXTEXT (clist_row->cell[column])->text = g_strdup (text);
2381           GTK_CMCELL_PIXTEXT (clist_row->cell[column])->spacing = spacing;
2382           GTK_CMCELL_PIXTEXT (clist_row->cell[column])->pixbuf = pixbuf;
2383         }
2384       break;
2385     default:
2386       break;
2387     }
2388   
2389   if (visible && clist->column[column].auto_resize &&
2390       !GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist))
2391     column_auto_resize (clist, clist_row, column, requisition.width);
2392
2393   g_free (old_text);
2394   if (old_pixbuf)
2395     g_object_unref (old_pixbuf);
2396 }
2397
2398 static void 
2399 set_node_info (GtkCMCTree     *ctree,
2400                GtkCMCTreeNode *node,
2401                const gchar  *text,
2402                guint8        spacing,
2403                GdkPixbuf    *pixbuf_closed,
2404                GdkPixbuf    *pixbuf_opened,
2405                gboolean      is_leaf,
2406                gboolean      expanded)
2407 {
2408   if (GTK_CMCTREE_ROW (node)->pixbuf_opened)
2409     {
2410       g_object_unref (GTK_CMCTREE_ROW (node)->pixbuf_opened);
2411     }
2412   if (GTK_CMCTREE_ROW (node)->pixbuf_closed)
2413     {
2414       g_object_unref (GTK_CMCTREE_ROW (node)->pixbuf_closed);
2415     }
2416
2417   GTK_CMCTREE_ROW (node)->pixbuf_opened = NULL;
2418   GTK_CMCTREE_ROW (node)->pixbuf_closed = NULL;
2419
2420   if (pixbuf_closed)
2421     {
2422       GTK_CMCTREE_ROW (node)->pixbuf_closed = g_object_ref (pixbuf_closed);
2423     }
2424   if (pixbuf_opened)
2425     {
2426       GTK_CMCTREE_ROW (node)->pixbuf_opened = g_object_ref (pixbuf_opened);
2427     }
2428
2429   GTK_CMCTREE_ROW (node)->is_leaf  = is_leaf;
2430   GTK_CMCTREE_ROW (node)->expanded = (is_leaf) ? FALSE : expanded;
2431
2432   if (GTK_CMCTREE_ROW (node)->expanded)
2433     gtk_cmctree_node_set_pixtext (ctree, node, ctree->tree_column,
2434                                 text, spacing, pixbuf_opened);
2435   else 
2436     gtk_cmctree_node_set_pixtext (ctree, node, ctree->tree_column,
2437                                 text, spacing, pixbuf_closed);
2438 }
2439
2440 static void
2441 tree_delete (GtkCMCTree     *ctree, 
2442              GtkCMCTreeNode *node, 
2443              gpointer      data)
2444 {
2445   tree_unselect (ctree,  node, NULL);
2446   row_delete (ctree, GTK_CMCTREE_ROW (node));
2447   g_list_free_1 ((GList *)node);
2448 }
2449
2450 static void
2451 tree_delete_row (GtkCMCTree     *ctree, 
2452                  GtkCMCTreeNode *node, 
2453                  gpointer      data)
2454 {
2455   row_delete (ctree, GTK_CMCTREE_ROW (node));
2456   g_list_free_1 ((GList *)node);
2457 }
2458
2459 static void
2460 tree_update_level (GtkCMCTree     *ctree, 
2461                    GtkCMCTreeNode *node, 
2462                    gpointer      data)
2463 {
2464   if (!node)
2465     return;
2466
2467   if (GTK_CMCTREE_ROW (node)->parent)
2468       GTK_CMCTREE_ROW (node)->level = 
2469         GTK_CMCTREE_ROW (GTK_CMCTREE_ROW (node)->parent)->level + 1;
2470   else
2471       GTK_CMCTREE_ROW (node)->level = 1;
2472 }
2473
2474 static void
2475 tree_select (GtkCMCTree     *ctree, 
2476              GtkCMCTreeNode *node, 
2477              gpointer      data)
2478 {
2479   if (node && GTK_CMCTREE_ROW (node)->row.state != GTK_STATE_SELECTED &&
2480       GTK_CMCTREE_ROW (node)->row.selectable)
2481     g_signal_emit (G_OBJECT (ctree), ctree_signals[TREE_SELECT_ROW], 0,
2482                      node, -1);
2483 }
2484
2485 static void
2486 tree_unselect (GtkCMCTree     *ctree, 
2487                GtkCMCTreeNode *node, 
2488                gpointer      data)
2489 {
2490   if (node && GTK_CMCTREE_ROW (node)->row.state == GTK_STATE_SELECTED)
2491     g_signal_emit (G_OBJECT (ctree), ctree_signals[TREE_UNSELECT_ROW], 0,
2492                      node, -1);
2493 }
2494
2495 static void
2496 tree_expand (GtkCMCTree     *ctree, 
2497              GtkCMCTreeNode *node, 
2498              gpointer      data)
2499 {
2500   if (node && !GTK_CMCTREE_ROW (node)->expanded)
2501     g_signal_emit (G_OBJECT (ctree), ctree_signals[TREE_EXPAND], 0,node);
2502 }
2503
2504 static void
2505 tree_collapse (GtkCMCTree     *ctree, 
2506                GtkCMCTreeNode *node, 
2507                gpointer      data)
2508 {
2509   if (node && GTK_CMCTREE_ROW (node)->expanded)
2510     g_signal_emit (G_OBJECT (ctree), ctree_signals[TREE_COLLAPSE], 0,node);
2511 }
2512
2513 static void
2514 tree_collapse_to_depth (GtkCMCTree     *ctree, 
2515                         GtkCMCTreeNode *node, 
2516                         gint          depth)
2517 {
2518   if (node && GTK_CMCTREE_ROW (node)->level == depth)
2519     gtk_cmctree_collapse_recursive (ctree, node);
2520 }
2521
2522 static void
2523 tree_toggle_expansion (GtkCMCTree     *ctree,
2524                        GtkCMCTreeNode *node,
2525                        gpointer      data)
2526 {
2527   if (!node)
2528     return;
2529
2530   if (GTK_CMCTREE_ROW (node)->expanded)
2531     g_signal_emit (G_OBJECT (ctree), ctree_signals[TREE_COLLAPSE], 0,node);
2532   else
2533     g_signal_emit (G_OBJECT (ctree), ctree_signals[TREE_EXPAND], 0,node);
2534 }
2535
2536 static GtkCMCTreeRow *
2537 row_new (GtkCMCTree *ctree)
2538 {
2539   GtkCMCList *clist;
2540   GtkCMCTreeRow *ctree_row;
2541   int i;
2542
2543   clist = GTK_CMCLIST (ctree);
2544   ctree_row = g_slice_new (GtkCMCTreeRow);
2545   ctree_row->row.cell = g_slice_alloc (sizeof (GtkCMCell) * clist->columns);
2546
2547   for (i = 0; i < clist->columns; i++)
2548     {
2549       ctree_row->row.cell[i].type = GTK_CMCELL_EMPTY;
2550       ctree_row->row.cell[i].vertical = 0;
2551       ctree_row->row.cell[i].horizontal = 0;
2552       ctree_row->row.cell[i].style = NULL;
2553     }
2554   GTK_CMCELL_PIXTEXT (ctree_row->row.cell[ctree->tree_column])->text = NULL;
2555
2556   ctree_row->row.fg_set     = FALSE;
2557   ctree_row->row.bg_set     = FALSE;
2558   ctree_row->row.style      = NULL;
2559   ctree_row->row.selectable = TRUE;
2560   ctree_row->row.state      = GTK_STATE_NORMAL;
2561   ctree_row->row.data       = NULL;
2562   ctree_row->row.destroy    = NULL;
2563
2564   ctree_row->level         = 0;
2565   ctree_row->expanded      = FALSE;
2566   ctree_row->parent        = NULL;
2567   ctree_row->sibling       = NULL;
2568   ctree_row->children      = NULL;
2569   ctree_row->pixbuf_closed = NULL;
2570   ctree_row->pixbuf_opened = NULL;
2571   
2572   return ctree_row;
2573 }
2574
2575 static void
2576 row_delete (GtkCMCTree    *ctree,
2577             GtkCMCTreeRow *ctree_row)
2578 {
2579   GtkCMCList *clist;
2580   gint i;
2581
2582   clist = GTK_CMCLIST (ctree);
2583
2584   for (i = 0; i < clist->columns; i++)
2585     {
2586       GTK_CMCLIST_GET_CLASS (clist)->set_cell_contents
2587         (clist, &(ctree_row->row), i, GTK_CMCELL_EMPTY, NULL, 0, NULL);
2588       if (ctree_row->row.cell[i].style)
2589         {
2590           if (gtk_widget_get_realized (GTK_WIDGET(ctree)))
2591             gtk_style_detach (ctree_row->row.cell[i].style);
2592           g_object_unref (ctree_row->row.cell[i].style);
2593         }
2594     }
2595
2596   if (ctree_row->row.style)
2597     {
2598       if (gtk_widget_get_realized (GTK_WIDGET(ctree)))
2599         gtk_style_detach (ctree_row->row.style);
2600       g_object_unref (ctree_row->row.style);
2601     }
2602
2603   if (ctree_row->pixbuf_closed)
2604     {
2605       g_object_unref (ctree_row->pixbuf_closed);
2606     }
2607
2608   if (ctree_row->pixbuf_opened)
2609     {
2610       g_object_unref (ctree_row->pixbuf_opened);
2611     }
2612
2613   if (ctree_row->row.destroy)
2614     {
2615       GDestroyNotify dnotify = ctree_row->row.destroy;
2616       gpointer ddata = ctree_row->row.data;
2617
2618       ctree_row->row.destroy = NULL;
2619       ctree_row->row.data = NULL;
2620
2621       dnotify (ddata);
2622     }
2623
2624   g_slice_free1 (sizeof (GtkCMCell) * clist->columns, ctree_row->row.cell);
2625   g_slice_free (GtkCMCTreeRow, ctree_row);
2626 }
2627
2628 static void
2629 real_select_row (GtkCMCList *clist,
2630                  gint      row,
2631                  gint      column,
2632                  GdkEvent *event)
2633 {
2634   GList *node;
2635
2636   cm_return_if_fail (GTK_IS_CMCTREE (clist));
2637   
2638   if ((node = g_list_nth (clist->row_list, row)) &&
2639       GTK_CMCTREE_ROW (node)->row.selectable)
2640     g_signal_emit (G_OBJECT (clist), ctree_signals[TREE_SELECT_ROW],0,
2641                      node, column);
2642 }
2643
2644 static void
2645 real_unselect_row (GtkCMCList *clist,
2646                    gint      row,
2647                    gint      column,
2648                    GdkEvent *event)
2649 {
2650   GList *node;
2651
2652   cm_return_if_fail (GTK_IS_CMCTREE (clist));
2653
2654   if ((node = g_list_nth (clist->row_list, row)))
2655     g_signal_emit (G_OBJECT (clist), ctree_signals[TREE_UNSELECT_ROW],0,
2656                      node, column);
2657 }
2658
2659 static void
2660 tree_draw_node (GtkCMCTree     *ctree, 
2661                GtkCMCTreeNode *node)
2662 {
2663   GtkCMCList *clist;
2664   
2665   clist = GTK_CMCLIST (ctree);
2666
2667   if (CLIST_UNFROZEN (clist) && gtk_cmctree_is_viewable (ctree, node))
2668     {
2669       GtkCMCTreeNode *work;
2670       gint num = 0;
2671
2672       work = GTK_CMCTREE_NODE (clist->row_list);
2673       while (work && work != node)
2674         {
2675           work = GTK_CMCTREE_NODE_NEXT (work);
2676           num++;
2677         }
2678       if (work && gtk_cmclist_row_is_visible (clist, num) != GTK_VISIBILITY_NONE)
2679         GTK_CMCLIST_GET_CLASS(ctree)->draw_row
2680           (clist, NULL, num, GTK_CMCLIST_ROW ((GList *) node));
2681     }
2682 }
2683
2684 static void
2685 real_tree_select (GtkCMCTree     *ctree,
2686                   GtkCMCTreeNode *node,
2687                   gint          column)
2688 {
2689   GtkCMCList *clist;
2690   GList *list;
2691   GtkCMCTreeNode *sel_row;
2692   gboolean node_selected;
2693
2694   cm_return_if_fail (GTK_IS_CMCTREE (ctree));
2695
2696   if (!node || GTK_CMCTREE_ROW (node)->row.state == GTK_STATE_SELECTED ||
2697       !GTK_CMCTREE_ROW (node)->row.selectable)
2698     return;
2699
2700   clist = GTK_CMCLIST (ctree);
2701
2702   switch (clist->selection_mode)
2703     {
2704     case GTK_SELECTION_SINGLE:
2705     case GTK_SELECTION_BROWSE:
2706
2707       node_selected = FALSE;
2708       list = clist->selection;
2709
2710       while (list)
2711         {
2712           sel_row = list->data;
2713           list = list->next;
2714           
2715           if (node == sel_row)
2716             node_selected = TRUE;
2717           else
2718             g_signal_emit (G_OBJECT (ctree),
2719                              ctree_signals[TREE_UNSELECT_ROW], 0, sel_row, column);
2720         }
2721
2722       if (node_selected)
2723         return;
2724
2725     default:
2726       break;
2727     }
2728
2729   GTK_CMCTREE_ROW (node)->row.state = GTK_STATE_SELECTED;
2730
2731   if (!clist->selection)
2732     {
2733       clist->selection = g_list_append (clist->selection, node);
2734       clist->selection_end = clist->selection;
2735     }
2736   else
2737     clist->selection_end = g_list_append (clist->selection_end, node)->next;
2738
2739   tree_draw_node (ctree, node);
2740 }
2741
2742 static void
2743 real_tree_unselect (GtkCMCTree     *ctree,
2744                     GtkCMCTreeNode *node,
2745                     gint          column)
2746 {
2747   GtkCMCList *clist;
2748
2749   cm_return_if_fail (GTK_IS_CMCTREE (ctree));
2750
2751   if (!node || GTK_CMCTREE_ROW (node)->row.state != GTK_STATE_SELECTED)
2752     return;
2753
2754   clist = GTK_CMCLIST (ctree);
2755
2756   if (clist->selection_end && clist->selection_end->data == node)
2757     clist->selection_end = clist->selection_end->prev;
2758
2759   clist->selection = g_list_remove (clist->selection, node);
2760   
2761   GTK_CMCTREE_ROW (node)->row.state = GTK_STATE_NORMAL;
2762
2763   tree_draw_node (ctree, node);
2764 }
2765
2766 static void
2767 select_row_recursive (GtkCMCTree     *ctree, 
2768                       GtkCMCTreeNode *node, 
2769                       gpointer      data)
2770 {
2771   if (!node || GTK_CMCTREE_ROW (node)->row.state == GTK_STATE_SELECTED ||
2772       !GTK_CMCTREE_ROW (node)->row.selectable)
2773     return;
2774
2775   GTK_CMCLIST (ctree)->undo_unselection = 
2776     g_list_prepend (GTK_CMCLIST (ctree)->undo_unselection, node);
2777   gtk_cmctree_select (ctree, node);
2778 }
2779
2780 static void
2781 real_select_all (GtkCMCList *clist)
2782 {
2783   GtkCMCTree *ctree;
2784   GtkCMCTreeNode *node;
2785   
2786   cm_return_if_fail (GTK_IS_CMCTREE (clist));
2787
2788   ctree = GTK_CMCTREE (clist);
2789
2790   switch (clist->selection_mode)
2791     {
2792     case GTK_SELECTION_SINGLE:
2793     case GTK_SELECTION_BROWSE:
2794       return;
2795
2796     case GTK_SELECTION_MULTIPLE:
2797
2798       gtk_cmclist_freeze (clist);
2799
2800       g_list_free (clist->undo_selection);
2801       g_list_free (clist->undo_unselection);
2802       clist->undo_selection = NULL;
2803       clist->undo_unselection = NULL;
2804           
2805       clist->anchor_state = GTK_STATE_SELECTED;
2806       clist->anchor = -1;
2807       clist->drag_pos = -1;
2808       clist->undo_anchor = clist->focus_row;
2809
2810       for (node = GTK_CMCTREE_NODE (clist->row_list); node;
2811            node = GTK_CMCTREE_NODE_NEXT (node))
2812         gtk_cmctree_pre_recursive (ctree, node, select_row_recursive, NULL);
2813
2814       gtk_cmclist_thaw (clist);
2815       break;
2816
2817     default:
2818       /* do nothing */
2819       break;
2820     }
2821 }
2822
2823 static void
2824 real_unselect_all (GtkCMCList *clist)
2825 {
2826   GtkCMCTree *ctree;
2827   GtkCMCTreeNode *node;
2828   GList *list;
2829  
2830   cm_return_if_fail (GTK_IS_CMCTREE (clist));
2831   
2832   ctree = GTK_CMCTREE (clist);
2833
2834   switch (clist->selection_mode)
2835     {
2836     case GTK_SELECTION_BROWSE:
2837       if (clist->focus_row >= 0)
2838         {
2839           gtk_cmctree_select
2840             (ctree,
2841              GTK_CMCTREE_NODE (g_list_nth (clist->row_list, clist->focus_row)));
2842           return;
2843         }
2844       break;
2845
2846     case GTK_SELECTION_MULTIPLE:
2847       g_list_free (clist->undo_selection);
2848       g_list_free (clist->undo_unselection);
2849       clist->undo_selection = NULL;
2850       clist->undo_unselection = NULL;
2851
2852       clist->anchor = -1;
2853       clist->drag_pos = -1;
2854       clist->undo_anchor = clist->focus_row;
2855       break;
2856
2857     default:
2858       break;
2859     }
2860
2861   list = clist->selection;
2862
2863   while (list)
2864     {
2865       node = list->data;
2866       list = list->next;
2867       gtk_cmctree_unselect (ctree, node);
2868     }
2869 }
2870
2871 static gboolean
2872 ctree_is_hot_spot (GtkCMCTree     *ctree, 
2873                    GtkCMCTreeNode *node,
2874                    gint          row, 
2875                    gint          x, 
2876                    gint          y)
2877 {
2878   GtkCMCTreeRow *tree_row;
2879   GtkCMCList *clist;
2880   gint xl;
2881   gint yu;
2882   gint hotspot_size;
2883
2884   cm_return_val_if_fail (GTK_IS_CMCTREE (ctree), FALSE);
2885   cm_return_val_if_fail (node != NULL, FALSE);
2886
2887   clist = GTK_CMCLIST (ctree);
2888
2889   if (!clist->column[ctree->tree_column].visible ||
2890       ctree->expander_style == GTK_CMCTREE_EXPANDER_NONE)
2891     return FALSE;
2892
2893   tree_row = GTK_CMCTREE_ROW (node);
2894
2895   hotspot_size = clist->row_height-2;
2896   if (hotspot_size > clist->column[ctree->tree_column].area.width - 2)
2897         hotspot_size = clist->column[ctree->tree_column].area.width - 2;
2898
2899   yu = (ROW_TOP_YPIXEL (clist, row) + (clist->row_height - hotspot_size) / 2 -
2900         (clist->row_height - 1) % 2);
2901
2902   if (clist->column[ctree->tree_column].justification == GTK_JUSTIFY_RIGHT)
2903     xl = (clist->column[ctree->tree_column].area.x + 
2904           clist->column[ctree->tree_column].area.width - 1 + clist->hoffset -
2905           (tree_row->level - 1) * ctree->tree_indent - hotspot_size);
2906   else
2907     xl = (clist->column[ctree->tree_column].area.x + clist->hoffset +
2908           (tree_row->level - 1) * ctree->tree_indent);
2909
2910   return (x >= xl && x <= xl + hotspot_size && y >= yu && y <= yu + hotspot_size);
2911 }
2912
2913 /***********************************************************
2914  ***********************************************************
2915  ***                  Public interface                   ***
2916  ***********************************************************
2917  ***********************************************************/
2918
2919
2920 /***********************************************************
2921  *           Creation, insertion, deletion                 *
2922  ***********************************************************/
2923
2924 static GObject*
2925 gtk_cmctree_constructor (GType                  type,
2926                        guint                  n_construct_properties,
2927                        GObjectConstructParam *construct_properties)
2928 {
2929   GObject *object = G_OBJECT_CLASS (parent_class)->constructor (type,
2930                                                                 n_construct_properties,
2931                                                                 construct_properties);
2932
2933   return object;
2934 }
2935
2936 GtkWidget*
2937 gtk_cmctree_new_with_titles (gint         columns, 
2938                            gint         tree_column,
2939                            gchar       *titles[])
2940 {
2941   GtkWidget *widget;
2942
2943   cm_return_val_if_fail (columns > 0, NULL);
2944   cm_return_val_if_fail (tree_column >= 0 && tree_column < columns, NULL);
2945
2946   widget = gtk_widget_new (GTK_TYPE_CMCTREE,
2947                            "n_columns", columns,
2948                            "tree_column", tree_column,
2949                            NULL);
2950   if (titles)
2951     {
2952       GtkCMCList *clist = GTK_CMCLIST (widget);
2953       guint i;
2954
2955       for (i = 0; i < columns; i++)
2956         gtk_cmclist_set_column_title (clist, i, titles[i]);
2957       gtk_cmclist_column_titles_show (clist);
2958     }
2959
2960   return widget;
2961 }
2962
2963 GtkWidget *
2964 gtk_cmctree_new (gint columns, 
2965                gint tree_column)
2966 {
2967   return gtk_cmctree_new_with_titles (columns, tree_column, NULL);
2968 }
2969
2970 static gint
2971 real_insert_row (GtkCMCList *clist,
2972                  gint      row,
2973                  gchar    *text[])
2974 {
2975   GtkCMCTreeNode *parent = NULL;
2976   GtkCMCTreeNode *sibling;
2977   GtkCMCTreeNode *node;
2978
2979   cm_return_val_if_fail (GTK_IS_CMCTREE (clist), -1);
2980
2981   sibling = GTK_CMCTREE_NODE (g_list_nth (clist->row_list, row));
2982   if (sibling)
2983     parent = GTK_CMCTREE_ROW (sibling)->parent;
2984
2985   node = gtk_cmctree_insert_node (GTK_CMCTREE (clist), parent, sibling, text, 5,
2986                                 NULL, NULL, TRUE, FALSE);
2987
2988   if (GTK_CMCLIST_AUTO_SORT (clist) || !sibling)
2989     return g_list_position (clist->row_list, (GList *) node);
2990   
2991   return row;
2992 }
2993
2994 GtkCMCTreeNode * 
2995 gtk_cmctree_insert_node (GtkCMCTree     *ctree,
2996                        GtkCMCTreeNode *parent, 
2997                        GtkCMCTreeNode *sibling,
2998                        gchar        *text[],
2999                        guint8        spacing,
3000                        GdkPixbuf    *pixbuf_closed,
3001                        GdkPixbuf    *pixbuf_opened,
3002                        gboolean      is_leaf,
3003                        gboolean      expanded)
3004 {
3005   GtkCMCList *clist;
3006   GtkCMCTreeRow *new_row;
3007   GtkCMCTreeNode *node;
3008   GList *list;
3009   gint i;
3010
3011   cm_return_val_if_fail (GTK_IS_CMCTREE (ctree), NULL);
3012   if (sibling)
3013     cm_return_val_if_fail (GTK_CMCTREE_ROW (sibling)->parent == parent, NULL);
3014
3015   if (parent && GTK_CMCTREE_ROW (parent)->is_leaf)
3016     return NULL;
3017
3018   clist = GTK_CMCLIST (ctree);
3019
3020   /* create the row */
3021   new_row = row_new (ctree);
3022   list = g_list_alloc ();
3023   list->data = new_row;
3024   node = GTK_CMCTREE_NODE (list);
3025
3026   if (text)
3027     for (i = 0; i < clist->columns; i++)
3028       if (text[i] && i != ctree->tree_column)
3029         GTK_CMCLIST_GET_CLASS (clist)->set_cell_contents
3030           (clist, &(new_row->row), i, GTK_CMCELL_TEXT, text[i], 0, NULL);
3031
3032   set_node_info (ctree, node, text ?
3033                  text[ctree->tree_column] : NULL, spacing, pixbuf_closed,
3034                  pixbuf_opened, is_leaf, expanded);
3035
3036   /* sorted insertion */
3037   if (GTK_CMCLIST_AUTO_SORT (clist))
3038     {
3039       if (parent)
3040         sibling = GTK_CMCTREE_ROW (parent)->children;
3041       else
3042         sibling = GTK_CMCTREE_NODE (clist->row_list);
3043
3044       while (sibling && clist->compare
3045              (clist, GTK_CMCTREE_ROW (node), GTK_CMCTREE_ROW (sibling)) > 0)
3046         sibling = GTK_CMCTREE_ROW (sibling)->sibling;
3047     }
3048
3049   gtk_cmctree_link (ctree, node, parent, sibling, TRUE);
3050
3051   if (text && !GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist) &&
3052       gtk_cmctree_is_viewable (ctree, node))
3053     {
3054       for (i = 0; i < clist->columns; i++)
3055         if (clist->column[i].auto_resize)
3056           column_auto_resize (clist, &(new_row->row), i, 0);
3057     }
3058
3059   if (clist->rows == 1)
3060     {
3061       clist->focus_row = 0;
3062       if (clist->selection_mode == GTK_SELECTION_BROWSE)
3063         gtk_cmctree_select (ctree, node);
3064     }
3065
3066
3067   CLIST_REFRESH (clist);
3068
3069   return node;
3070 }
3071
3072 GtkCMCTreeNode *
3073 gtk_cmctree_insert_gnode (GtkCMCTree          *ctree,
3074                         GtkCMCTreeNode      *parent,
3075                         GtkCMCTreeNode      *sibling,
3076                         GNode             *gnode,
3077                         GtkCMCTreeGNodeFunc  func,
3078                         gpointer           data)
3079 {
3080   GtkCMCList *clist;
3081   GtkCMCTreeNode *cnode = NULL;
3082   GtkCMCTreeNode *child = NULL;
3083   GtkCMCTreeNode *new_child;
3084   GList *list;
3085   GNode *work;
3086   guint depth = 1;
3087
3088   cm_return_val_if_fail (GTK_IS_CMCTREE (ctree), NULL);
3089   cm_return_val_if_fail (gnode != NULL, NULL);
3090   cm_return_val_if_fail (func != NULL, NULL);
3091   if (sibling)
3092     cm_return_val_if_fail (GTK_CMCTREE_ROW (sibling)->parent == parent, NULL);
3093   
3094   clist = GTK_CMCLIST (ctree);
3095
3096   if (parent)
3097     depth = GTK_CMCTREE_ROW (parent)->level + 1;
3098
3099   list = g_list_alloc ();
3100   list->data = row_new (ctree);
3101   cnode = GTK_CMCTREE_NODE (list);
3102
3103   gtk_cmclist_freeze (clist);
3104
3105   set_node_info (ctree, cnode, "", 0, NULL, NULL, TRUE, FALSE);
3106
3107   if (!func (ctree, depth, gnode, cnode, data))
3108     {
3109       tree_delete_row (ctree, cnode, NULL);
3110       gtk_cmclist_thaw (clist);
3111       return NULL;
3112     }
3113
3114   if (GTK_CMCLIST_AUTO_SORT (clist))
3115     {
3116       if (parent)
3117         sibling = GTK_CMCTREE_ROW (parent)->children;
3118       else
3119         sibling = GTK_CMCTREE_NODE (clist->row_list);
3120
3121       while (sibling && clist->compare
3122              (clist, GTK_CMCTREE_ROW (cnode), GTK_CMCTREE_ROW (sibling)) > 0)
3123         sibling = GTK_CMCTREE_ROW (sibling)->sibling;
3124     }
3125
3126   gtk_cmctree_link (ctree, cnode, parent, sibling, TRUE);
3127
3128   for (work = g_node_last_child (gnode); work; work = work->prev)
3129     {
3130       new_child = gtk_cmctree_insert_gnode (ctree, cnode, child,
3131                                           work, func, data);
3132       if (new_child)
3133         child = new_child;
3134     }   
3135   
3136   gtk_cmclist_thaw (clist);
3137
3138   return cnode;
3139 }
3140
3141 GNode *
3142 gtk_cmctree_export_to_gnode (GtkCMCTree          *ctree,
3143                            GNode             *parent,
3144                            GNode             *sibling,
3145                            GtkCMCTreeNode      *node,
3146                            GtkCMCTreeGNodeFunc  func,
3147                            gpointer           data)
3148 {
3149   GtkCMCTreeNode *work;
3150   GNode *gnode;
3151   gint depth;
3152
3153   cm_return_val_if_fail (GTK_IS_CMCTREE (ctree), NULL);
3154   cm_return_val_if_fail (node != NULL, NULL);
3155   cm_return_val_if_fail (func != NULL, NULL);
3156   if (sibling)
3157     {
3158       cm_return_val_if_fail (parent != NULL, NULL);
3159       cm_return_val_if_fail (sibling->parent == parent, NULL);
3160     }
3161
3162   gnode = g_node_new (NULL);
3163   depth = g_node_depth (parent) + 1;
3164   
3165   if (!func (ctree, depth, gnode, node, data))
3166     {
3167       g_node_destroy (gnode);
3168       return NULL;
3169     }
3170
3171   if (parent)
3172     g_node_insert_before (parent, sibling, gnode);
3173
3174   if (!GTK_CMCTREE_ROW (node)->is_leaf)
3175     {
3176       GNode *new_sibling = NULL;
3177
3178       for (work = GTK_CMCTREE_ROW (node)->children; work;
3179            work = GTK_CMCTREE_ROW (work)->sibling)
3180         new_sibling = gtk_cmctree_export_to_gnode (ctree, gnode, new_sibling,
3181                                                  work, func, data);
3182
3183       g_node_reverse_children (gnode);
3184     }
3185
3186   return gnode;
3187 }
3188   
3189 static void
3190 real_remove_row (GtkCMCList *clist,
3191                  gint      row)
3192 {
3193   GtkCMCTreeNode *node;
3194
3195   cm_return_if_fail (GTK_IS_CMCTREE (clist));
3196
3197   node = GTK_CMCTREE_NODE (g_list_nth (clist->row_list, row));
3198
3199   if (node)
3200     gtk_cmctree_remove_node (GTK_CMCTREE (clist), node);
3201 }
3202
3203 void
3204 gtk_cmctree_remove_node (GtkCMCTree     *ctree, 
3205                        GtkCMCTreeNode *node)
3206 {
3207   GtkCMCList *clist;
3208
3209   cm_return_if_fail (GTK_IS_CMCTREE (ctree));
3210
3211   clist = GTK_CMCLIST (ctree);
3212
3213   gtk_cmclist_freeze (clist);
3214
3215   if (node)
3216     {
3217       gtk_cmctree_unlink (ctree, node, TRUE);
3218       gtk_cmctree_post_recursive (ctree, node, GTK_CMCTREE_FUNC (tree_delete),
3219                                 NULL);
3220       if (clist->selection_mode == GTK_SELECTION_BROWSE && !clist->selection &&
3221           clist->focus_row >= 0)
3222         gtk_cmclist_select_row (clist, clist->focus_row, -1);
3223
3224       auto_resize_columns (clist);
3225     }
3226   else
3227     gtk_cmclist_clear (clist);
3228
3229   gtk_cmclist_thaw (clist);
3230 }
3231
3232 static void
3233 real_clear (GtkCMCList *clist)
3234 {
3235   GtkCMCTree *ctree;
3236   GtkCMCTreeNode *work;
3237   GtkCMCTreeNode *ptr;
3238
3239   cm_return_if_fail (GTK_IS_CMCTREE (clist));
3240
3241   ctree = GTK_CMCTREE (clist);
3242
3243   /* remove all rows */
3244   work = GTK_CMCTREE_NODE (clist->row_list);
3245   clist->row_list = NULL;
3246   clist->row_list_end = NULL;
3247
3248   GTK_CMCLIST_SET_FLAG (clist, CMCLIST_AUTO_RESIZE_BLOCKED);
3249   while (work)
3250     {
3251       ptr = work;
3252       work = GTK_CMCTREE_ROW (work)->sibling;
3253       gtk_cmctree_post_recursive (ctree, ptr, GTK_CMCTREE_FUNC (tree_delete_row), 
3254                                 NULL);
3255     }
3256   GTK_CMCLIST_UNSET_FLAG (clist, CMCLIST_AUTO_RESIZE_BLOCKED);
3257
3258   parent_class->clear (clist);
3259 }
3260
3261
3262 /***********************************************************
3263  *  Generic recursive functions, querying / finding tree   *
3264  *  information                                            *
3265  ***********************************************************/
3266
3267
3268 void
3269 gtk_cmctree_post_recursive (GtkCMCTree     *ctree, 
3270                           GtkCMCTreeNode *node,
3271                           GtkCMCTreeFunc  func,
3272                           gpointer      data)
3273 {
3274   GtkCMCTreeNode *work;
3275   GtkCMCTreeNode *tmp;
3276
3277   cm_return_if_fail (GTK_IS_CMCTREE (ctree));
3278   cm_return_if_fail (func != NULL);
3279
3280   if (node)
3281     work = GTK_CMCTREE_ROW (node)->children;
3282   else
3283     work = GTK_CMCTREE_NODE (GTK_CMCLIST (ctree)->row_list);
3284
3285   while (work)
3286     {
3287       tmp = GTK_CMCTREE_ROW (work)->sibling;
3288       gtk_cmctree_post_recursive (ctree, work, func, data);
3289       work = tmp;
3290     }
3291
3292   if (node)
3293     func (ctree, node, data);
3294 }
3295
3296 void
3297 gtk_cmctree_post_recursive_to_depth (GtkCMCTree     *ctree, 
3298                                    GtkCMCTreeNode *node,
3299                                    gint          depth,
3300                                    GtkCMCTreeFunc  func,
3301                                    gpointer      data)
3302 {
3303   GtkCMCTreeNode *work;
3304   GtkCMCTreeNode *tmp;
3305
3306   cm_return_if_fail (GTK_IS_CMCTREE (ctree));
3307   cm_return_if_fail (func != NULL);
3308
3309   if (depth < 0)
3310     {
3311       gtk_cmctree_post_recursive (ctree, node, func, data);
3312       return;
3313     }
3314
3315   if (node)
3316     work = GTK_CMCTREE_ROW (node)->children;
3317   else
3318     work = GTK_CMCTREE_NODE (GTK_CMCLIST (ctree)->row_list);
3319
3320   if (work && GTK_CMCTREE_ROW (work)->level <= depth)
3321     {
3322       while (work)
3323         {
3324           tmp = GTK_CMCTREE_ROW (work)->sibling;
3325           gtk_cmctree_post_recursive_to_depth (ctree, work, depth, func, data);
3326           work = tmp;
3327         }
3328     }
3329
3330   if (node && GTK_CMCTREE_ROW (node)->level <= depth)
3331     func (ctree, node, data);
3332 }
3333
3334 void
3335 gtk_cmctree_pre_recursive (GtkCMCTree     *ctree, 
3336                          GtkCMCTreeNode *node,
3337                          GtkCMCTreeFunc  func,
3338                          gpointer      data)
3339 {
3340   GtkCMCTreeNode *work;
3341   GtkCMCTreeNode *tmp;
3342
3343   cm_return_if_fail (GTK_IS_CMCTREE (ctree));
3344   cm_return_if_fail (func != NULL);
3345
3346   if (node)
3347     {
3348       work = GTK_CMCTREE_ROW (node)->children;
3349       func (ctree, node, data);
3350     }
3351   else
3352     work = GTK_CMCTREE_NODE (GTK_CMCLIST (ctree)->row_list);
3353
3354   while (work)
3355     {
3356       tmp = GTK_CMCTREE_ROW (work)->sibling;
3357       gtk_cmctree_pre_recursive (ctree, work, func, data);
3358       work = tmp;
3359     }
3360 }
3361
3362 void
3363 gtk_cmctree_pre_recursive_to_depth (GtkCMCTree     *ctree, 
3364                                   GtkCMCTreeNode *node,
3365                                   gint          depth, 
3366                                   GtkCMCTreeFunc  func,
3367                                   gpointer      data)
3368 {
3369   GtkCMCTreeNode *work;
3370   GtkCMCTreeNode *tmp;
3371
3372   cm_return_if_fail (GTK_IS_CMCTREE (ctree));
3373   cm_return_if_fail (func != NULL);
3374
3375   if (depth < 0)
3376     {
3377       gtk_cmctree_pre_recursive (ctree, node, func, data);
3378       return;
3379     }
3380
3381   if (node)
3382     {
3383       work = GTK_CMCTREE_ROW (node)->children;
3384       if (GTK_CMCTREE_ROW (node)->level <= depth)
3385         func (ctree, node, data);
3386     }
3387   else
3388     work = GTK_CMCTREE_NODE (GTK_CMCLIST (ctree)->row_list);
3389
3390   if (work && GTK_CMCTREE_ROW (work)->level <= depth)
3391     {
3392       while (work)
3393         {
3394           tmp = GTK_CMCTREE_ROW (work)->sibling;
3395           gtk_cmctree_pre_recursive_to_depth (ctree, work, depth, func, data);
3396           work = tmp;
3397         }
3398     }
3399 }
3400
3401 gboolean
3402 gtk_cmctree_is_viewable (GtkCMCTree     *ctree, 
3403                        GtkCMCTreeNode *node)
3404
3405   GtkCMCTreeRow *work;
3406
3407   cm_return_val_if_fail (GTK_IS_CMCTREE (ctree), FALSE);
3408   cm_return_val_if_fail (node != NULL, FALSE);
3409
3410   work = GTK_CMCTREE_ROW (node);
3411
3412   while (work && work->parent && GTK_CMCTREE_ROW (work->parent)->expanded)
3413     work = GTK_CMCTREE_ROW (work->parent);
3414
3415   if (!work->parent)
3416     return TRUE;
3417
3418   return FALSE;
3419 }
3420
3421 GtkCMCTreeNode * 
3422 gtk_cmctree_last (GtkCMCTree     *ctree,
3423                 GtkCMCTreeNode *node)
3424 {
3425   cm_return_val_if_fail (GTK_IS_CMCTREE (ctree), NULL);
3426
3427   if (!node) 
3428     return NULL;
3429
3430   while (GTK_CMCTREE_ROW (node)->sibling)
3431     node = GTK_CMCTREE_ROW (node)->sibling;
3432   
3433   if (GTK_CMCTREE_ROW (node)->children)
3434     return gtk_cmctree_last (ctree, GTK_CMCTREE_ROW (node)->children);
3435   
3436   return node;
3437 }
3438
3439 GtkCMCTreeNode *
3440 gtk_cmctree_find_node_ptr (GtkCMCTree    *ctree,
3441                          GtkCMCTreeRow *ctree_row)
3442 {
3443   GtkCMCTreeNode *node;
3444   
3445   cm_return_val_if_fail (GTK_IS_CMCTREE (ctree), NULL);
3446   cm_return_val_if_fail (ctree_row != NULL, NULL);
3447   
3448   if (ctree_row->parent)
3449     node = GTK_CMCTREE_ROW (ctree_row->parent)->children;
3450   else
3451     node = GTK_CMCTREE_NODE (GTK_CMCLIST (ctree)->row_list);
3452
3453   while (GTK_CMCTREE_ROW (node) != ctree_row)
3454     node = GTK_CMCTREE_ROW (node)->sibling;
3455   
3456   return node;
3457 }
3458
3459 GtkCMCTreeNode *
3460 gtk_cmctree_node_nth (GtkCMCTree *ctree,
3461                     guint     row)
3462 {
3463   cm_return_val_if_fail (GTK_IS_CMCTREE (ctree), NULL);
3464
3465   if ((row >= GTK_CMCLIST(ctree)->rows))
3466     return NULL;
3467  
3468   return GTK_CMCTREE_NODE (g_list_nth (GTK_CMCLIST (ctree)->row_list, row));
3469 }
3470
3471 gboolean
3472 gtk_cmctree_find (GtkCMCTree     *ctree,
3473                 GtkCMCTreeNode *node,
3474                 GtkCMCTreeNode *child)
3475 {
3476   if (!child)
3477     return FALSE;
3478
3479   if (!node)
3480     node = GTK_CMCTREE_NODE (GTK_CMCLIST (ctree)->row_list);
3481
3482   while (node)
3483     {
3484       if (node == child) 
3485         return TRUE;
3486       if (GTK_CMCTREE_ROW (node)->children)
3487         {
3488           if (gtk_cmctree_find (ctree, GTK_CMCTREE_ROW (node)->children, child))
3489             return TRUE;
3490         }
3491       node = GTK_CMCTREE_ROW (node)->sibling;
3492     }
3493   return FALSE;
3494 }
3495
3496 gboolean
3497 gtk_cmctree_is_ancestor (GtkCMCTree     *ctree,
3498                        GtkCMCTreeNode *node,
3499                        GtkCMCTreeNode *child)
3500 {
3501   cm_return_val_if_fail (GTK_IS_CMCTREE (ctree), FALSE);
3502   cm_return_val_if_fail (node != NULL, FALSE);
3503
3504   if (GTK_CMCTREE_ROW (node)->children)
3505     return gtk_cmctree_find (ctree, GTK_CMCTREE_ROW (node)->children, child);
3506
3507   return FALSE;
3508 }
3509
3510 GtkCMCTreeNode *
3511 gtk_cmctree_find_by_row_data (GtkCMCTree     *ctree,
3512                             GtkCMCTreeNode *node,
3513                             gpointer      data)
3514 {
3515   GtkCMCTreeNode *work;
3516   
3517   if (!node)
3518     node = GTK_CMCTREE_NODE (GTK_CMCLIST (ctree)->row_list);
3519   
3520   while (node)
3521     {
3522       if (GTK_CMCTREE_ROW (node)->row.data == data) 
3523         return node;
3524       if (GTK_CMCTREE_ROW (node)->children &&
3525           (work = gtk_cmctree_find_by_row_data 
3526            (ctree, GTK_CMCTREE_ROW (node)->children, data)))
3527         return work;
3528       node = GTK_CMCTREE_ROW (node)->sibling;
3529     }
3530   return NULL;
3531 }
3532
3533 GList *
3534 gtk_cmctree_find_all_by_row_data (GtkCMCTree     *ctree,
3535                                 GtkCMCTreeNode *node,
3536                                 gpointer      data)
3537 {
3538   GList *list = NULL;
3539
3540   cm_return_val_if_fail (GTK_IS_CMCTREE (ctree), NULL);
3541
3542   /* if node == NULL then look in the whole tree */
3543   if (!node)
3544     node = GTK_CMCTREE_NODE (GTK_CMCLIST (ctree)->row_list);
3545
3546   while (node)
3547     {
3548       if (GTK_CMCTREE_ROW (node)->row.data == data)
3549         list = g_list_append (list, node);
3550
3551       if (GTK_CMCTREE_ROW (node)->children)
3552         {
3553           GList *sub_list;
3554
3555           sub_list = gtk_cmctree_find_all_by_row_data (ctree,
3556                                                      GTK_CMCTREE_ROW
3557                                                      (node)->children,
3558                                                      data);
3559           list = g_list_concat (list, sub_list);
3560         }
3561       node = GTK_CMCTREE_ROW (node)->sibling;
3562     }
3563   return list;
3564 }
3565
3566 GtkCMCTreeNode *
3567 gtk_cmctree_find_by_row_data_custom (GtkCMCTree     *ctree,
3568                                    GtkCMCTreeNode *node,
3569                                    gpointer      data,
3570                                    GCompareFunc  func)
3571 {
3572   GtkCMCTreeNode *work;
3573
3574   cm_return_val_if_fail (func != NULL, NULL);
3575
3576   if (!node)
3577     node = GTK_CMCTREE_NODE (GTK_CMCLIST (ctree)->row_list);
3578
3579   while (node)
3580     {
3581       if (!func (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_custom
3585            (ctree, GTK_CMCTREE_ROW (node)->children, data, func)))
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_custom (GtkCMCTree     *ctree,
3594                                        GtkCMCTreeNode *node,
3595                                        gpointer      data,
3596                                        GCompareFunc  func)
3597 {
3598   GList *list = NULL;
3599
3600   cm_return_val_if_fail (GTK_IS_CMCTREE (ctree), NULL);
3601   cm_return_val_if_fail (func != NULL, NULL);
3602
3603   /* if node == NULL then look in the whole tree */
3604   if (!node)
3605     node = GTK_CMCTREE_NODE (GTK_CMCLIST (ctree)->row_list);
3606
3607   while (node)
3608     {
3609       if (!func (GTK_CMCTREE_ROW (node)->row.data, data))
3610         list = g_list_append (list, node);
3611
3612       if (GTK_CMCTREE_ROW (node)->children)
3613         {
3614           GList *sub_list;
3615
3616           sub_list = gtk_cmctree_find_all_by_row_data_custom (ctree,
3617                                                             GTK_CMCTREE_ROW
3618                                                             (node)->children,
3619                                                             data,
3620                                                             func);
3621           list = g_list_concat (list, sub_list);
3622         }
3623       node = GTK_CMCTREE_ROW (node)->sibling;
3624     }
3625   return list;
3626 }
3627
3628 gboolean
3629 gtk_cmctree_is_hot_spot (GtkCMCTree *ctree, 
3630                        gint      x, 
3631                        gint      y)
3632 {
3633   GtkCMCTreeNode *node;
3634   gint column;
3635   gint row;
3636   
3637   cm_return_val_if_fail (GTK_IS_CMCTREE (ctree), FALSE);
3638
3639   if (gtk_cmclist_get_selection_info (GTK_CMCLIST (ctree), x, y, &row, &column))
3640     if ((node = GTK_CMCTREE_NODE(g_list_nth (GTK_CMCLIST (ctree)->row_list, row))))
3641       return ctree_is_hot_spot (ctree, node, row, x, y);
3642
3643   return FALSE;
3644 }
3645
3646
3647 /***********************************************************
3648  *   Tree signals : move, expand, collapse, (un)select     *
3649  ***********************************************************/
3650
3651
3652 void
3653 gtk_cmctree_move (GtkCMCTree     *ctree,
3654                 GtkCMCTreeNode *node,
3655                 GtkCMCTreeNode *new_parent, 
3656                 GtkCMCTreeNode *new_sibling)
3657 {
3658   cm_return_if_fail (GTK_IS_CMCTREE (ctree));
3659   cm_return_if_fail (node != NULL);
3660   
3661   g_signal_emit (G_OBJECT (ctree), ctree_signals[TREE_MOVE], 0, node,
3662                    new_parent, new_sibling);
3663 }
3664
3665 void
3666 gtk_cmctree_expand (GtkCMCTree     *ctree,
3667                   GtkCMCTreeNode *node)
3668 {
3669   cm_return_if_fail (GTK_IS_CMCTREE (ctree));
3670   cm_return_if_fail (node != NULL);
3671   
3672   if (GTK_CMCTREE_ROW (node)->is_leaf)
3673     return;
3674
3675   g_signal_emit (G_OBJECT (ctree), ctree_signals[TREE_EXPAND], 0, node);
3676 }
3677
3678 void 
3679 gtk_cmctree_expand_recursive (GtkCMCTree     *ctree,
3680                             GtkCMCTreeNode *node)
3681 {
3682   GtkCMCList *clist;
3683   gboolean thaw = FALSE;
3684
3685   cm_return_if_fail (GTK_IS_CMCTREE (ctree));
3686
3687   clist = GTK_CMCLIST (ctree);
3688
3689   if (node && GTK_CMCTREE_ROW (node)->is_leaf)
3690     return;
3691
3692   if (CLIST_UNFROZEN (clist) && (!node || gtk_cmctree_is_viewable (ctree, node)))
3693     {
3694       gtk_cmclist_freeze (clist);
3695       thaw = TRUE;
3696     }
3697
3698   gtk_cmctree_post_recursive (ctree, node, GTK_CMCTREE_FUNC (tree_expand), NULL);
3699
3700   if (thaw)
3701     gtk_cmclist_thaw (clist);
3702 }
3703
3704 void 
3705 gtk_cmctree_expand_to_depth (GtkCMCTree     *ctree,
3706                            GtkCMCTreeNode *node,
3707                            gint          depth)
3708 {
3709   GtkCMCList *clist;
3710   gboolean thaw = FALSE;
3711
3712   cm_return_if_fail (GTK_IS_CMCTREE (ctree));
3713
3714   clist = GTK_CMCLIST (ctree);
3715
3716   if (node && GTK_CMCTREE_ROW (node)->is_leaf)
3717     return;
3718
3719   if (CLIST_UNFROZEN (clist) && (!node || gtk_cmctree_is_viewable (ctree, node)))
3720     {
3721       gtk_cmclist_freeze (clist);
3722       thaw = TRUE;
3723     }
3724
3725   gtk_cmctree_post_recursive_to_depth (ctree, node, depth,
3726                                      GTK_CMCTREE_FUNC (tree_expand), NULL);
3727
3728   if (thaw)
3729     gtk_cmclist_thaw (clist);
3730 }
3731
3732 void
3733 gtk_cmctree_collapse (GtkCMCTree     *ctree,
3734                     GtkCMCTreeNode *node)
3735 {
3736   cm_return_if_fail (GTK_IS_CMCTREE (ctree));
3737   cm_return_if_fail (node != NULL);
3738   
3739   if (GTK_CMCTREE_ROW (node)->is_leaf)
3740     return;
3741
3742   g_signal_emit (G_OBJECT (ctree), ctree_signals[TREE_COLLAPSE], 0, node);
3743 }
3744
3745 void 
3746 gtk_cmctree_collapse_recursive (GtkCMCTree     *ctree,
3747                               GtkCMCTreeNode *node)
3748 {
3749   GtkCMCList *clist;
3750   gboolean thaw = FALSE;
3751   gint i;
3752
3753   cm_return_if_fail (GTK_IS_CMCTREE (ctree));
3754
3755   if (node && GTK_CMCTREE_ROW (node)->is_leaf)
3756     return;
3757
3758   clist = GTK_CMCLIST (ctree);
3759
3760   if (CLIST_UNFROZEN (clist) && (!node || gtk_cmctree_is_viewable (ctree, node)))
3761     {
3762       gtk_cmclist_freeze (clist);
3763       thaw = TRUE;
3764     }
3765
3766   GTK_CMCLIST_SET_FLAG (clist, CMCLIST_AUTO_RESIZE_BLOCKED);
3767   gtk_cmctree_post_recursive (ctree, node, GTK_CMCTREE_FUNC (tree_collapse), NULL);
3768   GTK_CMCLIST_UNSET_FLAG (clist, CMCLIST_AUTO_RESIZE_BLOCKED);
3769   for (i = 0; i < clist->columns; i++)
3770     if (clist->column[i].auto_resize)
3771       gtk_cmclist_set_column_width (clist, i,
3772                                   gtk_cmclist_optimal_column_width (clist, i));
3773
3774   if (thaw)
3775     gtk_cmclist_thaw (clist);
3776 }
3777
3778 void 
3779 gtk_cmctree_collapse_to_depth (GtkCMCTree     *ctree,
3780                              GtkCMCTreeNode *node,
3781                              gint          depth)
3782 {
3783   GtkCMCList *clist;
3784   gboolean thaw = FALSE;
3785   gint i;
3786
3787   cm_return_if_fail (GTK_IS_CMCTREE (ctree));
3788
3789   if (node && GTK_CMCTREE_ROW (node)->is_leaf)
3790     return;
3791
3792   clist = GTK_CMCLIST (ctree);
3793
3794   if (CLIST_UNFROZEN (clist) && (!node || gtk_cmctree_is_viewable (ctree, node)))
3795     {
3796       gtk_cmclist_freeze (clist);
3797       thaw = TRUE;
3798     }
3799
3800   GTK_CMCLIST_SET_FLAG (clist, CMCLIST_AUTO_RESIZE_BLOCKED);
3801   gtk_cmctree_post_recursive_to_depth (ctree, node, depth,
3802                                      GTK_CMCTREE_FUNC (tree_collapse_to_depth),
3803                                      GINT_TO_POINTER (depth));
3804   GTK_CMCLIST_UNSET_FLAG (clist, CMCLIST_AUTO_RESIZE_BLOCKED);
3805   for (i = 0; i < clist->columns; i++)
3806     if (clist->column[i].auto_resize)
3807       gtk_cmclist_set_column_width (clist, i,
3808                                   gtk_cmclist_optimal_column_width (clist, i));
3809
3810   if (thaw)
3811     gtk_cmclist_thaw (clist);
3812 }
3813
3814 void
3815 gtk_cmctree_toggle_expansion (GtkCMCTree     *ctree,
3816                             GtkCMCTreeNode *node)
3817 {
3818   cm_return_if_fail (GTK_IS_CMCTREE (ctree));
3819   cm_return_if_fail (node != NULL);
3820   
3821   if (GTK_CMCTREE_ROW (node)->is_leaf)
3822     return;
3823
3824   tree_toggle_expansion (ctree, node, NULL);
3825 }
3826
3827 void 
3828 gtk_cmctree_toggle_expansion_recursive (GtkCMCTree     *ctree,
3829                                       GtkCMCTreeNode *node)
3830 {
3831   GtkCMCList *clist;
3832   gboolean thaw = FALSE;
3833
3834   cm_return_if_fail (GTK_IS_CMCTREE (ctree));
3835   
3836   if (node && GTK_CMCTREE_ROW (node)->is_leaf)
3837     return;
3838
3839   clist = GTK_CMCLIST (ctree);
3840
3841   if (CLIST_UNFROZEN (clist) && (!node || gtk_cmctree_is_viewable (ctree, node)))
3842     {
3843       gtk_cmclist_freeze (clist);
3844       thaw = TRUE;
3845     }
3846   
3847   gtk_cmctree_post_recursive (ctree, node,
3848                             GTK_CMCTREE_FUNC (tree_toggle_expansion), NULL);
3849
3850   if (thaw)
3851     gtk_cmclist_thaw (clist);
3852 }
3853
3854 void
3855 gtk_cmctree_select (GtkCMCTree     *ctree, 
3856                   GtkCMCTreeNode *node)
3857 {
3858   cm_return_if_fail (GTK_IS_CMCTREE (ctree));
3859   cm_return_if_fail (node != NULL);
3860
3861   if (GTK_CMCTREE_ROW (node)->row.selectable)
3862     g_signal_emit (G_OBJECT (ctree), ctree_signals[TREE_SELECT_ROW], 0,
3863                      node, -1);
3864 }
3865
3866 void
3867 gtk_cmctree_unselect (GtkCMCTree     *ctree, 
3868                     GtkCMCTreeNode *node)
3869 {
3870   cm_return_if_fail (GTK_IS_CMCTREE (ctree));
3871   cm_return_if_fail (node != NULL);
3872
3873   g_signal_emit (G_OBJECT (ctree), ctree_signals[TREE_UNSELECT_ROW], 0,
3874                    node, -1);
3875 }
3876
3877 void
3878 gtk_cmctree_select_recursive (GtkCMCTree     *ctree, 
3879                             GtkCMCTreeNode *node)
3880 {
3881   gtk_cmctree_real_select_recursive (ctree, node, TRUE);
3882 }
3883
3884 void
3885 gtk_cmctree_unselect_recursive (GtkCMCTree     *ctree, 
3886                               GtkCMCTreeNode *node)
3887 {
3888   gtk_cmctree_real_select_recursive (ctree, node, FALSE);
3889 }
3890
3891 void
3892 gtk_cmctree_real_select_recursive (GtkCMCTree     *ctree, 
3893                                  GtkCMCTreeNode *node, 
3894                                  gint          state)
3895 {
3896   GtkCMCList *clist;
3897   gboolean thaw = FALSE;
3898
3899   cm_return_if_fail (GTK_IS_CMCTREE (ctree));
3900
3901   clist = GTK_CMCLIST (ctree);
3902
3903   if ((state && 
3904        (clist->selection_mode ==  GTK_SELECTION_BROWSE ||
3905         clist->selection_mode == GTK_SELECTION_SINGLE)) ||
3906       (!state && clist->selection_mode ==  GTK_SELECTION_BROWSE))
3907     return;
3908
3909   if (CLIST_UNFROZEN (clist) && (!node || gtk_cmctree_is_viewable (ctree, node)))
3910     {
3911       gtk_cmclist_freeze (clist);
3912       thaw = TRUE;
3913     }
3914
3915   if (clist->selection_mode == GTK_SELECTION_MULTIPLE)
3916     {
3917       GTK_CMCLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
3918       
3919       g_list_free (clist->undo_selection);
3920       g_list_free (clist->undo_unselection);
3921       clist->undo_selection = NULL;
3922       clist->undo_unselection = NULL;
3923     }
3924
3925   if (state)
3926     gtk_cmctree_post_recursive (ctree, node,
3927                               GTK_CMCTREE_FUNC (tree_select), NULL);
3928   else 
3929     gtk_cmctree_post_recursive (ctree, node,
3930                               GTK_CMCTREE_FUNC (tree_unselect), NULL);
3931   
3932   if (thaw)
3933     gtk_cmclist_thaw (clist);
3934 }
3935
3936
3937 /***********************************************************
3938  *           Analogons of GtkCMCList functions               *
3939  ***********************************************************/
3940
3941
3942 void 
3943 gtk_cmctree_node_set_text (GtkCMCTree     *ctree,
3944                          GtkCMCTreeNode *node,
3945                          gint          column,
3946                          const gchar  *text)
3947 {
3948   GtkCMCList *clist;
3949
3950   cm_return_if_fail (GTK_IS_CMCTREE (ctree));
3951   cm_return_if_fail (node != NULL);
3952
3953   if (column < 0 || column >= GTK_CMCLIST (ctree)->columns)
3954     return;
3955   
3956   clist = GTK_CMCLIST (ctree);
3957
3958   GTK_CMCLIST_GET_CLASS (clist)->set_cell_contents
3959     (clist, &(GTK_CMCTREE_ROW (node)->row), column, GTK_CMCELL_TEXT,
3960      text, 0, NULL);
3961
3962   tree_draw_node (ctree, node);
3963 }
3964
3965 void 
3966 gtk_cmctree_node_set_pixbuf (GtkCMCTree     *ctree,
3967                            GtkCMCTreeNode *node,
3968                            gint          column,
3969                            GdkPixbuf    *pixbuf)
3970 {
3971   GtkCMCList *clist;
3972
3973   cm_return_if_fail (GTK_IS_CMCTREE (ctree));
3974   cm_return_if_fail (node != NULL);
3975   cm_return_if_fail (pixbuf != NULL);
3976
3977   if (column < 0 || column >= GTK_CMCLIST (ctree)->columns)
3978     return;
3979
3980   g_object_ref (pixbuf);
3981
3982   clist = GTK_CMCLIST (ctree);
3983
3984   GTK_CMCLIST_GET_CLASS (clist)->set_cell_contents
3985     (clist, &(GTK_CMCTREE_ROW (node)->row), column, GTK_CMCELL_PIXBUF,
3986      NULL, 0, pixbuf);
3987
3988   tree_draw_node (ctree, node);
3989 }
3990
3991 void 
3992 gtk_cmctree_node_set_pixtext (GtkCMCTree     *ctree,
3993                             GtkCMCTreeNode *node,
3994                             gint          column,
3995                             const gchar  *text,
3996                             guint8        spacing,
3997                             GdkPixbuf    *pixbuf)
3998 {
3999   GtkCMCList *clist;
4000
4001   cm_return_if_fail (GTK_IS_CMCTREE (ctree));
4002   cm_return_if_fail (node != NULL);
4003   if (column != ctree->tree_column)
4004     cm_return_if_fail (pixbuf != NULL);
4005   if (column < 0 || column >= GTK_CMCLIST (ctree)->columns)
4006     return;
4007
4008   clist = GTK_CMCLIST (ctree);
4009
4010   if (pixbuf)
4011     {
4012       g_object_ref (pixbuf);
4013     }
4014
4015   GTK_CMCLIST_GET_CLASS (clist)->set_cell_contents
4016     (clist, &(GTK_CMCTREE_ROW (node)->row), column, GTK_CMCELL_PIXTEXT,
4017      text, spacing, pixbuf);
4018
4019   tree_draw_node (ctree, node);
4020 }
4021
4022 void 
4023 gtk_cmctree_set_node_info (GtkCMCTree     *ctree,
4024                          GtkCMCTreeNode *node,
4025                          const gchar  *text,
4026                          guint8        spacing,
4027                          GdkPixbuf    *pixbuf_closed,
4028                          GdkPixbuf    *pixbuf_opened,
4029                          gboolean      is_leaf,
4030                          gboolean      expanded)
4031 {
4032   gboolean old_leaf;
4033   gboolean old_expanded;
4034  
4035   cm_return_if_fail (GTK_IS_CMCTREE (ctree));
4036   cm_return_if_fail (node != NULL);
4037
4038   old_leaf = GTK_CMCTREE_ROW (node)->is_leaf;
4039   old_expanded = GTK_CMCTREE_ROW (node)->expanded;
4040
4041   if (is_leaf && GTK_CMCTREE_ROW (node)->children)
4042     {
4043       GtkCMCTreeNode *work;
4044       GtkCMCTreeNode *ptr;
4045       
4046       work = GTK_CMCTREE_ROW (node)->children;
4047       while (work)
4048         {
4049           ptr = work;
4050           work = GTK_CMCTREE_ROW (work)->sibling;
4051           gtk_cmctree_remove_node (ctree, ptr);
4052         }
4053     }
4054
4055   set_node_info (ctree, node, text, spacing, pixbuf_closed,
4056                  pixbuf_opened, is_leaf, expanded);
4057
4058   if (!is_leaf && !old_leaf)
4059     {
4060       GTK_CMCTREE_ROW (node)->expanded = old_expanded;
4061       if (expanded && !old_expanded)
4062         gtk_cmctree_expand (ctree, node);
4063       else if (!expanded && old_expanded)
4064         gtk_cmctree_collapse (ctree, node);
4065     }
4066
4067   GTK_CMCTREE_ROW (node)->expanded = (is_leaf) ? FALSE : expanded;
4068   
4069   tree_draw_node (ctree, node);
4070 }
4071
4072 void
4073 gtk_cmctree_node_set_shift (GtkCMCTree     *ctree,
4074                           GtkCMCTreeNode *node,
4075                           gint          column,
4076                           gint          vertical,
4077                           gint          horizontal)
4078 {
4079   GtkCMCList *clist;
4080   GtkRequisition requisition;
4081   gboolean visible = FALSE;
4082
4083   cm_return_if_fail (GTK_IS_CMCTREE (ctree));
4084   cm_return_if_fail (node != NULL);
4085
4086   if (column < 0 || column >= GTK_CMCLIST (ctree)->columns)
4087     return;
4088
4089   clist = GTK_CMCLIST (ctree);
4090
4091   if (clist->column[column].auto_resize &&
4092       !GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist))
4093     {
4094       visible = gtk_cmctree_is_viewable (ctree, node);
4095       if (visible)
4096         GTK_CMCLIST_GET_CLASS (clist)->cell_size_request
4097           (clist, &GTK_CMCTREE_ROW (node)->row, column, &requisition);
4098     }
4099
4100   GTK_CMCTREE_ROW (node)->row.cell[column].vertical   = vertical;
4101   GTK_CMCTREE_ROW (node)->row.cell[column].horizontal = horizontal;
4102
4103   if (visible)
4104     column_auto_resize (clist, &GTK_CMCTREE_ROW (node)->row,
4105                         column, requisition.width);
4106
4107   tree_draw_node (ctree, node);
4108 }
4109
4110 static void
4111 remove_grab (GtkCMCList *clist)
4112 {
4113   if (gdk_display_pointer_is_grabbed (gtk_widget_get_display (GTK_WIDGET (clist))) && 
4114       gtk_widget_has_grab (GTK_WIDGET(clist)))
4115     {
4116       gtk_grab_remove (GTK_WIDGET (clist));
4117       gdk_display_pointer_ungrab (gtk_widget_get_display (GTK_WIDGET (clist)),
4118                                   GDK_CURRENT_TIME);
4119     }
4120
4121   if (clist->htimer)
4122     {
4123       g_source_remove (clist->htimer);
4124       clist->htimer = 0;
4125     }
4126
4127   if (clist->vtimer)
4128     {
4129       g_source_remove (clist->vtimer);
4130       clist->vtimer = 0;
4131     }
4132 }
4133
4134 void
4135 gtk_cmctree_node_set_selectable (GtkCMCTree     *ctree,
4136                                GtkCMCTreeNode *node,
4137                                gboolean      selectable)
4138 {
4139   cm_return_if_fail (GTK_IS_CMCTREE (ctree));
4140   cm_return_if_fail (node != NULL);
4141
4142   if (selectable == GTK_CMCTREE_ROW (node)->row.selectable)
4143     return;
4144
4145   GTK_CMCTREE_ROW (node)->row.selectable = selectable;
4146
4147   if (!selectable && GTK_CMCTREE_ROW (node)->row.state == GTK_STATE_SELECTED)
4148     {
4149       GtkCMCList *clist;
4150
4151       clist = GTK_CMCLIST (ctree);
4152
4153       if (clist->anchor >= 0 &&
4154           clist->selection_mode == GTK_SELECTION_MULTIPLE)
4155         {
4156           clist->drag_button = 0;
4157           remove_grab (clist);
4158
4159           GTK_CMCLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
4160         }
4161       gtk_cmctree_unselect (ctree, node);
4162     }      
4163 }
4164
4165 gboolean
4166 gtk_cmctree_node_get_selectable (GtkCMCTree     *ctree,
4167                                GtkCMCTreeNode *node)
4168 {
4169   cm_return_val_if_fail (node != NULL, FALSE);
4170
4171   return GTK_CMCTREE_ROW (node)->row.selectable;
4172 }
4173
4174 GtkCMCellType 
4175 gtk_cmctree_node_get_cell_type (GtkCMCTree     *ctree,
4176                               GtkCMCTreeNode *node,
4177                               gint          column)
4178 {
4179   cm_return_val_if_fail (GTK_IS_CMCTREE (ctree), -1);
4180   cm_return_val_if_fail (node != NULL, -1);
4181
4182   if (column < 0 || column >= GTK_CMCLIST (ctree)->columns)
4183     return -1;
4184
4185   return GTK_CMCTREE_ROW (node)->row.cell[column].type;
4186 }
4187
4188 gboolean
4189 gtk_cmctree_node_get_text (GtkCMCTree      *ctree,
4190                          GtkCMCTreeNode  *node,
4191                          gint           column,
4192                          gchar        **text)
4193 {
4194   cm_return_val_if_fail (GTK_IS_CMCTREE (ctree), FALSE);
4195   cm_return_val_if_fail (node != NULL, FALSE);
4196
4197   if (column < 0 || column >= GTK_CMCLIST (ctree)->columns)
4198     return FALSE;
4199
4200   if (GTK_CMCTREE_ROW (node)->row.cell[column].type != GTK_CMCELL_TEXT)
4201     return FALSE;
4202
4203   if (text)
4204     *text = GTK_CMCELL_TEXT (GTK_CMCTREE_ROW (node)->row.cell[column])->text;
4205
4206   return TRUE;
4207 }
4208
4209 gboolean
4210 gtk_cmctree_node_get_pixbuf (GtkCMCTree     *ctree,
4211                            GtkCMCTreeNode *node,
4212                            gint          column,
4213                            GdkPixbuf   **pixbuf)
4214 {
4215   cm_return_val_if_fail (GTK_IS_CMCTREE (ctree), FALSE);
4216   cm_return_val_if_fail (node != NULL, FALSE);
4217
4218   if (column < 0 || column >= GTK_CMCLIST (ctree)->columns)
4219     return FALSE;
4220
4221   if (GTK_CMCTREE_ROW (node)->row.cell[column].type != GTK_CMCELL_PIXBUF)
4222     return FALSE;
4223
4224   if (pixbuf)
4225     *pixbuf = GTK_CMCELL_PIXBUF (GTK_CMCTREE_ROW (node)->row.cell[column])->pixbuf;
4226
4227   return TRUE;
4228 }
4229
4230 gboolean
4231 gtk_cmctree_node_get_pixtext (GtkCMCTree      *ctree,
4232                             GtkCMCTreeNode  *node,
4233                             gint           column,
4234                             gchar        **text,
4235                             guint8        *spacing,
4236                             GdkPixbuf    **pixbuf)
4237 {
4238   cm_return_val_if_fail (GTK_IS_CMCTREE (ctree), FALSE);
4239   cm_return_val_if_fail (node != NULL, FALSE);
4240   
4241   if (column < 0 || column >= GTK_CMCLIST (ctree)->columns)
4242     return FALSE;
4243   
4244   if (GTK_CMCTREE_ROW (node)->row.cell[column].type != GTK_CMCELL_PIXTEXT)
4245     return FALSE;
4246   
4247   if (text)
4248     *text = GTK_CMCELL_PIXTEXT (GTK_CMCTREE_ROW (node)->row.cell[column])->text;
4249   if (spacing)
4250     *spacing = GTK_CMCELL_PIXTEXT (GTK_CMCTREE_ROW 
4251                                  (node)->row.cell[column])->spacing;
4252   if (pixbuf)
4253     *pixbuf = GTK_CMCELL_PIXTEXT (GTK_CMCTREE_ROW 
4254                                 (node)->row.cell[column])->pixbuf;
4255   
4256   return TRUE;
4257 }
4258
4259 gboolean
4260 gtk_cmctree_get_node_info (GtkCMCTree      *ctree,
4261                          GtkCMCTreeNode  *node,
4262                          gchar        **text,
4263                          guint8        *spacing,
4264                          GdkPixbuf    **pixbuf_closed,
4265                          GdkPixbuf    **pixbuf_opened,
4266                          gboolean      *is_leaf,
4267                          gboolean      *expanded)
4268 {
4269   cm_return_val_if_fail (GTK_IS_CMCTREE (ctree), FALSE);
4270   cm_return_val_if_fail (node != NULL, FALSE);
4271   
4272   if (text)
4273     *text = GTK_CMCELL_PIXTEXT 
4274       (GTK_CMCTREE_ROW (node)->row.cell[ctree->tree_column])->text;
4275   if (spacing)
4276     *spacing = GTK_CMCELL_PIXTEXT 
4277       (GTK_CMCTREE_ROW (node)->row.cell[ctree->tree_column])->spacing;
4278   if (pixbuf_closed)
4279     *pixbuf_closed = GTK_CMCTREE_ROW (node)->pixbuf_closed;
4280   if (pixbuf_opened)
4281     *pixbuf_opened = GTK_CMCTREE_ROW (node)->pixbuf_opened;
4282   if (is_leaf)
4283     *is_leaf = GTK_CMCTREE_ROW (node)->is_leaf;
4284   if (expanded)
4285     *expanded = GTK_CMCTREE_ROW (node)->expanded;
4286   
4287   return TRUE;
4288 }
4289
4290 void
4291 gtk_cmctree_node_set_cell_style (GtkCMCTree     *ctree,
4292                                GtkCMCTreeNode *node,
4293                                gint          column,
4294                                GtkStyle     *style)
4295 {
4296   GtkCMCList *clist;
4297   GtkRequisition requisition;
4298   gboolean visible = FALSE;
4299
4300   cm_return_if_fail (GTK_IS_CMCTREE (ctree));
4301   cm_return_if_fail (node != NULL);
4302
4303   clist = GTK_CMCLIST (ctree);
4304
4305   if (column < 0 || column >= clist->columns)
4306     return;
4307
4308   if (GTK_CMCTREE_ROW (node)->row.cell[column].style == style)
4309     return;
4310
4311   if (clist->column[column].auto_resize &&
4312       !GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist))
4313     {
4314       visible = gtk_cmctree_is_viewable (ctree, node);
4315       if (visible)
4316         GTK_CMCLIST_GET_CLASS (clist)->cell_size_request
4317           (clist, &GTK_CMCTREE_ROW (node)->row, column, &requisition);
4318     }
4319
4320   if (GTK_CMCTREE_ROW (node)->row.cell[column].style)
4321     {
4322       if (gtk_widget_get_realized (GTK_WIDGET(ctree)))
4323         gtk_style_detach (GTK_CMCTREE_ROW (node)->row.cell[column].style);
4324       g_object_unref (GTK_CMCTREE_ROW (node)->row.cell[column].style);
4325     }
4326
4327   GTK_CMCTREE_ROW (node)->row.cell[column].style = style;
4328
4329   if (GTK_CMCTREE_ROW (node)->row.cell[column].style)
4330     {
4331       g_object_ref (GTK_CMCTREE_ROW (node)->row.cell[column].style);
4332       
4333       if (gtk_widget_get_realized (GTK_WIDGET(ctree)))
4334         GTK_CMCTREE_ROW (node)->row.cell[column].style =
4335           gtk_style_attach (GTK_CMCTREE_ROW (node)->row.cell[column].style,
4336                             clist->clist_window);
4337     }
4338
4339   if (visible)
4340     column_auto_resize (clist, &GTK_CMCTREE_ROW (node)->row, column,
4341                         requisition.width);
4342
4343   tree_draw_node (ctree, node);
4344 }
4345
4346 GtkStyle *
4347 gtk_cmctree_node_get_cell_style (GtkCMCTree     *ctree,
4348                                GtkCMCTreeNode *node,
4349                                gint          column)
4350 {
4351   cm_return_val_if_fail (GTK_IS_CMCTREE (ctree), NULL);
4352   cm_return_val_if_fail (node != NULL, NULL);
4353
4354   if (column < 0 || column >= GTK_