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