1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * e-cell-tree.c - Tree cell object.
4 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
5 *
6 * Authors:
7 * Chris Toshok <toshok@ximian.com>
8 *
9 * A majority of code taken from:
10 *
11 * the ECellText renderer.
12 * Copyright 1998, The Free Software Foundation
13 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
14 *
15 * This library is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU Library General Public License as
17 * published by the Free Software Foundation; either version 2 of the
18 * License, or (at your option) any later version.
19 *
20 * This library is distributed in the hope that it will be useful, but
21 * WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23 * Library General Public License for more details.
24 *
25 * You should have received a copy of the GNU Library General Public
26 * License along with this library; if not, write to the Free Software
27 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
28 * 02110-1301, USA.
29 */
30
31 #ifdef HAVE_CONFIG_H
32 #include <config.h>
33 #endif
34
35 #include <ctype.h>
36 #include <math.h>
37 #include <stdio.h>
38
39 #include <gdk/gdkkeysyms.h>
40 #include <gtk/gtk.h>
41 #include <libgnomecanvas/libgnomecanvas.h>
42
43 #include "gal-a11y-e-cell-registry.h"
44 #include "gal-a11y-e-cell-tree.h"
45 #include "e-util/e-util.h"
46
47 #include "e-cell-tree.h"
48 #include "e-table-item.h"
49 #include "e-tree.h"
50 #include "e-tree-model.h"
51 #include "e-tree-table-adapter.h"
52
53 G_DEFINE_TYPE (ECellTree, e_cell_tree, E_TYPE_CELL)
54
55 typedef struct {
56 ECellView cell_view;
57 ECellView *subcell_view;
58
59 GnomeCanvas *canvas;
60 gboolean prelit;
61 gint animate_timeout;
62
63 } ECellTreeView;
64
65 #define INDENT_AMOUNT 16
66
67 ECellView *
68 e_cell_tree_view_get_subcell_view (ECellView *ect)
69 {
70 return ((ECellTreeView *) ect)->subcell_view;
71 }
72
73 static ETreePath
74 e_cell_tree_get_node (ETableModel *table_model,
75 gint row)
76 {
77 return e_table_model_value_at (table_model, -1, row);
78 }
79
80 static ETreeModel *
81 e_cell_tree_get_tree_model (ETableModel *table_model,
82 gint row)
83 {
84 return e_table_model_value_at (table_model, -2, row);
85 }
86
87 static ETreeTableAdapter *
88 e_cell_tree_get_tree_table_adapter (ETableModel *table_model,
89 gint row)
90 {
91 return e_table_model_value_at (table_model, -3, row);
92 }
93
94 static gint
95 visible_depth_of_node (ETableModel *model,
96 gint row)
97 {
98 ETreeModel *tree_model = e_cell_tree_get_tree_model (model, row);
99 ETreeTableAdapter *adapter = e_cell_tree_get_tree_table_adapter (model, row);
100 ETreePath path = e_cell_tree_get_node (model, row);
101 return (e_tree_model_node_depth (tree_model, path)
102 - (e_tree_table_adapter_root_node_is_visible (adapter) ? 0 : 1));
103 }
104
105 /* If this is changed to not include the width of the expansion pixmap
106 * if the path is not expandable, then max_width needs to change as
107 * well. */
108 static gint
109 offset_of_node (ETableModel *table_model,
110 gint row)
111 {
112 ETreeModel *tree_model = e_cell_tree_get_tree_model (table_model, row);
113 ETreePath path = e_cell_tree_get_node (table_model, row);
114
115 if (visible_depth_of_node (table_model, row) >= 0 ||
116 e_tree_model_node_is_expandable (tree_model, path)) {
117 return (visible_depth_of_node (table_model, row) + 1) * INDENT_AMOUNT;
118 } else {
119 return 0;
120 }
121 }
122
123 /*
124 * ECell::new_view method
125 */
126 static ECellView *
127 ect_new_view (ECell *ecell,
128 ETableModel *table_model,
129 gpointer e_table_item_view)
130 {
131 ECellTree *ect = E_CELL_TREE (ecell);
132 ECellTreeView *tree_view = g_new0 (ECellTreeView, 1);
133 GnomeCanvas *canvas = GNOME_CANVAS_ITEM (e_table_item_view)->canvas;
134
135 tree_view->cell_view.ecell = ecell;
136 tree_view->cell_view.e_table_model = table_model;
137 tree_view->cell_view.e_table_item_view = e_table_item_view;
138 tree_view->cell_view.kill_view_cb = NULL;
139 tree_view->cell_view.kill_view_cb_data = NULL;
140
141 /* create our subcell view */
142 tree_view->subcell_view = e_cell_new_view (ect->subcell, table_model, e_table_item_view /* XXX */);
143
144 tree_view->canvas = canvas;
145
146 return (ECellView *) tree_view;
147 }
148
149 /*
150 * ECell::kill_view method
151 */
152 static void
153 ect_kill_view (ECellView *ecv)
154 {
155 ECellTreeView *tree_view = (ECellTreeView *) ecv;
156
157 if (tree_view->cell_view.kill_view_cb)
158 (tree_view->cell_view.kill_view_cb)(ecv, tree_view->cell_view.kill_view_cb_data);
159
160 if (tree_view->cell_view.kill_view_cb_data)
161 g_list_free (tree_view->cell_view.kill_view_cb_data);
162
163 /* kill our subcell view */
164 e_cell_kill_view (tree_view->subcell_view);
165
166 g_free (tree_view);
167 }
168
169 /*
170 * ECell::realize method
171 */
172 static void
173 ect_realize (ECellView *ecell_view)
174 {
175 ECellTreeView *tree_view = (ECellTreeView *) ecell_view;
176
177 /* realize our subcell view */
178 e_cell_realize (tree_view->subcell_view);
179
180 if (E_CELL_CLASS (e_cell_tree_parent_class)->realize)
181 (* E_CELL_CLASS (e_cell_tree_parent_class)->realize) (ecell_view);
182 }
183
184 /*
185 * ECell::unrealize method
186 */
187 static void
188 ect_unrealize (ECellView *ecv)
189 {
190 ECellTreeView *tree_view = (ECellTreeView *) ecv;
191
192 /* unrealize our subcell view. */
193 e_cell_unrealize (tree_view->subcell_view);
194
195 if (E_CELL_CLASS (e_cell_tree_parent_class)->unrealize)
196 (* E_CELL_CLASS (e_cell_tree_parent_class)->unrealize) (ecv);
197 }
198
199 static void
200 draw_expander (ECellTreeView *ectv,
201 cairo_t *cr,
202 GtkExpanderStyle expander_style,
203 GtkStateType state,
204 GdkRectangle *rect)
205 {
206 GtkStyle *style;
207 GtkWidget *tree;
208 gint exp_size;
209
210 tree = gtk_widget_get_parent (GTK_WIDGET (ectv->canvas));
211 style = gtk_widget_get_style (tree);
212
213 gtk_widget_style_get (tree, "expander_size", &exp_size, NULL);
214
215 gtk_paint_expander (
216 style, cr, state, tree, "treeview",
217 rect->x + rect->width - exp_size / 2,
218 rect->y + rect->height / 2, expander_style);
219 }
220
221 /*
222 * ECell::draw method
223 */
224 static void
225 ect_draw (ECellView *ecell_view,
226 cairo_t *cr,
227 gint model_col,
228 gint view_col,
229 gint row,
230 ECellFlags flags,
231 gint x1,
232 gint y1,
233 gint x2,
234 gint y2)
235 {
236 ECellTreeView *tree_view = (ECellTreeView *) ecell_view;
237 ETreeModel *tree_model = e_cell_tree_get_tree_model (ecell_view->e_table_model, row);
238 ETreeTableAdapter *tree_table_adapter = e_cell_tree_get_tree_table_adapter (ecell_view->e_table_model, row);
239 ETreePath node;
240 GdkRectangle rect;
241 gint offset, subcell_offset;
242
243 cairo_save (cr);
244
245 /* only draw the tree effects if we're the active sort */
246 if (/* XXX */ TRUE) {
247 GdkPixbuf *node_image;
248 gint node_image_width = 0, node_image_height = 0;
249
250 tree_view->prelit = FALSE;
251
252 node = e_cell_tree_get_node (ecell_view->e_table_model, row);
253
254 offset = offset_of_node (ecell_view->e_table_model, row);
255 subcell_offset = offset;
256
257 node_image = e_tree_model_icon_at (tree_model, node);
258
259 if (node_image) {
260 node_image_width = gdk_pixbuf_get_width (node_image);
261 node_image_height = gdk_pixbuf_get_height (node_image);
262 }
263
264 /*
265 * Be a nice citizen: clip to the region we are supposed to draw on
266 */
267 rect.x = x1;
268 rect.y = y1;
269 rect.width = subcell_offset + node_image_width;
270 rect.height = y2 - y1;
271
272 /* now draw our icon if we're expandable */
273 if (e_tree_model_node_is_expandable (tree_model, node)) {
274 gboolean expanded = e_tree_table_adapter_node_is_expanded (tree_table_adapter, node);
275 GdkRectangle r;
276
277 r = rect;
278 r.width -= node_image_width + 2;
279 draw_expander (tree_view, cr, expanded ? GTK_EXPANDER_EXPANDED : GTK_EXPANDER_COLLAPSED, GTK_STATE_NORMAL, &r);
280 }
281
282 if (node_image) {
283 gdk_cairo_set_source_pixbuf (
284 cr, node_image,
285 x1 + subcell_offset,
286 y1 + (y2 - y1) / 2 - node_image_height / 2);
287 cairo_paint (cr);
288
289 subcell_offset += node_image_width;
290 }
291 }
292
293 /* Now cause our subcell to draw its contents, shifted by
294 * subcell_offset pixels */
295 e_cell_draw (
296 tree_view->subcell_view, cr,
297 model_col, view_col, row, flags,
298 x1 + subcell_offset, y1, x2, y2);
Uninitialized variable: subcell_offset
(emitted by cppcheck)
Uninitialized variable: subcell_offset
(emitted by cppcheck)
299
300 cairo_restore (cr);
301 }
302
303 static void
304 adjust_event_position (GdkEvent *event,
305 gint offset)
306 {
307 switch (event->type) {
308 case GDK_BUTTON_PRESS:
309 case GDK_BUTTON_RELEASE:
310 case GDK_2BUTTON_PRESS:
311 case GDK_3BUTTON_PRESS:
312 event->button.x += offset;
313 break;
314 case GDK_MOTION_NOTIFY:
315 event->motion.x += offset;
316 break;
317 default:
318 break;
319 }
320 }
321
322 static gboolean
323 event_in_expander (GdkEvent *event,
324 gint offset,
325 gint height)
326 {
327 switch (event->type) {
328 case GDK_BUTTON_PRESS:
329 return (event->button.x > (offset - INDENT_AMOUNT) && event->button.x < offset);
330 case GDK_MOTION_NOTIFY:
331 return (event->motion.x > (offset - INDENT_AMOUNT) && event->motion.x < offset &&
332 event->motion.y > 2 && event->motion.y < (height - 2));
333 default:
334 break;
335 }
336
337 return FALSE;
338 }
339
340 /*
341 * ECell::height method
342 */
343 static gint
344 ect_height (ECellView *ecell_view,
345 gint model_col,
346 gint view_col,
347 gint row)
348 {
349 ECellTreeView *tree_view = (ECellTreeView *) ecell_view;
350
351 return (((e_cell_height (tree_view->subcell_view, model_col, view_col, row)) + 1) / 2) * 2;
352 }
353
354 typedef struct {
355 ECellTreeView *ectv;
356 ETreeTableAdapter *etta;
357 ETreePath node;
358 gboolean expanded;
359 gboolean finish;
360 GdkRectangle area;
361 } animate_closure_t;
362
363 static gboolean
364 animate_expander (gpointer data)
365 {
366 GtkLayout *layout;
367 GdkWindow *window;
368 animate_closure_t *closure = (animate_closure_t *) data;
369 cairo_t *cr;
370
371 if (closure->finish) {
372 e_tree_table_adapter_node_set_expanded (closure->etta, closure->node, !closure->expanded);
373 closure->ectv->animate_timeout = 0;
374 g_free (data);
375 return FALSE;
376 }
377
378 layout = GTK_LAYOUT (closure->ectv->canvas);
379 window = gtk_layout_get_bin_window (layout);
380
381 cr = gdk_cairo_create (window);
382
383 draw_expander (
384 closure->ectv, cr, closure->expanded ?
385 GTK_EXPANDER_SEMI_COLLAPSED :
386 GTK_EXPANDER_SEMI_EXPANDED,
387 GTK_STATE_NORMAL, &closure->area);
388 closure->finish = TRUE;
389
390 cairo_destroy (cr);
391
392 return TRUE;
393 }
394
395 /*
396 * ECell::event method
397 */
398 static gint
399 ect_event (ECellView *ecell_view,
400 GdkEvent *event,
401 gint model_col,
402 gint view_col,
403 gint row,
404 ECellFlags flags,
405 ECellActions *actions)
406 {
407 GtkLayout *layout;
408 GdkWindow *window;
409 ECellTreeView *tree_view = (ECellTreeView *) ecell_view;
410 ETreeModel *tree_model = e_cell_tree_get_tree_model (ecell_view->e_table_model, row);
411 ETreeTableAdapter *etta = e_cell_tree_get_tree_table_adapter (ecell_view->e_table_model, row);
412 ETreePath node = e_cell_tree_get_node (ecell_view->e_table_model, row);
413 gint offset = offset_of_node (ecell_view->e_table_model, row);
414 gint result;
415
416 layout = GTK_LAYOUT (tree_view->canvas);
417 window = gtk_layout_get_bin_window (layout);
418
419 switch (event->type) {
420 case GDK_BUTTON_PRESS:
421
422 if (event_in_expander (event, offset, 0)) {
423 if (e_tree_model_node_is_expandable (tree_model, node)) {
424 gboolean expanded = e_tree_table_adapter_node_is_expanded (etta, node);
425 gint tmp_row = row;
426 GdkRectangle area;
427 animate_closure_t *closure = g_new0 (animate_closure_t, 1);
428 cairo_t *cr;
429 gint hgt;
430
431 e_table_item_get_cell_geometry (
432 tree_view->cell_view.e_table_item_view,
433 &tmp_row, &view_col, &area.x, &area.y, NULL, &area.height);
434 area.width = offset - 2;
435 hgt = e_cell_height (ecell_view, model_col, view_col, row);
436
437 if (hgt != area.height) /* Composite cells */
438 area.height += hgt;
439
440 cr = gdk_cairo_create (window);
441 draw_expander (
442 tree_view, cr, expanded ?
443 GTK_EXPANDER_SEMI_EXPANDED :
444 GTK_EXPANDER_SEMI_COLLAPSED,
445 GTK_STATE_NORMAL, &area);
446 cairo_destroy (cr);
447
448 closure->ectv = tree_view;
449 closure->etta = etta;
450 closure->node = node;
451 closure->expanded = expanded;
452 closure->area = area;
453 tree_view->animate_timeout = g_timeout_add (50, animate_expander, closure);
454 return TRUE;
455 }
456 }
457 else if (event->button.x < (offset - INDENT_AMOUNT))
458 return FALSE;
459 break;
460
461 case GDK_MOTION_NOTIFY:
462
463 if (e_tree_model_node_is_expandable (tree_model, node)) {
464 gint height = ect_height (ecell_view, model_col, view_col, row);
465 GdkRectangle area;
466 gboolean in_expander = event_in_expander (event, offset, height);
467
468 if (tree_view->prelit ^ in_expander) {
469 gint tmp_row = row;
470 cairo_t *cr;
471
472 e_table_item_get_cell_geometry (
473 tree_view->cell_view.e_table_item_view,
474 &tmp_row, &view_col, &area.x, &area.y, NULL, &area.height);
475 area.width = offset - 2;
476
477 cr = gdk_cairo_create (window);
478 draw_expander (
479 tree_view, cr,
480 e_tree_table_adapter_node_is_expanded (etta, node) ?
481 GTK_EXPANDER_EXPANDED : GTK_EXPANDER_COLLAPSED,
482 in_expander ? GTK_STATE_PRELIGHT : GTK_STATE_NORMAL, &area);
483 cairo_destroy (cr);
484
485 tree_view->prelit = in_expander;
486 return TRUE;
487 }
488
489 }
490 break;
491
492 case GDK_LEAVE_NOTIFY:
493
494 if (tree_view->prelit) {
495 gint tmp_row = row;
496 GdkRectangle area;
497 cairo_t *cr;
498
499 e_table_item_get_cell_geometry (
500 tree_view->cell_view.e_table_item_view,
501 &tmp_row, &view_col, &area.x, &area.y, NULL, &area.height);
502 area.width = offset - 2;
503
504 cr = gdk_cairo_create (window);
505 draw_expander (
506 tree_view, cr,
507 e_tree_table_adapter_node_is_expanded (etta, node) ?
508 GTK_EXPANDER_EXPANDED : GTK_EXPANDER_COLLAPSED,
509 GTK_STATE_NORMAL, &area);
510 cairo_destroy (cr);
511
512 tree_view->prelit = FALSE;
513 }
514 return TRUE;
515
516 default:
517 break;
518 }
519
520 adjust_event_position (event, -offset);
521 result = e_cell_event (tree_view->subcell_view, event, model_col, view_col, row, flags, actions);
522 adjust_event_position (event, offset);
523
524 return result;
525 }
526
527 /*
528 * ECell::max_width method
529 */
530 static gint
531 ect_max_width (ECellView *ecell_view,
532 gint model_col,
533 gint view_col)
534 {
535 ECellTreeView *tree_view = (ECellTreeView *) ecell_view;
536 gint row;
537 gint number_of_rows;
538 gint max_width = 0;
539 gint width = 0;
540 gint subcell_max_width = 0;
541 gboolean per_row = e_cell_max_width_by_row_implemented (tree_view->subcell_view);
542
543 number_of_rows = e_table_model_row_count (ecell_view->e_table_model);
544
545 if (!per_row)
546 subcell_max_width = e_cell_max_width (tree_view->subcell_view, model_col, view_col);
547
548 for (row = 0; row < number_of_rows; row++) {
549 ETreeModel *tree_model = e_cell_tree_get_tree_model (ecell_view->e_table_model, row);
550 ETreePath node;
551 GdkPixbuf *node_image;
552 gint node_image_width = 0;
553
554 gint offset, subcell_offset;
555 #if 0
556 gboolean expanded, expandable;
557 ETreeTableAdapter *tree_table_adapter = e_cell_tree_get_tree_table_adapter (ecell_view->e_table_model, row);
558 #endif
559
560 node = e_cell_tree_get_node (ecell_view->e_table_model, row);
561
562 offset = offset_of_node (ecell_view->e_table_model, row);
563 subcell_offset = offset;
564
565 node_image = e_tree_model_icon_at (tree_model, node);
566
567 if (node_image) {
568 node_image_width = gdk_pixbuf_get_width (node_image);
569 }
570
571 width = subcell_offset + node_image_width;
572
573 if (per_row)
574 width += e_cell_max_width_by_row (tree_view->subcell_view, model_col, view_col, row);
575 else
576 width += subcell_max_width;
577
578 #if 0
579 expandable = e_tree_model_node_is_expandable (tree_model, node);
580 expanded = e_tree_table_adapter_node_is_expanded (tree_table_adapter, node);
581
582 /* This is unnecessary since this is already handled
583 * by the offset_of_node function. If that changes,
584 * this will have to change too. */
585
586 if (expandable) {
587 GdkPixbuf *image;
588
589 image = (expanded
590 ? E_CELL_TREE (tree_view->cell_view.ecell)->open_pixbuf
591 : E_CELL_TREE (tree_view->cell_view.ecell)->closed_pixbuf);
592
593 width += gdk_pixbuf_get_width (image);
594 }
595 #endif
596
597 max_width = MAX (max_width, width);
598 }
599
600 return max_width;
601 }
602
603 /*
604 * ECellView::get_bg_color method
605 */
606 static gchar *
607 ect_get_bg_color (ECellView *ecell_view,
608 gint row)
609 {
610 ECellTreeView *tree_view = (ECellTreeView *) ecell_view;
611
612 return e_cell_get_bg_color (tree_view->subcell_view, row);
613 }
614
615 /*
616 * ECellView::enter_edit method
617 */
618 static gpointer
619 ect_enter_edit (ECellView *ecell_view,
620 gint model_col,
621 gint view_col,
622 gint row)
623 {
624 /* just defer to our subcell's view */
625 ECellTreeView *tree_view = (ECellTreeView *) ecell_view;
626
627 return e_cell_enter_edit (tree_view->subcell_view, model_col, view_col, row);
628 }
629
630 /*
631 * ECellView::leave_edit method
632 */
633 static void
634 ect_leave_edit (ECellView *ecell_view,
635 gint model_col,
636 gint view_col,
637 gint row,
638 gpointer edit_context)
639 {
640 /* just defer to our subcell's view */
641 ECellTreeView *tree_view = (ECellTreeView *) ecell_view;
642
643 e_cell_leave_edit (tree_view->subcell_view, model_col, view_col, row, edit_context);
644 }
645
646 static void
647 ect_print (ECellView *ecell_view,
648 GtkPrintContext *context,
649 gint model_col,
650 gint view_col,
651 gint row,
652 gdouble width,
653 gdouble height)
654 {
655 ECellTreeView *tree_view = (ECellTreeView *) ecell_view;
656 cairo_t *cr = gtk_print_context_get_cairo_context (context);
657
658 cairo_save (cr);
659
660 if (/* XXX only if we're the active sort */ TRUE) {
661 ETreeModel *tree_model = e_cell_tree_get_tree_model (ecell_view->e_table_model, row);
662 ETreeTableAdapter *tree_table_adapter = e_cell_tree_get_tree_table_adapter (ecell_view->e_table_model, row);
663 ETreePath node = e_cell_tree_get_node (ecell_view->e_table_model, row);
664 gint offset = offset_of_node (ecell_view->e_table_model, row);
665 gint subcell_offset = offset;
666 gboolean expandable = e_tree_model_node_is_expandable (tree_model, node);
667
668 /* draw our lines */
669 if (E_CELL_TREE (tree_view->cell_view.ecell)->draw_lines) {
670 gint depth;
671
672 if (!e_tree_model_node_is_root (tree_model, node)
673 || e_tree_model_node_get_children (tree_model, node, NULL) > 0) {
674 cairo_move_to (
675 cr,
676 offset - INDENT_AMOUNT / 2,
677 height / 2);
678 cairo_line_to (cr, offset, height / 2);
679 }
680
681 if (visible_depth_of_node (ecell_view->e_table_model, row) != 0) {
682 cairo_move_to (
683 cr,
684 offset - INDENT_AMOUNT / 2, height);
685 cairo_line_to (
686 cr,
687 offset - INDENT_AMOUNT / 2,
688 e_tree_table_adapter_node_get_next
689 (tree_table_adapter, node) ? 0 :
690 height / 2);
691 }
692
693 /* now traverse back up to the root of the tree, checking at
694 * each level if the node has siblings, and drawing the
695 * correct vertical pipe for it's configuration. */
696 node = e_tree_model_node_get_parent (tree_model, node);
697 depth = visible_depth_of_node (ecell_view->e_table_model, row) - 1;
698 offset -= INDENT_AMOUNT;
699 while (node && depth != 0) {
700 if (e_tree_table_adapter_node_get_next (tree_table_adapter, node)) {
701 cairo_move_to (
702 cr,
703 offset - INDENT_AMOUNT / 2,
704 height);
705 cairo_line_to (
706 cr,
707 offset - INDENT_AMOUNT / 2, 0);
708 }
709 node = e_tree_model_node_get_parent (tree_model, node);
710 depth--;
711 offset -= INDENT_AMOUNT;
712 }
713 }
714
715 /* now draw our icon if we're expandable */
716 if (expandable) {
717 gboolean expanded;
718 GdkRectangle r;
719 gint exp_size = 0;
720
721 gtk_widget_style_get (GTK_WIDGET (gtk_widget_get_parent (GTK_WIDGET (tree_view->canvas))), "expander_size", &exp_size, NULL);
722
723 node = e_cell_tree_get_node (ecell_view->e_table_model, row);
724 expanded = e_tree_table_adapter_node_is_expanded (tree_table_adapter, node);
725
726 r.x = 0;
727 r.y = 0;
728 r.width = MIN (width, exp_size);
729 r.height = height;
730
731 draw_expander (tree_view, cr, expanded ? GTK_EXPANDER_EXPANDED : GTK_EXPANDER_COLLAPSED, GTK_STATE_NORMAL, &r);
732 }
733
734 cairo_stroke (cr);
735
736 cairo_translate (cr, subcell_offset, 0);
737 width -= subcell_offset;
738 }
739
740 cairo_restore (cr);
741
742 e_cell_print (tree_view->subcell_view, context, model_col, view_col, row, width, height);
743 }
744
745 static gdouble
746 ect_print_height (ECellView *ecell_view,
747 GtkPrintContext *context,
748 gint model_col,
749 gint view_col,
750 gint row,
751 gdouble width)
752 {
753 return 12; /* XXX */
754 }
755
756 /*
757 * GObject::dispose method
758 */
759 static void
760 ect_dispose (GObject *object)
761 {
762 ECellTree *ect = E_CELL_TREE (object);
763
764 /* destroy our subcell */
765 if (ect->subcell)
766 g_object_unref (ect->subcell);
767 ect->subcell = NULL;
768
769 G_OBJECT_CLASS (e_cell_tree_parent_class)->dispose (object);
770 }
771
772 static void
773 e_cell_tree_class_init (ECellTreeClass *class)
774 {
775 GObjectClass *object_class = G_OBJECT_CLASS (class);
776 ECellClass *ecc = E_CELL_CLASS (class);
777
778 object_class->dispose = ect_dispose;
779
780 ecc->new_view = ect_new_view;
781 ecc->kill_view = ect_kill_view;
782 ecc->realize = ect_realize;
783 ecc->unrealize = ect_unrealize;
784 ecc->draw = ect_draw;
785 ecc->event = ect_event;
786 ecc->height = ect_height;
787 ecc->enter_edit = ect_enter_edit;
788 ecc->leave_edit = ect_leave_edit;
789 ecc->print = ect_print;
790 ecc->print_height = ect_print_height;
791 ecc->max_width = ect_max_width;
792 ecc->get_bg_color = ect_get_bg_color;
793
794 gal_a11y_e_cell_registry_add_cell_type (NULL, E_TYPE_CELL_TREE, gal_a11y_e_cell_tree_new);
795 }
796
797 static void
798 e_cell_tree_init (ECellTree *ect)
799 {
800 /* nothing to do */
801 }
802
803 /**
804 * e_cell_tree_construct:
805 * @ect: the ECellTree we're constructing.
806 * @draw_lines: whether or not to draw the lines between parents/children/siblings.
807 * @subcell: the ECell to render to the right of the tree effects.
808 *
809 * Constructs an ECellTree. used by subclasses that need to
810 * initialize a nested ECellTree. See e_cell_tree_new() for more info.
811 *
812 **/
813 void
814 e_cell_tree_construct (ECellTree *ect,
815 gboolean draw_lines,
816 ECell *subcell)
817 {
818 ect->subcell = subcell;
819 if (subcell)
820 g_object_ref_sink (subcell);
821
822 ect->draw_lines = draw_lines;
823 }
824
825 /**
826 * e_cell_tree_new:
827 * @draw_lines: whether or not to draw the lines between parents/children/siblings.
828 * @subcell: the ECell to render to the right of the tree effects.
829 *
830 * Creates a new ECell renderer that can be used to render tree
831 * effects that come from an ETreeModel. Various assumptions are made
832 * as to the fact that the ETableModel the ETable this cell is
833 * associated with is in fact an ETreeModel. The cell uses special
834 * columns to get at structural information (needed to draw the
835 * lines/icons.
836 *
837 * Return value: an ECell object that can be used to render trees.
838 **/
839 ECell *
840 e_cell_tree_new (gboolean draw_lines,
841 ECell *subcell)
842 {
843 ECellTree *ect = g_object_new (E_TYPE_CELL_TREE, NULL);
844
845 e_cell_tree_construct (ect, draw_lines, subcell);
846
847 return (ECell *) ect;
848 }