368d162d887d32ad5ca105412a206b9ec21459e4
[claws.git] / src / gtk / gtksctree.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Parts of this file:
4  * Copyright (C) 1999-2009 Hiroyuki Yamamoto and the Claws Mail team
5  *
6  * Parts of this file from gtk/gtkctree.c and gtk/gtkclist.c:
7  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball, Josh MacDonald, 
8  * Copyright (C) 1997-1998 Jay Painter <jpaint@serv.net><jpaint@gimp.org>  
9  *
10  * Parts of this file from gtkflist.c:
11  * Copyright (C) 1999 The Free Software Foundation
12  * Author: Federico Mena <federico@nuclecu.unam.mx>
13  *
14  * This program is free software; you can redistribute it and/or modify
15  * it under the terms of the GNU General Public License as published by
16  * the Free Software Foundation; either version 3 of the License, or
17  * (at your option) any later version.
18  *
19  * This program is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU General Public License for more details.
23  *
24  * You should have received a copy of the GNU General Public License
25  * along with this program. If not, see <http://www.gnu.org/licenses/>.
26  * 
27  */
28
29 #include <stdlib.h>
30
31 #include "gtksctree.h"
32 #include "claws-marshal.h"
33 #include "prefs_common.h"
34 #include "utils.h"
35 #include "gtkutils.h"
36
37 #define CLIST_UNFROZEN(clist)     (((GtkCMCList*) (clist))->freeze_count == 0)
38 #define CLIST_REFRESH(clist)    G_STMT_START { \
39   if (CLIST_UNFROZEN (clist)) \
40     GTK_CMCLIST_GET_CLASS (clist)->refresh ((GtkCMCList*) (clist)); \
41 } G_STMT_END
42 #define CELL_SPACING               1
43 #define CLIST_OPTIMUM_SIZE         64
44 #define COLUMN_INSET               3
45 #define PM_SIZE                    8
46 #define TAB_SIZE                   (PM_SIZE + 6)
47 #define ROW_TOP_YPIXEL(clist, row) (((clist)->row_height * (row)) + \
48                                     (((row) + 1) * CELL_SPACING) + \
49                                     (clist)->voffset)
50 #define ROW_FROM_YPIXEL(clist, y)  (((y) - (clist)->voffset) / \
51                                     ((clist)->row_height + CELL_SPACING))
52 #define COLUMN_LEFT_XPIXEL(clist, col)  ((clist)->column[(col)].area.x \
53                                     + (clist)->hoffset)
54 #define COLUMN_LEFT(clist, column) ((clist)->column[(column)].area.x)
55
56 enum {
57         ROW_POPUP_MENU,
58         EMPTY_POPUP_MENU,
59         OPEN_ROW,
60         START_DRAG,
61         LAST_SIGNAL
62 };
63
64 static void gtk_sctree_class_init (GtkSCTreeClass *class);
65 static void gtk_sctree_init (GtkSCTree *sctree);
66
67 static gint gtk_sctree_button_press (GtkWidget *widget, GdkEventButton *event);
68 static gint gtk_sctree_button_release (GtkWidget *widget, GdkEventButton *event);
69 static gint gtk_sctree_motion (GtkWidget *widget, GdkEventMotion *event);
70 static void gtk_sctree_drag_begin (GtkWidget *widget, GdkDragContext *context);
71 static void gtk_sctree_drag_end (GtkWidget *widget, GdkDragContext *context);
72 static void gtk_sctree_drag_data_get (GtkWidget *widget, GdkDragContext *context,
73                                      GtkSelectionData *data, guint info, guint time);
74 static void gtk_sctree_drag_leave (GtkWidget *widget, GdkDragContext *context, guint time);
75 static gboolean gtk_sctree_drag_motion (GtkWidget *widget, GdkDragContext *context,
76                                        gint x, gint y, guint time);
77 static gboolean gtk_sctree_drag_drop (GtkWidget *widget, GdkDragContext *context,
78                                      gint x, gint y, guint time);
79 static void gtk_sctree_drag_data_received (GtkWidget *widget, GdkDragContext *context,
80                                           gint x, gint y, GtkSelectionData *data,
81                                           guint info, guint time);
82
83 static void gtk_sctree_clear (GtkCMCList *clist);
84 static void gtk_sctree_real_unselect_all (GtkCMCList *clist);
85        
86 static void stree_sort (GtkCMCTree *ctree, GtkCMCTreeNode  *node, gpointer data);
87 void gtk_sctree_sort_node (GtkCMCTree *ctree, GtkCMCTreeNode *node);
88 void gtk_sctree_sort_recursive (GtkCMCTree *ctree, GtkCMCTreeNode *node);
89
90 static void gtk_sctree_link (GtkCMCTree *ctree,
91                         GtkCMCTreeNode  *node,
92                         GtkCMCTreeNode  *parent,
93                         GtkCMCTreeNode  *sibling,
94                         gboolean       update_focus_row);
95
96 static void gtk_sctree_unlink (GtkCMCTree      *ctree, 
97                         GtkCMCTreeNode  *node,
98                         gboolean       update_focus_row);
99
100 static void stree_update_level (GtkCMCTree      *ctree, 
101                         GtkCMCTreeNode  *node, 
102                         gpointer       data);
103
104 static GtkCMCTreeNode * gtk_sctree_last_visible (GtkCMCTree     *ctree,
105                                               GtkCMCTreeNode *node);
106 static void gtk_sctree_real_tree_expand            (GtkCMCTree      *ctree,
107                                                  GtkCMCTreeNode  *node);
108 static void gtk_sctree_real_tree_collapse          (GtkCMCTree      *ctree,
109                                                  GtkCMCTreeNode  *node);
110 static void
111 sreal_tree_move (GtkCMCTree     *ctree,
112                 GtkCMCTreeNode *node,
113                 GtkCMCTreeNode *new_parent, 
114                 GtkCMCTreeNode *new_sibling);
115
116 static GtkCMCTreeClass *parent_class;
117
118 static guint sctree_signals[LAST_SIGNAL];
119
120 /**
121  * gtk_sctree_get_type:
122  * @void: 
123  * 
124  * Creates the GtkSCTree class and its type information
125  * 
126  * Return value: The type ID for GtkSCTreeClass
127  **/
128 GType
129 gtk_sctree_get_type (void)
130 {
131         static GType sctree_type = 0;
132
133         if (!sctree_type) {
134                 GTypeInfo sctree_info = {
135                         sizeof (GtkSCTreeClass),
136
137                         (GBaseInitFunc) NULL,
138                         (GBaseFinalizeFunc) NULL,
139
140                         (GClassInitFunc) gtk_sctree_class_init,
141                         (GClassFinalizeFunc) NULL,
142                         NULL,   /* class_data */
143
144                         sizeof (GtkSCTree),
145                         0,      /* n_preallocs */
146                         (GInstanceInitFunc) gtk_sctree_init,
147                 };
148
149                 sctree_type = g_type_register_static (GTK_TYPE_CMCTREE, "GtkSCTree", &sctree_info, (GTypeFlags)0);
150         }
151
152         return sctree_type;
153 }
154
155 static gint
156 gtk_sctree_draw_cell_pixbuf (GdkWindow    *window,
157                   GdkRectangle *clip_rectangle,
158                   GdkGC        *fg_gc,
159                   GdkPixbuf    *pixbuf,
160                   gint          x,
161                   gint          y,
162                   gint          width,
163                   gint          height)
164 {
165   gint xsrc = 0;
166   gint ysrc = 0;
167
168   gdk_gc_set_clip_origin (fg_gc, x, y);
169   if (x < clip_rectangle->x)
170     {
171       xsrc = clip_rectangle->x - x;
172       width -= xsrc;
173       x = clip_rectangle->x;
174     }
175   if (x + width > clip_rectangle->x + clip_rectangle->width)
176     width = clip_rectangle->x + clip_rectangle->width - x;
177
178   if (y < clip_rectangle->y)
179     {
180       ysrc = clip_rectangle->y - y;
181       height -= ysrc;
182       y = clip_rectangle->y;
183     }
184   if (y + height > clip_rectangle->y + clip_rectangle->height)
185     height = clip_rectangle->y + clip_rectangle->height - y;
186
187   if (width > 0 && height > 0)
188     gdk_draw_pixbuf (window, fg_gc, pixbuf, xsrc, ysrc, x, y, width, height, GDK_RGB_DITHER_NONE, 0, 0);
189
190   gdk_gc_set_clip_origin (fg_gc, 0, 0);
191
192   return x + MAX (width, 0);
193 }
194
195 static void
196 gtk_sctree_get_cell_style (GtkCMCList     *clist,
197                 GtkCMCListRow  *clist_row,
198                 gint          state,
199                 gint          row,
200                 gint          column,
201                 GtkStyle    **style,
202                 GdkGC       **fg_gc,
203                 GdkGC       **bg_gc)
204 {
205   gint fg_state;
206
207   if ((state == GTK_STATE_NORMAL) &&
208       (GTK_WIDGET (clist)->state == GTK_STATE_INSENSITIVE))
209     fg_state = GTK_STATE_INSENSITIVE;
210   else
211     fg_state = state;
212
213   if (clist_row->cell[column].style)
214     {
215       if (style)
216         *style = clist_row->cell[column].style;
217       if (fg_gc)
218         *fg_gc = clist_row->cell[column].style->fg_gc[fg_state];
219       if (bg_gc) {
220         if (state == GTK_STATE_SELECTED)
221           *bg_gc = clist_row->cell[column].style->bg_gc[state];
222       }
223     }
224   else if (clist_row->style)
225     {
226       if (style)
227         *style = clist_row->style;
228       if (fg_gc)
229         *fg_gc = clist_row->style->fg_gc[fg_state];
230       if (bg_gc) {
231         if (state == GTK_STATE_SELECTED)
232           *bg_gc = clist_row->style->bg_gc[state];
233         else
234           *bg_gc = clist_row->bg_set ? 
235                 clist->bg_gc : clist_row->style->base_gc[state];
236       }
237     }
238   else
239     {
240       if (style)
241         *style = GTK_WIDGET (clist)->style;
242       if (fg_gc)
243         *fg_gc = GTK_WIDGET (clist)->style->fg_gc[fg_state];
244       if (bg_gc) {
245         if (state == GTK_STATE_SELECTED)
246           *bg_gc = GTK_WIDGET (clist)->style->bg_gc[state];
247         else
248           *bg_gc = GTK_WIDGET (clist)->style->base_gc[state];
249       }
250
251       if (state != GTK_STATE_SELECTED)
252         {
253           if (fg_gc && clist_row->fg_set)
254             *fg_gc = clist->fg_gc;
255           if (bg_gc && clist_row->bg_set)
256             *bg_gc = clist->bg_gc;
257         }
258     }
259 }
260
261 static gint
262 gtk_sctree_draw_expander (GtkCMCTree     *ctree,
263                          GtkCMCTreeRow  *ctree_row,
264                          GtkStyle     *style,
265                          GdkRectangle *clip_rectangle,
266                          gint          x)
267 {
268   GtkCMCList *clist;
269   GdkPoint points[3];
270   gint justification_factor;
271   gint y;
272
273  if (ctree->expander_style == GTK_CMCTREE_EXPANDER_NONE)
274    return x;
275
276   clist = GTK_CMCLIST (ctree);
277   if (clist->column[ctree->tree_column].justification == GTK_JUSTIFY_RIGHT)
278     justification_factor = -1;
279   else
280     justification_factor = 1;
281   if (!GTK_CMCLIST_ROW_HEIGHT_SET(GTK_CMCLIST(clist)))
282       y = (clip_rectangle->y + (clip_rectangle->height - PM_SIZE) / 2 -
283           (clip_rectangle->height + 1) % 2);
284   else
285       y = (clip_rectangle->y + (clip_rectangle->height/2 - PM_SIZE) / 2 -
286           (clip_rectangle->height/2 + 1) % 2);
287
288   if (!ctree_row->children)
289     {
290       switch (ctree->expander_style)
291         {
292         case GTK_CMCTREE_EXPANDER_NONE:
293           return x;
294         case GTK_CMCTREE_EXPANDER_TRIANGLE:
295           return x + justification_factor * (PM_SIZE + 3);
296         case GTK_CMCTREE_EXPANDER_SQUARE:
297         case GTK_CMCTREE_EXPANDER_CIRCULAR:
298           return x + justification_factor * (PM_SIZE + 1);
299         }
300     }
301
302   gdk_gc_set_clip_rectangle (style->fg_gc[GTK_STATE_NORMAL], clip_rectangle);
303   gdk_gc_set_clip_rectangle (style->base_gc[GTK_STATE_NORMAL], clip_rectangle);
304
305   switch (ctree->expander_style)
306     {
307     case GTK_CMCTREE_EXPANDER_NONE:
308       break;
309     case GTK_CMCTREE_EXPANDER_TRIANGLE:
310       if (ctree_row->expanded)
311         {
312           points[0].x = x;
313           points[0].y = y + (PM_SIZE + 2) / 6;
314           points[1].x = points[0].x + justification_factor * (PM_SIZE + 2);
315           points[1].y = points[0].y;
316           points[2].x = (points[0].x +
317                          justification_factor * (PM_SIZE + 2) / 2);
318           points[2].y = y + 2 * (PM_SIZE + 2) / 3;
319         }
320       else
321         {
322           points[0].x = x + justification_factor * ((PM_SIZE + 2) / 6 + 2);
323           points[0].y = y - 1;
324           points[1].x = points[0].x;
325           points[1].y = points[0].y + (PM_SIZE + 2);
326           points[2].x = (points[0].x +
327                          justification_factor * (2 * (PM_SIZE + 2) / 3 - 1));
328           points[2].y = points[0].y + (PM_SIZE + 2) / 2;
329         }
330
331       gdk_draw_polygon (clist->clist_window, style->base_gc[GTK_STATE_NORMAL],
332                         TRUE, points, 3);
333       gdk_draw_polygon (clist->clist_window, style->fg_gc[GTK_STATE_NORMAL],
334                         FALSE, points, 3);
335
336       x += justification_factor * (PM_SIZE + 3);
337       break;
338     case GTK_CMCTREE_EXPANDER_SQUARE:
339     case GTK_CMCTREE_EXPANDER_CIRCULAR:
340       if (justification_factor == -1)
341         x += justification_factor * (PM_SIZE + 1);
342
343       if (ctree->expander_style == GTK_CMCTREE_EXPANDER_CIRCULAR)
344         {
345           gdk_draw_arc (clist->clist_window, style->base_gc[GTK_STATE_NORMAL],
346                         TRUE, x, y, PM_SIZE, PM_SIZE, 0, 360 * 64);
347           gdk_draw_arc (clist->clist_window, style->fg_gc[GTK_STATE_NORMAL],
348                         FALSE, x, y, PM_SIZE, PM_SIZE, 0, 360 * 64);
349         }
350       else
351         {
352           gdk_draw_rectangle (clist->clist_window,
353                               style->base_gc[GTK_STATE_NORMAL], TRUE,
354                               x, y, PM_SIZE, PM_SIZE);
355           gdk_draw_rectangle (clist->clist_window,
356                               style->fg_gc[GTK_STATE_NORMAL], FALSE,
357                               x, y, PM_SIZE, PM_SIZE);
358         }
359
360       gdk_draw_line (clist->clist_window, style->fg_gc[GTK_STATE_NORMAL], 
361                      x + 2, y + PM_SIZE / 2, x + PM_SIZE - 2, y + PM_SIZE / 2);
362
363       if (!ctree_row->expanded)
364         gdk_draw_line (clist->clist_window, style->fg_gc[GTK_STATE_NORMAL],
365                        x + PM_SIZE / 2, y + 2,
366                        x + PM_SIZE / 2, y + PM_SIZE - 2);
367
368       if (justification_factor == 1)
369         x += justification_factor * (PM_SIZE + 1);
370       break;
371     }
372
373   gdk_gc_set_clip_rectangle (style->fg_gc[GTK_STATE_NORMAL], NULL);
374   gdk_gc_set_clip_rectangle (style->base_gc[GTK_STATE_NORMAL], NULL);
375
376   return x;
377 }
378
379 static gint
380 gtk_sctree_draw_lines (GtkCMCTree     *ctree,
381                       GtkCMCTreeRow  *ctree_row,
382                       gint          row,
383                       gint          column,
384                       gint          state,
385                       GdkRectangle *clip_rectangle,
386                       GdkRectangle *cell_rectangle,
387                       GdkRectangle *crect,
388                       GdkRectangle *area,
389                       GtkStyle     *style)
390 {
391   GtkCMCList *clist;
392   GtkCMCTreeNode *node;
393   GtkCMCTreeNode *parent;
394   GdkRectangle tree_rectangle;
395   GdkRectangle tc_rectangle;
396   GdkGC *bg_gc;
397   gint offset;
398   gint offset_x;
399   gint offset_y;
400   gint xcenter;
401   gint ycenter;
402   gint next_level;
403   gint column_right;
404   gint column_left;
405   gint justify_right;
406   gint justification_factor;
407   
408   clist = GTK_CMCLIST (ctree);
409   ycenter = clip_rectangle->y + (clip_rectangle->height / 2);
410   justify_right = (clist->column[column].justification == GTK_JUSTIFY_RIGHT);
411
412   if (justify_right)
413     {
414       offset = (clip_rectangle->x + clip_rectangle->width - 1 -
415                 ctree->tree_indent * (ctree_row->level - 1));
416       justification_factor = -1;
417     }
418   else
419     {
420       offset = clip_rectangle->x + ctree->tree_indent * (ctree_row->level - 1);
421       justification_factor = 1;
422     }
423
424   switch (ctree->line_style)
425     {
426     case GTK_CMCTREE_LINES_NONE:
427       break;
428     case GTK_CMCTREE_LINES_TABBED:
429       xcenter = offset + justification_factor * TAB_SIZE;
430
431       column_right = (COLUMN_LEFT_XPIXEL (clist, ctree->tree_column) +
432                       clist->column[ctree->tree_column].area.width +
433                       COLUMN_INSET);
434       column_left = (COLUMN_LEFT_XPIXEL (clist, ctree->tree_column) -
435                      COLUMN_INSET - CELL_SPACING);
436
437       if (area)
438         {
439           tree_rectangle.y = crect->y;
440           tree_rectangle.height = crect->height;
441
442           if (justify_right)
443             {
444               tree_rectangle.x = xcenter;
445               tree_rectangle.width = column_right - xcenter;
446             }
447           else
448             {
449               tree_rectangle.x = column_left;
450               tree_rectangle.width = xcenter - column_left;
451             }
452
453           if (!gdk_rectangle_intersect (area, &tree_rectangle, &tc_rectangle))
454             {
455               offset += justification_factor * 3;
456               break;
457             }
458         }
459
460       gdk_gc_set_clip_rectangle (ctree->lines_gc, crect);
461
462       next_level = ctree_row->level;
463
464       if (!ctree_row->sibling || (ctree_row->children && ctree_row->expanded))
465         {
466           node = gtk_cmctree_find_node_ptr (ctree, ctree_row);
467           if (GTK_CMCTREE_NODE_NEXT (node))
468             next_level = GTK_CMCTREE_ROW (GTK_CMCTREE_NODE_NEXT (node))->level;
469           else
470             next_level = 0;
471         }
472
473       if (ctree->tree_indent > 0)
474         {
475           node = ctree_row->parent;
476           while (node)
477             {
478               xcenter -= (justification_factor * ctree->tree_indent);
479
480               if ((justify_right && xcenter < column_left) ||
481                   (!justify_right && xcenter > column_right))
482                 {
483                   node = GTK_CMCTREE_ROW (node)->parent;
484                   continue;
485                 }
486
487               tree_rectangle.y = cell_rectangle->y;
488               tree_rectangle.height = cell_rectangle->height;
489               if (justify_right)
490                 {
491                   tree_rectangle.x = MAX (xcenter - ctree->tree_indent + 1,
492                                           column_left);
493                   tree_rectangle.width = MIN (xcenter - column_left,
494                                               ctree->tree_indent);
495                 }
496               else
497                 {
498                   tree_rectangle.x = xcenter;
499                   tree_rectangle.width = MIN (column_right - xcenter,
500                                               ctree->tree_indent);
501                 }
502
503               if (!area || gdk_rectangle_intersect (area, &tree_rectangle,
504                                                     &tc_rectangle))
505                 {
506                   gtk_sctree_get_cell_style (clist, &GTK_CMCTREE_ROW (node)->row,
507                                   state, row, column, NULL, NULL, &bg_gc);
508
509                   if (bg_gc == clist->bg_gc)
510                     gdk_gc_set_foreground
511                       (clist->bg_gc, &GTK_CMCTREE_ROW (node)->row.background);
512
513                   if (!area)
514                     gdk_draw_rectangle (clist->clist_window, bg_gc, TRUE,
515                                         tree_rectangle.x,
516                                         tree_rectangle.y,
517                                         tree_rectangle.width,
518                                         tree_rectangle.height);
519                   else 
520                     gdk_draw_rectangle (clist->clist_window, bg_gc, TRUE,
521                                         tc_rectangle.x,
522                                         tc_rectangle.y,
523                                         tc_rectangle.width,
524                                         tc_rectangle.height);
525                 }
526               if (next_level > GTK_CMCTREE_ROW (node)->level)
527                 gdk_draw_line (clist->clist_window, ctree->lines_gc,
528                                xcenter, crect->y,
529                                xcenter, crect->y + crect->height);
530               else
531                 {
532                   gint width;
533
534                   offset_x = MIN (ctree->tree_indent, 2 * TAB_SIZE);
535                   width = offset_x / 2 + offset_x % 2;
536
537                   parent = GTK_CMCTREE_ROW (node)->parent;
538
539                   tree_rectangle.y = ycenter;
540                   tree_rectangle.height = (cell_rectangle->y - ycenter +
541                                            cell_rectangle->height);
542
543                   if (justify_right)
544                     {
545                       tree_rectangle.x = MAX(xcenter + 1 - width, column_left);
546                       tree_rectangle.width = MIN (xcenter + 1 - column_left,
547                                                   width);
548                     }
549                   else
550                     {
551                       tree_rectangle.x = xcenter;
552                       tree_rectangle.width = MIN (column_right - xcenter,
553                                                   width);
554                     }
555
556                   if (!area ||
557                       gdk_rectangle_intersect (area, &tree_rectangle,
558                                                &tc_rectangle))
559                     {
560                       if (parent)
561                         {
562                           gtk_sctree_get_cell_style (clist, &GTK_CMCTREE_ROW (parent)->row,
563                                           state, row, column, NULL, NULL, &bg_gc);
564                           if (bg_gc == clist->bg_gc)
565                             gdk_gc_set_foreground
566                               (clist->bg_gc,
567                                &GTK_CMCTREE_ROW (parent)->row.background);
568                         }
569                       else if (state == GTK_STATE_SELECTED)
570                         bg_gc = style->base_gc[state];
571                       else
572                         bg_gc = GTK_WIDGET (clist)->style->base_gc[state];
573
574                       if (!area)
575                         gdk_draw_rectangle (clist->clist_window, bg_gc, TRUE,
576                                             tree_rectangle.x,
577                                             tree_rectangle.y,
578                                             tree_rectangle.width,
579                                             tree_rectangle.height);
580                       else
581                         gdk_draw_rectangle (clist->clist_window,
582                                             bg_gc, TRUE,
583                                             tc_rectangle.x,
584                                             tc_rectangle.y,
585                                             tc_rectangle.width,
586                                             tc_rectangle.height);
587                     }
588
589                   gtk_sctree_get_cell_style (clist, &GTK_CMCTREE_ROW (node)->row,
590                                   state, row, column, NULL, NULL, &bg_gc);
591                   if (bg_gc == clist->bg_gc)
592                     gdk_gc_set_foreground
593                       (clist->bg_gc, &GTK_CMCTREE_ROW (node)->row.background);
594
595                   gdk_gc_set_clip_rectangle (bg_gc, crect);
596                   gdk_draw_arc (clist->clist_window, bg_gc, TRUE,
597                                 xcenter - (justify_right * offset_x),
598                                 cell_rectangle->y,
599                                 offset_x, clist->row_height,
600                                 (180 + (justify_right * 90)) * 64, 90 * 64);
601                   gdk_gc_set_clip_rectangle (bg_gc, NULL);
602
603                   gdk_draw_line (clist->clist_window, ctree->lines_gc, 
604                                  xcenter, cell_rectangle->y, xcenter, ycenter);
605
606                   if (justify_right)
607                     gdk_draw_arc (clist->clist_window, ctree->lines_gc, FALSE,
608                                   xcenter - offset_x, cell_rectangle->y,
609                                   offset_x, clist->row_height,
610                                   270 * 64, 90 * 64);
611                   else
612                     gdk_draw_arc (clist->clist_window, ctree->lines_gc, FALSE,
613                                   xcenter, cell_rectangle->y,
614                                   offset_x, clist->row_height,
615                                   180 * 64, 90 * 64);
616                 }
617               node = GTK_CMCTREE_ROW (node)->parent;
618             }
619         }
620
621       if (state != GTK_STATE_SELECTED)
622         {
623           tree_rectangle.y = clip_rectangle->y;
624           tree_rectangle.height = clip_rectangle->height;
625           tree_rectangle.width = COLUMN_INSET + CELL_SPACING +
626             MIN (clist->column[ctree->tree_column].area.width + COLUMN_INSET,
627                  TAB_SIZE);
628
629           if (justify_right)
630             tree_rectangle.x = MAX (xcenter + 1, column_left);
631           else
632             tree_rectangle.x = column_left;
633
634           if (!area)
635             gdk_draw_rectangle (clist->clist_window,
636                                 GTK_WIDGET
637                                 (ctree)->style->base_gc[GTK_STATE_NORMAL],
638                                 TRUE,
639                                 tree_rectangle.x,
640                                 tree_rectangle.y,
641                                 tree_rectangle.width,
642                                 tree_rectangle.height);
643           else if (gdk_rectangle_intersect (area, &tree_rectangle,
644                                             &tc_rectangle))
645             gdk_draw_rectangle (clist->clist_window,
646                                 GTK_WIDGET
647                                 (ctree)->style->base_gc[GTK_STATE_NORMAL],
648                                 TRUE,
649                                 tc_rectangle.x,
650                                 tc_rectangle.y,
651                                 tc_rectangle.width,
652                                 tc_rectangle.height);
653         }
654
655       xcenter = offset + (justification_factor * ctree->tree_indent / 2);
656
657       gtk_sctree_get_cell_style (clist, &ctree_row->row, state, row, column, NULL, NULL,
658                       &bg_gc);
659       if (bg_gc == clist->bg_gc)
660         gdk_gc_set_foreground (clist->bg_gc, &ctree_row->row.background);
661
662       gdk_gc_set_clip_rectangle (bg_gc, crect);
663       if (ctree_row->is_leaf)
664         {
665           GdkPoint points[6];
666
667           points[0].x = offset + justification_factor * TAB_SIZE;
668           points[0].y = cell_rectangle->y;
669
670           points[1].x = points[0].x - justification_factor * 4;
671           points[1].y = points[0].y;
672
673           points[2].x = points[1].x - justification_factor * 2;
674           points[2].y = points[1].y + 3;
675
676           points[3].x = points[2].x;
677           points[3].y = points[2].y + clist->row_height - 5;
678
679           points[4].x = points[3].x + justification_factor * 2;
680           points[4].y = points[3].y + 3;
681
682           points[5].x = points[4].x + justification_factor * 4;
683           points[5].y = points[4].y;
684
685           gdk_draw_polygon (clist->clist_window, bg_gc, TRUE, points, 6);
686           gdk_draw_lines (clist->clist_window, ctree->lines_gc, points, 6);
687         }
688       else 
689         {
690           gdk_draw_arc (clist->clist_window, bg_gc, TRUE,
691                         offset - (justify_right * 2 * TAB_SIZE),
692                         cell_rectangle->y,
693                         2 * TAB_SIZE, clist->row_height,
694                         (90 + (180 * justify_right)) * 64, 180 * 64);
695           gdk_draw_arc (clist->clist_window, ctree->lines_gc, FALSE,
696                         offset - (justify_right * 2 * TAB_SIZE),
697                         cell_rectangle->y,
698                         2 * TAB_SIZE, clist->row_height,
699                         (90 + (180 * justify_right)) * 64, 180 * 64);
700         }
701       gdk_gc_set_clip_rectangle (bg_gc, NULL);
702       gdk_gc_set_clip_rectangle (ctree->lines_gc, NULL);
703
704       offset += justification_factor * 3;
705       break;
706     default:
707       xcenter = offset + justification_factor * PM_SIZE / 2;
708
709       if (area)
710         {
711           tree_rectangle.y = crect->y;
712           tree_rectangle.height = crect->height;
713
714           if (justify_right)
715             {
716               tree_rectangle.x = xcenter - PM_SIZE / 2 - 2;
717               tree_rectangle.width = (clip_rectangle->x +
718                                       clip_rectangle->width -tree_rectangle.x);
719             }
720           else
721             {
722               tree_rectangle.x = clip_rectangle->x + PM_SIZE / 2;
723               tree_rectangle.width = (xcenter + PM_SIZE / 2 + 2 -
724                                       clip_rectangle->x);
725             }
726
727           if (!gdk_rectangle_intersect (area, &tree_rectangle, &tc_rectangle))
728             break;
729         }
730
731       offset_x = 1;
732       offset_y = 0;
733       if (ctree->line_style == GTK_CMCTREE_LINES_DOTTED)
734         {
735           offset_x += abs((clip_rectangle->x + clist->hoffset) % 2);
736           offset_y  = abs((cell_rectangle->y + clist->voffset) % 2);
737         }
738
739       clip_rectangle->y--;
740       clip_rectangle->height++;
741       gdk_gc_set_clip_rectangle (ctree->lines_gc, clip_rectangle);
742       gdk_draw_line (clist->clist_window, ctree->lines_gc,
743                      xcenter,
744                      (ctree->show_stub || clist->row_list->data != ctree_row) ?
745                      cell_rectangle->y + offset_y : ycenter,
746                      xcenter,
747                      (ctree_row->sibling) ? crect->y +crect->height : ycenter);
748
749       gdk_draw_line (clist->clist_window, ctree->lines_gc,
750                      xcenter + (justification_factor * offset_x), ycenter,
751                      xcenter + (justification_factor * (PM_SIZE / 2 + 2)),
752                      ycenter);
753
754       node = ctree_row->parent;
755       while (node)
756         {
757           xcenter -= (justification_factor * ctree->tree_indent);
758
759           if (GTK_CMCTREE_ROW (node)->sibling)
760             gdk_draw_line (clist->clist_window, ctree->lines_gc, 
761                            xcenter, cell_rectangle->y + offset_y,
762                            xcenter, crect->y + crect->height);
763           node = GTK_CMCTREE_ROW (node)->parent;
764         }
765       gdk_gc_set_clip_rectangle (ctree->lines_gc, NULL);
766       clip_rectangle->y++;
767       clip_rectangle->height--;
768       break;
769     }
770   return offset;
771 }
772
773 static gboolean filter_fg (PangoAttribute *attribute, gpointer data)
774 {
775         const PangoAttrClass *klass = attribute->klass;
776         if (klass->type == PANGO_ATTR_FOREGROUND)
777                 return TRUE;
778
779         return FALSE;   
780 }
781
782 static PangoLayout *
783 sc_gtk_cmclist_create_cell_layout (GtkCMCList       *clist,
784                                GtkCMCListRow    *clist_row,
785                                gint            column)
786 {
787   PangoLayout *layout;
788   GtkStyle *style;
789   GtkCMCell *cell;
790   gchar *text;
791   
792   gtk_sctree_get_cell_style (clist, clist_row, GTK_STATE_NORMAL, 0, column, &style,
793                   NULL, NULL);
794
795
796   cell = &clist_row->cell[column];
797   switch (cell->type)
798     {
799     case GTK_CMCELL_TEXT:
800     case GTK_CMCELL_PIXTEXT:
801       text = ((cell->type == GTK_CMCELL_PIXTEXT) ?
802               GTK_CMCELL_PIXTEXT (*cell)->text :
803               GTK_CMCELL_TEXT (*cell)->text);
804
805       if (!text)
806         return NULL;
807       
808       if (!GTK_SCTREE(clist)->use_markup[column]) {
809               layout = gtk_widget_create_pango_layout (GTK_WIDGET (clist),
810                                                        ((cell->type == GTK_CMCELL_PIXTEXT) ?
811                                                         GTK_CMCELL_PIXTEXT (*cell)->text :
812                                                         GTK_CMCELL_TEXT (*cell)->text));
813               pango_layout_set_font_description (layout, style->font_desc);
814       } else {
815               PangoContext *context = gtk_widget_get_pango_context (GTK_WIDGET(clist));
816               layout = pango_layout_new (context);
817               pango_layout_set_markup (layout, text, -1);
818               pango_layout_set_font_description (layout, style->font_desc);
819               if (clist_row->state == GTK_STATE_SELECTED) {
820                       /* for selected row, we should remove any forced foreground color
821                        * or it looks like shit */
822                       PangoAttrList *list = pango_layout_get_attributes(layout);
823                       PangoAttrList *rem = pango_attr_list_filter(list, filter_fg, NULL);
824                       if (rem)
825                               pango_attr_list_unref(rem);
826               }
827       }
828       
829       return layout;
830       
831     default:
832       return NULL;
833     }
834 }
835
836 static void
837 gtk_sctree_draw_row (GtkCMCList     *clist,
838           GdkRectangle *area,
839           gint          row,
840           GtkCMCListRow  *clist_row)
841 {
842   GtkWidget *widget;
843   GtkCMCTree  *ctree;
844   GdkRectangle *rect;
845   GdkRectangle *crect;
846   GdkRectangle row_rectangle;
847   GdkRectangle cell_rectangle; 
848   GdkRectangle clip_rectangle;
849   GdkRectangle intersect_rectangle;
850   gint last_column;
851   gint column_left = 0;
852   gint column_right = 0;
853   gint offset = 0;
854   gint state;
855   gint i;
856   static GdkColor greybg={0, 0, 0, 0};
857   static gboolean color_change = TRUE;
858
859   if (greybg.pixel == 0 &&
860       greybg.red == 0 &&
861       greybg.green == 0 &&
862       greybg.blue == 0) {
863         GdkColor normalbg = {0, 0xffff, 0xffff, 0xffff};
864         if (GTK_WIDGET (clist)->style) {
865                 normalbg = GTK_WIDGET (clist)->style->base[GTK_STATE_NORMAL];
866         }
867         if (normalbg.red > 0x8888 && normalbg.green > 0x8888 && normalbg.blue > 0x8888) {
868                 greybg.pixel = normalbg.pixel;
869                 greybg.red = normalbg.red - prefs_common.stripes_color_offset;
870                 greybg.green = normalbg.green - prefs_common.stripes_color_offset;
871                 greybg.blue = normalbg.blue - prefs_common.stripes_color_offset;
872         } else if (normalbg.red < 0x8888 && normalbg.green < 0x8888 && normalbg.blue < 0x8888) {
873                 greybg.pixel = normalbg.pixel;
874                 greybg.red = normalbg.red + prefs_common.stripes_color_offset;
875                 greybg.green = normalbg.green + prefs_common.stripes_color_offset;
876                 greybg.blue = normalbg.blue + prefs_common.stripes_color_offset;
877         } else {
878                 color_change = FALSE;
879         }
880   }
881
882   cm_return_if_fail (clist != NULL);
883
884   /* bail now if we arn't drawable yet */
885   if (!gtkut_widget_is_drawable (GTK_WIDGET(clist)) || row < 0 || row >= clist->rows)
886     return;
887
888   widget = GTK_WIDGET (clist);
889   ctree  = GTK_CMCTREE  (clist);
890
891   /* if the function is passed the pointer to the row instead of null,
892    * it avoids this expensive lookup */
893   if (!clist_row)
894     clist_row = (g_list_nth (clist->row_list, row))->data;
895
896   /* rectangle of the entire row */
897   row_rectangle.x = 0;
898   row_rectangle.y = ROW_TOP_YPIXEL (clist, row);
899   row_rectangle.width = clist->clist_window_width;
900   row_rectangle.height = clist->row_height;
901
902   /* rectangle of the cell spacing above the row */
903   cell_rectangle.x = 0;
904   cell_rectangle.y = row_rectangle.y - CELL_SPACING;
905   cell_rectangle.width = row_rectangle.width;
906   cell_rectangle.height = CELL_SPACING;
907
908   /* rectangle used to clip drawing operations, its y and height
909    * positions only need to be set once, so we set them once here. 
910    * the x and width are set withing the drawing loop below once per
911    * column */
912   clip_rectangle.y = row_rectangle.y;
913   clip_rectangle.height = row_rectangle.height;
914
915   if (prefs_common.use_stripes_everywhere && GTK_SCTREE(ctree)->show_stripes
916       && color_change && row % 2) {
917     clist_row->background = greybg;
918     clist_row->bg_set = TRUE;
919   } else {
920     clist_row->bg_set = FALSE;
921   }
922   if (clist_row->state == GTK_STATE_NORMAL)
923     {
924       if (clist_row->fg_set)
925         gdk_gc_set_foreground (clist->fg_gc, &clist_row->foreground);
926       if (clist_row->bg_set)
927         gdk_gc_set_rgb_fg_color (clist->bg_gc, &clist_row->background);
928     }
929   
930   state = clist_row->state;
931
932   gdk_gc_set_foreground (ctree->lines_gc,
933                          &widget->style->fg[clist_row->state]);
934
935   /* draw the cell borders */
936   if (area)
937     {
938       rect = &intersect_rectangle;
939       crect = &intersect_rectangle;
940
941       if (gdk_rectangle_intersect (area, &cell_rectangle, crect))
942         gdk_draw_rectangle (clist->clist_window,
943                             widget->style->base_gc[GTK_STATE_NORMAL], TRUE,
944                             crect->x, crect->y, crect->width, crect->height);
945     }
946   else
947     {
948       rect = &clip_rectangle;
949       crect = &cell_rectangle;
950
951       gdk_draw_rectangle (clist->clist_window,
952                           widget->style->base_gc[GTK_STATE_NORMAL], TRUE,
953                           crect->x, crect->y, crect->width, crect->height);
954     }
955
956   /* horizontal black lines */
957   if (ctree->line_style == GTK_CMCTREE_LINES_TABBED)
958     { 
959
960       column_right = (COLUMN_LEFT_XPIXEL (clist, ctree->tree_column) +
961                       clist->column[ctree->tree_column].area.width +
962                       COLUMN_INSET);
963       column_left = (COLUMN_LEFT_XPIXEL (clist, ctree->tree_column) -
964                      COLUMN_INSET - (ctree->tree_column != 0) * CELL_SPACING);
965
966       switch (clist->column[ctree->tree_column].justification)
967         {
968         case GTK_JUSTIFY_CENTER:
969         case GTK_JUSTIFY_FILL:
970         case GTK_JUSTIFY_LEFT:
971           offset = (column_left + ctree->tree_indent *
972                     (((GtkCMCTreeRow *)clist_row)->level - 1));
973
974           gdk_draw_line (clist->clist_window, ctree->lines_gc, 
975                          MIN (offset + TAB_SIZE, column_right),
976                          cell_rectangle.y,
977                          clist->clist_window_width, cell_rectangle.y);
978           break;
979         case GTK_JUSTIFY_RIGHT:
980           offset = (column_right - 1 - ctree->tree_indent *
981                     (((GtkCMCTreeRow *)clist_row)->level - 1));
982
983           gdk_draw_line (clist->clist_window, ctree->lines_gc,
984                          -1, cell_rectangle.y,
985                          MAX (offset - TAB_SIZE, column_left),
986                          cell_rectangle.y);
987           break;
988         }
989     }
990
991   /* the last row has to clear its bottom cell spacing too */
992   if (clist_row == clist->row_list_end->data)
993     {
994       cell_rectangle.y += clist->row_height + CELL_SPACING;
995
996       if (!area || gdk_rectangle_intersect (area, &cell_rectangle, crect))
997         {
998           gdk_draw_rectangle (clist->clist_window,
999                               widget->style->base_gc[GTK_STATE_NORMAL], TRUE,
1000                               crect->x, crect->y, crect->width, crect->height);
1001
1002           /* horizontal black lines */
1003           if (ctree->line_style == GTK_CMCTREE_LINES_TABBED)
1004             { 
1005               switch (clist->column[ctree->tree_column].justification)
1006                 {
1007                 case GTK_JUSTIFY_CENTER:
1008                 case GTK_JUSTIFY_FILL:
1009                 case GTK_JUSTIFY_LEFT:
1010                   gdk_draw_line (clist->clist_window, ctree->lines_gc, 
1011                                  MIN (column_left + TAB_SIZE + COLUMN_INSET +
1012                                       (((GtkCMCTreeRow *)clist_row)->level > 1) *
1013                                       MIN (ctree->tree_indent / 2, TAB_SIZE),
1014                                       column_right),
1015                                  cell_rectangle.y,
1016                                  clist->clist_window_width, cell_rectangle.y);
1017                   break;
1018                 case GTK_JUSTIFY_RIGHT:
1019                   gdk_draw_line (clist->clist_window, ctree->lines_gc, 
1020                                  -1, cell_rectangle.y,
1021                                  MAX (column_right - TAB_SIZE - 1 -
1022                                       COLUMN_INSET -
1023                                       (((GtkCMCTreeRow *)clist_row)->level > 1) *
1024                                       MIN (ctree->tree_indent / 2, TAB_SIZE),
1025                                       column_left - 1), cell_rectangle.y);
1026                   break;
1027                 }
1028             }
1029         }
1030     }     
1031
1032   for (last_column = clist->columns - 1;
1033        last_column >= 0 && !clist->column[last_column].visible; last_column--)
1034     ;
1035
1036   /* iterate and draw all the columns (row cells) and draw their contents */
1037   for (i = 0; i < clist->columns; i++)
1038     {
1039       GtkStyle *style;
1040       GdkGC *fg_gc; 
1041       GdkGC *bg_gc;
1042       PangoLayout *layout = NULL;
1043       PangoRectangle logical_rect;
1044
1045       gint width;
1046       gint height;
1047       gint pixbuf_width;
1048       gint string_width;
1049       gint old_offset;
1050
1051       if (!clist->column[i].visible)
1052         continue;
1053
1054       gtk_sctree_get_cell_style (clist, clist_row, state, row, i, &style, &fg_gc, &bg_gc);
1055
1056       /* calculate clipping region */
1057       clip_rectangle.x = clist->column[i].area.x + clist->hoffset;
1058       clip_rectangle.width = clist->column[i].area.width;
1059
1060       cell_rectangle.x = clip_rectangle.x - COLUMN_INSET - CELL_SPACING;
1061       cell_rectangle.width = (clip_rectangle.width + 2 * COLUMN_INSET +
1062                               (1 + (i == last_column)) * CELL_SPACING);
1063       cell_rectangle.y = clip_rectangle.y;
1064       cell_rectangle.height = clip_rectangle.height;
1065
1066       string_width = 0;
1067       pixbuf_width = 0;
1068       height = 0;
1069
1070       if (area && !gdk_rectangle_intersect (area, &cell_rectangle,
1071                                             &intersect_rectangle))
1072         {
1073           if (i != ctree->tree_column)
1074             continue;
1075         }
1076       else
1077         {
1078           gdk_draw_rectangle (clist->clist_window, bg_gc, TRUE,
1079                               crect->x, crect->y, crect->width, crect->height);
1080
1081
1082           layout = sc_gtk_cmclist_create_cell_layout (clist, clist_row, i);
1083           if (layout)
1084             {
1085               pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
1086               width = logical_rect.width;
1087             }
1088           else
1089             width = 0;
1090
1091           switch (clist_row->cell[i].type)
1092             {
1093             case GTK_CMCELL_PIXBUF:
1094               pixbuf_width = gdk_pixbuf_get_width(GTK_CMCELL_PIXBUF (clist_row->cell[i])->pixbuf);
1095               height = gdk_pixbuf_get_height(GTK_CMCELL_PIXBUF (clist_row->cell[i])->pixbuf);
1096               width += pixbuf_width;
1097               break;
1098             case GTK_CMCELL_PIXTEXT:
1099               if (GTK_CMCELL_PIXTEXT (clist_row->cell[i])->pixbuf)
1100                 {
1101                   pixbuf_width = gdk_pixbuf_get_width(GTK_CMCELL_PIXTEXT (clist_row->cell[i])->pixbuf);
1102                   height = gdk_pixbuf_get_height(GTK_CMCELL_PIXTEXT (clist_row->cell[i])->pixbuf);
1103                   width += pixbuf_width;
1104                 }
1105
1106               if (GTK_CMCELL_PIXTEXT (clist_row->cell[i])->text &&
1107                   GTK_CMCELL_PIXTEXT (clist_row->cell[i])->pixbuf)
1108                 width +=  GTK_CMCELL_PIXTEXT (clist_row->cell[i])->spacing;
1109
1110               if (i == ctree->tree_column)
1111                 width += (ctree->tree_indent *
1112                           ((GtkCMCTreeRow *)clist_row)->level);
1113               break;
1114             default:
1115               break;
1116             }
1117
1118           switch (clist->column[i].justification)
1119             {
1120             case GTK_JUSTIFY_LEFT:
1121               offset = clip_rectangle.x + clist_row->cell[i].horizontal;
1122               break;
1123             case GTK_JUSTIFY_RIGHT:
1124               offset = (clip_rectangle.x + clist_row->cell[i].horizontal +
1125                         clip_rectangle.width - width);
1126               break;
1127             case GTK_JUSTIFY_CENTER:
1128             case GTK_JUSTIFY_FILL:
1129               offset = (clip_rectangle.x + clist_row->cell[i].horizontal +
1130                         (clip_rectangle.width / 2) - (width / 2));
1131               break;
1132             };
1133
1134           if (i != ctree->tree_column)
1135             {
1136               int start_y = (clip_rectangle.height - height) / 2;
1137               if (GTK_CMCLIST_ROW_HEIGHT_SET(GTK_CMCLIST(clist)))
1138                       start_y = (clip_rectangle.height/2 - height) / 2;
1139
1140               offset += clist_row->cell[i].horizontal;
1141               switch (clist_row->cell[i].type)
1142                 {
1143                 case GTK_CMCELL_PIXBUF:
1144                   gtk_sctree_draw_cell_pixbuf
1145                     (clist->clist_window, &clip_rectangle, fg_gc,
1146                      GTK_CMCELL_PIXBUF (clist_row->cell[i])->pixbuf,
1147                      offset,
1148                      clip_rectangle.y + clist_row->cell[i].vertical +
1149                      start_y,
1150                      pixbuf_width, height);
1151                   break;
1152                 case GTK_CMCELL_PIXTEXT:
1153                   offset = gtk_sctree_draw_cell_pixbuf
1154                     (clist->clist_window, &clip_rectangle, fg_gc,
1155                      GTK_CMCELL_PIXTEXT (clist_row->cell[i])->pixbuf,
1156                      offset,
1157                      clip_rectangle.y + clist_row->cell[i].vertical +
1158                      start_y,
1159                      pixbuf_width, height);
1160                   offset += GTK_CMCELL_PIXTEXT (clist_row->cell[i])->spacing;
1161
1162                   /* Fall through */
1163                 case GTK_CMCELL_TEXT:
1164                   if (layout)
1165                     {
1166                       gint row_center_offset = (clist->row_height - logical_rect.height) / 2;
1167
1168                       gdk_gc_set_clip_rectangle (fg_gc, &clip_rectangle);
1169                       gdk_draw_layout (clist->clist_window, fg_gc,
1170                                        offset,
1171                                        row_rectangle.y + row_center_offset + clist_row->cell[i].vertical,
1172                                        layout);
1173                       gdk_gc_set_clip_rectangle (fg_gc, NULL);
1174                       g_object_unref (G_OBJECT (layout));
1175                     }
1176                   break;
1177                 default:
1178                   break;
1179                 }
1180               continue;
1181             }
1182         }
1183
1184       if (bg_gc == clist->bg_gc)
1185         gdk_gc_set_background (ctree->lines_gc, &clist_row->background);
1186
1187       /* draw ctree->tree_column */
1188       cell_rectangle.y -= CELL_SPACING;
1189       cell_rectangle.height += CELL_SPACING;
1190
1191       if (area && !gdk_rectangle_intersect (area, &cell_rectangle,
1192                                             &intersect_rectangle))
1193         {
1194           if (layout)
1195             g_object_unref (G_OBJECT (layout));
1196           continue;
1197         }
1198
1199       /* draw lines */
1200       offset = gtk_sctree_draw_lines (ctree, (GtkCMCTreeRow *)clist_row, row, i,
1201                                      state, &clip_rectangle, &cell_rectangle,
1202                                      crect, area, style);
1203
1204       /* draw expander */
1205       offset = gtk_sctree_draw_expander (ctree, (GtkCMCTreeRow *)clist_row,
1206                                         style, &clip_rectangle, offset);
1207
1208       if (clist->column[i].justification == GTK_JUSTIFY_RIGHT)
1209         offset -= ctree->tree_spacing;
1210       else
1211         offset += ctree->tree_spacing;
1212
1213       if (clist->column[i].justification == GTK_JUSTIFY_RIGHT)
1214         offset -= (pixbuf_width + clist_row->cell[i].horizontal);
1215       else
1216         offset += clist_row->cell[i].horizontal;
1217
1218       old_offset = offset;
1219       offset = gtk_sctree_draw_cell_pixbuf (clist->clist_window, &clip_rectangle, fg_gc,
1220                                  GTK_CMCELL_PIXTEXT (clist_row->cell[i])->pixbuf,
1221                                  offset, 
1222                                  clip_rectangle.y + clist_row->cell[i].vertical
1223                                  + (clip_rectangle.height - height) / 2,
1224                                  pixbuf_width, height);
1225
1226       if (layout)
1227         {
1228           gint row_center_offset = (clist->row_height - logical_rect.height) / 2;
1229           
1230           if (clist->column[i].justification == GTK_JUSTIFY_RIGHT)
1231             {
1232               offset = (old_offset - string_width);
1233               if (GTK_CMCELL_PIXTEXT (clist_row->cell[i])->pixbuf)
1234                 offset -= GTK_CMCELL_PIXTEXT (clist_row->cell[i])->spacing;
1235             }
1236           else
1237             {
1238               if (GTK_CMCELL_PIXTEXT (clist_row->cell[i])->pixbuf)
1239                 offset += GTK_CMCELL_PIXTEXT (clist_row->cell[i])->spacing;
1240             }
1241           
1242           gdk_gc_set_clip_rectangle (fg_gc, &clip_rectangle);
1243           gdk_draw_layout (clist->clist_window, fg_gc,
1244                            offset,
1245                            row_rectangle.y + row_center_offset + clist_row->cell[i].vertical,
1246                            layout);
1247
1248           g_object_unref (G_OBJECT (layout));
1249         }
1250       gdk_gc_set_clip_rectangle (fg_gc, NULL);
1251     }
1252
1253   /* draw focus rectangle */
1254   if (clist->focus_row == row &&
1255       gtkut_widget_get_can_focus (widget) && gtkut_widget_has_focus (widget))
1256     {
1257       if (!area)
1258         gdk_draw_rectangle (clist->clist_window, clist->xor_gc, FALSE,
1259                             row_rectangle.x, row_rectangle.y,
1260                             row_rectangle.width - 1, row_rectangle.height - 1);
1261       else if (gdk_rectangle_intersect (area, &row_rectangle,
1262                                         &intersect_rectangle))
1263         {
1264           gdk_gc_set_clip_rectangle (clist->xor_gc, &intersect_rectangle);
1265           gdk_draw_rectangle (clist->clist_window, clist->xor_gc, FALSE,
1266                               row_rectangle.x, row_rectangle.y,
1267                               row_rectangle.width - 1,
1268                               row_rectangle.height - 1);
1269           gdk_gc_set_clip_rectangle (clist->xor_gc, NULL);
1270         }
1271     }
1272 }
1273
1274 static void
1275 gtk_sctree_change_focus_row_expansion (GtkCMCTree          *ctree,
1276                             GtkCMCTreeExpansionType action)
1277 {
1278   GtkCMCList *clist;
1279   GtkCMCTreeNode *node;
1280
1281   cm_return_if_fail (GTK_IS_CMCTREE (ctree));
1282
1283   clist = GTK_CMCLIST (ctree);
1284
1285   if (gdk_display_pointer_is_grabbed (gtk_widget_get_display (GTK_WIDGET (ctree))) && 
1286       gtkut_widget_has_grab (GTK_WIDGET(ctree)))
1287     return;
1288   
1289   if (!(node =
1290         GTK_CMCTREE_NODE (g_list_nth (clist->row_list, clist->focus_row))) ||
1291       GTK_CMCTREE_ROW (node)->is_leaf || !(GTK_CMCTREE_ROW (node)->children))
1292     return;
1293
1294   switch (action)
1295     {
1296     case GTK_CMCTREE_EXPANSION_EXPAND:
1297       if (GTK_SCTREE(ctree)->always_expand_recursively)
1298               gtk_cmctree_expand_recursive (ctree, node);
1299       else
1300               gtk_cmctree_expand (ctree, node);
1301
1302       break;
1303     case GTK_CMCTREE_EXPANSION_EXPAND_RECURSIVE:
1304       gtk_cmctree_expand_recursive (ctree, node);
1305       break;
1306     case GTK_CMCTREE_EXPANSION_COLLAPSE:
1307       gtk_cmctree_collapse (ctree, node);
1308       break;
1309     case GTK_CMCTREE_EXPANSION_COLLAPSE_RECURSIVE:
1310       gtk_cmctree_collapse_recursive (ctree, node);
1311       break;
1312     case GTK_CMCTREE_EXPANSION_TOGGLE:
1313       if (GTK_SCTREE(ctree)->always_expand_recursively)
1314               gtk_cmctree_toggle_expansion_recursive (ctree, node);
1315       else
1316               gtk_cmctree_toggle_expansion (ctree, node);
1317       break;
1318     case GTK_CMCTREE_EXPANSION_TOGGLE_RECURSIVE:
1319       gtk_cmctree_toggle_expansion_recursive (ctree, node);
1320       break;
1321     }
1322 }
1323
1324 static void gtk_sctree_finalize(GObject *object)
1325 {
1326         GtkSCTree *sctree = GTK_SCTREE(object);
1327         g_free(sctree->use_markup);
1328         sctree->use_markup = NULL;
1329         G_OBJECT_CLASS (parent_class)->finalize (object);
1330 }
1331
1332 /* Standard class initialization function */
1333 static void
1334 gtk_sctree_class_init (GtkSCTreeClass *klass)
1335 {
1336         GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
1337         GtkObjectClass *object_class;
1338         GtkWidgetClass *widget_class;
1339         GtkCMCListClass *clist_class;
1340         GtkCMCTreeClass *ctree_class;
1341
1342         object_class = (GtkObjectClass *) klass;
1343         widget_class = (GtkWidgetClass *) klass;
1344         clist_class = (GtkCMCListClass *) klass;
1345         ctree_class = (GtkCMCTreeClass *) klass;
1346
1347         parent_class = g_type_class_peek (gtk_cmctree_get_type ());
1348
1349         sctree_signals[ROW_POPUP_MENU] =
1350                 g_signal_new ("row_popup_menu",
1351                               G_TYPE_FROM_CLASS (klass),
1352                               G_SIGNAL_RUN_FIRST,
1353                               G_STRUCT_OFFSET (GtkSCTreeClass, row_popup_menu),
1354                               NULL, NULL,
1355                               claws_marshal_VOID__POINTER,
1356                               G_TYPE_NONE, 1,
1357                               GDK_TYPE_EVENT);
1358         sctree_signals[EMPTY_POPUP_MENU] =
1359                 g_signal_new ("empty_popup_menu",
1360                               G_TYPE_FROM_CLASS (klass),
1361                               G_SIGNAL_RUN_FIRST,
1362                               G_STRUCT_OFFSET (GtkSCTreeClass, empty_popup_menu),
1363                               NULL, NULL,
1364                               claws_marshal_VOID__POINTER,
1365                               G_TYPE_NONE, 1,
1366                               GDK_TYPE_EVENT);
1367         sctree_signals[OPEN_ROW] =
1368                 g_signal_new ("open_row",
1369                               G_TYPE_FROM_CLASS (klass),
1370                               G_SIGNAL_RUN_FIRST,
1371                               G_STRUCT_OFFSET (GtkSCTreeClass, open_row),
1372                               NULL, NULL,
1373                               g_cclosure_marshal_VOID__VOID,
1374                               G_TYPE_NONE, 0);
1375         sctree_signals[START_DRAG] =
1376                 g_signal_new ("start_drag",
1377                               G_TYPE_FROM_CLASS (klass),
1378                               G_SIGNAL_RUN_FIRST,
1379                               G_STRUCT_OFFSET (GtkSCTreeClass, start_drag),
1380                               NULL, NULL,
1381                               claws_marshal_VOID__INT_POINTER,
1382                               G_TYPE_NONE, 2,
1383                               G_TYPE_INT,
1384                               GDK_TYPE_EVENT);
1385
1386         /* gtk_object_class_add_signals (object_class, sctree_signals, LAST_SIGNAL); */
1387
1388         clist_class->clear = gtk_sctree_clear;
1389         clist_class->draw_row = gtk_sctree_draw_row;
1390         clist_class->unselect_all = gtk_sctree_real_unselect_all;
1391         ctree_class->tree_collapse = gtk_sctree_real_tree_collapse;
1392         ctree_class->tree_expand = gtk_sctree_real_tree_expand;
1393         ctree_class->tree_move = sreal_tree_move;
1394         ctree_class->change_focus_row_expansion = gtk_sctree_change_focus_row_expansion;
1395         
1396         widget_class->button_press_event = gtk_sctree_button_press;
1397         widget_class->button_release_event = gtk_sctree_button_release;
1398         widget_class->motion_notify_event = gtk_sctree_motion;
1399         widget_class->drag_begin = gtk_sctree_drag_begin;
1400         widget_class->drag_end = gtk_sctree_drag_end;
1401         widget_class->drag_data_get = gtk_sctree_drag_data_get;
1402         widget_class->drag_leave = gtk_sctree_drag_leave;
1403         widget_class->drag_motion = gtk_sctree_drag_motion;
1404         widget_class->drag_drop = gtk_sctree_drag_drop;
1405         widget_class->drag_data_received = gtk_sctree_drag_data_received;
1406         
1407         gobject_class->finalize = gtk_sctree_finalize;
1408 }
1409
1410 /* Standard object initialization function */
1411 static void
1412 gtk_sctree_init (GtkSCTree *sctree)
1413 {
1414         sctree->anchor_row = NULL;
1415
1416         /* GtkCMCTree does not specify pointer motion by default */
1417         gtk_widget_add_events (GTK_WIDGET (sctree), GDK_POINTER_MOTION_MASK);
1418         gtk_widget_add_events (GTK_WIDGET (sctree), GDK_POINTER_MOTION_MASK);
1419 }
1420
1421 /* Get information the specified row is selected. */
1422
1423 static gboolean
1424 row_is_selected(GtkSCTree *sctree, gint row)
1425 {
1426         GtkCMCListRow *clist_row;
1427         clist_row =  g_list_nth (GTK_CMCLIST(sctree)->row_list, row)->data;
1428         return clist_row ? clist_row->state == GTK_STATE_SELECTED : FALSE;
1429 }
1430
1431 /* Selects the rows between the anchor to the specified row, inclusive.  */
1432 static void
1433 select_range (GtkSCTree *sctree, gint row)
1434 {
1435         gint prev_row;
1436         gint min, max;
1437         gint i;
1438         GList *node;
1439         if (sctree->anchor_row == NULL) {
1440                 prev_row = row;
1441                 sctree->anchor_row = gtk_cmctree_node_nth(GTK_CMCTREE(sctree), row);
1442         } else
1443                 prev_row = g_list_position(GTK_CMCLIST(sctree)->row_list,
1444                                            (GList *)sctree->anchor_row);
1445
1446         if (row < prev_row) {
1447                 min = row;
1448                 max = prev_row;
1449                 GTK_CMCLIST(sctree)->focus_row = max;
1450         } else {
1451                 min = prev_row;
1452                 max = row;
1453         }
1454         sctree->selecting_range++;
1455         
1456         if (max < min) {
1457                 int t = min;
1458                 min = max;
1459                 max = t;
1460         }
1461         
1462         if (max - min > 10)
1463                 gtk_cmclist_freeze(GTK_CMCLIST(sctree));
1464
1465         node = g_list_nth((GTK_CMCLIST(sctree))->row_list, min);
1466         for (i = min; i < max; i++) {
1467                 if (node && GTK_CMCTREE_ROW (node)->row.selectable) {
1468                         g_signal_emit_by_name(G_OBJECT(sctree), "tree_select_row",
1469                                 node, -1);
1470                 }
1471                 node = node->next;
1472         }
1473         if (max - min > 10)
1474                 gtk_cmclist_thaw(GTK_CMCLIST(sctree));
1475
1476
1477         sctree->selecting_range--;
1478         gtk_cmclist_select_row (GTK_CMCLIST (sctree), max, -1);
1479 }
1480
1481 /* Handles row selection according to the specified modifier state */
1482 /* in certain cases, we arrive here from a function knowing the GtkCMCTreeNode, and having
1483  * already slowly found row using g_list_position. In which case, _node will be non-NULL
1484  * to avoid this function having to slowly find it with g_list_nth. */
1485 static void
1486 select_row (GtkSCTree *sctree, gint row, gint col, guint state, GtkCMCTreeNode *_node)
1487 {
1488         gboolean range, additive;
1489         cm_return_if_fail (sctree != NULL);
1490         cm_return_if_fail (GTK_IS_SCTREE (sctree));
1491     
1492         range = ((state & GDK_SHIFT_MASK) != 0) &&
1493                 (GTK_CMCLIST(sctree)->selection_mode != GTK_SELECTION_SINGLE) &&
1494                 (GTK_CMCLIST(sctree)->selection_mode != GTK_SELECTION_BROWSE);
1495         additive = ((state & GDK_CONTROL_MASK) != 0) &&
1496                    (GTK_CMCLIST(sctree)->selection_mode != GTK_SELECTION_SINGLE) &&
1497                    (GTK_CMCLIST(sctree)->selection_mode != GTK_SELECTION_BROWSE);
1498
1499         if (!range && !additive && sctree->force_additive_sel)
1500                 additive = TRUE;
1501
1502         GTK_CMCLIST(sctree)->focus_row = row;
1503
1504         if (!additive) {
1505                 gtk_cmclist_unselect_all (GTK_CMCLIST (sctree));
1506         }
1507
1508         if (!range) {
1509                 GtkCMCTreeNode *node;
1510
1511                 node = _node ? _node : gtk_cmctree_node_nth (GTK_CMCTREE(sctree), row);
1512
1513                 /*No need to manage overlapped list*/
1514                 if (additive) {
1515                         if (row_is_selected(sctree, row))
1516                                 gtk_cmclist_unselect_row (GTK_CMCLIST (sctree), row, col);
1517                         else
1518                                 g_signal_emit_by_name
1519                                         (G_OBJECT (sctree),
1520                                          "tree_select_row", node, col);
1521                 } else {
1522                         g_signal_emit_by_name
1523                                 (G_OBJECT (sctree),
1524                                  "tree_select_row", node, col);
1525                 }
1526                 sctree->anchor_row = node;
1527         } else
1528                 select_range (sctree, row);
1529 }
1530
1531 static gboolean
1532 sctree_is_hot_spot (GtkSCTree     *sctree, 
1533                    GtkCMCTreeNode *node,
1534                    gint          row, 
1535                    gint          x, 
1536                    gint          y)
1537 {
1538   GtkCMCTreeRow *tree_row;
1539   GtkCMCList *clist;
1540   GtkCMCTree *ctree;
1541   GtkCMCellPixText *cell;
1542   gint xl, xmax;
1543   gint yu;
1544   
1545   cm_return_val_if_fail (GTK_IS_SCTREE (sctree), FALSE);
1546   cm_return_val_if_fail (node != NULL, FALSE);
1547
1548   clist = GTK_CMCLIST (sctree);
1549   ctree = GTK_CMCTREE (sctree);
1550
1551   if (!clist->column[ctree->tree_column].visible ||
1552       ctree->expander_style == GTK_CMCTREE_EXPANDER_NONE)
1553     return FALSE;
1554
1555   tree_row = GTK_CMCTREE_ROW (node);
1556
1557   cell = GTK_CMCELL_PIXTEXT (tree_row->row.cell[ctree->tree_column]);
1558
1559   if (!GTK_CMCLIST_ROW_HEIGHT_SET(GTK_CMCLIST(clist)))
1560      yu = (ROW_TOP_YPIXEL (clist, row) + (clist->row_height - PM_SIZE) / 2 -
1561         (clist->row_height - 1) % 2);
1562   else
1563      yu = (ROW_TOP_YPIXEL (clist, row) + (clist->row_height/2 - PM_SIZE) / 2 -
1564         (clist->row_height/2 - 1) % 2);
1565
1566 #ifndef GENERIC_UMPC
1567   if (clist->column[ctree->tree_column].justification == GTK_JUSTIFY_RIGHT)
1568     xl = (clist->column[ctree->tree_column].area.x + 
1569           clist->column[ctree->tree_column].area.width - 1 + clist->hoffset -
1570           (tree_row->level - 1) * ctree->tree_indent - PM_SIZE -
1571           (ctree->line_style == GTK_CMCTREE_LINES_TABBED) * 3);
1572   else
1573     xl = (clist->column[ctree->tree_column].area.x + clist->hoffset +
1574           (tree_row->level - 1) * ctree->tree_indent +
1575           (ctree->line_style == GTK_CMCTREE_LINES_TABBED) * 3);
1576
1577   xmax = xl + PM_SIZE;
1578 #else
1579   if (clist->column[ctree->tree_column].justification == GTK_JUSTIFY_RIGHT) {
1580     xl = (clist->column[ctree->tree_column].area.x + 
1581           clist->column[ctree->tree_column].area.width - 1 + clist->hoffset -
1582           (tree_row->level - 1) * ctree->tree_indent - PM_SIZE -
1583           (ctree->line_style == GTK_CMCTREE_LINES_TABBED) * 3);
1584     xmax = xl + PM_SIZE;
1585   } else if (ctree->tree_column == 0) {
1586     xl = (clist->column[ctree->tree_column].area.x + clist->hoffset +
1587           (ctree->line_style == GTK_CMCTREE_LINES_TABBED) * 3);
1588     xmax = (clist->column[ctree->tree_column].area.x + clist->hoffset +
1589            (tree_row->level - 1) * ctree->tree_indent +
1590            (ctree->line_style == GTK_CMCTREE_LINES_TABBED) * 3) +
1591            PM_SIZE;
1592   } else {
1593     xl = (clist->column[ctree->tree_column].area.x + clist->hoffset +
1594           (tree_row->level - 1) * ctree->tree_indent +
1595           (ctree->line_style == GTK_CMCTREE_LINES_TABBED) * 3);
1596     xmax = xl + PM_SIZE;
1597   }
1598 #endif
1599   return (x >= xl && x <= xmax && y >= yu && y <= yu + PM_SIZE);
1600 }
1601
1602 gboolean
1603 gtk_sctree_is_hot_spot (GtkSCTree *ctree, 
1604                        gint      x, 
1605                        gint      y)
1606 {
1607   GtkCMCTreeNode *node;
1608   gint column;
1609   gint row;
1610   
1611   cm_return_val_if_fail (GTK_IS_SCTREE (ctree), FALSE);
1612
1613   if (gtk_cmclist_get_selection_info (GTK_CMCLIST (ctree), x, y, &row, &column))
1614     if ((node = GTK_CMCTREE_NODE(g_list_nth (GTK_CMCLIST (ctree)->row_list, row))))
1615       return sctree_is_hot_spot (ctree, node, row, x, y);
1616
1617   return FALSE;
1618 }
1619
1620 /* Our handler for button_press events.  We override all of GtkCMCList's broken
1621  * behavior.
1622  */
1623 static gint
1624 gtk_sctree_button_press (GtkWidget *widget, GdkEventButton *event)
1625 {
1626         GtkSCTree *sctree;
1627         GtkCMCList *clist;
1628         gboolean on_row;
1629         gint row;
1630         gint col;
1631         gint retval;
1632
1633         cm_return_val_if_fail (widget != NULL, FALSE);
1634         cm_return_val_if_fail (GTK_IS_SCTREE (widget), FALSE);
1635         cm_return_val_if_fail (event != NULL, FALSE);
1636
1637         sctree = GTK_SCTREE (widget);
1638         clist = GTK_CMCLIST (widget);
1639         retval = FALSE;
1640
1641         if (event->window != clist->clist_window)
1642                 return (* GTK_WIDGET_CLASS (parent_class)->button_press_event) (widget, event);
1643
1644         on_row = gtk_cmclist_get_selection_info (clist, event->x, event->y, &row, &col);
1645
1646         if (on_row && !gtkut_widget_has_focus(widget))
1647                 gtk_widget_grab_focus (widget);
1648
1649         if (gtk_sctree_is_hot_spot (GTK_SCTREE(sctree), event->x, event->y)) {
1650                 GtkCMCTreeNode *node = gtk_cmctree_node_nth(GTK_CMCTREE(sctree), row);
1651                 if (GTK_CMCTREE_ROW (node)->expanded)
1652                         gtk_cmctree_collapse(GTK_CMCTREE(sctree), node);
1653                 else if (GTK_SCTREE(sctree)->always_expand_recursively)
1654                         gtk_cmctree_expand_recursive (GTK_CMCTREE(sctree), node);
1655                 else
1656                         gtk_cmctree_expand(GTK_CMCTREE(sctree), node);
1657                 return TRUE;
1658         }
1659
1660         switch (event->type) {
1661         case GDK_BUTTON_PRESS:
1662                 if (event->button == 1 || event->button == 2) {
1663                         if (event->button == 2)
1664                                 event->state &= ~(GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1665                         if (on_row) {
1666                                 /* Save the mouse info for DnD */
1667                                 sctree->dnd_press_button = event->button;
1668                                 sctree->dnd_press_x = event->x;
1669                                 sctree->dnd_press_y = event->y;
1670
1671                                 /* Handle selection */
1672                                 if ((row_is_selected (sctree, row)
1673                                      && !(event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK)))
1674                                     || ((event->state & GDK_CONTROL_MASK)
1675                                         && !(event->state & GDK_SHIFT_MASK))) {
1676                                         sctree->dnd_select_pending = TRUE;
1677                                         sctree->dnd_select_pending_state = event->state;
1678                                         sctree->dnd_select_pending_row = row;
1679                                 } else {
1680                                         select_row (sctree, row, col, event->state, NULL);
1681                                 }
1682                         } else {
1683                                 gtk_cmclist_unselect_all (clist);
1684                         }
1685
1686                         retval = TRUE;
1687                 } else if (event->button == 3) {
1688                         /* Emit *_popup_menu signal*/
1689                         if (on_row) {
1690                                 if (!row_is_selected(sctree,row))
1691                                         select_row (sctree, row, col, 0, NULL);
1692                                 g_signal_emit (G_OBJECT (sctree),
1693                                                  sctree_signals[ROW_POPUP_MENU],
1694                                                  0, event);
1695                         } else {
1696                                 gtk_cmclist_unselect_all(clist);
1697                                 g_signal_emit (G_OBJECT (sctree),
1698                                                  sctree_signals[EMPTY_POPUP_MENU],
1699                                                  0, event);
1700                         }
1701                         retval = TRUE;
1702                 }
1703
1704                 break;
1705
1706         case GDK_2BUTTON_PRESS:
1707                 if (event->button != 1)
1708                         break;
1709
1710                 sctree->dnd_select_pending = FALSE;
1711                 sctree->dnd_select_pending_state = 0;
1712
1713                 if (on_row)
1714                         g_signal_emit (G_OBJECT (sctree),
1715                                        sctree_signals[OPEN_ROW], 0);
1716
1717                 retval = TRUE;
1718                 break;
1719
1720         default:
1721                 break;
1722         }
1723
1724         return retval;
1725 }
1726
1727 /* Our handler for button_release events.  We override all of GtkCMCList's broken
1728  * behavior.
1729  */
1730 static gint
1731 gtk_sctree_button_release (GtkWidget *widget, GdkEventButton *event)
1732 {
1733         GtkSCTree *sctree;
1734         GtkCMCList *clist;
1735         gint on_row;
1736         gint row, col;
1737         gint retval;
1738
1739         cm_return_val_if_fail (widget != NULL, FALSE);
1740         cm_return_val_if_fail (GTK_IS_SCTREE (widget), FALSE);
1741         cm_return_val_if_fail (event != NULL, FALSE);
1742
1743         sctree = GTK_SCTREE (widget);
1744         clist = GTK_CMCLIST (widget);
1745         retval = FALSE;
1746
1747         if (event->window != clist->clist_window)
1748                 return (* GTK_WIDGET_CLASS (parent_class)->button_release_event) (widget, event);
1749
1750         on_row = gtk_cmclist_get_selection_info (clist, event->x, event->y, &row, &col);
1751
1752         if (!(event->button == 1 || event->button == 2))
1753                 return FALSE;
1754
1755         sctree->dnd_press_button = 0;
1756         sctree->dnd_press_x = 0;
1757         sctree->dnd_press_y = 0;
1758
1759         if (on_row) {
1760                 if (sctree->dnd_select_pending) {
1761                         select_row (sctree, row, col, sctree->dnd_select_pending_state, NULL);
1762                         sctree->dnd_select_pending = FALSE;
1763                         sctree->dnd_select_pending_state = 0;
1764                 }
1765
1766                 retval = TRUE;
1767         }
1768
1769         return retval;
1770 }
1771
1772 /* Our handler for motion_notify events.  We override all of GtkCMCList's broken
1773  * behavior.
1774  */
1775 static gint
1776 gtk_sctree_motion (GtkWidget *widget, GdkEventMotion *event)
1777 {
1778         GtkSCTree *sctree;
1779         GtkCMCList *clist;
1780
1781         cm_return_val_if_fail (widget != NULL, FALSE);
1782         cm_return_val_if_fail (GTK_IS_SCTREE (widget), FALSE);
1783         cm_return_val_if_fail (event != NULL, FALSE);
1784
1785         sctree = GTK_SCTREE (widget);
1786         clist = GTK_CMCLIST (widget);
1787
1788         if (event->window != clist->clist_window)
1789                 return (* GTK_WIDGET_CLASS (parent_class)->motion_notify_event) (widget, event);
1790
1791         if (!((sctree->dnd_press_button == 1 && (event->state & GDK_BUTTON1_MASK))
1792               || (sctree->dnd_press_button == 2 && (event->state & GDK_BUTTON2_MASK))))
1793                 return FALSE;
1794
1795         /* This is the same threshold value that is used in gtkdnd.c */
1796
1797 #ifndef GENERIC_UMPC
1798 #define THRESHOLD 3
1799 #else
1800 #define THRESHOLD 8
1801 #endif
1802         if (MAX (ABS (sctree->dnd_press_x - event->x),
1803                  ABS (sctree->dnd_press_y - event->y)) <= THRESHOLD)
1804                 return FALSE;
1805
1806         /* Handle any pending selections */
1807
1808         if (sctree->dnd_select_pending) {
1809                 if (!row_is_selected(sctree,sctree->dnd_select_pending_row))
1810                         select_row (sctree,
1811                                     sctree->dnd_select_pending_row,
1812                                     -1,
1813                                     sctree->dnd_select_pending_state,
1814                                     NULL);
1815
1816                 sctree->dnd_select_pending = FALSE;
1817                 sctree->dnd_select_pending_state = 0;
1818         }
1819
1820         g_signal_emit (G_OBJECT (sctree),
1821                        sctree_signals[START_DRAG],
1822                        0,
1823                        sctree->dnd_press_button,
1824                        event);
1825         return TRUE;
1826 }
1827
1828 /* We override the drag_begin signal to do nothing */
1829 static void
1830 gtk_sctree_drag_begin (GtkWidget *widget, GdkDragContext *context)
1831 {
1832         /* nothing */
1833 }
1834
1835 /* We override the drag_end signal to do nothing */
1836 static void
1837 gtk_sctree_drag_end (GtkWidget *widget, GdkDragContext *context)
1838 {
1839         /* nothing */
1840 }
1841
1842 /* We override the drag_data_get signal to do nothing */
1843 static void
1844 gtk_sctree_drag_data_get (GtkWidget *widget, GdkDragContext *context,
1845                                      GtkSelectionData *data, guint info, guint time)
1846 {
1847         /* nothing */
1848 }
1849
1850 /* We override the drag_leave signal to do nothing */
1851 static void
1852 gtk_sctree_drag_leave (GtkWidget *widget, GdkDragContext *context, guint time)
1853 {
1854         /* nothing */
1855 }
1856
1857 /* We override the drag_motion signal to do nothing */
1858 static gboolean
1859 gtk_sctree_drag_motion (GtkWidget *widget, GdkDragContext *context,
1860                                    gint x, gint y, guint time)
1861 {
1862         return FALSE;
1863 }
1864
1865 /* We override the drag_drop signal to do nothing */
1866 static gboolean
1867 gtk_sctree_drag_drop (GtkWidget *widget, GdkDragContext *context,
1868                                  gint x, gint y, guint time)
1869 {
1870         return FALSE;
1871 }
1872
1873 /* We override the drag_data_received signal to do nothing */
1874 static void
1875 gtk_sctree_drag_data_received (GtkWidget *widget, GdkDragContext *context,
1876                                           gint x, gint y, GtkSelectionData *data,
1877                                           guint info, guint time)
1878 {
1879         /* nothing */
1880 }
1881
1882 /* Our handler for the clear signal of the clist.  We have to reset the anchor
1883  * to null.
1884  */
1885 static void
1886 gtk_sctree_clear (GtkCMCList *clist)
1887 {
1888         GtkSCTree *sctree;
1889
1890         cm_return_if_fail (clist != NULL);
1891         cm_return_if_fail (GTK_IS_SCTREE (clist));
1892
1893         sctree = GTK_SCTREE (clist);
1894         sctree->anchor_row = NULL;
1895
1896         if (((GtkCMCListClass *)parent_class)->clear)
1897                 (* ((GtkCMCListClass *)parent_class)->clear) (clist);
1898 }
1899
1900 static void
1901 gtk_sctree_real_unselect_all (GtkCMCList *clist)
1902 {
1903         GtkSCTree *sctree;
1904         gboolean should_freeze = FALSE;
1905
1906         cm_return_if_fail (clist != NULL);
1907         cm_return_if_fail (GTK_IS_SCTREE (clist));
1908
1909         sctree = GTK_SCTREE (clist);
1910
1911         if (sc_g_list_bigger(GTK_CMCLIST(sctree)->selection, 10)) {
1912                 should_freeze = TRUE;
1913                 sctree->selecting_range++;
1914                 gtk_cmclist_freeze (GTK_CMCLIST (sctree));
1915         }
1916
1917         if (((GtkCMCListClass *)parent_class)->unselect_all)
1918                 (* ((GtkCMCListClass *)parent_class)->unselect_all) (clist);
1919
1920         if (should_freeze) {
1921                 gtk_cmclist_thaw (GTK_CMCLIST (sctree));
1922                 sctree->selecting_range--;
1923         }
1924 }
1925
1926 static void
1927 gtk_sctree_column_auto_resize (GtkCMCList    *clist,
1928                     GtkCMCListRow *clist_row,
1929                     gint         column,
1930                     gint         old_width)
1931 {
1932   /* resize column if needed for auto_resize */
1933   GtkRequisition requisition;
1934
1935   if (!clist->column[column].auto_resize ||
1936       GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist))
1937     return;
1938
1939   if (clist_row)
1940     GTK_CMCLIST_GET_CLASS (clist)->cell_size_request (clist, clist_row,
1941                                                    column, &requisition);
1942   else
1943     requisition.width = 0;
1944
1945   if (requisition.width > clist->column[column].width)
1946     gtk_cmclist_set_column_width (clist, column, requisition.width);
1947   else if (requisition.width < old_width &&
1948            old_width == clist->column[column].width)
1949     {
1950       GList *list;
1951       gint new_width;
1952
1953       /* run a "gtk_cmclist_optimal_column_width" but break, if
1954        * the column doesn't shrink */
1955       if (GTK_CMCLIST_SHOW_TITLES (clist) && clist->column[column].button)
1956         new_width = (clist->column[column].button->requisition.width -
1957                      (CELL_SPACING + (2 * COLUMN_INSET)));
1958       else
1959         new_width = 0;
1960
1961       for (list = clist->row_list; list; list = list->next)
1962         {
1963           GTK_CMCLIST_GET_CLASS (clist)->cell_size_request
1964             (clist, GTK_CMCLIST_ROW (list), column, &requisition);
1965           new_width = MAX (new_width, requisition.width);
1966           if (new_width == clist->column[column].width)
1967             break;
1968         }
1969       if (new_width < clist->column[column].width)
1970         gtk_cmclist_set_column_width (clist, column, new_width);
1971     }
1972 }
1973
1974 static void
1975 gtk_sctree_auto_resize_columns (GtkCMCList *clist)
1976 {
1977   gint i;
1978
1979   if (GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist))
1980     return;
1981
1982   for (i = 0; i < clist->columns; i++)
1983     gtk_sctree_column_auto_resize (clist, NULL, i, clist->column[i].width);
1984 }
1985
1986 static void 
1987 gtk_sctree_real_tree_collapse (GtkCMCTree     *ctree,
1988                     GtkCMCTreeNode *node)
1989 {
1990   GtkCMCList *clist;
1991   GtkCMCTreeNode *work;
1992   GtkRequisition requisition;
1993   gboolean visible;
1994   gint level;
1995
1996   cm_return_if_fail (GTK_IS_CMCTREE (ctree));
1997
1998   if (!node || !GTK_CMCTREE_ROW (node)->expanded ||
1999       GTK_CMCTREE_ROW (node)->is_leaf)
2000     return;
2001
2002   clist = GTK_CMCLIST (ctree);
2003
2004   GTK_CMCLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
2005   
2006   GTK_CMCTREE_ROW (node)->expanded = FALSE;
2007   level = GTK_CMCTREE_ROW (node)->level;
2008
2009   visible = gtk_cmctree_is_viewable (ctree, node);
2010   /* get cell width if tree_column is auto resized */
2011   if (visible && clist->column[ctree->tree_column].auto_resize &&
2012       !GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist))
2013     GTK_CMCLIST_GET_CLASS (clist)->cell_size_request
2014       (clist, &GTK_CMCTREE_ROW (node)->row, ctree->tree_column, &requisition);
2015
2016   /* unref/unset opened pixbuf */
2017   if (GTK_CMCELL_PIXTEXT 
2018       (GTK_CMCTREE_ROW (node)->row.cell[ctree->tree_column])->pixbuf)
2019     {
2020       g_object_unref
2021         (GTK_CMCELL_PIXTEXT
2022          (GTK_CMCTREE_ROW (node)->row.cell[ctree->tree_column])->pixbuf);
2023       
2024       GTK_CMCELL_PIXTEXT
2025         (GTK_CMCTREE_ROW (node)->row.cell[ctree->tree_column])->pixbuf = NULL;
2026     }
2027
2028   /* set/ref closed pixbuf */
2029   if (GTK_CMCTREE_ROW (node)->pixbuf_closed)
2030     {
2031       GTK_CMCELL_PIXTEXT 
2032         (GTK_CMCTREE_ROW (node)->row.cell[ctree->tree_column])->pixbuf = 
2033         g_object_ref (GTK_CMCTREE_ROW (node)->pixbuf_closed);
2034     }
2035
2036   work = GTK_CMCTREE_ROW (node)->children;
2037   if (work)
2038     {
2039       gint tmp = 0;
2040       gint row;
2041       GList *list;
2042
2043       while (work && GTK_CMCTREE_ROW (work)->level > level)
2044         {
2045           work = GTK_CMCTREE_NODE_NEXT (work);
2046           tmp++;
2047         }
2048
2049       if (work)
2050         {
2051           list = (GList *)node;
2052           list->next = (GList *)work;
2053           list = (GList *)GTK_CMCTREE_NODE_PREV (work);
2054           list->next = NULL;
2055           list = (GList *)work;
2056           list->prev = (GList *)node;
2057         }
2058       else
2059         {
2060           list = (GList *)node;
2061           list->next = NULL;
2062           clist->row_list_end = (GList *)node;
2063         }
2064
2065       if (visible)
2066         {
2067           /* resize auto_resize columns if needed */
2068           gtk_sctree_auto_resize_columns (clist);
2069
2070           if (!GTK_SCTREE(clist)->sorting) {
2071                   row = g_list_position (clist->row_list, (GList *)node);
2072                   if (row < clist->focus_row)
2073                     clist->focus_row -= tmp;
2074           }
2075           clist->rows -= tmp;
2076           CLIST_REFRESH (clist);
2077         }
2078     }
2079   else if (visible && clist->column[ctree->tree_column].auto_resize &&
2080            !GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist))
2081     /* resize tree_column if needed */
2082     gtk_sctree_column_auto_resize (clist, &GTK_CMCTREE_ROW (node)->row, ctree->tree_column,
2083                         requisition.width);
2084     
2085 }
2086
2087
2088 GtkWidget *gtk_sctree_new_with_titles (gint columns, gint tree_column, 
2089                                        gchar *titles[])
2090 {
2091         GtkWidget *widget;
2092                                                                                                             
2093         cm_return_val_if_fail (columns > 0, NULL);
2094         cm_return_val_if_fail (tree_column >= 0, NULL);
2095         
2096         if (tree_column >= columns) {
2097                 g_warning("Wrong tree column");
2098                 tree_column = 0;
2099                 print_backtrace();
2100         }
2101         
2102         widget = gtk_widget_new (TYPE_GTK_SCTREE,
2103                                  "n_columns", columns,
2104                                  "tree_column", tree_column,
2105                                  NULL);
2106         if (titles) {
2107                 GtkCMCList *clist = GTK_CMCLIST (widget);
2108                 guint i;
2109
2110                 for (i = 0; i < columns; i++)
2111                         gtk_cmclist_set_column_title (clist, i, titles[i]);
2112                 gtk_cmclist_column_titles_show (clist);
2113         }
2114
2115         GTK_SCTREE(widget)->show_stripes = TRUE;
2116         GTK_SCTREE(widget)->always_expand_recursively = TRUE;
2117         GTK_SCTREE(widget)->force_additive_sel = FALSE;
2118         
2119         GTK_SCTREE(widget)->use_markup = g_new0(gboolean, columns);
2120
2121         return widget;
2122 }
2123
2124 void gtk_sctree_set_use_markup              (GtkSCTree          *sctree,
2125                                              int                 column,
2126                                              gboolean            markup)
2127 {
2128         gint columns = 0;
2129         GValue value = { 0 };
2130         
2131         cm_return_if_fail(GTK_IS_SCTREE(sctree));
2132
2133         g_value_init (&value, G_TYPE_INT);      
2134         g_object_get_property (G_OBJECT (sctree), "n-columns", &value);
2135         columns = g_value_get_int (&value);
2136         g_value_unset (&value);
2137
2138         cm_return_if_fail(column < columns);
2139
2140         sctree->use_markup[column] = markup;
2141 }
2142
2143 void gtk_sctree_select (GtkSCTree *sctree, GtkCMCTreeNode *node)
2144 {
2145         select_row(sctree, 
2146                    g_list_position(GTK_CMCLIST(sctree)->row_list, (GList *)node),
2147                    -1, 0, node);
2148 }
2149
2150 void gtk_sctree_select_with_state (GtkSCTree *sctree, GtkCMCTreeNode *node, int state)
2151 {
2152         select_row(sctree, 
2153                    g_list_position(GTK_CMCLIST(sctree)->row_list, (GList *)node),
2154                    -1, state, node);
2155 }
2156
2157 void gtk_sctree_unselect_all (GtkSCTree *sctree)
2158 {
2159         gtk_cmclist_unselect_all(GTK_CMCLIST(sctree));
2160         sctree->anchor_row = NULL;
2161 }
2162
2163 void gtk_sctree_set_anchor_row (GtkSCTree *sctree, GtkCMCTreeNode *node)
2164 {
2165         sctree->anchor_row = node;
2166 }
2167
2168 void gtk_sctree_remove_node (GtkSCTree *sctree, GtkCMCTreeNode *node)
2169 {
2170         if (sctree->anchor_row == node)
2171                 sctree->anchor_row = NULL;
2172         gtk_cmctree_remove_node(GTK_CMCTREE(sctree), node);
2173 }
2174
2175 void gtk_sctree_set_stripes(GtkSCTree  *sctree, gboolean show_stripes)
2176 {
2177         sctree->show_stripes = show_stripes;
2178 }
2179
2180 void gtk_sctree_set_recursive_expand(GtkSCTree  *sctree, gboolean rec_exp)
2181 {
2182         sctree->always_expand_recursively = rec_exp;
2183 }
2184
2185 /***********************************************************
2186  *             Tree sorting functions                      *
2187  ***********************************************************/
2188
2189 static void sink(GtkCMCList *clist, GPtrArray *numbers, gint root, gint bottom)
2190 {
2191         gint j, k ;
2192         GtkCMCTreeNode *temp;
2193
2194         j = 2 * root;
2195         k = j + 1;
2196
2197         /* find the maximum element of numbers[root],
2198            numbers[2*root] and numbers[2*root+1] */
2199         if (j <= bottom) {
2200                 if (clist->compare( clist, GTK_CMCTREE_ROW (g_ptr_array_index(numbers, root)),
2201                                     GTK_CMCTREE_ROW(g_ptr_array_index( numbers, j))) >= 0)
2202                         j = root;
2203                 if (k <= bottom)
2204                         if (clist->compare( clist, GTK_CMCTREE_ROW (g_ptr_array_index(numbers, k)),
2205                                             GTK_CMCTREE_ROW (g_ptr_array_index( numbers, j))) > 0)
2206                                 j = k;
2207                 /* if numbers[root] wasn't the maximum element then
2208                    sink again */
2209                 if (root != j) {
2210                         temp = g_ptr_array_index( numbers,root);
2211                         g_ptr_array_index( numbers, root) = g_ptr_array_index( numbers, j);
2212                         g_ptr_array_index( numbers, j) = temp;
2213                         sink( clist, numbers, j, bottom);
2214                 }
2215         }
2216 }
2217
2218 static void heap_sort(GtkCMCList *clist, GPtrArray *numbers, gint array_size)
2219 {
2220         gint i;
2221         GtkCMCTreeNode *temp;
2222         
2223         /* build the Heap */
2224         for (i = (array_size / 2); i >= 1; i--)
2225                 sink( clist, numbers, i, array_size);
2226         /* output the Heap */
2227         for (i = array_size; i >= 2; i--) {
2228                 temp = g_ptr_array_index( numbers, 1);
2229                 g_ptr_array_index( numbers, 1) = g_ptr_array_index( numbers, i);
2230                 g_ptr_array_index( numbers, i) = temp;
2231                 sink( clist, numbers, 1, i-1);
2232         }
2233 }
2234
2235 static void
2236 stree_sort (GtkCMCTree    *ctree,
2237            GtkCMCTreeNode *node,
2238            gpointer      data)
2239 {
2240         GtkCMCTreeNode *list_start, *work, *next;
2241         GPtrArray *row_array, *viewable_array;
2242         GtkCMCList *clist;
2243         gint i;
2244
2245         clist = GTK_CMCLIST (ctree);
2246
2247         if (node)
2248                 work = GTK_CMCTREE_ROW (node)->children;
2249         else
2250                 work = GTK_CMCTREE_NODE (clist->row_list);
2251
2252         row_array = g_ptr_array_new();
2253         viewable_array = g_ptr_array_new();
2254
2255         if (work) {
2256                 g_ptr_array_add( row_array, NULL);
2257                 while (work) {
2258                         /* add all rows to row_array */
2259                         g_ptr_array_add( row_array, work);
2260                         if (GTK_CMCTREE_ROW (work)->parent && gtk_cmctree_is_viewable( ctree, work))
2261                                 g_ptr_array_add( viewable_array, GTK_CMCTREE_ROW (work)->parent);
2262                         next = GTK_CMCTREE_ROW (work)->sibling;
2263                         gtk_sctree_unlink( ctree, work, FALSE);
2264                         work = next;
2265                 }
2266
2267                 heap_sort( clist, row_array, (row_array->len)-1);
2268
2269                 if (node)
2270                         list_start = GTK_CMCTREE_ROW (node)->children;
2271                 else
2272                         list_start = GTK_CMCTREE_NODE (clist->row_list);
2273
2274                 if (clist->sort_type == GTK_SORT_ASCENDING) {
2275                         for (i=(row_array->len)-1; i>=1; i--) {
2276                                 work = g_ptr_array_index( row_array, i);
2277                                 gtk_sctree_link( ctree, work, node, list_start, FALSE);
2278                                 list_start = work;
2279                                 /* insert work at the beginning of the list */
2280                         }
2281                 } else {
2282                         for (i=1; i<row_array->len; i++) {
2283                                 work = g_ptr_array_index( row_array, i);
2284                                 gtk_sctree_link( ctree, work, node, list_start, FALSE);
2285                                 list_start = work;
2286                                 /* insert work at the beginning of the list */
2287                         }
2288                 }
2289
2290                 for (i=0; i<viewable_array->len; i++) {
2291                         gtk_cmctree_expand( ctree, g_ptr_array_index( viewable_array, i));
2292                 }
2293                 
2294         }
2295         g_ptr_array_free( row_array, TRUE);
2296         g_ptr_array_free( viewable_array, TRUE);
2297 }
2298
2299 void
2300 gtk_sctree_sort_recursive (GtkCMCTree     *ctree, 
2301                           GtkCMCTreeNode *node)
2302 {
2303         GtkCMCList *clist;
2304         GtkCMCTreeNode *focus_node = NULL;
2305
2306         cm_return_if_fail (ctree != NULL);
2307         cm_return_if_fail (GTK_IS_CMCTREE (ctree));
2308
2309         clist = GTK_CMCLIST (ctree);
2310
2311         gtk_cmclist_freeze (clist);
2312
2313         if (clist->selection_mode == GTK_SELECTION_EXTENDED) {
2314                 GTK_CMCLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
2315       
2316                 g_list_free (clist->undo_selection);
2317                 g_list_free (clist->undo_unselection);
2318                 clist->undo_selection = NULL;
2319                 clist->undo_unselection = NULL;
2320         }
2321
2322         if (!node || (node && gtk_cmctree_is_viewable (ctree, node)))
2323                 focus_node = GTK_CMCTREE_NODE (g_list_nth (clist->row_list, clist->focus_row));
2324       
2325         GTK_SCTREE(ctree)->sorting = TRUE;
2326
2327         gtk_cmctree_post_recursive (ctree, node, GTK_CMCTREE_FUNC (stree_sort), NULL);
2328
2329         if (!node)
2330                 stree_sort (ctree, NULL, NULL);
2331
2332         GTK_SCTREE(ctree)->sorting = FALSE;
2333
2334         if (focus_node) {
2335                 clist->focus_row = g_list_position (clist->row_list,(GList *)focus_node);
2336                 clist->undo_anchor = clist->focus_row;
2337         }
2338
2339         gtk_cmclist_thaw (clist);
2340 }
2341
2342 void
2343 gtk_sctree_sort_node (GtkCMCTree     *ctree, 
2344                      GtkCMCTreeNode *node)
2345 {
2346         GtkCMCList *clist;
2347         GtkCMCTreeNode *focus_node = NULL;
2348
2349         cm_return_if_fail (ctree != NULL);
2350         cm_return_if_fail (GTK_IS_CMCTREE (ctree));
2351
2352         clist = GTK_CMCLIST (ctree);
2353
2354         gtk_cmclist_freeze (clist);
2355
2356         if (clist->selection_mode == GTK_SELECTION_EXTENDED) {
2357                 GTK_CMCLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
2358
2359                 g_list_free (clist->undo_selection);
2360                 g_list_free (clist->undo_unselection);
2361                 clist->undo_selection = NULL;
2362                 clist->undo_unselection = NULL;
2363         }
2364
2365         if (!node || (node && gtk_cmctree_is_viewable (ctree, node)))
2366                 focus_node = GTK_CMCTREE_NODE (g_list_nth (clist->row_list, clist->focus_row));
2367
2368         GTK_SCTREE(ctree)->sorting = TRUE;
2369
2370         stree_sort (ctree, node, NULL);
2371
2372         GTK_SCTREE(ctree)->sorting = FALSE;
2373
2374         if (focus_node) {
2375                 clist->focus_row = g_list_position (clist->row_list,(GList *)focus_node);
2376                 clist->undo_anchor = clist->focus_row;
2377         }
2378
2379         gtk_cmclist_thaw (clist);
2380 }
2381
2382 /************************************************************************/
2383
2384 static void
2385 gtk_sctree_unlink (GtkCMCTree     *ctree, 
2386                   GtkCMCTreeNode *node,
2387                   gboolean      update_focus_row)
2388 {
2389         GtkCMCList *clist;
2390         gint rows;
2391         gint level;
2392         gint visible;
2393         GtkCMCTreeNode *work;
2394         GtkCMCTreeNode *parent;
2395         GList *list;
2396
2397         cm_return_if_fail (ctree != NULL);
2398         cm_return_if_fail (GTK_IS_CMCTREE (ctree));
2399         cm_return_if_fail (node != NULL);
2400
2401         clist = GTK_CMCLIST (ctree);
2402   
2403         if (update_focus_row && clist->selection_mode == GTK_SELECTION_EXTENDED) {
2404                 GTK_CMCLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
2405
2406                 g_list_free (clist->undo_selection);
2407                 g_list_free (clist->undo_unselection);
2408                 clist->undo_selection = NULL;
2409                 clist->undo_unselection = NULL;
2410         }
2411
2412         visible = gtk_cmctree_is_viewable (ctree, node);
2413
2414         /* clist->row_list_end unlinked ? */
2415         if (visible && (GTK_CMCTREE_NODE_NEXT (node) == NULL ||
2416            (GTK_CMCTREE_ROW (node)->children && gtk_cmctree_is_ancestor (ctree, node,
2417             GTK_CMCTREE_NODE (clist->row_list_end)))))
2418                 clist->row_list_end = (GList *) (GTK_CMCTREE_NODE_PREV (node));
2419
2420         /* update list */
2421         rows = 0;
2422         level = GTK_CMCTREE_ROW (node)->level;
2423         work = GTK_CMCTREE_NODE_NEXT (node);
2424         while (work && GTK_CMCTREE_ROW (work)->level > level) {
2425                 work = GTK_CMCTREE_NODE_NEXT (work);
2426                 rows++;
2427         }
2428
2429         if (visible) {
2430                 clist->rows -= (rows + 1);
2431
2432                 if (update_focus_row) {
2433                         gint pos;
2434                         pos = g_list_position (clist->row_list, (GList *)node);
2435                         if (pos + rows < clist->focus_row)
2436                                 clist->focus_row -= (rows + 1);
2437                         else if (pos <= clist->focus_row) {
2438                                 if (!GTK_CMCTREE_ROW (node)->sibling)
2439                                         clist->focus_row = MAX (pos - 1, 0);
2440                                 else
2441                                         clist->focus_row = pos;
2442               
2443                                 clist->focus_row = MIN (clist->focus_row, clist->rows - 1);
2444                         }
2445                         clist->undo_anchor = clist->focus_row;
2446                 }
2447         }
2448
2449         if (work) {
2450                 list = (GList *)GTK_CMCTREE_NODE_PREV (work);
2451                 list->next = NULL;
2452                 list = (GList *)work;
2453                 list->prev = (GList *)GTK_CMCTREE_NODE_PREV (node);
2454         }
2455
2456         if (GTK_CMCTREE_NODE_PREV (node) &&
2457             GTK_CMCTREE_NODE_NEXT (GTK_CMCTREE_NODE_PREV (node)) == node) {
2458                 list = (GList *)GTK_CMCTREE_NODE_PREV (node);
2459                 list->next = (GList *)work;
2460         }
2461
2462         /* update tree */
2463         parent = GTK_CMCTREE_ROW (node)->parent;
2464         if (parent) {
2465                 if (GTK_CMCTREE_ROW (parent)->children == node) {
2466                         GTK_CMCTREE_ROW (parent)->children = GTK_CMCTREE_ROW (node)->sibling;
2467                 }
2468                 else {
2469                         GtkCMCTreeNode *sibling;
2470
2471                         sibling = GTK_CMCTREE_ROW (parent)->children;
2472                         while (GTK_CMCTREE_ROW (sibling)->sibling != node)
2473                                 sibling = GTK_CMCTREE_ROW (sibling)->sibling;
2474                         GTK_CMCTREE_ROW (sibling)->sibling = GTK_CMCTREE_ROW (node)->sibling;
2475                 }
2476         }
2477         else {
2478                 if (clist->row_list == (GList *)node)
2479                         clist->row_list = (GList *) (GTK_CMCTREE_ROW (node)->sibling);
2480                 else {
2481                         GtkCMCTreeNode *sibling;
2482
2483                         sibling = GTK_CMCTREE_NODE (clist->row_list);
2484                         while (GTK_CMCTREE_ROW (sibling)->sibling != node)
2485                                 sibling = GTK_CMCTREE_ROW (sibling)->sibling;
2486                         GTK_CMCTREE_ROW (sibling)->sibling = GTK_CMCTREE_ROW (node)->sibling;
2487                 }
2488         }
2489 }
2490
2491 static void
2492 gtk_sctree_link (GtkCMCTree     *ctree,
2493                 GtkCMCTreeNode *node,
2494                 GtkCMCTreeNode *parent,
2495                 GtkCMCTreeNode *sibling,
2496                 gboolean      update_focus_row)
2497 {
2498         GtkCMCList *clist;
2499         GList *list_end;
2500         GList *list;
2501         GList *work;
2502         gboolean visible = FALSE;
2503         gint rows = 0;
2504   
2505         if (sibling)
2506                 cm_return_if_fail (GTK_CMCTREE_ROW (sibling)->parent == parent);
2507         cm_return_if_fail (node != NULL);
2508         cm_return_if_fail (node != sibling);
2509         cm_return_if_fail (node != parent);
2510
2511         clist = GTK_CMCLIST (ctree);
2512
2513         if (update_focus_row && clist->selection_mode == GTK_SELECTION_EXTENDED) {
2514                 GTK_CMCLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
2515
2516                 g_list_free (clist->undo_selection);
2517                 g_list_free (clist->undo_unselection);
2518                 clist->undo_selection = NULL;
2519                 clist->undo_unselection = NULL;
2520         }
2521
2522         for (rows = 1, list_end = (GList *)node; list_end->next;
2523              list_end = list_end->next)
2524                 rows++;
2525
2526         GTK_CMCTREE_ROW (node)->parent = parent;
2527         GTK_CMCTREE_ROW (node)->sibling = sibling;
2528
2529         if (!parent || (parent && (gtk_cmctree_is_viewable (ctree, parent) &&
2530             GTK_CMCTREE_ROW (parent)->expanded))) {
2531                 visible = TRUE;
2532                 clist->rows += rows;
2533         }
2534
2535         if (parent)
2536                 work = (GList *)(GTK_CMCTREE_ROW (parent)->children);
2537         else
2538                 work = clist->row_list;
2539
2540         if (sibling) {
2541                 if (work != (GList *)sibling) {
2542                         while (GTK_CMCTREE_ROW (work)->sibling != sibling)
2543                                 work = (GList *)(GTK_CMCTREE_ROW (work)->sibling);
2544                         GTK_CMCTREE_ROW (work)->sibling = node;
2545                 }
2546
2547                 if (sibling == GTK_CMCTREE_NODE (clist->row_list))
2548                 clist->row_list = (GList *) node;
2549                 if (GTK_CMCTREE_NODE_PREV (sibling) &&
2550                     GTK_CMCTREE_NODE_NEXT (GTK_CMCTREE_NODE_PREV (sibling)) == sibling) {
2551                         list = (GList *)GTK_CMCTREE_NODE_PREV (sibling);
2552                         list->next = (GList *)node;
2553                 }
2554
2555                 list = (GList *)node;
2556                 list->prev = (GList *)GTK_CMCTREE_NODE_PREV (sibling);
2557                 list_end->next = (GList *)sibling;
2558                 list = (GList *)sibling;
2559                 list->prev = list_end;
2560                 if (parent && GTK_CMCTREE_ROW (parent)->children == sibling)
2561                         GTK_CMCTREE_ROW (parent)->children = node;
2562         }
2563         else {
2564                 if (work) {
2565                         /* find sibling */
2566                         while (GTK_CMCTREE_ROW (work)->sibling)
2567                         work = (GList *)(GTK_CMCTREE_ROW (work)->sibling);
2568                         GTK_CMCTREE_ROW (work)->sibling = node;
2569
2570                         /* find last visible child of sibling */
2571                         work = (GList *) gtk_sctree_last_visible (ctree,
2572                                GTK_CMCTREE_NODE (work));
2573
2574                         list_end->next = work->next;
2575                         if (work->next)
2576                                 list = work->next->prev = list_end;
2577                         work->next = (GList *)node;
2578                         list = (GList *)node;
2579                         list->prev = work;
2580                 }
2581                 else {
2582                         if (parent) {
2583                                 GTK_CMCTREE_ROW (parent)->children = node;
2584                                 list = (GList *)node;
2585                                 list->prev = (GList *)parent;
2586                                 if (GTK_CMCTREE_ROW (parent)->expanded) {
2587                                         list_end->next = (GList *)GTK_CMCTREE_NODE_NEXT (parent);
2588                                         if (GTK_CMCTREE_NODE_NEXT(parent)) {
2589                                                 list = (GList *)GTK_CMCTREE_NODE_NEXT (parent);
2590                                                 list->prev = list_end;
2591                                         }
2592                                         list = (GList *)parent;
2593                                         list->next = (GList *)node;
2594                                 }
2595                                 else
2596                                         list_end->next = NULL;
2597                         }
2598                         else {
2599                                 clist->row_list = (GList *)node;
2600                                 list = (GList *)node;
2601                                 list->prev = NULL;
2602                                 list_end->next = NULL;
2603                         }
2604                 }
2605         }
2606
2607         gtk_cmctree_pre_recursive (ctree, node, stree_update_level, NULL); 
2608
2609         if (clist->row_list_end == NULL ||
2610             clist->row_list_end->next == (GList *)node)
2611                 clist->row_list_end = list_end;
2612
2613         if (visible && update_focus_row) {
2614                 gint pos;
2615                 pos = g_list_position (clist->row_list, (GList *)node);
2616   
2617                 if (pos <= clist->focus_row) {
2618                         clist->focus_row += rows;
2619                         clist->undo_anchor = clist->focus_row;
2620                 }
2621         }
2622 }
2623
2624 static void
2625 stree_update_level (GtkCMCTree     *ctree, 
2626                    GtkCMCTreeNode *node, 
2627                    gpointer      data)
2628 {
2629         if (!node)
2630                 return;
2631
2632         if (GTK_CMCTREE_ROW (node)->parent)
2633                 GTK_CMCTREE_ROW (node)->level = 
2634                 GTK_CMCTREE_ROW (GTK_CMCTREE_ROW (node)->parent)->level + 1;
2635         else
2636                 GTK_CMCTREE_ROW (node)->level = 1;
2637 }
2638
2639 static GtkCMCTreeNode *
2640 gtk_sctree_last_visible (GtkCMCTree     *ctree,
2641                         GtkCMCTreeNode *node)
2642 {
2643         GtkCMCTreeNode *work;
2644   
2645         if (!node)
2646                 return NULL;
2647
2648         work = GTK_CMCTREE_ROW (node)->children;
2649
2650         if (!work || !GTK_CMCTREE_ROW (node)->expanded)
2651                 return node;
2652
2653         while (GTK_CMCTREE_ROW (work)->sibling)
2654                 work = GTK_CMCTREE_ROW (work)->sibling;
2655
2656         return gtk_sctree_last_visible (ctree, work);
2657 }
2658
2659 static void 
2660 sset_node_info (GtkCMCTree     *ctree,
2661                GtkCMCTreeNode *node,
2662                const gchar  *text,
2663                guint8        spacing,
2664                GdkPixbuf    *pixbuf_closed,
2665                GdkPixbuf    *pixbuf_opened,
2666                gboolean      is_leaf,
2667                gboolean      expanded)
2668 {
2669   if (GTK_CMCTREE_ROW (node)->pixbuf_opened)
2670     {
2671       g_object_unref (GTK_CMCTREE_ROW (node)->pixbuf_opened);
2672     }
2673   if (GTK_CMCTREE_ROW (node)->pixbuf_closed)
2674     {
2675       g_object_unref (GTK_CMCTREE_ROW (node)->pixbuf_closed);
2676     }
2677
2678   GTK_CMCTREE_ROW (node)->pixbuf_opened = NULL;
2679   GTK_CMCTREE_ROW (node)->pixbuf_closed = NULL;
2680
2681   if (pixbuf_closed)
2682     {
2683       GTK_CMCTREE_ROW (node)->pixbuf_closed = g_object_ref (pixbuf_closed);
2684     }
2685   if (pixbuf_opened)
2686     {
2687       GTK_CMCTREE_ROW (node)->pixbuf_opened = g_object_ref (pixbuf_opened);
2688     }
2689
2690   GTK_CMCTREE_ROW (node)->is_leaf  = is_leaf;
2691   GTK_CMCTREE_ROW (node)->expanded = (is_leaf) ? FALSE : expanded;
2692
2693   if (GTK_CMCTREE_ROW (node)->expanded)
2694     gtk_cmctree_node_set_pixtext (ctree, node, ctree->tree_column,
2695                                 text, spacing, pixbuf_opened);
2696   else 
2697     gtk_cmctree_node_set_pixtext (ctree, node, ctree->tree_column,
2698                                 text, spacing, pixbuf_closed);
2699 }
2700
2701 static void
2702 stree_draw_node (GtkCMCTree     *ctree, 
2703                 GtkCMCTreeNode *node)
2704 {
2705   GtkCMCList *clist;
2706   
2707   clist = GTK_CMCLIST (ctree);
2708
2709   if (CLIST_UNFROZEN (clist) && gtk_cmctree_is_viewable (ctree, node))
2710     {
2711       GtkCMCTreeNode *work;
2712       gint num = 0;
2713       
2714       work = GTK_CMCTREE_NODE (clist->row_list);
2715       while (work && work != node)
2716         {
2717           work = GTK_CMCTREE_NODE_NEXT (work);
2718           num++;
2719         }
2720       if (work && gtk_cmclist_row_is_visible (clist, num) != GTK_VISIBILITY_NONE)
2721         GTK_CMCLIST_GET_CLASS (clist)->draw_row
2722           (clist, NULL, num, GTK_CMCLIST_ROW ((GList *) node));
2723     }
2724 }
2725
2726 /* this wrapper simply replaces NULL pixbufs 
2727  * with a transparent, 1x1 pixbuf. This works
2728  * around a memory problem deep inside gtk, 
2729  * revealed by valgrind. 
2730  */
2731 void        gtk_sctree_set_node_info        (GtkCMCTree *ctree,
2732                                              GtkCMCTreeNode *node,
2733                                              const gchar *text,
2734                                              guint8 spacing,
2735                                              GdkPixbuf *pixbuf_closed,
2736                                              GdkPixbuf *pixbuf_opened,
2737                                              gboolean is_leaf,
2738                                              gboolean expanded)
2739 {
2740   gboolean old_leaf;
2741   gboolean old_expanded;
2742   GtkCMCTreeNode *work;
2743  
2744   if (!GTK_IS_CMCTREE (ctree) || !node) return;
2745
2746   old_leaf = GTK_CMCTREE_ROW (node)->is_leaf;
2747   old_expanded = GTK_CMCTREE_ROW (node)->expanded;
2748
2749   if (is_leaf && (work = GTK_CMCTREE_ROW (node)->children) != NULL)
2750     {
2751       GtkCMCTreeNode *ptr;
2752       
2753       while (work)
2754         {
2755           ptr = work;
2756           work = GTK_CMCTREE_ROW (work)->sibling;
2757           gtk_cmctree_remove_node (ctree, ptr);
2758         }
2759     }
2760
2761   sset_node_info (ctree, node, text, spacing, pixbuf_closed,
2762                  pixbuf_opened, is_leaf, expanded);
2763
2764   if (!is_leaf && !old_leaf)
2765     {
2766       GTK_CMCTREE_ROW (node)->expanded = old_expanded;
2767       if (expanded && !old_expanded)
2768         gtk_cmctree_expand (ctree, node);
2769       else if (!expanded && old_expanded)
2770         gtk_cmctree_collapse (ctree, node);
2771     }
2772
2773   GTK_CMCTREE_ROW (node)->expanded = (is_leaf) ? FALSE : expanded;
2774   
2775   stree_draw_node (ctree, node);
2776 }
2777
2778 static GtkCMCTreeRow *
2779 srow_new (GtkCMCTree *ctree)
2780 {
2781   GtkCMCList *clist;
2782   GtkCMCTreeRow *ctree_row;
2783   int i;
2784
2785   clist = GTK_CMCLIST (ctree);
2786 #if GLIB_CHECK_VERSION(2,10,0)
2787   ctree_row = g_slice_new (GtkCMCTreeRow);
2788   ctree_row->row.cell = g_slice_alloc (sizeof (GtkCMCell) * clist->columns);
2789 #else
2790   ctree_row = g_chunk_new (GtkCMCTreeRow, (GMemChunk *)clist->row_mem_chunk);
2791   ctree_row->row.cell = g_chunk_new (GtkCMCell, (GMemChunk *)clist->cell_mem_chunk);
2792 #endif
2793   for (i = 0; i < clist->columns; i++)
2794     {
2795       ctree_row->row.cell[i].type = GTK_CMCELL_EMPTY;
2796       ctree_row->row.cell[i].vertical = 0;
2797       ctree_row->row.cell[i].horizontal = 0;
2798       ctree_row->row.cell[i].style = NULL;
2799     }
2800
2801   GTK_CMCELL_PIXTEXT (ctree_row->row.cell[ctree->tree_column])->text = NULL;
2802
2803   ctree_row->row.fg_set     = FALSE;
2804   ctree_row->row.bg_set     = FALSE;
2805   ctree_row->row.style      = NULL;
2806   ctree_row->row.selectable = TRUE;
2807   ctree_row->row.state      = GTK_STATE_NORMAL;
2808   ctree_row->row.data       = NULL;
2809   ctree_row->row.destroy    = NULL;
2810
2811   ctree_row->level         = 0;
2812   ctree_row->expanded      = FALSE;
2813   ctree_row->parent        = NULL;
2814   ctree_row->sibling       = NULL;
2815   ctree_row->children      = NULL;
2816   ctree_row->pixbuf_closed = NULL;
2817   ctree_row->pixbuf_opened = NULL;
2818   
2819   return ctree_row;
2820 }
2821
2822 static void
2823 srow_delete (GtkCMCTree    *ctree,
2824             GtkCMCTreeRow *ctree_row)
2825 {
2826   GtkCMCList *clist;
2827   gint i;
2828
2829   clist = GTK_CMCLIST (ctree);
2830
2831   for (i = 0; i < clist->columns; i++)
2832     {
2833       GTK_CMCLIST_GET_CLASS (clist)->set_cell_contents
2834         (clist, &(ctree_row->row), i, GTK_CMCELL_EMPTY, NULL, 0, NULL);
2835       if (ctree_row->row.cell[i].style)
2836         {
2837           if (gtkut_widget_get_realized (GTK_WIDGET(ctree)))
2838             gtk_style_detach (ctree_row->row.cell[i].style);
2839           g_object_unref (ctree_row->row.cell[i].style);
2840         }
2841     }
2842
2843   if (ctree_row->row.style)
2844     {
2845       if (gtkut_widget_get_realized (GTK_WIDGET(ctree)))
2846         gtk_style_detach (ctree_row->row.style);
2847       g_object_unref (ctree_row->row.style);
2848     }
2849
2850   if (ctree_row->pixbuf_closed)
2851     {
2852       g_object_unref (ctree_row->pixbuf_closed);
2853     }
2854
2855   if (ctree_row->pixbuf_opened)
2856     {
2857       g_object_unref (ctree_row->pixbuf_opened);
2858     }
2859
2860   if (ctree_row->row.destroy)
2861     {
2862       GDestroyNotify dnotify = ctree_row->row.destroy;
2863       gpointer ddata = ctree_row->row.data;
2864
2865       ctree_row->row.destroy = NULL;
2866       ctree_row->row.data = NULL;
2867
2868       dnotify (ddata);
2869     }
2870
2871 #if GLIB_CHECK_VERSION(2,10,0)  
2872   g_slice_free1 (sizeof (GtkCMCell) * clist->columns, ctree_row->row.cell);
2873   g_slice_free (GtkCMCTreeRow, ctree_row);
2874 #else
2875   g_mem_chunk_free ((GMemChunk *)clist->cell_mem_chunk, ctree_row->row.cell);
2876   g_mem_chunk_free ((GMemChunk *)clist->row_mem_chunk, ctree_row);
2877 #endif
2878 }
2879
2880 static void
2881 stree_delete_row (GtkCMCTree     *ctree, 
2882                  GtkCMCTreeNode *node, 
2883                  gpointer      data)
2884 {
2885   srow_delete (ctree, GTK_CMCTREE_ROW (node));
2886   g_list_free_1 ((GList *)node);
2887 }
2888
2889 static void 
2890 gtk_sctree_real_tree_expand (GtkCMCTree     *ctree,
2891                   GtkCMCTreeNode *node)
2892 {
2893   GtkCMCList *clist;
2894   GtkCMCTreeNode *work;
2895   GtkRequisition requisition;
2896   gboolean visible;
2897   gint level;
2898
2899   cm_return_if_fail (GTK_IS_CMCTREE (ctree));
2900
2901   if (!node || GTK_CMCTREE_ROW (node)->expanded || GTK_CMCTREE_ROW (node)->is_leaf)
2902     return;
2903
2904   clist = GTK_CMCLIST (ctree);
2905   
2906   GTK_CMCLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
2907
2908   GTK_CMCTREE_ROW (node)->expanded = TRUE;
2909   level = GTK_CMCTREE_ROW (node)->level;
2910
2911   visible = gtk_cmctree_is_viewable (ctree, node);
2912   /* get cell width if tree_column is auto resized */
2913   if (visible && clist->column[ctree->tree_column].auto_resize &&
2914       !GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist))
2915     GTK_CMCLIST_GET_CLASS (clist)->cell_size_request
2916       (clist, &GTK_CMCTREE_ROW (node)->row, ctree->tree_column, &requisition);
2917
2918   /* unref/unset closed pixbuf */
2919   if (GTK_CMCELL_PIXTEXT 
2920       (GTK_CMCTREE_ROW (node)->row.cell[ctree->tree_column])->pixbuf)
2921     {
2922       g_object_unref
2923         (GTK_CMCELL_PIXTEXT
2924          (GTK_CMCTREE_ROW (node)->row.cell[ctree->tree_column])->pixbuf);
2925       
2926       GTK_CMCELL_PIXTEXT
2927         (GTK_CMCTREE_ROW (node)->row.cell[ctree->tree_column])->pixbuf = NULL;
2928     }
2929
2930   /* set/ref opened pixbuf */
2931   if (GTK_CMCTREE_ROW (node)->pixbuf_opened)
2932     {
2933       GTK_CMCELL_PIXTEXT 
2934         (GTK_CMCTREE_ROW (node)->row.cell[ctree->tree_column])->pixbuf = 
2935         g_object_ref (GTK_CMCTREE_ROW (node)->pixbuf_opened);
2936     }
2937
2938
2939   work = GTK_CMCTREE_ROW (node)->children;
2940   if (work)
2941     {
2942       GList *list = (GList *)work;
2943       gint *cell_width = NULL;
2944       gint tmp = 0;
2945       gint row;
2946       gint i;
2947       
2948       if (visible && !GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist))
2949         {
2950           cell_width = g_new0 (gint, clist->columns);
2951           if (clist->column[ctree->tree_column].auto_resize)
2952               cell_width[ctree->tree_column] = requisition.width;
2953
2954           while (work)
2955             {
2956               /* search maximum cell widths of auto_resize columns */
2957               for (i = 0; i < clist->columns; i++)
2958                 if (clist->column[i].auto_resize)
2959                   {
2960                     GTK_CMCLIST_GET_CLASS (clist)->cell_size_request
2961                       (clist, &GTK_CMCTREE_ROW (work)->row, i, &requisition);
2962                     cell_width[i] = MAX (requisition.width, cell_width[i]);
2963                   }
2964
2965               list = (GList *)work;
2966               work = GTK_CMCTREE_NODE_NEXT (work);
2967               tmp++;
2968             }
2969         }
2970       else
2971         while (work)
2972           {
2973             list = (GList *)work;
2974             work = GTK_CMCTREE_NODE_NEXT (work);
2975             tmp++;
2976           }
2977
2978       list->next = (GList *)GTK_CMCTREE_NODE_NEXT (node);
2979
2980       if (GTK_CMCTREE_NODE_NEXT (node))
2981         {
2982           GList *tmp_list;
2983
2984           if (clist->row_list_end == list)
2985               clist->row_list_end = g_list_last(list);
2986
2987           tmp_list = (GList *)GTK_CMCTREE_NODE_NEXT (node);
2988           tmp_list->prev = list;
2989         }
2990       else
2991         clist->row_list_end = list;
2992
2993       list = (GList *)node;
2994       list->next = (GList *)(GTK_CMCTREE_ROW (node)->children);
2995
2996       if (visible && !GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist))
2997         {
2998           /* resize auto_resize columns if needed */
2999           for (i = 0; i < clist->columns; i++)
3000             if (clist->column[i].auto_resize &&
3001                 cell_width[i] > clist->column[i].width)
3002               gtk_cmclist_set_column_width (clist, i, cell_width[i]);
3003           g_free (cell_width);
3004         
3005           if (!GTK_SCTREE(ctree)->sorting) {
3006                   /* update focus_row position */
3007                   row = g_list_position (clist->row_list, (GList *)node);
3008                   if (row < clist->focus_row)
3009                     clist->focus_row += tmp;
3010           }
3011           clist->rows += tmp;
3012           CLIST_REFRESH (clist);
3013         }
3014     }
3015   else if (visible && clist->column[ctree->tree_column].auto_resize)
3016     /* resize tree_column if needed */
3017     gtk_sctree_column_auto_resize (clist, &GTK_CMCTREE_ROW (node)->row, ctree->tree_column,
3018                         requisition.width);
3019
3020 }
3021
3022 GtkCMCTreeNode * 
3023 gtk_sctree_insert_node (GtkCMCTree     *ctree,
3024                        GtkCMCTreeNode *parent, 
3025                        GtkCMCTreeNode *sibling,
3026                        gchar        *text[],
3027                        guint8        spacing,
3028                        GdkPixbuf    *pixbuf_closed,
3029                        GdkPixbuf    *pixbuf_opened,
3030                        gboolean      is_leaf,
3031                        gboolean      expanded)
3032 {
3033   GtkCMCList *clist;
3034   GtkCMCTreeRow *new_row;
3035   GtkCMCTreeNode *node;
3036   GList *list;
3037   gint i;
3038
3039   cm_return_val_if_fail (GTK_IS_CMCTREE (ctree), NULL);
3040   if (sibling)
3041     cm_return_val_if_fail (GTK_CMCTREE_ROW (sibling)->parent == parent, NULL);
3042
3043   if (parent && GTK_CMCTREE_ROW (parent)->is_leaf)
3044     return NULL;
3045
3046   clist = GTK_CMCLIST (ctree);
3047
3048   /* create the row */
3049   new_row = srow_new (ctree);
3050   list = g_list_alloc ();
3051   list->data = new_row;
3052   node = GTK_CMCTREE_NODE (list);
3053
3054   if (text)
3055     for (i = 0; i < clist->columns; i++)
3056       if (text[i] && i != ctree->tree_column)
3057         GTK_CMCLIST_GET_CLASS (clist)->set_cell_contents
3058           (clist, &(new_row->row), i, GTK_CMCELL_TEXT, text[i], 0, NULL);
3059
3060   sset_node_info (ctree, node, text ?
3061                  text[ctree->tree_column] : NULL, spacing, pixbuf_closed,
3062                  pixbuf_opened, is_leaf, expanded);
3063
3064   /* sorted insertion */
3065   if (GTK_CMCLIST_AUTO_SORT (clist))
3066     {
3067       if (parent)
3068         sibling = GTK_CMCTREE_ROW (parent)->children;
3069       else
3070         sibling = GTK_CMCTREE_NODE (clist->row_list);
3071
3072       while (sibling && clist->compare
3073              (clist, GTK_CMCTREE_ROW (node), GTK_CMCTREE_ROW (sibling)) > 0)
3074         sibling = GTK_CMCTREE_ROW (sibling)->sibling;
3075     }
3076
3077   gtk_sctree_link (ctree, node, parent, sibling, FALSE);
3078
3079   if (text && !GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist) &&
3080       gtk_cmctree_is_viewable (ctree, node))
3081     {
3082       for (i = 0; i < clist->columns; i++)
3083         if (clist->column[i].auto_resize)
3084           gtk_sctree_column_auto_resize (clist, &(new_row->row), i, 0);
3085     }
3086
3087   if (clist->rows == 1)
3088     {
3089       clist->focus_row = 0;
3090       if (clist->selection_mode == GTK_SELECTION_BROWSE)
3091         gtk_sctree_select (GTK_SCTREE(ctree), node);
3092     }
3093
3094
3095   CLIST_REFRESH (clist);
3096
3097   return node;
3098 }
3099
3100 GtkCMCTreeNode *
3101 gtk_sctree_insert_gnode (GtkCMCTree          *ctree,
3102                         GtkCMCTreeNode      *parent,
3103                         GtkCMCTreeNode      *sibling,
3104                         GNode             *gnode,
3105                         GtkCMCTreeGNodeFunc  func,
3106                         gpointer           data)
3107 {
3108   GtkCMCList *clist;
3109   GtkCMCTreeNode *cnode = NULL;
3110   GtkCMCTreeNode *child = NULL;
3111   GtkCMCTreeNode *new_child;
3112   GList *list;
3113   GNode *work;
3114   guint depth = 1;
3115
3116   cm_return_val_if_fail (GTK_IS_CMCTREE (ctree), NULL);
3117   cm_return_val_if_fail (gnode != NULL, NULL);
3118   cm_return_val_if_fail (func != NULL, NULL);
3119   if (sibling)
3120     cm_return_val_if_fail (GTK_CMCTREE_ROW (sibling)->parent == parent, NULL);
3121   
3122   clist = GTK_CMCLIST (ctree);
3123
3124   if (parent)
3125     depth = GTK_CMCTREE_ROW (parent)->level + 1;
3126
3127   list = g_list_alloc ();
3128   list->data = srow_new (ctree);
3129   cnode = GTK_CMCTREE_NODE (list);
3130
3131   gtk_cmclist_freeze (clist);
3132
3133   sset_node_info (ctree, cnode, "", 0, NULL, NULL, TRUE, FALSE);
3134
3135   if (!func (ctree, depth, gnode, cnode, data))
3136     {
3137       stree_delete_row (ctree, cnode, NULL);
3138       gtk_cmclist_thaw (clist);
3139       return NULL;
3140     }
3141
3142   if (GTK_CMCLIST_AUTO_SORT (clist))
3143     {
3144       if (parent)
3145         sibling = GTK_CMCTREE_ROW (parent)->children;
3146       else
3147         sibling = GTK_CMCTREE_NODE (clist->row_list);
3148
3149       while (sibling && clist->compare
3150              (clist, GTK_CMCTREE_ROW (cnode), GTK_CMCTREE_ROW (sibling)) > 0)
3151         sibling = GTK_CMCTREE_ROW (sibling)->sibling;
3152     }
3153
3154   gtk_sctree_link (ctree, cnode, parent, sibling, FALSE);
3155
3156   for (work = g_node_last_child (gnode); work; work = work->prev)
3157     {
3158       new_child = gtk_sctree_insert_gnode (ctree, cnode, child,
3159                                           work, func, data);
3160       if (new_child)
3161         child = new_child;
3162     }   
3163   
3164   gtk_cmclist_thaw (clist);
3165
3166   return cnode;
3167 }
3168
3169 static void
3170 sreal_tree_move (GtkCMCTree     *ctree,
3171                 GtkCMCTreeNode *node,
3172                 GtkCMCTreeNode *new_parent, 
3173                 GtkCMCTreeNode *new_sibling)
3174 {
3175   GtkCMCList *clist;
3176   GtkCMCTreeNode *work;
3177   gboolean visible = FALSE;
3178
3179   cm_return_if_fail (ctree != NULL);
3180   cm_return_if_fail (node != NULL);
3181   cm_return_if_fail (!new_sibling || 
3182                     GTK_CMCTREE_ROW (new_sibling)->parent == new_parent);
3183
3184   if (new_parent && GTK_CMCTREE_ROW (new_parent)->is_leaf)
3185     return;
3186
3187   /* new_parent != child of child */
3188   for (work = new_parent; work; work = GTK_CMCTREE_ROW (work)->parent)
3189     if (work == node)
3190       return;
3191
3192   clist = GTK_CMCLIST (ctree);
3193
3194   visible = gtk_cmctree_is_viewable (ctree, node);
3195
3196   if (clist->selection_mode == GTK_SELECTION_MULTIPLE)
3197     {
3198       GTK_CMCLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
3199       
3200       g_list_free (clist->undo_selection);
3201       g_list_free (clist->undo_unselection);
3202       clist->undo_selection = NULL;
3203       clist->undo_unselection = NULL;
3204     }
3205
3206   if (GTK_CMCLIST_AUTO_SORT (clist))
3207     {
3208       if (new_parent == GTK_CMCTREE_ROW (node)->parent)
3209         return;
3210       
3211       if (new_parent)
3212         new_sibling = GTK_CMCTREE_ROW (new_parent)->children;
3213       else
3214         new_sibling = GTK_CMCTREE_NODE (clist->row_list);
3215
3216       while (new_sibling && clist->compare
3217              (clist, GTK_CMCTREE_ROW (node), GTK_CMCTREE_ROW (new_sibling)) > 0)
3218         new_sibling = GTK_CMCTREE_ROW (new_sibling)->sibling;
3219     }
3220
3221   if (new_parent == GTK_CMCTREE_ROW (node)->parent && 
3222       new_sibling == GTK_CMCTREE_ROW (node)->sibling)
3223     return;
3224
3225   gtk_cmclist_freeze (clist);
3226
3227   work = NULL;
3228
3229   if (!GTK_SCTREE(ctree)->sorting && gtk_cmctree_is_viewable (ctree, node))
3230     work = GTK_CMCTREE_NODE (g_list_nth (clist->row_list, clist->focus_row));
3231       
3232   gtk_sctree_unlink (ctree, node, FALSE);
3233   gtk_sctree_link (ctree, node, new_parent, new_sibling, FALSE);
3234   
3235   if (!GTK_SCTREE(ctree)->sorting && work)
3236     {
3237       while (work &&  !gtk_cmctree_is_viewable (ctree, work))
3238         work = GTK_CMCTREE_ROW (work)->parent;
3239       clist->focus_row = g_list_position (clist->row_list, (GList *)work);
3240       clist->undo_anchor = clist->focus_row;
3241     }
3242
3243   if (clist->column[ctree->tree_column].auto_resize &&
3244       !GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist) &&
3245       (visible || gtk_cmctree_is_viewable (ctree, node)))
3246     gtk_cmclist_set_column_width
3247       (clist, ctree->tree_column,
3248        gtk_cmclist_optimal_column_width (clist, ctree->tree_column));
3249
3250   gtk_cmclist_thaw (clist);
3251 }
3252
3253 void gtk_sctree_set_column_tooltip          (GtkSCTree          *sctree,
3254                                              int                 column,
3255                                              const gchar        *tip)
3256 {
3257 #if !(GTK_CHECK_VERSION(2,12,0))
3258         GtkTooltips *tips;
3259         if (!sctree->tooltips)
3260                 sctree->tooltips = gtk_tooltips_new();
3261         tips = sctree->tooltips;
3262 #endif
3263
3264         CLAWS_SET_TIP(GTK_CMCLIST(sctree)->column[column].button,
3265                         tip);
3266 }
3267