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