2011-10-22 [colin] 3.7.10cvs41
[claws.git] / src / gtk / gtkcmctree.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball, Josh MacDonald, 
3  * Copyright (C) 1997-1998 Jay Painter <jpaint@serv.net><jpaint@gimp.org>  
4  *
5  * GtkCMCTree widget for GTK+
6  * Copyright (C) 1998 Lars Hamann and Stefan Jeske
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the
20  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21  * Boston, MA 02111-1307, USA.
22  */
23
24 /*
25  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
26  * file for a list of people on the GTK+ Team.  See the ChangeLog
27  * files for a list of changes.  These files are distributed with
28  * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
29  */
30
31 #include <config.h>
32 #include <stdlib.h>
33
34 #include <gtk/gtk.h>
35 #include <gdk/gdkkeysyms.h>
36 #include "gtkcmctree.h"
37 #include "claws-marshal.h"
38 #include "utils.h"
39 #include "gtkutils.c"
40
41 #define PM_SIZE                    8
42 #define TAB_SIZE                   (PM_SIZE + 6)
43 #define CELL_SPACING               1
44 #define CLIST_OPTIMUM_SIZE         64
45 #define COLUMN_INSET               3
46 #define DRAG_WIDTH                 6
47
48 #define ROW_TOP_YPIXEL(clist, row) (((clist)->row_height * (row)) + \
49                                     (((row) + 1) * CELL_SPACING) + \
50                                     (clist)->voffset)
51 #define ROW_FROM_YPIXEL(clist, y)  (((y) - (clist)->voffset) / \
52                                     ((clist)->row_height + CELL_SPACING))
53 #define COLUMN_LEFT_XPIXEL(clist, col)  ((clist)->column[(col)].area.x \
54                                     + (clist)->hoffset)
55 #define COLUMN_LEFT(clist, column) ((clist)->column[(column)].area.x)
56
57 GType
58 gtk_cmctree_pos_get_type (void)
59 {
60   static GType etype = 0;
61   if (etype == 0) {
62     static const GEnumValue values[] = {
63       { GTK_CMCTREE_POS_BEFORE, "GTK_CMCTREE_POS_BEFORE", "before" },
64       { GTK_CMCTREE_POS_AS_CHILD, "GTK_CMCTREE_POS_AS_CHILD", "as-child" },
65       { GTK_CMCTREE_POS_AFTER, "GTK_CMCTREE_POS_AFTER", "after" },
66       { 0, NULL, NULL }
67     };
68 #if GLIB_CHECK_VERSION(2,10,0)
69     etype = g_enum_register_static (g_intern_static_string ("GtkCMCTreePos"), values);
70 #else
71     etype = g_enum_register_static ("GtkCMCTreePos", values);
72 #endif
73   }
74   return etype;
75 }
76 GType
77 gtk_cmctree_line_style_get_type (void)
78 {
79   static GType etype = 0;
80   if (etype == 0) {
81     static const GEnumValue values[] = {
82       { GTK_CMCTREE_LINES_NONE, "GTK_CMCTREE_LINES_NONE", "none" },
83       { GTK_CMCTREE_LINES_SOLID, "GTK_CMCTREE_LINES_SOLID", "solid" },
84       { GTK_CMCTREE_LINES_DOTTED, "GTK_CMCTREE_LINES_DOTTED", "dotted" },
85       { GTK_CMCTREE_LINES_TABBED, "GTK_CMCTREE_LINES_TABBED", "tabbed" },
86       { 0, NULL, NULL }
87     };
88 #if GLIB_CHECK_VERSION(2,10,0)
89     etype = g_enum_register_static (g_intern_static_string ("GtkCMCTreeLineStyle"), values);
90 #else
91     etype = g_enum_register_static ("GtkCMCTreeLineStyle", values);
92 #endif
93   }
94   return etype;
95 }
96 GType
97 gtk_cmctree_expander_style_get_type (void)
98 {
99   static GType etype = 0;
100   if (etype == 0) {
101     static const GEnumValue values[] = {
102       { GTK_CMCTREE_EXPANDER_NONE, "GTK_CMCTREE_EXPANDER_NONE", "none" },
103       { GTK_CMCTREE_EXPANDER_SQUARE, "GTK_CMCTREE_EXPANDER_SQUARE", "square" },
104       { GTK_CMCTREE_EXPANDER_TRIANGLE, "GTK_CMCTREE_EXPANDER_TRIANGLE", "triangle" },
105       { GTK_CMCTREE_EXPANDER_CIRCULAR, "GTK_CMCTREE_EXPANDER_CIRCULAR", "circular" },
106       { 0, NULL, NULL }
107     };
108 #if GLIB_CHECK_VERSION(2,10,0)
109     etype = g_enum_register_static (g_intern_static_string ("GtkCMCTreeExpanderStyle"), values);
110 #else
111     etype = g_enum_register_static ("GtkCMCTreeExpanderStyle", values);
112 #endif
113   }
114   return etype;
115 }
116 GType
117 gtk_cmctree_expansion_type_get_type (void)
118 {
119   static GType etype = 0;
120   if (etype == 0) {
121     static const GEnumValue values[] = {
122       { GTK_CMCTREE_EXPANSION_EXPAND, "GTK_CMCTREE_EXPANSION_EXPAND", "expand" },
123       { GTK_CMCTREE_EXPANSION_EXPAND_RECURSIVE, "GTK_CMCTREE_EXPANSION_EXPAND_RECURSIVE", "expand-recursive" },
124       { GTK_CMCTREE_EXPANSION_COLLAPSE, "GTK_CMCTREE_EXPANSION_COLLAPSE", "collapse" },
125       { GTK_CMCTREE_EXPANSION_COLLAPSE_RECURSIVE, "GTK_CMCTREE_EXPANSION_COLLAPSE_RECURSIVE", "collapse-recursive" },
126       { GTK_CMCTREE_EXPANSION_TOGGLE, "GTK_CMCTREE_EXPANSION_TOGGLE", "toggle" },
127       { GTK_CMCTREE_EXPANSION_TOGGLE_RECURSIVE, "GTK_CMCTREE_EXPANSION_TOGGLE_RECURSIVE", "toggle-recursive" },
128       { 0, NULL, NULL }
129     };
130 #if GLIB_CHECK_VERSION(2,10,0)
131     etype = g_enum_register_static (g_intern_static_string ("GtkCMCTreeExpansionType"), values);
132 #else
133     etype = g_enum_register_static ("GtkCMCTreeExpansionType", values);
134 #endif
135   }
136   return etype;
137 }
138
139
140 static inline gint
141 COLUMN_FROM_XPIXEL (GtkCMCList * clist,
142                     gint x)
143 {
144   gint i, cx;
145
146   for (i = 0; i < clist->columns; i++)
147     if (clist->column[i].visible)
148       {
149         cx = clist->column[i].area.x + clist->hoffset;
150
151         if (x >= (cx - (COLUMN_INSET + CELL_SPACING)) &&
152             x <= (cx + clist->column[i].area.width + COLUMN_INSET))
153           return i;
154       }
155
156   /* no match */
157   return -1;
158 }
159
160 #define CLIST_UNFROZEN(clist)     (((GtkCMCList*) (clist))->freeze_count == 0)
161 #define CLIST_REFRESH(clist)    G_STMT_START { \
162   if (CLIST_UNFROZEN (clist)) \
163     GTK_CMCLIST_GET_CLASS (clist)->refresh ((GtkCMCList*) (clist)); \
164 } G_STMT_END
165
166
167 enum {
168   ARG_0,
169   ARG_N_COLUMNS,
170   ARG_TREE_COLUMN,
171   ARG_INDENT,
172   ARG_SPACING,
173   ARG_SHOW_STUB,
174   ARG_LINE_STYLE,
175   ARG_EXPANDER_STYLE
176 };
177
178
179 static void     gtk_cmctree_class_init    (GtkCMCTreeClass         *klass);
180 static void     gtk_cmctree_init          (GtkCMCTree              *ctree);
181 static GObject* gtk_cmctree_constructor   (GType                  type,
182                                          guint                  n_construct_properties,
183                                          GObjectConstructParam *construct_params);
184 static void gtk_cmctree_set_arg         (GObject *object,
185                                 guint      arg_id,
186                                 const GValue *value,
187                                 GParamSpec *spec);
188 static void gtk_cmctree_get_arg         (GObject *object,
189                                 guint      arg_id,
190                                 GValue *value,
191                                 GParamSpec *spec);
192 static void gtk_cmctree_realize           (GtkWidget      *widget);
193 static void gtk_cmctree_unrealize         (GtkWidget      *widget);
194 static gint gtk_cmctree_button_press      (GtkWidget      *widget,
195                                          GdkEventButton *event);
196 static void ctree_attach_styles         (GtkCMCTree       *ctree,
197                                          GtkCMCTreeNode   *node,
198                                          gpointer        data);
199 static void ctree_detach_styles         (GtkCMCTree       *ctree,
200                                          GtkCMCTreeNode   *node, 
201                                          gpointer        data);
202 static gint draw_cell_pixbuf            (GdkWindow      *window,
203                                          GdkRectangle   *clip_rectangle,
204                                          GdkGC          *fg_gc,
205                                          GdkPixbuf      *pixbuf,
206                                          gint            x,
207                                          gint            y,
208                                          gint            width,
209                                          gint            height);
210 static void get_cell_style              (GtkCMCList       *clist,
211                                          GtkCMCListRow    *clist_row,
212                                          gint            state,
213                                          gint            column,
214                                          GtkStyle      **style,
215                                          GdkGC         **fg_gc,
216                                          GdkGC         **bg_gc);
217 static gint gtk_cmctree_draw_expander     (GtkCMCTree       *ctree,
218                                          GtkCMCTreeRow    *ctree_row,
219                                          GtkStyle       *style,
220                                          GdkRectangle   *clip_rectangle,
221                                          gint            x);
222 static gint gtk_cmctree_draw_lines        (GtkCMCTree       *ctree,
223                                          GtkCMCTreeRow    *ctree_row,
224                                          gint            row,
225                                          gint            column,
226                                          gint            state,
227                                          GdkRectangle   *clip_rectangle,
228                                          GdkRectangle   *cell_rectangle,
229                                          GdkRectangle   *crect,
230                                          GdkRectangle   *area,
231                                          GtkStyle       *style);
232 static void draw_row                    (GtkCMCList       *clist,
233                                          GdkRectangle   *area,
234                                          gint            row,
235                                          GtkCMCListRow    *clist_row);
236 static void draw_drag_highlight         (GtkCMCList        *clist,
237                                          GtkCMCListRow     *dest_row,
238                                          gint             dest_row_number,
239                                          GtkCMCListDragPos  drag_pos);
240 static void tree_draw_node              (GtkCMCTree      *ctree,
241                                          GtkCMCTreeNode  *node);
242 static void set_cell_contents           (GtkCMCList      *clist,
243                                          GtkCMCListRow   *clist_row,
244                                          gint           column,
245                                          GtkCMCellType    type,
246                                          const gchar   *text,
247                                          guint8         spacing,
248                                          GdkPixbuf     *pixbuf);
249 static void set_node_info               (GtkCMCTree      *ctree,
250                                          GtkCMCTreeNode  *node,
251                                          const gchar   *text,
252                                          guint8         spacing,
253                                          GdkPixbuf     *pixbuf_closed,
254                                          GdkPixbuf     *pixbuf_opened,
255                                          gboolean       is_leaf,
256                                          gboolean       expanded);
257 static GtkCMCTreeRow *row_new             (GtkCMCTree      *ctree);
258 static void row_delete                  (GtkCMCTree      *ctree,
259                                          GtkCMCTreeRow   *ctree_row);
260 static void tree_delete                 (GtkCMCTree      *ctree, 
261                                          GtkCMCTreeNode  *node, 
262                                          gpointer       data);
263 static void tree_delete_row             (GtkCMCTree      *ctree, 
264                                          GtkCMCTreeNode  *node, 
265                                          gpointer       data);
266 static void real_clear                  (GtkCMCList      *clist);
267 static void tree_update_level           (GtkCMCTree      *ctree, 
268                                          GtkCMCTreeNode  *node, 
269                                          gpointer       data);
270 static void tree_select                 (GtkCMCTree      *ctree, 
271                                          GtkCMCTreeNode  *node, 
272                                          gpointer       data);
273 static void tree_unselect               (GtkCMCTree      *ctree, 
274                                          GtkCMCTreeNode  *node, 
275                                          gpointer       data);
276 static void real_select_all             (GtkCMCList      *clist);
277 static void real_unselect_all           (GtkCMCList      *clist);
278 static void tree_expand                 (GtkCMCTree      *ctree, 
279                                          GtkCMCTreeNode  *node,
280                                          gpointer       data);
281 static void tree_collapse               (GtkCMCTree      *ctree, 
282                                          GtkCMCTreeNode  *node,
283                                          gpointer       data);
284 static void tree_collapse_to_depth      (GtkCMCTree      *ctree, 
285                                          GtkCMCTreeNode  *node, 
286                                          gint           depth);
287 static void tree_toggle_expansion       (GtkCMCTree      *ctree,
288                                          GtkCMCTreeNode  *node,
289                                          gpointer       data);
290 static void change_focus_row_expansion  (GtkCMCTree      *ctree,
291                                          GtkCMCTreeExpansionType expansion);
292 static void real_select_row             (GtkCMCList      *clist,
293                                          gint           row,
294                                          gint           column,
295                                          GdkEvent      *event);
296 static void real_unselect_row           (GtkCMCList      *clist,
297                                          gint           row,
298                                          gint           column,
299                                          GdkEvent      *event);
300 static void real_tree_select            (GtkCMCTree      *ctree,
301                                          GtkCMCTreeNode  *node,
302                                          gint           column);
303 static void real_tree_unselect          (GtkCMCTree      *ctree,
304                                          GtkCMCTreeNode  *node,
305                                          gint           column);
306 static void real_tree_expand            (GtkCMCTree      *ctree,
307                                          GtkCMCTreeNode  *node);
308 static void real_tree_collapse          (GtkCMCTree      *ctree,
309                                          GtkCMCTreeNode  *node);
310 static void real_tree_move              (GtkCMCTree      *ctree,
311                                          GtkCMCTreeNode  *node,
312                                          GtkCMCTreeNode  *new_parent, 
313                                          GtkCMCTreeNode  *new_sibling);
314 static void real_row_move               (GtkCMCList      *clist,
315                                          gint           source_row,
316                                          gint           dest_row);
317 static void gtk_cmctree_link              (GtkCMCTree      *ctree,
318                                          GtkCMCTreeNode  *node,
319                                          GtkCMCTreeNode  *parent,
320                                          GtkCMCTreeNode  *sibling,
321                                          gboolean       update_focus_row);
322 static void gtk_cmctree_unlink            (GtkCMCTree      *ctree, 
323                                          GtkCMCTreeNode  *node,
324                                          gboolean       update_focus_row);
325 static GtkCMCTreeNode * gtk_cmctree_last_visible (GtkCMCTree     *ctree,
326                                               GtkCMCTreeNode *node);
327 static gboolean ctree_is_hot_spot       (GtkCMCTree      *ctree, 
328                                          GtkCMCTreeNode  *node,
329                                          gint           row, 
330                                          gint           x, 
331                                          gint           y);
332 static void tree_sort                   (GtkCMCTree      *ctree,
333                                          GtkCMCTreeNode  *node,
334                                          gpointer       data);
335 static void fake_unselect_all           (GtkCMCList      *clist,
336                                          gint           row);
337 static GList * selection_find           (GtkCMCList      *clist,
338                                          gint           row_number,
339                                          GList         *row_list_element);
340 static void resync_selection            (GtkCMCList      *clist,
341                                          GdkEvent      *event);
342 static void real_undo_selection         (GtkCMCList      *clist);
343 static void select_row_recursive        (GtkCMCTree      *ctree, 
344                                          GtkCMCTreeNode  *node, 
345                                          gpointer       data);
346 static gint real_insert_row             (GtkCMCList      *clist,
347                                          gint           row,
348                                          gchar         *text[]);
349 static void real_remove_row             (GtkCMCList      *clist,
350                                          gint           row);
351 static void real_sort_list              (GtkCMCList      *clist);
352 static void cell_size_request           (GtkCMCList       *clist,
353                                          GtkCMCListRow    *clist_row,
354                                          gint            column,
355                                          GtkRequisition *requisition);
356 static void column_auto_resize          (GtkCMCList       *clist,
357                                          GtkCMCListRow    *clist_row,
358                                          gint            column,
359                                          gint            old_width);
360 static void auto_resize_columns         (GtkCMCList       *clist);
361
362
363 static gboolean check_drag               (GtkCMCTree         *ctree,
364                                           GtkCMCTreeNode     *drag_source,
365                                           GtkCMCTreeNode     *drag_target,
366                                           GtkCMCListDragPos   insert_pos);
367 static void gtk_cmctree_drag_begin         (GtkWidget        *widget,
368                                           GdkDragContext   *context);
369 static gint gtk_cmctree_drag_motion        (GtkWidget        *widget,
370                                           GdkDragContext   *context,
371                                           gint              x,
372                                           gint              y,
373                                           guint             time);
374 static void gtk_cmctree_drag_data_received (GtkWidget        *widget,
375                                           GdkDragContext   *context,
376                                           gint              x,
377                                           gint              y,
378                                           GtkSelectionData *selection_data,
379                                           guint             info,
380                                           guint32           time);
381 static void remove_grab                  (GtkCMCList         *clist);
382 static void drag_dest_cell               (GtkCMCList         *clist,
383                                           gint              x,
384                                           gint              y,
385                                           GtkCMCListDestInfo *dest_info);
386
387
388 enum
389 {
390   TREE_SELECT_ROW,
391   TREE_UNSELECT_ROW,
392   TREE_EXPAND,
393   TREE_COLLAPSE,
394   TREE_MOVE,
395   CHANGE_FOCUS_ROW_EXPANSION,
396   LAST_SIGNAL
397 };
398
399 static GtkCMCListClass *parent_class = NULL;
400 static GtkContainerClass *container_class = NULL;
401 static guint ctree_signals[LAST_SIGNAL] = {0};
402
403
404 GType
405 gtk_cmctree_get_type (void)
406 {
407   static GType ctree_type = 0;
408
409   if (!ctree_type)
410     {
411       static const GTypeInfo ctree_info =
412       {
413                         sizeof (GtkCMCTreeClass),
414
415                         (GBaseInitFunc) NULL,
416                         (GBaseFinalizeFunc) NULL,
417
418                         (GClassInitFunc) gtk_cmctree_class_init,
419                         (GClassFinalizeFunc) NULL,
420                         NULL,   /* class_data */
421
422                         sizeof (GtkCMCTree),
423                         0,      /* n_preallocs */
424                         (GInstanceInitFunc) gtk_cmctree_init,
425       };
426
427         ctree_type = g_type_register_static (GTK_TYPE_CMCLIST, "GtkCMCTree", &ctree_info, (GTypeFlags)0);
428     }
429
430   return ctree_type;
431 }
432
433 static void
434 gtk_cmctree_class_init (GtkCMCTreeClass *klass)
435 {
436   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
437   GtkObjectClass *object_class;
438   GtkWidgetClass *widget_class;
439   GtkCMCListClass *clist_class;
440   GtkBindingSet *binding_set;
441
442   gobject_class->constructor = gtk_cmctree_constructor;
443
444   object_class = (GtkObjectClass *) klass;
445   widget_class = (GtkWidgetClass *) klass;
446   container_class = (GtkContainerClass *) klass;
447   clist_class = (GtkCMCListClass *) klass;
448
449   parent_class = g_type_class_peek (GTK_TYPE_CMCLIST);
450   container_class = g_type_class_peek (GTK_TYPE_CONTAINER);
451
452   gobject_class->set_property = gtk_cmctree_set_arg;
453   gobject_class->get_property = gtk_cmctree_get_arg;
454
455   widget_class->realize = gtk_cmctree_realize;
456   widget_class->unrealize = gtk_cmctree_unrealize;
457   widget_class->button_press_event = gtk_cmctree_button_press;
458
459   widget_class->drag_begin = gtk_cmctree_drag_begin;
460   widget_class->drag_motion = gtk_cmctree_drag_motion;
461   widget_class->drag_data_received = gtk_cmctree_drag_data_received;
462
463   clist_class->select_row = real_select_row;
464   clist_class->unselect_row = real_unselect_row;
465   clist_class->row_move = real_row_move;
466   clist_class->undo_selection = real_undo_selection;
467   clist_class->resync_selection = resync_selection;
468   clist_class->selection_find = selection_find;
469   clist_class->click_column = NULL;
470   clist_class->draw_row = draw_row;
471   clist_class->draw_drag_highlight = draw_drag_highlight;
472   clist_class->clear = real_clear;
473   clist_class->select_all = real_select_all;
474   clist_class->unselect_all = real_unselect_all;
475   clist_class->fake_unselect_all = fake_unselect_all;
476   clist_class->insert_row = real_insert_row;
477   clist_class->remove_row = real_remove_row;
478   clist_class->sort_list = real_sort_list;
479   clist_class->set_cell_contents = set_cell_contents;
480   clist_class->cell_size_request = cell_size_request;
481
482   klass->tree_select_row = real_tree_select;
483   klass->tree_unselect_row = real_tree_unselect;
484   klass->tree_expand = real_tree_expand;
485   klass->tree_collapse = real_tree_collapse;
486   klass->tree_move = real_tree_move;
487   klass->change_focus_row_expansion = change_focus_row_expansion;
488
489   g_object_class_install_property (gobject_class,
490                                 ARG_N_COLUMNS,
491                                 g_param_spec_uint ("n-columns",
492                                 "N-Columns",
493                                 "N-Columns",
494                                 1,
495                                 G_MAXINT,
496                                 1,
497                                 G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
498   g_object_class_install_property (gobject_class,
499                                 ARG_TREE_COLUMN,
500                                 g_param_spec_uint ("tree-column",
501                                 "tree-column",
502                                 "tree-column",
503                                 0,
504                                 G_MAXINT,
505                                 0,
506                                 G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
507   g_object_class_install_property (gobject_class,
508                                 ARG_INDENT,
509                                 g_param_spec_uint ("indent",
510                                 "indent",
511                                 "indent",
512                                 1,
513                                 G_MAXINT,
514                                 1,
515                                 G_PARAM_READWRITE));
516   g_object_class_install_property (gobject_class,
517                                 ARG_SPACING,
518                                 g_param_spec_uint ("spacing",
519                                 "spacing",
520                                 "spacing",
521                                 1,
522                                 G_MAXINT,
523                                 1,
524                                 G_PARAM_READWRITE));
525   g_object_class_install_property (gobject_class,
526                                 ARG_SHOW_STUB,
527                                 g_param_spec_boolean ("show-stub",
528                                 "show-stub",
529                                 "show-stub",
530                                 TRUE,
531                                 G_PARAM_READWRITE));
532   g_object_class_install_property (gobject_class,
533                                 ARG_LINE_STYLE,
534                                 g_param_spec_enum ("line-style",
535                                 "line-style",
536                                 "line-style",
537                                 GTK_TYPE_CMCTREE_LINE_STYLE, 0,
538                                 G_PARAM_READWRITE));
539   g_object_class_install_property (gobject_class,
540                                 ARG_EXPANDER_STYLE,
541                                 g_param_spec_enum ("expander-style",
542                                 "expander-style",
543                                 "expander-style",
544                                 GTK_TYPE_CMCTREE_EXPANDER_STYLE, 0,
545                                 G_PARAM_READWRITE));
546
547   ctree_signals[TREE_SELECT_ROW] =
548                 g_signal_new ("tree_select_row",
549                               G_TYPE_FROM_CLASS (object_class),
550                               G_SIGNAL_RUN_FIRST,
551                               G_STRUCT_OFFSET (GtkCMCTreeClass, tree_select_row),
552                               NULL, NULL,
553                               claws_marshal_VOID__POINTER_INT,
554                               G_TYPE_NONE, 2,
555                               GTK_TYPE_CMCTREE_NODE,
556                               G_TYPE_INT);
557   ctree_signals[TREE_UNSELECT_ROW] =
558                 g_signal_new ("tree_unselect_row",
559                               G_TYPE_FROM_CLASS (object_class),
560                               G_SIGNAL_RUN_FIRST,
561                               G_STRUCT_OFFSET (GtkCMCTreeClass, tree_unselect_row),
562                               NULL, NULL,
563                               claws_marshal_VOID__POINTER_INT,
564                               G_TYPE_NONE, 2,
565                               GTK_TYPE_CMCTREE_NODE,
566                               G_TYPE_INT);
567   ctree_signals[TREE_EXPAND] =
568                 g_signal_new ("tree_expand",
569                               G_TYPE_FROM_CLASS (object_class),
570                               G_SIGNAL_RUN_LAST,
571                               G_STRUCT_OFFSET (GtkCMCTreeClass, tree_expand),
572                               NULL, NULL,
573                               claws_marshal_VOID__POINTER,
574                               G_TYPE_NONE, 1,
575                               GTK_TYPE_CMCTREE_NODE);
576   ctree_signals[TREE_COLLAPSE] =
577                 g_signal_new ("tree_collapse",
578                               G_TYPE_FROM_CLASS (object_class),
579                               G_SIGNAL_RUN_LAST,
580                               G_STRUCT_OFFSET (GtkCMCTreeClass, tree_collapse),
581                               NULL, NULL,
582                               claws_marshal_VOID__POINTER,
583                               G_TYPE_NONE, 1,
584                               GTK_TYPE_CMCTREE_NODE);
585   ctree_signals[TREE_MOVE] =
586                 g_signal_new ("tree_move",
587                               G_TYPE_FROM_CLASS (object_class),
588                               G_SIGNAL_RUN_LAST,
589                               G_STRUCT_OFFSET (GtkCMCTreeClass, tree_move),
590                               NULL, NULL,
591                               claws_marshal_VOID__POINTER_POINTER_POINTER,
592                               G_TYPE_NONE, 3,
593                               GTK_TYPE_CMCTREE_NODE,GTK_TYPE_CMCTREE_NODE,GTK_TYPE_CMCTREE_NODE);
594   ctree_signals[CHANGE_FOCUS_ROW_EXPANSION] =
595                 g_signal_new ("change_focus_row_expansion",
596                               G_TYPE_FROM_CLASS (object_class),
597                               G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
598                               G_STRUCT_OFFSET (GtkCMCTreeClass, change_focus_row_expansion),
599                               NULL, NULL,
600                               claws_marshal_VOID__ENUM,
601                               G_TYPE_NONE, 1, GTK_TYPE_CMCTREE_EXPANSION_TYPE);
602
603   binding_set = gtk_binding_set_by_class (klass);
604   gtk_binding_entry_add_signal (binding_set,
605                                 GDK_KEY_plus, 0,
606                                 "change_focus_row_expansion", 1,
607                                 G_TYPE_ENUM, GTK_CMCTREE_EXPANSION_EXPAND);
608   gtk_binding_entry_add_signal (binding_set,
609                                 GDK_KEY_plus, GDK_CONTROL_MASK,
610                                 "change_focus_row_expansion", 1,
611                                 G_TYPE_ENUM, GTK_CMCTREE_EXPANSION_EXPAND_RECURSIVE);
612
613   gtk_binding_entry_add_signal (binding_set,
614                                 GDK_KEY_KP_Add, 0,
615                                 "change_focus_row_expansion", 1,
616                                 G_TYPE_ENUM, GTK_CMCTREE_EXPANSION_EXPAND);
617   gtk_binding_entry_add_signal (binding_set,
618                                 GDK_KEY_KP_Add, GDK_CONTROL_MASK,
619                                 "change_focus_row_expansion", 1,
620                                 G_TYPE_ENUM, GTK_CMCTREE_EXPANSION_EXPAND_RECURSIVE);
621   
622   gtk_binding_entry_add_signal (binding_set,
623                                 GDK_KEY_minus, 0,
624                                 "change_focus_row_expansion", 1,
625                                 G_TYPE_ENUM, GTK_CMCTREE_EXPANSION_COLLAPSE);
626   gtk_binding_entry_add_signal (binding_set,
627                                 GDK_KEY_minus, GDK_CONTROL_MASK,
628                                 "change_focus_row_expansion", 1,
629                                 G_TYPE_ENUM,
630                                 GTK_CMCTREE_EXPANSION_COLLAPSE_RECURSIVE);
631   gtk_binding_entry_add_signal (binding_set,
632                                 GDK_KEY_KP_Subtract, 0,
633                                 "change_focus_row_expansion", 1,
634                                 G_TYPE_ENUM, GTK_CMCTREE_EXPANSION_COLLAPSE);
635   gtk_binding_entry_add_signal (binding_set,
636                                 GDK_KEY_KP_Subtract, GDK_CONTROL_MASK,
637                                 "change_focus_row_expansion", 1,
638                                 G_TYPE_ENUM,
639                                 GTK_CMCTREE_EXPANSION_COLLAPSE_RECURSIVE);
640   gtk_binding_entry_add_signal (binding_set,
641                                 GDK_KEY_equal, 0,
642                                 "change_focus_row_expansion", 1,
643                                 G_TYPE_ENUM, GTK_CMCTREE_EXPANSION_TOGGLE);
644   gtk_binding_entry_add_signal (binding_set,
645                                 GDK_KEY_KP_Equal, 0,
646                                 "change_focus_row_expansion", 1,
647                                 G_TYPE_ENUM, GTK_CMCTREE_EXPANSION_TOGGLE);
648   gtk_binding_entry_add_signal (binding_set,
649                                 GDK_KEY_KP_Multiply, 0,
650                                 "change_focus_row_expansion", 1,
651                                 G_TYPE_ENUM, GTK_CMCTREE_EXPANSION_TOGGLE);
652   gtk_binding_entry_add_signal (binding_set,
653                                 GDK_KEY_asterisk, 0,
654                                 "change_focus_row_expansion", 1,
655                                 G_TYPE_ENUM, GTK_CMCTREE_EXPANSION_TOGGLE);
656   gtk_binding_entry_add_signal (binding_set,
657                                 GDK_KEY_KP_Multiply, GDK_CONTROL_MASK,
658                                 "change_focus_row_expansion", 1,
659                                 G_TYPE_ENUM,
660                                 GTK_CMCTREE_EXPANSION_TOGGLE_RECURSIVE);
661   gtk_binding_entry_add_signal (binding_set,
662                                 GDK_KEY_asterisk, GDK_CONTROL_MASK,
663                                 "change_focus_row_expansion", 1,
664                                 G_TYPE_ENUM,
665                                 GTK_CMCTREE_EXPANSION_TOGGLE_RECURSIVE);  
666 }
667
668 static void
669 gtk_cmctree_set_arg (GObject *object,
670                                 guint      arg_id,
671                                 const GValue *value,
672                                 GParamSpec *spec)
673 {
674   GtkCMCTree *ctree;
675   GtkCMCList *clist;
676
677   ctree = GTK_CMCTREE (object);
678   clist = GTK_CMCLIST (ctree);
679
680   switch (arg_id)
681     {
682     case ARG_N_COLUMNS: /* construct-only arg, only set at construction time */
683 #if !GLIB_CHECK_VERSION(2,10,0)
684       cm_return_if_fail (clist->row_mem_chunk == NULL);
685 #endif
686       clist->columns = MAX (1, g_value_get_uint (value));
687 #if !GLIB_CHECK_VERSION(2,10,0)
688       clist->row_mem_chunk = g_mem_chunk_new ("ctree row mem chunk",
689                                               sizeof (GtkCMCTreeRow),
690                                               sizeof (GtkCMCTreeRow)
691                                               * CLIST_OPTIMUM_SIZE,
692                                               G_ALLOC_AND_FREE);
693       clist->cell_mem_chunk = g_mem_chunk_new ("ctree cell mem chunk",
694                                                sizeof (GtkCMCell) * clist->columns,
695                                                sizeof (GtkCMCell) * clist->columns
696                                                * CLIST_OPTIMUM_SIZE,
697                                                G_ALLOC_AND_FREE);
698 #endif
699       ctree->tree_column = CLAMP (ctree->tree_column, 0, clist->columns);
700       break;
701     case ARG_TREE_COLUMN: /* construct-only arg, only set at construction time */
702       ctree->tree_column = g_value_get_uint (value);
703 #if !GLIB_CHECK_VERSION(2,10,0)
704       if (clist->row_mem_chunk)
705 #endif
706         ctree->tree_column = CLAMP (ctree->tree_column, 0, clist->columns);
707       break;
708     case ARG_INDENT:
709       gtk_cmctree_set_indent (ctree, g_value_get_uint (value));
710       break;
711     case ARG_SPACING:
712       gtk_cmctree_set_spacing (ctree, g_value_get_uint (value));
713       break;
714     case ARG_SHOW_STUB:
715       gtk_cmctree_set_show_stub (ctree, g_value_get_boolean (value));
716       break;
717     case ARG_LINE_STYLE:
718       gtk_cmctree_set_line_style (ctree, g_value_get_enum (value));
719       break;
720     case ARG_EXPANDER_STYLE:
721       gtk_cmctree_set_expander_style (ctree, g_value_get_enum (value));
722       break;
723     default:
724       break;
725     }
726 }
727
728 static void
729 gtk_cmctree_get_arg (GObject *object,
730                                 guint      arg_id,
731                                 GValue *value,
732                                 GParamSpec *spec)
733 {
734   GtkCMCTree *ctree;
735
736   ctree = GTK_CMCTREE (object);
737
738   switch (arg_id)
739     {
740     case ARG_N_COLUMNS:
741       g_value_set_uint(value, GTK_CMCLIST (ctree)->columns);
742       break;
743     case ARG_TREE_COLUMN:
744       g_value_set_uint(value, ctree->tree_column);
745       break;
746     case ARG_INDENT:
747       g_value_set_uint(value, ctree->tree_indent);
748       break;
749     case ARG_SPACING:
750       g_value_set_uint(value, ctree->tree_spacing);
751       break;
752     case ARG_SHOW_STUB:
753       g_value_set_boolean(value, ctree->show_stub);
754       break;
755     case ARG_LINE_STYLE:
756       g_value_set_enum(value, ctree->line_style);
757       break;
758     case ARG_EXPANDER_STYLE:
759       g_value_set_enum(value, ctree->expander_style);
760       break;
761     default:
762       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, arg_id, spec);
763       break;
764     }
765 }
766
767 static void
768 gtk_cmctree_init (GtkCMCTree *ctree)
769 {
770   GtkCMCList *clist;
771
772   GTK_CMCLIST_SET_FLAG (ctree, CMCLIST_DRAW_DRAG_RECT);
773   GTK_CMCLIST_SET_FLAG (ctree, CMCLIST_DRAW_DRAG_LINE);
774
775   clist = GTK_CMCLIST (ctree);
776
777   ctree->tree_indent    = 20;
778   ctree->tree_spacing   = 5;
779   ctree->tree_column    = 0;
780   ctree->line_style     = GTK_CMCTREE_LINES_SOLID;
781   ctree->expander_style = GTK_CMCTREE_EXPANDER_SQUARE;
782   ctree->drag_compare   = NULL;
783   ctree->show_stub      = TRUE;
784
785   clist->button_actions[0] |= GTK_CMBUTTON_EXPANDS;
786 }
787
788 static void
789 ctree_attach_styles (GtkCMCTree     *ctree,
790                      GtkCMCTreeNode *node,
791                      gpointer      data)
792 {
793   GtkCMCList *clist;
794   gint i;
795
796   clist = GTK_CMCLIST (ctree);
797
798   if (GTK_CMCTREE_ROW (node)->row.style)
799     GTK_CMCTREE_ROW (node)->row.style =
800       gtk_style_attach (GTK_CMCTREE_ROW (node)->row.style, clist->clist_window);
801
802   if (GTK_CMCTREE_ROW (node)->row.fg_set || GTK_CMCTREE_ROW (node)->row.bg_set)
803     {
804       GdkColormap *colormap;
805
806       colormap = gtk_widget_get_colormap (GTK_WIDGET (ctree));
807       if (GTK_CMCTREE_ROW (node)->row.fg_set)
808         gdk_colormap_alloc_color (colormap, &(GTK_CMCTREE_ROW (node)->row.foreground), TRUE, TRUE);
809       if (GTK_CMCTREE_ROW (node)->row.bg_set)
810         gdk_colormap_alloc_color (colormap, &(GTK_CMCTREE_ROW (node)->row.background), TRUE, TRUE);
811     }
812
813   for (i = 0; i < clist->columns; i++)
814     if  (GTK_CMCTREE_ROW (node)->row.cell[i].style)
815       GTK_CMCTREE_ROW (node)->row.cell[i].style =
816         gtk_style_attach (GTK_CMCTREE_ROW (node)->row.cell[i].style,
817                           clist->clist_window);
818 }
819
820 static void
821 ctree_detach_styles (GtkCMCTree     *ctree,
822                      GtkCMCTreeNode *node,
823                      gpointer      data)
824 {
825   GtkCMCList *clist;
826   gint i;
827
828   clist = GTK_CMCLIST (ctree);
829
830   if (GTK_CMCTREE_ROW (node)->row.style)
831     gtk_style_detach (GTK_CMCTREE_ROW (node)->row.style);
832   for (i = 0; i < clist->columns; i++)
833     if  (GTK_CMCTREE_ROW (node)->row.cell[i].style)
834       gtk_style_detach (GTK_CMCTREE_ROW (node)->row.cell[i].style);
835 }
836
837 static void
838 gtk_cmctree_realize (GtkWidget *widget)
839 {
840   GtkCMCTree *ctree;
841   GtkCMCList *clist;
842   GdkGCValues values;
843   GtkCMCTreeNode *node;
844   GtkCMCTreeNode *child;
845   GtkStyle *style;
846   gint i;
847
848   cm_return_if_fail (GTK_IS_CMCTREE (widget));
849
850   GTK_WIDGET_CLASS (parent_class)->realize (widget);
851
852   ctree = GTK_CMCTREE (widget);
853   clist = GTK_CMCLIST (widget);
854
855   node = GTK_CMCTREE_NODE (clist->row_list);
856   for (i = 0; i < clist->rows; i++)
857     {
858       if (GTK_CMCTREE_ROW (node)->children && !GTK_CMCTREE_ROW (node)->expanded)
859         for (child = GTK_CMCTREE_ROW (node)->children; child;
860              child = GTK_CMCTREE_ROW (child)->sibling)
861           gtk_cmctree_pre_recursive (ctree, child, ctree_attach_styles, NULL);
862       node = GTK_CMCTREE_NODE_NEXT (node);
863     }
864
865   style = gtk_widget_get_style(widget);
866
867   values.foreground = style->fg[GTK_STATE_NORMAL];
868   values.background = style->base[GTK_STATE_NORMAL];
869   values.subwindow_mode = GDK_INCLUDE_INFERIORS;
870   values.line_style = GDK_LINE_SOLID;
871   ctree->lines_gc = gdk_gc_new_with_values (GTK_CMCLIST(widget)->clist_window, 
872                                             &values,
873                                             GDK_GC_FOREGROUND |
874                                             GDK_GC_BACKGROUND |
875                                             GDK_GC_SUBWINDOW |
876                                             GDK_GC_LINE_STYLE);
877
878   if (ctree->line_style == GTK_CMCTREE_LINES_DOTTED)
879     {
880       gdk_gc_set_line_attributes (ctree->lines_gc, 1, 
881                                   GDK_LINE_ON_OFF_DASH, GDK_CAP_BUTT, GDK_JOIN_MITER);
882       gdk_gc_set_dashes (ctree->lines_gc, 0, "\1\1", 2);
883     }
884 }
885
886 static void
887 gtk_cmctree_unrealize (GtkWidget *widget)
888 {
889   GtkCMCTree *ctree;
890   GtkCMCList *clist;
891
892   cm_return_if_fail (GTK_IS_CMCTREE (widget));
893
894   GTK_WIDGET_CLASS (parent_class)->unrealize (widget);
895
896   ctree = GTK_CMCTREE (widget);
897   clist = GTK_CMCLIST (widget);
898
899   if (gtk_widget_get_realized (widget))
900     {
901       GtkCMCTreeNode *node;
902       GtkCMCTreeNode *child;
903       gint i;
904
905       node = GTK_CMCTREE_NODE (clist->row_list);
906       for (i = 0; i < clist->rows; i++)
907         {
908           if (GTK_CMCTREE_ROW (node)->children &&
909               !GTK_CMCTREE_ROW (node)->expanded)
910             for (child = GTK_CMCTREE_ROW (node)->children; child;
911                  child = GTK_CMCTREE_ROW (child)->sibling)
912               gtk_cmctree_pre_recursive(ctree, child, ctree_detach_styles, NULL);
913           node = GTK_CMCTREE_NODE_NEXT (node);
914         }
915     }
916
917   g_object_unref (ctree->lines_gc);
918 }
919
920 static gint
921 gtk_cmctree_button_press (GtkWidget      *widget,
922                         GdkEventButton *event)
923 {
924   GtkCMCTree *ctree;
925   GtkCMCList *clist;
926   gint button_actions;
927
928   cm_return_val_if_fail (GTK_IS_CMCTREE (widget), FALSE);
929   cm_return_val_if_fail (event != NULL, FALSE);
930
931   ctree = GTK_CMCTREE (widget);
932   clist = GTK_CMCLIST (widget);
933
934   button_actions = clist->button_actions[event->button - 1];
935
936   if (button_actions == GTK_CMBUTTON_IGNORED)
937     return FALSE;
938
939   if (event->window == clist->clist_window)
940     {
941       GtkCMCTreeNode *work;
942       gint x;
943       gint y;
944       gint row;
945       gint column;
946
947       x = event->x;
948       y = event->y;
949
950       if (!gtk_cmclist_get_selection_info (clist, x, y, &row, &column))
951         return FALSE;
952
953       work = GTK_CMCTREE_NODE (g_list_nth (clist->row_list, row));
954           
955       if (button_actions & GTK_CMBUTTON_EXPANDS &&
956           (GTK_CMCTREE_ROW (work)->children && !GTK_CMCTREE_ROW (work)->is_leaf  &&
957            (event->type == GDK_2BUTTON_PRESS ||
958             ctree_is_hot_spot (ctree, work, row, x, y))))
959         {
960           if (GTK_CMCTREE_ROW (work)->expanded)
961             gtk_cmctree_collapse (ctree, work);
962           else
963             gtk_cmctree_expand (ctree, work);
964
965           return TRUE;
966         }
967     }
968   
969   return GTK_WIDGET_CLASS (parent_class)->button_press_event (widget, event);
970 }
971
972 static void
973 draw_drag_highlight (GtkCMCList        *clist,
974                      GtkCMCListRow     *dest_row,
975                      gint             dest_row_number,
976                      GtkCMCListDragPos  drag_pos)
977 {
978   GtkAllocation allocation;
979   GtkCMCTree *ctree;
980   GdkPoint points[4];
981   gint level;
982   gint i;
983   gint y = 0;
984
985   cm_return_if_fail (GTK_IS_CMCTREE (clist));
986
987   ctree = GTK_CMCTREE (clist);
988   gtk_widget_get_allocation(GTK_WIDGET(ctree), &allocation);
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                            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_pixbuf (GdkWindow    *window,
1128                   GdkRectangle *clip_rectangle,
1129                   GdkGC        *fg_gc,
1130                   GdkPixbuf    *pixbuf,
1131                   gint          x,
1132                   gint          y,
1133                   gint          width,
1134                   gint          height)
1135 {
1136   gint xsrc = 0;
1137   gint ysrc = 0;
1138
1139   gdk_gc_set_clip_origin (fg_gc, x, y);
1140   if (x < clip_rectangle->x)
1141     {
1142       xsrc = clip_rectangle->x - x;
1143       width -= xsrc;
1144       x = clip_rectangle->x;
1145     }
1146   if (x + width > clip_rectangle->x + clip_rectangle->width)
1147     width = clip_rectangle->x + clip_rectangle->width - x;
1148
1149   if (y < clip_rectangle->y)
1150     {
1151       ysrc = clip_rectangle->y - y;
1152       height -= ysrc;
1153       y = clip_rectangle->y;
1154     }
1155   if (y + height > clip_rectangle->y + clip_rectangle->height)
1156     height = clip_rectangle->y + clip_rectangle->height - y;
1157
1158   if (width > 0 && height > 0)
1159     gdk_draw_pixbuf (window, fg_gc, pixbuf, xsrc, ysrc, x, y, width, height, GDK_RGB_DITHER_NONE, 0, 0);
1160
1161   gdk_gc_set_clip_origin (fg_gc, 0, 0);
1162
1163   return x + MAX (width, 0);
1164 }
1165
1166 static void
1167 get_cell_style (GtkCMCList     *clist,
1168                 GtkCMCListRow  *clist_row,
1169                 gint          state,
1170                 gint          column,
1171                 GtkStyle    **style,
1172                 GdkGC       **fg_gc,
1173                 GdkGC       **bg_gc)
1174 {
1175   GtkStyle *gtkstyle;
1176   gint fg_state;
1177
1178   gtkstyle = gtk_widget_get_style (GTK_WIDGET (clist));
1179   if ((state == GTK_STATE_NORMAL) &&
1180       (gtk_widget_get_state(GTK_WIDGET (clist)) == GTK_STATE_INSENSITIVE))
1181     fg_state = GTK_STATE_INSENSITIVE;
1182   else
1183     fg_state = state;
1184
1185   if (clist_row->cell[column].style)
1186     {
1187       if (style)
1188         *style = clist_row->cell[column].style;
1189       if (fg_gc)
1190         *fg_gc = clist_row->cell[column].style->fg_gc[fg_state];
1191       if (bg_gc) {
1192         if (state == GTK_STATE_SELECTED)
1193           *bg_gc = clist_row->cell[column].style->bg_gc[state];
1194         else
1195           *bg_gc = clist_row->cell[column].style->base_gc[state];
1196       }
1197     }
1198   else if (clist_row->style)
1199     {
1200       if (style)
1201         *style = clist_row->style;
1202       if (fg_gc)
1203         *fg_gc = clist_row->style->fg_gc[fg_state];
1204       if (bg_gc) {
1205         if (state == GTK_STATE_SELECTED)
1206           *bg_gc = clist_row->style->bg_gc[state];
1207         else
1208           *bg_gc = clist_row->style->base_gc[state];
1209       }
1210     }
1211   else
1212     {
1213       if (style)
1214         *style = gtkstyle;
1215       if (fg_gc)
1216         *fg_gc = gtkstyle->fg_gc[fg_state];
1217       if (bg_gc) {
1218         if (state == GTK_STATE_SELECTED)
1219           *bg_gc = gtkstyle->bg_gc[state];
1220         else
1221           *bg_gc = gtkstyle->base_gc[state];
1222       }
1223
1224       if (state != GTK_STATE_SELECTED)
1225         {
1226           if (fg_gc && clist_row->fg_set)
1227             *fg_gc = clist->fg_gc;
1228           if (bg_gc && clist_row->bg_set)
1229             *bg_gc = clist->bg_gc;
1230         }
1231     }
1232 }
1233
1234 static gint
1235 gtk_cmctree_draw_expander (GtkCMCTree     *ctree,
1236                          GtkCMCTreeRow  *ctree_row,
1237                          GtkStyle     *style,
1238                          GdkRectangle *clip_rectangle,
1239                          gint          x)
1240 {
1241   GtkCMCList *clist;
1242   GdkPoint points[3];
1243   gint justification_factor;
1244   gint y;
1245
1246  if (ctree->expander_style == GTK_CMCTREE_EXPANDER_NONE)
1247    return x;
1248
1249   clist = GTK_CMCLIST (ctree);
1250   if (clist->column[ctree->tree_column].justification == GTK_JUSTIFY_RIGHT)
1251     justification_factor = -1;
1252   else
1253     justification_factor = 1;
1254   y = (clip_rectangle->y + (clip_rectangle->height - PM_SIZE) / 2 -
1255        (clip_rectangle->height + 1) % 2);
1256
1257   if (!ctree_row->children)
1258     {
1259       switch (ctree->expander_style)
1260         {
1261         case GTK_CMCTREE_EXPANDER_NONE:
1262           return x;
1263         case GTK_CMCTREE_EXPANDER_TRIANGLE:
1264           return x + justification_factor * (PM_SIZE + 3);
1265         case GTK_CMCTREE_EXPANDER_SQUARE:
1266         case GTK_CMCTREE_EXPANDER_CIRCULAR:
1267           return x + justification_factor * (PM_SIZE + 1);
1268         }
1269     }
1270
1271   gdk_gc_set_clip_rectangle (style->fg_gc[GTK_STATE_NORMAL], clip_rectangle);
1272   gdk_gc_set_clip_rectangle (style->base_gc[GTK_STATE_NORMAL], clip_rectangle);
1273
1274   switch (ctree->expander_style)
1275     {
1276     case GTK_CMCTREE_EXPANDER_NONE:
1277       break;
1278     case GTK_CMCTREE_EXPANDER_TRIANGLE:
1279       if (ctree_row->expanded)
1280         {
1281           points[0].x = x;
1282           points[0].y = y + (PM_SIZE + 2) / 6;
1283           points[1].x = points[0].x + justification_factor * (PM_SIZE + 2);
1284           points[1].y = points[0].y;
1285           points[2].x = (points[0].x +
1286                          justification_factor * (PM_SIZE + 2) / 2);
1287           points[2].y = y + 2 * (PM_SIZE + 2) / 3;
1288         }
1289       else
1290         {
1291           points[0].x = x + justification_factor * ((PM_SIZE + 2) / 6 + 2);
1292           points[0].y = y - 1;
1293           points[1].x = points[0].x;
1294           points[1].y = points[0].y + (PM_SIZE + 2);
1295           points[2].x = (points[0].x +
1296                          justification_factor * (2 * (PM_SIZE + 2) / 3 - 1));
1297           points[2].y = points[0].y + (PM_SIZE + 2) / 2;
1298         }
1299
1300       gdk_draw_polygon (clist->clist_window, style->base_gc[GTK_STATE_NORMAL],
1301                         TRUE, points, 3);
1302       gdk_draw_polygon (clist->clist_window, style->fg_gc[GTK_STATE_NORMAL],
1303                         FALSE, points, 3);
1304
1305       x += justification_factor * (PM_SIZE + 3);
1306       break;
1307     case GTK_CMCTREE_EXPANDER_SQUARE:
1308     case GTK_CMCTREE_EXPANDER_CIRCULAR:
1309       if (justification_factor == -1)
1310         x += justification_factor * (PM_SIZE + 1);
1311
1312       if (ctree->expander_style == GTK_CMCTREE_EXPANDER_CIRCULAR)
1313         {
1314           gdk_draw_arc (clist->clist_window, style->base_gc[GTK_STATE_NORMAL],
1315                         TRUE, x, y, PM_SIZE, PM_SIZE, 0, 360 * 64);
1316           gdk_draw_arc (clist->clist_window, style->fg_gc[GTK_STATE_NORMAL],
1317                         FALSE, x, y, PM_SIZE, PM_SIZE, 0, 360 * 64);
1318         }
1319       else
1320         {
1321           gdk_draw_rectangle (clist->clist_window,
1322                               style->base_gc[GTK_STATE_NORMAL], TRUE,
1323                               x, y, PM_SIZE, PM_SIZE);
1324           gdk_draw_rectangle (clist->clist_window,
1325                               style->fg_gc[GTK_STATE_NORMAL], FALSE,
1326                               x, y, PM_SIZE, PM_SIZE);
1327         }
1328
1329       gdk_draw_line (clist->clist_window, style->fg_gc[GTK_STATE_NORMAL], 
1330                      x + 2, y + PM_SIZE / 2, x + PM_SIZE - 2, y + PM_SIZE / 2);
1331
1332       if (!ctree_row->expanded)
1333         gdk_draw_line (clist->clist_window, style->fg_gc[GTK_STATE_NORMAL],
1334                        x + PM_SIZE / 2, y + 2,
1335                        x + PM_SIZE / 2, y + PM_SIZE - 2);
1336
1337       if (justification_factor == 1)
1338         x += justification_factor * (PM_SIZE + 1);
1339       break;
1340     }
1341
1342   gdk_gc_set_clip_rectangle (style->fg_gc[GTK_STATE_NORMAL], NULL);
1343   gdk_gc_set_clip_rectangle (style->base_gc[GTK_STATE_NORMAL], NULL);
1344
1345   return x;
1346 }
1347
1348
1349 static gint
1350 gtk_cmctree_draw_lines (GtkCMCTree     *ctree,
1351                       GtkCMCTreeRow  *ctree_row,
1352                       gint          row,
1353                       gint          column,
1354                       gint          state,
1355                       GdkRectangle *clip_rectangle,
1356                       GdkRectangle *cell_rectangle,
1357                       GdkRectangle *crect,
1358                       GdkRectangle *area,
1359                       GtkStyle     *style)
1360 {
1361   GtkCMCList *clist;
1362   GtkCMCTreeNode *node;
1363   GtkCMCTreeNode *parent;
1364   GdkRectangle tree_rectangle;
1365   GdkRectangle tc_rectangle;
1366   GdkGC *bg_gc;
1367   gint offset;
1368   gint offset_x;
1369   gint offset_y;
1370   gint xcenter;
1371   gint ycenter;
1372   gint next_level;
1373   gint column_right;
1374   gint column_left;
1375   gint justify_right;
1376   gint justification_factor;
1377   
1378   clist = GTK_CMCLIST (ctree);
1379   ycenter = clip_rectangle->y + (clip_rectangle->height / 2);
1380   justify_right = (clist->column[column].justification == GTK_JUSTIFY_RIGHT);
1381
1382   if (justify_right)
1383     {
1384       offset = (clip_rectangle->x + clip_rectangle->width - 1 -
1385                 ctree->tree_indent * (ctree_row->level - 1));
1386       justification_factor = -1;
1387     }
1388   else
1389     {
1390       offset = clip_rectangle->x + ctree->tree_indent * (ctree_row->level - 1);
1391       justification_factor = 1;
1392     }
1393
1394   switch (ctree->line_style)
1395     {
1396     case GTK_CMCTREE_LINES_NONE:
1397       break;
1398     case GTK_CMCTREE_LINES_TABBED:
1399       xcenter = offset + justification_factor * TAB_SIZE;
1400
1401       column_right = (COLUMN_LEFT_XPIXEL (clist, ctree->tree_column) +
1402                       clist->column[ctree->tree_column].area.width +
1403                       COLUMN_INSET);
1404       column_left = (COLUMN_LEFT_XPIXEL (clist, ctree->tree_column) -
1405                      COLUMN_INSET - CELL_SPACING);
1406
1407       if (area)
1408         {
1409           tree_rectangle.y = crect->y;
1410           tree_rectangle.height = crect->height;
1411
1412           if (justify_right)
1413             {
1414               tree_rectangle.x = xcenter;
1415               tree_rectangle.width = column_right - xcenter;
1416             }
1417           else
1418             {
1419               tree_rectangle.x = column_left;
1420               tree_rectangle.width = xcenter - column_left;
1421             }
1422
1423           if (!gdk_rectangle_intersect (area, &tree_rectangle, &tc_rectangle))
1424             {
1425               offset += justification_factor * 3;
1426               break;
1427             }
1428         }
1429
1430       gdk_gc_set_clip_rectangle (ctree->lines_gc, crect);
1431
1432       next_level = ctree_row->level;
1433
1434       if (!ctree_row->sibling || (ctree_row->children && ctree_row->expanded))
1435         {
1436           node = gtk_cmctree_find_node_ptr (ctree, ctree_row);
1437           if (GTK_CMCTREE_NODE_NEXT (node))
1438             next_level = GTK_CMCTREE_ROW (GTK_CMCTREE_NODE_NEXT (node))->level;
1439           else
1440             next_level = 0;
1441         }
1442
1443       if (ctree->tree_indent > 0)
1444         {
1445           node = ctree_row->parent;
1446           while (node)
1447             {
1448               xcenter -= (justification_factor * ctree->tree_indent);
1449
1450               if ((justify_right && xcenter < column_left) ||
1451                   (!justify_right && xcenter > column_right))
1452                 {
1453                   node = GTK_CMCTREE_ROW (node)->parent;
1454                   continue;
1455                 }
1456
1457               tree_rectangle.y = cell_rectangle->y;
1458               tree_rectangle.height = cell_rectangle->height;
1459               if (justify_right)
1460                 {
1461                   tree_rectangle.x = MAX (xcenter - ctree->tree_indent + 1,
1462                                           column_left);
1463                   tree_rectangle.width = MIN (xcenter - column_left,
1464                                               ctree->tree_indent);
1465                 }
1466               else
1467                 {
1468                   tree_rectangle.x = xcenter;
1469                   tree_rectangle.width = MIN (column_right - xcenter,
1470                                               ctree->tree_indent);
1471                 }
1472
1473               if (!area || gdk_rectangle_intersect (area, &tree_rectangle,
1474                                                     &tc_rectangle))
1475                 {
1476                   get_cell_style (clist, &GTK_CMCTREE_ROW (node)->row,
1477                                   state, column, NULL, NULL, &bg_gc);
1478
1479                   if (bg_gc == clist->bg_gc)
1480                     gdk_gc_set_foreground
1481                       (clist->bg_gc, &GTK_CMCTREE_ROW (node)->row.background);
1482
1483                   if (!area)
1484                     gdk_draw_rectangle (clist->clist_window, bg_gc, TRUE,
1485                                         tree_rectangle.x,
1486                                         tree_rectangle.y,
1487                                         tree_rectangle.width,
1488                                         tree_rectangle.height);
1489                   else 
1490                     gdk_draw_rectangle (clist->clist_window, bg_gc, TRUE,
1491                                         tc_rectangle.x,
1492                                         tc_rectangle.y,
1493                                         tc_rectangle.width,
1494                                         tc_rectangle.height);
1495                 }
1496               if (next_level > GTK_CMCTREE_ROW (node)->level)
1497                 gdk_draw_line (clist->clist_window, ctree->lines_gc,
1498                                xcenter, crect->y,
1499                                xcenter, crect->y + crect->height);
1500               else
1501                 {
1502                   gint width;
1503
1504                   offset_x = MIN (ctree->tree_indent, 2 * TAB_SIZE);
1505                   width = offset_x / 2 + offset_x % 2;
1506
1507                   parent = GTK_CMCTREE_ROW (node)->parent;
1508
1509                   tree_rectangle.y = ycenter;
1510                   tree_rectangle.height = (cell_rectangle->y - ycenter +
1511                                            cell_rectangle->height);
1512
1513                   if (justify_right)
1514                     {
1515                       tree_rectangle.x = MAX(xcenter + 1 - width, column_left);
1516                       tree_rectangle.width = MIN (xcenter + 1 - column_left,
1517                                                   width);
1518                     }
1519                   else
1520                     {
1521                       tree_rectangle.x = xcenter;
1522                       tree_rectangle.width = MIN (column_right - xcenter,
1523                                                   width);
1524                     }
1525
1526                   if (!area ||
1527                       gdk_rectangle_intersect (area, &tree_rectangle,
1528                                                &tc_rectangle))
1529                     {
1530                       if (parent)
1531                         {
1532                           get_cell_style (clist, &GTK_CMCTREE_ROW (parent)->row,
1533                                           state, column, NULL, NULL, &bg_gc);
1534                           if (bg_gc == clist->bg_gc)
1535                             gdk_gc_set_foreground
1536                               (clist->bg_gc,
1537                                &GTK_CMCTREE_ROW (parent)->row.background);
1538                         }
1539                       else if (state == GTK_STATE_SELECTED)
1540                         bg_gc = style->base_gc[state];
1541                       else
1542                         bg_gc = gtk_widget_get_style (GTK_WIDGET (clist))->base_gc[state];
1543
1544                       if (!area)
1545                         gdk_draw_rectangle (clist->clist_window, bg_gc, TRUE,
1546                                             tree_rectangle.x,
1547                                             tree_rectangle.y,
1548                                             tree_rectangle.width,
1549                                             tree_rectangle.height);
1550                       else
1551                         gdk_draw_rectangle (clist->clist_window,
1552                                             bg_gc, TRUE,
1553                                             tc_rectangle.x,
1554                                             tc_rectangle.y,
1555                                             tc_rectangle.width,
1556                                             tc_rectangle.height);
1557                     }
1558
1559                   get_cell_style (clist, &GTK_CMCTREE_ROW (node)->row,
1560                                   state, column, NULL, NULL, &bg_gc);
1561                   if (bg_gc == clist->bg_gc)
1562                     gdk_gc_set_foreground
1563                       (clist->bg_gc, &GTK_CMCTREE_ROW (node)->row.background);
1564
1565                   gdk_gc_set_clip_rectangle (bg_gc, crect);
1566                   gdk_draw_arc (clist->clist_window, bg_gc, TRUE,
1567                                 xcenter - (justify_right * offset_x),
1568                                 cell_rectangle->y,
1569                                 offset_x, clist->row_height,
1570                                 (180 + (justify_right * 90)) * 64, 90 * 64);
1571                   gdk_gc_set_clip_rectangle (bg_gc, NULL);
1572
1573                   gdk_draw_line (clist->clist_window, ctree->lines_gc, 
1574                                  xcenter, cell_rectangle->y, xcenter, ycenter);
1575
1576                   if (justify_right)
1577                     gdk_draw_arc (clist->clist_window, ctree->lines_gc, FALSE,
1578                                   xcenter - offset_x, cell_rectangle->y,
1579                                   offset_x, clist->row_height,
1580                                   270 * 64, 90 * 64);
1581                   else
1582                     gdk_draw_arc (clist->clist_window, ctree->lines_gc, FALSE,
1583                                   xcenter, cell_rectangle->y,
1584                                   offset_x, clist->row_height,
1585                                   180 * 64, 90 * 64);
1586                 }
1587               node = GTK_CMCTREE_ROW (node)->parent;
1588             }
1589         }
1590
1591       if (state != GTK_STATE_SELECTED)
1592         {
1593           tree_rectangle.y = clip_rectangle->y;
1594           tree_rectangle.height = clip_rectangle->height;
1595           tree_rectangle.width = COLUMN_INSET + CELL_SPACING +
1596             MIN (clist->column[ctree->tree_column].area.width + COLUMN_INSET,
1597                  TAB_SIZE);
1598
1599           if (justify_right)
1600             tree_rectangle.x = MAX (xcenter + 1, column_left);
1601           else
1602             tree_rectangle.x = column_left;
1603
1604           if (!area)
1605             gdk_draw_rectangle (clist->clist_window,
1606                                 gtk_widget_get_style (GTK_WIDGET
1607                                 (ctree))->base_gc[GTK_STATE_NORMAL],
1608                                 TRUE,
1609                                 tree_rectangle.x,
1610                                 tree_rectangle.y,
1611                                 tree_rectangle.width,
1612                                 tree_rectangle.height);
1613           else if (gdk_rectangle_intersect (area, &tree_rectangle,
1614                                             &tc_rectangle))
1615             gdk_draw_rectangle (clist->clist_window,
1616                                 gtk_widget_get_style (GTK_WIDGET
1617                                 (ctree))->base_gc[GTK_STATE_NORMAL],
1618                                 TRUE,
1619                                 tc_rectangle.x,
1620                                 tc_rectangle.y,
1621                                 tc_rectangle.width,
1622                                 tc_rectangle.height);
1623         }
1624
1625       xcenter = offset + (justification_factor * ctree->tree_indent / 2);
1626
1627       get_cell_style (clist, &ctree_row->row, state, column, NULL, NULL,
1628                       &bg_gc);
1629       if (bg_gc == clist->bg_gc)
1630         gdk_gc_set_foreground (clist->bg_gc, &ctree_row->row.background);
1631
1632       gdk_gc_set_clip_rectangle (bg_gc, crect);
1633       if (ctree_row->is_leaf)
1634         {
1635           GdkPoint points[6];
1636
1637           points[0].x = offset + justification_factor * TAB_SIZE;
1638           points[0].y = cell_rectangle->y;
1639
1640           points[1].x = points[0].x - justification_factor * 4;
1641           points[1].y = points[0].y;
1642
1643           points[2].x = points[1].x - justification_factor * 2;
1644           points[2].y = points[1].y + 3;
1645
1646           points[3].x = points[2].x;
1647           points[3].y = points[2].y + clist->row_height - 5;
1648
1649           points[4].x = points[3].x + justification_factor * 2;
1650           points[4].y = points[3].y + 3;
1651
1652           points[5].x = points[4].x + justification_factor * 4;
1653           points[5].y = points[4].y;
1654
1655           gdk_draw_polygon (clist->clist_window, bg_gc, TRUE, points, 6);
1656           gdk_draw_lines (clist->clist_window, ctree->lines_gc, points, 6);
1657         }
1658       else 
1659         {
1660           gdk_draw_arc (clist->clist_window, bg_gc, TRUE,
1661                         offset - (justify_right * 2 * TAB_SIZE),
1662                         cell_rectangle->y,
1663                         2 * TAB_SIZE, clist->row_height,
1664                         (90 + (180 * justify_right)) * 64, 180 * 64);
1665           gdk_draw_arc (clist->clist_window, ctree->lines_gc, FALSE,
1666                         offset - (justify_right * 2 * TAB_SIZE),
1667                         cell_rectangle->y,
1668                         2 * TAB_SIZE, clist->row_height,
1669                         (90 + (180 * justify_right)) * 64, 180 * 64);
1670         }
1671       gdk_gc_set_clip_rectangle (bg_gc, NULL);
1672       gdk_gc_set_clip_rectangle (ctree->lines_gc, NULL);
1673
1674       offset += justification_factor * 3;
1675       break;
1676     default:
1677       xcenter = offset + justification_factor * PM_SIZE / 2;
1678
1679       if (area)
1680         {
1681           tree_rectangle.y = crect->y;
1682           tree_rectangle.height = crect->height;
1683
1684           if (justify_right)
1685             {
1686               tree_rectangle.x = xcenter - PM_SIZE / 2 - 2;
1687               tree_rectangle.width = (clip_rectangle->x +
1688                                       clip_rectangle->width -tree_rectangle.x);
1689             }
1690           else
1691             {
1692               tree_rectangle.x = clip_rectangle->x + PM_SIZE / 2;
1693               tree_rectangle.width = (xcenter + PM_SIZE / 2 + 2 -
1694                                       clip_rectangle->x);
1695             }
1696
1697           if (!gdk_rectangle_intersect (area, &tree_rectangle, &tc_rectangle))
1698             break;
1699         }
1700
1701       offset_x = 1;
1702       offset_y = 0;
1703       if (ctree->line_style == GTK_CMCTREE_LINES_DOTTED)
1704         {
1705           offset_x += abs((clip_rectangle->x + clist->hoffset) % 2);
1706           offset_y  = abs((cell_rectangle->y + clist->voffset) % 2);
1707         }
1708
1709       clip_rectangle->y--;
1710       clip_rectangle->height++;
1711       gdk_gc_set_clip_rectangle (ctree->lines_gc, clip_rectangle);
1712       gdk_draw_line (clist->clist_window, ctree->lines_gc,
1713                      xcenter,
1714                      (ctree->show_stub || clist->row_list->data != ctree_row) ?
1715                      cell_rectangle->y + offset_y : ycenter,
1716                      xcenter,
1717                      (ctree_row->sibling) ? crect->y +crect->height : ycenter);
1718
1719       gdk_draw_line (clist->clist_window, ctree->lines_gc,
1720                      xcenter + (justification_factor * offset_x), ycenter,
1721                      xcenter + (justification_factor * (PM_SIZE / 2 + 2)),
1722                      ycenter);
1723
1724       node = ctree_row->parent;
1725       while (node)
1726         {
1727           xcenter -= (justification_factor * ctree->tree_indent);
1728
1729           if (GTK_CMCTREE_ROW (node)->sibling)
1730             gdk_draw_line (clist->clist_window, ctree->lines_gc, 
1731                            xcenter, cell_rectangle->y + offset_y,
1732                            xcenter, crect->y + crect->height);
1733           node = GTK_CMCTREE_ROW (node)->parent;
1734         }
1735       gdk_gc_set_clip_rectangle (ctree->lines_gc, NULL);
1736       clip_rectangle->y++;
1737       clip_rectangle->height--;
1738       break;
1739     }
1740   return offset;
1741 }
1742
1743 static void
1744 draw_row (GtkCMCList     *clist,
1745           GdkRectangle *area,
1746           gint          row,
1747           GtkCMCListRow  *clist_row)
1748 {
1749   GtkStyle *style;
1750   GtkWidget *widget;
1751   GtkCMCTree  *ctree;
1752   GdkRectangle *crect;
1753   GdkRectangle row_rectangle;
1754   GdkRectangle cell_rectangle; 
1755   GdkRectangle clip_rectangle;
1756   GdkRectangle intersect_rectangle;
1757   gint last_column;
1758   gint column_left = 0;
1759   gint column_right = 0;
1760   gint offset = 0;
1761   gint state;
1762   gint i;
1763
1764   cm_return_if_fail (clist != NULL);
1765
1766   /* bail now if we arn't drawable yet */
1767   if (!gtk_widget_is_drawable (GTK_WIDGET(clist)) || row < 0 || row >= clist->rows)
1768     return;
1769
1770   widget = GTK_WIDGET (clist);
1771   ctree  = GTK_CMCTREE  (clist);
1772
1773   /* if the function is passed the pointer to the row instead of null,
1774    * it avoids this expensive lookup */
1775   if (!clist_row)
1776     clist_row = (g_list_nth (clist->row_list, row))->data;
1777
1778   /* rectangle of the entire row */
1779   row_rectangle.x = 0;
1780   row_rectangle.y = ROW_TOP_YPIXEL (clist, row);
1781   row_rectangle.width = clist->clist_window_width;
1782   row_rectangle.height = clist->row_height;
1783
1784   /* rectangle of the cell spacing above the row */
1785   cell_rectangle.x = 0;
1786   cell_rectangle.y = row_rectangle.y - CELL_SPACING;
1787   cell_rectangle.width = row_rectangle.width;
1788   cell_rectangle.height = CELL_SPACING;
1789
1790   /* rectangle used to clip drawing operations, its y and height
1791    * positions only need to be set once, so we set them once here. 
1792    * the x and width are set withing the drawing loop below once per
1793    * column */
1794   clip_rectangle.y = row_rectangle.y;
1795   clip_rectangle.height = row_rectangle.height;
1796
1797   if (clist_row->state == GTK_STATE_NORMAL)
1798     {
1799       if (clist_row->fg_set)
1800         gdk_gc_set_foreground (clist->fg_gc, &clist_row->foreground);
1801       if (clist_row->bg_set)
1802         gdk_gc_set_foreground (clist->bg_gc, &clist_row->background);
1803     }
1804   
1805   state = clist_row->state;
1806
1807   style = gtk_widget_get_style (widget);
1808   gdk_gc_set_foreground (ctree->lines_gc,
1809                          &style->fg[clist_row->state]);
1810
1811   /* draw the cell borders */
1812   if (area)
1813     {
1814       crect = &intersect_rectangle;
1815
1816       if (gdk_rectangle_intersect (area, &cell_rectangle, crect))
1817         gdk_draw_rectangle (clist->clist_window,
1818                             style->base_gc[GTK_STATE_NORMAL], TRUE,
1819                             crect->x, crect->y, crect->width, crect->height);
1820     }
1821   else
1822     {
1823       crect = &cell_rectangle;
1824
1825       gdk_draw_rectangle (clist->clist_window,
1826                           style->base_gc[GTK_STATE_NORMAL], TRUE,
1827                           crect->x, crect->y, crect->width, crect->height);
1828     }
1829
1830   /* horizontal black lines */
1831   if (ctree->line_style == GTK_CMCTREE_LINES_TABBED)
1832     { 
1833
1834       column_right = (COLUMN_LEFT_XPIXEL (clist, ctree->tree_column) +
1835                       clist->column[ctree->tree_column].area.width +
1836                       COLUMN_INSET);
1837       column_left = (COLUMN_LEFT_XPIXEL (clist, ctree->tree_column) -
1838                      COLUMN_INSET - (ctree->tree_column != 0) * CELL_SPACING);
1839
1840       switch (clist->column[ctree->tree_column].justification)
1841         {
1842         case GTK_JUSTIFY_CENTER:
1843         case GTK_JUSTIFY_FILL:
1844         case GTK_JUSTIFY_LEFT:
1845           offset = (column_left + ctree->tree_indent *
1846                     (((GtkCMCTreeRow *)clist_row)->level - 1));
1847
1848           gdk_draw_line (clist->clist_window, ctree->lines_gc, 
1849                          MIN (offset + TAB_SIZE, column_right),
1850                          cell_rectangle.y,
1851                          clist->clist_window_width, cell_rectangle.y);
1852           break;
1853         case GTK_JUSTIFY_RIGHT:
1854           offset = (column_right - 1 - ctree->tree_indent *
1855                     (((GtkCMCTreeRow *)clist_row)->level - 1));
1856
1857           gdk_draw_line (clist->clist_window, ctree->lines_gc,
1858                          -1, cell_rectangle.y,
1859                          MAX (offset - TAB_SIZE, column_left),
1860                          cell_rectangle.y);
1861           break;
1862         }
1863     }
1864
1865   /* the last row has to clear its bottom cell spacing too */
1866   if (clist_row == clist->row_list_end->data)
1867     {
1868       cell_rectangle.y += clist->row_height + CELL_SPACING;
1869
1870       if (!area || gdk_rectangle_intersect (area, &cell_rectangle, crect))
1871         {
1872           gdk_draw_rectangle (clist->clist_window,
1873                               gtk_widget_get_style (widget)->base_gc[GTK_STATE_NORMAL], TRUE,
1874                               crect->x, crect->y, crect->width, crect->height);
1875
1876           /* horizontal black lines */
1877           if (ctree->line_style == GTK_CMCTREE_LINES_TABBED)
1878             { 
1879               switch (clist->column[ctree->tree_column].justification)
1880                 {
1881                 case GTK_JUSTIFY_CENTER:
1882                 case GTK_JUSTIFY_FILL:
1883                 case GTK_JUSTIFY_LEFT:
1884                   gdk_draw_line (clist->clist_window, ctree->lines_gc, 
1885                                  MIN (column_left + TAB_SIZE + COLUMN_INSET +
1886                                       (((GtkCMCTreeRow *)clist_row)->level > 1) *
1887                                       MIN (ctree->tree_indent / 2, TAB_SIZE),
1888                                       column_right),
1889                                  cell_rectangle.y,
1890                                  clist->clist_window_width, cell_rectangle.y);
1891                   break;
1892                 case GTK_JUSTIFY_RIGHT:
1893                   gdk_draw_line (clist->clist_window, ctree->lines_gc, 
1894                                  -1, cell_rectangle.y,
1895                                  MAX (column_right - TAB_SIZE - 1 -
1896                                       COLUMN_INSET -
1897                                       (((GtkCMCTreeRow *)clist_row)->level > 1) *
1898                                       MIN (ctree->tree_indent / 2, TAB_SIZE),
1899                                       column_left - 1), cell_rectangle.y);
1900                   break;
1901                 }
1902             }
1903         }
1904     }     
1905
1906   for (last_column = clist->columns - 1;
1907        last_column >= 0 && !clist->column[last_column].visible; last_column--)
1908     ;
1909
1910   /* iterate and draw all the columns (row cells) and draw their contents */
1911   for (i = 0; i < clist->columns; i++)
1912     {
1913       GtkStyle *style;
1914       GdkGC *fg_gc; 
1915       GdkGC *bg_gc;
1916       PangoLayout *layout = NULL;
1917       PangoRectangle logical_rect;
1918
1919       gint width;
1920       gint height;
1921       gint pixbuf_width;
1922       gint string_width;
1923       gint old_offset;
1924
1925       if (!clist->column[i].visible)
1926         continue;
1927
1928       get_cell_style (clist, clist_row, state, i, &style, &fg_gc, &bg_gc);
1929
1930       /* calculate clipping region */
1931       clip_rectangle.x = clist->column[i].area.x + clist->hoffset;
1932       clip_rectangle.width = clist->column[i].area.width;
1933
1934       cell_rectangle.x = clip_rectangle.x - COLUMN_INSET - CELL_SPACING;
1935       cell_rectangle.width = (clip_rectangle.width + 2 * COLUMN_INSET +
1936                               (1 + (i == last_column)) * CELL_SPACING);
1937       cell_rectangle.y = clip_rectangle.y;
1938       cell_rectangle.height = clip_rectangle.height;
1939
1940       string_width = 0;
1941       pixbuf_width = 0;
1942       height = 0;
1943
1944       if (area && !gdk_rectangle_intersect (area, &cell_rectangle,
1945                                             &intersect_rectangle))
1946         {
1947           if (i != ctree->tree_column)
1948             continue;
1949         }
1950       else
1951         {
1952           gdk_draw_rectangle (clist->clist_window, bg_gc, TRUE,
1953                               crect->x, crect->y, crect->width, crect->height);
1954
1955
1956           layout = _gtk_cmclist_create_cell_layout (clist, clist_row, i);
1957           if (layout)
1958             {
1959               pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
1960               width = logical_rect.width;
1961             }
1962           else
1963             width = 0;
1964
1965           switch (clist_row->cell[i].type)
1966             {
1967             case GTK_CMCELL_PIXBUF:
1968               pixbuf_width = gdk_pixbuf_get_width(GTK_CMCELL_PIXBUF (clist_row->cell[i])->pixbuf);
1969               height = gdk_pixbuf_get_height(GTK_CMCELL_PIXBUF (clist_row->cell[i])->pixbuf);
1970               width += pixbuf_width;
1971               break;
1972             case GTK_CMCELL_PIXTEXT:
1973               if (GTK_CMCELL_PIXTEXT (clist_row->cell[i])->pixbuf)
1974                 {
1975                   pixbuf_width = gdk_pixbuf_get_width(GTK_CMCELL_PIXTEXT (clist_row->cell[i])->pixbuf);
1976                   height = gdk_pixbuf_get_height(GTK_CMCELL_PIXTEXT (clist_row->cell[i])->pixbuf);
1977                   width += pixbuf_width;
1978                 }
1979
1980               if (GTK_CMCELL_PIXTEXT (clist_row->cell[i])->text &&
1981                   GTK_CMCELL_PIXTEXT (clist_row->cell[i])->pixbuf)
1982                 width +=  GTK_CMCELL_PIXTEXT (clist_row->cell[i])->spacing;
1983
1984               if (i == ctree->tree_column)
1985                 width += (ctree->tree_indent *
1986                           ((GtkCMCTreeRow *)clist_row)->level);
1987               break;
1988             default:
1989               break;
1990             }
1991
1992           switch (clist->column[i].justification)
1993             {
1994             case GTK_JUSTIFY_LEFT:
1995               offset = clip_rectangle.x + clist_row->cell[i].horizontal;
1996               break;
1997             case GTK_JUSTIFY_RIGHT:
1998               offset = (clip_rectangle.x + clist_row->cell[i].horizontal +
1999                         clip_rectangle.width - width);
2000               break;
2001             case GTK_JUSTIFY_CENTER:
2002             case GTK_JUSTIFY_FILL:
2003               offset = (clip_rectangle.x + clist_row->cell[i].horizontal +
2004                         (clip_rectangle.width / 2) - (width / 2));
2005               break;
2006             };
2007
2008           if (i != ctree->tree_column)
2009             {
2010               offset += clist_row->cell[i].horizontal;
2011               switch (clist_row->cell[i].type)
2012                 {
2013                 case GTK_CMCELL_PIXBUF:
2014                   draw_cell_pixbuf
2015                     (clist->clist_window, &clip_rectangle, fg_gc,
2016                      GTK_CMCELL_PIXBUF (clist_row->cell[i])->pixbuf,
2017                      offset,
2018                      clip_rectangle.y + clist_row->cell[i].vertical +
2019                      (clip_rectangle.height - height) / 2,
2020                      pixbuf_width, height);
2021                   break;
2022                 case GTK_CMCELL_PIXTEXT:
2023                   offset = draw_cell_pixbuf
2024                     (clist->clist_window, &clip_rectangle, fg_gc,
2025                      GTK_CMCELL_PIXTEXT (clist_row->cell[i])->pixbuf,
2026                      offset,
2027                      clip_rectangle.y + clist_row->cell[i].vertical +
2028                      (clip_rectangle.height - height) / 2,
2029                      pixbuf_width, height);
2030                   offset += GTK_CMCELL_PIXTEXT (clist_row->cell[i])->spacing;
2031
2032                   /* Fall through */
2033                 case GTK_CMCELL_TEXT:
2034                   if (layout)
2035                     {
2036                       gint row_center_offset = (clist->row_height - logical_rect.height) / 2;
2037
2038                       gdk_gc_set_clip_rectangle (fg_gc, &clip_rectangle);
2039                       gdk_draw_layout (clist->clist_window, fg_gc,
2040                                        offset,
2041                                        row_rectangle.y + row_center_offset + clist_row->cell[i].vertical,
2042                                        layout);
2043                       gdk_gc_set_clip_rectangle (fg_gc, NULL);
2044                       g_object_unref (G_OBJECT (layout));
2045                     }
2046                   break;
2047                 default:
2048                   break;
2049                 }
2050               continue;
2051             }
2052         }
2053
2054       if (bg_gc == clist->bg_gc)
2055         gdk_gc_set_background (ctree->lines_gc, &clist_row->background);
2056
2057       /* draw ctree->tree_column */
2058       cell_rectangle.y -= CELL_SPACING;
2059       cell_rectangle.height += CELL_SPACING;
2060
2061       if (area && !gdk_rectangle_intersect (area, &cell_rectangle,
2062                                             &intersect_rectangle))
2063         {
2064           if (layout)
2065             g_object_unref (G_OBJECT (layout));
2066           continue;
2067         }
2068
2069       /* draw lines */
2070       offset = gtk_cmctree_draw_lines (ctree, (GtkCMCTreeRow *)clist_row, row, i,
2071                                      state, &clip_rectangle, &cell_rectangle,
2072                                      crect, area, style);
2073
2074       /* draw expander */
2075       offset = gtk_cmctree_draw_expander (ctree, (GtkCMCTreeRow *)clist_row,
2076                                         style, &clip_rectangle, offset);
2077
2078       if (clist->column[i].justification == GTK_JUSTIFY_RIGHT)
2079         offset -= ctree->tree_spacing;
2080       else
2081         offset += ctree->tree_spacing;
2082
2083       if (clist->column[i].justification == GTK_JUSTIFY_RIGHT)
2084         offset -= (pixbuf_width + clist_row->cell[i].horizontal);
2085       else
2086         offset += clist_row->cell[i].horizontal;
2087
2088       old_offset = offset;
2089       offset = draw_cell_pixbuf (clist->clist_window, &clip_rectangle, fg_gc,
2090                                  GTK_CMCELL_PIXTEXT (clist_row->cell[i])->pixbuf,
2091                                  offset, 
2092                                  clip_rectangle.y + clist_row->cell[i].vertical
2093                                  + (clip_rectangle.height - height) / 2,
2094                                  pixbuf_width, height);
2095
2096       if (layout)
2097         {
2098           gint row_center_offset = (clist->row_height - logical_rect.height) / 2;
2099           
2100           if (clist->column[i].justification == GTK_JUSTIFY_RIGHT)
2101             {
2102               offset = (old_offset - string_width);
2103               if (GTK_CMCELL_PIXTEXT (clist_row->cell[i])->pixbuf)
2104                 offset -= GTK_CMCELL_PIXTEXT (clist_row->cell[i])->spacing;
2105             }
2106           else
2107             {
2108               if (GTK_CMCELL_PIXTEXT (clist_row->cell[i])->pixbuf)
2109                 offset += GTK_CMCELL_PIXTEXT (clist_row->cell[i])->spacing;
2110             }
2111           
2112           gdk_gc_set_clip_rectangle (fg_gc, &clip_rectangle);
2113           gdk_draw_layout (clist->clist_window, fg_gc,
2114                            offset,
2115                            row_rectangle.y + row_center_offset + clist_row->cell[i].vertical,
2116                            layout);
2117
2118           g_object_unref (G_OBJECT (layout));
2119         }
2120       gdk_gc_set_clip_rectangle (fg_gc, NULL);
2121     }
2122
2123   /* draw focus rectangle */
2124   if (clist->focus_row == row &&
2125       gtk_widget_get_can_focus (widget) && gtk_widget_has_focus (widget))
2126     {
2127       if (!area)
2128         gdk_draw_rectangle (clist->clist_window, clist->xor_gc, FALSE,
2129                             row_rectangle.x, row_rectangle.y,
2130                             row_rectangle.width - 1, row_rectangle.height - 1);
2131       else if (gdk_rectangle_intersect (area, &row_rectangle,
2132                                         &intersect_rectangle))
2133         {
2134           gdk_gc_set_clip_rectangle (clist->xor_gc, &intersect_rectangle);
2135           gdk_draw_rectangle (clist->clist_window, clist->xor_gc, FALSE,
2136                               row_rectangle.x, row_rectangle.y,
2137                               row_rectangle.width - 1,
2138                               row_rectangle.height - 1);
2139           gdk_gc_set_clip_rectangle (clist->xor_gc, NULL);
2140         }
2141     }
2142 }
2143
2144 static void
2145 tree_draw_node (GtkCMCTree     *ctree, 
2146                 GtkCMCTreeNode *node)
2147 {
2148   GtkCMCList *clist;
2149   
2150   clist = GTK_CMCLIST (ctree);
2151
2152   if (CLIST_UNFROZEN (clist) && gtk_cmctree_is_viewable (ctree, node))
2153     {
2154       GtkCMCTreeNode *work;
2155       gint num = 0;
2156       
2157       work = GTK_CMCTREE_NODE (clist->row_list);
2158       while (work && work != node)
2159         {
2160           work = GTK_CMCTREE_NODE_NEXT (work);
2161           num++;
2162         }
2163       if (work && gtk_cmclist_row_is_visible (clist, num) != GTK_VISIBILITY_NONE)
2164         GTK_CMCLIST_GET_CLASS (clist)->draw_row
2165           (clist, NULL, num, GTK_CMCLIST_ROW ((GList *) node));
2166     }
2167 }
2168
2169 static GtkCMCTreeNode *
2170 gtk_cmctree_last_visible (GtkCMCTree     *ctree,
2171                         GtkCMCTreeNode *node)
2172 {
2173   GtkCMCTreeNode *work;
2174   
2175   if (!node)
2176     return NULL;
2177
2178   work = GTK_CMCTREE_ROW (node)->children;
2179
2180   if (!work || !GTK_CMCTREE_ROW (node)->expanded)
2181     return node;
2182
2183   while (GTK_CMCTREE_ROW (work)->sibling)
2184     work = GTK_CMCTREE_ROW (work)->sibling;
2185
2186   return gtk_cmctree_last_visible (ctree, work);
2187 }
2188
2189 static void
2190 gtk_cmctree_link (GtkCMCTree     *ctree,
2191                 GtkCMCTreeNode *node,
2192                 GtkCMCTreeNode *parent,
2193                 GtkCMCTreeNode *sibling,
2194                 gboolean      update_focus_row)
2195 {
2196   GtkCMCList *clist;
2197   GList *list_end;
2198   GList *list;
2199   GList *work;
2200   gboolean visible = FALSE;
2201   gint rows = 0;
2202   
2203   if (sibling)
2204     cm_return_if_fail (GTK_CMCTREE_ROW (sibling)->parent == parent);
2205   cm_return_if_fail (node != NULL);
2206   cm_return_if_fail (node != sibling);
2207   cm_return_if_fail (node != parent);
2208
2209   clist = GTK_CMCLIST (ctree);
2210
2211   if (update_focus_row && clist->selection_mode == GTK_SELECTION_MULTIPLE)
2212     {
2213       GTK_CMCLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
2214       
2215       g_list_free (clist->undo_selection);
2216       g_list_free (clist->undo_unselection);
2217       clist->undo_selection = NULL;
2218       clist->undo_unselection = NULL;
2219     }
2220
2221   for (rows = 1, list_end = (GList *)node; list_end->next;
2222        list_end = list_end->next)
2223     rows++;
2224
2225   GTK_CMCTREE_ROW (node)->parent = parent;
2226   GTK_CMCTREE_ROW (node)->sibling = sibling;
2227
2228   if (!parent || (parent && (gtk_cmctree_is_viewable (ctree, parent) &&
2229                              GTK_CMCTREE_ROW (parent)->expanded)))
2230     {
2231       visible = TRUE;
2232       clist->rows += rows;
2233     }
2234
2235   if (parent)
2236     work = (GList *)(GTK_CMCTREE_ROW (parent)->children);
2237   else
2238     work = clist->row_list;
2239
2240   if (sibling)
2241     {
2242       if (work != (GList *)sibling)
2243         {
2244           while (GTK_CMCTREE_ROW (work)->sibling != sibling)
2245             work = (GList *)(GTK_CMCTREE_ROW (work)->sibling);
2246           GTK_CMCTREE_ROW (work)->sibling = node;
2247         }
2248
2249       if (sibling == GTK_CMCTREE_NODE (clist->row_list))
2250         clist->row_list = (GList *) node;
2251       if (GTK_CMCTREE_NODE_PREV (sibling) &&
2252           GTK_CMCTREE_NODE_NEXT (GTK_CMCTREE_NODE_PREV (sibling)) == sibling)
2253         {
2254           list = (GList *)GTK_CMCTREE_NODE_PREV (sibling);
2255           list->next = (GList *)node;
2256         }
2257       
2258       list = (GList *)node;
2259       list->prev = (GList *)GTK_CMCTREE_NODE_PREV (sibling);
2260       list_end->next = (GList *)sibling;
2261       list = (GList *)sibling;
2262       list->prev = list_end;
2263       if (parent && GTK_CMCTREE_ROW (parent)->children == sibling)
2264         GTK_CMCTREE_ROW (parent)->children = node;
2265     }
2266   else
2267     {
2268       if (work)
2269         {
2270           /* find sibling */
2271           while (GTK_CMCTREE_ROW (work)->sibling)
2272             work = (GList *)(GTK_CMCTREE_ROW (work)->sibling);
2273           GTK_CMCTREE_ROW (work)->sibling = node;
2274           
2275           /* find last visible child of sibling */
2276           work = (GList *) gtk_cmctree_last_visible (ctree,
2277                                                    GTK_CMCTREE_NODE (work));
2278           
2279           list_end->next = work->next;
2280           if (work->next)
2281             list = work->next->prev = list_end;
2282           work->next = (GList *)node;
2283           list = (GList *)node;
2284           list->prev = work;
2285         }
2286       else
2287         {
2288           if (parent)
2289             {
2290               GTK_CMCTREE_ROW (parent)->children = node;
2291               list = (GList *)node;
2292               list->prev = (GList *)parent;
2293               if (GTK_CMCTREE_ROW (parent)->expanded)
2294                 {
2295                   list_end->next = (GList *)GTK_CMCTREE_NODE_NEXT (parent);
2296                   if (GTK_CMCTREE_NODE_NEXT(parent))
2297                     {
2298                       list = (GList *)GTK_CMCTREE_NODE_NEXT (parent);
2299                       list->prev = list_end;
2300                     }
2301                   list = (GList *)parent;
2302                   list->next = (GList *)node;
2303                 }
2304               else
2305                 list_end->next = NULL;
2306             }
2307           else
2308             {
2309               clist->row_list = (GList *)node;
2310               list = (GList *)node;
2311               list->prev = NULL;
2312               list_end->next = NULL;
2313             }
2314         }
2315     }
2316
2317   gtk_cmctree_pre_recursive (ctree, node, tree_update_level, NULL); 
2318
2319   if (clist->row_list_end == NULL ||
2320       clist->row_list_end->next == (GList *)node)
2321     clist->row_list_end = list_end;
2322
2323   if (visible && update_focus_row)
2324     {
2325       gint pos;
2326           
2327       pos = g_list_position (clist->row_list, (GList *)node);
2328   
2329       if (pos <= clist->focus_row)
2330         {
2331           clist->focus_row += rows;
2332           clist->undo_anchor = clist->focus_row;
2333         }
2334     }
2335 }
2336
2337 static void
2338 gtk_cmctree_unlink (GtkCMCTree     *ctree, 
2339                   GtkCMCTreeNode *node,
2340                   gboolean      update_focus_row)
2341 {
2342   GtkCMCList *clist;
2343   gint rows;
2344   gint level;
2345   gint visible;
2346   GtkCMCTreeNode *work;
2347   GtkCMCTreeNode *parent;
2348   GList *list;
2349
2350   cm_return_if_fail (GTK_IS_CMCTREE (ctree));
2351   cm_return_if_fail (node != NULL);
2352
2353   clist = GTK_CMCLIST (ctree);
2354   
2355   if (update_focus_row && clist->selection_mode == GTK_SELECTION_MULTIPLE)
2356     {
2357       GTK_CMCLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
2358       
2359       g_list_free (clist->undo_selection);
2360       g_list_free (clist->undo_unselection);
2361       clist->undo_selection = NULL;
2362       clist->undo_unselection = NULL;
2363     }
2364
2365   visible = gtk_cmctree_is_viewable (ctree, node);
2366
2367   /* clist->row_list_end unlinked ? */
2368   if (visible &&
2369       (GTK_CMCTREE_NODE_NEXT (node) == NULL ||
2370        (GTK_CMCTREE_ROW (node)->children &&
2371         gtk_cmctree_is_ancestor (ctree, node,
2372                                GTK_CMCTREE_NODE (clist->row_list_end)))))
2373     clist->row_list_end = (GList *) (GTK_CMCTREE_NODE_PREV (node));
2374
2375   /* update list */
2376   rows = 0;
2377   level = GTK_CMCTREE_ROW (node)->level;
2378   work = GTK_CMCTREE_NODE_NEXT (node);
2379   while (work && GTK_CMCTREE_ROW (work)->level > level)
2380     {
2381       work = GTK_CMCTREE_NODE_NEXT (work);
2382       rows++;
2383     }
2384
2385   if (visible)
2386     {
2387       clist->rows -= (rows + 1);
2388
2389       if (update_focus_row)
2390         {
2391           gint pos;
2392           
2393           pos = g_list_position (clist->row_list, (GList *)node);
2394           if (pos + rows < clist->focus_row)
2395             clist->focus_row -= (rows + 1);
2396           else if (pos <= clist->focus_row)
2397             {
2398               if (!GTK_CMCTREE_ROW (node)->sibling)
2399                 clist->focus_row = MAX (pos - 1, 0);
2400               else
2401                 clist->focus_row = pos;
2402               
2403               clist->focus_row = MIN (clist->focus_row, clist->rows - 1);
2404             }
2405           clist->undo_anchor = clist->focus_row;
2406         }
2407     }
2408
2409   if (work)
2410     {
2411       list = (GList *)GTK_CMCTREE_NODE_PREV (work);
2412       list->next = NULL;
2413       list = (GList *)work;
2414       list->prev = (GList *)GTK_CMCTREE_NODE_PREV (node);
2415     }
2416
2417   if (GTK_CMCTREE_NODE_PREV (node) &&
2418       GTK_CMCTREE_NODE_NEXT (GTK_CMCTREE_NODE_PREV (node)) == node)
2419     {
2420       list = (GList *)GTK_CMCTREE_NODE_PREV (node);
2421       list->next = (GList *)work;
2422     }
2423
2424   /* update tree */
2425   parent = GTK_CMCTREE_ROW (node)->parent;
2426   if (parent)
2427     {
2428       if (GTK_CMCTREE_ROW (parent)->children == node)
2429         {
2430           GTK_CMCTREE_ROW (parent)->children = GTK_CMCTREE_ROW (node)->sibling;
2431           if (!GTK_CMCTREE_ROW (parent)->children)
2432             gtk_cmctree_collapse (ctree, parent);
2433         }
2434       else
2435         {
2436           GtkCMCTreeNode *sibling;
2437
2438           sibling = GTK_CMCTREE_ROW (parent)->children;
2439           while (GTK_CMCTREE_ROW (sibling)->sibling != node)
2440             sibling = GTK_CMCTREE_ROW (sibling)->sibling;
2441           GTK_CMCTREE_ROW (sibling)->sibling = GTK_CMCTREE_ROW (node)->sibling;
2442         }
2443     }
2444   else
2445     {
2446       if (clist->row_list == (GList *)node)
2447         clist->row_list = (GList *) (GTK_CMCTREE_ROW (node)->sibling);
2448       else
2449         {
2450           GtkCMCTreeNode *sibling;
2451
2452           sibling = GTK_CMCTREE_NODE (clist->row_list);
2453           while (GTK_CMCTREE_ROW (sibling)->sibling != node)
2454             sibling = GTK_CMCTREE_ROW (sibling)->sibling;
2455           GTK_CMCTREE_ROW (sibling)->sibling = GTK_CMCTREE_ROW (node)->sibling;
2456         }
2457     }
2458 }
2459
2460 static void
2461 real_row_move (GtkCMCList *clist,
2462                gint      source_row,
2463                gint      dest_row)
2464 {
2465   GtkCMCTree *ctree;
2466   GtkCMCTreeNode *node;
2467
2468   cm_return_if_fail (GTK_IS_CMCTREE (clist));
2469
2470   if (GTK_CMCLIST_AUTO_SORT (clist))
2471     return;
2472
2473   if (source_row < 0 || source_row >= clist->rows ||
2474       dest_row   < 0 || dest_row   >= clist->rows ||
2475       source_row == dest_row)
2476     return;
2477
2478   ctree = GTK_CMCTREE (clist);
2479   node = GTK_CMCTREE_NODE (g_list_nth (clist->row_list, source_row));
2480
2481   if (source_row < dest_row)
2482     {
2483       GtkCMCTreeNode *work; 
2484
2485       dest_row++;
2486       work = GTK_CMCTREE_ROW (node)->children;
2487
2488       while (work && GTK_CMCTREE_ROW (work)->level > GTK_CMCTREE_ROW (node)->level)
2489         {
2490           work = GTK_CMCTREE_NODE_NEXT (work);
2491           dest_row++;
2492         }
2493
2494       if (dest_row > clist->rows)
2495         dest_row = clist->rows;
2496     }
2497
2498   if (dest_row < clist->rows)
2499     {
2500       GtkCMCTreeNode *sibling;
2501
2502       sibling = GTK_CMCTREE_NODE (g_list_nth (clist->row_list, dest_row));
2503       gtk_cmctree_move (ctree, node, GTK_CMCTREE_ROW (sibling)->parent, sibling);
2504     }
2505   else
2506     gtk_cmctree_move (ctree, node, NULL, NULL);
2507 }
2508
2509 static void
2510 real_tree_move (GtkCMCTree     *ctree,
2511                 GtkCMCTreeNode *node,
2512                 GtkCMCTreeNode *new_parent, 
2513                 GtkCMCTreeNode *new_sibling)
2514 {
2515   GtkCMCList *clist;
2516   GtkCMCTreeNode *work;
2517   gboolean visible = FALSE;
2518
2519   cm_return_if_fail (ctree != NULL);
2520   cm_return_if_fail (node != NULL);
2521   cm_return_if_fail (!new_sibling || 
2522                     GTK_CMCTREE_ROW (new_sibling)->parent == new_parent);
2523
2524   if (new_parent && GTK_CMCTREE_ROW (new_parent)->is_leaf)
2525     return;
2526
2527   /* new_parent != child of child */
2528   for (work = new_parent; work; work = GTK_CMCTREE_ROW (work)->parent)
2529     if (work == node)
2530       return;
2531
2532   clist = GTK_CMCLIST (ctree);
2533
2534   visible = gtk_cmctree_is_viewable (ctree, node);
2535
2536   if (clist->selection_mode == GTK_SELECTION_MULTIPLE)
2537     {
2538       GTK_CMCLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
2539       
2540       g_list_free (clist->undo_selection);
2541       g_list_free (clist->undo_unselection);
2542       clist->undo_selection = NULL;
2543       clist->undo_unselection = NULL;
2544     }
2545
2546   if (GTK_CMCLIST_AUTO_SORT (clist))
2547     {
2548       if (new_parent == GTK_CMCTREE_ROW (node)->parent)
2549         return;
2550       
2551       if (new_parent)
2552         new_sibling = GTK_CMCTREE_ROW (new_parent)->children;
2553       else
2554         new_sibling = GTK_CMCTREE_NODE (clist->row_list);
2555
2556       while (new_sibling && clist->compare
2557              (clist, GTK_CMCTREE_ROW (node), GTK_CMCTREE_ROW (new_sibling)) > 0)
2558         new_sibling = GTK_CMCTREE_ROW (new_sibling)->sibling;
2559     }
2560
2561   if (new_parent == GTK_CMCTREE_ROW (node)->parent && 
2562       new_sibling == GTK_CMCTREE_ROW (node)->sibling)
2563     return;
2564
2565   gtk_cmclist_freeze (clist);
2566
2567   work = NULL;
2568   if (gtk_cmctree_is_viewable (ctree, node))
2569     work = GTK_CMCTREE_NODE (g_list_nth (clist->row_list, clist->focus_row));
2570       
2571   gtk_cmctree_unlink (ctree, node, FALSE);
2572   gtk_cmctree_link (ctree, node, new_parent, new_sibling, FALSE);
2573   
2574   if (work)
2575     {
2576       while (work &&  !gtk_cmctree_is_viewable (ctree, work))
2577         work = GTK_CMCTREE_ROW (work)->parent;
2578       clist->focus_row = g_list_position (clist->row_list, (GList *)work);
2579       clist->undo_anchor = clist->focus_row;
2580     }
2581
2582   if (clist->column[ctree->tree_column].auto_resize &&
2583       !GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist) &&
2584       (visible || gtk_cmctree_is_viewable (ctree, node)))
2585     gtk_cmclist_set_column_width
2586       (clist, ctree->tree_column,
2587        gtk_cmclist_optimal_column_width (clist, ctree->tree_column));
2588
2589   gtk_cmclist_thaw (clist);
2590 }
2591
2592 static void
2593 change_focus_row_expansion (GtkCMCTree          *ctree,
2594                             GtkCMCTreeExpansionType action)
2595 {
2596   GtkCMCList *clist;
2597   GtkCMCTreeNode *node;
2598
2599   cm_return_if_fail (GTK_IS_CMCTREE (ctree));
2600
2601   clist = GTK_CMCLIST (ctree);
2602
2603   if (gdk_display_pointer_is_grabbed (gtk_widget_get_display (GTK_WIDGET (ctree))) && 
2604       gtk_widget_has_grab (GTK_WIDGET(ctree)))
2605     return;
2606   
2607   if (!(node =
2608         GTK_CMCTREE_NODE (g_list_nth (clist->row_list, clist->focus_row))) ||
2609       GTK_CMCTREE_ROW (node)->is_leaf || !(GTK_CMCTREE_ROW (node)->children))
2610     return;
2611
2612   switch (action)
2613     {
2614     case GTK_CMCTREE_EXPANSION_EXPAND:
2615       gtk_cmctree_expand (ctree, node);
2616       break;
2617     case GTK_CMCTREE_EXPANSION_EXPAND_RECURSIVE:
2618       gtk_cmctree_expand_recursive (ctree, node);
2619       break;
2620     case GTK_CMCTREE_EXPANSION_COLLAPSE:
2621       gtk_cmctree_collapse (ctree, node);
2622       break;
2623     case GTK_CMCTREE_EXPANSION_COLLAPSE_RECURSIVE:
2624       gtk_cmctree_collapse_recursive (ctree, node);
2625       break;
2626     case GTK_CMCTREE_EXPANSION_TOGGLE:
2627       gtk_cmctree_toggle_expansion (ctree, node);
2628       break;
2629     case GTK_CMCTREE_EXPANSION_TOGGLE_RECURSIVE:
2630       gtk_cmctree_toggle_expansion_recursive (ctree, node);
2631       break;
2632     }
2633 }
2634
2635 static void 
2636 real_tree_expand (GtkCMCTree     *ctree,
2637                   GtkCMCTreeNode *node)
2638 {
2639   GtkCMCList *clist;
2640   GtkCMCTreeNode *work;
2641   GtkRequisition requisition;
2642   gboolean visible;
2643
2644   cm_return_if_fail (GTK_IS_CMCTREE (ctree));
2645
2646   if (!node || GTK_CMCTREE_ROW (node)->expanded || GTK_CMCTREE_ROW (node)->is_leaf)
2647     return;
2648
2649   clist = GTK_CMCLIST (ctree);
2650   
2651   GTK_CMCLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
2652
2653   GTK_CMCTREE_ROW (node)->expanded = TRUE;
2654
2655   visible = gtk_cmctree_is_viewable (ctree, node);
2656   /* get cell width if tree_column is auto resized */
2657   if (visible && clist->column[ctree->tree_column].auto_resize &&
2658       !GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist))
2659     GTK_CMCLIST_GET_CLASS (clist)->cell_size_request
2660       (clist, &GTK_CMCTREE_ROW (node)->row, ctree->tree_column, &requisition);
2661
2662   /* unref/unset closed pixbuf */
2663   if (GTK_CMCELL_PIXTEXT 
2664       (GTK_CMCTREE_ROW (node)->row.cell[ctree->tree_column])->pixbuf)
2665     {
2666       g_object_unref
2667         (GTK_CMCELL_PIXTEXT
2668          (GTK_CMCTREE_ROW (node)->row.cell[ctree->tree_column])->pixbuf);
2669       
2670       GTK_CMCELL_PIXTEXT
2671         (GTK_CMCTREE_ROW (node)->row.cell[ctree->tree_column])->pixbuf = NULL;
2672     }
2673
2674   /* set/ref opened pixbuf */
2675   if (GTK_CMCTREE_ROW (node)->pixbuf_opened)
2676     {
2677       GTK_CMCELL_PIXTEXT 
2678         (GTK_CMCTREE_ROW (node)->row.cell[ctree->tree_column])->pixbuf = 
2679         g_object_ref (GTK_CMCTREE_ROW (node)->pixbuf_opened);
2680     }
2681
2682
2683   work = GTK_CMCTREE_ROW (node)->children;
2684   if (work)
2685     {
2686       GList *list = (GList *)work;
2687       gint *cell_width = NULL;
2688       gint tmp = 0;
2689       gint row;
2690       gint i;
2691       
2692       if (visible && !GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist))
2693         {
2694           cell_width = g_new0 (gint, clist->columns);
2695           if (clist->column[ctree->tree_column].auto_resize)
2696               cell_width[ctree->tree_column] = requisition.width;
2697
2698           while (work)
2699             {
2700               /* search maximum cell widths of auto_resize columns */
2701               for (i = 0; i < clist->columns; i++)
2702                 if (clist->column[i].auto_resize)
2703                   {
2704                     GTK_CMCLIST_GET_CLASS (clist)->cell_size_request
2705                       (clist, &GTK_CMCTREE_ROW (work)->row, i, &requisition);
2706                     cell_width[i] = MAX (requisition.width, cell_width[i]);
2707                   }
2708
2709               list = (GList *)work;
2710               work = GTK_CMCTREE_NODE_NEXT (work);
2711               tmp++;
2712             }
2713         }
2714       else
2715         while (work)
2716           {
2717             list = (GList *)work;
2718             work = GTK_CMCTREE_NODE_NEXT (work);
2719             tmp++;
2720           }
2721
2722       list->next = (GList *)GTK_CMCTREE_NODE_NEXT (node);
2723
2724       if (GTK_CMCTREE_NODE_NEXT (node))
2725         {
2726           GList *tmp_list;
2727
2728           tmp_list = (GList *)GTK_CMCTREE_NODE_NEXT (node);
2729           tmp_list->prev = list;
2730         }
2731       else
2732         clist->row_list_end = list;
2733
2734       list = (GList *)node;
2735       list->next = (GList *)(GTK_CMCTREE_ROW (node)->children);
2736
2737       if (visible && !GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist))
2738         {
2739           /* resize auto_resize columns if needed */
2740           for (i = 0; i < clist->columns; i++)
2741             if (clist->column[i].auto_resize &&
2742                 cell_width[i] > clist->column[i].width)
2743               gtk_cmclist_set_column_width (clist, i, cell_width[i]);
2744           g_free (cell_width);
2745
2746           /* update focus_row position */
2747           row = g_list_position (clist->row_list, (GList *)node);
2748           if (row < clist->focus_row)
2749             clist->focus_row += tmp;
2750
2751           clist->rows += tmp;
2752           CLIST_REFRESH (clist);
2753         }
2754     }
2755   else if (visible && clist->column[ctree->tree_column].auto_resize)
2756     /* resize tree_column if needed */
2757     column_auto_resize (clist, &GTK_CMCTREE_ROW (node)->row, ctree->tree_column,
2758                         requisition.width);
2759 }
2760
2761 static void 
2762 real_tree_collapse (GtkCMCTree     *ctree,
2763                     GtkCMCTreeNode *node)
2764 {
2765   GtkCMCList *clist;
2766   GtkCMCTreeNode *work;
2767   GtkRequisition requisition;
2768   gboolean visible;
2769   gint level;
2770
2771   cm_return_if_fail (GTK_IS_CMCTREE (ctree));
2772
2773   if (!node || !GTK_CMCTREE_ROW (node)->expanded ||
2774       GTK_CMCTREE_ROW (node)->is_leaf)
2775     return;
2776
2777   clist = GTK_CMCLIST (ctree);
2778
2779   GTK_CMCLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
2780   
2781   GTK_CMCTREE_ROW (node)->expanded = FALSE;
2782   level = GTK_CMCTREE_ROW (node)->level;
2783
2784   visible = gtk_cmctree_is_viewable (ctree, node);
2785   /* get cell width if tree_column is auto resized */
2786   if (visible && clist->column[ctree->tree_column].auto_resize &&
2787       !GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist))
2788     GTK_CMCLIST_GET_CLASS (clist)->cell_size_request
2789       (clist, &GTK_CMCTREE_ROW (node)->row, ctree->tree_column, &requisition);
2790
2791   /* unref/unset opened pixbuf */
2792   if (GTK_CMCELL_PIXTEXT 
2793       (GTK_CMCTREE_ROW (node)->row.cell[ctree->tree_column])->pixbuf)
2794     {
2795       g_object_unref
2796         (GTK_CMCELL_PIXTEXT
2797          (GTK_CMCTREE_ROW (node)->row.cell[ctree->tree_column])->pixbuf);
2798       
2799       GTK_CMCELL_PIXTEXT
2800         (GTK_CMCTREE_ROW (node)->row.cell[ctree->tree_column])->pixbuf = NULL;
2801     }
2802
2803   /* set/ref closed pixbuf */
2804   if (GTK_CMCTREE_ROW (node)->pixbuf_closed)
2805     {
2806       GTK_CMCELL_PIXTEXT 
2807         (GTK_CMCTREE_ROW (node)->row.cell[ctree->tree_column])->pixbuf = 
2808         g_object_ref (GTK_CMCTREE_ROW (node)->pixbuf_closed);
2809     }
2810
2811   work = GTK_CMCTREE_ROW (node)->children;
2812   if (work)
2813     {
2814       gint tmp = 0;
2815       gint row;
2816       GList *list;
2817
2818       while (work && GTK_CMCTREE_ROW (work)->level > level)
2819         {
2820           work = GTK_CMCTREE_NODE_NEXT (work);
2821           tmp++;
2822         }
2823
2824       if (work)
2825         {
2826           list = (GList *)node;
2827           list->next = (GList *)work;
2828           list = (GList *)GTK_CMCTREE_NODE_PREV (work);
2829           list->next = NULL;
2830           list = (GList *)work;
2831           list->prev = (GList *)node;
2832         }
2833       else
2834         {
2835           list = (GList *)node;
2836           list->next = NULL;
2837           clist->row_list_end = (GList *)node;
2838         }
2839
2840       if (visible)
2841         {
2842           /* resize auto_resize columns if needed */
2843           auto_resize_columns (clist);
2844
2845           row = g_list_position (clist->row_list, (GList *)node);
2846           if (row < clist->focus_row)
2847             clist->focus_row -= tmp;
2848           clist->rows -= tmp;
2849           CLIST_REFRESH (clist);
2850         }
2851     }
2852   else if (visible && clist->column[ctree->tree_column].auto_resize &&
2853            !GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist))
2854     /* resize tree_column if needed */
2855     column_auto_resize (clist, &GTK_CMCTREE_ROW (node)->row, ctree->tree_column,
2856                         requisition.width);
2857     
2858 }
2859
2860 static void
2861 column_auto_resize (GtkCMCList    *clist,
2862                     GtkCMCListRow *clist_row,
2863                     gint         column,
2864                     gint         old_width)
2865 {
2866   /* resize column if needed for auto_resize */
2867   GtkRequisition requisition;
2868
2869   if (!clist->column[column].auto_resize ||
2870       GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist))
2871     return;
2872
2873   if (clist_row)
2874     GTK_CMCLIST_GET_CLASS (clist)->cell_size_request (clist, clist_row,
2875                                                    column, &requisition);
2876   else
2877     requisition.width = 0;
2878
2879   if (requisition.width > clist->column[column].width)
2880     gtk_cmclist_set_column_width (clist, column, requisition.width);
2881   else if (requisition.width < old_width &&
2882            old_width == clist->column[column].width)
2883     {
2884       GList *list;
2885       gint new_width;
2886
2887       /* run a "gtk_cmclist_optimal_column_width" but break, if
2888        * the column doesn't shrink */
2889       if (GTK_CMCLIST_SHOW_TITLES (clist) && clist->column[column].button)
2890         {
2891         GtkRequisition req;
2892         gtk_widget_get_requisition (clist->column[column].button, &req);
2893         new_width = (req.width -
2894                      (CELL_SPACING + (2 * COLUMN_INSET)));
2895         }
2896       else
2897         new_width = 0;
2898
2899       for (list = clist->row_list; list; list = list->next)
2900         {
2901           GTK_CMCLIST_GET_CLASS (clist)->cell_size_request
2902             (clist, GTK_CMCLIST_ROW (list), column, &requisition);
2903           new_width = MAX (new_width, requisition.width);
2904           if (new_width == clist->column[column].width)
2905             break;
2906         }
2907       if (new_width < clist->column[column].width)
2908         gtk_cmclist_set_column_width (clist, column, new_width);
2909     }
2910 }
2911
2912 static void
2913 auto_resize_columns (GtkCMCList *clist)
2914 {
2915   gint i;
2916
2917   if (GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist))
2918     return;
2919
2920   for (i = 0; i < clist->columns; i++)
2921     column_auto_resize (clist, NULL, i, clist->column[i].width);
2922 }
2923
2924 static void
2925 cell_size_request (GtkCMCList       *clist,
2926                    GtkCMCListRow    *clist_row,
2927                    gint            column,
2928                    GtkRequisition *requisition)
2929 {
2930   GtkCMCTree *ctree;
2931   gint width;
2932   gint height;
2933   PangoLayout *layout;
2934   PangoRectangle logical_rect;
2935
2936   cm_return_if_fail (GTK_IS_CMCTREE (clist));
2937   cm_return_if_fail (requisition != NULL);
2938
2939   ctree = GTK_CMCTREE (clist);
2940
2941   layout = _gtk_cmclist_create_cell_layout (clist, clist_row, column);
2942   if (layout)
2943     {
2944       pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
2945
2946       requisition->width = logical_rect.width;
2947       requisition->height = logical_rect.height;
2948       
2949       g_object_unref (G_OBJECT (layout));
2950     }
2951   else
2952     {
2953       requisition->width  = 0;
2954       requisition->height = 0;
2955     }
2956
2957   switch (clist_row->cell[column].type)
2958     {
2959     case GTK_CMCELL_PIXTEXT:
2960       if (GTK_CMCELL_PIXTEXT (clist_row->cell[column])->pixbuf)
2961         {
2962           width = gdk_pixbuf_get_width(GTK_CMCELL_PIXTEXT (clist_row->cell[column])->pixbuf);
2963           height = gdk_pixbuf_get_height(GTK_CMCELL_PIXTEXT (clist_row->cell[column])->pixbuf);
2964           width += GTK_CMCELL_PIXTEXT (clist_row->cell[column])->spacing;
2965         }
2966       else
2967         width = height = 0;
2968           
2969       requisition->width += width;
2970       requisition->height = MAX (requisition->height, height);
2971       
2972       if (column == ctree->tree_column)
2973         {
2974           requisition->width += (ctree->tree_spacing + ctree->tree_indent *
2975                                  (((GtkCMCTreeRow *) clist_row)->level - 1));
2976           switch (ctree->expander_style)
2977             {
2978             case GTK_CMCTREE_EXPANDER_NONE:
2979               break;
2980             case GTK_CMCTREE_EXPANDER_TRIANGLE:
2981               requisition->width += PM_SIZE + 3;
2982               break;
2983             case GTK_CMCTREE_EXPANDER_SQUARE:
2984             case GTK_CMCTREE_EXPANDER_CIRCULAR:
2985               requisition->width += PM_SIZE + 1;
2986               break;
2987             }
2988           if (ctree->line_style == GTK_CMCTREE_LINES_TABBED)
2989             requisition->width += 3;
2990         }
2991       break;
2992     case GTK_CMCELL_PIXBUF:
2993       width = gdk_pixbuf_get_width(GTK_CMCELL_PIXBUF (clist_row->cell[column])->pixbuf);
2994       height = gdk_pixbuf_get_height(GTK_CMCELL_PIXBUF (clist_row->cell[column])->pixbuf);
2995       requisition->width += width;
2996       requisition->height = MAX (requisition->height, height);
2997       break;
2998     default:
2999       break;
3000     }
3001
3002   requisition->width  += clist_row->cell[column].horizontal;
3003   requisition->height += clist_row->cell[column].vertical;
3004 }
3005
3006 static void
3007 set_cell_contents (GtkCMCList    *clist,
3008                    GtkCMCListRow *clist_row,
3009                    gint         column,
3010                    GtkCMCellType  type,
3011                    const gchar *text,
3012                    guint8       spacing,
3013                    GdkPixbuf   *pixbuf)
3014 {
3015   gboolean visible = FALSE;
3016   GtkCMCTree *ctree;
3017   GtkRequisition requisition;
3018   gchar *old_text = NULL;
3019   GdkPixbuf *old_pixbuf = NULL;
3020
3021   cm_return_if_fail (GTK_IS_CMCTREE (clist));
3022   cm_return_if_fail (clist_row != NULL);
3023
3024   ctree = GTK_CMCTREE (clist);
3025
3026   if (clist->column[column].auto_resize &&
3027       !GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist))
3028     {
3029       GtkCMCTreeNode *parent;
3030
3031       parent = ((GtkCMCTreeRow *)clist_row)->parent;
3032       if ((parent && GTK_CMCTREE_ROW (parent)->expanded &&
3033                       gtk_cmctree_is_viewable (ctree, parent)))
3034         {
3035           visible = TRUE;
3036           GTK_CMCLIST_GET_CLASS (clist)->cell_size_request (clist, clist_row,
3037                                                          column, &requisition);
3038         }
3039     }
3040
3041   switch (clist_row->cell[column].type)
3042     {
3043     case GTK_CMCELL_EMPTY:
3044       break;
3045     case GTK_CMCELL_TEXT:
3046       old_text = GTK_CMCELL_TEXT (clist_row->cell[column])->text;
3047       break;
3048     case GTK_CMCELL_PIXBUF:
3049       old_pixbuf = GTK_CMCELL_PIXBUF (clist_row->cell[column])->pixbuf;
3050       break;
3051     case GTK_CMCELL_PIXTEXT:
3052       old_text = GTK_CMCELL_PIXTEXT (clist_row->cell[column])->text;
3053       old_pixbuf = GTK_CMCELL_PIXTEXT (clist_row->cell[column])->pixbuf;
3054       break;
3055     case GTK_CMCELL_WIDGET:
3056       /* unimplemented */
3057       break;
3058       
3059     default:
3060       break;
3061     }
3062
3063   clist_row->cell[column].type = GTK_CMCELL_EMPTY;
3064   if (column == ctree->tree_column && type != GTK_CMCELL_EMPTY)
3065     type = GTK_CMCELL_PIXTEXT;
3066
3067   /* Note that pixbuf and mask were already ref'ed by the caller
3068    */
3069   switch (type)
3070     {
3071     case GTK_CMCELL_TEXT:
3072       if (text)
3073         {
3074           clist_row->cell[column].type = GTK_CMCELL_TEXT;
3075           GTK_CMCELL_TEXT (clist_row->cell[column])->text = g_strdup (text);
3076         }
3077       break;
3078     case GTK_CMCELL_PIXBUF:
3079       if (pixbuf)
3080         {
3081           clist_row->cell[column].type = GTK_CMCELL_PIXBUF;
3082           GTK_CMCELL_PIXBUF (clist_row->cell[column])->pixbuf = pixbuf;
3083         }
3084       break;
3085     case GTK_CMCELL_PIXTEXT:
3086       if (column == ctree->tree_column)
3087         {
3088           clist_row->cell[column].type = GTK_CMCELL_PIXTEXT;
3089           GTK_CMCELL_PIXTEXT (clist_row->cell[column])->spacing = spacing;
3090           if (text)
3091             GTK_CMCELL_PIXTEXT (clist_row->cell[column])->text = g_strdup (text);
3092           else
3093             GTK_CMCELL_PIXTEXT (clist_row->cell[column])->text = NULL;
3094           if (pixbuf)
3095             {
3096               GTK_CMCELL_PIXTEXT (clist_row->cell[column])->pixbuf = pixbuf;
3097             }
3098           else
3099             {
3100               GTK_CMCELL_PIXTEXT (clist_row->cell[column])->pixbuf = NULL;
3101             }
3102         }
3103       else if (text && pixbuf)
3104         {
3105           clist_row->cell[column].type = GTK_CMCELL_PIXTEXT;
3106           GTK_CMCELL_PIXTEXT (clist_row->cell[column])->text = g_strdup (text);
3107           GTK_CMCELL_PIXTEXT (clist_row->cell[column])->spacing = spacing;
3108           GTK_CMCELL_PIXTEXT (clist_row->cell[column])->pixbuf = pixbuf;
3109         }
3110       break;
3111     default:
3112       break;
3113     }
3114   
3115   if (visible && clist->column[column].auto_resize &&
3116       !GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist))
3117     column_auto_resize (clist, clist_row, column, requisition.width);
3118
3119   g_free (old_text);
3120   if (old_pixbuf)
3121     g_object_unref (old_pixbuf);
3122 }
3123
3124 static void 
3125 set_node_info (GtkCMCTree     *ctree,
3126                GtkCMCTreeNode *node,
3127                const gchar  *text,
3128                guint8        spacing,
3129                GdkPixbuf    *pixbuf_closed,
3130                GdkPixbuf    *pixbuf_opened,
3131                gboolean      is_leaf,
3132                gboolean      expanded)
3133 {
3134   if (GTK_CMCTREE_ROW (node)->pixbuf_opened)
3135     {
3136       g_object_unref (GTK_CMCTREE_ROW (node)->pixbuf_opened);
3137     }
3138   if (GTK_CMCTREE_ROW (node)->pixbuf_closed)
3139     {
3140       g_object_unref (GTK_CMCTREE_ROW (node)->pixbuf_closed);
3141     }
3142
3143   GTK_CMCTREE_ROW (node)->pixbuf_opened = NULL;
3144   GTK_CMCTREE_ROW (node)->pixbuf_closed = NULL;
3145
3146   if (pixbuf_closed)
3147     {
3148       GTK_CMCTREE_ROW (node)->pixbuf_closed = g_object_ref (pixbuf_closed);
3149     }
3150   if (pixbuf_opened)
3151     {
3152       GTK_CMCTREE_ROW (node)->pixbuf_opened = g_object_ref (pixbuf_opened);
3153     }
3154
3155   GTK_CMCTREE_ROW (node)->is_leaf  = is_leaf;
3156   GTK_CMCTREE_ROW (node)->expanded = (is_leaf) ? FALSE : expanded;
3157
3158   if (GTK_CMCTREE_ROW (node)->expanded)
3159     gtk_cmctree_node_set_pixtext (ctree, node, ctree->tree_column,
3160                                 text, spacing, pixbuf_opened);
3161   else 
3162     gtk_cmctree_node_set_pixtext (ctree, node, ctree->tree_column,
3163                                 text, spacing, pixbuf_closed);
3164 }
3165
3166 static void
3167 tree_delete (GtkCMCTree     *ctree, 
3168              GtkCMCTreeNode *node, 
3169              gpointer      data)
3170 {
3171   tree_unselect (ctree,  node, NULL);
3172   row_delete (ctree, GTK_CMCTREE_ROW (node));
3173   g_list_free_1 ((GList *)node);
3174 }
3175
3176 static void
3177 tree_delete_row (GtkCMCTree     *ctree, 
3178                  GtkCMCTreeNode *node, 
3179                  gpointer      data)
3180 {
3181   row_delete (ctree, GTK_CMCTREE_ROW (node));
3182   g_list_free_1 ((GList *)node);
3183 }
3184
3185 static void
3186 tree_update_level (GtkCMCTree     *ctree, 
3187                    GtkCMCTreeNode *node, 
3188                    gpointer      data)
3189 {
3190   if (!node)
3191     return;
3192
3193   if (GTK_CMCTREE_ROW (node)->parent)
3194       GTK_CMCTREE_ROW (node)->level = 
3195         GTK_CMCTREE_ROW (GTK_CMCTREE_ROW (node)->parent)->level + 1;
3196   else
3197       GTK_CMCTREE_ROW (node)->level = 1;
3198 }
3199
3200 static void
3201 tree_select (GtkCMCTree     *ctree, 
3202              GtkCMCTreeNode *node, 
3203              gpointer      data)
3204 {
3205   if (node && GTK_CMCTREE_ROW (node)->row.state != GTK_STATE_SELECTED &&
3206       GTK_CMCTREE_ROW (node)->row.selectable)
3207     g_signal_emit (G_OBJECT (ctree), ctree_signals[TREE_SELECT_ROW], 0,
3208                      node, -1);
3209 }
3210
3211 static void
3212 tree_unselect (GtkCMCTree     *ctree, 
3213                GtkCMCTreeNode *node, 
3214                gpointer      data)
3215 {
3216   if (node && GTK_CMCTREE_ROW (node)->row.state == GTK_STATE_SELECTED)
3217     g_signal_emit (G_OBJECT (ctree), ctree_signals[TREE_UNSELECT_ROW], 0,
3218                      node, -1);
3219 }
3220
3221 static void
3222 tree_expand (GtkCMCTree     *ctree, 
3223              GtkCMCTreeNode *node, 
3224              gpointer      data)
3225 {
3226   if (node && !GTK_CMCTREE_ROW (node)->expanded)
3227     g_signal_emit (G_OBJECT (ctree), ctree_signals[TREE_EXPAND], 0,node);
3228 }
3229
3230 static void
3231 tree_collapse (GtkCMCTree     *ctree, 
3232                GtkCMCTreeNode *node, 
3233                gpointer      data)
3234 {
3235   if (node && GTK_CMCTREE_ROW (node)->expanded)
3236     g_signal_emit (G_OBJECT (ctree), ctree_signals[TREE_COLLAPSE], 0,node);
3237 }
3238
3239 static void
3240 tree_collapse_to_depth (GtkCMCTree     *ctree, 
3241                         GtkCMCTreeNode *node, 
3242                         gint          depth)
3243 {
3244   if (node && GTK_CMCTREE_ROW (node)->level == depth)
3245     gtk_cmctree_collapse_recursive (ctree, node);
3246 }
3247
3248 static void
3249 tree_toggle_expansion (GtkCMCTree     *ctree,
3250                        GtkCMCTreeNode *node,
3251                        gpointer      data)
3252 {
3253   if (!node)
3254     return;
3255
3256   if (GTK_CMCTREE_ROW (node)->expanded)
3257     g_signal_emit (G_OBJECT (ctree), ctree_signals[TREE_COLLAPSE], 0,node);
3258   else
3259     g_signal_emit (G_OBJECT (ctree), ctree_signals[TREE_EXPAND], 0,node);
3260 }
3261
3262 static GtkCMCTreeRow *
3263 row_new (GtkCMCTree *ctree)
3264 {
3265   GtkCMCList *clist;
3266   GtkCMCTreeRow *ctree_row;
3267   int i;
3268
3269   clist = GTK_CMCLIST (ctree);
3270 #if GLIB_CHECK_VERSION(2,10,0)
3271   ctree_row = g_slice_new (GtkCMCTreeRow);
3272   ctree_row->row.cell = g_slice_alloc (sizeof (GtkCMCell) * clist->columns);
3273 #else
3274   ctree_row = g_chunk_new (GtkCMCTreeRow, (GMemChunk *)clist->row_mem_chunk);
3275   ctree_row->row.cell = g_chunk_new (GtkCMCell, (GMemChunk *)clist->cell_mem_chunk);
3276 #endif
3277
3278   for (i = 0; i < clist->columns; i++)
3279     {
3280       ctree_row->row.cell[i].type = GTK_CMCELL_EMPTY;
3281       ctree_row->row.cell[i].vertical = 0;
3282       ctree_row->row.cell[i].horizontal = 0;
3283       ctree_row->row.cell[i].style = NULL;
3284     }
3285   GTK_CMCELL_PIXTEXT (ctree_row->row.cell[ctree->tree_column])->text = NULL;
3286
3287   ctree_row->row.fg_set     = FALSE;
3288   ctree_row->row.bg_set     = FALSE;
3289   ctree_row->row.style      = NULL;
3290   ctree_row->row.selectable = TRUE;
3291   ctree_row->row.state      = GTK_STATE_NORMAL;
3292   ctree_row->row.data       = NULL;
3293   ctree_row->row.destroy    = NULL;
3294
3295   ctree_row->level         = 0;
3296   ctree_row->expanded      = FALSE;
3297   ctree_row->parent        = NULL;
3298   ctree_row->sibling       = NULL;
3299   ctree_row->children      = NULL;
3300   ctree_row->pixbuf_closed = NULL;
3301   ctree_row->pixbuf_opened = NULL;
3302   
3303   return ctree_row;
3304 }
3305
3306 static void
3307 row_delete (GtkCMCTree    *ctree,
3308             GtkCMCTreeRow *ctree_row)
3309 {
3310   GtkCMCList *clist;
3311   gint i;
3312
3313   clist = GTK_CMCLIST (ctree);
3314
3315   for (i = 0; i < clist->columns; i++)
3316     {
3317       GTK_CMCLIST_GET_CLASS (clist)->set_cell_contents
3318         (clist, &(ctree_row->row), i, GTK_CMCELL_EMPTY, NULL, 0, NULL);
3319       if (ctree_row->row.cell[i].style)
3320         {
3321           if (gtk_widget_get_realized (GTK_WIDGET(ctree)))
3322             gtk_style_detach (ctree_row->row.cell[i].style);
3323           g_object_unref (ctree_row->row.cell[i].style);
3324         }
3325     }
3326
3327   if (ctree_row->row.style)
3328     {
3329       if (gtk_widget_get_realized (GTK_WIDGET(ctree)))
3330         gtk_style_detach (ctree_row->row.style);
3331       g_object_unref (ctree_row->row.style);
3332     }
3333
3334   if (ctree_row->pixbuf_closed)
3335     {
3336       g_object_unref (ctree_row->pixbuf_closed);
3337     }
3338
3339   if (ctree_row->pixbuf_opened)
3340     {
3341       g_object_unref (ctree_row->pixbuf_opened);
3342     }
3343
3344   if (ctree_row->row.destroy)
3345     {
3346       GDestroyNotify dnotify = ctree_row->row.destroy;
3347       gpointer ddata = ctree_row->row.data;
3348
3349       ctree_row->row.destroy = NULL;
3350       ctree_row->row.data = NULL;
3351
3352       dnotify (ddata);
3353     }
3354
3355 #if GLIB_CHECK_VERSION(2,10,0)  
3356   g_slice_free1 (sizeof (GtkCMCell) * clist->columns, ctree_row->row.cell);
3357   g_slice_free (GtkCMCTreeRow, ctree_row);
3358 #else
3359   g_mem_chunk_free ((GMemChunk *)clist->cell_mem_chunk, ctree_row->row.cell);
3360   g_mem_chunk_free ((GMemChunk *)clist->row_mem_chunk, ctree_row);
3361 #endif
3362 }
3363
3364 static void
3365 real_select_row (GtkCMCList *clist,
3366                  gint      row,
3367                  gint      column,
3368                  GdkEvent *event)
3369 {
3370   GList *node;
3371
3372   cm_return_if_fail (GTK_IS_CMCTREE (clist));
3373   
3374   if ((node = g_list_nth (clist->row_list, row)) &&
3375       GTK_CMCTREE_ROW (node)->row.selectable)
3376     g_signal_emit (G_OBJECT (clist), ctree_signals[TREE_SELECT_ROW],0,
3377                      node, column);
3378 }
3379
3380 static void
3381 real_unselect_row (GtkCMCList *clist,
3382                    gint      row,
3383                    gint      column,
3384                    GdkEvent *event)
3385 {
3386   GList *node;
3387
3388   cm_return_if_fail (GTK_IS_CMCTREE (clist));
3389
3390   if ((node = g_list_nth (clist->row_list, row)))
3391     g_signal_emit (G_OBJECT (clist), ctree_signals[TREE_UNSELECT_ROW],0,
3392                      node, column);
3393 }
3394
3395 static void
3396 real_tree_select (GtkCMCTree     *ctree,
3397                   GtkCMCTreeNode *node,
3398                   gint          column)
3399 {
3400   GtkCMCList *clist;
3401   GList *list;
3402   GtkCMCTreeNode *sel_row;
3403   gboolean node_selected;
3404
3405   cm_return_if_fail (GTK_IS_CMCTREE (ctree));
3406
3407   if (!node || GTK_CMCTREE_ROW (node)->row.state == GTK_STATE_SELECTED ||
3408       !GTK_CMCTREE_ROW (node)->row.selectable)
3409     return;
3410
3411   clist = GTK_CMCLIST (ctree);
3412
3413   switch (clist->selection_mode)
3414     {
3415     case GTK_SELECTION_SINGLE:
3416     case GTK_SELECTION_BROWSE:
3417
3418       node_selected = FALSE;
3419       list = clist->selection;
3420
3421       while (list)
3422         {
3423           sel_row = list->data;
3424           list = list->next;
3425           
3426           if (node == sel_row)
3427             node_selected = TRUE;
3428           else
3429             g_signal_emit (G_OBJECT (ctree),
3430                              ctree_signals[TREE_UNSELECT_ROW], 0, sel_row, column);
3431         }
3432
3433       if (node_selected)
3434         return;
3435
3436     default:
3437       break;
3438     }
3439
3440   GTK_CMCTREE_ROW (node)->row.state = GTK_STATE_SELECTED;
3441
3442   if (!clist->selection)
3443     {
3444       clist->selection = g_list_append (clist->selection, node);
3445       clist->selection_end = clist->selection;
3446     }
3447   else
3448     clist->selection_end = g_list_append (clist->selection_end, node)->next;
3449
3450   tree_draw_node (ctree, node);