1 /*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU Lesser General Public
4 * License as published by the Free Software Foundation; either
5 * version 2 of the License, or (at your option) version 3.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10 * Lesser General Public License for more details.
11 *
12 * You should have received a copy of the GNU Lesser General Public
13 * License along with the program; if not, see <http://www.gnu.org/licenses/>
14 *
15 *
16 * Authors:
17 * Chris Lahey <clahey@ximian.com>
18 *
19 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
20 *
21 */
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <gtk/gtk.h>
28 #include <gdk/gdkkeysyms.h>
29 #include <libgnomecanvas/libgnomecanvas.h>
30
31 #include "text/e-text.h"
32 #include <glib/gi18n.h>
33 #include "e-util/e-util.h"
34 #include "misc/e-canvas-utils.h"
35 #include "misc/e-canvas.h"
36 #include "e-util/e-unicode.h"
37
38 #include "e-table-defines.h"
39 #include "e-table-group-container.h"
40 #include "e-table-group-leaf.h"
41 #include "e-table-item.h"
42 #include "e-table-sorting-utils.h"
43
44 #define TITLE_HEIGHT 16
45
46 /* workaround for avoiding API breakage */
47 #define etgc_get_type e_table_group_container_get_type
48 G_DEFINE_TYPE (ETableGroupContainer, etgc, E_TYPE_TABLE_GROUP)
49
50 enum {
51 PROP_0,
52 PROP_HEIGHT,
53 PROP_WIDTH,
54 PROP_MINIMUM_WIDTH,
55 PROP_FROZEN,
56 PROP_TABLE_ALTERNATING_ROW_COLORS,
57 PROP_TABLE_HORIZONTAL_DRAW_GRID,
58 PROP_TABLE_VERTICAL_DRAW_GRID,
59 PROP_TABLE_DRAW_FOCUS,
60 PROP_CURSOR_MODE,
61 PROP_SELECTION_MODEL,
62 PROP_LENGTH_THRESHOLD,
63 PROP_UNIFORM_ROW_HEIGHT
64 };
65
66 static EPrintable *
67 etgc_get_printable (ETableGroup *etg);
68
69 static void
70 e_table_group_container_child_node_free (ETableGroupContainer *etgc,
71 ETableGroupContainerChildNode *child_node)
72 {
73 ETableGroup *etg = E_TABLE_GROUP (etgc);
74 ETableGroup *child = child_node->child;
75
76 g_object_run_dispose (G_OBJECT (child));
77 e_table_model_free_value (
78 etg->model, etgc->ecol->col_idx,
79 child_node->key);
80 g_free (child_node->string);
81 g_object_run_dispose (G_OBJECT (child_node->text));
82 g_object_run_dispose (G_OBJECT (child_node->rect));
83 }
84
85 static void
86 e_table_group_container_list_free (ETableGroupContainer *etgc)
87 {
88 ETableGroupContainerChildNode *child_node;
89 GList *list;
90
91 for (list = etgc->children; list; list = g_list_next (list)) {
92 child_node = (ETableGroupContainerChildNode *) list->data;
93 e_table_group_container_child_node_free (etgc, child_node);
94 }
95
96 g_list_free (etgc->children);
97 etgc->children = NULL;
98 }
99
100 static void
101 etgc_dispose (GObject *object)
102 {
103 ETableGroupContainer *etgc = E_TABLE_GROUP_CONTAINER (object);
104
105 if (etgc->children)
106 e_table_group_container_list_free (etgc);
107
108 if (etgc->font_desc)
109 pango_font_description_free (etgc->font_desc);
110 etgc->font_desc = NULL;
111
112 if (etgc->ecol)
113 g_object_unref (etgc->ecol);
114 etgc->ecol = NULL;
115
116 if (etgc->sort_info)
117 g_object_unref (etgc->sort_info);
118 etgc->sort_info = NULL;
119
120 if (etgc->selection_model)
121 g_object_unref (etgc->selection_model);
122 etgc->selection_model = NULL;
123
124 if (etgc->rect)
125 g_object_run_dispose (G_OBJECT (etgc->rect));
126 etgc->rect = NULL;
127
128 G_OBJECT_CLASS (etgc_parent_class)->dispose (object);
129 }
130
131 /**
132 * e_table_group_container_construct
133 * @parent: The %GnomeCanvasGroup to create a child of.
134 * @etgc: The %ETableGroupContainer.
135 * @full_header: The full header of the %ETable.
136 * @header: The current header of the %ETable.
137 * @model: The %ETableModel of the %ETable.
138 * @sort_info: The %ETableSortInfo of the %ETable.
139 * @n: Which grouping level this is (Starts at 0 and sends n + 1 to any child %ETableGroups.
140 *
141 * This routine constructs the new %ETableGroupContainer.
142 */
143 void
144 e_table_group_container_construct (GnomeCanvasGroup *parent,
145 ETableGroupContainer *etgc,
146 ETableHeader *full_header,
147 ETableHeader *header,
148 ETableModel *model,
149 ETableSortInfo *sort_info,
150 gint n)
151 {
152 ETableCol *col;
153 ETableSortColumn column = e_table_sort_info_grouping_get_nth (sort_info, n);
154 GtkWidget *widget;
155 GtkStyle *style;
156
157 col = e_table_header_get_column_by_col_idx (full_header, column.column);
158 if (col == NULL)
159 col = e_table_header_get_column (full_header, e_table_header_count (full_header) - 1);
160
161 e_table_group_construct (parent, E_TABLE_GROUP (etgc), full_header, header, model);
162 etgc->ecol = col;
163 g_object_ref (etgc->ecol);
164 etgc->sort_info = sort_info;
165 g_object_ref (etgc->sort_info);
166 etgc->n = n;
167 etgc->ascending = column.ascending;
168
169 widget = GTK_WIDGET (GNOME_CANVAS_ITEM (etgc)->canvas);
170 style = gtk_widget_get_style (widget);
171 etgc->font_desc = pango_font_description_copy (style->font_desc);
172
173 etgc->open = TRUE;
174 }
175
176 /**
177 * e_table_group_container_new
178 * @parent: The %GnomeCanvasGroup to create a child of.
179 * @full_header: The full header of the %ETable.
180 * @header: The current header of the %ETable.
181 * @model: The %ETableModel of the %ETable.
182 * @sort_info: The %ETableSortInfo of the %ETable.
183 * @n: Which grouping level this is (Starts at 0 and sends n + 1 to any child %ETableGroups.
184 *
185 * %ETableGroupContainer is an %ETableGroup which groups by the nth
186 * grouping of the %ETableSortInfo. It creates %ETableGroups as
187 * children.
188 *
189 * Returns: The new %ETableGroupContainer.
190 */
191 ETableGroup *
192 e_table_group_container_new (GnomeCanvasGroup *parent,
193 ETableHeader *full_header,
194 ETableHeader *header,
195 ETableModel *model,
196 ETableSortInfo *sort_info,
197 gint n)
198 {
199 ETableGroupContainer *etgc;
200
201 g_return_val_if_fail (parent != NULL, NULL);
202
203 etgc = g_object_new (E_TYPE_TABLE_GROUP_CONTAINER, NULL);
204
205 e_table_group_container_construct (
206 parent, etgc, full_header, header,
207 model, sort_info, n);
208 return E_TABLE_GROUP (etgc);
209 }
210
211 static gint
212 etgc_event (GnomeCanvasItem *item,
213 GdkEvent *event)
214 {
215 ETableGroupContainer *etgc = E_TABLE_GROUP_CONTAINER (item);
216 gboolean return_val = TRUE;
217 gboolean change_focus = FALSE;
218 gboolean use_col = FALSE;
219 gint start_col = 0;
220 gint old_col;
221 EFocus direction = E_FOCUS_START;
222
223 switch (event->type) {
224 case GDK_KEY_PRESS:
225 if (event->key.keyval == GDK_KEY_Tab ||
226 event->key.keyval == GDK_KEY_KP_Tab ||
227 event->key.keyval == GDK_KEY_ISO_Left_Tab) {
228 change_focus = TRUE;
229 use_col = TRUE;
230 start_col = (event->key.state & GDK_SHIFT_MASK) ? -1 : 0;
231 direction = (event->key.state & GDK_SHIFT_MASK) ? E_FOCUS_END : E_FOCUS_START;
232 } else if (event->key.keyval == GDK_KEY_Left ||
233 event->key.keyval == GDK_KEY_KP_Left) {
234 change_focus = TRUE;
235 use_col = TRUE;
236 start_col = -1;
237 direction = E_FOCUS_END;
238 } else if (event->key.keyval == GDK_KEY_Right ||
239 event->key.keyval == GDK_KEY_KP_Right) {
240 change_focus = TRUE;
241 use_col = TRUE;
242 start_col = 0;
243 direction = E_FOCUS_START;
244 } else if (event->key.keyval == GDK_KEY_Down ||
245 event->key.keyval == GDK_KEY_KP_Down) {
246 change_focus = TRUE;
247 use_col = FALSE;
248 direction = E_FOCUS_START;
249 } else if (event->key.keyval == GDK_KEY_Up ||
250 event->key.keyval == GDK_KEY_KP_Up) {
251 change_focus = TRUE;
252 use_col = FALSE;
253 direction = E_FOCUS_END;
254 } else if (event->key.keyval == GDK_KEY_Return ||
255 event->key.keyval == GDK_KEY_KP_Enter) {
256 change_focus = TRUE;
257 use_col = FALSE;
258 direction = E_FOCUS_START;
259 }
260 if (change_focus) {
261 GList *list;
262 for (list = etgc->children; list; list = list->next) {
263 ETableGroupContainerChildNode *child_node;
264 ETableGroup *child;
265
266 child_node = (ETableGroupContainerChildNode *) list->data;
267 child = child_node->child;
268
269 if (e_table_group_get_focus (child)) {
270 old_col = e_table_group_get_focus_column (child);
271 if (old_col == -1)
272 old_col = 0;
273 if (start_col == -1)
274 start_col = e_table_header_count (e_table_group_get_header (child)) - 1;
275
276 if (direction == E_FOCUS_END)
277 list = list->prev;
278 else
279 list = list->next;
280
281 if (list) {
282 child_node = (ETableGroupContainerChildNode *) list->data;
283 child = child_node->child;
284 if (use_col)
285 e_table_group_set_focus (child, direction, start_col);
286 else
287 e_table_group_set_focus (child, direction, old_col);
288 return 1;
289 } else {
290 return 0;
291 }
292 }
293 }
294 if (direction == E_FOCUS_END)
295 list = g_list_last (etgc->children);
296 else
297 list = etgc->children;
298 if (list) {
299 ETableGroupContainerChildNode *child_node;
300 ETableGroup *child;
301
302 child_node = (ETableGroupContainerChildNode *) list->data;
303 child = child_node->child;
304
305 if (start_col == -1)
306 start_col = e_table_header_count (e_table_group_get_header (child)) - 1;
307
308 e_table_group_set_focus (child, direction, start_col);
309 return 1;
310 }
311 }
312 return_val = FALSE;
313 break;
314 default:
315 return_val = FALSE;
316 break;
317 }
318 if (return_val == FALSE) {
319 if (GNOME_CANVAS_ITEM_CLASS (etgc_parent_class)->event)
320 return GNOME_CANVAS_ITEM_CLASS (etgc_parent_class)->event (item, event);
321 }
322 return return_val;
323
324 }
325
326 /* Realize handler for the text item */
327 static void
328 etgc_realize (GnomeCanvasItem *item)
329 {
330 ETableGroupContainer *etgc;
331
332 if (GNOME_CANVAS_ITEM_CLASS (etgc_parent_class)->realize)
333 (* GNOME_CANVAS_ITEM_CLASS (etgc_parent_class)->realize) (item);
334
335 etgc = E_TABLE_GROUP_CONTAINER (item);
336
337 e_canvas_item_request_reflow (GNOME_CANVAS_ITEM (etgc));
338 }
339
340 /* Unrealize handler for the etgc item */
341 static void
342 etgc_unrealize (GnomeCanvasItem *item)
343 {
344 if (GNOME_CANVAS_ITEM_CLASS (etgc_parent_class)->unrealize)
345 (* GNOME_CANVAS_ITEM_CLASS (etgc_parent_class)->unrealize) (item);
346 }
347
348 static void
349 compute_text (ETableGroupContainer *etgc,
350 ETableGroupContainerChildNode *child_node)
351 {
352 gchar *text;
353
354 if (etgc->ecol->text) {
355 /* Translators: This text is used as a special row when an ETable
356 * has turned on grouping on a column, which has set a title.
357 * The first %s is replaced with a column title.
358 * The second %s is replaced with an actual group value.
359 * Finally the %d is replaced with count of items in this group.
360 * Example: "Family name: Smith (13 items)"
361 */
362 text = g_strdup_printf (
363 ngettext (
364 "%s: %s (%d item)",
365 "%s: %s (%d items)",
366 child_node->count),
367 etgc->ecol->text, child_node->string,
368 (gint) child_node->count);
369 } else {
370 /* Translators: This text is used as a special row when an ETable
371 * has turned on grouping on a column, which doesn't have set a title.
372 * The %s is replaced with an actual group value.
373 * The %d is replaced with count of items in this group.
374 * Example: "Smith (13 items)"
375 */
376 text = g_strdup_printf (
377 ngettext (
378 "%s (%d item)",
379 "%s (%d items)",
380 child_node->count),
381 child_node->string,
382 (gint) child_node->count);
383 }
384 gnome_canvas_item_set (
385 child_node->text,
386 "text", text,
387 NULL);
388 g_free (text);
389 }
390
391 static void
392 child_cursor_change (ETableGroup *etg,
393 gint row,
394 ETableGroupContainer *etgc)
395 {
396 e_table_group_cursor_change (E_TABLE_GROUP (etgc), row);
397 }
398
399 static void
400 child_cursor_activated (ETableGroup *etg,
401 gint row,
402 ETableGroupContainer *etgc)
403 {
404 e_table_group_cursor_activated (E_TABLE_GROUP (etgc), row);
405 }
406
407 static void
408 child_double_click (ETableGroup *etg,
409 gint row,
410 gint col,
411 GdkEvent *event,
412 ETableGroupContainer *etgc)
413 {
414 e_table_group_double_click (E_TABLE_GROUP (etgc), row, col, event);
415 }
416
417 static gboolean
418 child_right_click (ETableGroup *etg,
419 gint row,
420 gint col,
421 GdkEvent *event,
422 ETableGroupContainer *etgc)
423 {
424 return e_table_group_right_click (E_TABLE_GROUP (etgc), row, col, event);
425 }
426
427 static gboolean
428 child_click (ETableGroup *etg,
429 gint row,
430 gint col,
431 GdkEvent *event,
432 ETableGroupContainer *etgc)
433 {
434 return e_table_group_click (E_TABLE_GROUP (etgc), row, col, event);
435 }
436
437 static gboolean
438 child_key_press (ETableGroup *etg,
439 gint row,
440 gint col,
441 GdkEvent *event,
442 ETableGroupContainer *etgc)
443 {
444 return e_table_group_key_press (E_TABLE_GROUP (etgc), row, col, event);
445 }
446
447 static gboolean
448 child_start_drag (ETableGroup *etg,
449 gint row,
450 gint col,
451 GdkEvent *event,
452 ETableGroupContainer *etgc)
453 {
454 return e_table_group_start_drag (E_TABLE_GROUP (etgc), row, col, event);
455 }
456
457 static ETableGroupContainerChildNode *
458 create_child_node (ETableGroupContainer *etgc,
459 gpointer val)
460 {
461 ETableGroup *child;
462 ETableGroupContainerChildNode *child_node;
463 ETableGroup *etg = E_TABLE_GROUP (etgc);
464
465 child_node = g_new (ETableGroupContainerChildNode, 1);
466 child_node->rect = gnome_canvas_item_new (
467 GNOME_CANVAS_GROUP (etgc),
468 gnome_canvas_rect_get_type (),
469 "fill_color", "grey70",
470 "outline_color", "grey50",
471 NULL);
472 child_node->text = gnome_canvas_item_new (
473 GNOME_CANVAS_GROUP (etgc),
474 e_text_get_type (),
475 "fill_color", "black",
476 NULL);
477 child = e_table_group_new (
478 GNOME_CANVAS_GROUP (etgc), etg->full_header,
479 etg->header, etg->model, etgc->sort_info, etgc->n + 1);
480 gnome_canvas_item_set (
481 GNOME_CANVAS_ITEM (child),
482 "alternating_row_colors", etgc->alternating_row_colors,
483 "horizontal_draw_grid", etgc->horizontal_draw_grid,
484 "vertical_draw_grid", etgc->vertical_draw_grid,
485 "drawfocus", etgc->draw_focus,
486 "cursor_mode", etgc->cursor_mode,
487 "selection_model", etgc->selection_model,
488 "length_threshold", etgc->length_threshold,
489 "uniform_row_height", etgc->uniform_row_height,
490 "minimum_width", etgc->minimum_width - GROUP_INDENT,
491 NULL);
492
493 g_signal_connect (
494 child, "cursor_change",
495 G_CALLBACK (child_cursor_change), etgc);
496 g_signal_connect (
497 child, "cursor_activated",
498 G_CALLBACK (child_cursor_activated), etgc);
499 g_signal_connect (
500 child, "double_click",
501 G_CALLBACK (child_double_click), etgc);
502 g_signal_connect (
503 child, "right_click",
504 G_CALLBACK (child_right_click), etgc);
505 g_signal_connect (
506 child, "click",
507 G_CALLBACK (child_click), etgc);
508 g_signal_connect (
509 child, "key_press",
510 G_CALLBACK (child_key_press), etgc);
511 g_signal_connect (
512 child, "start_drag",
513 G_CALLBACK (child_start_drag), etgc);
514 child_node->child = child;
515 child_node->key = e_table_model_duplicate_value (etg->model, etgc->ecol->col_idx, val);
516 child_node->string = e_table_model_value_to_string (etg->model, etgc->ecol->col_idx, val);
517 child_node->count = 0;
518
519 return child_node;
520 }
521
522 static void
523 etgc_add (ETableGroup *etg,
524 gint row)
525 {
526 ETableGroupContainer *etgc = E_TABLE_GROUP_CONTAINER (etg);
527 gpointer val = e_table_model_value_at (etg->model, etgc->ecol->col_idx, row);
528 GCompareDataFunc comp = etgc->ecol->compare;
529 gpointer cmp_cache = e_table_sorting_utils_create_cmp_cache ();
530 GList *list = etgc->children;
531 ETableGroup *child;
532 ETableGroupContainerChildNode *child_node;
533 gint i = 0;
534
535 for (; list; list = g_list_next (list), i++) {
536 gint comp_val;
537
538 child_node = list->data;
539 comp_val = (*comp)(child_node->key, val, cmp_cache);
540 if (comp_val == 0) {
541 e_table_sorting_utils_free_cmp_cache (cmp_cache);
542 child = child_node->child;
543 child_node->count++;
544 e_table_group_add (child, row);
545 compute_text (etgc, child_node);
546 return;
547 }
548 if ((comp_val > 0 && etgc->ascending) ||
549 (comp_val < 0 && (!etgc->ascending)))
550 break;
551 }
552 e_table_sorting_utils_free_cmp_cache (cmp_cache);
553 child_node = create_child_node (etgc, val);
554 child = child_node->child;
555 child_node->count = 1;
556 e_table_group_add (child, row);
557
558 if (list)
559 etgc->children = g_list_insert (etgc->children, child_node, i);
560 else
561 etgc->children = g_list_append (etgc->children, child_node);
562
563 compute_text (etgc, child_node);
564 e_canvas_item_request_reflow (GNOME_CANVAS_ITEM (etgc));
565 }
566
567 static void
568 etgc_add_array (ETableGroup *etg,
569 const gint *array,
570 gint count)
571 {
572 gint i;
573 ETableGroupContainer *etgc = E_TABLE_GROUP_CONTAINER (etg);
574 gpointer lastval = NULL;
575 gint laststart = 0;
576 GCompareDataFunc comp = etgc->ecol->compare;
577 gpointer cmp_cache;
578 ETableGroupContainerChildNode *child_node;
579 ETableGroup *child;
580
581 if (count <= 0)
582 return;
583
584 e_table_group_container_list_free (etgc);
585 etgc->children = NULL;
586 cmp_cache = e_table_sorting_utils_create_cmp_cache ();
587
588 lastval = e_table_model_value_at (etg->model, etgc->ecol->col_idx, array[0]);
589
590 for (i = 1; i < count; i++) {
591 gpointer val = e_table_model_value_at (etg->model, etgc->ecol->col_idx, array[i]);
592 gint comp_val;
593
594 comp_val = (*comp)(lastval, val, cmp_cache);
595 if (comp_val != 0) {
596 child_node = create_child_node (etgc, lastval);
597 child = child_node->child;
598
599 e_table_group_add_array (child, array + laststart, i - laststart);
600 child_node->count = i - laststart;
601
602 etgc->children = g_list_append (etgc->children, child_node);
603 compute_text (etgc, child_node);
604 laststart = i;
605 lastval = val;
606 }
607 }
608
609 e_table_sorting_utils_free_cmp_cache (cmp_cache);
610
611 child_node = create_child_node (etgc, lastval);
612 child = child_node->child;
613
614 e_table_group_add_array (child, array + laststart, i - laststart);
615 child_node->count = i - laststart;
616
617 etgc->children = g_list_append (etgc->children, child_node);
618 compute_text (etgc, child_node);
619
620 e_canvas_item_request_reflow (GNOME_CANVAS_ITEM (etgc));
621 }
622
623 static void
624 etgc_add_all (ETableGroup *etg)
625 {
626 ETableGroupContainer *etgc = E_TABLE_GROUP_CONTAINER (etg);
627 ESorter *sorter = etgc->selection_model->sorter;
628 gint *array;
629 gint count;
630
631 e_sorter_get_sorted_to_model_array (sorter, &array, &count);
632
633 etgc_add_array (etg, array, count);
634 }
635
636 static gboolean
637 etgc_remove (ETableGroup *etg,
638 gint row)
639 {
640 ETableGroupContainer *etgc = E_TABLE_GROUP_CONTAINER (etg);
641 GList *list;
642
643 for (list = etgc->children; list; list = g_list_next (list)) {
644 ETableGroupContainerChildNode *child_node = list->data;
645 ETableGroup *child = child_node->child;
646
647 if (e_table_group_remove (child, row)) {
648 child_node->count--;
649 if (child_node->count == 0) {
650 e_table_group_container_child_node_free (etgc, child_node);
651 etgc->children = g_list_remove (etgc->children, child_node);
652 g_free (child_node);
653 } else
654 compute_text (etgc, child_node);
655
656 e_canvas_item_request_reflow (GNOME_CANVAS_ITEM (etgc));
657
658 return TRUE;
659 }
660 }
661 return FALSE;
662 }
663
664 static gint
665 etgc_row_count (ETableGroup *etg)
666 {
667 ETableGroupContainer *etgc = E_TABLE_GROUP_CONTAINER (etg);
668 GList *list;
669 gint count = 0;
670 for (list = etgc->children; list; list = g_list_next (list)) {
671 ETableGroup *group = ((ETableGroupContainerChildNode *) list->data)->child;
672 gint this_count = e_table_group_row_count (group);
673 count += this_count;
674 }
675 return count;
676 }
677
678 static void
679 etgc_increment (ETableGroup *etg,
680 gint position,
681 gint amount)
682 {
683 ETableGroupContainer *etgc = E_TABLE_GROUP_CONTAINER (etg);
684 GList *list;
685
686 for (list = etgc->children; list; list = g_list_next (list))
687 e_table_group_increment (
688 ((ETableGroupContainerChildNode *) list->data)->child,
689 position, amount);
690 }
691
692 static void
693 etgc_decrement (ETableGroup *etg,
694 gint position,
695 gint amount)
696 {
697 ETableGroupContainer *etgc = E_TABLE_GROUP_CONTAINER (etg);
698 GList *list;
699
700 for (list = etgc->children; list; list = g_list_next (list))
701 e_table_group_decrement (
702 ((ETableGroupContainerChildNode *) list->data)->child,
703 position, amount);
704 }
705
706 static void
707 etgc_set_focus (ETableGroup *etg,
708 EFocus direction,
709 gint view_col)
710 {
711 ETableGroupContainer *etgc = E_TABLE_GROUP_CONTAINER (etg);
712 if (etgc->children) {
713 if (direction == E_FOCUS_END)
714 e_table_group_set_focus (
715 ((ETableGroupContainerChildNode *) g_list_last (etgc->children)->data)->child,
716 direction, view_col);
717 else
718 e_table_group_set_focus (
719 ((ETableGroupContainerChildNode *) etgc->children->data)->child,
720 direction, view_col);
721 }
722 }
723
724 static gint
725 etgc_get_focus_column (ETableGroup *etg)
726 {
727 ETableGroupContainer *etgc = E_TABLE_GROUP_CONTAINER (etg);
728 if (etgc->children) {
729 GList *list;
730 for (list = etgc->children; list; list = list->next) {
731 ETableGroupContainerChildNode *child_node = (ETableGroupContainerChildNode *) list->data;
732 ETableGroup *child = child_node->child;
733 if (e_table_group_get_focus (child)) {
734 return e_table_group_get_focus_column (child);
735 }
736 }
737 }
738 return 0;
739 }
740
741 static void
742 etgc_compute_location (ETableGroup *etg,
743 gint *x,
744 gint *y,
745 gint *row,
746 gint *col)
747 {
748 ETableGroupContainer *etgc = E_TABLE_GROUP_CONTAINER (etg);
749
750 if (row)
751 *row = -1;
752 if (col)
753 *col = -1;
754
755 *x -= GROUP_INDENT;
756 *y -= TITLE_HEIGHT;
757
758 if (*x >= 0 && *y >= 0 && etgc->children) {
759 GList *list;
760 for (list = etgc->children; list; list = list->next) {
761 ETableGroupContainerChildNode *child_node = (ETableGroupContainerChildNode *) list->data;
762 ETableGroup *child = child_node->child;
763
764 e_table_group_compute_location (child, x, y, row, col);
765 if ((*row != -1) && (*col != -1))
(emitted by clang-analyzer)TODO: a detailed trace is available in the data model (not yet rendered in this report)
(emitted by clang-analyzer)TODO: a detailed trace is available in the data model (not yet rendered in this report)
(emitted by clang-analyzer)TODO: a detailed trace is available in the data model (not yet rendered in this report)
(emitted by clang-analyzer)TODO: a detailed trace is available in the data model (not yet rendered in this report)
766 return;
767 }
768 }
769 }
770
771 static void
772 etgc_get_mouse_over (ETableGroup *etg,
773 gint *row,
774 gint *col)
775 {
776 ETableGroupContainer *etgc = E_TABLE_GROUP_CONTAINER (etg);
777
778 if (row)
779 *row = -1;
780 if (col)
781 *col = -1;
782
783 if (etgc->children) {
784 gint row_plus = 0;
785 GList *list;
786
787 for (list = etgc->children; list; list = list->next) {
788 ETableGroupContainerChildNode *child_node = (ETableGroupContainerChildNode *) list->data;
789 ETableGroup *child = child_node->child;
790
791 e_table_group_get_mouse_over (child, row, col);
792
793 if ((!row || *row != -1) && (!col || *col != -1)) {
794 if (row)
795 *row += row_plus;
796 return;
797 }
798
799 row_plus += e_table_group_row_count (child);
800 }
801 }
802 }
803
804 static void
805 etgc_get_cell_geometry (ETableGroup *etg,
806 gint *row,
807 gint *col,
808 gint *x,
809 gint *y,
810 gint *width,
811 gint *height)
812 {
813 ETableGroupContainer *etgc = E_TABLE_GROUP_CONTAINER (etg);
814
815 gint ypos;
816
817 ypos = 0;
818
819 if (etgc->children) {
820 GList *list;
821 for (list = etgc->children; list; list = list->next) {
822 ETableGroupContainerChildNode *child_node = (ETableGroupContainerChildNode *) list->data;
823 ETableGroup *child = child_node->child;
824 gint thisy;
825
826 e_table_group_get_cell_geometry (child, row, col, x, &thisy, width, height);
827 ypos += thisy;
828 if ((*row == -1) || (*col == -1)) {
829 ypos += TITLE_HEIGHT;
830 *x += GROUP_INDENT;
831 *y = ypos;
832 return;
833 }
834 }
835 }
836 }
837
838 static void etgc_thaw (ETableGroup *etg)
839 {
840 e_canvas_item_request_reflow (GNOME_CANVAS_ITEM (etg));
841 }
842
843 static void
844 etgc_set_property (GObject *object,
845 guint property_id,
846 const GValue *value,
847 GParamSpec *pspec)
848 {
849 ETableGroup *etg = E_TABLE_GROUP (object);
850 ETableGroupContainer *etgc = E_TABLE_GROUP_CONTAINER (object);
851 GList *list;
852
853 switch (property_id) {
854 case PROP_FROZEN:
855 if (g_value_get_boolean (value))
856 etg->frozen = TRUE;
857 else {
858 etg->frozen = FALSE;
859 etgc_thaw (etg);
860 }
861 break;
862 case PROP_MINIMUM_WIDTH:
863 case PROP_WIDTH:
864 etgc->minimum_width = g_value_get_double (value);
865
866 for (list = etgc->children; list; list = g_list_next (list)) {
867 ETableGroupContainerChildNode *child_node = (ETableGroupContainerChildNode *) list->data;
868 g_object_set (
869 child_node->child,
870 "minimum_width", etgc->minimum_width - GROUP_INDENT,
871 NULL);
872 }
873 break;
874 case PROP_LENGTH_THRESHOLD:
875 etgc->length_threshold = g_value_get_int (value);
876 for (list = etgc->children; list; list = g_list_next (list)) {
877 ETableGroupContainerChildNode *child_node = (ETableGroupContainerChildNode *) list->data;
878 g_object_set (
879 child_node->child,
880 "length_threshold", etgc->length_threshold,
881 NULL);
882 }
883 break;
884 case PROP_UNIFORM_ROW_HEIGHT:
885 etgc->uniform_row_height = g_value_get_boolean (value);
886 for (list = etgc->children; list; list = g_list_next (list)) {
887 ETableGroupContainerChildNode *child_node = (ETableGroupContainerChildNode *) list->data;
888 g_object_set (
889 child_node->child,
890 "uniform_row_height", etgc->uniform_row_height,
891 NULL);
892 }
893 break;
894
895 case PROP_SELECTION_MODEL:
896 if (etgc->selection_model)
897 g_object_unref (etgc->selection_model);
898 etgc->selection_model = E_SELECTION_MODEL (g_value_get_object (value));
899 if (etgc->selection_model)
900 g_object_ref (etgc->selection_model);
901 for (list = etgc->children; list; list = g_list_next (list)) {
902 ETableGroupContainerChildNode *child_node = (ETableGroupContainerChildNode *) list->data;
903 g_object_set (
904 child_node->child,
905 "selection_model", etgc->selection_model,
906 NULL);
907 }
908 break;
909
910 case PROP_TABLE_ALTERNATING_ROW_COLORS:
911 etgc->alternating_row_colors = g_value_get_boolean (value);
912 for (list = etgc->children; list; list = g_list_next (list)) {
913 ETableGroupContainerChildNode *child_node = (ETableGroupContainerChildNode *) list->data;
914 g_object_set (
915 child_node->child,
916 "alternating_row_colors", etgc->alternating_row_colors,
917 NULL);
918 }
919 break;
920
921 case PROP_TABLE_HORIZONTAL_DRAW_GRID:
922 etgc->horizontal_draw_grid = g_value_get_boolean (value);
923 for (list = etgc->children; list; list = g_list_next (list)) {
924 ETableGroupContainerChildNode *child_node = (ETableGroupContainerChildNode *) list->data;
925 g_object_set (
926 child_node->child,
927 "horizontal_draw_grid", etgc->horizontal_draw_grid,
928 NULL);
929 }
930 break;
931
932 case PROP_TABLE_VERTICAL_DRAW_GRID:
933 etgc->vertical_draw_grid = g_value_get_boolean (value);
934 for (list = etgc->children; list; list = g_list_next (list)) {
935 ETableGroupContainerChildNode *child_node = (ETableGroupContainerChildNode *) list->data;
936 g_object_set (
937 child_node->child,
938 "vertical_draw_grid", etgc->vertical_draw_grid,
939 NULL);
940 }
941 break;
942
943 case PROP_TABLE_DRAW_FOCUS:
944 etgc->draw_focus = g_value_get_boolean (value);
945 for (list = etgc->children; list; list = g_list_next (list)) {
946 ETableGroupContainerChildNode *child_node = (ETableGroupContainerChildNode *) list->data;
947 g_object_set (
948 child_node->child,
949 "drawfocus", etgc->draw_focus,
950 NULL);
951 }
952 break;
953
954 case PROP_CURSOR_MODE:
955 etgc->cursor_mode = g_value_get_int (value);
956 for (list = etgc->children; list; list = g_list_next (list)) {
957 ETableGroupContainerChildNode *child_node = (ETableGroupContainerChildNode *) list->data;
958 g_object_set (
959 child_node->child,
960 "cursor_mode", etgc->cursor_mode,
961 NULL);
962 }
963 break;
964 default:
965 break;
966 }
967 }
968
969 static void
970 etgc_get_property (GObject *object,
971 guint property_id,
972 GValue *value,
973 GParamSpec *pspec)
974 {
975 ETableGroup *etg = E_TABLE_GROUP (object);
976 ETableGroupContainer *etgc = E_TABLE_GROUP_CONTAINER (object);
977
978 switch (property_id) {
979 case PROP_FROZEN:
980 g_value_set_boolean (value, etg->frozen);
981 break;
982 case PROP_HEIGHT:
983 g_value_set_double (value, etgc->height);
984 break;
985 case PROP_WIDTH:
986 g_value_set_double (value, etgc->width);
987 break;
988 case PROP_MINIMUM_WIDTH:
989 g_value_set_double (value, etgc->minimum_width);
990 break;
991 case PROP_UNIFORM_ROW_HEIGHT:
992 g_value_set_boolean (value, etgc->uniform_row_height);
993 break;
994 default:
995 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
996 break;
997 }
998 }
999
1000 static void
1001 etgc_class_init (ETableGroupContainerClass *class)
1002 {
1003 GnomeCanvasItemClass *item_class = GNOME_CANVAS_ITEM_CLASS (class);
1004 GObjectClass *object_class = G_OBJECT_CLASS (class);
1005 ETableGroupClass *e_group_class = E_TABLE_GROUP_CLASS (class);
1006
1007 object_class->dispose = etgc_dispose;
1008 object_class->set_property = etgc_set_property;
1009 object_class->get_property = etgc_get_property;
1010
1011 item_class->event = etgc_event;
1012 item_class->realize = etgc_realize;
1013 item_class->unrealize = etgc_unrealize;
1014
1015 e_group_class->add = etgc_add;
1016 e_group_class->add_array = etgc_add_array;
1017 e_group_class->add_all = etgc_add_all;
1018 e_group_class->remove = etgc_remove;
1019 e_group_class->increment = etgc_increment;
1020 e_group_class->decrement = etgc_decrement;
1021 e_group_class->row_count = etgc_row_count;
1022 e_group_class->set_focus = etgc_set_focus;
1023 e_group_class->get_focus_column = etgc_get_focus_column;
1024 e_group_class->get_printable = etgc_get_printable;
1025 e_group_class->compute_location = etgc_compute_location;
1026 e_group_class->get_mouse_over = etgc_get_mouse_over;
1027 e_group_class->get_cell_geometry = etgc_get_cell_geometry;
1028
1029 g_object_class_install_property (
1030 object_class,
1031 PROP_TABLE_ALTERNATING_ROW_COLORS,
1032 g_param_spec_boolean (
1033 "alternating_row_colors",
1034 "Alternating Row Colors",
1035 "Alternating Row Colors",
1036 FALSE,
1037 G_PARAM_WRITABLE));
1038
1039 g_object_class_install_property (
1040 object_class,
1041 PROP_TABLE_HORIZONTAL_DRAW_GRID,
1042 g_param_spec_boolean (
1043 "horizontal_draw_grid",
1044 "Horizontal Draw Grid",
1045 "Horizontal Draw Grid",
1046 FALSE,
1047 G_PARAM_WRITABLE));
1048
1049 g_object_class_install_property (
1050 object_class,
1051 PROP_TABLE_VERTICAL_DRAW_GRID,
1052 g_param_spec_boolean (
1053 "vertical_draw_grid",
1054 "Vertical Draw Grid",
1055 "Vertical Draw Grid",
1056 FALSE,
1057 G_PARAM_WRITABLE));
1058
1059 g_object_class_install_property (
1060 object_class,
1061 PROP_TABLE_DRAW_FOCUS,
1062 g_param_spec_boolean (
1063 "drawfocus",
1064 "Draw focus",
1065 "Draw focus",
1066 FALSE,
1067 G_PARAM_WRITABLE));
1068
1069 g_object_class_install_property (
1070 object_class,
1071 PROP_CURSOR_MODE,
1072 g_param_spec_int (
1073 "cursor_mode",
1074 "Cursor mode",
1075 "Cursor mode",
1076 E_CURSOR_LINE,
1077 E_CURSOR_SPREADSHEET,
1078 E_CURSOR_LINE,
1079 G_PARAM_WRITABLE));
1080
1081 g_object_class_install_property (
1082 object_class,
1083 PROP_SELECTION_MODEL,
1084 g_param_spec_object (
1085 "selection_model",
1086 "Selection model",
1087 "Selection model",
1088 E_TYPE_SELECTION_MODEL,
1089 G_PARAM_WRITABLE));
1090
1091 g_object_class_install_property (
1092 object_class,
1093 PROP_LENGTH_THRESHOLD,
1094 g_param_spec_int (
1095 "length_threshold",
1096 "Length Threshold",
1097 "Length Threshold",
1098 -1, G_MAXINT, 0,
1099 G_PARAM_READWRITE));
1100
1101 g_object_class_install_property (
1102 object_class,
1103 PROP_UNIFORM_ROW_HEIGHT,
1104 g_param_spec_boolean (
1105 "uniform_row_height",
1106 "Uniform row height",
1107 "Uniform row height",
1108 FALSE,
1109 G_PARAM_READWRITE));
1110
1111 g_object_class_install_property (
1112 object_class,
1113 PROP_FROZEN,
1114 g_param_spec_boolean (
1115 "frozen",
1116 "Frozen",
1117 "Frozen",
1118 FALSE,
1119 G_PARAM_READWRITE));
1120
1121 g_object_class_install_property (
1122 object_class,
1123 PROP_HEIGHT,
1124 g_param_spec_double (
1125 "height",
1126 "Height",
1127 "Height",
1128 0.0, G_MAXDOUBLE, 0.0,
1129 G_PARAM_READWRITE));
1130
1131 g_object_class_install_property (
1132 object_class,
1133 PROP_WIDTH,
1134 g_param_spec_double (
1135 "width",
1136 "Width",
1137 "Width",
1138 0.0, G_MAXDOUBLE, 0.0,
1139 G_PARAM_READWRITE));
1140
1141 g_object_class_install_property (
1142 object_class,
1143 PROP_MINIMUM_WIDTH,
1144 g_param_spec_double (
1145 "minimum_width",
1146 "Minimum width",
1147 "Minimum Width",
1148 0.0, G_MAXDOUBLE, 0.0,
1149 G_PARAM_READWRITE));
1150 }
1151
1152 static void
1153 etgc_reflow (GnomeCanvasItem *item,
1154 gint flags)
1155 {
1156 ETableGroupContainer *etgc = E_TABLE_GROUP_CONTAINER (item);
1157 gboolean frozen;
1158
1159 g_object_get (
1160 etgc,
1161 "frozen", &frozen,
1162 NULL);
1163
1164 if (frozen)
1165 return;
1166
1167 if (item->flags & GNOME_CANVAS_ITEM_REALIZED) {
1168 gdouble running_height = 0;
1169 gdouble running_width = 0;
1170 gdouble old_height;
1171 gdouble old_width;
1172
1173 old_height = etgc->height;
1174 old_width = etgc->width;
1175 if (etgc->children == NULL) {
1176 } else {
1177 GList *list;
1178 gdouble extra_height = 0;
1179 gdouble item_height = 0;
1180 gdouble item_width = 0;
1181
1182 if (etgc->font_desc) {
1183 PangoContext *context;
1184 PangoFontMetrics *metrics;
1185
1186 context = gtk_widget_get_pango_context (GTK_WIDGET (item->canvas));
1187 metrics = pango_context_get_metrics (context, etgc->font_desc, NULL);
1188 extra_height +=
1189 PANGO_PIXELS (pango_font_metrics_get_ascent (metrics)) +
1190 PANGO_PIXELS (pango_font_metrics_get_descent (metrics)) +
1191 BUTTON_PADDING * 2;
1192 pango_font_metrics_unref (metrics);
1193 }
1194
1195 extra_height = MAX (extra_height, BUTTON_HEIGHT + BUTTON_PADDING * 2);
1196
1197 running_height = extra_height;
1198
1199 for (list = etgc->children; list; list = g_list_next (list)) {
1200 ETableGroupContainerChildNode *child_node = (ETableGroupContainerChildNode *) list->data;
1201 ETableGroup *child = child_node->child;
1202
1203 g_object_get (
1204 child,
1205 "width", &item_width,
1206 NULL);
1207
1208 if (item_width > running_width)
1209 running_width = item_width;
1210 }
1211 for (list = etgc->children; list; list = g_list_next (list)) {
1212 ETableGroupContainerChildNode *child_node = (ETableGroupContainerChildNode *) list->data;
1213 ETableGroup *child = child_node->child;
1214 g_object_get (
1215 child,
1216 "height", &item_height,
1217 NULL);
1218
1219 e_canvas_item_move_absolute (
1220 GNOME_CANVAS_ITEM (child_node->text),
1221 GROUP_INDENT,
1222 running_height - GROUP_INDENT - BUTTON_PADDING);
1223
1224 e_canvas_item_move_absolute (
1225 GNOME_CANVAS_ITEM (child),
1226 GROUP_INDENT,
1227 running_height);
1228
1229 gnome_canvas_item_set (
1230 GNOME_CANVAS_ITEM (child_node->rect),
1231 "x1", (gdouble) 0,
1232 "x2", (gdouble) running_width + GROUP_INDENT,
1233 "y1", (gdouble) running_height - extra_height,
1234 "y2", (gdouble) running_height + item_height,
1235 NULL);
1236
1237 running_height += item_height + extra_height;
1238 }
1239 running_height -= extra_height;
1240 }
1241 if (running_height != old_height || running_width != old_width) {
1242 etgc->height = running_height;
1243 etgc->width = running_width;
1244 e_canvas_item_request_parent_reflow (item);
1245 }
1246 }
1247 }
1248
1249 static void
1250 etgc_init (ETableGroupContainer *container)
1251 {
1252 container->children = NULL;
1253
1254 e_canvas_item_set_reflow_callback (GNOME_CANVAS_ITEM (container), etgc_reflow);
1255
1256 container->alternating_row_colors = 1;
1257 container->horizontal_draw_grid = 1;
1258 container->vertical_draw_grid = 1;
1259 container->draw_focus = 1;
1260 container->cursor_mode = E_CURSOR_SIMPLE;
1261 container->length_threshold = -1;
1262 container->selection_model = NULL;
1263 container->uniform_row_height = FALSE;
1264 }
1265
1266 void
1267 e_table_group_apply_to_leafs (ETableGroup *etg,
1268 ETableGroupLeafFn fn,
1269 gpointer closure)
1270 {
1271 if (E_IS_TABLE_GROUP_CONTAINER (etg)) {
1272 ETableGroupContainer *etgc = E_TABLE_GROUP_CONTAINER (etg);
1273 GList *list;
1274
1275 /* Protect from unrefs in the callback functions */
1276 g_object_ref (etg);
1277
1278 for (list = etgc->children; list; list = list->next) {
1279 ETableGroupContainerChildNode *child_node = list->data;
1280
1281 e_table_group_apply_to_leafs (child_node->child, fn, closure);
1282 }
1283
1284 g_object_unref (etg);
1285 } else if (E_IS_TABLE_GROUP_LEAF (etg)) {
1286 (*fn) (E_TABLE_GROUP_LEAF (etg)->item, closure);
1287 } else {
1288 g_error (
(emitted by clang-analyzer)TODO: a detailed trace is available in the data model (not yet rendered in this report)
(emitted by clang-analyzer)TODO: a detailed trace is available in the data model (not yet rendered in this report)
1289 "Unknown ETableGroup found: %s",
1290 g_type_name (G_TYPE_FROM_INSTANCE (etg)));
1291 }
1292 }
1293
1294 typedef struct {
1295 ETableGroupContainer *etgc;
1296 GList *child;
1297 EPrintable *child_printable;
1298 } ETGCPrintContext;
1299
1300 #define CHECK(x) if((x) == -1) return -1;
1301
1302 #if 0
1303 static gint
1304 gp_draw_rect (GtkPrintContext *context,
1305 gdouble x,
1306 gdouble y,
1307 gdouble width,
1308 gdouble height)
1309 {
1310 cairo_t *cr;
1311 cr = gtk_print_context_get_cairo_context (context);
1312 cairo_move_to (cr, x, y);
1313 cairo_rectangle (cr, x, y, x + width, y + height);
1314 cairo_fill (cr);
1315 }
1316 #endif
1317
1318 #define TEXT_HEIGHT (12)
1319 #define TEXT_AREA_HEIGHT (TEXT_HEIGHT + 4)
1320
1321 static void
1322 e_table_group_container_print_page (EPrintable *ep,
1323 GtkPrintContext *context,
1324 gdouble width,
1325 gdouble height,
1326 gboolean quantize,
1327 ETGCPrintContext *groupcontext)
1328 {
1329 cairo_t *cr = NULL;
1330 GtkPageSetup *setup;
1331 gdouble yd;
1332 gdouble page_height, page_margin;
1333 gdouble child_height, child_margin = 0;
1334 ETableGroupContainerChildNode *child_node;
1335 GList *child;
1336 EPrintable *child_printable;
1337 gchar *string;
1338 PangoLayout *layout;
1339 PangoFontDescription *desc;
1340
1341 child_printable = groupcontext->child_printable;
1342 child = groupcontext->child;
1343 setup = gtk_print_context_get_page_setup (context);
1344 page_height = gtk_page_setup_get_page_height (setup, GTK_UNIT_POINTS);
1345 page_margin = gtk_page_setup_get_bottom_margin (setup, GTK_UNIT_POINTS) + gtk_page_setup_get_top_margin (setup, GTK_UNIT_POINTS);
1346 yd = page_height - page_margin;
1347
1348 if (child_printable) {
1349 if (child)
1350 child_node = child->data;
1351 else
1352 child_node = NULL;
1353 g_object_ref (child_printable);
1354 } else {
1355 if (!child) {
1356 return;
1357 } else {
1358 child_node = child->data;
1359 child_printable = e_table_group_get_printable (child_node->child);
1360 if (child_printable)
1361 g_object_ref (child_printable);
1362 e_printable_reset (child_printable);
1363 }
1364 }
1365
1366 layout = gtk_print_context_create_pango_layout (context);
1367
1368 desc = pango_font_description_new ();
1369 pango_font_description_set_family_static (desc, "Helvetica");
1370 pango_font_description_set_size (desc, TEXT_HEIGHT);
1371 pango_layout_set_font_description (layout, desc);
1372 pango_font_description_free (desc);
1373
1374 while (1) {
1375 child_height = e_printable_height (child_printable, context, width,yd, quantize);
1376 if (child_height < 0)
1377 child_height = -child_height;
1378 if (cr && yd < 2 * TEXT_AREA_HEIGHT + 20 + child_height) {
1379 cairo_show_page (cr);
1380 cairo_translate (cr, -2 * TEXT_AREA_HEIGHT, -TEXT_AREA_HEIGHT);
1381 break;
1382 }
1383
1384 cr = gtk_print_context_get_cairo_context (context);
1385 cairo_save (cr);
1386 cairo_rectangle (cr, 0.0, 0.0, width, TEXT_AREA_HEIGHT);
1387 cairo_rectangle (cr, 0.0, 0.0, 2 * TEXT_AREA_HEIGHT, child_height + 2 * TEXT_AREA_HEIGHT);
1388 cairo_set_source_rgb (cr, .7, .7, .7);
1389 cairo_fill (cr);
1390 cairo_restore (cr);
1391 child_margin = TEXT_AREA_HEIGHT;
1392
1393 cairo_save (cr);
1394 cairo_rectangle (cr, 2 * TEXT_AREA_HEIGHT, TEXT_AREA_HEIGHT, width - 2 * TEXT_AREA_HEIGHT, TEXT_AREA_HEIGHT);
1395 cairo_clip (cr);
1396 cairo_restore (cr);
1397
1398 cairo_move_to (cr, 0, 0);
1399 if (groupcontext->etgc->ecol->text)
1400 string = g_strdup_printf (
1401 "%s : %s (%d item%s)",
1402 groupcontext->etgc->ecol->text,
1403 child_node->string,
(emitted by clang-analyzer)TODO: a detailed trace is available in the data model (not yet rendered in this report)
(emitted by clang-analyzer)TODO: a detailed trace is available in the data model (not yet rendered in this report)
1404 (gint) child_node->count,
1405 child_node->count == 1 ? "" : "s");
1406 else
1407 string = g_strdup_printf (
1408 "%s (%d item%s)",
1409 child_node->string,
(emitted by clang-analyzer)TODO: a detailed trace is available in the data model (not yet rendered in this report)
(emitted by clang-analyzer)TODO: a detailed trace is available in the data model (not yet rendered in this report)
1410 (gint) child_node->count,
1411 child_node->count == 1 ? "" : "s");
1412 pango_layout_set_text (layout, string, -1);
1413 pango_cairo_show_layout (cr, layout);
1414 g_free (string);
1415
1416 cairo_translate (cr, 2 * TEXT_AREA_HEIGHT, TEXT_AREA_HEIGHT);
1417 cairo_move_to (cr, 0, 0);
1418 cairo_save (cr);
1419 cairo_rectangle (cr, 0, child_margin, width - 2 * TEXT_AREA_HEIGHT, child_height + child_margin + 20);
1420 cairo_clip (cr);
1421
1422 e_printable_print_page (child_printable, context, width - 2 * TEXT_AREA_HEIGHT, child_margin, quantize);
1423 yd -= child_height + TEXT_AREA_HEIGHT;
1424
1425 if (e_printable_data_left (child_printable)) {
1426 cairo_restore (cr);
1427 cairo_translate (cr, -2 * TEXT_AREA_HEIGHT, -TEXT_AREA_HEIGHT);
1428 break;
1429 }
1430
1431 child = child->next;
1432 if (!child) {
1433 child_printable = NULL;
1434 break;
1435 }
1436
1437 child_node = child->data;
1438 if (child_printable)
1439 g_object_unref (child_printable);
1440
1441 child_printable = e_table_group_get_printable (child_node->child);
1442 cairo_restore (cr);
1443 cairo_translate (cr, -2 * TEXT_AREA_HEIGHT, child_height + child_margin + 20);
1444
1445 if (child_printable)
1446 g_object_ref (child_printable);
1447 e_printable_reset (child_printable);
1448 }
1449 if (groupcontext->child_printable)
1450 g_object_unref (groupcontext->child_printable);
1451 groupcontext->child_printable = child_printable;
1452 groupcontext->child = child;
1453
1454 g_object_unref (layout);
1455 }
1456
1457 static gboolean
1458 e_table_group_container_data_left (EPrintable *ep,
1459 ETGCPrintContext *groupcontext)
1460 {
1461 g_signal_stop_emission_by_name (ep, "data_left");
1462 return groupcontext->child != NULL;
1463 }
1464
1465 static void
1466 e_table_group_container_reset (EPrintable *ep,
1467 ETGCPrintContext *groupcontext)
1468 {
1469 groupcontext->child = groupcontext->etgc->children;
1470 if (groupcontext->child_printable)
1471 g_object_unref (groupcontext->child_printable);
1472 groupcontext->child_printable = NULL;
1473 }
1474
1475 static gdouble
1476 e_table_group_container_height (EPrintable *ep,
1477 GtkPrintContext *context,
1478 gdouble width,
1479 gdouble max_height,
1480 gboolean quantize,
1481 ETGCPrintContext *groupcontext)
1482 {
1483 gdouble height = 0;
1484 gdouble child_height;
1485 gdouble yd = max_height;
1486 ETableGroupContainerChildNode *child_node;
1487 GList *child;
1488 EPrintable *child_printable;
1489
1490 child_printable = groupcontext->child_printable;
1491 child = groupcontext->child;
1492
1493 if (child_printable)
1494 g_object_ref (child_printable);
1495 else {
1496 if (!child) {
1497 g_signal_stop_emission_by_name (ep, "height");
1498 return 0;
1499 } else {
1500 child_node = child->data;
1501 child_printable = e_table_group_get_printable (child_node->child);
1502 if (child_printable)
1503 g_object_ref (child_printable);
1504 e_printable_reset (child_printable);
1505 }
1506 }
1507
1508 if (yd != -1 && yd < TEXT_AREA_HEIGHT)
1509 return 0;
1510
1511 while (1) {
1512 child_height = e_printable_height (child_printable, context, width - 36, yd - (yd == -1 ? 0 : TEXT_AREA_HEIGHT), quantize);
1513
1514 height -= child_height + TEXT_AREA_HEIGHT;
1515
1516 if (yd != -1) {
1517 if (!e_printable_will_fit (child_printable, context, width - 36, yd - (yd == -1 ? 0 : TEXT_AREA_HEIGHT), quantize)) {
1518 break;
1519 }
1520
1521 yd += child_height + TEXT_AREA_HEIGHT;
1522 }
1523
1524 child = child->next;
1525 if (!child) {
1526 break;
1527 }
1528
1529 child_node = child->data;
1530 if (child_printable)
1531 g_object_unref (child_printable);
1532 child_printable = e_table_group_get_printable (child_node->child);
1533 if (child_printable)
1534 g_object_ref (child_printable);
1535 e_printable_reset (child_printable);
1536 }
1537 if (child_printable)
1538 g_object_unref (child_printable);
1539 g_signal_stop_emission_by_name (ep, "height");
1540 return height;
1541 }
1542
1543 static gboolean
1544 e_table_group_container_will_fit (EPrintable *ep,
1545 GtkPrintContext *context,
1546 gdouble width,
1547 gdouble max_height,
1548 gboolean quantize,
1549 ETGCPrintContext *groupcontext)
1550 {
1551 gboolean will_fit = TRUE;
1552 gdouble child_height;
1553 gdouble yd = max_height;
1554 ETableGroupContainerChildNode *child_node;
1555 GList *child;
1556 EPrintable *child_printable;
1557
1558 child_printable = groupcontext->child_printable;
1559 child = groupcontext->child;
1560
1561 if (child_printable)
1562 g_object_ref (child_printable);
1563 else {
1564 if (!child) {
1565 g_signal_stop_emission_by_name (ep, "will_fit");
1566 return will_fit;
1567 } else {
1568 child_node = child->data;
1569 child_printable = e_table_group_get_printable (child_node->child);
1570 if (child_printable)
1571 g_object_ref (child_printable);
1572 e_printable_reset (child_printable);
1573 }
1574 }
1575
1576 if (yd != -1 && yd < TEXT_AREA_HEIGHT)
1577 will_fit = FALSE;
1578 else {
1579 while (1) {
1580 child_height = e_printable_height (child_printable, context, width - 36, yd - (yd == -1 ? 0 : TEXT_AREA_HEIGHT), quantize);
1581
1582 if (yd != -1) {
1583 if (!e_printable_will_fit (child_printable, context, width - 36, yd - (yd == -1 ? 0 : TEXT_AREA_HEIGHT), quantize)) {
1584 will_fit = FALSE;
1585 break;
1586 }
1587
1588 yd += child_height + TEXT_AREA_HEIGHT;
1589 }
1590
1591 child = child->next;
1592 if (!child) {
1593 break;
1594 }
1595
1596 child_node = child->data;
1597 if (child_printable)
1598 g_object_unref (child_printable);
1599 child_printable = e_table_group_get_printable (child_node->child);
1600 if (child_printable)
1601 g_object_ref (child_printable);
1602 e_printable_reset (child_printable);
1603 }
1604 }
1605
1606 if (child_printable)
1607 g_object_unref (child_printable);
1608
1609 g_signal_stop_emission_by_name (ep, "will_fit");
1610 return will_fit;
1611 }
1612
1613 static void
1614 e_table_group_container_printable_destroy (gpointer data,
1615 GObject *where_object_was)
1616
1617 {
1618 ETGCPrintContext *groupcontext = data;
1619
1620 g_object_unref (groupcontext->etgc);
1621 if (groupcontext->child_printable)
1622 g_object_ref (groupcontext->child_printable);
1623 g_free (groupcontext);
1624 }
1625
1626 static EPrintable *
1627 etgc_get_printable (ETableGroup *etg)
1628 {
1629 ETableGroupContainer *etgc = E_TABLE_GROUP_CONTAINER (etg);
1630 EPrintable *printable = e_printable_new ();
1631 ETGCPrintContext *groupcontext;
1632
1633 groupcontext = g_new (ETGCPrintContext, 1);
1634 groupcontext->etgc = etgc;
1635 g_object_ref (etgc);
1636 groupcontext->child = etgc->children;
1637 groupcontext->child_printable = NULL;
1638
1639 g_signal_connect (
1640 printable, "print_page",
1641 G_CALLBACK (e_table_group_container_print_page),
1642 groupcontext);
1643 g_signal_connect (
1644 printable, "data_left",
1645 G_CALLBACK (e_table_group_container_data_left),
1646 groupcontext);
1647 g_signal_connect (
1648 printable, "reset",
1649 G_CALLBACK (e_table_group_container_reset),
1650 groupcontext);
1651 g_signal_connect (
1652 printable, "height",
1653 G_CALLBACK (e_table_group_container_height),
1654 groupcontext);
1655 g_signal_connect (
1656 printable, "will_fit",
1657 G_CALLBACK (e_table_group_container_will_fit),
1658 groupcontext);
1659 g_object_weak_ref (
1660 G_OBJECT (printable),
1661 e_table_group_container_printable_destroy,
1662 groupcontext);
1663
1664 return printable;
1665 }