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