1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball, Josh MacDonald,
3 * Copyright (C) 1997-1998 Jay Painter <jpaint@serv.net><jpaint@gimp.org>
4 *
5 * GtkCMCTree widget for GTK+
6 * Copyright (C) 1998 Lars Hamann and Stefan Jeske
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the
20 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21 * Boston, MA 02111-1307, USA.
22 */
23
24 /*
25 * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
26 * file for a list of people on the GTK+ Team. See the ChangeLog
27 * files for a list of changes. These files are distributed with
28 * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
29 */
30
31 #include <config.h>
32 #include <stdlib.h>
33
34 #include <gtk/gtk.h>
35 #include <gdk/gdkkeysyms.h>
36 #include "gtkcmctree.h"
37 #include "claws-marshal.h"
38 #include "utils.h"
39 #include "gtkutils.c"
40
41 #define PM_SIZE 8
42 #define TAB_SIZE (PM_SIZE + 6)
43 #define CELL_SPACING 1
44 #define CLIST_OPTIMUM_SIZE 64
45 #define COLUMN_INSET 3
46 #define DRAG_WIDTH 6
47
48 #define ROW_TOP_YPIXEL(clist, row) (((clist)->row_height * (row)) + \
49 (((row) + 1) * CELL_SPACING) + \
50 (clist)->voffset)
51 #define ROW_FROM_YPIXEL(clist, y) (((y) - (clist)->voffset) / \
52 ((clist)->row_height + CELL_SPACING))
53 #define COLUMN_LEFT_XPIXEL(clist, col) ((clist)->column[(col)].area.x \
54 + (clist)->hoffset)
55 #define COLUMN_LEFT(clist, column) ((clist)->column[(column)].area.x)
56
57 GType
gtk_cmctree_pos_get_type(void)58 gtk_cmctree_pos_get_type (void)
59 {
60 static GType etype = 0;
61 if (etype == 0) {
62 static const GEnumValue values[] = {
63 { GTK_CMCTREE_POS_BEFORE, "GTK_CMCTREE_POS_BEFORE", "before" },
64 { GTK_CMCTREE_POS_AS_CHILD, "GTK_CMCTREE_POS_AS_CHILD", "as-child" },
65 { GTK_CMCTREE_POS_AFTER, "GTK_CMCTREE_POS_AFTER", "after" },
66 { 0, NULL, NULL }
67 };
68 etype = g_enum_register_static (g_intern_static_string ("GtkCMCTreePos"), values);
69 }
70 return etype;
71 }
72 GType
gtk_cmctree_line_style_get_type(void)73 gtk_cmctree_line_style_get_type (void)
74 {
75 static GType etype = 0;
76 if (etype == 0) {
77 static const GEnumValue values[] = {
78 { GTK_CMCTREE_LINES_NONE, "GTK_CMCTREE_LINES_NONE", "none" },
79 { 0, NULL, NULL }
80 };
81 etype = g_enum_register_static (g_intern_static_string ("GtkCMCTreeLineStyle"), values);
82 }
83 return etype;
84 }
85 GType
gtk_cmctree_expander_style_get_type(void)86 gtk_cmctree_expander_style_get_type (void)
87 {
88 static GType etype = 0;
89 if (etype == 0) {
90 static const GEnumValue values[] = {
91 { GTK_CMCTREE_EXPANDER_NONE, "GTK_CMCTREE_EXPANDER_NONE", "none" },
92 { GTK_CMCTREE_EXPANDER_TRIANGLE, "GTK_CMCTREE_EXPANDER_TRIANGLE", "triangle" },
93 { 0, NULL, NULL }
94 };
95 etype = g_enum_register_static (g_intern_static_string ("GtkCMCTreeExpanderStyle"), values);
96 }
97 return etype;
98 }
99 GType
gtk_cmctree_expansion_type_get_type(void)100 gtk_cmctree_expansion_type_get_type (void)
101 {
102 static GType etype = 0;
103 if (etype == 0) {
104 static const GEnumValue values[] = {
105 { GTK_CMCTREE_EXPANSION_EXPAND, "GTK_CMCTREE_EXPANSION_EXPAND", "expand" },
106 { GTK_CMCTREE_EXPANSION_EXPAND_RECURSIVE, "GTK_CMCTREE_EXPANSION_EXPAND_RECURSIVE", "expand-recursive" },
107 { GTK_CMCTREE_EXPANSION_COLLAPSE, "GTK_CMCTREE_EXPANSION_COLLAPSE", "collapse" },
108 { GTK_CMCTREE_EXPANSION_COLLAPSE_RECURSIVE, "GTK_CMCTREE_EXPANSION_COLLAPSE_RECURSIVE", "collapse-recursive" },
109 { GTK_CMCTREE_EXPANSION_TOGGLE, "GTK_CMCTREE_EXPANSION_TOGGLE", "toggle" },
110 { GTK_CMCTREE_EXPANSION_TOGGLE_RECURSIVE, "GTK_CMCTREE_EXPANSION_TOGGLE_RECURSIVE", "toggle-recursive" },
111 { 0, NULL, NULL }
112 };
113 etype = g_enum_register_static (g_intern_static_string ("GtkCMCTreeExpansionType"), values);
114 }
115 return etype;
116 }
117
118
119 static inline gint
COLUMN_FROM_XPIXEL(GtkCMCList * clist,gint x)120 COLUMN_FROM_XPIXEL (GtkCMCList * clist,
121 gint x)
122 {
123 gint i, cx;
124
125 for (i = 0; i < clist->columns; i++)
126 if (clist->column[i].visible)
127 {
128 cx = clist->column[i].area.x + clist->hoffset;
129
130 if (x >= (cx - (COLUMN_INSET + CELL_SPACING)) &&
131 x <= (cx + clist->column[i].area.width + COLUMN_INSET))
132 return i;
133 }
134
135 /* no match */
136 return -1;
137 }
138
139 #define CLIST_UNFROZEN(clist) (((GtkCMCList*) (clist))->freeze_count == 0)
140 #define CLIST_REFRESH(clist) G_STMT_START { \
141 if (CLIST_UNFROZEN (clist)) \
142 GTK_CMCLIST_GET_CLASS (clist)->refresh ((GtkCMCList*) (clist)); \
143 } G_STMT_END
144
145
146 enum {
147 ARG_0,
148 ARG_N_COLUMNS,
149 ARG_TREE_COLUMN,
150 ARG_INDENT,
151 ARG_SPACING,
152 ARG_SHOW_STUB,
153 ARG_LINE_STYLE,
154 ARG_EXPANDER_STYLE
155 };
156
157
158 static void gtk_cmctree_class_init (GtkCMCTreeClass *klass);
159 static void gtk_cmctree_init (GtkCMCTree *ctree);
160 static GObject* gtk_cmctree_constructor (GType type,
161 guint n_construct_properties,
162 GObjectConstructParam *construct_params);
163 static void gtk_cmctree_set_arg (GObject *object,
164 guint arg_id,
165 const GValue *value,
166 GParamSpec *spec);
167 static void gtk_cmctree_get_arg (GObject *object,
168 guint arg_id,
169 GValue *value,
170 GParamSpec *spec);
171 static void gtk_cmctree_realize (GtkWidget *widget);
172 static void gtk_cmctree_unrealize (GtkWidget *widget);
173 static gint gtk_cmctree_button_press (GtkWidget *widget,
174 GdkEventButton *event);
175 static void ctree_attach_styles (GtkCMCTree *ctree,
176 GtkCMCTreeNode *node,
177 gpointer data);
178 static void ctree_detach_styles (GtkCMCTree *ctree,
179 GtkCMCTreeNode *node,
180 gpointer data);
181 static void set_cell_contents (GtkCMCList *clist,
182 GtkCMCListRow *clist_row,
183 gint column,
184 GtkCMCellType type,
185 const gchar *text,
186 guint8 spacing,
187 GdkPixbuf *pixbuf);
188 static void set_node_info (GtkCMCTree *ctree,
189 GtkCMCTreeNode *node,
190 const gchar *text,
191 guint8 spacing,
192 GdkPixbuf *pixbuf_closed,
193 GdkPixbuf *pixbuf_opened,
194 gboolean is_leaf,
195 gboolean expanded);
196 static GtkCMCTreeRow *row_new (GtkCMCTree *ctree);
197 static void row_delete (GtkCMCTree *ctree,
198 GtkCMCTreeRow *ctree_row);
199 static void tree_delete (GtkCMCTree *ctree,
200 GtkCMCTreeNode *node,
201 gpointer data);
202 static void tree_delete_row (GtkCMCTree *ctree,
203 GtkCMCTreeNode *node,
204 gpointer data);
205 static void real_clear (GtkCMCList *clist);
206 static void tree_update_level (GtkCMCTree *ctree,
207 GtkCMCTreeNode *node,
208 gpointer data);
209 static void tree_select (GtkCMCTree *ctree,
210 GtkCMCTreeNode *node,
211 gpointer data);
212 static void tree_unselect (GtkCMCTree *ctree,
213 GtkCMCTreeNode *node,
214 gpointer data);
215 static void real_select_all (GtkCMCList *clist);
216 static void real_unselect_all (GtkCMCList *clist);
217 static void tree_expand (GtkCMCTree *ctree,
218 GtkCMCTreeNode *node,
219 gpointer data);
220 static void tree_collapse (GtkCMCTree *ctree,
221 GtkCMCTreeNode *node,
222 gpointer data);
223 static void tree_collapse_to_depth (GtkCMCTree *ctree,
224 GtkCMCTreeNode *node,
225 gint depth);
226 static void tree_toggle_expansion (GtkCMCTree *ctree,
227 GtkCMCTreeNode *node,
228 gpointer data);
229 static void change_focus_row_expansion (GtkCMCTree *ctree,
230 GtkCMCTreeExpansionType expansion);
231 static void real_select_row (GtkCMCList *clist,
232 gint row,
233 gint column,
234 GdkEvent *event);
235 static void real_unselect_row (GtkCMCList *clist,
236 gint row,
237 gint column,
238 GdkEvent *event);
239 static void real_tree_select (GtkCMCTree *ctree,
240 GtkCMCTreeNode *node,
241 gint column);
242 static void real_tree_unselect (GtkCMCTree *ctree,
243 GtkCMCTreeNode *node,
244 gint column);
245 static void real_tree_expand (GtkCMCTree *ctree,
246 GtkCMCTreeNode *node);
247 static void real_tree_collapse (GtkCMCTree *ctree,
248 GtkCMCTreeNode *node);
249 static void real_tree_move (GtkCMCTree *ctree,
250 GtkCMCTreeNode *node,
251 GtkCMCTreeNode *new_parent,
252 GtkCMCTreeNode *new_sibling);
253 static void real_row_move (GtkCMCList *clist,
254 gint source_row,
255 gint dest_row);
256 static void gtk_cmctree_link (GtkCMCTree *ctree,
257 GtkCMCTreeNode *node,
258 GtkCMCTreeNode *parent,
259 GtkCMCTreeNode *sibling,
260 gboolean update_focus_row);
261 static void gtk_cmctree_unlink (GtkCMCTree *ctree,
262 GtkCMCTreeNode *node,
263 gboolean update_focus_row);
264 static GtkCMCTreeNode * gtk_cmctree_last_visible (GtkCMCTree *ctree,
265 GtkCMCTreeNode *node);
266 static gboolean ctree_is_hot_spot (GtkCMCTree *ctree,
267 GtkCMCTreeNode *node,
268 gint row,
269 gint x,
270 gint y);
271 static void tree_sort (GtkCMCTree *ctree,
272 GtkCMCTreeNode *node,
273 gpointer data);
274 static void fake_unselect_all (GtkCMCList *clist,
275 gint row);
276 static GList * selection_find (GtkCMCList *clist,
277 gint row_number,
278 GList *row_list_element);
279 static void resync_selection (GtkCMCList *clist,
280 GdkEvent *event);
281 static void real_undo_selection (GtkCMCList *clist);
282 static void select_row_recursive (GtkCMCTree *ctree,
283 GtkCMCTreeNode *node,
284 gpointer data);
285 static gint real_insert_row (GtkCMCList *clist,
286 gint row,
287 gchar *text[]);
288 static void real_remove_row (GtkCMCList *clist,
289 gint row);
290 static void real_sort_list (GtkCMCList *clist);
291 static void cell_size_request (GtkCMCList *clist,
292 GtkCMCListRow *clist_row,
293 gint column,
294 GtkRequisition *requisition);
295 static void column_auto_resize (GtkCMCList *clist,
296 GtkCMCListRow *clist_row,
297 gint column,
298 gint old_width);
299 static void auto_resize_columns (GtkCMCList *clist);
300
301
302 static gboolean check_drag (GtkCMCTree *ctree,
303 GtkCMCTreeNode *drag_source,
304 GtkCMCTreeNode *drag_target,
305 GtkCMCListDragPos insert_pos);
306 static void gtk_cmctree_drag_begin (GtkWidget *widget,
307 GdkDragContext *context);
308 static gint gtk_cmctree_drag_motion (GtkWidget *widget,
309 GdkDragContext *context,
310 gint x,
311 gint y,
312 guint time);
313 static void gtk_cmctree_drag_data_received (GtkWidget *widget,
314 GdkDragContext *context,
315 gint x,
316 gint y,
317 GtkSelectionData *selection_data,
318 guint info,
319 guint32 time);
320 static void remove_grab (GtkCMCList *clist);
321 static void drag_dest_cell (GtkCMCList *clist,
322 gint x,
323 gint y,
324 GtkCMCListDestInfo *dest_info);
325
326
327 enum
328 {
329 TREE_SELECT_ROW,
330 TREE_UNSELECT_ROW,
331 TREE_EXPAND,
332 TREE_COLLAPSE,
333 TREE_MOVE,
334 CHANGE_FOCUS_ROW_EXPANSION,
335 LAST_SIGNAL
336 };
337
338 static GtkCMCListClass *parent_class = NULL;
339 static GtkContainerClass *container_class = NULL;
340 static guint ctree_signals[LAST_SIGNAL] = {0};
341
342
343 GType
gtk_cmctree_get_type(void)344 gtk_cmctree_get_type (void)
345 {
346 static GType ctree_type = 0;
347
348 if (!ctree_type)
349 {
350 static const GTypeInfo ctree_info =
351 {
352 sizeof (GtkCMCTreeClass),
353
354 (GBaseInitFunc) NULL,
355 (GBaseFinalizeFunc) NULL,
356
357 (GClassInitFunc) gtk_cmctree_class_init,
358 (GClassFinalizeFunc) NULL,
359 NULL, /* class_data */
360
361 sizeof (GtkCMCTree),
362 0, /* n_preallocs */
363 (GInstanceInitFunc) gtk_cmctree_init,
364
365 (const GTypeValueTable *) NULL /* value table */
366 };
367
368 ctree_type = g_type_register_static (GTK_TYPE_CMCLIST, "GtkCMCTree", &ctree_info, (GTypeFlags)0);
369 }
370
371 return ctree_type;
372 }
373
374 static gint
draw_cell_pixbuf(GdkWindow * window,GdkRectangle * clip_rectangle,cairo_t * cr,GdkPixbuf * pixbuf,gint x,gint y,gint width,gint height)375 draw_cell_pixbuf (GdkWindow *window,
376 GdkRectangle *clip_rectangle,
377 cairo_t *cr,
378 GdkPixbuf *pixbuf,
379 gint x,
380 gint y,
381 gint width,
382 gint height)
383 {
384 gint xsrc = 0;
385 gint ysrc = 0;
386
387 if (!pixbuf || (width == 0 && height == 0))
388 return x;
389
390 if (x < clip_rectangle->x)
391 {
392 xsrc = clip_rectangle->x - x;
393 width -= xsrc;
394 x = clip_rectangle->x;
395 }
396 if (x + width > clip_rectangle->x + clip_rectangle->width)
397 width = clip_rectangle->x + clip_rectangle->width - x;
398
399 if (y < clip_rectangle->y)
400 {
401 ysrc = clip_rectangle->y - y;
402 height -= ysrc;
403 y = clip_rectangle->y;
404 }
405
406 if (y + height > clip_rectangle->y + clip_rectangle->height)
407 height = clip_rectangle->y + clip_rectangle->height - y;
408
409 gdk_cairo_set_source_pixbuf(cr, pixbuf, x, y);
410 cairo_paint(cr);
411
412 return x + MAX (width, 0);
413 }
414
415 static gint
draw_expander(GtkCMCTree * ctree,GtkCMCTreeRow * ctree_row,GtkStyle * style,GdkRectangle * clip_rectangle,cairo_t * cr,gint x)416 draw_expander (GtkCMCTree *ctree,
417 GtkCMCTreeRow *ctree_row,
418 GtkStyle *style,
419 GdkRectangle *clip_rectangle,
420 cairo_t *cr,
421 gint x)
422 {
423 GtkCMCList *clist;
424 gint justification_factor;
425 gint y;
426
427 if (ctree->expander_style == GTK_CMCTREE_EXPANDER_NONE)
428 return x;
429
430 clist = GTK_CMCLIST (ctree);
431 if (clist->column[ctree->tree_column].justification == GTK_JUSTIFY_RIGHT)
432 justification_factor = -1;
433 else
434 justification_factor = 1;
435 if (!GTK_CMCLIST_ROW_HEIGHT_SET(GTK_CMCLIST(clist)))
436 y = (clip_rectangle->y + (clip_rectangle->height - PM_SIZE) / 2 -
437 (clip_rectangle->height + 1) % 2) + 1;
438 else
439 y = (clip_rectangle->y + (clip_rectangle->height/2 - PM_SIZE) / 2 -
440 (clip_rectangle->height/2 + 1) % 2) + 1;
441
442 if (!ctree_row->children)
443 {
444 return x + justification_factor * (PM_SIZE + 3);
445 }
446
447 /* pixel offsets +/- 1 or +/- justification_factor here and there ..
448 * to fill correctly, somewhat ... what do I do wrong?
449 */
450 gdk_cairo_set_source_color(cr, >k_widget_get_style(GTK_WIDGET(ctree))->text[GTK_STATE_NORMAL]);
451 if (ctree_row->expanded)
452 {
453 gint tmp3 = PM_SIZE / 2;
454 gint tmp6 = PM_SIZE / 6;
455 cairo_move_to(cr, x + justification_factor * (tmp3 + tmp6) + (PM_SIZE / 2), y + 1);
456 cairo_rel_line_to(cr, 0, tmp3 + tmp6 + 1);
457 cairo_rel_line_to(cr, -justification_factor * (tmp3 + tmp6) - justification_factor, -1);
458 }
459 else
460 {
461 gint tmp3 = PM_SIZE / 2;
462 gint tmp6 = PM_SIZE / 6;
463 cairo_move_to(cr, x + tmp6 - justification_factor + (PM_SIZE / 2), y + tmp6 - 1);
464 cairo_rel_line_to(cr, justification_factor * tmp3, tmp3);
465 cairo_rel_line_to(cr, -justification_factor * tmp3, tmp3);
466 }
467 cairo_fill(cr);
468
469 x += justification_factor * (PM_SIZE + 3);
470
471 return x;
472 }
473
474 static gint
get_offset(GtkCMCTree * ctree,GtkCMCTreeRow * ctree_row,gint column,GdkRectangle * clip_rectangle)475 get_offset(GtkCMCTree *ctree,
476 GtkCMCTreeRow *ctree_row,
477 gint column,
478 GdkRectangle *clip_rectangle)
479 {
480 gint justify_right;
481 justify_right = (GTK_CMCLIST (ctree)->column[column].justification == GTK_JUSTIFY_RIGHT);
482
483 if (justify_right)
484 return (clip_rectangle->x + clip_rectangle->width - 1 -
485 ctree->tree_indent * (ctree_row->level - 1));
486
487 return clip_rectangle->x + ctree->tree_indent * (ctree_row->level - 1);
488 }
489
490 static void
get_cell_style(GtkCMCList * clist,GtkCMCListRow * clist_row,gint state,gint column,GtkStyle ** style)491 get_cell_style (GtkCMCList *clist,
492 GtkCMCListRow *clist_row,
493 gint state,
494 gint column,
495 GtkStyle **style)
496 {
497 GtkStyle *gtkstyle;
498
499 gtkstyle = gtk_widget_get_style (GTK_WIDGET (clist));
500
501 if (clist_row->cell[column].style)
502 {
503 if (style)
504 *style = clist_row->cell[column].style;
505 }
506 else if (clist_row->style)
507 {
508 if (style)
509 *style = clist_row->style;
510 }
511 else
512 {
513 if (style)
514 *style = gtkstyle;
515 }
516 }
517
filter_fg(PangoAttribute * attribute,gpointer data)518 static gboolean filter_fg (PangoAttribute *attribute, gpointer data)
519 {
520 const PangoAttrClass *klass = attribute->klass;
521 if (klass->type == PANGO_ATTR_FOREGROUND)
522 return TRUE;
523
524 return FALSE;
525 }
526
527 static PangoLayout *
create_cell_layout(GtkCMCList * clist,GtkCMCListRow * clist_row,gint column)528 create_cell_layout (GtkCMCList *clist,
529 GtkCMCListRow *clist_row,
530 gint column)
531 {
532 PangoLayout *layout;
533 GtkStyle *style;
534 GtkCMCell *cell;
535 gchar *text;
536
537 get_cell_style (clist, clist_row, GTK_STATE_NORMAL, column, &style);
538
539
540 cell = &clist_row->cell[column];
541 switch (cell->type)
542 {
543 case GTK_CMCELL_TEXT:
544 case GTK_CMCELL_PIXTEXT:
545 text = ((cell->type == GTK_CMCELL_PIXTEXT) ?
546 GTK_CMCELL_PIXTEXT (*cell)->text :
547 GTK_CMCELL_TEXT (*cell)->text);
548
549 if (!text)
550 return NULL;
551
552 if (!GTK_SCTREE(clist)->use_markup[column]) {
553 layout = gtk_widget_create_pango_layout (GTK_WIDGET (clist),
554 ((cell->type == GTK_CMCELL_PIXTEXT) ?
555 GTK_CMCELL_PIXTEXT (*cell)->text :
556 GTK_CMCELL_TEXT (*cell)->text));
557 pango_layout_set_font_description (layout, style->font_desc);
558 } else {
559 PangoContext *context = gtk_widget_get_pango_context (GTK_WIDGET(clist));
560 layout = pango_layout_new (context);
561 pango_layout_set_markup (layout, text, -1);
562 pango_layout_set_font_description (layout, style->font_desc);
563 if (clist_row->state == GTK_STATE_SELECTED) {
564 /* for selected row, we should remove any forced foreground color
565 * or it looks like shit */
566 PangoAttrList *list = pango_layout_get_attributes(layout);
567 PangoAttrList *rem = pango_attr_list_filter(list, filter_fg, NULL);
568 if (rem)
569 pango_attr_list_unref(rem);
570 }
571 }
572
573 return layout;
574
575 default:
576 return NULL;
577 }
578 }
579
580
581 static void
draw_row(GtkCMCList * clist,GdkRectangle * area,gint row,GtkCMCListRow * clist_row)582 draw_row (GtkCMCList *clist,
583 GdkRectangle *area,
584 gint row,
585 GtkCMCListRow *clist_row)
586 {
587 GtkWidget *widget;
588 GtkStyle *style;
589 GtkCMCTree *ctree;
590 GdkRectangle *crect;
591 GdkRectangle row_rectangle;
592 GdkRectangle cell_rectangle;
593 GdkRectangle clip_rectangle;
594 GdkRectangle intersect_rectangle;
595 gint last_column;
596 gint offset = 0;
597 gint state;
598 gint i;
599 static GdkColor greybg={0, 0, 0, 0};
600 static gboolean color_change = TRUE;
601 cairo_t *cr;
602 cairo_t *cr_hw;
603 cairo_surface_t *image_surface;
604 GdkColor *fgcolor, *bgcolor;
605
606 cm_return_if_fail (clist != NULL);
607 widget = GTK_WIDGET (clist);
608
609 /* if the function is passed the pointer to the row instead of null,
610 * it avoids this expensive lookup */
611 if (!clist_row)
612 clist_row = (g_list_nth (clist->row_list, row))->data;
613
614 style = clist_row->style ? clist_row->style : gtk_widget_get_style (widget);
615
616 if (greybg.pixel == 0 &&
617 greybg.red == 0 &&
618 greybg.green == 0 &&
619 greybg.blue == 0) {
620 GdkColor normalbg = {0, 0xffff, 0xffff, 0xffff};
621 if (style) {
622 normalbg = style->base[GTK_STATE_NORMAL];
623 }
624 if (normalbg.red > 0x8888 && normalbg.green > 0x8888 && normalbg.blue > 0x8888) {
625 greybg.pixel = normalbg.pixel;
626 greybg.red = normalbg.red - prefs_common.stripes_color_offset;
627 greybg.green = normalbg.green - prefs_common.stripes_color_offset;
628 greybg.blue = normalbg.blue - prefs_common.stripes_color_offset;
629 } else if (normalbg.red < 0x8888 && normalbg.green < 0x8888 && normalbg.blue < 0x8888) {
630 greybg.pixel = normalbg.pixel;
631 greybg.red = normalbg.red + prefs_common.stripes_color_offset;
632 greybg.green = normalbg.green + prefs_common.stripes_color_offset;
633 greybg.blue = normalbg.blue + prefs_common.stripes_color_offset;
634 } else {
635 color_change = FALSE;
636 }
637 }
638
639 /* bail now if we arn't drawable yet */
640 if (!gtk_widget_is_drawable (GTK_WIDGET(clist)) || row < 0 || row >= clist->rows)
641 return;
642
643 ctree = GTK_CMCTREE (clist);
644
645 /* rectangle of the entire row */
646 row_rectangle.x = 0;
647 row_rectangle.y = ROW_TOP_YPIXEL (clist, row);
648 row_rectangle.width = clist->clist_window_width;
649 row_rectangle.height = clist->row_height;
650
651 /* rectangle of the cell spacing above the row */
652 cell_rectangle.x = 0;
653 cell_rectangle.y = row_rectangle.y - CELL_SPACING;
654 cell_rectangle.width = row_rectangle.width;
655 cell_rectangle.height = CELL_SPACING;
656
657 /* rectangle used to clip drawing operations, its y and height
658 * positions only need to be set once, so we set them once here.
659 * the x and width are set withing the drawing loop below once per
660 * column */
661 clip_rectangle.y = row_rectangle.y;
662 clip_rectangle.height = row_rectangle.height;
663
664 if (prefs_common.use_stripes_everywhere && GTK_SCTREE(ctree)->show_stripes
665 && color_change && row % 2) {
666 bgcolor = &greybg;
667 } else {
668 bgcolor = &style->base[GTK_STATE_NORMAL];
669 }
670 state = clist_row->state;
671
672 cr_hw = gdk_cairo_create(clist->clist_window);
673 image_surface = cairo_surface_create_similar_image(cairo_get_target(cr_hw),
674 CAIRO_FORMAT_RGB24,
675 gdk_window_get_width(clist->clist_window),
676 gdk_window_get_height(clist->clist_window));
677 cr = cairo_create(image_surface);
678
679 if (clist_row->fg_set && state != GTK_STATE_SELECTED)
680 fgcolor = &clist_row->foreground;
681 else
682 fgcolor = &style->text[clist_row->state];
683 /* draw the cell borders */
684 if (area)
685 {
686 crect = &intersect_rectangle;
687
688 if (gdk_rectangle_intersect (area, &cell_rectangle, crect)) {
689 gdk_cairo_rectangle(cr, &cell_rectangle);
690 gdk_cairo_set_source_color(cr, &style->base[GTK_STATE_NORMAL]);
691 cairo_fill(cr);
692 cairo_rectangle(cr, cell_rectangle.x, cell_rectangle.y + row_rectangle.height + 1,cell_rectangle.width,cell_rectangle.height);
693 cairo_fill(cr);
694 }
695 }
696 else
697 {
698 crect = &cell_rectangle;
699
700 gdk_cairo_rectangle(cr, &cell_rectangle);
701 gdk_cairo_set_source_color(cr, &style->base[GTK_STATE_NORMAL]);
702 cairo_fill(cr);
703 cairo_rectangle(cr, cell_rectangle.x, cell_rectangle.y + row_rectangle.height + 1,cell_rectangle.width,cell_rectangle.height);
704 cairo_fill(cr);
705 }
706
707 /* the last row has to clear its bottom cell spacing too */
708 if (clist_row == clist->row_list_end->data)
709 {
710 cell_rectangle.y += clist->row_height + CELL_SPACING;
711
712 if (!area || gdk_rectangle_intersect (area, &cell_rectangle, crect))
713 {
714 gdk_cairo_rectangle(cr, crect);
715 gdk_cairo_set_source_color(cr, &style->base[GTK_STATE_NORMAL]);
716 cairo_fill(cr);
717 }
718 }
719
720 for (last_column = clist->columns - 1;
721 last_column >= 0 && !clist->column[last_column].visible; last_column--)
722 ;
723
724 /* iterate and draw all the columns (row cells) and draw their contents */
725 for (i = 0; i < clist->columns; i++)
726 {
727 GtkStyle *style;
728 PangoLayout *layout = NULL;
729 PangoRectangle logical_rect;
730
731 gint width;
732 gint height;
733 gint pixbuf_width;
734 gint string_width;
735 gint old_offset;
736
737 if (!clist->column[i].visible)
738 continue;
739
740 get_cell_style (clist, clist_row, state, i, &style);
741
742 /* calculate clipping region */
743 clip_rectangle.x = clist->column[i].area.x + clist->hoffset;
744 clip_rectangle.width = clist->column[i].area.width;
745
746 cell_rectangle.x = clip_rectangle.x - COLUMN_INSET - CELL_SPACING;
747 cell_rectangle.width = (clip_rectangle.width + 2 * COLUMN_INSET +
748 (1 + (i == last_column)) * CELL_SPACING);
749 cell_rectangle.y = clip_rectangle.y;
750 cell_rectangle.height = clip_rectangle.height;
751
752 string_width = 0;
753 pixbuf_width = 0;
754 height = 0;
755
756 if (area && !gdk_rectangle_intersect (area, &cell_rectangle,
757 &intersect_rectangle))
758 {
759 if (i != ctree->tree_column)
760 continue;
761 }
762 else
763 {
764 gdk_cairo_rectangle(cr, &cell_rectangle);
765 if (state == GTK_STATE_NORMAL)
766 gdk_cairo_set_source_color(cr, bgcolor);
767 else
768 gdk_cairo_set_source_color(cr, &style->base[state]);
769 cairo_fill(cr);
770 layout = create_cell_layout (clist, clist_row, i);
771 if (layout)
772 {
773 pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
774 width = logical_rect.width;
775 }
776 else
777 width = 0;
778
779 switch (clist_row->cell[i].type)
780 {
781 case GTK_CMCELL_PIXBUF:
782 pixbuf_width = gdk_pixbuf_get_width(GTK_CMCELL_PIXBUF (clist_row->cell[i])->pixbuf);
783 height = gdk_pixbuf_get_height(GTK_CMCELL_PIXBUF (clist_row->cell[i])->pixbuf);
784 width += pixbuf_width;
785 break;
786 case GTK_CMCELL_PIXTEXT:
787 if (GTK_CMCELL_PIXTEXT (clist_row->cell[i])->pixbuf)
788 {
789 pixbuf_width = gdk_pixbuf_get_width(GTK_CMCELL_PIXTEXT (clist_row->cell[i])->pixbuf);
790 height = gdk_pixbuf_get_height(GTK_CMCELL_PIXTEXT (clist_row->cell[i])->pixbuf);
791 width += pixbuf_width;
792 }
793
794 if (GTK_CMCELL_PIXTEXT (clist_row->cell[i])->text &&
795 GTK_CMCELL_PIXTEXT (clist_row->cell[i])->pixbuf)
796 width += GTK_CMCELL_PIXTEXT (clist_row->cell[i])->spacing;
797
798 if (i == ctree->tree_column)
799 width += (ctree->tree_indent *
800 ((GtkCMCTreeRow *)clist_row)->level);
801 break;
802 default:
803 break;
804 }
805
806 switch (clist->column[i].justification)
807 {
808 case GTK_JUSTIFY_LEFT:
809 offset = clip_rectangle.x + clist_row->cell[i].horizontal;
810 break;
811 case GTK_JUSTIFY_RIGHT:
812 offset = (clip_rectangle.x + clist_row->cell[i].horizontal +
813 clip_rectangle.width - width);
814 break;
815 case GTK_JUSTIFY_CENTER:
816 case GTK_JUSTIFY_FILL:
817 offset = (clip_rectangle.x + clist_row->cell[i].horizontal +
818 (clip_rectangle.width / 2) - (width / 2));
819 break;
820 };
821
822 if (i != ctree->tree_column)
823 {
824 int start_y = (clip_rectangle.height - height) / 2;
825 if (GTK_CMCLIST_ROW_HEIGHT_SET(GTK_CMCLIST(clist)))
826 start_y = (clip_rectangle.height/2 - height) / 2;
827
828 offset += clist_row->cell[i].horizontal;
829 switch (clist_row->cell[i].type)
830 {
831 case GTK_CMCELL_PIXBUF:
832 draw_cell_pixbuf
833 (clist->clist_window, &clip_rectangle, cr,
834 GTK_CMCELL_PIXBUF (clist_row->cell[i])->pixbuf,
835 offset,
836 clip_rectangle.y + clist_row->cell[i].vertical +
837 start_y,
838 pixbuf_width, height);
839 break;
840 case GTK_CMCELL_PIXTEXT:
841 offset = draw_cell_pixbuf
842 (clist->clist_window, &clip_rectangle, cr,
843 GTK_CMCELL_PIXTEXT (clist_row->cell[i])->pixbuf,
844 offset,
845 clip_rectangle.y + clist_row->cell[i].vertical +
846 start_y,
847 pixbuf_width, height);
848 offset += GTK_CMCELL_PIXTEXT (clist_row->cell[i])->spacing;
849
850 /* Fall through */
851 case GTK_CMCELL_TEXT:
852 if (layout)
853 {
854 gint row_center_offset = (clist->row_height - logical_rect.height) / 2;
855 gdk_cairo_set_source_color(cr, fgcolor);
856 cairo_move_to(cr, offset, row_rectangle.y + row_center_offset + clist_row->cell[i].vertical);
857 pango_cairo_show_layout(cr, layout);
858 g_object_unref (G_OBJECT (layout));
859 }
860 break;
861 default:
862 break;
863 }
864 continue;
865 }
866 }
867
868 /* draw ctree->tree_column */
869 cell_rectangle.y -= CELL_SPACING;
870 cell_rectangle.height += CELL_SPACING;
871
872 if (area && !gdk_rectangle_intersect (area, &cell_rectangle,
873 &intersect_rectangle))
874 {
875 if (layout)
876 g_object_unref (G_OBJECT (layout));
877 continue;
878 }
879
880
881 /* draw lines */
882 offset = get_offset (ctree, (GtkCMCTreeRow *)clist_row, i,
883 &clip_rectangle);
884
885 /* draw expander */
886 offset = draw_expander (ctree, (GtkCMCTreeRow *)clist_row,
887 style, &clip_rectangle, cr, offset);
888
889 if (clist->column[i].justification == GTK_JUSTIFY_RIGHT)
890 offset -= ctree->tree_spacing;
891 else
892 offset += ctree->tree_spacing;
893
894 if (clist->column[i].justification == GTK_JUSTIFY_RIGHT)
895 offset -= (pixbuf_width + clist_row->cell[i].horizontal);
896 else
897 offset += clist_row->cell[i].horizontal;
898
899 old_offset = offset;
900 offset = draw_cell_pixbuf (clist->clist_window, &clip_rectangle, cr,
901 GTK_CMCELL_PIXTEXT (clist_row->cell[i])->pixbuf,
902 offset,
903 clip_rectangle.y + clist_row->cell[i].vertical
904 + (clip_rectangle.height - height) / 2,
905 pixbuf_width, height);
906
907 if (layout)
908 {
909 gint row_center_offset = (clist->row_height - logical_rect.height) / 2;
910
911 if (clist->column[i].justification == GTK_JUSTIFY_RIGHT)
912 {
913 offset = (old_offset - string_width);
914 if (GTK_CMCELL_PIXTEXT (clist_row->cell[i])->pixbuf)
915 offset -= GTK_CMCELL_PIXTEXT (clist_row->cell[i])->spacing;
916 }
917 else
918 {
919 if (GTK_CMCELL_PIXTEXT (clist_row->cell[i])->pixbuf)
920 offset += GTK_CMCELL_PIXTEXT (clist_row->cell[i])->spacing;
921 }
922
923 cairo_move_to(cr, offset, row_rectangle.y + row_center_offset + clist_row->cell[i].vertical);
924 gdk_cairo_set_source_color(cr, fgcolor);
925 pango_cairo_show_layout(cr, layout);
926 g_object_unref (G_OBJECT (layout));
927 }
928 }
929 /* draw focus rectangle */
930 if (clist->focus_row == row &&
931 gtk_widget_get_can_focus (widget) && gtk_widget_has_focus (widget)
932 && state == GTK_STATE_SELECTED)
933 {
934 if (!area || gdk_rectangle_intersect (area, &row_rectangle,
935 &intersect_rectangle))
936 {
937 cairo_set_line_width(cr, 1.0);
938 cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE);
939 gdk_cairo_set_source_color(cr, &style->text[GTK_STATE_NORMAL]);
940 cairo_move_to (cr, row_rectangle.x, row_rectangle.y + 0.5);
941 cairo_line_to (cr, row_rectangle.x + row_rectangle.width, row_rectangle.y + 0.5);
942 cairo_move_to (cr, row_rectangle.x, row_rectangle.y + row_rectangle.height - 0.5);
943 cairo_line_to (cr, row_rectangle.x + row_rectangle.width, row_rectangle.y + row_rectangle.height - 0.5);
944 cairo_stroke(cr);
945 }
946 }
947
948 cairo_set_operator(cr_hw, CAIRO_OPERATOR_SOURCE);
949 cairo_set_source_surface(cr_hw, image_surface, 0, 0);
950 cairo_rectangle(cr_hw,
951 row_rectangle.x,
952 row_rectangle.y - CELL_SPACING,
953 row_rectangle.width,
954 row_rectangle.height + CELL_SPACING * 2);
955 cairo_fill(cr_hw);
956
957 cairo_destroy(cr);
958 cairo_surface_destroy(image_surface);
959 cairo_destroy(cr_hw);
960 }
961
962 static void
gtk_cmctree_class_init(GtkCMCTreeClass * klass)963 gtk_cmctree_class_init (GtkCMCTreeClass *klass)
964 {
965 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
966 GtkObjectClass *object_class;
967 GtkWidgetClass *widget_class;
968 GtkCMCListClass *clist_class;
969 GtkBindingSet *binding_set;
970
971 gobject_class->constructor = gtk_cmctree_constructor;
972
973 object_class = (GtkObjectClass *) klass;
974 widget_class = (GtkWidgetClass *) klass;
975 container_class = (GtkContainerClass *) klass;
976 clist_class = (GtkCMCListClass *) klass;
977
978 parent_class = g_type_class_peek (GTK_TYPE_CMCLIST);
979 container_class = g_type_class_peek (GTK_TYPE_CONTAINER);
980
981 gobject_class->set_property = gtk_cmctree_set_arg;
982 gobject_class->get_property = gtk_cmctree_get_arg;
983
984 widget_class->realize = gtk_cmctree_realize;
985 widget_class->unrealize = gtk_cmctree_unrealize;
986 widget_class->button_press_event = gtk_cmctree_button_press;
987
988 widget_class->drag_begin = gtk_cmctree_drag_begin;
989 widget_class->drag_motion = gtk_cmctree_drag_motion;
990 widget_class->drag_data_received = gtk_cmctree_drag_data_received;
991
992 clist_class->select_row = real_select_row;
993 clist_class->unselect_row = real_unselect_row;
994 clist_class->row_move = real_row_move;
995 clist_class->undo_selection = real_undo_selection;
996 clist_class->resync_selection = resync_selection;
997 clist_class->selection_find = selection_find;
998 clist_class->click_column = NULL;
999 clist_class->draw_row = draw_row;
1000 clist_class->clear = real_clear;
1001 clist_class->select_all = real_select_all;
1002 clist_class->unselect_all = real_unselect_all;
1003 clist_class->fake_unselect_all = fake_unselect_all;
1004 clist_class->insert_row = real_insert_row;
1005 clist_class->remove_row = real_remove_row;
1006 clist_class->sort_list = real_sort_list;
1007 clist_class->set_cell_contents = set_cell_contents;
1008 clist_class->cell_size_request = cell_size_request;
1009
1010 klass->tree_select_row = real_tree_select;
1011 klass->tree_unselect_row = real_tree_unselect;
1012 klass->tree_expand = real_tree_expand;
1013 klass->tree_collapse = real_tree_collapse;
1014 klass->tree_move = real_tree_move;
1015 klass->change_focus_row_expansion = change_focus_row_expansion;
1016
1017 g_object_class_install_property (gobject_class,
1018 ARG_N_COLUMNS,
1019 g_param_spec_uint ("n-columns",
1020 "N-Columns",
1021 "N-Columns",
1022 1,
1023 G_MAXINT,
1024 1,
1025 G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
1026 g_object_class_install_property (gobject_class,
1027 ARG_TREE_COLUMN,
1028 g_param_spec_uint ("tree-column",
1029 "tree-column",
1030 "tree-column",
1031 0,
1032 G_MAXINT,
1033 0,
1034 G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
1035 g_object_class_install_property (gobject_class,
1036 ARG_INDENT,
1037 g_param_spec_uint ("indent",
1038 "indent",
1039 "indent",
1040 1,
1041 G_MAXINT,
1042 1,
1043 G_PARAM_READWRITE));
1044 g_object_class_install_property (gobject_class,
1045 ARG_SPACING,
1046 g_param_spec_uint ("spacing",
1047 "spacing",
1048 "spacing",
1049 1,
1050 G_MAXINT,
1051 1,
1052 G_PARAM_READWRITE));
1053 g_object_class_install_property (gobject_class,
1054 ARG_SHOW_STUB,
1055 g_param_spec_boolean ("show-stub",
1056 "show-stub",
1057 "show-stub",
1058 TRUE,
1059 G_PARAM_READWRITE));
1060 g_object_class_install_property (gobject_class,
1061 ARG_LINE_STYLE,
1062 g_param_spec_enum ("line-style",
1063 "line-style",
1064 "line-style",
1065 GTK_TYPE_CMCTREE_LINE_STYLE, 0,
1066 G_PARAM_READWRITE));
1067 g_object_class_install_property (gobject_class,
1068 ARG_EXPANDER_STYLE,
1069 g_param_spec_enum ("expander-style",
1070 "expander-style",
1071 "expander-style",
1072 GTK_TYPE_CMCTREE_EXPANDER_STYLE, 0,
1073 G_PARAM_READWRITE));
1074
1075 ctree_signals[TREE_SELECT_ROW] =
1076 g_signal_new ("tree_select_row",
1077 G_TYPE_FROM_CLASS (object_class),
1078 G_SIGNAL_RUN_FIRST,
1079 G_STRUCT_OFFSET (GtkCMCTreeClass, tree_select_row),
1080 NULL, NULL,
1081 claws_marshal_VOID__POINTER_INT,
1082 G_TYPE_NONE, 2,
1083 GTK_TYPE_CMCTREE_NODE,
1084 G_TYPE_INT);
1085 ctree_signals[TREE_UNSELECT_ROW] =
1086 g_signal_new ("tree_unselect_row",
1087 G_TYPE_FROM_CLASS (object_class),
1088 G_SIGNAL_RUN_FIRST,
1089 G_STRUCT_OFFSET (GtkCMCTreeClass, tree_unselect_row),
1090 NULL, NULL,
1091 claws_marshal_VOID__POINTER_INT,
1092 G_TYPE_NONE, 2,
1093 GTK_TYPE_CMCTREE_NODE,
1094 G_TYPE_INT);
1095 ctree_signals[TREE_EXPAND] =
1096 g_signal_new ("tree_expand",
1097 G_TYPE_FROM_CLASS (object_class),
1098 G_SIGNAL_RUN_LAST,
1099 G_STRUCT_OFFSET (GtkCMCTreeClass, tree_expand),
1100 NULL, NULL,
1101 claws_marshal_VOID__POINTER,
1102 G_TYPE_NONE, 1,
1103 GTK_TYPE_CMCTREE_NODE);
1104 ctree_signals[TREE_COLLAPSE] =
1105 g_signal_new ("tree_collapse",
1106 G_TYPE_FROM_CLASS (object_class),
1107 G_SIGNAL_RUN_LAST,
1108 G_STRUCT_OFFSET (GtkCMCTreeClass, tree_collapse),
1109 NULL, NULL,
1110 claws_marshal_VOID__POINTER,
1111 G_TYPE_NONE, 1,
1112 GTK_TYPE_CMCTREE_NODE);
1113 ctree_signals[TREE_MOVE] =
1114 g_signal_new ("tree_move",
1115 G_TYPE_FROM_CLASS (object_class),
1116 G_SIGNAL_RUN_LAST,
1117 G_STRUCT_OFFSET (GtkCMCTreeClass, tree_move),
1118 NULL, NULL,
1119 claws_marshal_VOID__POINTER_POINTER_POINTER,
1120 G_TYPE_NONE, 3,
1121 GTK_TYPE_CMCTREE_NODE,GTK_TYPE_CMCTREE_NODE,GTK_TYPE_CMCTREE_NODE);
1122 ctree_signals[CHANGE_FOCUS_ROW_EXPANSION] =
1123 g_signal_new ("change_focus_row_expansion",
1124 G_TYPE_FROM_CLASS (object_class),
1125 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1126 G_STRUCT_OFFSET (GtkCMCTreeClass, change_focus_row_expansion),
1127 NULL, NULL,
1128 claws_marshal_VOID__ENUM,
1129 G_TYPE_NONE, 1, GTK_TYPE_CMCTREE_EXPANSION_TYPE);
1130
1131 binding_set = gtk_binding_set_by_class (klass);
1132 gtk_binding_entry_add_signal (binding_set,
1133 GDK_KEY_plus, 0,
1134 "change_focus_row_expansion", 1,
1135 G_TYPE_ENUM, GTK_CMCTREE_EXPANSION_EXPAND);
1136 gtk_binding_entry_add_signal (binding_set,
1137 GDK_KEY_plus, GDK_CONTROL_MASK,
1138 "change_focus_row_expansion", 1,
1139 G_TYPE_ENUM, GTK_CMCTREE_EXPANSION_EXPAND_RECURSIVE);
1140
1141 gtk_binding_entry_add_signal (binding_set,
1142 GDK_KEY_KP_Add, 0,
1143 "change_focus_row_expansion", 1,
1144 G_TYPE_ENUM, GTK_CMCTREE_EXPANSION_EXPAND);
1145 gtk_binding_entry_add_signal (binding_set,
1146 GDK_KEY_KP_Add, GDK_CONTROL_MASK,
1147 "change_focus_row_expansion", 1,
1148 G_TYPE_ENUM, GTK_CMCTREE_EXPANSION_EXPAND_RECURSIVE);
1149
1150 gtk_binding_entry_add_signal (binding_set,
1151 GDK_KEY_minus, 0,
1152 "change_focus_row_expansion", 1,
1153 G_TYPE_ENUM, GTK_CMCTREE_EXPANSION_COLLAPSE);
1154 gtk_binding_entry_add_signal (binding_set,
1155 GDK_KEY_minus, GDK_CONTROL_MASK,
1156 "change_focus_row_expansion", 1,
1157 G_TYPE_ENUM,
1158 GTK_CMCTREE_EXPANSION_COLLAPSE_RECURSIVE);
1159 gtk_binding_entry_add_signal (binding_set,
1160 GDK_KEY_KP_Subtract, 0,
1161 "change_focus_row_expansion", 1,
1162 G_TYPE_ENUM, GTK_CMCTREE_EXPANSION_COLLAPSE);
1163 gtk_binding_entry_add_signal (binding_set,
1164 GDK_KEY_KP_Subtract, GDK_CONTROL_MASK,
1165 "change_focus_row_expansion", 1,
1166 G_TYPE_ENUM,
1167 GTK_CMCTREE_EXPANSION_COLLAPSE_RECURSIVE);
1168 gtk_binding_entry_add_signal (binding_set,
1169 GDK_KEY_equal, 0,
1170 "change_focus_row_expansion", 1,
1171 G_TYPE_ENUM, GTK_CMCTREE_EXPANSION_TOGGLE);
1172 gtk_binding_entry_add_signal (binding_set,
1173 GDK_KEY_KP_Equal, 0,
1174 "change_focus_row_expansion", 1,
1175 G_TYPE_ENUM, GTK_CMCTREE_EXPANSION_TOGGLE);
1176 gtk_binding_entry_add_signal (binding_set,
1177 GDK_KEY_KP_Multiply, 0,
1178 "change_focus_row_expansion", 1,
1179 G_TYPE_ENUM, GTK_CMCTREE_EXPANSION_TOGGLE);
1180 gtk_binding_entry_add_signal (binding_set,
1181 GDK_KEY_asterisk, 0,
1182 "change_focus_row_expansion", 1,
1183 G_TYPE_ENUM, GTK_CMCTREE_EXPANSION_TOGGLE);
1184 gtk_binding_entry_add_signal (binding_set,
1185 GDK_KEY_KP_Multiply, GDK_CONTROL_MASK,
1186 "change_focus_row_expansion", 1,
1187 G_TYPE_ENUM,
1188 GTK_CMCTREE_EXPANSION_TOGGLE_RECURSIVE);
1189 gtk_binding_entry_add_signal (binding_set,
1190 GDK_KEY_asterisk, GDK_CONTROL_MASK,
1191 "change_focus_row_expansion", 1,
1192 G_TYPE_ENUM,
1193 GTK_CMCTREE_EXPANSION_TOGGLE_RECURSIVE);
1194 }
1195
1196 static void
gtk_cmctree_set_arg(GObject * object,guint arg_id,const GValue * value,GParamSpec * spec)1197 gtk_cmctree_set_arg (GObject *object,
1198 guint arg_id,
1199 const GValue *value,
1200 GParamSpec *spec)
1201 {
1202 GtkCMCTree *ctree;
1203 GtkCMCList *clist;
1204
1205 ctree = GTK_CMCTREE (object);
1206 clist = GTK_CMCLIST (ctree);
1207
1208 switch (arg_id)
1209 {
1210 case ARG_N_COLUMNS: /* construct-only arg, only set at construction time */
1211 clist->columns = MAX (1, g_value_get_uint (value));
1212 ctree->tree_column = CLAMP (ctree->tree_column, 0, clist->columns);
1213 break;
1214 case ARG_TREE_COLUMN: /* construct-only arg, only set at construction time */
1215 ctree->tree_column = g_value_get_uint (value);
1216 ctree->tree_column = CLAMP (ctree->tree_column, 0, clist->columns);
1217 break;
1218 case ARG_INDENT:
1219 gtk_cmctree_set_indent (ctree, g_value_get_uint (value));
1220 break;
1221 case ARG_SPACING:
1222 gtk_cmctree_set_spacing (ctree, g_value_get_uint (value));
1223 break;
1224 case ARG_SHOW_STUB:
1225 gtk_cmctree_set_show_stub (ctree, g_value_get_boolean (value));
1226 break;
1227 case ARG_LINE_STYLE:
1228 gtk_cmctree_set_line_style (ctree, g_value_get_enum (value));
1229 break;
1230 case ARG_EXPANDER_STYLE:
1231 gtk_cmctree_set_expander_style (ctree, g_value_get_enum (value));
1232 break;
1233 default:
1234 break;
1235 }
1236 }
1237
1238 static void
gtk_cmctree_get_arg(GObject * object,guint arg_id,GValue * value,GParamSpec * spec)1239 gtk_cmctree_get_arg (GObject *object,
1240 guint arg_id,
1241 GValue *value,
1242 GParamSpec *spec)
1243 {
1244 GtkCMCTree *ctree;
1245
1246 ctree = GTK_CMCTREE (object);
1247
1248 switch (arg_id)
1249 {
1250 case ARG_N_COLUMNS:
1251 g_value_set_uint(value, GTK_CMCLIST (ctree)->columns);
1252 break;
1253 case ARG_TREE_COLUMN:
1254 g_value_set_uint(value, ctree->tree_column);
1255 break;
1256 case ARG_INDENT:
1257 g_value_set_uint(value, ctree->tree_indent);
1258 break;
1259 case ARG_SPACING:
1260 g_value_set_uint(value, ctree->tree_spacing);
1261 break;
1262 case ARG_SHOW_STUB:
1263 g_value_set_boolean(value, ctree->show_stub);
1264 break;
1265 case ARG_LINE_STYLE:
1266 g_value_set_enum(value, ctree->line_style);
1267 break;
1268 case ARG_EXPANDER_STYLE:
1269 g_value_set_enum(value, ctree->expander_style);
1270 break;
1271 default:
1272 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, arg_id, spec);
1273 break;
1274 }
1275 }
1276
1277 static void
gtk_cmctree_init(GtkCMCTree * ctree)1278 gtk_cmctree_init (GtkCMCTree *ctree)
1279 {
1280 GtkCMCList *clist;
1281
1282 GTK_CMCLIST_SET_FLAG (ctree, CMCLIST_DRAW_DRAG_RECT);
1283 GTK_CMCLIST_SET_FLAG (ctree, CMCLIST_DRAW_DRAG_LINE);
1284
1285 clist = GTK_CMCLIST (ctree);
1286
1287 ctree->tree_indent = 20;
1288 ctree->tree_spacing = 5;
1289 ctree->tree_column = 0;
1290 ctree->line_style = GTK_CMCTREE_LINES_NONE;
1291 ctree->expander_style = GTK_CMCTREE_EXPANDER_TRIANGLE;
1292 ctree->drag_compare = NULL;
1293 ctree->show_stub = TRUE;
1294
1295 clist->button_actions[0] |= GTK_CMBUTTON_EXPANDS;
1296 }
1297
1298 static void
ctree_attach_styles(GtkCMCTree * ctree,GtkCMCTreeNode * node,gpointer data)1299 ctree_attach_styles (GtkCMCTree *ctree,
1300 GtkCMCTreeNode *node,
1301 gpointer data)
1302 {
1303 GtkCMCList *clist;
1304 gint i;
1305
1306 clist = GTK_CMCLIST (ctree);
1307
1308 if (GTK_CMCTREE_ROW (node)->row.style)
1309 GTK_CMCTREE_ROW (node)->row.style =
1310 gtk_style_attach (GTK_CMCTREE_ROW (node)->row.style, clist->clist_window);
1311
1312 if (GTK_CMCTREE_ROW (node)->row.fg_set || GTK_CMCTREE_ROW (node)->row.bg_set)
1313 {
1314 GdkColormap *colormap;
1315
1316 colormap = gtk_widget_get_colormap (GTK_WIDGET (ctree));
1317 if (GTK_CMCTREE_ROW (node)->row.fg_set)
1318 gdk_colormap_alloc_color (colormap, &(GTK_CMCTREE_ROW (node)->row.foreground), TRUE, TRUE);
1319 if (GTK_CMCTREE_ROW (node)->row.bg_set)
1320 gdk_colormap_alloc_color (colormap, &(GTK_CMCTREE_ROW (node)->row.background), TRUE, TRUE);
1321 }
1322
1323 for (i = 0; i < clist->columns; i++)
1324 if (GTK_CMCTREE_ROW (node)->row.cell[i].style)
1325 GTK_CMCTREE_ROW (node)->row.cell[i].style =
1326 gtk_style_attach (GTK_CMCTREE_ROW (node)->row.cell[i].style,
1327 clist->clist_window);
1328 }
1329
1330 static void
ctree_detach_styles(GtkCMCTree * ctree,GtkCMCTreeNode * node,gpointer data)1331 ctree_detach_styles (GtkCMCTree *ctree,
1332 GtkCMCTreeNode *node,
1333 gpointer data)
1334 {
1335 GtkCMCList *clist;
1336 gint i;
1337
1338 clist = GTK_CMCLIST (ctree);
1339
1340 if (GTK_CMCTREE_ROW (node)->row.style)
1341 gtk_style_detach (GTK_CMCTREE_ROW (node)->row.style);
1342 for (i = 0; i < clist->columns; i++)
1343 if (GTK_CMCTREE_ROW (node)->row.cell[i].style)
1344 gtk_style_detach (GTK_CMCTREE_ROW (node)->row.cell[i].style);
1345 }
1346
1347 static void
gtk_cmctree_realize(GtkWidget * widget)1348 gtk_cmctree_realize (GtkWidget *widget)
1349 {
1350 GtkCMCTree *ctree;
1351 GtkCMCList *clist;
1352 GtkCMCTreeNode *node;
1353 GtkCMCTreeNode *child;
1354 gint i;
1355
1356 cm_return_if_fail (GTK_IS_CMCTREE (widget));
1357
1358 GTK_WIDGET_CLASS (parent_class)->realize (widget);
1359
1360 ctree = GTK_CMCTREE (widget);
1361 clist = GTK_CMCLIST (widget);
1362
1363 node = GTK_CMCTREE_NODE (clist->row_list);
1364 for (i = 0; i < clist->rows; i++)
1365 {
1366 if (GTK_CMCTREE_ROW (node)->children && !GTK_CMCTREE_ROW (node)->expanded)
1367 for (child = GTK_CMCTREE_ROW (node)->children; child;
1368 child = GTK_CMCTREE_ROW (child)->sibling)
1369 gtk_cmctree_pre_recursive (ctree, child, ctree_attach_styles, NULL);
1370 node = GTK_CMCTREE_NODE_NEXT (node);
1371 }
1372 }
1373
1374 static void
gtk_cmctree_unrealize(GtkWidget * widget)1375 gtk_cmctree_unrealize (GtkWidget *widget)
1376 {
1377 GtkCMCTree *ctree;
1378 GtkCMCList *clist;
1379
1380 cm_return_if_fail (GTK_IS_CMCTREE (widget));
1381
1382 GTK_WIDGET_CLASS (parent_class)->unrealize (widget);
1383
1384 ctree = GTK_CMCTREE (widget);
1385 clist = GTK_CMCLIST (widget);
1386
1387 if (gtk_widget_get_realized (widget))
1388 {
1389 GtkCMCTreeNode *node;
1390 GtkCMCTreeNode *child;
1391 gint i;
1392
1393 node = GTK_CMCTREE_NODE (clist->row_list);
1394 for (i = 0; i < clist->rows; i++)
1395 {
1396 if (GTK_CMCTREE_ROW (node)->children &&
1397 !GTK_CMCTREE_ROW (node)->expanded)
1398 for (child = GTK_CMCTREE_ROW (node)->children; child;
1399 child = GTK_CMCTREE_ROW (child)->sibling)
1400 gtk_cmctree_pre_recursive(ctree, child, ctree_detach_styles, NULL);
1401 node = GTK_CMCTREE_NODE_NEXT (node);
1402 }
1403 }
1404 }
1405
1406 static gint
gtk_cmctree_button_press(GtkWidget * widget,GdkEventButton * event)1407 gtk_cmctree_button_press (GtkWidget *widget,
1408 GdkEventButton *event)
1409 {
1410 GtkCMCTree *ctree;
1411 GtkCMCList *clist;
1412 gint button_actions;
1413
1414 cm_return_val_if_fail (GTK_IS_CMCTREE (widget), FALSE);
1415 cm_return_val_if_fail (event != NULL, FALSE);
1416
1417 ctree = GTK_CMCTREE (widget);
1418 clist = GTK_CMCLIST (widget);
1419
1420 button_actions = clist->button_actions[event->button - 1];
1421
1422 if (button_actions == GTK_CMBUTTON_IGNORED)
1423 return FALSE;
1424
1425 if (event->window == clist->clist_window)
1426 {
1427 GtkCMCTreeNode *work;
1428 gint x;
1429 gint y;
1430 gint row;
1431 gint column;
1432
1433 x = event->x;
1434 y = event->y;
1435
1436 if (!gtk_cmclist_get_selection_info (clist, x, y, &row, &column))
1437 return FALSE;
1438
1439 work = GTK_CMCTREE_NODE (g_list_nth (clist->row_list, row));
1440
1441 if (button_actions & GTK_CMBUTTON_EXPANDS &&
1442 (GTK_CMCTREE_ROW (work)->children && !GTK_CMCTREE_ROW (work)->is_leaf &&
1443 (event->type == GDK_2BUTTON_PRESS ||
1444 ctree_is_hot_spot (ctree, work, row, x, y))))
1445 {
1446 if (GTK_CMCTREE_ROW (work)->expanded)
1447 gtk_cmctree_collapse (ctree, work);
1448 else
1449 gtk_cmctree_expand (ctree, work);
1450
1451 return TRUE;
1452 }
1453 }
1454
1455 return GTK_WIDGET_CLASS (parent_class)->button_press_event (widget, event);
1456 }
1457
1458 static GtkCMCTreeNode *
gtk_cmctree_last_visible(GtkCMCTree * ctree,GtkCMCTreeNode * node)1459 gtk_cmctree_last_visible (GtkCMCTree *ctree,
1460 GtkCMCTreeNode *node)
1461 {
1462 GtkCMCTreeNode *work;
1463
1464 if (!node)
1465 return NULL;
1466
1467 work = GTK_CMCTREE_ROW (node)->children;
1468
1469 if (!work || !GTK_CMCTREE_ROW (node)->expanded)
1470 return node;
1471
1472 while (GTK_CMCTREE_ROW (work)->sibling)
1473 work = GTK_CMCTREE_ROW (work)->sibling;
1474
1475 return gtk_cmctree_last_visible (ctree, work);
1476 }
1477
1478 static void
gtk_cmctree_link(GtkCMCTree * ctree,GtkCMCTreeNode * node,GtkCMCTreeNode * parent,GtkCMCTreeNode * sibling,gboolean update_focus_row)1479 gtk_cmctree_link (GtkCMCTree *ctree,
1480 GtkCMCTreeNode *node,
1481 GtkCMCTreeNode *parent,
1482 GtkCMCTreeNode *sibling,
1483 gboolean update_focus_row)
1484 {
1485 GtkCMCList *clist;
1486 GList *list_end;
1487 GList *list;
1488 GList *work;
1489 gboolean visible = FALSE;
1490 gint rows = 0;
1491
1492 if (sibling)
1493 cm_return_if_fail (GTK_CMCTREE_ROW (sibling)->parent == parent);
1494 cm_return_if_fail (node != NULL);
1495 cm_return_if_fail (node != sibling);
1496 cm_return_if_fail (node != parent);
1497
1498 clist = GTK_CMCLIST (ctree);
1499
1500 if (update_focus_row && clist->selection_mode == GTK_SELECTION_MULTIPLE)
1501 {
1502 GTK_CMCLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
1503
1504 g_list_free (clist->undo_selection);
1505 g_list_free (clist->undo_unselection);
1506 clist->undo_selection = NULL;
1507 clist->undo_unselection = NULL;
1508 }
1509
1510 for (rows = 1, list_end = (GList *)node; list_end->next;
1511 list_end = list_end->next)
1512 rows++;
1513
1514 GTK_CMCTREE_ROW (node)->parent = parent;
1515 GTK_CMCTREE_ROW (node)->sibling = sibling;
1516
1517 if (!parent || (parent && (gtk_cmctree_is_viewable (ctree, parent) &&
1518 GTK_CMCTREE_ROW (parent)->expanded)))
1519 {
1520 visible = TRUE;
1521 clist->rows += rows;
1522 }
1523
1524 if (parent)
1525 work = (GList *)(GTK_CMCTREE_ROW (parent)->children);
1526 else
1527 work = clist->row_list;
1528
1529 if (sibling)
1530 {
1531 if (work != (GList *)sibling)
1532 {
1533 while (GTK_CMCTREE_ROW (work)->sibling != sibling)
1534 work = (GList *)(GTK_CMCTREE_ROW (work)->sibling);
1535 GTK_CMCTREE_ROW (work)->sibling = node;
1536 }
1537
1538 if (sibling == GTK_CMCTREE_NODE (clist->row_list))
1539 clist->row_list = (GList *) node;
1540 if (GTK_CMCTREE_NODE_PREV (sibling) &&
1541 GTK_CMCTREE_NODE_NEXT (GTK_CMCTREE_NODE_PREV (sibling)) == sibling)
1542 {
1543 list = (GList *)GTK_CMCTREE_NODE_PREV (sibling);
1544 list->next = (GList *)node;
1545 }
1546
1547 list = (GList *)node;
1548 list->prev = (GList *)GTK_CMCTREE_NODE_PREV (sibling);
1549 list_end->next = (GList *)sibling;
1550 list = (GList *)sibling;
1551 list->prev = list_end;
1552 if (parent && GTK_CMCTREE_ROW (parent)->children == sibling)
1553 GTK_CMCTREE_ROW (parent)->children = node;
1554 }
1555 else
1556 {
1557 if (work)
1558 {
1559 /* find sibling */
1560 while (GTK_CMCTREE_ROW (work)->sibling)
1561 work = (GList *)(GTK_CMCTREE_ROW (work)->sibling);
1562 GTK_CMCTREE_ROW (work)->sibling = node;
1563
1564 /* find last visible child of sibling */
1565 work = (GList *) gtk_cmctree_last_visible (ctree,
1566 GTK_CMCTREE_NODE (work));
1567
1568 list_end->next = work->next;
1569 if (work->next)
1570 work->next->prev = list_end;
1571 work->next = (GList *)node;
1572 list = (GList *)node;
1573 list->prev = work;
1574 }
1575 else
1576 {
1577 if (parent)
1578 {
1579 GTK_CMCTREE_ROW (parent)->children = node;
1580 list = (GList *)node;
1581 list->prev = (GList *)parent;
1582 if (GTK_CMCTREE_ROW (parent)->expanded)
1583 {
1584 list_end->next = (GList *)GTK_CMCTREE_NODE_NEXT (parent);
1585 if (GTK_CMCTREE_NODE_NEXT(parent))
1586 {
1587 list = (GList *)GTK_CMCTREE_NODE_NEXT (parent);
1588 list->prev = list_end;
1589 }
1590 list = (GList *)parent;
1591 list->next = (GList *)node;
1592 }
1593 else
1594 list_end->next = NULL;
1595 }
1596 else
1597 {
1598 clist->row_list = (GList *)node;
1599 list = (GList *)node;
1600 list->prev = NULL;
1601 list_end->next = NULL;
1602 }
1603 }
1604 }
1605
1606 gtk_cmctree_pre_recursive (ctree, node, tree_update_level, NULL);
1607
1608 if (clist->row_list_end == NULL ||
1609 clist->row_list_end->next == (GList *)node)
1610 clist->row_list_end = list_end;
1611
1612 if (visible && update_focus_row)
1613 {
1614 gint pos;
1615
1616 pos = g_list_position (clist->row_list, (GList *)node);
1617
1618 if (pos <= clist->focus_row)
1619 {
1620 clist->focus_row += rows;
1621 clist->undo_anchor = clist->focus_row;
1622 }
1623 }
1624 }
1625
1626 static void
gtk_cmctree_unlink(GtkCMCTree * ctree,GtkCMCTreeNode * node,gboolean update_focus_row)1627 gtk_cmctree_unlink (GtkCMCTree *ctree,
1628 GtkCMCTreeNode *node,
1629 gboolean update_focus_row)
1630 {
1631 GtkCMCList *clist;
1632 gint rows;
1633 gint level;
1634 gint visible;
1635 GtkCMCTreeNode *work;
1636 GtkCMCTreeNode *parent;
1637 GList *list;
1638
1639 cm_return_if_fail (GTK_IS_CMCTREE (ctree));
1640 cm_return_if_fail (node != NULL);
1641
1642 clist = GTK_CMCLIST (ctree);
1643
1644 if (update_focus_row && clist->selection_mode == GTK_SELECTION_MULTIPLE)
1645 {
1646 GTK_CMCLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
1647
1648 g_list_free (clist->undo_selection);
1649 g_list_free (clist->undo_unselection);
1650 clist->undo_selection = NULL;
1651 clist->undo_unselection = NULL;
1652 }
1653
1654 visible = gtk_cmctree_is_viewable (ctree, node);
1655
1656 /* clist->row_list_end unlinked ? */
1657 if (visible &&
1658 (GTK_CMCTREE_NODE_NEXT (node) == NULL ||
1659 (GTK_CMCTREE_ROW (node)->children &&
1660 gtk_cmctree_is_ancestor (ctree, node,
1661 GTK_CMCTREE_NODE (clist->row_list_end)))))
1662 clist->row_list_end = (GList *) (GTK_CMCTREE_NODE_PREV (node));
1663
1664 /* update list */
1665 rows = 0;
1666 level = GTK_CMCTREE_ROW (node)->level;
1667 work = GTK_CMCTREE_NODE_NEXT (node);
1668 while (work && GTK_CMCTREE_ROW (work)->level > level)
1669 {
1670 work = GTK_CMCTREE_NODE_NEXT (work);
1671 rows++;
1672 }
1673
1674 if (visible)
1675 {
1676 clist->rows -= (rows + 1);
1677
1678 if (update_focus_row)
1679 {
1680 gint pos;
1681
1682 pos = g_list_position (clist->row_list, (GList *)node);
1683 if (pos + rows < clist->focus_row)
1684 clist->focus_row -= (rows + 1);
1685 else if (pos <= clist->focus_row)
1686 {
1687 if (!GTK_CMCTREE_ROW (node)->sibling)
1688 clist->focus_row = MAX (pos - 1, 0);
1689 else
1690 clist->focus_row = pos;
1691
1692 clist->focus_row = MIN (clist->focus_row, clist->rows - 1);
1693 }
1694 clist->undo_anchor = clist->focus_row;
1695 }
1696 }
1697
1698 if (work)
1699 {
1700 list = (GList *)GTK_CMCTREE_NODE_PREV (work);
1701 list->next = NULL;
1702 list = (GList *)work;
1703 list->prev = (GList *)GTK_CMCTREE_NODE_PREV (node);
1704 }
1705
1706 if (GTK_CMCTREE_NODE_PREV (node) &&
1707 GTK_CMCTREE_NODE_NEXT (GTK_CMCTREE_NODE_PREV (node)) == node)
1708 {
1709 list = (GList *)GTK_CMCTREE_NODE_PREV (node);
1710 list->next = (GList *)work;
1711 }
1712
1713 /* update tree */
1714 parent = GTK_CMCTREE_ROW (node)->parent;
1715 if (parent)
1716 {
1717 if (GTK_CMCTREE_ROW (parent)->children == node)
1718 {
1719 GTK_CMCTREE_ROW (parent)->children = GTK_CMCTREE_ROW (node)->sibling;
1720 if (!GTK_CMCTREE_ROW (parent)->children)
1721 gtk_cmctree_collapse (ctree, parent);
1722 }
1723 else
1724 {
1725 GtkCMCTreeNode *sibling;
1726
1727 sibling = GTK_CMCTREE_ROW (parent)->children;
1728 while (GTK_CMCTREE_ROW (sibling)->sibling != node)
1729 sibling = GTK_CMCTREE_ROW (sibling)->sibling;
1730 GTK_CMCTREE_ROW (sibling)->sibling = GTK_CMCTREE_ROW (node)->sibling;
1731 }
1732 }
1733 else
1734 {
1735 if (clist->row_list == (GList *)node)
1736 clist->row_list = (GList *) (GTK_CMCTREE_ROW (node)->sibling);
1737 else
1738 {
1739 GtkCMCTreeNode *sibling;
1740
1741 sibling = GTK_CMCTREE_NODE (clist->row_list);
1742 while (GTK_CMCTREE_ROW (sibling)->sibling != node)
1743 sibling = GTK_CMCTREE_ROW (sibling)->sibling;
1744 GTK_CMCTREE_ROW (sibling)->sibling = GTK_CMCTREE_ROW (node)->sibling;
1745 }
1746 }
1747 }
1748
1749 static void
real_row_move(GtkCMCList * clist,gint source_row,gint dest_row)1750 real_row_move (GtkCMCList *clist,
1751 gint source_row,
1752 gint dest_row)
1753 {
1754 GtkCMCTree *ctree;
1755 GtkCMCTreeNode *node;
1756
1757 cm_return_if_fail (GTK_IS_CMCTREE (clist));
1758
1759 if (GTK_CMCLIST_AUTO_SORT (clist))
1760 return;
1761
1762 if (source_row < 0 || source_row >= clist->rows ||
1763 dest_row < 0 || dest_row >= clist->rows ||
1764 source_row == dest_row)
1765 return;
1766
1767 ctree = GTK_CMCTREE (clist);
1768 node = GTK_CMCTREE_NODE (g_list_nth (clist->row_list, source_row));
1769
1770 if (source_row < dest_row)
1771 {
1772 GtkCMCTreeNode *work;
1773
1774 dest_row++;
1775 work = GTK_CMCTREE_ROW (node)->children;
1776
1777 while (work && GTK_CMCTREE_ROW (work)->level > GTK_CMCTREE_ROW (node)->level)
1778 {
1779 work = GTK_CMCTREE_NODE_NEXT (work);
1780 dest_row++;
1781 }
1782
1783 if (dest_row > clist->rows)
1784 dest_row = clist->rows;
1785 }
1786
1787 if (dest_row < clist->rows)
1788 {
1789 GtkCMCTreeNode *sibling;
1790
1791 sibling = GTK_CMCTREE_NODE (g_list_nth (clist->row_list, dest_row));
1792 gtk_cmctree_move (ctree, node, GTK_CMCTREE_ROW (sibling)->parent, sibling);
1793 }
1794 else
1795 gtk_cmctree_move (ctree, node, NULL, NULL);
1796 }
1797
1798 static void
real_tree_move(GtkCMCTree * ctree,GtkCMCTreeNode * node,GtkCMCTreeNode * new_parent,GtkCMCTreeNode * new_sibling)1799 real_tree_move (GtkCMCTree *ctree,
1800 GtkCMCTreeNode *node,
1801 GtkCMCTreeNode *new_parent,
1802 GtkCMCTreeNode *new_sibling)
1803 {
1804 GtkCMCList *clist;
1805 GtkCMCTreeNode *work;
1806 gboolean visible = FALSE;
1807
1808 cm_return_if_fail (ctree != NULL);
1809 cm_return_if_fail (node != NULL);
1810 cm_return_if_fail (!new_sibling ||
1811 GTK_CMCTREE_ROW (new_sibling)->parent == new_parent);
1812
1813 if (new_parent && GTK_CMCTREE_ROW (new_parent)->is_leaf)
1814 return;
1815
1816 /* new_parent != child of child */
1817 for (work = new_parent; work; work = GTK_CMCTREE_ROW (work)->parent)
1818 if (work == node)
1819 return;
1820
1821 clist = GTK_CMCLIST (ctree);
1822
1823 visible = gtk_cmctree_is_viewable (ctree, node);
1824
1825 if (clist->selection_mode == GTK_SELECTION_MULTIPLE)
1826 {
1827 GTK_CMCLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
1828
1829 g_list_free (clist->undo_selection);
1830 g_list_free (clist->undo_unselection);
1831 clist->undo_selection = NULL;
1832 clist->undo_unselection = NULL;
1833 }
1834
1835 if (GTK_CMCLIST_AUTO_SORT (clist))
1836 {
1837 if (new_parent == GTK_CMCTREE_ROW (node)->parent)
1838 return;
1839
1840 if (new_parent)
1841 new_sibling = GTK_CMCTREE_ROW (new_parent)->children;
1842 else
1843 new_sibling = GTK_CMCTREE_NODE (clist->row_list);
1844
1845 while (new_sibling && clist->compare
1846 (clist, GTK_CMCTREE_ROW (node), GTK_CMCTREE_ROW (new_sibling)) > 0)
1847 new_sibling = GTK_CMCTREE_ROW (new_sibling)->sibling;
1848 }
1849
1850 if (new_parent == GTK_CMCTREE_ROW (node)->parent &&
1851 new_sibling == GTK_CMCTREE_ROW (node)->sibling)
1852 return;
1853
1854 gtk_cmclist_freeze (clist);
1855
1856 work = NULL;
1857 if (gtk_cmctree_is_viewable (ctree, node))
1858 work = GTK_CMCTREE_NODE (g_list_nth (clist->row_list, clist->focus_row));
1859
1860 gtk_cmctree_unlink (ctree, node, FALSE);
1861 gtk_cmctree_link (ctree, node, new_parent, new_sibling, FALSE);
1862
1863 if (work)
1864 {
1865 while (work && !gtk_cmctree_is_viewable (ctree, work))
1866 work = GTK_CMCTREE_ROW (work)->parent;
1867 clist->focus_row = g_list_position (clist->row_list, (GList *)work);
1868 clist->undo_anchor = clist->focus_row;
1869 }
1870
1871 if (clist->column[ctree->tree_column].auto_resize &&
1872 !GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist) &&
1873 (visible || gtk_cmctree_is_viewable (ctree, node)))
1874 gtk_cmclist_set_column_width
1875 (clist, ctree->tree_column,
1876 gtk_cmclist_optimal_column_width (clist, ctree->tree_column));
1877
1878 gtk_cmclist_thaw (clist);
1879 }
1880
1881 static void
change_focus_row_expansion(GtkCMCTree * ctree,GtkCMCTreeExpansionType action)1882 change_focus_row_expansion (GtkCMCTree *ctree,
1883 GtkCMCTreeExpansionType action)
1884 {
1885 GtkCMCList *clist;
1886 GtkCMCTreeNode *node;
1887
1888 cm_return_if_fail (GTK_IS_CMCTREE (ctree));
1889
1890 clist = GTK_CMCLIST (ctree);
1891
1892 if (gdk_display_pointer_is_grabbed (gtk_widget_get_display (GTK_WIDGET (ctree))) &&
1893 gtk_widget_has_grab (GTK_WIDGET(ctree)))
1894 return;
1895
1896 if (!(node =
1897 GTK_CMCTREE_NODE (g_list_nth (clist->row_list, clist->focus_row))) ||
1898 GTK_CMCTREE_ROW (node)->is_leaf || !(GTK_CMCTREE_ROW (node)->children))
1899 return;
1900
1901 switch (action)
1902 {
1903 case GTK_CMCTREE_EXPANSION_EXPAND:
1904 gtk_cmctree_expand (ctree, node);
1905 break;
1906 case GTK_CMCTREE_EXPANSION_EXPAND_RECURSIVE:
1907 gtk_cmctree_expand_recursive (ctree, node);
1908 break;
1909 case GTK_CMCTREE_EXPANSION_COLLAPSE:
1910 gtk_cmctree_collapse (ctree, node);
1911 break;
1912 case GTK_CMCTREE_EXPANSION_COLLAPSE_RECURSIVE:
1913 gtk_cmctree_collapse_recursive (ctree, node);
1914 break;
1915 case GTK_CMCTREE_EXPANSION_TOGGLE:
1916 gtk_cmctree_toggle_expansion (ctree, node);
1917 break;
1918 case GTK_CMCTREE_EXPANSION_TOGGLE_RECURSIVE:
1919 gtk_cmctree_toggle_expansion_recursive (ctree, node);
1920 break;
1921 }
1922 }
1923
1924 static void
real_tree_expand(GtkCMCTree * ctree,GtkCMCTreeNode * node)1925 real_tree_expand (GtkCMCTree *ctree,
1926 GtkCMCTreeNode *node)
1927 {
1928 GtkCMCList *clist;
1929 GtkCMCTreeNode *work;
1930 GtkRequisition requisition;
1931 gboolean visible;
1932
1933 cm_return_if_fail (GTK_IS_CMCTREE (ctree));
1934
1935 if (!node || GTK_CMCTREE_ROW (node)->expanded || GTK_CMCTREE_ROW (node)->is_leaf)
1936 return;
1937
1938 clist = GTK_CMCLIST (ctree);
1939
1940 GTK_CMCLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
1941
1942 GTK_CMCTREE_ROW (node)->expanded = TRUE;
1943
1944 visible = gtk_cmctree_is_viewable (ctree, node);
1945 /* get cell width if tree_column is auto resized */
1946 if (visible && clist->column[ctree->tree_column].auto_resize &&
1947 !GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist))
1948 GTK_CMCLIST_GET_CLASS (clist)->cell_size_request
1949 (clist, >K_CMCTREE_ROW (node)->row, ctree->tree_column, &requisition);
1950
1951 /* unref/unset closed pixbuf */
1952 if (GTK_CMCELL_PIXTEXT
1953 (GTK_CMCTREE_ROW (node)->row.cell[ctree->tree_column])->pixbuf)
1954 {
1955 g_object_unref
1956 (GTK_CMCELL_PIXTEXT
1957 (GTK_CMCTREE_ROW (node)->row.cell[ctree->tree_column])->pixbuf);
1958
1959 GTK_CMCELL_PIXTEXT
1960 (GTK_CMCTREE_ROW (node)->row.cell[ctree->tree_column])->pixbuf = NULL;
1961 }
1962
1963 /* set/ref opened pixbuf */
1964 if (GTK_CMCTREE_ROW (node)->pixbuf_opened)
1965 {
1966 GTK_CMCELL_PIXTEXT
1967 (GTK_CMCTREE_ROW (node)->row.cell[ctree->tree_column])->pixbuf =
1968 g_object_ref (GTK_CMCTREE_ROW (node)->pixbuf_opened);
1969 }
1970
1971
1972 work = GTK_CMCTREE_ROW (node)->children;
1973 if (work)
1974 {
1975 GList *list = (GList *)work;
1976 gint *cell_width = NULL;
1977 gint tmp = 0;
1978 gint row;
1979 gint i;
1980
1981 if (visible && !GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist))
1982 {
1983 cell_width = g_new0 (gint, clist->columns);
1984 if (clist->column[ctree->tree_column].auto_resize)
1985 cell_width[ctree->tree_column] = requisition.width;
1986
1987 while (work)
1988 {
1989 /* search maximum cell widths of auto_resize columns */
1990 for (i = 0; i < clist->columns; i++)
1991 if (clist->column[i].auto_resize)
1992 {
1993 GTK_CMCLIST_GET_CLASS (clist)->cell_size_request
1994 (clist, >K_CMCTREE_ROW (work)->row, i, &requisition);
1995 cell_width[i] = MAX (requisition.width, cell_width[i]);
1996 }
1997
1998 list = (GList *)work;
1999 work = GTK_CMCTREE_NODE_NEXT (work);
2000 tmp++;
2001 }
2002 }
2003 else
2004 while (work)
2005 {
2006 list = (GList *)work;
2007 work = GTK_CMCTREE_NODE_NEXT (work);
2008 tmp++;
2009 }
2010
2011 list->next = (GList *)GTK_CMCTREE_NODE_NEXT (node);
2012
2013 if (GTK_CMCTREE_NODE_NEXT (node))
2014 {
2015 GList *tmp_list;
2016
2017 tmp_list = (GList *)GTK_CMCTREE_NODE_NEXT (node);
2018 tmp_list->prev = list;
2019 }
2020 else
2021 clist->row_list_end = list;
2022
2023 list = (GList *)node;
2024 list->next = (GList *)(GTK_CMCTREE_ROW (node)->children);
2025
2026 if (visible && !GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist))
2027 {
2028 /* resize auto_resize columns if needed */
2029 for (i = 0; i < clist->columns; i++)
2030 if (clist->column[i].auto_resize &&
2031 cell_width[i] > clist->column[i].width)
2032 gtk_cmclist_set_column_width (clist, i, cell_width[i]);
2033 g_free (cell_width);
2034
2035 /* update focus_row position */
2036 row = g_list_position (clist->row_list, (GList *)node);
2037 if (row < clist->focus_row)
2038 clist->focus_row += tmp;
2039
2040 clist->rows += tmp;
2041 CLIST_REFRESH (clist);
2042 }
2043 }
2044 else if (visible && clist->column[ctree->tree_column].auto_resize)
2045 /* resize tree_column if needed */
2046 column_auto_resize (clist, >K_CMCTREE_ROW (node)->row, ctree->tree_column,
2047 requisition.width);
2048 }
2049
2050 static void
real_tree_collapse(GtkCMCTree * ctree,GtkCMCTreeNode * node)2051 real_tree_collapse (GtkCMCTree *ctree,
2052 GtkCMCTreeNode *node)
2053 {
2054 GtkCMCList *clist;
2055 GtkCMCTreeNode *work;
2056 GtkRequisition requisition;
2057 gboolean visible;
2058 gint level;
2059
2060 cm_return_if_fail (GTK_IS_CMCTREE (ctree));
2061
2062 if (!node || !GTK_CMCTREE_ROW (node)->expanded ||
2063 GTK_CMCTREE_ROW (node)->is_leaf)
2064 return;
2065
2066 clist = GTK_CMCLIST (ctree);
2067
2068 GTK_CMCLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
2069
2070 GTK_CMCTREE_ROW (node)->expanded = FALSE;
2071 level = GTK_CMCTREE_ROW (node)->level;
2072
2073 visible = gtk_cmctree_is_viewable (ctree, node);
2074 /* get cell width if tree_column is auto resized */
2075 if (visible && clist->column[ctree->tree_column].auto_resize &&
2076 !GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist))
2077 GTK_CMCLIST_GET_CLASS (clist)->cell_size_request
2078 (clist, >K_CMCTREE_ROW (node)->row, ctree->tree_column, &requisition);
2079
2080 /* unref/unset opened pixbuf */
2081 if (GTK_CMCELL_PIXTEXT
2082 (GTK_CMCTREE_ROW (node)->row.cell[ctree->tree_column])->pixbuf)
2083 {
2084 g_object_unref
2085 (GTK_CMCELL_PIXTEXT
2086 (GTK_CMCTREE_ROW (node)->row.cell[ctree->tree_column])->pixbuf);
2087
2088 GTK_CMCELL_PIXTEXT
2089 (GTK_CMCTREE_ROW (node)->row.cell[ctree->tree_column])->pixbuf = NULL;
2090 }
2091
2092 /* set/ref closed pixbuf */
2093 if (GTK_CMCTREE_ROW (node)->pixbuf_closed)
2094 {
2095 GTK_CMCELL_PIXTEXT
2096 (GTK_CMCTREE_ROW (node)->row.cell[ctree->tree_column])->pixbuf =
2097 g_object_ref (GTK_CMCTREE_ROW (node)->pixbuf_closed);
2098 }
2099
2100 work = GTK_CMCTREE_ROW (node)->children;
2101 if (work)
2102 {
2103 gint tmp = 0;
2104 gint row;
2105 GList *list;
2106
2107 while (work && GTK_CMCTREE_ROW (work)->level > level)
2108 {
2109 work = GTK_CMCTREE_NODE_NEXT (work);
2110 tmp++;
2111 }
2112
2113 if (work)
2114 {
2115 list = (GList *)node;
2116 list->next = (GList *)work;
2117 list = (GList *)GTK_CMCTREE_NODE_PREV (work);
2118 list->next = NULL;
2119 list = (GList *)work;
2120 list->prev = (GList *)node;
2121 }
2122 else
2123 {
2124 list = (GList *)node;
2125 list->next = NULL;
2126 clist->row_list_end = (GList *)node;
2127 }
2128
2129 if (visible)
2130 {
2131 /* resize auto_resize columns if needed */
2132 auto_resize_columns (clist);
2133
2134 row = g_list_position (clist->row_list, (GList *)node);
2135 if (row < clist->focus_row)
2136 clist->focus_row -= tmp;
2137 clist->rows -= tmp;
2138 CLIST_REFRESH (clist);
2139 }
2140 }
2141 else if (visible && clist->column[ctree->tree_column].auto_resize &&
2142 !GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist))
2143 /* resize tree_column if needed */
2144 column_auto_resize (clist, >K_CMCTREE_ROW (node)->row, ctree->tree_column,
2145 requisition.width);
2146
2147 }
2148
2149 static void
column_auto_resize(GtkCMCList * clist,GtkCMCListRow * clist_row,gint column,gint old_width)2150 column_auto_resize (GtkCMCList *clist,
2151 GtkCMCListRow *clist_row,
2152 gint column,
2153 gint old_width)
2154 {
2155 /* resize column if needed for auto_resize */
2156 GtkRequisition requisition;
2157
2158 if (!clist->column[column].auto_resize ||
2159 GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist))
2160 return;
2161
2162 if (clist_row)
2163 GTK_CMCLIST_GET_CLASS (clist)->cell_size_request (clist, clist_row,
2164 column, &requisition);
2165 else
2166 requisition.width = 0;
2167
2168 if (requisition.width > clist->column[column].width)
2169 gtk_cmclist_set_column_width (clist, column, requisition.width);
2170 else if (requisition.width < old_width &&
2171 old_width == clist->column[column].width)
2172 {
2173 GList *list;
2174 gint new_width;
2175
2176 /* run a "gtk_cmclist_optimal_column_width" but break, if
2177 * the column doesn't shrink */
2178 if (GTK_CMCLIST_SHOW_TITLES (clist) && clist->column[column].button)
2179 {
2180 GtkRequisition req;
2181 gtk_widget_get_requisition (clist->column[column].button, &req);
2182 new_width = (req.width -
2183 (CELL_SPACING + (2 * COLUMN_INSET)));
2184 }
2185 else
2186 new_width = 0;
2187
2188 for (list = clist->row_list; list; list = list->next)
2189 {
2190 GTK_CMCLIST_GET_CLASS (clist)->cell_size_request
2191 (clist, GTK_CMCLIST_ROW (list), column, &requisition);
2192 new_width = MAX (new_width, requisition.width);
2193 if (new_width == clist->column[column].width)
2194 break;
2195 }
2196 if (new_width < clist->column[column].width)
2197 gtk_cmclist_set_column_width (clist, column, new_width);
2198 }
2199 }
2200
2201 static void
auto_resize_columns(GtkCMCList * clist)2202 auto_resize_columns (GtkCMCList *clist)
2203 {
2204 gint i;
2205
2206 if (GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist))
2207 return;
2208
2209 for (i = 0; i < clist->columns; i++)
2210 column_auto_resize (clist, NULL, i, clist->column[i].width);
2211 }
2212
2213 static void
cell_size_request(GtkCMCList * clist,GtkCMCListRow * clist_row,gint column,GtkRequisition * requisition)2214 cell_size_request (GtkCMCList *clist,
2215 GtkCMCListRow *clist_row,
2216 gint column,
2217 GtkRequisition *requisition)
2218 {
2219 GtkCMCTree *ctree;
2220 gint width;
2221 gint height;
2222 PangoLayout *layout;
2223 PangoRectangle logical_rect;
2224
2225 cm_return_if_fail (GTK_IS_CMCTREE (clist));
2226 cm_return_if_fail (requisition != NULL);
2227
2228 ctree = GTK_CMCTREE (clist);
2229
2230 layout = create_cell_layout (clist, clist_row, column);
2231 if (layout)
2232 {
2233 pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
2234
2235 requisition->width = logical_rect.width;
2236 requisition->height = logical_rect.height;
2237
2238 g_object_unref (G_OBJECT (layout));
2239 }
2240 else
2241 {
2242 requisition->width = 0;
2243 requisition->height = 0;
2244 }
2245
2246 switch (clist_row->cell[column].type)
2247 {
2248 case GTK_CMCELL_PIXTEXT:
2249 if (GTK_CMCELL_PIXTEXT (clist_row->cell[column])->pixbuf)
2250 {
2251 width = gdk_pixbuf_get_width(GTK_CMCELL_PIXTEXT (clist_row->cell[column])->pixbuf);
2252 height = gdk_pixbuf_get_height(GTK_CMCELL_PIXTEXT (clist_row->cell[column])->pixbuf);
2253 width += GTK_CMCELL_PIXTEXT (clist_row->cell[column])->spacing;
2254 }
2255 else
2256 width = height = 0;
2257
2258 requisition->width += width;
2259 requisition->height = MAX (requisition->height, height);
2260
2261 if (column == ctree->tree_column)
2262 {
2263 requisition->width += (ctree->tree_spacing + ctree->tree_indent *
2264 (((GtkCMCTreeRow *) clist_row)->level - 1));
2265 switch (ctree->expander_style)
2266 {
2267 case GTK_CMCTREE_EXPANDER_NONE:
2268 break;
2269 case GTK_CMCTREE_EXPANDER_TRIANGLE:
2270 requisition->width += PM_SIZE + 3;
2271 break;
2272 }
2273 }
2274 break;
2275 case GTK_CMCELL_PIXBUF:
2276 width = gdk_pixbuf_get_width(GTK_CMCELL_PIXBUF (clist_row->cell[column])->pixbuf);
2277 height = gdk_pixbuf_get_height(GTK_CMCELL_PIXBUF (clist_row->cell[column])->pixbuf);
2278 requisition->width += width;
2279 requisition->height = MAX (requisition->height, height);
2280 break;
2281 default:
2282 break;
2283 }
2284
2285 requisition->width += clist_row->cell[column].horizontal;
2286 requisition->height += clist_row->cell[column].vertical;
2287 }
2288
2289 static void
set_cell_contents(GtkCMCList * clist,GtkCMCListRow * clist_row,gint column,GtkCMCellType type,const gchar * text,guint8 spacing,GdkPixbuf * pixbuf)2290 set_cell_contents (GtkCMCList *clist,
2291 GtkCMCListRow *clist_row,
2292 gint column,
2293 GtkCMCellType type,
2294 const gchar *text,
2295 guint8 spacing,
2296 GdkPixbuf *pixbuf)
2297 {
2298 gboolean visible = FALSE;
2299 GtkCMCTree *ctree;
2300 GtkRequisition requisition;
2301 gchar *old_text = NULL;
2302 GdkPixbuf *old_pixbuf = NULL;
2303
2304 cm_return_if_fail (GTK_IS_CMCTREE (clist));
2305 cm_return_if_fail (clist_row != NULL);
2306
2307 ctree = GTK_CMCTREE (clist);
2308
2309 if (clist->column[column].auto_resize &&
2310 !GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist))
2311 {
2312 GtkCMCTreeNode *parent;
2313
2314 parent = ((GtkCMCTreeRow *)clist_row)->parent;
2315 if ((parent && GTK_CMCTREE_ROW (parent)->expanded &&
2316 gtk_cmctree_is_viewable (ctree, parent)))
2317 {
2318 visible = TRUE;
2319 GTK_CMCLIST_GET_CLASS (clist)->cell_size_request (clist, clist_row,
2320 column, &requisition);
2321 }
2322 }
2323
2324 switch (clist_row->cell[column].type)
2325 {
2326 case GTK_CMCELL_EMPTY:
2327 break;
2328 case GTK_CMCELL_TEXT:
2329 old_text = GTK_CMCELL_TEXT (clist_row->cell[column])->text;
2330 break;
2331 case GTK_CMCELL_PIXBUF:
2332 old_pixbuf = GTK_CMCELL_PIXBUF (clist_row->cell[column])->pixbuf;
2333 break;
2334 case GTK_CMCELL_PIXTEXT:
2335 old_text = GTK_CMCELL_PIXTEXT (clist_row->cell[column])->text;
2336 old_pixbuf = GTK_CMCELL_PIXTEXT (clist_row->cell[column])->pixbuf;
2337 break;
2338 case GTK_CMCELL_WIDGET:
2339 /* unimplemented */
2340 break;
2341
2342 default:
2343 break;
2344 }
2345
2346 clist_row->cell[column].type = GTK_CMCELL_EMPTY;
2347 if (column == ctree->tree_column && type != GTK_CMCELL_EMPTY)
2348 type = GTK_CMCELL_PIXTEXT;
2349
2350 /* Note that pixbuf and mask were already ref'ed by the caller
2351 */
2352 switch (type)
2353 {
2354 case GTK_CMCELL_TEXT:
2355 if (text)
2356 {
2357 clist_row->cell[column].type = GTK_CMCELL_TEXT;
2358 GTK_CMCELL_TEXT (clist_row->cell[column])->text = g_strdup (text);
2359 }
2360 break;
2361 case GTK_CMCELL_PIXBUF:
2362 if (pixbuf)
2363 {
2364 clist_row->cell[column].type = GTK_CMCELL_PIXBUF;
2365 GTK_CMCELL_PIXBUF (clist_row->cell[column])->pixbuf = pixbuf;
2366 }
2367 break;
2368 case GTK_CMCELL_PIXTEXT:
2369 if (column == ctree->tree_column)
2370 {
2371 clist_row->cell[column].type = GTK_CMCELL_PIXTEXT;
2372 GTK_CMCELL_PIXTEXT (clist_row->cell[column])->spacing = spacing;
2373 if (text)
2374 GTK_CMCELL_PIXTEXT (clist_row->cell[column])->text = g_strdup (text);
2375 else
2376 GTK_CMCELL_PIXTEXT (clist_row->cell[column])->text = NULL;
2377 if (pixbuf)
2378 {
2379 GTK_CMCELL_PIXTEXT (clist_row->cell[column])->pixbuf = pixbuf;
2380 }
2381 else
2382 {
2383 GTK_CMCELL_PIXTEXT (clist_row->cell[column])->pixbuf = NULL;
2384 }
2385 }
2386 else if (text && pixbuf)
2387 {
2388 clist_row->cell[column].type = GTK_CMCELL_PIXTEXT;
2389 GTK_CMCELL_PIXTEXT (clist_row->cell[column])->text = g_strdup (text);
2390 GTK_CMCELL_PIXTEXT (clist_row->cell[column])->spacing = spacing;
2391 GTK_CMCELL_PIXTEXT (clist_row->cell[column])->pixbuf = pixbuf;
2392 }
2393 break;
2394 default:
2395 break;
2396 }
2397
2398 if (visible && clist->column[column].auto_resize &&
2399 !GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist))
2400 column_auto_resize (clist, clist_row, column, requisition.width);
2401
2402 g_free (old_text);
2403 if (old_pixbuf)
2404 g_object_unref (old_pixbuf);
2405 }
2406
2407 static void
set_node_info(GtkCMCTree * ctree,GtkCMCTreeNode * node,const gchar * text,guint8 spacing,GdkPixbuf * pixbuf_closed,GdkPixbuf * pixbuf_opened,gboolean is_leaf,gboolean expanded)2408 set_node_info (GtkCMCTree *ctree,
2409 GtkCMCTreeNode *node,
2410 const gchar *text,
2411 guint8 spacing,
2412 GdkPixbuf *pixbuf_closed,
2413 GdkPixbuf *pixbuf_opened,
2414 gboolean is_leaf,
2415 gboolean expanded)
2416 {
2417 if (GTK_CMCTREE_ROW (node)->pixbuf_opened)
2418 {
2419 g_object_unref (GTK_CMCTREE_ROW (node)->pixbuf_opened);
2420 }
2421 if (GTK_CMCTREE_ROW (node)->pixbuf_closed)
2422 {
2423 g_object_unref (GTK_CMCTREE_ROW (node)->pixbuf_closed);
2424 }
2425
2426 GTK_CMCTREE_ROW (node)->pixbuf_opened = NULL;
2427 GTK_CMCTREE_ROW (node)->pixbuf_closed = NULL;
2428
2429 if (pixbuf_closed)
2430 {
2431 GTK_CMCTREE_ROW (node)->pixbuf_closed = g_object_ref (pixbuf_closed);
2432 }
2433 if (pixbuf_opened)
2434 {
2435 GTK_CMCTREE_ROW (node)->pixbuf_opened = g_object_ref (pixbuf_opened);
2436 }
2437
2438 GTK_CMCTREE_ROW (node)->is_leaf = is_leaf;
2439 GTK_CMCTREE_ROW (node)->expanded = (is_leaf) ? FALSE : expanded;
2440
2441 if (GTK_CMCTREE_ROW (node)->expanded)
2442 gtk_cmctree_node_set_pixtext (ctree, node, ctree->tree_column,
2443 text, spacing, pixbuf_opened);
2444 else
2445 gtk_cmctree_node_set_pixtext (ctree, node, ctree->tree_column,
2446 text, spacing, pixbuf_closed);
2447 }
2448
2449 static void
tree_delete(GtkCMCTree * ctree,GtkCMCTreeNode * node,gpointer data)2450 tree_delete (GtkCMCTree *ctree,
2451 GtkCMCTreeNode *node,
2452 gpointer data)
2453 {
2454 tree_unselect (ctree, node, NULL);
2455 row_delete (ctree, GTK_CMCTREE_ROW (node));
2456 g_list_free_1 ((GList *)node);
2457 }
2458
2459 static void
tree_delete_row(GtkCMCTree * ctree,GtkCMCTreeNode * node,gpointer data)2460 tree_delete_row (GtkCMCTree *ctree,
2461 GtkCMCTreeNode *node,
2462 gpointer data)
2463 {
2464 row_delete (ctree, GTK_CMCTREE_ROW (node));
2465 g_list_free_1 ((GList *)node);
2466 }
2467
2468 static void
tree_update_level(GtkCMCTree * ctree,GtkCMCTreeNode * node,gpointer data)2469 tree_update_level (GtkCMCTree *ctree,
2470 GtkCMCTreeNode *node,
2471 gpointer data)
2472 {
2473 if (!node)
2474 return;
2475
2476 if (GTK_CMCTREE_ROW (node)->parent)
2477 GTK_CMCTREE_ROW (node)->level =
2478 GTK_CMCTREE_ROW (GTK_CMCTREE_ROW (node)->parent)->level + 1;
2479 else
2480 GTK_CMCTREE_ROW (node)->level = 1;
2481 }
2482
2483 static void
tree_select(GtkCMCTree * ctree,GtkCMCTreeNode * node,gpointer data)2484 tree_select (GtkCMCTree *ctree,
2485 GtkCMCTreeNode *node,
2486 gpointer data)
2487 {
2488 if (node && GTK_CMCTREE_ROW (node)->row.state != GTK_STATE_SELECTED &&
2489 GTK_CMCTREE_ROW (node)->row.selectable)
2490 g_signal_emit (G_OBJECT (ctree), ctree_signals[TREE_SELECT_ROW], 0,
2491 node, -1);
2492 }
2493
2494 static void
tree_unselect(GtkCMCTree * ctree,GtkCMCTreeNode * node,gpointer data)2495 tree_unselect (GtkCMCTree *ctree,
2496 GtkCMCTreeNode *node,
2497 gpointer data)
2498 {
2499 if (node && GTK_CMCTREE_ROW (node)->row.state == GTK_STATE_SELECTED)
2500 g_signal_emit (G_OBJECT (ctree), ctree_signals[TREE_UNSELECT_ROW], 0,
2501 node, -1);
2502 }
2503
2504 static void
tree_expand(GtkCMCTree * ctree,GtkCMCTreeNode * node,gpointer data)2505 tree_expand (GtkCMCTree *ctree,
2506 GtkCMCTreeNode *node,
2507 gpointer data)
2508 {
2509 if (node && !GTK_CMCTREE_ROW (node)->expanded)
2510 g_signal_emit (G_OBJECT (ctree), ctree_signals[TREE_EXPAND], 0,node);
2511 }
2512
2513 static void
tree_collapse(GtkCMCTree * ctree,GtkCMCTreeNode * node,gpointer data)2514 tree_collapse (GtkCMCTree *ctree,
2515 GtkCMCTreeNode *node,
2516 gpointer data)
2517 {
2518 if (node && GTK_CMCTREE_ROW (node)->expanded)
2519 g_signal_emit (G_OBJECT (ctree), ctree_signals[TREE_COLLAPSE], 0,node);
2520 }
2521
2522 static void
tree_collapse_to_depth(GtkCMCTree * ctree,GtkCMCTreeNode * node,gint depth)2523 tree_collapse_to_depth (GtkCMCTree *ctree,
2524 GtkCMCTreeNode *node,
2525 gint depth)
2526 {
2527 if (node && GTK_CMCTREE_ROW (node)->level == depth)
2528 gtk_cmctree_collapse_recursive (ctree, node);
2529 }
2530
2531 static void
tree_toggle_expansion(GtkCMCTree * ctree,GtkCMCTreeNode * node,gpointer data)2532 tree_toggle_expansion (GtkCMCTree *ctree,
2533 GtkCMCTreeNode *node,
2534 gpointer data)
2535 {
2536 if (!node)
2537 return;
2538
2539 if (GTK_CMCTREE_ROW (node)->expanded)
2540 g_signal_emit (G_OBJECT (ctree), ctree_signals[TREE_COLLAPSE], 0,node);
2541 else
2542 g_signal_emit (G_OBJECT (ctree), ctree_signals[TREE_EXPAND], 0,node);
2543 }
2544
2545 static GtkCMCTreeRow *
row_new(GtkCMCTree * ctree)2546 row_new (GtkCMCTree *ctree)
2547 {
2548 GtkCMCList *clist;
2549 GtkCMCTreeRow *ctree_row;
2550 int i;
2551
2552 clist = GTK_CMCLIST (ctree);
2553 ctree_row = g_slice_new (GtkCMCTreeRow);
2554 ctree_row->row.cell = g_slice_alloc (sizeof (GtkCMCell) * clist->columns);
2555
2556 for (i = 0; i < clist->columns; i++)
2557 {
2558 ctree_row->row.cell[i].type = GTK_CMCELL_EMPTY;
2559 ctree_row->row.cell[i].vertical = 0;
2560 ctree_row->row.cell[i].horizontal = 0;
2561 ctree_row->row.cell[i].style = NULL;
2562 }
2563 GTK_CMCELL_PIXTEXT (ctree_row->row.cell[ctree->tree_column])->text = NULL;
2564
2565 ctree_row->row.fg_set = FALSE;
2566 ctree_row->row.bg_set = FALSE;
2567 ctree_row->row.style = NULL;
2568 ctree_row->row.selectable = TRUE;
2569 ctree_row->row.state = GTK_STATE_NORMAL;
2570 ctree_row->row.data = NULL;
2571 ctree_row->row.destroy = NULL;
2572
2573 ctree_row->level = 0;
2574 ctree_row->expanded = FALSE;
2575 ctree_row->parent = NULL;
2576 ctree_row->sibling = NULL;
2577 ctree_row->children = NULL;
2578 ctree_row->pixbuf_closed = NULL;
2579 ctree_row->pixbuf_opened = NULL;
2580
2581 return ctree_row;
2582 }
2583
2584 static void
row_delete(GtkCMCTree * ctree,GtkCMCTreeRow * ctree_row)2585 row_delete (GtkCMCTree *ctree,
2586 GtkCMCTreeRow *ctree_row)
2587 {
2588 GtkCMCList *clist;
2589 gint i;
2590
2591 clist = GTK_CMCLIST (ctree);
2592
2593 for (i = 0; i < clist->columns; i++)
2594 {
2595 GTK_CMCLIST_GET_CLASS (clist)->set_cell_contents
2596 (clist, &(ctree_row->row), i, GTK_CMCELL_EMPTY, NULL, 0, NULL);
2597 if (ctree_row->row.cell[i].style)
2598 {
2599 if (gtk_widget_get_realized (GTK_WIDGET(ctree)))
2600 gtk_style_detach (ctree_row->row.cell[i].style);
2601 g_object_unref (ctree_row->row.cell[i].style);
2602 }
2603 }
2604
2605 if (ctree_row->row.style)
2606 {
2607 if (gtk_widget_get_realized (GTK_WIDGET(ctree)))
2608 gtk_style_detach (ctree_row->row.style);
2609 g_object_unref (ctree_row->row.style);
2610 }
2611
2612 if (ctree_row->pixbuf_closed)
2613 {
2614 g_object_unref (ctree_row->pixbuf_closed);
2615 }
2616
2617 if (ctree_row->pixbuf_opened)
2618 {
2619 g_object_unref (ctree_row->pixbuf_opened);
2620 }
2621
2622 if (ctree_row->row.destroy)
2623 {
2624 GDestroyNotify dnotify = ctree_row->row.destroy;
2625 gpointer ddata = ctree_row->row.data;
2626
2627 ctree_row->row.destroy = NULL;
2628 ctree_row->row.data = NULL;
2629
2630 dnotify (ddata);
2631 }
2632
2633 g_slice_free1 (sizeof (GtkCMCell) * clist->columns, ctree_row->row.cell);
2634 g_slice_free (GtkCMCTreeRow, ctree_row);
2635 }
2636
2637 static void
real_select_row(GtkCMCList * clist,gint row,gint column,GdkEvent * event)2638 real_select_row (GtkCMCList *clist,
2639 gint row,
2640 gint column,
2641 GdkEvent *event)
2642 {
2643 GList *node;
2644
2645 cm_return_if_fail (GTK_IS_CMCTREE (clist));
2646
2647 if ((node = g_list_nth (clist->row_list, row)) &&
2648 GTK_CMCTREE_ROW (node)->row.selectable)
2649 g_signal_emit (G_OBJECT (clist), ctree_signals[TREE_SELECT_ROW],0,
2650 node, column);
2651 }
2652
2653 static void
real_unselect_row(GtkCMCList * clist,gint row,gint column,GdkEvent * event)2654 real_unselect_row (GtkCMCList *clist,
2655 gint row,
2656 gint column,
2657 GdkEvent *event)
2658 {
2659 GList *node;
2660
2661 cm_return_if_fail (GTK_IS_CMCTREE (clist));
2662
2663 if ((node = g_list_nth (clist->row_list, row)))
2664 g_signal_emit (G_OBJECT (clist), ctree_signals[TREE_UNSELECT_ROW],0,
2665 node, column);
2666 }
2667
2668 static void
tree_draw_node(GtkCMCTree * ctree,GtkCMCTreeNode * node)2669 tree_draw_node (GtkCMCTree *ctree,
2670 GtkCMCTreeNode *node)
2671 {
2672 GtkCMCList *clist;
2673
2674 clist = GTK_CMCLIST (ctree);
2675
2676 if (CLIST_UNFROZEN (clist) && gtk_cmctree_is_viewable (ctree, node))
2677 {
2678 GtkCMCTreeNode *work;
2679 gint num = 0;
2680
2681 work = GTK_CMCTREE_NODE (clist->row_list);
2682 while (work && work != node)
2683 {
2684 work = GTK_CMCTREE_NODE_NEXT (work);
2685 num++;
2686 }
2687 if (work && gtk_cmclist_row_is_visible (clist, num) != GTK_VISIBILITY_NONE)
2688 GTK_CMCLIST_GET_CLASS(ctree)->draw_row
2689 (clist, NULL, num, GTK_CMCLIST_ROW ((GList *) node));
2690 }
2691 }
2692
2693 static void
real_tree_select(GtkCMCTree * ctree,GtkCMCTreeNode * node,gint column)2694 real_tree_select (GtkCMCTree *ctree,
2695 GtkCMCTreeNode *node,
2696 gint column)
2697 {
2698 GtkCMCList *clist;
2699 GList *list;
2700 GtkCMCTreeNode *sel_row;
2701 gboolean node_selected;
2702
2703 cm_return_if_fail (GTK_IS_CMCTREE (ctree));
2704
2705 if (!node || GTK_CMCTREE_ROW (node)->row.state == GTK_STATE_SELECTED ||
2706 !GTK_CMCTREE_ROW (node)->row.selectable)
2707 return;
2708
2709 clist = GTK_CMCLIST (ctree);
2710
2711 switch (clist->selection_mode)
2712 {
2713 case GTK_SELECTION_SINGLE:
2714 case GTK_SELECTION_BROWSE:
2715
2716 node_selected = FALSE;
2717 list = clist->selection;
2718
2719 while (list)
2720 {
2721 sel_row = list->data;
2722 list = list->next;
2723
2724 if (node == sel_row)
2725 node_selected = TRUE;
2726 else
2727 g_signal_emit (G_OBJECT (ctree),
2728 ctree_signals[TREE_UNSELECT_ROW], 0, sel_row, column);
2729 }
2730
2731 if (node_selected)
2732 return;
2733
2734 default:
2735 break;
2736 }
2737
2738 GTK_CMCTREE_ROW (node)->row.state = GTK_STATE_SELECTED;
2739
2740 if (!clist->selection)
2741 {
2742 clist->selection = g_list_append (clist->selection, node);
2743 clist->selection_end = clist->selection;
2744 }
2745 else
2746 clist->selection_end = g_list_append (clist->selection_end, node)->next;
2747
2748 tree_draw_node (ctree, node);
2749 }
2750
2751 static void
real_tree_unselect(GtkCMCTree * ctree,GtkCMCTreeNode * node,gint column)2752 real_tree_unselect (GtkCMCTree *ctree,
2753 GtkCMCTreeNode *node,
2754 gint column)
2755 {
2756 GtkCMCList *clist;
2757
2758 cm_return_if_fail (GTK_IS_CMCTREE (ctree));
2759
2760 if (!node || GTK_CMCTREE_ROW (node)->row.state != GTK_STATE_SELECTED)
2761 return;
2762
2763 clist = GTK_CMCLIST (ctree);
2764
2765 if (clist->selection_end && clist->selection_end->data == node)
2766 clist->selection_end = clist->selection_end->prev;
2767
2768 clist->selection = g_list_remove (clist->selection, node);
2769
2770 GTK_CMCTREE_ROW (node)->row.state = GTK_STATE_NORMAL;
2771
2772 tree_draw_node (ctree, node);
2773 }
2774
2775 static void
select_row_recursive(GtkCMCTree * ctree,GtkCMCTreeNode * node,gpointer data)2776 select_row_recursive (GtkCMCTree *ctree,
2777 GtkCMCTreeNode *node,
2778 gpointer data)
2779 {
2780 if (!node || GTK_CMCTREE_ROW (node)->row.state == GTK_STATE_SELECTED ||
2781 !GTK_CMCTREE_ROW (node)->row.selectable)
2782 return;
2783
2784 GTK_CMCLIST (ctree)->undo_unselection =
2785 g_list_prepend (GTK_CMCLIST (ctree)->undo_unselection, node);
2786 gtk_cmctree_select (ctree, node);
2787 }
2788
2789 static void
real_select_all(GtkCMCList * clist)2790 real_select_all (GtkCMCList *clist)
2791 {
2792 GtkCMCTree *ctree;
2793 GtkCMCTreeNode *node;
2794
2795 cm_return_if_fail (GTK_IS_CMCTREE (clist));
2796
2797 ctree = GTK_CMCTREE (clist);
2798
2799 switch (clist->selection_mode)
2800 {
2801 case GTK_SELECTION_SINGLE:
2802 case GTK_SELECTION_BROWSE:
2803 return;
2804
2805 case GTK_SELECTION_MULTIPLE:
2806
2807 gtk_cmclist_freeze (clist);
2808
2809 g_list_free (clist->undo_selection);
2810 g_list_free (clist->undo_unselection);
2811 clist->undo_selection = NULL;
2812 clist->undo_unselection = NULL;
2813
2814 clist->anchor_state = GTK_STATE_SELECTED;
2815 clist->anchor = -1;
2816 clist->drag_pos = -1;
2817 clist->undo_anchor = clist->focus_row;
2818
2819 for (node = GTK_CMCTREE_NODE (clist->row_list); node;
2820 node = GTK_CMCTREE_NODE_NEXT (node))
2821 gtk_cmctree_pre_recursive (ctree, node, select_row_recursive, NULL);
2822
2823 gtk_cmclist_thaw (clist);
2824 break;
2825
2826 default:
2827 /* do nothing */
2828 break;
2829 }
2830 }
2831
2832 static void
real_unselect_all(GtkCMCList * clist)2833 real_unselect_all (GtkCMCList *clist)
2834 {
2835 GtkCMCTree *ctree;
2836 GtkCMCTreeNode *node;
2837 GList *list;
2838
2839 cm_return_if_fail (GTK_IS_CMCTREE (clist));
2840
2841 ctree = GTK_CMCTREE (clist);
2842
2843 switch (clist->selection_mode)
2844 {
2845 case GTK_SELECTION_BROWSE:
2846 if (clist->focus_row >= 0)
2847 {
2848 gtk_cmctree_select
2849 (ctree,
2850 GTK_CMCTREE_NODE (g_list_nth (clist->row_list, clist->focus_row)));
2851 return;
2852 }
2853 break;
2854
2855 case GTK_SELECTION_MULTIPLE:
2856 g_list_free (clist->undo_selection);
2857 g_list_free (clist->undo_unselection);
2858 clist->undo_selection = NULL;
2859 clist->undo_unselection = NULL;
2860
2861 clist->anchor = -1;
2862 clist->drag_pos = -1;
2863 clist->undo_anchor = clist->focus_row;
2864 break;
2865
2866 default:
2867 break;
2868 }
2869
2870 list = clist->selection;
2871
2872 while (list)
2873 {
2874 node = list->data;
2875 list = list->next;
2876 gtk_cmctree_unselect (ctree, node);
2877 }
2878 }
2879
2880 static gboolean
ctree_is_hot_spot(GtkCMCTree * ctree,GtkCMCTreeNode * node,gint row,gint x,gint y)2881 ctree_is_hot_spot (GtkCMCTree *ctree,
2882 GtkCMCTreeNode *node,
2883 gint row,
2884 gint x,
2885 gint y)
2886 {
2887 GtkCMCTreeRow *tree_row;
2888 GtkCMCList *clist;
2889 gint xl;
2890 gint yu;
2891 gint hotspot_size;
2892
2893 cm_return_val_if_fail (GTK_IS_CMCTREE (ctree), FALSE);
2894 cm_return_val_if_fail (node != NULL, FALSE);
2895
2896 clist = GTK_CMCLIST (ctree);
2897
2898 if (!clist->column[ctree->tree_column].visible ||
2899 ctree->expander_style == GTK_CMCTREE_EXPANDER_NONE)
2900 return FALSE;
2901
2902 tree_row = GTK_CMCTREE_ROW (node);
2903
2904 hotspot_size = clist->row_height-2;
2905 if (hotspot_size > clist->column[ctree->tree_column].area.width - 2)
2906 hotspot_size = clist->column[ctree->tree_column].area.width - 2;
2907
2908 yu = (ROW_TOP_YPIXEL (clist, row) + (clist->row_height - hotspot_size) / 2 -
2909 (clist->row_height - 1) % 2);
2910
2911 if (clist->column[ctree->tree_column].justification == GTK_JUSTIFY_RIGHT)
2912 xl = (clist->column[ctree->tree_column].area.x +
2913 clist->column[ctree->tree_column].area.width - 1 + clist->hoffset -
2914 (tree_row->level - 1) * ctree->tree_indent - hotspot_size);
2915 else
2916 xl = (clist->column[ctree->tree_column].area.x + clist->hoffset +
2917 (tree_row->level - 1) * ctree->tree_indent);
2918
2919 return (x >= xl && x <= xl + hotspot_size && y >= yu && y <= yu + hotspot_size);
2920 }
2921
2922 /***********************************************************
2923 ***********************************************************
2924 *** Public interface ***
2925 ***********************************************************
2926 ***********************************************************/
2927
2928
2929 /***********************************************************
2930 * Creation, insertion, deletion *
2931 ***********************************************************/
2932
2933 static GObject*
gtk_cmctree_constructor(GType type,guint n_construct_properties,GObjectConstructParam * construct_properties)2934 gtk_cmctree_constructor (GType type,
2935 guint n_construct_properties,
2936 GObjectConstructParam *construct_properties)
2937 {
2938 GObject *object = G_OBJECT_CLASS (parent_class)->constructor (type,
2939 n_construct_properties,
2940 construct_properties);
2941
2942 return object;
2943 }
2944
2945 GtkWidget*
gtk_cmctree_new_with_titles(gint columns,gint tree_column,gchar * titles[])2946 gtk_cmctree_new_with_titles (gint columns,
2947 gint tree_column,
2948 gchar *titles[])
2949 {
2950 GtkWidget *widget;
2951
2952 cm_return_val_if_fail (columns > 0, NULL);
2953 cm_return_val_if_fail (tree_column >= 0 && tree_column < columns, NULL);
2954
2955 widget = gtk_widget_new (GTK_TYPE_CMCTREE,
2956 "n_columns", columns,
2957 "tree_column", tree_column,
2958 NULL);
2959 if (titles)
2960 {
2961 GtkCMCList *clist = GTK_CMCLIST (widget);
2962 guint i;
2963
2964 for (i = 0; i < columns; i++)
2965 gtk_cmclist_set_column_title (clist, i, titles[i]);
2966 gtk_cmclist_column_titles_show (clist);
2967 }
2968
2969 return widget;
2970 }
2971
2972 GtkWidget *
gtk_cmctree_new(gint columns,gint tree_column)2973 gtk_cmctree_new (gint columns,
2974 gint tree_column)
2975 {
2976 return gtk_cmctree_new_with_titles (columns, tree_column, NULL);
2977 }
2978
2979 static gint
real_insert_row(GtkCMCList * clist,gint row,gchar * text[])2980 real_insert_row (GtkCMCList *clist,
2981 gint row,
2982 gchar *text[])
2983 {
2984 GtkCMCTreeNode *parent = NULL;
2985 GtkCMCTreeNode *sibling;
2986 GtkCMCTreeNode *node;
2987
2988 cm_return_val_if_fail (GTK_IS_CMCTREE (clist), -1);
2989
2990 sibling = GTK_CMCTREE_NODE (g_list_nth (clist->row_list, row));
2991 if (sibling)
2992 parent = GTK_CMCTREE_ROW (sibling)->parent;
2993
2994 node = gtk_cmctree_insert_node (GTK_CMCTREE (clist), parent, sibling, text, 5,
2995 NULL, NULL, TRUE, FALSE);
2996
2997 if (GTK_CMCLIST_AUTO_SORT (clist) || !sibling)
2998 return g_list_position (clist->row_list, (GList *) node);
2999
3000 return row;
3001 }
3002
3003 GtkCMCTreeNode *
gtk_cmctree_insert_node(GtkCMCTree * ctree,GtkCMCTreeNode * parent,GtkCMCTreeNode * sibling,gchar * text[],guint8 spacing,GdkPixbuf * pixbuf_closed,GdkPixbuf * pixbuf_opened,gboolean is_leaf,gboolean expanded)3004 gtk_cmctree_insert_node (GtkCMCTree *ctree,
3005 GtkCMCTreeNode *parent,
3006 GtkCMCTreeNode *sibling,
3007 gchar *text[],
3008 guint8 spacing,
3009 GdkPixbuf *pixbuf_closed,
3010 GdkPixbuf *pixbuf_opened,
3011 gboolean is_leaf,
3012 gboolean expanded)
3013 {
3014 GtkCMCList *clist;
3015 GtkCMCTreeRow *new_row;
3016 GtkCMCTreeNode *node;
3017 GList *list;
3018 gint i;
3019
3020 cm_return_val_if_fail (GTK_IS_CMCTREE (ctree), NULL);
3021 if (sibling)
3022 cm_return_val_if_fail (GTK_CMCTREE_ROW (sibling)->parent == parent, NULL);
3023
3024 if (parent && GTK_CMCTREE_ROW (parent)->is_leaf)
3025 return NULL;
3026
3027 clist = GTK_CMCLIST (ctree);
3028
3029 /* create the row */
3030 new_row = row_new (ctree);
3031 list = g_list_alloc ();
3032 list->data = new_row;
3033 node = GTK_CMCTREE_NODE (list);
3034
3035 if (text)
3036 for (i = 0; i < clist->columns; i++)
3037 if (text[i] && i != ctree->tree_column)
3038 GTK_CMCLIST_GET_CLASS (clist)->set_cell_contents
3039 (clist, &(new_row->row), i, GTK_CMCELL_TEXT, text[i], 0, NULL);
3040
3041 set_node_info (ctree, node, text ?
3042 text[ctree->tree_column] : NULL, spacing, pixbuf_closed,
3043 pixbuf_opened, is_leaf, expanded);
3044
3045 /* sorted insertion */
3046 if (GTK_CMCLIST_AUTO_SORT (clist))
3047 {
3048 if (parent)
3049 sibling = GTK_CMCTREE_ROW (parent)->children;
3050 else
3051 sibling = GTK_CMCTREE_NODE (clist->row_list);
3052
3053 while (sibling && clist->compare
3054 (clist, GTK_CMCTREE_ROW (node), GTK_CMCTREE_ROW (sibling)) > 0)
3055 sibling = GTK_CMCTREE_ROW (sibling)->sibling;
3056 }
3057
3058 gtk_cmctree_link (ctree, node, parent, sibling, TRUE);
3059
3060 if (text && !GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist) &&
3061 gtk_cmctree_is_viewable (ctree, node))
3062 {
3063 for (i = 0; i < clist->columns; i++)
3064 if (clist->column[i].auto_resize)
3065 column_auto_resize (clist, &(new_row->row), i, 0);
3066 }
3067
3068 if (clist->rows == 1)
3069 {
3070 clist->focus_row = 0;
3071 if (clist->selection_mode == GTK_SELECTION_BROWSE)
3072 gtk_cmctree_select (ctree, node);
3073 }
3074
3075
3076 CLIST_REFRESH (clist);
3077
3078 return node;
3079 }
3080
3081 GtkCMCTreeNode *
gtk_cmctree_insert_gnode(GtkCMCTree * ctree,GtkCMCTreeNode * parent,GtkCMCTreeNode * sibling,GNode * gnode,GtkCMCTreeGNodeFunc func,gpointer data)3082 gtk_cmctree_insert_gnode (GtkCMCTree *ctree,
3083 GtkCMCTreeNode *parent,
3084 GtkCMCTreeNode *sibling,
3085 GNode *gnode,
3086 GtkCMCTreeGNodeFunc func,
3087 gpointer data)
3088 {
3089 GtkCMCList *clist;
3090 GtkCMCTreeNode *cnode = NULL;
3091 GtkCMCTreeNode *child = NULL;
3092 GtkCMCTreeNode *new_child;
3093 GList *list;
3094 GNode *work;
3095 guint depth = 1;
3096
3097 cm_return_val_if_fail (GTK_IS_CMCTREE (ctree), NULL);
3098 cm_return_val_if_fail (gnode != NULL, NULL);
3099 cm_return_val_if_fail (func != NULL, NULL);
3100 if (sibling)
3101 cm_return_val_if_fail (GTK_CMCTREE_ROW (sibling)->parent == parent, NULL);
3102
3103 clist = GTK_CMCLIST (ctree);
3104
3105 if (parent)
3106 depth = GTK_CMCTREE_ROW (parent)->level + 1;
3107
3108 list = g_list_alloc ();
3109 list->data = row_new (ctree);
3110 cnode = GTK_CMCTREE_NODE (list);
3111
3112 gtk_cmclist_freeze (clist);
3113
3114 set_node_info (ctree, cnode, "", 0, NULL, NULL, TRUE, FALSE);
3115
3116 if (!func (ctree, depth, gnode, cnode, data))
3117 {
3118 tree_delete_row (ctree, cnode, NULL);
3119 gtk_cmclist_thaw (clist);
3120 return NULL;
3121 }
3122
3123 if (GTK_CMCLIST_AUTO_SORT (clist))
3124 {
3125 if (parent)
3126 sibling = GTK_CMCTREE_ROW (parent)->children;
3127 else
3128 sibling = GTK_CMCTREE_NODE (clist->row_list);
3129
3130 while (sibling && clist->compare
3131 (clist, GTK_CMCTREE_ROW (cnode), GTK_CMCTREE_ROW (sibling)) > 0)
3132 sibling = GTK_CMCTREE_ROW (sibling)->sibling;
3133 }
3134
3135 gtk_cmctree_link (ctree, cnode, parent, sibling, TRUE);
3136
3137 for (work = g_node_last_child (gnode); work; work = work->prev)
3138 {
3139 new_child = gtk_cmctree_insert_gnode (ctree, cnode, child,
3140 work, func, data);
3141 if (new_child)
3142 child = new_child;
3143 }
3144
3145 gtk_cmclist_thaw (clist);
3146
3147 return cnode;
3148 }
3149
3150 GNode *
gtk_cmctree_export_to_gnode(GtkCMCTree * ctree,GNode * parent,GNode * sibling,GtkCMCTreeNode * node,GtkCMCTreeGNodeFunc func,gpointer data)3151 gtk_cmctree_export_to_gnode (GtkCMCTree *ctree,
3152 GNode *parent,
3153 GNode *sibling,
3154 GtkCMCTreeNode *node,
3155 GtkCMCTreeGNodeFunc func,
3156 gpointer data)
3157 {
3158 GtkCMCTreeNode *work;
3159 GNode *gnode;
3160 gint depth;
3161
3162 cm_return_val_if_fail (GTK_IS_CMCTREE (ctree), NULL);
3163 cm_return_val_if_fail (node != NULL, NULL);
3164 cm_return_val_if_fail (func != NULL, NULL);
3165 if (sibling)
3166 {
3167 cm_return_val_if_fail (parent != NULL, NULL);
3168 cm_return_val_if_fail (sibling->parent == parent, NULL);
3169 }
3170
3171 gnode = g_node_new (NULL);
3172 depth = g_node_depth (parent) + 1;
3173
3174 if (!func (ctree, depth, gnode, node, data))
3175 {
3176 g_node_destroy (gnode);
3177 return NULL;
3178 }
3179
3180 if (parent)
3181 g_node_insert_before (parent, sibling, gnode);
3182
3183 if (!GTK_CMCTREE_ROW (node)->is_leaf)
3184 {
3185 GNode *new_sibling = NULL;
3186
3187 for (work = GTK_CMCTREE_ROW (node)->children; work;
3188 work = GTK_CMCTREE_ROW (work)->sibling)
3189 new_sibling = gtk_cmctree_export_to_gnode (ctree, gnode, new_sibling,
3190 work, func, data);
3191
3192 g_node_reverse_children (gnode);
3193 }
3194
3195 return gnode;
3196 }
3197
3198 static void
real_remove_row(GtkCMCList * clist,gint row)3199 real_remove_row (GtkCMCList *clist,
3200 gint row)
3201 {
3202 GtkCMCTreeNode *node;
3203
3204 cm_return_if_fail (GTK_IS_CMCTREE (clist));
3205
3206 node = GTK_CMCTREE_NODE (g_list_nth (clist->row_list, row));
3207
3208 if (node)
3209 gtk_cmctree_remove_node (GTK_CMCTREE (clist), node);
3210 }
3211
3212 void
gtk_cmctree_remove_node(GtkCMCTree * ctree,GtkCMCTreeNode * node)3213 gtk_cmctree_remove_node (GtkCMCTree *ctree,
3214 GtkCMCTreeNode *node)
3215 {
3216 GtkCMCList *clist;
3217
3218 cm_return_if_fail (GTK_IS_CMCTREE (ctree));
3219
3220 clist = GTK_CMCLIST (ctree);
3221
3222 gtk_cmclist_freeze (clist);
3223
3224 if (node)
3225 {
3226 gtk_cmctree_unlink (ctree, node, TRUE);
3227 gtk_cmctree_post_recursive (ctree, node, GTK_CMCTREE_FUNC (tree_delete),
3228 NULL);
3229 if (clist->selection_mode == GTK_SELECTION_BROWSE && !clist->selection &&
3230 clist->focus_row >= 0)
3231 gtk_cmclist_select_row (clist, clist->focus_row, -1);
3232
3233 auto_resize_columns (clist);
3234 }
3235 else
3236 gtk_cmclist_clear (clist);
3237
3238 gtk_cmclist_thaw (clist);
3239 }
3240
3241 static void
real_clear(GtkCMCList * clist)3242 real_clear (GtkCMCList *clist)
3243 {
3244 GtkCMCTree *ctree;
3245 GtkCMCTreeNode *work;
3246 GtkCMCTreeNode *ptr;
3247
3248 cm_return_if_fail (GTK_IS_CMCTREE (clist));
3249
3250 ctree = GTK_CMCTREE (clist);
3251
3252 /* remove all rows */
3253 work = GTK_CMCTREE_NODE (clist->row_list);
3254 clist->row_list = NULL;
3255 clist->row_list_end = NULL;
3256
3257 GTK_CMCLIST_SET_FLAG (clist, CMCLIST_AUTO_RESIZE_BLOCKED);
3258 while (work)
3259 {
3260 ptr = work;
3261 work = GTK_CMCTREE_ROW (work)->sibling;
3262 gtk_cmctree_post_recursive (ctree, ptr, GTK_CMCTREE_FUNC (tree_delete_row),
3263 NULL);
3264 }
3265 GTK_CMCLIST_UNSET_FLAG (clist, CMCLIST_AUTO_RESIZE_BLOCKED);
3266
3267 parent_class->clear (clist);
3268 }
3269
3270
3271 /***********************************************************
3272 * Generic recursive functions, querying / finding tree *
3273 * information *
3274 ***********************************************************/
3275
3276
3277 void
gtk_cmctree_post_recursive(GtkCMCTree * ctree,GtkCMCTreeNode * node,GtkCMCTreeFunc func,gpointer data)3278 gtk_cmctree_post_recursive (GtkCMCTree *ctree,
3279 GtkCMCTreeNode *node,
3280 GtkCMCTreeFunc func,
3281 gpointer data)
3282 {
3283 GtkCMCTreeNode *work;
3284 GtkCMCTreeNode *tmp;
3285
3286 cm_return_if_fail (GTK_IS_CMCTREE (ctree));
3287 cm_return_if_fail (func != NULL);
3288
3289 if (node)
3290 work = GTK_CMCTREE_ROW (node)->children;
3291 else
3292 work = GTK_CMCTREE_NODE (GTK_CMCLIST (ctree)->row_list);
3293
3294 while (work)
3295 {
3296 tmp = GTK_CMCTREE_ROW (work)->sibling;
3297 gtk_cmctree_post_recursive (ctree, work, func, data);
3298 work = tmp;
3299 }
3300
3301 if (node)
3302 func (ctree, node, data);
3303 }
3304
3305 void
gtk_cmctree_post_recursive_to_depth(GtkCMCTree * ctree,GtkCMCTreeNode * node,gint depth,GtkCMCTreeFunc func,gpointer data)3306 gtk_cmctree_post_recursive_to_depth (GtkCMCTree *ctree,
3307 GtkCMCTreeNode *node,
3308 gint depth,
3309 GtkCMCTreeFunc func,
3310 gpointer data)
3311 {
3312 GtkCMCTreeNode *work;
3313 GtkCMCTreeNode *tmp;
3314
3315 cm_return_if_fail (GTK_IS_CMCTREE (ctree));
3316 cm_return_if_fail (func != NULL);
3317
3318 if (depth < 0)
3319 {
3320 gtk_cmctree_post_recursive (ctree, node, func, data);
3321 return;
3322 }
3323
3324 if (node)
3325 work = GTK_CMCTREE_ROW (node)->children;
3326 else
3327 work = GTK_CMCTREE_NODE (GTK_CMCLIST (ctree)->row_list);
3328
3329 if (work && GTK_CMCTREE_ROW (work)->level <= depth)
3330 {
3331 while (work)
3332 {
3333 tmp = GTK_CMCTREE_ROW (work)->sibling;
3334 gtk_cmctree_post_recursive_to_depth (ctree, work, depth, func, data);
3335 work = tmp;
3336 }
3337 }
3338
3339 if (node && GTK_CMCTREE_ROW (node)->level <= depth)
3340 func (ctree, node, data);
3341 }
3342
3343 void
gtk_cmctree_pre_recursive(GtkCMCTree * ctree,GtkCMCTreeNode * node,GtkCMCTreeFunc func,gpointer data)3344 gtk_cmctree_pre_recursive (GtkCMCTree *ctree,
3345 GtkCMCTreeNode *node,
3346 GtkCMCTreeFunc func,
3347 gpointer data)
3348 {
3349 GtkCMCTreeNode *work;
3350 GtkCMCTreeNode *tmp;
3351
3352 cm_return_if_fail (GTK_IS_CMCTREE (ctree));
3353 cm_return_if_fail (func != NULL);
3354
3355 if (node)
3356 {
3357 work = GTK_CMCTREE_ROW (node)->children;
3358 func (ctree, node, data);
3359 }
3360 else
3361 work = GTK_CMCTREE_NODE (GTK_CMCLIST (ctree)->row_list);
3362
3363 while (work)
3364 {
3365 tmp = GTK_CMCTREE_ROW (work)->sibling;
3366 gtk_cmctree_pre_recursive (ctree, work, func, data);
3367 work = tmp;
3368 }
3369 }
3370
3371 void
gtk_cmctree_pre_recursive_to_depth(GtkCMCTree * ctree,GtkCMCTreeNode * node,gint depth,GtkCMCTreeFunc func,gpointer data)3372 gtk_cmctree_pre_recursive_to_depth (GtkCMCTree *ctree,
3373 GtkCMCTreeNode *node,
3374 gint depth,
3375 GtkCMCTreeFunc func,
3376 gpointer data)
3377 {
3378 GtkCMCTreeNode *work;
3379 GtkCMCTreeNode *tmp;
3380
3381 cm_return_if_fail (GTK_IS_CMCTREE (ctree));
3382 cm_return_if_fail (func != NULL);
3383
3384 if (depth < 0)
3385 {
3386 gtk_cmctree_pre_recursive (ctree, node, func, data);
3387 return;
3388 }
3389
3390 if (node)
3391 {
3392 work = GTK_CMCTREE_ROW (node)->children;
3393 if (GTK_CMCTREE_ROW (node)->level <= depth)
3394 func (ctree, node, data);
3395 }
3396 else
3397 work = GTK_CMCTREE_NODE (GTK_CMCLIST (ctree)->row_list);
3398
3399 if (work && GTK_CMCTREE_ROW (work)->level <= depth)
3400 {
3401 while (work)
3402 {
3403 tmp = GTK_CMCTREE_ROW (work)->sibling;
3404 gtk_cmctree_pre_recursive_to_depth (ctree, work, depth, func, data);
3405 work = tmp;
3406 }
3407 }
3408 }
3409
3410 gboolean
gtk_cmctree_is_viewable(GtkCMCTree * ctree,GtkCMCTreeNode * node)3411 gtk_cmctree_is_viewable (GtkCMCTree *ctree,
3412 GtkCMCTreeNode *node)
3413 {
3414 GtkCMCTreeRow *work;
3415
3416 cm_return_val_if_fail (GTK_IS_CMCTREE (ctree), FALSE);
3417 cm_return_val_if_fail (node != NULL, FALSE);
3418
3419 work = GTK_CMCTREE_ROW (node);
3420
3421 while (work && work->parent && GTK_CMCTREE_ROW (work->parent)->expanded)
3422 work = GTK_CMCTREE_ROW (work->parent);
3423
3424 if (!work->parent)
3425 return TRUE;
3426
3427 return FALSE;
3428 }
3429
3430 GtkCMCTreeNode *
gtk_cmctree_last(GtkCMCTree * ctree,GtkCMCTreeNode * node)3431 gtk_cmctree_last (GtkCMCTree *ctree,
3432 GtkCMCTreeNode *node)
3433 {
3434 cm_return_val_if_fail (GTK_IS_CMCTREE (ctree), NULL);
3435
3436 if (!node)
3437 return NULL;
3438
3439 while (GTK_CMCTREE_ROW (node)->sibling)
3440 node = GTK_CMCTREE_ROW (node)->sibling;
3441
3442 if (GTK_CMCTREE_ROW (node)->children)
3443 return gtk_cmctree_last (ctree, GTK_CMCTREE_ROW (node)->children);
3444
3445 return node;
3446 }
3447
3448 GtkCMCTreeNode *
gtk_cmctree_find_node_ptr(GtkCMCTree * ctree,GtkCMCTreeRow * ctree_row)3449 gtk_cmctree_find_node_ptr (GtkCMCTree *ctree,
3450 GtkCMCTreeRow *ctree_row)
3451 {
3452 GtkCMCTreeNode *node;
3453
3454 cm_return_val_if_fail (GTK_IS_CMCTREE (ctree), NULL);
3455 cm_return_val_if_fail (ctree_row != NULL, NULL);
3456
3457 if (ctree_row->parent)
3458 node = GTK_CMCTREE_ROW (ctree_row->parent)->children;
3459 else
3460 node = GTK_CMCTREE_NODE (GTK_CMCLIST (ctree)->row_list);
3461
3462 while (GTK_CMCTREE_ROW (node) != ctree_row)
3463 node = GTK_CMCTREE_ROW (node)->sibling;
3464
3465 return node;
3466 }
3467
3468 GtkCMCTreeNode *
gtk_cmctree_node_nth(GtkCMCTree * ctree,guint row)3469 gtk_cmctree_node_nth (GtkCMCTree *ctree,
3470 guint row)
3471 {
3472 cm_return_val_if_fail (GTK_IS_CMCTREE (ctree), NULL);
3473
3474 if ((row >= GTK_CMCLIST(ctree)->rows))
3475 return NULL;
3476
3477 return GTK_CMCTREE_NODE (g_list_nth (GTK_CMCLIST (ctree)->row_list, row));
3478 }
3479
3480 gboolean
gtk_cmctree_find(GtkCMCTree * ctree,GtkCMCTreeNode * node,GtkCMCTreeNode * child)3481 gtk_cmctree_find (GtkCMCTree *ctree,
3482 GtkCMCTreeNode *node,
3483 GtkCMCTreeNode *child)
3484 {
3485 if (!child)
3486 return FALSE;
3487
3488 if (!node)
3489 node = GTK_CMCTREE_NODE (GTK_CMCLIST (ctree)->row_list);
3490
3491 while (node)
3492 {
3493 if (node == child)
3494 return TRUE;
3495 if (GTK_CMCTREE_ROW (node)->children)
3496 {
3497 if (gtk_cmctree_find (ctree, GTK_CMCTREE_ROW (node)->children, child))
3498 return TRUE;
3499 }
3500 node = GTK_CMCTREE_ROW (node)->sibling;
3501 }
3502 return FALSE;
3503 }
3504
3505 gboolean
gtk_cmctree_is_ancestor(GtkCMCTree * ctree,GtkCMCTreeNode * node,GtkCMCTreeNode * child)3506 gtk_cmctree_is_ancestor (GtkCMCTree *ctree,
3507 GtkCMCTreeNode *node,
3508 GtkCMCTreeNode *child)
3509 {
3510 cm_return_val_if_fail (GTK_IS_CMCTREE (ctree), FALSE);
3511 cm_return_val_if_fail (node != NULL, FALSE);
3512
3513 if (GTK_CMCTREE_ROW (node)->children)
3514 return gtk_cmctree_find (ctree, GTK_CMCTREE_ROW (node)->children, child);
3515
3516 return FALSE;
3517 }
3518
3519 GtkCMCTreeNode *
gtk_cmctree_find_by_row_data(GtkCMCTree * ctree,GtkCMCTreeNode * node,gpointer data)3520 gtk_cmctree_find_by_row_data (GtkCMCTree *ctree,
3521 GtkCMCTreeNode *node,
3522 gpointer data)
3523 {
3524 GtkCMCTreeNode *work;
3525
3526 if (!node)
3527 node = GTK_CMCTREE_NODE (GTK_CMCLIST (ctree)->row_list);
3528
3529 while (node)
3530 {
3531 if (GTK_CMCTREE_ROW (node)->row.data == data)
3532 return node;
3533 if (GTK_CMCTREE_ROW (node)->children &&
3534 (work = gtk_cmctree_find_by_row_data
3535 (ctree, GTK_CMCTREE_ROW (node)->children, data)))
3536 return work;
3537 node = GTK_CMCTREE_ROW (node)->sibling;
3538 }
3539 return NULL;
3540 }
3541
3542 GList *
gtk_cmctree_find_all_by_row_data(GtkCMCTree * ctree,GtkCMCTreeNode * node,gpointer data)3543 gtk_cmctree_find_all_by_row_data (GtkCMCTree *ctree,
3544 GtkCMCTreeNode *node,
3545 gpointer data)
3546 {
3547 GList *list = NULL;
3548
3549 cm_return_val_if_fail (GTK_IS_CMCTREE (ctree), NULL);
3550
3551 /* if node == NULL then look in the whole tree */
3552 if (!node)
3553 node = GTK_CMCTREE_NODE (GTK_CMCLIST (ctree)->row_list);
3554
3555 while (node)
3556 {
3557 if (GTK_CMCTREE_ROW (node)->row.data == data)
3558 list = g_list_append (list, node);
3559
3560 if (GTK_CMCTREE_ROW (node)->children)
3561 {
3562 GList *sub_list;
3563
3564 sub_list = gtk_cmctree_find_all_by_row_data (ctree,
3565 GTK_CMCTREE_ROW
3566 (node)->children,
3567 data);
3568 list = g_list_concat (list, sub_list);
3569 }
3570 node = GTK_CMCTREE_ROW (node)->sibling;
3571 }
3572 return list;
3573 }
3574
3575 GtkCMCTreeNode *
gtk_cmctree_find_by_row_data_custom(GtkCMCTree * ctree,GtkCMCTreeNode * node,gpointer data,GCompareFunc func)3576 gtk_cmctree_find_by_row_data_custom (GtkCMCTree *ctree,
3577 GtkCMCTreeNode *node,
3578 gpointer data,
3579 GCompareFunc func)
3580 {
3581 GtkCMCTreeNode *work;
3582
3583 cm_return_val_if_fail (func != NULL, NULL);
3584
3585 if (!node)
3586 node = GTK_CMCTREE_NODE (GTK_CMCLIST (ctree)->row_list);
3587
3588 while (node)
3589 {
3590 if (!func (GTK_CMCTREE_ROW (node)->row.data, data))
3591 return node;
3592 if (GTK_CMCTREE_ROW (node)->children &&
3593 (work = gtk_cmctree_find_by_row_data_custom
3594 (ctree, GTK_CMCTREE_ROW (node)->children, data, func)))
3595 return work;
3596 node = GTK_CMCTREE_ROW (node)->sibling;
3597 }
3598 return NULL;
3599 }
3600
3601 GList *
gtk_cmctree_find_all_by_row_data_custom(GtkCMCTree * ctree,GtkCMCTreeNode * node,gpointer data,GCompareFunc func)3602 gtk_cmctree_find_all_by_row_data_custom (GtkCMCTree *ctree,
3603 GtkCMCTreeNode *node,
3604 gpointer data,
3605 GCompareFunc func)
3606 {
3607 GList *list = NULL;
3608
3609 cm_return_val_if_fail (GTK_IS_CMCTREE (ctree), NULL);
3610 cm_return_val_if_fail (func != NULL, NULL);
3611
3612 /* if node == NULL then look in the whole tree */
3613 if (!node)
3614 node = GTK_CMCTREE_NODE (GTK_CMCLIST (ctree)->row_list);
3615
3616 while (node)
3617 {
3618 if (!func (GTK_CMCTREE_ROW (node)->row.data, data))
3619 list = g_list_append (list, node);
3620
3621 if (GTK_CMCTREE_ROW (node)->children)
3622 {
3623 GList *sub_list;
3624
3625 sub_list = gtk_cmctree_find_all_by_row_data_custom (ctree,
3626 GTK_CMCTREE_ROW
3627 (node)->children,
3628 data,
3629 func);
3630 list = g_list_concat (list, sub_list);
3631 }
3632 node = GTK_CMCTREE_ROW (node)->sibling;
3633 }
3634 return list;
3635 }
3636
3637 gboolean
gtk_cmctree_is_hot_spot(GtkCMCTree * ctree,gint x,gint y)3638 gtk_cmctree_is_hot_spot (GtkCMCTree *ctree,
3639 gint x,
3640 gint y)
3641 {
3642 GtkCMCTreeNode *node;
3643 gint column;
3644 gint row;
3645
3646 cm_return_val_if_fail (GTK_IS_CMCTREE (ctree), FALSE);
3647
3648 if (gtk_cmclist_get_selection_info (GTK_CMCLIST (ctree), x, y, &row, &column))
3649 if ((node = GTK_CMCTREE_NODE(g_list_nth (GTK_CMCLIST (ctree)->row_list, row))))
3650 return ctree_is_hot_spot (ctree, node, row, x, y);
3651
3652 return FALSE;
3653 }
3654
3655
3656 /***********************************************************
3657 * Tree signals : move, expand, collapse, (un)select *
3658 ***********************************************************/
3659
3660
3661 void
gtk_cmctree_move(GtkCMCTree * ctree,GtkCMCTreeNode * node,GtkCMCTreeNode * new_parent,GtkCMCTreeNode * new_sibling)3662 gtk_cmctree_move (GtkCMCTree *ctree,
3663 GtkCMCTreeNode *node,
3664 GtkCMCTreeNode *new_parent,
3665 GtkCMCTreeNode *new_sibling)
3666 {
3667 cm_return_if_fail (GTK_IS_CMCTREE (ctree));
3668 cm_return_if_fail (node != NULL);
3669
3670 g_signal_emit (G_OBJECT (ctree), ctree_signals[TREE_MOVE], 0, node,
3671 new_parent, new_sibling);
3672 }
3673
3674 void
gtk_cmctree_expand(GtkCMCTree * ctree,GtkCMCTreeNode * node)3675 gtk_cmctree_expand (GtkCMCTree *ctree,
3676 GtkCMCTreeNode *node)
3677 {
3678 cm_return_if_fail (GTK_IS_CMCTREE (ctree));
3679 cm_return_if_fail (node != NULL);
3680
3681 if (GTK_CMCTREE_ROW (node)->is_leaf)
3682 return;
3683
3684 g_signal_emit (G_OBJECT (ctree), ctree_signals[TREE_EXPAND], 0, node);
3685 }
3686
3687 void
gtk_cmctree_expand_recursive(GtkCMCTree * ctree,GtkCMCTreeNode * node)3688 gtk_cmctree_expand_recursive (GtkCMCTree *ctree,
3689 GtkCMCTreeNode *node)
3690 {
3691 GtkCMCList *clist;
3692 gboolean thaw = FALSE;
3693
3694 cm_return_if_fail (GTK_IS_CMCTREE (ctree));
3695
3696 clist = GTK_CMCLIST (ctree);
3697
3698 if (node && GTK_CMCTREE_ROW (node)->is_leaf)
3699 return;
3700
3701 if (CLIST_UNFROZEN (clist) && (!node || gtk_cmctree_is_viewable (ctree, node)))
3702 {
3703 gtk_cmclist_freeze (clist);
3704 thaw = TRUE;
3705 }
3706
3707 gtk_cmctree_post_recursive (ctree, node, GTK_CMCTREE_FUNC (tree_expand), NULL);
3708
3709 if (thaw)
3710 gtk_cmclist_thaw (clist);
3711 }
3712
3713 void
gtk_cmctree_expand_to_depth(GtkCMCTree * ctree,GtkCMCTreeNode * node,gint depth)3714 gtk_cmctree_expand_to_depth (GtkCMCTree *ctree,
3715 GtkCMCTreeNode *node,
3716 gint depth)
3717 {
3718 GtkCMCList *clist;
3719 gboolean thaw = FALSE;
3720
3721 cm_return_if_fail (GTK_IS_CMCTREE (ctree));
3722
3723 clist = GTK_CMCLIST (ctree);
3724
3725 if (node && GTK_CMCTREE_ROW (node)->is_leaf)
3726 return;
3727
3728 if (CLIST_UNFROZEN (clist) && (!node || gtk_cmctree_is_viewable (ctree, node)))
3729 {
3730 gtk_cmclist_freeze (clist);
3731 thaw = TRUE;
3732 }
3733
3734 gtk_cmctree_post_recursive_to_depth (ctree, node, depth,
3735 GTK_CMCTREE_FUNC (tree_expand), NULL);
3736
3737 if (thaw)
3738 gtk_cmclist_thaw (clist);
3739 }
3740
3741 void
gtk_cmctree_collapse(GtkCMCTree * ctree,GtkCMCTreeNode * node)3742 gtk_cmctree_collapse (GtkCMCTree *ctree,
3743 GtkCMCTreeNode *node)
3744 {
3745 cm_return_if_fail (GTK_IS_CMCTREE (ctree));
3746 cm_return_if_fail (node != NULL);
3747
3748 if (GTK_CMCTREE_ROW (node)->is_leaf)
3749 return;
3750
3751 g_signal_emit (G_OBJECT (ctree), ctree_signals[TREE_COLLAPSE], 0, node);
3752 }
3753
3754 void
gtk_cmctree_collapse_recursive(GtkCMCTree * ctree,GtkCMCTreeNode * node)3755 gtk_cmctree_collapse_recursive (GtkCMCTree *ctree,
3756 GtkCMCTreeNode *node)
3757 {
3758 GtkCMCList *clist;
3759 gboolean thaw = FALSE;
3760 gint i;
3761
3762 cm_return_if_fail (GTK_IS_CMCTREE (ctree));
3763
3764 if (node && GTK_CMCTREE_ROW (node)->is_leaf)
3765 return;
3766
3767 clist = GTK_CMCLIST (ctree);
3768
3769 if (CLIST_UNFROZEN (clist) && (!node || gtk_cmctree_is_viewable (ctree, node)))
3770 {
3771 gtk_cmclist_freeze (clist);
3772 thaw = TRUE;
3773 }
3774
3775 GTK_CMCLIST_SET_FLAG (clist, CMCLIST_AUTO_RESIZE_BLOCKED);
3776 gtk_cmctree_post_recursive (ctree, node, GTK_CMCTREE_FUNC (tree_collapse), NULL);
3777 GTK_CMCLIST_UNSET_FLAG (clist, CMCLIST_AUTO_RESIZE_BLOCKED);
3778 for (i = 0; i < clist->columns; i++)
3779 if (clist->column[i].auto_resize)
3780 gtk_cmclist_set_column_width (clist, i,
3781 gtk_cmclist_optimal_column_width (clist, i));
3782
3783 if (thaw)
3784 gtk_cmclist_thaw (clist);
3785 }
3786
3787 void
gtk_cmctree_collapse_to_depth(GtkCMCTree * ctree,GtkCMCTreeNode * node,gint depth)3788 gtk_cmctree_collapse_to_depth (GtkCMCTree *ctree,
3789 GtkCMCTreeNode *node,
3790 gint depth)
3791 {
3792 GtkCMCList *clist;
3793 gboolean thaw = FALSE;
3794 gint i;
3795
3796 cm_return_if_fail (GTK_IS_CMCTREE (ctree));
3797
3798 if (node && GTK_CMCTREE_ROW (node)->is_leaf)
3799 return;
3800
3801 clist = GTK_CMCLIST (ctree);
3802
3803 if (CLIST_UNFROZEN (clist) && (!node || gtk_cmctree_is_viewable (ctree, node)))
3804 {
3805 gtk_cmclist_freeze (clist);
3806 thaw = TRUE;
3807 }
3808
3809 GTK_CMCLIST_SET_FLAG (clist, CMCLIST_AUTO_RESIZE_BLOCKED);
3810 gtk_cmctree_post_recursive_to_depth (ctree, node, depth,
3811 GTK_CMCTREE_FUNC (tree_collapse_to_depth),
3812 GINT_TO_POINTER (depth));
3813 GTK_CMCLIST_UNSET_FLAG (clist, CMCLIST_AUTO_RESIZE_BLOCKED);
3814 for (i = 0; i < clist->columns; i++)
3815 if (clist->column[i].auto_resize)
3816 gtk_cmclist_set_column_width (clist, i,
3817 gtk_cmclist_optimal_column_width (clist, i));
3818
3819 if (thaw)
3820 gtk_cmclist_thaw (clist);
3821 }
3822
3823 void
gtk_cmctree_toggle_expansion(GtkCMCTree * ctree,GtkCMCTreeNode * node)3824 gtk_cmctree_toggle_expansion (GtkCMCTree *ctree,
3825 GtkCMCTreeNode *node)
3826 {
3827 cm_return_if_fail (GTK_IS_CMCTREE (ctree));
3828 cm_return_if_fail (node != NULL);
3829
3830 if (GTK_CMCTREE_ROW (node)->is_leaf)
3831 return;
3832
3833 tree_toggle_expansion (ctree, node, NULL);
3834 }
3835
3836 void
gtk_cmctree_toggle_expansion_recursive(GtkCMCTree * ctree,GtkCMCTreeNode * node)3837 gtk_cmctree_toggle_expansion_recursive (GtkCMCTree *ctree,
3838 GtkCMCTreeNode *node)
3839 {
3840 GtkCMCList *clist;
3841 gboolean thaw = FALSE;
3842
3843 cm_return_if_fail (GTK_IS_CMCTREE (ctree));
3844
3845 if (node && GTK_CMCTREE_ROW (node)->is_leaf)
3846 return;
3847
3848 clist = GTK_CMCLIST (ctree);
3849
3850 if (CLIST_UNFROZEN (clist) && (!node || gtk_cmctree_is_viewable (ctree, node)))
3851 {
3852 gtk_cmclist_freeze (clist);
3853 thaw = TRUE;
3854 }
3855
3856 gtk_cmctree_post_recursive (ctree, node,
3857 GTK_CMCTREE_FUNC (tree_toggle_expansion), NULL);
3858
3859 if (thaw)
3860 gtk_cmclist_thaw (clist);
3861 }
3862
3863 void
gtk_cmctree_select(GtkCMCTree * ctree,GtkCMCTreeNode * node)3864 gtk_cmctree_select (GtkCMCTree *ctree,
3865 GtkCMCTreeNode *node)
3866 {
3867 cm_return_if_fail (GTK_IS_CMCTREE (ctree));
3868 cm_return_if_fail (node != NULL);
3869
3870 if (GTK_CMCTREE_ROW (node)->row.selectable)
3871 g_signal_emit (G_OBJECT (ctree), ctree_signals[TREE_SELECT_ROW], 0,
3872 node, -1);
3873 }
3874
3875 void
gtk_cmctree_unselect(GtkCMCTree * ctree,GtkCMCTreeNode * node)3876 gtk_cmctree_unselect (GtkCMCTree *ctree,
3877 GtkCMCTreeNode *node)
3878 {
3879 cm_return_if_fail (GTK_IS_CMCTREE (ctree));
3880 cm_return_if_fail (node != NULL);
3881
3882 g_signal_emit (G_OBJECT (ctree), ctree_signals[TREE_UNSELECT_ROW], 0,
3883 node, -1);
3884 }
3885
3886 void
gtk_cmctree_select_recursive(GtkCMCTree * ctree,GtkCMCTreeNode * node)3887 gtk_cmctree_select_recursive (GtkCMCTree *ctree,
3888 GtkCMCTreeNode *node)
3889 {
3890 gtk_cmctree_real_select_recursive (ctree, node, TRUE);
3891 }
3892
3893 void
gtk_cmctree_unselect_recursive(GtkCMCTree * ctree,GtkCMCTreeNode * node)3894 gtk_cmctree_unselect_recursive (GtkCMCTree *ctree,
3895 GtkCMCTreeNode *node)
3896 {
3897 gtk_cmctree_real_select_recursive (ctree, node, FALSE);
3898 }
3899
3900 void
gtk_cmctree_real_select_recursive(GtkCMCTree * ctree,GtkCMCTreeNode * node,gint state)3901 gtk_cmctree_real_select_recursive (GtkCMCTree *ctree,
3902 GtkCMCTreeNode *node,
3903 gint state)
3904 {
3905 GtkCMCList *clist;
3906 gboolean thaw = FALSE;
3907
3908 cm_return_if_fail (GTK_IS_CMCTREE (ctree));
3909
3910 clist = GTK_CMCLIST (ctree);
3911
3912 if ((state &&
3913 (clist->selection_mode == GTK_SELECTION_BROWSE ||
3914 clist->selection_mode == GTK_SELECTION_SINGLE)) ||
3915 (!state && clist->selection_mode == GTK_SELECTION_BROWSE))
3916 return;
3917
3918 if (CLIST_UNFROZEN (clist) && (!node || gtk_cmctree_is_viewable (ctree, node)))
3919 {
3920 gtk_cmclist_freeze (clist);
3921 thaw = TRUE;
3922 }
3923
3924 if (clist->selection_mode == GTK_SELECTION_MULTIPLE)
3925 {
3926 GTK_CMCLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
3927
3928 g_list_free (clist->undo_selection);
3929 g_list_free (clist->undo_unselection);
3930 clist->undo_selection = NULL;
3931 clist->undo_unselection = NULL;
3932 }
3933
3934 if (state)
3935 gtk_cmctree_post_recursive (ctree, node,
3936 GTK_CMCTREE_FUNC (tree_select), NULL);
3937 else
3938 gtk_cmctree_post_recursive (ctree, node,
3939 GTK_CMCTREE_FUNC (tree_unselect), NULL);
3940
3941 if (thaw)
3942 gtk_cmclist_thaw (clist);
3943 }
3944
3945
3946 /***********************************************************
3947 * Analogons of GtkCMCList functions *
3948 ***********************************************************/
3949
3950
3951 void
gtk_cmctree_node_set_text(GtkCMCTree * ctree,GtkCMCTreeNode * node,gint column,const gchar * text)3952 gtk_cmctree_node_set_text (GtkCMCTree *ctree,
3953 GtkCMCTreeNode *node,
3954 gint column,
3955 const gchar *text)
3956 {
3957 GtkCMCList *clist;
3958
3959 cm_return_if_fail (GTK_IS_CMCTREE (ctree));
3960 cm_return_if_fail (node != NULL);
3961
3962 if (column < 0 || column >= GTK_CMCLIST (ctree)->columns)
3963 return;
3964
3965 clist = GTK_CMCLIST (ctree);
3966
3967 GTK_CMCLIST_GET_CLASS (clist)->set_cell_contents
3968 (clist, &(GTK_CMCTREE_ROW (node)->row), column, GTK_CMCELL_TEXT,
3969 text, 0, NULL);
3970
3971 tree_draw_node (ctree, node);
3972 }
3973
3974 void
gtk_cmctree_node_set_pixbuf(GtkCMCTree * ctree,GtkCMCTreeNode * node,gint column,GdkPixbuf * pixbuf)3975 gtk_cmctree_node_set_pixbuf (GtkCMCTree *ctree,
3976 GtkCMCTreeNode *node,
3977 gint column,
3978 GdkPixbuf *pixbuf)
3979 {
3980 GtkCMCList *clist;
3981
3982 cm_return_if_fail (GTK_IS_CMCTREE (ctree));
3983 cm_return_if_fail (node != NULL);
3984 cm_return_if_fail (pixbuf != NULL);
3985
3986 if (column < 0 || column >= GTK_CMCLIST (ctree)->columns)
3987 return;
3988
3989 g_object_ref (pixbuf);
3990
3991 clist = GTK_CMCLIST (ctree);
3992
3993 GTK_CMCLIST_GET_CLASS (clist)->set_cell_contents
3994 (clist, &(GTK_CMCTREE_ROW (node)->row), column, GTK_CMCELL_PIXBUF,
3995 NULL, 0, pixbuf);
3996
3997 tree_draw_node (ctree, node);
3998 }
3999
4000 void
gtk_cmctree_node_set_pixtext(GtkCMCTree * ctree,GtkCMCTreeNode * node,gint column,const gchar * text,guint8 spacing,GdkPixbuf * pixbuf)4001 gtk_cmctree_node_set_pixtext (GtkCMCTree *ctree,
4002 GtkCMCTreeNode *node,
4003 gint column,
4004 const gchar *text,
4005 guint8 spacing,
4006 GdkPixbuf *pixbuf)
4007 {
4008 GtkCMCList *clist;
4009
4010 cm_return_if_fail (GTK_IS_CMCTREE (ctree));
4011 cm_return_if_fail (node != NULL);
4012 if (column != ctree->tree_column)
4013 cm_return_if_fail (pixbuf != NULL);
4014 if (column < 0 || column >= GTK_CMCLIST (ctree)->columns)
4015 return;
4016
4017 clist = GTK_CMCLIST (ctree);
4018
4019 if (pixbuf)
4020 {
4021 g_object_ref (pixbuf);
4022 }
4023
4024 GTK_CMCLIST_GET_CLASS (clist)->set_cell_contents
4025 (clist, &(GTK_CMCTREE_ROW (node)->row), column, GTK_CMCELL_PIXTEXT,
4026 text, spacing, pixbuf);
4027
4028 tree_draw_node (ctree, node);
4029 }
4030
4031 void
gtk_cmctree_set_node_info(GtkCMCTree * ctree,GtkCMCTreeNode * node,const gchar * text,guint8 spacing,GdkPixbuf * pixbuf_closed,GdkPixbuf * pixbuf_opened,gboolean is_leaf,gboolean expanded)4032 gtk_cmctree_set_node_info (GtkCMCTree *ctree,
4033 GtkCMCTreeNode *node,
4034 const gchar *text,
4035 guint8 spacing,
4036 GdkPixbuf *pixbuf_closed,
4037 GdkPixbuf *pixbuf_opened,
4038 gboolean is_leaf,
4039 gboolean expanded)
4040 {
4041 gboolean old_leaf;
4042 gboolean old_expanded;
4043
4044 cm_return_if_fail (GTK_IS_CMCTREE (ctree));
4045 cm_return_if_fail (node != NULL);
4046
4047 old_leaf = GTK_CMCTREE_ROW (node)->is_leaf;
4048 old_expanded = GTK_CMCTREE_ROW (node)->expanded;
4049
4050 if (is_leaf && GTK_CMCTREE_ROW (node)->children)
4051 {
4052 GtkCMCTreeNode *work;
4053 GtkCMCTreeNode *ptr;
4054
4055 work = GTK_CMCTREE_ROW (node)->children;
4056 while (work)
4057 {
4058 ptr = work;
4059 work = GTK_CMCTREE_ROW (work)->sibling;
4060 gtk_cmctree_remove_node (ctree, ptr);
4061 }
4062 }
4063
4064 set_node_info (ctree, node, text, spacing, pixbuf_closed,
4065 pixbuf_opened, is_leaf, expanded);
4066
4067 if (!is_leaf && !old_leaf)
4068 {
4069 GTK_CMCTREE_ROW (node)->expanded = old_expanded;
4070 if (expanded && !old_expanded)
4071 gtk_cmctree_expand (ctree, node);
4072 else if (!expanded && old_expanded)
4073 gtk_cmctree_collapse (ctree, node);
4074 }
4075
4076 GTK_CMCTREE_ROW (node)->expanded = (is_leaf) ? FALSE : expanded;
4077
4078 tree_draw_node (ctree, node);
4079 }
4080
4081 void
gtk_cmctree_node_set_shift(GtkCMCTree * ctree,GtkCMCTreeNode * node,gint column,gint vertical,gint horizontal)4082 gtk_cmctree_node_set_shift (GtkCMCTree *ctree,
4083 GtkCMCTreeNode *node,
4084 gint column,
4085 gint vertical,
4086 gint horizontal)
4087 {
4088 GtkCMCList *clist;
4089 GtkRequisition requisition;
4090 gboolean visible = FALSE;
4091
4092 cm_return_if_fail (GTK_IS_CMCTREE (ctree));
4093 cm_return_if_fail (node != NULL);
4094
4095 if (column < 0 || column >= GTK_CMCLIST (ctree)->columns)
4096 return;
4097
4098 clist = GTK_CMCLIST (ctree);
4099
4100 if (clist->column[column].auto_resize &&
4101 !GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist))
4102 {
4103 visible = gtk_cmctree_is_viewable (ctree, node);
4104 if (visible)
4105 GTK_CMCLIST_GET_CLASS (clist)->cell_size_request
4106 (clist, >K_CMCTREE_ROW (node)->row, column, &requisition);
4107 }
4108
4109 GTK_CMCTREE_ROW (node)->row.cell[column].vertical = vertical;
4110 GTK_CMCTREE_ROW (node)->row.cell[column].horizontal = horizontal;
4111
4112 if (visible)
4113 column_auto_resize (clist, >K_CMCTREE_ROW (node)->row,
4114 column, requisition.width);
4115
4116 tree_draw_node (ctree, node);
4117 }
4118
4119 static void
remove_grab(GtkCMCList * clist)4120 remove_grab (GtkCMCList *clist)
4121 {
4122 if (gdk_display_pointer_is_grabbed (gtk_widget_get_display (GTK_WIDGET (clist))) &&
4123 gtk_widget_has_grab (GTK_WIDGET(clist)))
4124 {
4125 gtk_grab_remove (GTK_WIDGET (clist));
4126 gdk_display_pointer_ungrab (gtk_widget_get_display (GTK_WIDGET (clist)),
4127 GDK_CURRENT_TIME);
4128 }
4129
4130 if (clist->htimer)
4131 {
4132 g_source_remove (clist->htimer);
4133 clist->htimer = 0;
4134 }
4135
4136 if (clist->vtimer)
4137 {
4138 g_source_remove (clist->vtimer);
4139 clist->vtimer = 0;
4140 }
4141 }
4142
4143 void
gtk_cmctree_node_set_selectable(GtkCMCTree * ctree,GtkCMCTreeNode * node,gboolean selectable)4144 gtk_cmctree_node_set_selectable (GtkCMCTree *ctree,
4145 GtkCMCTreeNode *node,
4146 gboolean selectable)
4147 {
4148 cm_return_if_fail (GTK_IS_CMCTREE (ctree));
4149 cm_return_if_fail (node != NULL);
4150
4151 if (selectable == GTK_CMCTREE_ROW (node)->row.selectable)
4152 return;
4153
4154 GTK_CMCTREE_ROW (node)->row.selectable = selectable;
4155
4156 if (!selectable && GTK_CMCTREE_ROW (node)->row.state == GTK_STATE_SELECTED)
4157 {
4158 GtkCMCList *clist;
4159
4160 clist = GTK_CMCLIST (ctree);
4161
4162 if (clist->anchor >= 0 &&
4163 clist->selection_mode == GTK_SELECTION_MULTIPLE)
4164 {
4165 clist->drag_button = 0;
4166 remove_grab (clist);
4167
4168 GTK_CMCLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
4169 }
4170 gtk_cmctree_unselect (ctree, node);
4171 }
4172 }
4173
4174 gboolean
gtk_cmctree_node_get_selectable(GtkCMCTree * ctree,GtkCMCTreeNode * node)4175 gtk_cmctree_node_get_selectable (GtkCMCTree *ctree,
4176 GtkCMCTreeNode *node)
4177 {
4178 cm_return_val_if_fail (node != NULL, FALSE);
4179
4180 return GTK_CMCTREE_ROW (node)->row.selectable;
4181 }
4182
4183 GtkCMCellType
gtk_cmctree_node_get_cell_type(GtkCMCTree * ctree,GtkCMCTreeNode * node,gint column)4184 gtk_cmctree_node_get_cell_type (GtkCMCTree *ctree,
4185 GtkCMCTreeNode *node,
4186 gint column)
4187 {
4188 cm_return_val_if_fail (GTK_IS_CMCTREE (ctree), -1);
4189 cm_return_val_if_fail (node != NULL, -1);
4190
4191 if (column < 0 || column >= GTK_CMCLIST (ctree)->columns)
4192 return -1;
4193
4194 return GTK_CMCTREE_ROW (node)->row.cell[column].type;
4195 }
4196
4197 gboolean
gtk_cmctree_node_get_text(GtkCMCTree * ctree,GtkCMCTreeNode * node,gint column,gchar ** text)4198 gtk_cmctree_node_get_text (GtkCMCTree *ctree,
4199 GtkCMCTreeNode *node,
4200 gint column,
4201 gchar **text)
4202 {
4203 cm_return_val_if_fail (GTK_IS_CMCTREE (ctree), FALSE);
4204 cm_return_val_if_fail (node != NULL, FALSE);
4205
4206 if (column < 0 || column >= GTK_CMCLIST (ctree)->columns)
4207 return FALSE;
4208
4209 if (GTK_CMCTREE_ROW (node)->row.cell[column].type != GTK_CMCELL_TEXT)
4210 return FALSE;
4211
4212 if (text)
4213 *text = GTK_CMCELL_TEXT (GTK_CMCTREE_ROW (node)->row.cell[column])->text;
4214
4215 return TRUE;
4216 }
4217
4218 gboolean
gtk_cmctree_node_get_pixbuf(GtkCMCTree * ctree,GtkCMCTreeNode * node,gint column,GdkPixbuf ** pixbuf)4219 gtk_cmctree_node_get_pixbuf (GtkCMCTree *ctree,
4220 GtkCMCTreeNode *node,
4221 gint column,
4222 GdkPixbuf **pixbuf)
4223 {
4224 cm_return_val_if_fail (GTK_IS_CMCTREE (ctree), FALSE);
4225 cm_return_val_if_fail (node != NULL, FALSE);
4226
4227 if (column < 0 || column >= GTK_CMCLIST (ctree)->columns)
4228 return FALSE;
4229
4230 if (GTK_CMCTREE_ROW (node)->row.cell[column].type != GTK_CMCELL_PIXBUF)
4231 return FALSE;
4232
4233 if (pixbuf)
4234 *pixbuf = GTK_CMCELL_PIXBUF (GTK_CMCTREE_ROW (node)->row.cell[column])->pixbuf;
4235
4236 return TRUE;
4237 }
4238
4239 gboolean
gtk_cmctree_node_get_pixtext(GtkCMCTree * ctree,GtkCMCTreeNode * node,gint column,gchar ** text,guint8 * spacing,GdkPixbuf ** pixbuf)4240 gtk_cmctree_node_get_pixtext (GtkCMCTree *ctree,
4241 GtkCMCTreeNode *node,
4242 gint column,
4243 gchar **text,
4244 guint8 *spacing,
4245 GdkPixbuf **pixbuf)
4246 {
4247 cm_return_val_if_fail (GTK_IS_CMCTREE (ctree), FALSE);
4248 cm_return_val_if_fail (node != NULL, FALSE);
4249
4250 if (column < 0 || column >= GTK_CMCLIST (ctree)->columns)
4251 return FALSE;
4252
4253 if (GTK_CMCTREE_ROW (node)->row.cell[column].type != GTK_CMCELL_PIXTEXT)
4254 return FALSE;
4255
4256 if (text)
4257 *text = GTK_CMCELL_PIXTEXT (GTK_CMCTREE_ROW (node)->row.cell[column])->text;
4258 if (spacing)
4259 *spacing = GTK_CMCELL_PIXTEXT (GTK_CMCTREE_ROW
4260 (node)->row.cell[column])->spacing;
4261 if (pixbuf)
4262 *pixbuf = GTK_CMCELL_PIXTEXT (GTK_CMCTREE_ROW
4263 (node)->row.cell[column])->pixbuf;
4264
4265 return TRUE;
4266 }
4267
4268 gboolean
gtk_cmctree_get_node_info(GtkCMCTree * ctree,GtkCMCTreeNode * node,gchar ** text,guint8 * spacing,GdkPixbuf ** pixbuf_closed,GdkPixbuf ** pixbuf_opened,gboolean * is_leaf,gboolean * expanded)4269 gtk_cmctree_get_node_info (GtkCMCTree *ctree,
4270 GtkCMCTreeNode *node,
4271 gchar **text,
4272 guint8 *spacing,
4273 GdkPixbuf **pixbuf_closed,
4274 GdkPixbuf **pixbuf_opened,
4275 gboolean *is_leaf,
4276 gboolean *expanded)
4277 {
4278 cm_return_val_if_fail (GTK_IS_CMCTREE (ctree), FALSE);
4279 cm_return_val_if_fail (node != NULL, FALSE);
4280
4281 if (text)
4282 *text = GTK_CMCELL_PIXTEXT
4283 (GTK_CMCTREE_ROW (node)->row.cell[ctree->tree_column])->text;
4284 if (spacing)
4285 *spacing = GTK_CMCELL_PIXTEXT
4286 (GTK_CMCTREE_ROW (node)->row.cell[ctree->tree_column])->spacing;
4287 if (pixbuf_closed)
4288 *pixbuf_closed = GTK_CMCTREE_ROW (node)->pixbuf_closed;
4289 if (pixbuf_opened)
4290 *pixbuf_opened = GTK_CMCTREE_ROW (node)->pixbuf_opened;
4291 if (is_leaf)
4292 *is_leaf = GTK_CMCTREE_ROW (node)->is_leaf;
4293 if (expanded)
4294 *expanded = GTK_CMCTREE_ROW (node)->expanded;
4295
4296 return TRUE;
4297 }
4298
4299 void
gtk_cmctree_node_set_cell_style(GtkCMCTree * ctree,GtkCMCTreeNode * node,gint column,GtkStyle * style)4300 gtk_cmctree_node_set_cell_style (GtkCMCTree *ctree,
4301 GtkCMCTreeNode *node,
4302 gint column,
4303 GtkStyle *style)
4304 {
4305 GtkCMCList *clist;
4306 GtkRequisition requisition;
4307 gboolean visible = FALSE;
4308
4309 cm_return_if_fail (GTK_IS_CMCTREE (ctree));
4310 cm_return_if_fail (node != NULL);
4311
4312 clist = GTK_CMCLIST (ctree);
4313
4314 if (column < 0 || column >= clist->columns)
4315 return;
4316
4317 if (GTK_CMCTREE_ROW (node)->row.cell[column].style == style)
4318 return;
4319
4320 if (clist->column[column].auto_resize &&
4321 !GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist))
4322 {
4323 visible = gtk_cmctree_is_viewable (ctree, node);
4324 if (visible)
4325 GTK_CMCLIST_GET_CLASS (clist)->cell_size_request
4326 (clist, >K_CMCTREE_ROW (node)->row, column, &requisition);
4327 }
4328
4329 if (GTK_CMCTREE_ROW (node)->row.cell[column].style)
4330 {
4331 if (gtk_widget_get_realized (GTK_WIDGET(ctree)))
4332 gtk_style_detach (GTK_CMCTREE_ROW (node)->row.cell[column].style);
4333 g_object_unref (GTK_CMCTREE_ROW (node)->row.cell[column].style);
4334 }
4335
4336 GTK_CMCTREE_ROW (node)->row.cell[column].style = style;
4337
4338 if (GTK_CMCTREE_ROW (node)->row.cell[column].style)
4339 {
4340 g_object_ref (GTK_CMCTREE_ROW (node)->row.cell[column].style);
4341
4342 if (gtk_widget_get_realized (GTK_WIDGET(ctree)))
4343 GTK_CMCTREE_ROW (node)->row.cell[column].style =
4344 gtk_style_attach (GTK_CMCTREE_ROW (node)->row.cell[column].style,
4345 clist->clist_window);
4346 }
4347
4348 if (visible)
4349 column_auto_resize (clist, >K_CMCTREE_ROW (node)->row, column,
4350 requisition.width);
4351
4352 tree_draw_node (ctree, node);
4353 }
4354
4355 GtkStyle *
gtk_cmctree_node_get_cell_style(GtkCMCTree * ctree,GtkCMCTreeNode * node,gint column)4356 gtk_cmctree_node_get_cell_style (GtkCMCTree *ctree,
4357 GtkCMCTreeNode *node,
4358 gint column)
4359 {
4360 cm_return_val_if_fail (GTK_IS_CMCTREE (ctree), NULL);
4361 cm_return_val_if_fail (node != NULL, NULL);
4362
4363 if (column < 0 || column >= GTK_CMCLIST (ctree)->columns)
4364 return NULL;
4365
4366 return GTK_CMCTREE_ROW (node)->row.cell[column].style;
4367 }
4368
4369 void
gtk_cmctree_node_set_row_style(GtkCMCTree * ctree,GtkCMCTreeNode * node,GtkStyle * style)4370 gtk_cmctree_node_set_row_style (GtkCMCTree *ctree,
4371 GtkCMCTreeNode *node,
4372 GtkStyle *style)
4373 {
4374 GtkCMCList *clist;
4375 GtkRequisition requisition;
4376 gboolean visible;
4377 gint *old_width = NULL;
4378 gint i;
4379
4380 cm_return_if_fail (GTK_IS_CMCTREE (ctree));
4381 cm_return_if_fail (node != NULL);
4382
4383 clist = GTK_CMCLIST (ctree);
4384
4385 if (GTK_CMCTREE_ROW (node)->row.style == style)
4386 return;
4387
4388 visible = gtk_cmctree_is_viewable (ctree, node);
4389 if (visible && !GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist))
4390 {
4391 old_width = g_new (gint, clist->columns);
4392 for (i = 0; i < clist->columns; i++)
4393 if (clist->column[i].auto_resize)
4394 {
4395 GTK_CMCLIST_GET_CLASS (clist)->cell_size_request
4396 (clist, >K_CMCTREE_ROW (node)->row, i, &requisition);
4397 old_width[i] = requisition.width;
4398 }
4399 }
4400
4401 if (GTK_CMCTREE_ROW (node)->row.style)
4402 {
4403 if (gtk_widget_get_realized (GTK_WIDGET(ctree)))
4404 gtk_style_detach (GTK_CMCTREE_ROW (node)->row.style);
4405 g_object_unref (GTK_CMCTREE_ROW (node)->row.style);
4406 }
4407
4408 GTK_CMCTREE_ROW (node)->row.style = style;
4409
4410 if (GTK_CMCTREE_ROW (node)->row.style)
4411 {
4412 g_object_ref (GTK_CMCTREE_ROW (node)->row.style);
4413
4414 if (gtk_widget_get_realized (GTK_WIDGET(ctree)))
4415 GTK_CMCTREE_ROW (node)->row.style =
4416 gtk_style_attach (GTK_CMCTREE_ROW (node)->row.style,
4417 clist->clist_window);
4418 }
4419
4420 if (visible && !GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist))
4421 {
4422 for (i = 0; i < clist->columns; i++)
4423 if (clist->column[i].auto_resize)
4424 column_auto_resize (clist, >K_CMCTREE_ROW (node)->row, i,
4425 old_width[i]);
4426 g_free (old_width);
4427 }
4428 tree_draw_node (ctree, node);
4429 }
4430
4431 GtkStyle *
gtk_cmctree_node_get_row_style(GtkCMCTree * ctree,GtkCMCTreeNode * node)4432 gtk_cmctree_node_get_row_style (GtkCMCTree *ctree,
4433 GtkCMCTreeNode *node)
4434 {
4435 cm_return_val_if_fail (GTK_IS_CMCTREE (ctree), NULL);
4436 cm_return_val_if_fail (node != NULL, NULL);
4437
4438 return GTK_CMCTREE_ROW (node)->row.style;
4439 }
4440
4441 void
gtk_cmctree_node_set_foreground(GtkCMCTree * ctree,GtkCMCTreeNode * node,const GdkColor * color)4442 gtk_cmctree_node_set_foreground (GtkCMCTree *ctree,
4443 GtkCMCTreeNode *node,
4444 const GdkColor *color)
4445 {
4446 cm_return_if_fail (GTK_IS_CMCTREE (ctree));
4447 cm_return_if_fail (node != NULL);
4448
4449 if (color)
4450 {
4451 GTK_CMCTREE_ROW (node)->row.foreground = *color;
4452 GTK_CMCTREE_ROW (node)->row.fg_set = TRUE;
4453 if (gtk_widget_get_realized (GTK_WIDGET(ctree)))
4454 gdk_colormap_alloc_color (gtk_widget_get_colormap (GTK_WIDGET (ctree)),
4455 >K_CMCTREE_ROW (node)->row.foreground, TRUE, TRUE);
4456 }
4457 else
4458 GTK_CMCTREE_ROW (node)->row.fg_set = FALSE;
4459
4460 tree_draw_node (ctree, node);
4461 }
4462
4463 void
gtk_cmctree_node_set_background(GtkCMCTree * ctree,GtkCMCTreeNode * node,const GdkColor * color)4464 gtk_cmctree_node_set_background (GtkCMCTree *ctree,
4465 GtkCMCTreeNode *node,
4466 const GdkColor *color)
4467 {
4468 cm_return_if_fail (GTK_IS_CMCTREE (ctree));
4469 cm_return_if_fail (node != NULL);
4470
4471 if (color)
4472 {
4473 GTK_CMCTREE_ROW (node)->row.background = *color;
4474 GTK_CMCTREE_ROW (node)->row.bg_set = TRUE;
4475 if (gtk_widget_get_realized (GTK_WIDGET(ctree)))
4476 gdk_colormap_alloc_color (gtk_widget_get_colormap (GTK_WIDGET (ctree)),
4477 >K_CMCTREE_ROW (node)->row.background, TRUE, TRUE);
4478 }
4479 else
4480 GTK_CMCTREE_ROW (node)->row.bg_set = FALSE;
4481
4482 tree_draw_node (ctree, node);
4483 }
4484
4485 void
gtk_cmctree_node_set_row_data(GtkCMCTree * ctree,GtkCMCTreeNode * node,gpointer data)4486 gtk_cmctree_node_set_row_data (GtkCMCTree *ctree,
4487 GtkCMCTreeNode *node,
4488 gpointer data)
4489 {
4490 gtk_cmctree_node_set_row_data_full (ctree, node, data, NULL);
4491 }
4492
4493 void
gtk_cmctree_node_set_row_data_full(GtkCMCTree * ctree,GtkCMCTreeNode * node,gpointer data,GDestroyNotify destroy)4494 gtk_cmctree_node_set_row_data_full (GtkCMCTree *ctree,
4495 GtkCMCTreeNode *node,
4496 gpointer data,
4497 GDestroyNotify destroy)
4498 {
4499 GDestroyNotify dnotify;
4500 gpointer ddata;
4501
4502 cm_return_if_fail (GTK_IS_CMCTREE (ctree));
4503 cm_return_if_fail (node != NULL);
4504
4505 dnotify = GTK_CMCTREE_ROW (node)->row.destroy;
4506 ddata = GTK_CMCTREE_ROW (node)->row.data;
4507
4508 GTK_CMCTREE_ROW (node)->row.data = data;
4509 GTK_CMCTREE_ROW (node)->row.destroy = destroy;
4510
4511 if (dnotify)
4512 dnotify (ddata);
4513 }
4514
4515 gpointer
gtk_cmctree_node_get_row_data(GtkCMCTree * ctree,GtkCMCTreeNode * node)4516 gtk_cmctree_node_get_row_data (GtkCMCTree *ctree,
4517 GtkCMCTreeNode *node)
4518 {
4519 cm_return_val_if_fail (GTK_IS_CMCTREE (ctree), NULL);
4520
4521 return node ? GTK_CMCTREE_ROW (node)->row.data : NULL;
4522 }
4523
4524 void
gtk_cmctree_node_moveto(GtkCMCTree * ctree,GtkCMCTreeNode * node,gint column,gfloat row_align,gfloat col_align)4525 gtk_cmctree_node_moveto (GtkCMCTree *ctree,
4526 GtkCMCTreeNode *node,
4527 gint column,
4528 gfloat row_align,
4529 gfloat col_align)
4530 {
4531 gint row = -1;
4532 GtkCMCList *clist;
4533
4534 cm_return_if_fail (GTK_IS_CMCTREE (ctree));
4535
4536 clist = GTK_CMCLIST (ctree);
4537
4538 while (node && !gtk_cmctree_is_viewable (ctree, node))
4539 node = GTK_CMCTREE_ROW (node)->parent;
4540
4541 if (node)
4542 row = g_list_position (clist->row_list, (GList *)node);
4543
4544 gtk_cmclist_moveto (clist, row, column, row_align, col_align);
4545 }
4546
4547 GtkVisibility
gtk_cmctree_node_is_visible(GtkCMCTree * ctree,GtkCMCTreeNode * node)4548 gtk_cmctree_node_is_visible (GtkCMCTree *ctree,
4549 GtkCMCTreeNode *node)
4550 {
4551 gint row;
4552
4553 cm_return_val_if_fail (ctree != NULL, 0);
4554 cm_return_val_if_fail (node != NULL, 0);
4555
4556 row = g_list_position (GTK_CMCLIST (ctree)->row_list, (GList*) node);
4557 return gtk_cmclist_row_is_visible (GTK_CMCLIST (ctree), row);
4558 }
4559
4560
4561 /***********************************************************
4562 * GtkCMCTree specific functions *
4563 ***********************************************************/
4564
4565 void
gtk_cmctree_set_indent(GtkCMCTree * ctree,gint indent)4566 gtk_cmctree_set_indent (GtkCMCTree *ctree,
4567 gint indent)
4568 {
4569 GtkCMCList *clist;
4570
4571 cm_return_if_fail (GTK_IS_CMCTREE (ctree));
4572 cm_return_if_fail (indent >= 0);
4573
4574 if (indent == ctree->tree_indent)
4575 return;
4576
4577 clist = GTK_CMCLIST (ctree);
4578 ctree->tree_indent = indent;
4579
4580 if (clist->column[ctree->tree_column].auto_resize &&
4581 !GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist))
4582 gtk_cmclist_set_column_width
4583 (clist, ctree->tree_column,
4584 gtk_cmclist_optimal_column_width (clist, ctree->tree_column));
4585 else
4586 CLIST_REFRESH (ctree);
4587 }
4588
4589 void
gtk_cmctree_set_spacing(GtkCMCTree * ctree,gint spacing)4590 gtk_cmctree_set_spacing (GtkCMCTree *ctree,
4591 gint spacing)
4592 {
4593 GtkCMCList *clist;
4594 gint old_spacing;
4595
4596 cm_return_if_fail (GTK_IS_CMCTREE (ctree));
4597 cm_return_if_fail (spacing >= 0);
4598
4599 if (spacing == ctree->tree_spacing)
4600 return;
4601
4602 clist = GTK_CMCLIST (ctree);
4603
4604 old_spacing = ctree->tree_spacing;
4605 ctree->tree_spacing = spacing;
4606
4607 if (clist->column[ctree->tree_column].auto_resize &&
4608 !GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist))
4609 gtk_cmclist_set_column_width (clist, ctree->tree_column,
4610 clist->column[ctree->tree_column].width +
4611 spacing - old_spacing);
4612 else
4613 CLIST_REFRESH (ctree);
4614 }
4615
4616 void
gtk_cmctree_set_show_stub(GtkCMCTree * ctree,gboolean show_stub)4617 gtk_cmctree_set_show_stub (GtkCMCTree *ctree,
4618 gboolean show_stub)
4619 {
4620 cm_return_if_fail (GTK_IS_CMCTREE (ctree));
4621
4622 show_stub = show_stub != FALSE;
4623
4624 if (show_stub != ctree->show_stub)
4625 {
4626 GtkCMCList *clist;
4627
4628 clist = GTK_CMCLIST (ctree);
4629 ctree->show_stub = show_stub;
4630
4631 if (CLIST_UNFROZEN (clist) && clist->rows &&
4632 gtk_cmclist_row_is_visible (clist, 0) != GTK_VISIBILITY_NONE)
4633 GTK_CMCLIST_GET_CLASS (clist)->draw_row
4634 (clist, NULL, 0, GTK_CMCLIST_ROW (clist->row_list));
4635 }
4636 }
4637
4638 void
gtk_cmctree_set_line_style(GtkCMCTree * ctree,GtkCMCTreeLineStyle line_style)4639 gtk_cmctree_set_line_style (GtkCMCTree *ctree,
4640 GtkCMCTreeLineStyle line_style)
4641 {
4642 }
4643
4644 void
gtk_cmctree_set_expander_style(GtkCMCTree * ctree,GtkCMCTreeExpanderStyle expander_style)4645 gtk_cmctree_set_expander_style (GtkCMCTree *ctree,
4646 GtkCMCTreeExpanderStyle expander_style)
4647 {
4648 GtkCMCList *clist;
4649 GtkCMCTreeExpanderStyle old_style;
4650
4651 cm_return_if_fail (GTK_IS_CMCTREE (ctree));
4652
4653 if (expander_style == ctree->expander_style)
4654 return;
4655
4656 clist = GTK_CMCLIST (ctree);
4657
4658 old_style = ctree->expander_style;
4659 ctree->expander_style = expander_style;
4660
4661 if (clist->column[ctree->tree_column].auto_resize &&
4662 !GTK_CMCLIST_AUTO_RESIZE_BLOCKED (clist))
4663 {
4664 gint new_width;
4665
4666 new_width = clist->column[ctree->tree_column].width;
4667 switch (old_style)
4668 {
4669 case GTK_CMCTREE_EXPANDER_NONE:
4670 break;
4671 case GTK_CMCTREE_EXPANDER_TRIANGLE:
4672 new_width -= PM_SIZE + 3;
4673 break;
4674 }
4675
4676 switch (expander_style)
4677 {
4678 case GTK_CMCTREE_EXPANDER_NONE:
4679 break;
4680 case GTK_CMCTREE_EXPANDER_TRIANGLE:
4681 new_width += PM_SIZE + 3;
4682 break;
4683 }
4684
4685 gtk_cmclist_set_column_width (clist, ctree->tree_column, new_width);
4686 }
4687
4688 if (gtk_widget_is_drawable (GTK_WIDGET(clist)))
4689 CLIST_REFRESH (clist);
4690 }
4691
4692
4693 /***********************************************************
4694 * Tree sorting functions *
4695 ***********************************************************/
4696
4697
4698 static void
tree_sort(GtkCMCTree * ctree,GtkCMCTreeNode * node,gpointer data)4699 tree_sort (GtkCMCTree *ctree,
4700 GtkCMCTreeNode *node,
4701 gpointer data)
4702 {
4703 GtkCMCTreeNode *list_start;
4704 GtkCMCTreeNode *cmp;
4705 GtkCMCTreeNode *work;
4706 GtkCMCList *clist;
4707
4708 clist = GTK_CMCLIST (ctree);
4709
4710 if (node)
4711 list_start = GTK_CMCTREE_ROW (node)->children;
4712 else
4713 list_start = GTK_CMCTREE_NODE (clist->row_list);
4714
4715 while (list_start)
4716 {
4717 cmp = list_start;
4718 work = GTK_CMCTREE_ROW (cmp)->sibling;
4719 while (work)
4720 {
4721 if (clist->sort_type == GTK_SORT_ASCENDING)
4722 {
4723 if (clist->compare
4724 (clist, GTK_CMCTREE_ROW (work), GTK_CMCTREE_ROW (cmp)) < 0)
4725 cmp = work;
4726 }
4727 else
4728 {
4729 if (clist->compare
4730 (clist, GTK_CMCTREE_ROW (work), GTK_CMCTREE_ROW (cmp)) > 0)
4731 cmp = work;
4732 }
4733 work = GTK_CMCTREE_ROW (work)->sibling;
4734 }
4735 if (cmp == list_start)
4736 list_start = GTK_CMCTREE_ROW (cmp)->sibling;
4737 else
4738 {
4739 gtk_cmctree_unlink (ctree, cmp, FALSE);
4740 gtk_cmctree_link (ctree, cmp, node, list_start, FALSE);
4741 }
4742 }
4743 }
4744
4745 void
gtk_cmctree_sort_recursive(GtkCMCTree * ctree,GtkCMCTreeNode * node)4746 gtk_cmctree_sort_recursive (GtkCMCTree *ctree,
4747 GtkCMCTreeNode *node)
4748 {
4749 GtkCMCList *clist;
4750 GtkCMCTreeNode *focus_node = NULL;
4751
4752 cm_return_if_fail (GTK_IS_CMCTREE (ctree));
4753
4754 clist = GTK_CMCLIST (ctree);
4755
4756 gtk_cmclist_freeze (clist);
4757
4758 if (clist->selection_mode == GTK_SELECTION_MULTIPLE)
4759 {
4760 GTK_CMCLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
4761
4762 g_list_free (clist->undo_selection);
4763 g_list_free (clist->undo_unselection);
4764 clist->undo_selection = NULL;
4765 clist->undo_unselection = NULL;
4766 }
4767
4768 if (!node || (node && gtk_cmctree_is_viewable (ctree, node)))
4769 focus_node =
4770 GTK_CMCTREE_NODE (g_list_nth (clist->row_list, clist->focus_row));
4771
4772 gtk_cmctree_post_recursive (ctree, node, GTK_CMCTREE_FUNC (tree_sort), NULL);
4773
4774 if (!node)
4775 tree_sort (ctree, NULL, NULL);
4776
4777 if (focus_node)
4778 {
4779 clist->focus_row = g_list_position (clist->row_list,(GList *)focus_node);
4780 clist->undo_anchor = clist->focus_row;
4781 }
4782
4783 gtk_cmclist_thaw (clist);
4784 }
4785
4786 static void
real_sort_list(GtkCMCList * clist)4787 real_sort_list (GtkCMCList *clist)
4788 {
4789 gtk_cmctree_sort_recursive (GTK_CMCTREE (clist), NULL);
4790 }
4791
4792 void
gtk_cmctree_sort_node(GtkCMCTree * ctree,GtkCMCTreeNode * node)4793 gtk_cmctree_sort_node (GtkCMCTree *ctree,
4794 GtkCMCTreeNode *node)
4795 {
4796 GtkCMCList *clist;
4797 GtkCMCTreeNode *focus_node = NULL;
4798
4799 cm_return_if_fail (GTK_IS_CMCTREE (ctree));
4800
4801 clist = GTK_CMCLIST (ctree);
4802
4803 gtk_cmclist_freeze (clist);
4804
4805 if (clist->selection_mode == GTK_SELECTION_MULTIPLE)
4806 {
4807 GTK_CMCLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
4808
4809 g_list_free (clist->undo_selection);
4810 g_list_free (clist->undo_unselection);
4811 clist->undo_selection = NULL;
4812 clist->undo_unselection = NULL;
4813 }
4814
4815 if (!node || (node && gtk_cmctree_is_viewable (ctree, node)))
4816 focus_node = GTK_CMCTREE_NODE
4817 (g_list_nth (clist->row_list, clist->focus_row));
4818
4819 tree_sort (ctree, node, NULL);
4820
4821 if (focus_node)
4822 {
4823 clist->focus_row = g_list_position (clist->row_list,(GList *)focus_node);
4824 clist->undo_anchor = clist->focus_row;
4825 }
4826
4827 gtk_cmclist_thaw (clist);
4828 }
4829
4830 /************************************************************************/
4831
4832 static void
fake_unselect_all(GtkCMCList * clist,gint row)4833 fake_unselect_all (GtkCMCList *clist,
4834 gint row)
4835 {
4836 GList *list;
4837 GList *focus_node = NULL;
4838
4839 if (row >= 0 && (focus_node = g_list_nth (clist->row_list, row)))
4840 {
4841 if (GTK_CMCTREE_ROW (focus_node)->row.state == GTK_STATE_NORMAL &&
4842 GTK_CMCTREE_ROW (focus_node)->row.selectable)
4843 {
4844 GTK_CMCTREE_ROW (focus_node)->row.state = GTK_STATE_SELECTED;
4845
4846 if (CLIST_UNFROZEN (clist) &&
4847 gtk_cmclist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE)
4848 GTK_CMCLIST_GET_CLASS (clist)->draw_row (clist, NULL, row,
4849 GTK_CMCLIST_ROW (focus_node));
4850 }
4851 }
4852
4853 clist->undo_selection = clist->selection;
4854 clist->selection = NULL;
4855 clist->selection_end = NULL;
4856
4857 for (list = clist->undo_selection; list; list = list->next)
4858 {
4859 if (list->data == focus_node)
4860 continue;
4861
4862 GTK_CMCTREE_ROW ((GList *)(list->data))->row.state = GTK_STATE_NORMAL;
4863 tree_draw_node (GTK_CMCTREE (clist), GTK_CMCTREE_NODE (list->data));
4864 }
4865 }
4866
4867 static GList *
selection_find(GtkCMCList * clist,gint row_number,GList * row_list_element)4868 selection_find (GtkCMCList *clist,
4869 gint row_number,
4870 GList *row_list_element)
4871 {
4872 return g_list_find (clist->selection, row_list_element);
4873 }
4874
4875 static void
resync_selection(GtkCMCList * clist,GdkEvent * event)4876 resync_selection (GtkCMCList *clist, GdkEvent *event)
4877 {
4878 GtkCMCTree *ctree;
4879 GList *list;
4880 GtkCMCTreeNode *node;
4881 gint i;
4882 gint e;
4883 gint row;
4884 gboolean unselect;
4885
4886 cm_return_if_fail (GTK_IS_CMCTREE (clist));
4887
4888 if (clist->selection_mode != GTK_SELECTION_MULTIPLE)
4889 return;
4890
4891 if (clist->anchor < 0 || clist->drag_pos < 0)
4892 return;
4893
4894 ctree = GTK_CMCTREE (clist);
4895
4896 clist->freeze_count++;
4897
4898 i = MIN (clist->anchor, clist->drag_pos);
4899 e = MAX (clist->anchor, clist->drag_pos);
4900
4901 if (clist->undo_selection)
4902 {
4903 list = clist->selection;
4904 clist->selection = clist->undo_selection;
4905 clist->selection_end = g_list_last (clist->selection);
4906 clist->undo_selection = list;
4907 list = clist->selection;
4908
4909 while (list)
4910 {
4911 node = list->data;
4912 list = list->next;
4913
4914 unselect = TRUE;
4915
4916 if (gtk_cmctree_is_viewable (ctree, node))
4917 {
4918 row = g_list_position (clist->row_list, (GList *)node);
4919 if (row >= i && row <= e)
4920 unselect = FALSE;
4921 }
4922 if (unselect && GTK_CMCTREE_ROW (node)->row.selectable)
4923 {
4924 GTK_CMCTREE_ROW (node)->row.state = GTK_STATE_SELECTED;
4925 gtk_cmctree_unselect (ctree, node);
4926 clist->undo_selection = g_list_prepend (clist->undo_selection,
4927 node);
4928 }
4929 }
4930 }
4931
4932 if (clist->anchor < clist->drag_pos)
4933 {
4934 for (node = GTK_CMCTREE_NODE (g_list_nth (clist->row_list, i)); i <= e;
4935 i++, node = GTK_CMCTREE_NODE_NEXT (node))
4936 if (GTK_CMCTREE_ROW (node)->row.selectable)
4937 {
4938 if (g_list_find (clist->selection, node))
4939 {
4940 if (GTK_CMCTREE_ROW (node)->row.state == GTK_STATE_NORMAL)
4941 {
4942 GTK_CMCTREE_ROW (node)->row.state = GTK_STATE_SELECTED;
4943 gtk_cmctree_unselect (ctree, node);
4944 clist->undo_selection =
4945 g_list_prepend (clist->undo_selection, node);
4946 }
4947 }
4948 else if (GTK_CMCTREE_ROW (node)->row.state == GTK_STATE_SELECTED)
4949 {
4950 GTK_CMCTREE_ROW (node)->row.state = GTK_STATE_NORMAL;
4951 clist->undo_unselection =
4952 g_list_prepend (clist->undo_unselection, node);
4953 }
4954 }
4955 }
4956 else
4957 {
4958 for (node = GTK_CMCTREE_NODE (g_list_nth (clist->row_list, e)); i <= e;
4959 e--, node = GTK_CMCTREE_NODE_PREV (node))
4960 if (GTK_CMCTREE_ROW (node)->row.selectable)
4961 {
4962 if (g_list_find (clist->selection, node))
4963 {
4964 if (GTK_CMCTREE_ROW (node)->row.state == GTK_STATE_NORMAL)
4965 {
4966 GTK_CMCTREE_ROW (node)->row.state = GTK_STATE_SELECTED;
4967 gtk_cmctree_unselect (ctree, node);
4968 clist->undo_selection =
4969 g_list_prepend (clist->undo_selection, node);
4970 }
4971 }
4972 else if (GTK_CMCTREE_ROW (node)->row.state == GTK_STATE_SELECTED)
4973 {
4974 GTK_CMCTREE_ROW (node)->row.state = GTK_STATE_NORMAL;
4975 clist->undo_unselection =
4976 g_list_prepend (clist->undo_unselection, node);
4977 }
4978 }
4979 }
4980
4981 clist->undo_unselection = g_list_reverse (clist->undo_unselection);
4982 for (list = clist->undo_unselection; list; list = list->next)
4983 gtk_cmctree_select (ctree, list->data);
4984
4985 clist->anchor = -1;
4986 clist->drag_pos = -1;
4987
4988 if (!CLIST_UNFROZEN (clist))
4989 clist->freeze_count--;
4990 }
4991
4992 static void
real_undo_selection(GtkCMCList * clist)4993 real_undo_selection (GtkCMCList *clist)
4994 {
4995 GtkCMCTree *ctree;
4996 GList *work;
4997
4998 cm_return_if_fail (GTK_IS_CMCTREE (clist));
4999
5000 if (clist->selection_mode != GTK_SELECTION_MULTIPLE)
5001 return;
5002
5003 if (!(clist->undo_selection || clist->undo_unselection))
5004 {
5005 gtk_cmclist_unselect_all (clist);
5006 return;
5007 }
5008
5009 ctree = GTK_CMCTREE (clist);
5010
5011 for (work = clist->undo_selection; work; work = work->next)
5012 if (GTK_CMCTREE_ROW (work->data)->row.selectable)
5013 gtk_cmctree_select (ctree, GTK_CMCTREE_NODE (work->data));
5014
5015 for (work = clist->undo_unselection; work; work = work->next)
5016 if (GTK_CMCTREE_ROW (work->data)->row.selectable)
5017 gtk_cmctree_unselect (ctree, GTK_CMCTREE_NODE (work->data));
5018
5019 if (gtk_widget_has_focus (GTK_WIDGET(clist)) &&
5020 clist->focus_row != clist->undo_anchor)
5021 {
5022 clist->focus_row = clist->undo_anchor;
5023 gtk_widget_queue_draw (GTK_WIDGET (clist));
5024 }
5025 else
5026 clist->focus_row = clist->undo_anchor;
5027
5028 clist->undo_anchor = -1;
5029
5030 g_list_free (clist->undo_selection);
5031 g_list_free (clist->undo_unselection);
5032 clist->undo_selection = NULL;
5033 clist->undo_unselection = NULL;
5034
5035 if (ROW_TOP_YPIXEL (clist, clist->focus_row) + clist->row_height >
5036 clist->clist_window_height)
5037 gtk_cmclist_moveto (clist, clist->focus_row, -1, 1, 0);
5038 else if (ROW_TOP_YPIXEL (clist, clist->focus_row) < 0)
5039 gtk_cmclist_moveto (clist, clist->focus_row, -1, 0, 0);
5040
5041 }
5042
5043 void
gtk_cmctree_set_drag_compare_func(GtkCMCTree * ctree,GtkCMCTreeCompareDragFunc cmp_func)5044 gtk_cmctree_set_drag_compare_func (GtkCMCTree *ctree,
5045 GtkCMCTreeCompareDragFunc cmp_func)
5046 {
5047 cm_return_if_fail (GTK_IS_CMCTREE (ctree));
5048
5049 ctree->drag_compare = cmp_func;
5050 }
5051
5052 static gboolean
check_drag(GtkCMCTree * ctree,GtkCMCTreeNode * drag_source,GtkCMCTreeNode * drag_target,GtkCMCListDragPos insert_pos)5053 check_drag (GtkCMCTree *ctree,
5054 GtkCMCTreeNode *drag_source,
5055 GtkCMCTreeNode *drag_target,
5056 GtkCMCListDragPos insert_pos)
5057 {
5058 cm_return_val_if_fail (GTK_IS_CMCTREE (ctree), FALSE);
5059
5060 if (drag_source && drag_source != drag_target &&
5061 (!GTK_CMCTREE_ROW (drag_source)->children ||
5062 !gtk_cmctree_is_ancestor (ctree, drag_source, drag_target)))
5063 {
5064 switch (insert_pos)
5065 {
5066 case GTK_CMCLIST_DRAG_NONE:
5067 return FALSE;
5068 case GTK_CMCLIST_DRAG_AFTER:
5069 if (GTK_CMCTREE_ROW (drag_target)->sibling != drag_source)
5070 return (!ctree->drag_compare ||
5071 ctree->drag_compare (ctree,
5072 drag_source,
5073 GTK_CMCTREE_ROW (drag_target)->parent,
5074 GTK_CMCTREE_ROW (drag_target)->sibling));
5075 break;
5076 case GTK_CMCLIST_DRAG_BEFORE:
5077 if (GTK_CMCTREE_ROW (drag_source)->sibling != drag_target)
5078 return (!ctree->drag_compare ||
5079 ctree->drag_compare (ctree,
5080 drag_source,
5081 GTK_CMCTREE_ROW (drag_target)->parent,
5082 drag_target));
5083 break;
5084 case GTK_CMCLIST_DRAG_INTO:
5085 if (!GTK_CMCTREE_ROW (drag_target)->is_leaf &&
5086 GTK_CMCTREE_ROW (drag_target)->children != drag_source)
5087 return (!ctree->drag_compare ||
5088 ctree->drag_compare (ctree,
5089 drag_source,
5090 drag_target,
5091 GTK_CMCTREE_ROW (drag_target)->children));
5092 break;
5093 }
5094 }
5095 return FALSE;
5096 }
5097
5098
5099
5100 /************************************/
5101 static void
drag_dest_info_destroy(gpointer data)5102 drag_dest_info_destroy (gpointer data)
5103 {
5104 GtkCMCListDestInfo *info = data;
5105
5106 g_free (info);
5107 }
5108
5109 static void
drag_dest_cell(GtkCMCList * clist,gint x,gint y,GtkCMCListDestInfo * dest_info)5110 drag_dest_cell (GtkCMCList *clist,
5111 gint x,
5112 gint y,
5113 GtkCMCListDestInfo *dest_info)
5114 {
5115 GtkStyle *style;
5116 GtkWidget *widget;
5117 guint border_width;
5118
5119 widget = GTK_WIDGET (clist);
5120 style = gtk_widget_get_style (widget);
5121
5122 dest_info->insert_pos = GTK_CMCLIST_DRAG_NONE;
5123
5124 border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
5125 y -= (border_width +
5126 style->ythickness + clist->column_title_area.height);
5127 dest_info->cell.row = ROW_FROM_YPIXEL (clist, y);
5128
5129 if (dest_info->cell.row >= clist->rows)
5130 {
5131 dest_info->cell.row = clist->rows - 1;
5132 y = ROW_TOP_YPIXEL (clist, dest_info->cell.row) + clist->row_height;
5133 }
5134 if (dest_info->cell.row < -1)
5135 dest_info->cell.row = -1;
5136
5137 x -= border_width + style->xthickness;
5138
5139 dest_info->cell.column = COLUMN_FROM_XPIXEL (clist, x);
5140
5141 if (dest_info->cell.row >= 0)
5142 {
5143 gint y_delta;
5144 gint h = 0;
5145
5146 y_delta = y - ROW_TOP_YPIXEL (clist, dest_info->cell.row);
5147
5148 if (GTK_CMCLIST_DRAW_DRAG_RECT(clist) &&
5149 !GTK_CMCTREE_ROW (g_list_nth (clist->row_list,
5150 dest_info->cell.row))->is_leaf)
5151 {
5152 dest_info->insert_pos = GTK_CMCLIST_DRAG_INTO;
5153 h = clist->row_height / 4;
5154 }
5155 else if (GTK_CMCLIST_DRAW_DRAG_LINE(clist))
5156 {
5157 dest_info->insert_pos = GTK_CMCLIST_DRAG_BEFORE;
5158 h = clist->row_height / 2;
5159 }
5160
5161 if (GTK_CMCLIST_DRAW_DRAG_LINE(clist))
5162 {
5163 if (y_delta < h)
5164 dest_info->insert_pos = GTK_CMCLIST_DRAG_BEFORE;
5165 else if (clist->row_height - y_delta < h)
5166 dest_info->insert_pos = GTK_CMCLIST_DRAG_AFTER;
5167 }
5168 }
5169 }
5170
5171 static void
gtk_cmctree_drag_begin(GtkWidget * widget,GdkDragContext * context)5172 gtk_cmctree_drag_begin (GtkWidget *widget,
5173 GdkDragContext *context)
5174 {
5175 GtkCMCList *clist;
5176 gboolean use_icons;
5177
5178 cm_return_if_fail (GTK_IS_CMCTREE (widget));
5179 cm_return_if_fail (context != NULL);
5180
5181 clist = GTK_CMCLIST (widget);
5182
5183 use_icons = GTK_CMCLIST_USE_DRAG_ICONS (clist);
5184 GTK_CMCLIST_UNSET_FLAG (clist, CMCLIST_USE_DRAG_ICONS);
5185 GTK_WIDGET_CLASS (parent_class)->drag_begin (widget, context);
5186
5187 if (use_icons)
5188 {
5189 GTK_CMCLIST_SET_FLAG (clist, CMCLIST_USE_DRAG_ICONS);
5190 gtk_drag_set_icon_default (context);
5191 }
5192 }
5193
5194 static gint
gtk_cmctree_drag_motion(GtkWidget * widget,GdkDragContext * context,gint x,gint y,guint time)5195 gtk_cmctree_drag_motion (GtkWidget *widget,
5196 GdkDragContext *context,
5197 gint x,
5198 gint y,
5199 guint time)
5200 {
5201 GtkCMCList *clist;
5202 GtkCMCTree *ctree;
5203 GtkCMCListDestInfo new_info;
5204 GtkCMCListDestInfo *dest_info;
5205
5206 cm_return_val_if_fail (GTK_IS_CMCTREE (widget), FALSE);
5207
5208 clist = GTK_CMCLIST (widget);
5209 ctree = GTK_CMCTREE (widget);
5210
5211 dest_info = g_dataset_get_data (context, "gtk-clist-drag-dest");
5212
5213 if (!dest_info)
5214 {
5215 dest_info = g_new (GtkCMCListDestInfo, 1);
5216
5217 dest_info->cell.row = -1;
5218 dest_info->cell.column = -1;
5219 dest_info->insert_pos = GTK_CMCLIST_DRAG_NONE;
5220
5221 g_dataset_set_data_full (context, "gtk-clist-drag-dest", dest_info,
5222 drag_dest_info_destroy);
5223 }
5224
5225 drag_dest_cell (clist, x, y, &new_info);
5226
5227 if (GTK_CMCLIST_REORDERABLE (clist))
5228 {
5229 GdkAtom atom = gdk_atom_intern_static_string ("gtk-clist-drag-reorder");
5230 GdkAtom found = gtk_drag_dest_find_target(widget, context, NULL);
5231
5232 if (atom == found)
5233 {
5234 GtkCMCTreeNode *drag_source;
5235 GtkCMCTreeNode *drag_target;
5236
5237 drag_source = GTK_CMCTREE_NODE (g_list_nth (clist->row_list,
5238 clist->click_cell.row));
5239 drag_target = GTK_CMCTREE_NODE (g_list_nth (clist->row_list,
5240 new_info.cell.row));
5241
5242 if (gtk_drag_get_source_widget (context) != widget ||
5243 !check_drag (ctree, drag_source, drag_target,
5244 new_info.insert_pos))
5245 {
5246 if (dest_info->cell.row < 0)
5247 {
5248 gdk_drag_status (context, GDK_ACTION_DEFAULT, time);
5249 return FALSE;
5250 }
5251 return TRUE;
5252 }
5253
5254 if (new_info.cell.row != dest_info->cell.row ||
5255 (new_info.cell.row == dest_info->cell.row &&
5256 dest_info->insert_pos != new_info.insert_pos))
5257 {
5258 dest_info->insert_pos = new_info.insert_pos;
5259 dest_info->cell.row = new_info.cell.row;
5260 dest_info->cell.column = new_info.cell.column;
5261
5262 clist->drag_highlight_row = dest_info->cell.row;
5263 clist->drag_highlight_pos = dest_info->insert_pos;
5264
5265 gdk_drag_status (context,
5266 gdk_drag_context_get_suggested_action(context), time);
5267 }
5268 return TRUE;
5269 }
5270 }
5271
5272 dest_info->insert_pos = new_info.insert_pos;
5273 dest_info->cell.row = new_info.cell.row;
5274 dest_info->cell.column = new_info.cell.column;
5275 return TRUE;
5276 }
5277
5278 static void
gtk_cmctree_drag_data_received(GtkWidget * widget,GdkDragContext * context,gint x,gint y,GtkSelectionData * selection_data,guint info,guint32 time)5279 gtk_cmctree_drag_data_received (GtkWidget *widget,
5280 GdkDragContext *context,
5281 gint x,
5282 gint y,
5283 GtkSelectionData *selection_data,
5284 guint info,
5285 guint32 time)
5286 {
5287 GtkCMCTree *ctree;
5288 GtkCMCList *clist;
5289
5290 cm_return_if_fail (GTK_IS_CMCTREE (widget));
5291 cm_return_if_fail (context != NULL);
5292 cm_return_if_fail (selection_data != NULL);
5293
5294 ctree = GTK_CMCTREE (widget);
5295 clist = GTK_CMCLIST (widget);
5296
5297 if (GTK_CMCLIST_REORDERABLE (clist) &&
5298 gtk_drag_get_source_widget (context) == widget &&
5299 gtk_selection_data_get_target (selection_data) ==
5300 gdk_atom_intern_static_string ("gtk-clist-drag-reorder") &&
5301 gtk_selection_data_get_format (selection_data) == 8 &&
5302 gtk_selection_data_get_length (selection_data) == sizeof (GtkCMCListCellInfo))
5303 {
5304 GtkCMCListCellInfo *source_info;
5305
5306 source_info = (GtkCMCListCellInfo *)(gtk_selection_data_get_data (selection_data));
5307 if (source_info)
5308 {
5309 GtkCMCListDestInfo dest_info;
5310 GtkCMCTreeNode *source_node;
5311 GtkCMCTreeNode *dest_node;
5312
5313 drag_dest_cell (clist, x, y, &dest_info);
5314
5315 source_node = GTK_CMCTREE_NODE (g_list_nth (clist->row_list,
5316 source_info->row));
5317 dest_node = GTK_CMCTREE_NODE (g_list_nth (clist->row_list,
5318 dest_info.cell.row));
5319
5320 if (!source_node || !dest_node)
5321 return;
5322
5323 switch (dest_info.insert_pos)
5324 {
5325 case GTK_CMCLIST_DRAG_NONE:
5326 break;
5327 case GTK_CMCLIST_DRAG_INTO:
5328 if (check_drag (ctree, source_node, dest_node,
5329 dest_info.insert_pos))
5330 gtk_cmctree_move (ctree, source_node, dest_node,
5331 GTK_CMCTREE_ROW (dest_node)->children);
5332 g_dataset_remove_data (context, "gtk-clist-drag-dest");
5333 break;
5334 case GTK_CMCLIST_DRAG_BEFORE:
5335 if (check_drag (ctree, source_node, dest_node,
5336 dest_info.insert_pos))
5337 gtk_cmctree_move (ctree, source_node,
5338 GTK_CMCTREE_ROW (dest_node)->parent, dest_node);
5339 g_dataset_remove_data (context, "gtk-clist-drag-dest");
5340 break;
5341 case GTK_CMCLIST_DRAG_AFTER:
5342 if (check_drag (ctree, source_node, dest_node,
5343 dest_info.insert_pos))
5344 gtk_cmctree_move (ctree, source_node,
5345 GTK_CMCTREE_ROW (dest_node)->parent,
5346 GTK_CMCTREE_ROW (dest_node)->sibling);
5347 g_dataset_remove_data (context, "gtk-clist-drag-dest");
5348 break;
5349 }
5350 }
5351 }
5352 }
5353
5354 GType
gtk_cmctree_node_get_type(void)5355 gtk_cmctree_node_get_type (void)
5356 {
5357 static GType our_type = 0;
5358
5359 if (our_type == 0)
5360 our_type = g_pointer_type_register_static ("GtkCMCTreeNode");
5361
5362 return our_type;
5363 }
5364