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