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