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