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