1 /*
2 * This program is free software; you can redistribute it and/or modify it
3 * under the terms of the GNU Lesser General Public License as published by
4 * the Free Software Foundation.
5 *
6 * This program is distributed in the hope that it will be useful, but
7 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
8 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
9 * for more details.
10 *
11 * You should have received a copy of the GNU Lesser General Public License
12 * along with this program; if not, see <http://www.gnu.org/licenses/>.
13 *
14 *
15 * Authors:
16 * Chris Lahey <clahey@ximian.com>
17 *
18 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
19 *
20 */
21
22 #include "evolution-config.h"
23
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <string.h>
27
28 #include <gtk/gtk.h>
29 #include <glib/gi18n.h>
30 #include <gdk/gdkkeysyms.h>
31
32 #include <libgnomecanvas/libgnomecanvas.h>
33
34 #include "e-canvas-background.h"
35 #include "e-canvas-utils.h"
36 #include "e-canvas.h"
37 #include "e-cell-tree.h"
38 #include "e-table-column-specification.h"
39 #include "e-table-header-item.h"
40 #include "e-table-header.h"
41 #include "e-table-item.h"
42 #include "e-table-sort-info.h"
43 #include "e-table-utils.h"
44 #include "e-text.h"
45 #include "e-tree-selection-model.h"
46 #include "e-tree-table-adapter.h"
47 #include "e-tree.h"
48 #include "e-misc-utils.h"
49 #include "gal-a11y-e-tree.h"
50
51 #define COLUMN_HEADER_HEIGHT 16
52
53 #define d(x)
54
55 #define E_TREE_GET_PRIVATE(obj) \
56 (G_TYPE_INSTANCE_GET_PRIVATE \
57 ((obj), E_TYPE_TREE, ETreePrivate))
58
59 typedef struct _ETreeDragSourceSite ETreeDragSourceSite;
60
61 enum {
62 CURSOR_CHANGE,
63 CURSOR_ACTIVATED,
64 SELECTION_CHANGE,
65 DOUBLE_CLICK,
66 RIGHT_CLICK,
67 CLICK,
68 KEY_PRESS,
69 START_DRAG,
70 STATE_CHANGE,
71 WHITE_SPACE_EVENT,
72
73 CUT_CLIPBOARD,
74 COPY_CLIPBOARD,
75 PASTE_CLIPBOARD,
76 SELECT_ALL,
77
78 TREE_DRAG_BEGIN,
79 TREE_DRAG_END,
80 TREE_DRAG_DATA_GET,
81 TREE_DRAG_DATA_DELETE,
82
83 TREE_DRAG_LEAVE,
84 TREE_DRAG_MOTION,
85 TREE_DRAG_DROP,
86 TREE_DRAG_DATA_RECEIVED,
87
88 LAST_SIGNAL
89 };
90
91 enum {
92 PROP_0,
93 PROP_LENGTH_THRESHOLD,
94 PROP_HORIZONTAL_DRAW_GRID,
95 PROP_VERTICAL_DRAW_GRID,
96 PROP_DRAW_FOCUS,
97 PROP_ETTA,
98 PROP_UNIFORM_ROW_HEIGHT,
99 PROP_IS_EDITING,
100 PROP_ALWAYS_SEARCH,
101 PROP_HADJUSTMENT,
102 PROP_VADJUSTMENT,
103 PROP_HSCROLL_POLICY,
104 PROP_VSCROLL_POLICY,
105 PROP_SORT_CHILDREN_ASCENDING
106 };
107
108 enum {
109 ET_SCROLL_UP = 1 << 0,
110 ET_SCROLL_DOWN = 1 << 1,
111 ET_SCROLL_LEFT = 1 << 2,
112 ET_SCROLL_RIGHT = 1 << 3
113 };
114
115 struct _ETreePrivate {
116 ETreeModel *model;
117 ETreeTableAdapter *etta;
118
119 ETableHeader *full_header, *header;
120
121 guint structure_change_id, expansion_change_id;
122
123 ETableSortInfo *sort_info;
124
125 guint sort_info_change_id, group_info_change_id;
126
127 ESelectionModel *selection;
128 ETableSpecification *spec;
129
130 ETableSearch *search;
131
132 ETableCol *current_search_col;
133
134 guint search_search_id;
135 guint search_accept_id;
136
137 gint reflow_idle_id;
138 gint scroll_idle_id;
139 gint hover_idle_id;
140
141 gboolean show_cursor_after_reflow;
142
143 gint table_model_change_id;
144 gint table_row_change_id;
145 gint table_cell_change_id;
146 gint table_rows_delete_id;
147
148 GnomeCanvasItem *info_text;
149 guint info_text_resize_id;
150
151 GnomeCanvas *header_canvas, *table_canvas;
152
153 GnomeCanvasItem *header_item, *root;
154
155 GnomeCanvasItem *white_item;
156 GnomeCanvasItem *item;
157
158 gint length_threshold;
159
160 GtkAdjustment *table_canvas_vadjustment;
161
162 /*
163 * Configuration settings
164 */
165 guint alternating_row_colors : 1;
166 guint horizontal_draw_grid : 1;
167 guint vertical_draw_grid : 1;
168 guint draw_focus : 1;
169 guint row_selection_active : 1;
170
171 guint horizontal_scrolling : 1;
172
173 guint scroll_direction : 4;
174
175 guint do_drag : 1;
176
177 guint uniform_row_height : 1;
178
179 guint search_col_set : 1;
180 guint always_search : 1;
181
182 ECursorMode cursor_mode;
183
184 gint drop_row;
185 ETreePath drop_path;
186 gint drop_col;
187
188 GnomeCanvasItem *drop_highlight;
189 gint last_drop_x;
190 gint last_drop_y;
191 gint last_drop_time;
192 GdkDragContext *last_drop_context;
193
194 gint hover_x;
195 gint hover_y;
196
197 gint drag_row;
198 ETreePath drag_path;
199 gint drag_col;
200 ETreeDragSourceSite *site;
201
202 GList *expanded_list;
203
204 gboolean state_changed;
205 guint state_change_freeze;
206
207 gboolean is_dragging;
208
209 gboolean grouped_view;
210 gboolean sort_children_ascending;
211 };
212
213 static guint signals[LAST_SIGNAL];
214
215 static void et_grab_focus (GtkWidget *widget);
216
217 static void et_drag_begin (GtkWidget *widget,
218 GdkDragContext *context,
219 ETree *tree);
220 static void et_drag_end (GtkWidget *widget,
221 GdkDragContext *context,
222 ETree *tree);
223 static void et_drag_data_get (GtkWidget *widget,
224 GdkDragContext *context,
225 GtkSelectionData *selection_data,
226 guint info,
227 guint time,
228 ETree *tree);
229 static void et_drag_data_delete (GtkWidget *widget,
230 GdkDragContext *context,
231 ETree *tree);
232
233 static void et_drag_leave (GtkWidget *widget,
234 GdkDragContext *context,
235 guint time,
236 ETree *tree);
237 static gboolean et_drag_motion (GtkWidget *widget,
238 GdkDragContext *context,
239 gint x,
240 gint y,
241 guint time,
242 ETree *tree);
243 static gboolean et_drag_drop (GtkWidget *widget,
244 GdkDragContext *context,
245 gint x,
246 gint y,
247 guint time,
248 ETree *tree);
249 static void et_drag_data_received (GtkWidget *widget,
250 GdkDragContext *context,
251 gint x,
252 gint y,
253 GtkSelectionData *selection_data,
254 guint info,
255 guint time,
256 ETree *tree);
257
258 static void scroll_off (ETree *tree);
259 static void scroll_on (ETree *tree, guint scroll_direction);
260 static void hover_off (ETree *tree);
261 static void hover_on (ETree *tree, gint x, gint y);
262 static void context_destroyed (gpointer data, GObject *ctx);
263
264 static void e_tree_scrollable_init (GtkScrollableInterface *iface);
265
G_DEFINE_TYPE_WITH_CODE(ETree,e_tree,GTK_TYPE_TABLE,G_IMPLEMENT_INTERFACE (GTK_TYPE_SCROLLABLE,e_tree_scrollable_init))266 G_DEFINE_TYPE_WITH_CODE (ETree, e_tree, GTK_TYPE_TABLE,
267 G_IMPLEMENT_INTERFACE (GTK_TYPE_SCROLLABLE, e_tree_scrollable_init))
268
269 static void
270 tree_item_is_editing_changed_cb (ETableItem *item,
271 GParamSpec *param,
272 ETree *tree)
273 {
274 g_return_if_fail (E_IS_TREE (tree));
275
276 g_object_notify (G_OBJECT (tree), "is-editing");
277 }
278
279 static void
et_disconnect_from_etta(ETree * tree)280 et_disconnect_from_etta (ETree *tree)
281 {
282 if (tree->priv->table_model_change_id != 0)
283 g_signal_handler_disconnect (
284 tree->priv->etta,
285 tree->priv->table_model_change_id);
286 if (tree->priv->table_row_change_id != 0)
287 g_signal_handler_disconnect (
288 tree->priv->etta,
289 tree->priv->table_row_change_id);
290 if (tree->priv->table_cell_change_id != 0)
291 g_signal_handler_disconnect (
292 tree->priv->etta,
293 tree->priv->table_cell_change_id);
294 if (tree->priv->table_rows_delete_id != 0)
295 g_signal_handler_disconnect (
296 tree->priv->etta,
297 tree->priv->table_rows_delete_id);
298
299 tree->priv->table_model_change_id = 0;
300 tree->priv->table_row_change_id = 0;
301 tree->priv->table_cell_change_id = 0;
302 tree->priv->table_rows_delete_id = 0;
303 }
304
305 static void
clear_current_search_col(ETree * tree)306 clear_current_search_col (ETree *tree)
307 {
308 tree->priv->search_col_set = FALSE;
309 }
310
311 static ETableCol *
current_search_col(ETree * tree)312 current_search_col (ETree *tree)
313 {
314 if (!tree->priv->search_col_set) {
315 tree->priv->current_search_col =
316 e_table_util_calculate_current_search_col (
317 tree->priv->header,
318 tree->priv->full_header,
319 tree->priv->sort_info,
320 tree->priv->always_search);
321 tree->priv->search_col_set = TRUE;
322 }
323
324 return tree->priv->current_search_col;
325 }
326
327 static void
e_tree_state_change(ETree * tree)328 e_tree_state_change (ETree *tree)
329 {
330 if (tree->priv->state_change_freeze)
331 tree->priv->state_changed = TRUE;
332 else
333 g_signal_emit (tree, signals[STATE_CHANGE], 0);
334 }
335
336 static void
change_trigger(GObject * object,ETree * tree)337 change_trigger (GObject *object,
338 ETree *tree)
339 {
340 e_tree_state_change (tree);
341 }
342
343 static void
search_col_change_trigger(GObject * object,ETree * tree)344 search_col_change_trigger (GObject *object,
345 ETree *tree)
346 {
347 clear_current_search_col (tree);
348 e_tree_state_change (tree);
349 }
350
351 static void
disconnect_header(ETree * tree)352 disconnect_header (ETree *tree)
353 {
354 if (tree->priv->header == NULL)
355 return;
356
357 if (tree->priv->structure_change_id)
358 g_signal_handler_disconnect (
359 tree->priv->header,
360 tree->priv->structure_change_id);
361 if (tree->priv->expansion_change_id)
362 g_signal_handler_disconnect (
363 tree->priv->header,
364 tree->priv->expansion_change_id);
365 if (tree->priv->sort_info) {
366 if (tree->priv->sort_info_change_id)
367 g_signal_handler_disconnect (
368 tree->priv->sort_info,
369 tree->priv->sort_info_change_id);
370 if (tree->priv->group_info_change_id)
371 g_signal_handler_disconnect (
372 tree->priv->sort_info,
373 tree->priv->group_info_change_id);
374
375 g_object_unref (tree->priv->sort_info);
376 }
377 g_object_unref (tree->priv->header);
378 tree->priv->header = NULL;
379 tree->priv->sort_info = NULL;
380 }
381
382 static void
connect_header(ETree * tree,ETableState * state)383 connect_header (ETree *tree,
384 ETableState *state)
385 {
386 GValue *val = g_new0 (GValue, 1);
387
388 if (tree->priv->header != NULL)
389 disconnect_header (tree);
390
391 tree->priv->header = e_table_state_to_header (
392 GTK_WIDGET (tree), tree->priv->full_header, state);
393
394 tree->priv->structure_change_id = g_signal_connect (
395 tree->priv->header, "structure_change",
396 G_CALLBACK (search_col_change_trigger), tree);
397
398 tree->priv->expansion_change_id = g_signal_connect (
399 tree->priv->header, "expansion_change",
400 G_CALLBACK (change_trigger), tree);
401
402 if (state->sort_info) {
403 tree->priv->sort_info = e_table_sort_info_duplicate (state->sort_info);
404 e_table_sort_info_set_can_group (tree->priv->sort_info, FALSE);
405 tree->priv->sort_info_change_id = g_signal_connect (
406 tree->priv->sort_info, "sort_info_changed",
407 G_CALLBACK (search_col_change_trigger), tree);
408
409 tree->priv->group_info_change_id = g_signal_connect (
410 tree->priv->sort_info, "group_info_changed",
411 G_CALLBACK (search_col_change_trigger), tree);
412 } else
413 tree->priv->sort_info = NULL;
414
415 g_value_init (val, G_TYPE_OBJECT);
416 g_value_set_object (val, tree->priv->sort_info);
417 g_object_set_property (G_OBJECT (tree->priv->header), "sort_info", val);
418 g_free (val);
419 }
420
421 static void
et_dispose(GObject * object)422 et_dispose (GObject *object)
423 {
424 ETreePrivate *priv;
425
426 priv = E_TREE_GET_PRIVATE (object);
427
428 if (priv->search != NULL) {
429 g_signal_handler_disconnect (
430 priv->search, priv->search_search_id);
431 g_signal_handler_disconnect (
432 priv->search, priv->search_accept_id);
433 g_object_unref (priv->search);
434 priv->search = NULL;
435 }
436
437 if (priv->reflow_idle_id > 0) {
438 g_source_remove (priv->reflow_idle_id);
439 priv->reflow_idle_id = 0;
440 }
441
442 scroll_off (E_TREE (object));
443 hover_off (E_TREE (object));
444 g_list_foreach (
445 priv->expanded_list,
446 (GFunc) g_free, NULL);
447 g_list_free (priv->expanded_list);
448 priv->expanded_list = NULL;
449
450 et_disconnect_from_etta (E_TREE (object));
451
452 g_clear_object (&priv->etta);
453 g_clear_object (&priv->model);
454 g_clear_object (&priv->full_header);
455
456 disconnect_header (E_TREE (object));
457
458 g_clear_object (&priv->selection);
459 g_clear_object (&priv->spec);
460
461 if (priv->header_canvas != NULL) {
462 gtk_widget_destroy (GTK_WIDGET (priv->header_canvas));
463 priv->header_canvas = NULL;
464 }
465
466 if (priv->site)
467 e_tree_drag_source_unset (E_TREE (object));
468
469 if (priv->last_drop_context != NULL) {
470 g_object_weak_unref (
471 G_OBJECT (priv->last_drop_context),
472 context_destroyed, object);
473 priv->last_drop_context = NULL;
474 }
475
476 if (priv->info_text != NULL) {
477 g_object_run_dispose (G_OBJECT (priv->info_text));
478 priv->info_text = NULL;
479 }
480 priv->info_text_resize_id = 0;
481
482 if (priv->table_canvas != NULL) {
483 g_signal_handlers_disconnect_by_data (priv->table_canvas, object);
484 gtk_widget_destroy (GTK_WIDGET (priv->table_canvas));
485 priv->table_canvas = NULL;
486 }
487
488 if (priv->table_canvas_vadjustment) {
489 g_signal_handlers_disconnect_by_data (priv->table_canvas_vadjustment, object);
490 g_clear_object (&priv->table_canvas_vadjustment);
491 }
492
493 /* do not unref it, it was owned by priv->table_canvas */
494 priv->item = NULL;
495
496 /* Chain up to parent's dispose() method. */
497 G_OBJECT_CLASS (e_tree_parent_class)->dispose (object);
498 }
499
500 static void
et_unrealize(GtkWidget * widget)501 et_unrealize (GtkWidget *widget)
502 {
503 scroll_off (E_TREE (widget));
504 hover_off (E_TREE (widget));
505
506 if (GTK_WIDGET_CLASS (e_tree_parent_class)->unrealize)
507 GTK_WIDGET_CLASS (e_tree_parent_class)->unrealize (widget);
508 }
509
510 typedef struct {
511 ETree *tree;
512 gchar *string;
513 } SearchSearchStruct;
514
515 static gboolean
search_search_callback(ETreeModel * model,ETreePath path,gpointer data)516 search_search_callback (ETreeModel *model,
517 ETreePath path,
518 gpointer data)
519 {
520 SearchSearchStruct *cb_data = data;
521 gconstpointer value;
522 ETableCol *col = current_search_col (cb_data->tree);
523
524 value = e_tree_model_value_at (
525 model, path,
526 cb_data->tree->priv->current_search_col->spec->model_col);
527
528 return col->search (value, cb_data->string);
529 }
530
531 static gboolean
et_search_search(ETableSearch * search,gchar * string,ETableSearchFlags flags,ETree * tree)532 et_search_search (ETableSearch *search,
533 gchar *string,
534 ETableSearchFlags flags,
535 ETree *tree)
536 {
537 ETreePath cursor;
538 ETreePath found;
539 SearchSearchStruct cb_data;
540 ETableCol *col = current_search_col (tree);
541
542 if (col == NULL)
543 return FALSE;
544
545 cb_data.tree = tree;
546 cb_data.string = string;
547
548 cursor = e_tree_get_cursor (tree);
549
550 if (cursor && (flags & E_TABLE_SEARCH_FLAGS_CHECK_CURSOR_FIRST)) {
551 gconstpointer value;
552
553 value = e_tree_model_value_at (
554 tree->priv->model, cursor, col->spec->model_col);
555
556 if (col->search (value, string)) {
557 return TRUE;
558 }
559 }
560
561 found = e_tree_model_node_find (
562 tree->priv->model, cursor, NULL,
563 search_search_callback, &cb_data);
564 if (found == NULL)
565 found = e_tree_model_node_find (
566 tree->priv->model, NULL, cursor,
567 search_search_callback, &cb_data);
568
569 if (found && found != cursor) {
570 gint model_row;
571
572 e_tree_table_adapter_show_node (tree->priv->etta, found);
573 model_row = e_tree_table_adapter_row_of_node (tree->priv->etta, found);
574
575 e_selection_model_select_as_key_press (
576 E_SELECTION_MODEL (tree->priv->selection),
577 model_row, col->spec->model_col,
578 GDK_CONTROL_MASK);
579 return TRUE;
580 } else if (cursor && !(flags & E_TABLE_SEARCH_FLAGS_CHECK_CURSOR_FIRST)) {
581 gconstpointer value;
582
583 value = e_tree_model_value_at (
584 tree->priv->model, cursor, col->spec->model_col);
585
586 return col->search (value, string);
587 } else
588 return FALSE;
589 }
590
591 static void
et_search_accept(ETableSearch * search,ETree * tree)592 et_search_accept (ETableSearch *search,
593 ETree *tree)
594 {
595 ETableCol *col = current_search_col (tree);
596 gint cursor;
597
598 if (col == NULL)
599 return;
600
601 g_object_get (tree->priv->selection, "cursor_row", &cursor, NULL);
602
603 e_selection_model_select_as_key_press (
604 E_SELECTION_MODEL (tree->priv->selection),
605 cursor, col->spec->model_col, 0);
606 }
607
608 static void
e_tree_init(ETree * tree)609 e_tree_init (ETree *tree)
610 {
611 gtk_widget_set_can_focus (GTK_WIDGET (tree), TRUE);
612
613 gtk_table_set_homogeneous (GTK_TABLE (tree), FALSE);
614
615 tree->priv = E_TREE_GET_PRIVATE (tree);
616
617 tree->priv->alternating_row_colors = 1;
618 tree->priv->horizontal_draw_grid = 1;
619 tree->priv->vertical_draw_grid = 1;
620 tree->priv->draw_focus = 1;
621 tree->priv->cursor_mode = E_CURSOR_SIMPLE;
622 tree->priv->length_threshold = 200;
623
624 tree->priv->drop_row = -1;
625 tree->priv->drop_col = -1;
626
627 tree->priv->drag_row = -1;
628 tree->priv->drag_col = -1;
629
630 tree->priv->selection =
631 E_SELECTION_MODEL (e_tree_selection_model_new ());
632
633 tree->priv->search = e_table_search_new ();
634
635 tree->priv->search_search_id = g_signal_connect (
636 tree->priv->search, "search",
637 G_CALLBACK (et_search_search), tree);
638
639 tree->priv->search_accept_id = g_signal_connect (
640 tree->priv->search, "accept",
641 G_CALLBACK (et_search_accept), tree);
642
643 tree->priv->always_search = g_getenv ("GAL_ALWAYS_SEARCH") ? TRUE : FALSE;
644
645 tree->priv->state_changed = FALSE;
646 tree->priv->state_change_freeze = 0;
647
648 tree->priv->is_dragging = FALSE;
649 tree->priv->grouped_view = TRUE;
650 }
651
652 /* Grab_focus handler for the ETree */
653 static void
et_grab_focus(GtkWidget * widget)654 et_grab_focus (GtkWidget *widget)
655 {
656 ETree *tree;
657
658 tree = E_TREE (widget);
659
660 gtk_widget_grab_focus (GTK_WIDGET (tree->priv->table_canvas));
661 }
662
663 /* Focus handler for the ETree */
664 static gint
et_focus(GtkWidget * container,GtkDirectionType direction)665 et_focus (GtkWidget *container,
666 GtkDirectionType direction)
667 {
668 ETree *tree;
669
670 tree = E_TREE (container);
671
672 if (gtk_container_get_focus_child (GTK_CONTAINER (container))) {
673 gtk_container_set_focus_child (GTK_CONTAINER (container), NULL);
674 return FALSE;
675 }
676
677 return gtk_widget_child_focus (
678 GTK_WIDGET (tree->priv->table_canvas), direction);
679 }
680
681 static void
set_header_canvas_width(ETree * tree)682 set_header_canvas_width (ETree *tree)
683 {
684 gdouble oldwidth, oldheight, width;
685
686 if (!(tree->priv->header_item &&
687 tree->priv->header_canvas && tree->priv->table_canvas))
688 return;
689
690 gnome_canvas_get_scroll_region (
691 GNOME_CANVAS (tree->priv->table_canvas),
692 NULL, NULL, &width, NULL);
693 gnome_canvas_get_scroll_region (
694 GNOME_CANVAS (tree->priv->header_canvas),
695 NULL, NULL, &oldwidth, &oldheight);
696
697 if (oldwidth != width ||
698 oldheight != E_TABLE_HEADER_ITEM (tree->priv->header_item)->height - 1)
699 gnome_canvas_set_scroll_region (
700 GNOME_CANVAS (tree->priv->header_canvas),
701 0, 0, width, /* COLUMN_HEADER_HEIGHT - 1 */
702 E_TABLE_HEADER_ITEM (tree->priv->header_item)->height - 1);
703
704 }
705
706 static void
header_canvas_size_allocate(GtkWidget * widget,GtkAllocation * alloc,ETree * tree)707 header_canvas_size_allocate (GtkWidget *widget,
708 GtkAllocation *alloc,
709 ETree *tree)
710 {
711 GtkAllocation allocation;
712
713 set_header_canvas_width (tree);
714
715 widget = GTK_WIDGET (tree->priv->header_canvas);
716 gtk_widget_get_allocation (widget, &allocation);
717
718 /* When the header item is created ->height == 0,
719 * as the font is only created when everything is realized.
720 * So we set the usize here as well, so that the size of the
721 * header is correct */
722 if (allocation.height != E_TABLE_HEADER_ITEM (tree->priv->header_item)->height)
723 gtk_widget_set_size_request (
724 widget, -1,
725 E_TABLE_HEADER_ITEM (tree->priv->header_item)->height);
726 }
727
728 static void
e_tree_setup_header(ETree * tree)729 e_tree_setup_header (ETree *tree)
730 {
731 GtkWidget *widget;
732 gchar *pointer;
733
734 widget = e_canvas_new ();
735 gtk_style_context_add_class (
736 gtk_widget_get_style_context (widget), "table-header");
737 gtk_widget_set_can_focus (widget, FALSE);
738 tree->priv->header_canvas = GNOME_CANVAS (widget);
739 gtk_widget_show (widget);
740
741 pointer = g_strdup_printf ("%p", (gpointer) tree);
742
743 tree->priv->header_item = gnome_canvas_item_new (
744 gnome_canvas_root (tree->priv->header_canvas),
745 e_table_header_item_get_type (),
746 "ETableHeader", tree->priv->header,
747 "full_header", tree->priv->full_header,
748 "sort_info", tree->priv->sort_info,
749 "dnd_code", pointer,
750 "tree", tree,
751 NULL);
752
753 g_free (pointer);
754
755 g_signal_connect (
756 tree->priv->header_canvas, "size_allocate",
757 G_CALLBACK (header_canvas_size_allocate), tree);
758
759 gtk_widget_set_size_request (
760 GTK_WIDGET (tree->priv->header_canvas), -1,
761 E_TABLE_HEADER_ITEM (tree->priv->header_item)->height);
762 }
763
764 static void
scroll_to_cursor(ETree * tree)765 scroll_to_cursor (ETree *tree)
766 {
767 ETreePath path;
768 GtkAdjustment *adjustment;
769 GtkScrollable *scrollable;
770 gint x, y, w, h;
771 gdouble page_size;
772 gdouble lower;
773 gdouble upper;
774 gdouble value;
775
776 path = e_tree_get_cursor (tree);
777 x = y = w = h = 0;
778
779 if (path != NULL) {
780 ETreeTableAdapter *adapter;
781 gint row;
782 gint col = 0;
783
784 adapter = e_tree_get_table_adapter (tree);
785 row = e_tree_table_adapter_row_of_node (adapter, path);
786
787 if (row >= 0)
788 e_table_item_get_cell_geometry (
789 E_TABLE_ITEM (tree->priv->item),
790 &row, &col, &x, &y, &w, &h);
791 }
792
793 e_table_item_cancel_scroll_to_cursor (E_TABLE_ITEM (tree->priv->item));
794
795 scrollable = GTK_SCROLLABLE (tree->priv->table_canvas);
796 adjustment = gtk_scrollable_get_vadjustment (scrollable);
797
798 page_size = gtk_adjustment_get_page_size (adjustment);
799 lower = gtk_adjustment_get_lower (adjustment);
800 upper = gtk_adjustment_get_upper (adjustment);
801 value = gtk_adjustment_get_value (adjustment);
802
803 if (y < value || y + h > value + page_size) {
804 value = CLAMP (y - page_size / 2, lower, upper - page_size);
805 gtk_adjustment_set_value (adjustment, value);
806 }
807 }
808
809 static gboolean
tree_canvas_reflow_idle(ETree * tree)810 tree_canvas_reflow_idle (ETree *tree)
811 {
812 gdouble height, width;
813 gdouble oldheight, oldwidth;
814 GtkAllocation allocation;
815 GtkWidget *widget;
816
817 widget = GTK_WIDGET (tree->priv->table_canvas);
818 gtk_widget_get_allocation (widget, &allocation);
819
820 g_object_get (
821 tree->priv->item,
822 "height", &height, "width", &width, NULL);
823
824 height = MAX ((gint) height, allocation.height);
825 width = MAX ((gint) width, allocation.width);
826
827 /* I have no idea why this needs to be -1, but it works. */
828 gnome_canvas_get_scroll_region (
829 GNOME_CANVAS (tree->priv->table_canvas),
830 NULL, NULL, &oldwidth, &oldheight);
831
832 if (oldwidth != width - 1 ||
833 oldheight != height - 1) {
834 gnome_canvas_set_scroll_region (
835 GNOME_CANVAS (tree->priv->table_canvas),
836 0, 0, width - 1, height - 1);
837 set_header_canvas_width (tree);
838 }
839
840 tree->priv->reflow_idle_id = 0;
841
842 if (tree->priv->show_cursor_after_reflow) {
843 tree->priv->show_cursor_after_reflow = FALSE;
844 scroll_to_cursor (tree);
845 }
846
847 return FALSE;
848 }
849
850 static void
tree_canvas_size_allocate(GtkWidget * widget,GtkAllocation * alloc,ETree * tree)851 tree_canvas_size_allocate (GtkWidget *widget,
852 GtkAllocation *alloc,
853 ETree *tree)
854 {
855 gdouble width;
856 gdouble height;
857 GValue *val = g_new0 (GValue, 1);
858 g_value_init (val, G_TYPE_DOUBLE);
859
860 width = alloc->width;
861 g_value_set_double (val, width);
862 g_object_get (
863 tree->priv->item,
864 "height", &height,
865 NULL);
866 height = MAX ((gint) height, alloc->height);
867
868 g_object_set (
869 tree->priv->item,
870 "width", width,
871 NULL);
872 g_object_set_property (G_OBJECT (tree->priv->header), "width", val);
873 g_free (val);
874
875 if (tree->priv->reflow_idle_id)
876 g_source_remove (tree->priv->reflow_idle_id);
877 tree_canvas_reflow_idle (tree);
878 }
879
880 static void
tree_canvas_reflow(GnomeCanvas * canvas,ETree * tree)881 tree_canvas_reflow (GnomeCanvas *canvas,
882 ETree *tree)
883 {
884 if (!tree->priv->reflow_idle_id)
885 tree->priv->reflow_idle_id = g_idle_add_full (
886 400, (GSourceFunc) tree_canvas_reflow_idle,
887 tree, NULL);
888 }
889
890 static void
item_cursor_change(ETableItem * eti,gint row,ETree * tree)891 item_cursor_change (ETableItem *eti,
892 gint row,
893 ETree *tree)
894 {
895 ETreePath path = e_tree_table_adapter_node_at_row (tree->priv->etta, row);
896
897 g_signal_emit (tree, signals[CURSOR_CHANGE], 0, row, path);
898 }
899
900 static void
item_cursor_activated(ETableItem * eti,gint row,ETree * tree)901 item_cursor_activated (ETableItem *eti,
902 gint row,
903 ETree *tree)
904 {
905 ETreePath path = e_tree_table_adapter_node_at_row (tree->priv->etta, row);
906
907 g_signal_emit (tree, signals[CURSOR_ACTIVATED], 0, row, path);
908 }
909
910 static void
item_double_click(ETableItem * eti,gint row,gint col,GdkEvent * event,ETree * tree)911 item_double_click (ETableItem *eti,
912 gint row,
913 gint col,
914 GdkEvent *event,
915 ETree *tree)
916 {
917 ETreePath path = e_tree_table_adapter_node_at_row (tree->priv->etta, row);
918
919 g_signal_emit (tree, signals[DOUBLE_CLICK], 0, row, path, col, event);
920 }
921
922 static gboolean
item_right_click(ETableItem * eti,gint row,gint col,GdkEvent * event,ETree * tree)923 item_right_click (ETableItem *eti,
924 gint row,
925 gint col,
926 GdkEvent *event,
927 ETree *tree)
928 {
929 ETreePath path = e_tree_table_adapter_node_at_row (tree->priv->etta, row);
930 gboolean return_val = 0;
931
932 g_signal_emit (
933 tree, signals[RIGHT_CLICK], 0,
934 row, path, col, event, &return_val);
935
936 return return_val;
937 }
938
939 static gboolean
item_click(ETableItem * eti,gint row,gint col,GdkEvent * event,ETree * tree)940 item_click (ETableItem *eti,
941 gint row,
942 gint col,
943 GdkEvent *event,
944 ETree *tree)
945 {
946 gboolean return_val = 0;
947 ETreePath path = e_tree_table_adapter_node_at_row (tree->priv->etta, row);
948
949 g_signal_emit (
950 tree, signals[CLICK], 0, row, path, col, event, &return_val);
951
952 return return_val;
953 }
954
955 static gint
item_key_press(ETableItem * eti,gint row,gint col,GdkEvent * event,ETree * tree)956 item_key_press (ETableItem *eti,
957 gint row,
958 gint col,
959 GdkEvent *event,
960 ETree *tree)
961 {
962 gint return_val = 0;
963 GdkEventKey *key = (GdkEventKey *) event;
964 ETreePath path;
965 gint y, row_local, col_local;
966 GtkAdjustment *adjustment;
967 GtkScrollable *scrollable;
968 gdouble page_size;
969 gdouble upper;
970 gdouble value;
971
972 scrollable = GTK_SCROLLABLE (tree->priv->table_canvas);
973 adjustment = gtk_scrollable_get_vadjustment (scrollable);
974
975 page_size = gtk_adjustment_get_page_size (adjustment);
976 upper = gtk_adjustment_get_upper (adjustment);
977 value = gtk_adjustment_get_value (adjustment);
978
979 switch (key->keyval) {
980 case GDK_KEY_Page_Down:
981 case GDK_KEY_KP_Page_Down:
982 y = CLAMP (value + (2 * page_size - 50), 0, upper);
983 y -= value;
984 e_tree_get_cell_at (tree, 30, y, &row_local, &col_local);
985
986 if (row_local == -1)
987 row_local = e_table_model_row_count (
988 E_TABLE_MODEL (tree->priv->etta)) - 1;
989
990 col_local = e_selection_model_cursor_col (
991 E_SELECTION_MODEL (tree->priv->selection));
992 e_selection_model_select_as_key_press (
993 E_SELECTION_MODEL (tree->priv->selection),
994 row_local, col_local, key->state);
995
996 return_val = 1;
997 break;
998 case GDK_KEY_Page_Up:
999 case GDK_KEY_KP_Page_Up:
1000 y = CLAMP (value - (page_size - 50), 0, upper);
1001 y -= value;
1002 e_tree_get_cell_at (tree, 30, y, &row_local, &col_local);
1003
1004 if (row_local == -1)
1005 row_local = e_table_model_row_count (
1006 E_TABLE_MODEL (tree->priv->etta)) - 1;
1007
1008 col_local = e_selection_model_cursor_col (
1009 E_SELECTION_MODEL (tree->priv->selection));
1010 e_selection_model_select_as_key_press (
1011 E_SELECTION_MODEL (tree->priv->selection),
1012 row_local, col_local, key->state);
1013
1014 return_val = 1;
1015 break;
1016 case GDK_KEY_plus:
1017 case GDK_KEY_KP_Add:
1018 case GDK_KEY_Right:
1019 case GDK_KEY_KP_Right:
1020 /* Only allow if the Shift modifier is used.
1021 * eg. Ctrl-Equal shouldn't be handled. */
1022 if ((key->state & (GDK_SHIFT_MASK | GDK_LOCK_MASK | GDK_MOD1_MASK)) != GDK_SHIFT_MASK) {
1023 /* Allow also plain (without modifiers) expand when in the 'line' cursor mode */
1024 if (eti->cursor_mode != E_CURSOR_LINE ||
1025 (key->state & (GDK_SHIFT_MASK | GDK_LOCK_MASK | GDK_MOD1_MASK)) != 0)
1026 break;
1027 }
1028
1029 if (row != -1) {
1030 path = e_tree_table_adapter_node_at_row (tree->priv->etta, row);
1031 if (path) {
1032 if ((key->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK)) == (GDK_CONTROL_MASK | GDK_SHIFT_MASK))
1033 e_tree_table_adapter_node_set_expanded_recurse (tree->priv->etta, path, TRUE);
1034 else
1035 e_tree_table_adapter_node_set_expanded (tree->priv->etta, path, TRUE);
1036 }
1037 }
1038 return_val = 1;
1039 break;
1040 case GDK_KEY_minus:
1041 case GDK_KEY_underscore:
1042 case GDK_KEY_KP_Subtract:
1043 case GDK_KEY_Left:
1044 case GDK_KEY_KP_Left:
1045 /* Only allow if the Shift modifier is used.
1046 * eg. Ctrl-Minus shouldn't be handled. */
1047 if ((key->state & (GDK_SHIFT_MASK | GDK_LOCK_MASK | GDK_MOD1_MASK)) != GDK_SHIFT_MASK) {
1048 /* Allow also plain (without modifiers) collapse when in the 'line' cursor mode */
1049 if (eti->cursor_mode != E_CURSOR_LINE ||
1050 (key->state & (GDK_SHIFT_MASK | GDK_LOCK_MASK | GDK_MOD1_MASK)) != 0)
1051 break;
1052 }
1053
1054 if (row != -1) {
1055 path = e_tree_table_adapter_node_at_row (tree->priv->etta, row);
1056 if (path) {
1057 if ((key->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK)) == (GDK_CONTROL_MASK | GDK_SHIFT_MASK))
1058 e_tree_table_adapter_node_set_expanded_recurse (tree->priv->etta, path, FALSE);
1059 else
1060 e_tree_table_adapter_node_set_expanded (tree->priv->etta, path, FALSE);
1061 }
1062 }
1063 return_val = 1;
1064 break;
1065 case GDK_KEY_BackSpace:
1066 if (e_table_search_backspace (tree->priv->search))
1067 return TRUE;
1068 /* Fallthrough */
1069 default:
1070 if ((key->state & ~(GDK_SHIFT_MASK | GDK_LOCK_MASK |
1071 GDK_MOD1_MASK | GDK_MOD2_MASK | GDK_MOD3_MASK |
1072 GDK_MOD4_MASK | GDK_MOD5_MASK)) == 0
1073 && ((key->keyval >= GDK_KEY_a && key->keyval <= GDK_KEY_z) ||
1074 (key->keyval >= GDK_KEY_A && key->keyval <= GDK_KEY_Z) ||
1075 (key->keyval >= GDK_KEY_0 && key->keyval <= GDK_KEY_9))) {
1076 e_table_search_input_character (tree->priv->search, key->keyval);
1077 }
1078 path = e_tree_table_adapter_node_at_row (tree->priv->etta, row);
1079 g_signal_emit (
1080 tree,
1081 signals[KEY_PRESS], 0,
1082 row, path, col, event, &return_val);
1083 break;
1084 }
1085 return return_val;
1086 }
1087
1088 static gint
item_start_drag(ETableItem * eti,gint row,gint col,GdkEvent * event,ETree * tree)1089 item_start_drag (ETableItem *eti,
1090 gint row,
1091 gint col,
1092 GdkEvent *event,
1093 ETree *tree)
1094 {
1095 ETreePath path;
1096 gint return_val = 0;
1097
1098 path = e_tree_table_adapter_node_at_row (tree->priv->etta, row);
1099
1100 g_signal_emit (
1101 tree, signals[START_DRAG], 0,
1102 row, path, col, event, &return_val);
1103
1104 return return_val;
1105 }
1106
1107 static void
et_selection_model_selection_changed(ETableSelectionModel * etsm,ETree * tree)1108 et_selection_model_selection_changed (ETableSelectionModel *etsm,
1109 ETree *tree)
1110 {
1111 g_signal_emit (tree, signals[SELECTION_CHANGE], 0);
1112 }
1113
1114 static void
et_selection_model_selection_row_changed(ETableSelectionModel * etsm,gint row,ETree * tree)1115 et_selection_model_selection_row_changed (ETableSelectionModel *etsm,
1116 gint row,
1117 ETree *tree)
1118 {
1119 g_signal_emit (tree, signals[SELECTION_CHANGE], 0);
1120 }
1121
1122 static void
et_build_item(ETree * tree)1123 et_build_item (ETree *tree)
1124 {
1125 gboolean alternating_row_colors;
1126
1127 alternating_row_colors = tree->priv->alternating_row_colors;
1128 if (alternating_row_colors) {
1129 gboolean bvalue = TRUE;
1130
1131 /* user can only disable this option, if it's enabled by the specification */
1132 gtk_widget_style_get (GTK_WIDGET (tree), "alternating-row-colors", &bvalue, NULL);
1133
1134 alternating_row_colors = bvalue ? 1 : 0;
1135 }
1136
1137 tree->priv->item = gnome_canvas_item_new (
1138 GNOME_CANVAS_GROUP (
1139 gnome_canvas_root (tree->priv->table_canvas)),
1140 e_table_item_get_type (),
1141 "ETableHeader", tree->priv->header,
1142 "ETableModel", tree->priv->etta,
1143 "selection_model", tree->priv->selection,
1144 "alternating_row_colors", alternating_row_colors,
1145 "horizontal_draw_grid", tree->priv->horizontal_draw_grid,
1146 "vertical_draw_grid", tree->priv->vertical_draw_grid,
1147 "drawfocus", tree->priv->draw_focus,
1148 "cursor_mode", tree->priv->cursor_mode,
1149 "length_threshold", tree->priv->length_threshold,
1150 "uniform_row_height", tree->priv->uniform_row_height,
1151 NULL);
1152
1153 g_signal_connect (
1154 tree->priv->item, "cursor_change",
1155 G_CALLBACK (item_cursor_change), tree);
1156 g_signal_connect (
1157 tree->priv->item, "cursor_activated",
1158 G_CALLBACK (item_cursor_activated), tree);
1159 g_signal_connect (
1160 tree->priv->item, "double_click",
1161 G_CALLBACK (item_double_click), tree);
1162 g_signal_connect (
1163 tree->priv->item, "right_click",
1164 G_CALLBACK (item_right_click), tree);
1165 g_signal_connect (
1166 tree->priv->item, "click",
1167 G_CALLBACK (item_click), tree);
1168 g_signal_connect (
1169 tree->priv->item, "key_press",
1170 G_CALLBACK (item_key_press), tree);
1171 g_signal_connect (
1172 tree->priv->item, "start_drag",
1173 G_CALLBACK (item_start_drag), tree);
1174 e_signal_connect_notify (
1175 tree->priv->item, "notify::is-editing",
1176 G_CALLBACK (tree_item_is_editing_changed_cb), tree);
1177 }
1178
1179 static void
et_canvas_style_updated(GtkWidget * widget)1180 et_canvas_style_updated (GtkWidget *widget)
1181 {
1182 GdkColor color;
1183
1184 GTK_WIDGET_CLASS (e_tree_parent_class)->style_updated (widget);
1185
1186 e_utils_get_theme_color_color (widget, "theme_base_color", E_UTILS_DEFAULT_THEME_BASE_COLOR, &color);
1187
1188 gnome_canvas_item_set (
1189 E_TREE (widget)->priv->white_item,
1190 "fill_color_gdk", &color,
1191 NULL);
1192 }
1193
1194 static gboolean
white_item_event(GnomeCanvasItem * white_item,GdkEvent * event,ETree * tree)1195 white_item_event (GnomeCanvasItem *white_item,
1196 GdkEvent *event,
1197 ETree *tree)
1198 {
1199 gboolean return_val = 0;
1200
1201 g_signal_emit (
1202 tree,
1203 signals[WHITE_SPACE_EVENT], 0,
1204 event, &return_val);
1205
1206 if (!return_val && event && tree->priv->item) {
1207 guint event_button = 0;
1208
1209 gdk_event_get_button (event, &event_button);
1210
1211 if (event->type == GDK_BUTTON_PRESS && (event_button == 1 || event_button == 2)) {
1212 gnome_canvas_item_grab_focus (GNOME_CANVAS_ITEM (tree->priv->item));
1213 return_val = TRUE;
1214 }
1215 }
1216
1217 return return_val;
1218 }
1219
1220 static gint
et_canvas_root_event(GnomeCanvasItem * root,GdkEvent * event,ETree * tree)1221 et_canvas_root_event (GnomeCanvasItem *root,
1222 GdkEvent *event,
1223 ETree *tree)
1224 {
1225 switch (event->type) {
1226 case GDK_BUTTON_PRESS:
1227 case GDK_2BUTTON_PRESS:
1228 case GDK_BUTTON_RELEASE:
1229 if (event->button.button != 4 && event->button.button != 5) {
1230 if (gtk_widget_has_focus (GTK_WIDGET (root->canvas))) {
1231 GnomeCanvasItem *item = GNOME_CANVAS (root->canvas)->focused_item;
1232
1233 if (E_IS_TABLE_ITEM (item)) {
1234 e_table_item_leave_edit (E_TABLE_ITEM (item));
1235 return TRUE;
1236 }
1237 }
1238 }
1239 break;
1240 default:
1241 break;
1242 }
1243
1244 return FALSE;
1245 }
1246
1247 /* Handler for focus events in the table_canvas; we have to repaint ourselves
1248 * and give the focus to some ETableItem.
1249 */
1250 static gboolean
table_canvas_focus_event_cb(GtkWidget * widget,GdkEventFocus * event,gpointer data)1251 table_canvas_focus_event_cb (GtkWidget *widget,
1252 GdkEventFocus *event,
1253 gpointer data)
1254 {
1255 GnomeCanvas *canvas;
1256 ETree *tree;
1257
1258 gtk_widget_queue_draw (widget);
1259
1260 if (!event->in)
1261 return TRUE;
1262
1263 canvas = GNOME_CANVAS (widget);
1264 tree = E_TREE (data);
1265
1266 if (!canvas->focused_item ||
1267 (e_selection_model_cursor_row (tree->priv->selection) == -1)) {
1268 e_table_item_set_cursor (E_TABLE_ITEM (tree->priv->item), 0, 0);
1269 gnome_canvas_item_grab_focus (tree->priv->item);
1270 }
1271
1272 return TRUE;
1273 }
1274
1275 static void
e_tree_table_canvas_scrolled_cb(GtkAdjustment * vadjustment,GParamSpec * param,ETree * tree)1276 e_tree_table_canvas_scrolled_cb (GtkAdjustment *vadjustment,
1277 GParamSpec *param,
1278 ETree *tree)
1279 {
1280 g_return_if_fail (E_IS_TREE (tree));
1281
1282 if (tree->priv->item)
1283 e_table_item_cursor_scrolled (E_TABLE_ITEM (tree->priv->item));
1284 }
1285
1286 static void
et_setup_table_canvas_vadjustment(ETree * tree)1287 et_setup_table_canvas_vadjustment (ETree *tree)
1288 {
1289 GtkAdjustment *vadjustment = NULL;
1290
1291 g_return_if_fail (E_IS_TREE (tree));
1292
1293 if (tree->priv->table_canvas_vadjustment) {
1294 g_signal_handlers_disconnect_by_data (tree->priv->table_canvas_vadjustment, tree);
1295 g_clear_object (&tree->priv->table_canvas_vadjustment);
1296 }
1297
1298 if (tree->priv->table_canvas)
1299 vadjustment = gtk_scrollable_get_vadjustment (
1300 GTK_SCROLLABLE (tree->priv->table_canvas));
1301
1302 if (vadjustment) {
1303 tree->priv->table_canvas_vadjustment = g_object_ref (vadjustment);
1304 g_signal_connect (
1305 vadjustment, "notify::value",
1306 G_CALLBACK (e_tree_table_canvas_scrolled_cb), tree);
1307 }
1308 }
1309
1310 static void
e_tree_setup_table(ETree * tree)1311 e_tree_setup_table (ETree *tree)
1312 {
1313 GtkWidget *widget;
1314 GdkColor color;
1315
1316 tree->priv->table_canvas = GNOME_CANVAS (e_canvas_new ());
1317 g_signal_connect (
1318 tree->priv->table_canvas, "size_allocate",
1319 G_CALLBACK (tree_canvas_size_allocate), tree);
1320 g_signal_connect (
1321 tree->priv->table_canvas, "focus_in_event",
1322 G_CALLBACK (table_canvas_focus_event_cb), tree);
1323 g_signal_connect (
1324 tree->priv->table_canvas, "focus_out_event",
1325 G_CALLBACK (table_canvas_focus_event_cb), tree);
1326
1327 g_signal_connect (
1328 tree->priv->table_canvas, "drag_begin",
1329 G_CALLBACK (et_drag_begin), tree);
1330 g_signal_connect (
1331 tree->priv->table_canvas, "drag_end",
1332 G_CALLBACK (et_drag_end), tree);
1333 g_signal_connect (
1334 tree->priv->table_canvas, "drag_data_get",
1335 G_CALLBACK (et_drag_data_get), tree);
1336 g_signal_connect (
1337 tree->priv->table_canvas, "drag_data_delete",
1338 G_CALLBACK (et_drag_data_delete), tree);
1339 g_signal_connect (
1340 tree, "drag_motion",
1341 G_CALLBACK (et_drag_motion), tree);
1342 g_signal_connect (
1343 tree, "drag_leave",
1344 G_CALLBACK (et_drag_leave), tree);
1345 g_signal_connect (
1346 tree, "drag_drop",
1347 G_CALLBACK (et_drag_drop), tree);
1348 g_signal_connect (
1349 tree, "drag_data_received",
1350 G_CALLBACK (et_drag_data_received), tree);
1351
1352 g_signal_connect (
1353 tree->priv->table_canvas, "reflow",
1354 G_CALLBACK (tree_canvas_reflow), tree);
1355
1356 et_setup_table_canvas_vadjustment (tree);
1357 g_signal_connect_swapped (
1358 tree->priv->table_canvas, "notify::vadjustment",
1359 G_CALLBACK (et_setup_table_canvas_vadjustment), tree);
1360
1361 widget = GTK_WIDGET (tree->priv->table_canvas);
1362
1363 gtk_widget_show (widget);
1364
1365 e_utils_get_theme_color_color (widget, "theme_base_color", E_UTILS_DEFAULT_THEME_BASE_COLOR, &color);
1366
1367 tree->priv->white_item = gnome_canvas_item_new (
1368 gnome_canvas_root (tree->priv->table_canvas),
1369 e_canvas_background_get_type (),
1370 "fill_color_gdk", &color,
1371 NULL);
1372
1373 g_signal_connect (
1374 tree->priv->white_item, "event",
1375 G_CALLBACK (white_item_event), tree);
1376 g_signal_connect (
1377 gnome_canvas_root (tree->priv->table_canvas), "event",
1378 G_CALLBACK (et_canvas_root_event), tree);
1379
1380 et_build_item (tree);
1381 }
1382
1383 void
e_tree_set_state_object(ETree * tree,ETableState * state)1384 e_tree_set_state_object (ETree *tree,
1385 ETableState *state)
1386 {
1387 GValue *val;
1388 GtkAllocation allocation;
1389 GtkWidget *widget;
1390
1391 val = g_new0 (GValue, 1);
1392 g_value_init (val, G_TYPE_DOUBLE);
1393
1394 connect_header (tree, state);
1395
1396 widget = GTK_WIDGET (tree->priv->table_canvas);
1397 gtk_widget_get_allocation (widget, &allocation);
1398
1399 g_value_set_double (val, (gdouble) allocation.width);
1400 g_object_set_property (G_OBJECT (tree->priv->header), "width", val);
1401 g_free (val);
1402
1403 if (tree->priv->header_item)
1404 g_object_set (
1405 tree->priv->header_item,
1406 "ETableHeader", tree->priv->header,
1407 "sort_info", tree->priv->sort_info,
1408 NULL);
1409
1410 if (tree->priv->item)
1411 g_object_set (
1412 tree->priv->item,
1413 "ETableHeader", tree->priv->header,
1414 NULL);
1415
1416 if (tree->priv->etta)
1417 e_tree_table_adapter_set_sort_info (
1418 tree->priv->etta, tree->priv->sort_info);
1419
1420 e_tree_state_change (tree);
1421 }
1422
1423 /**
1424 * e_tree_get_state_object:
1425 * @tree: #ETree object to act on
1426 *
1427 * Builds an #ETableState corresponding to the current state of the
1428 * #ETree.
1429 *
1430 * Return value:
1431 * The %ETableState object generated.
1432 **/
1433 ETableState *
e_tree_get_state_object(ETree * tree)1434 e_tree_get_state_object (ETree *tree)
1435 {
1436 ETableState *state;
1437 GPtrArray *columns;
1438 gint full_col_count;
1439 gint i, j;
1440
1441 columns = e_table_specification_ref_columns (tree->priv->spec);
1442
1443 state = e_table_state_new (tree->priv->spec);
1444
1445 g_clear_object (&state->sort_info);
1446 if (tree->priv->sort_info != NULL)
1447 state->sort_info = g_object_ref (tree->priv->sort_info);
1448
1449 state->col_count = e_table_header_count (tree->priv->header);
1450 full_col_count = e_table_header_count (tree->priv->full_header);
1451
1452 state->column_specs = g_new (
1453 ETableColumnSpecification *, state->col_count);
1454 state->expansions = g_new (gdouble, state->col_count);
1455
1456 for (i = 0; i < state->col_count; i++) {
1457 ETableCol *col = e_table_header_get_column (tree->priv->header, i);
1458 state->column_specs[i] = NULL;
1459 for (j = 0; j < full_col_count; j++) {
1460 if (col->spec->model_col == e_table_header_index (tree->priv->full_header, j)) {
1461 state->column_specs[i] =
1462 g_object_ref (columns->pdata[j]);
1463 break;
1464 }
1465 }
1466 state->expansions[i] = col->expansion;
1467 }
1468
1469 g_ptr_array_unref (columns);
1470
1471 return state;
1472 }
1473
1474 /**
1475 * e_tree_get_spec:
1476 * @tree: The #ETree to query
1477 *
1478 * Returns the specification object.
1479 *
1480 * Return value:
1481 **/
1482 ETableSpecification *
e_tree_get_spec(ETree * tree)1483 e_tree_get_spec (ETree *tree)
1484 {
1485 g_return_val_if_fail (E_IS_TREE (tree), NULL);
1486
1487 return tree->priv->spec;
1488 }
1489
1490 static void
et_table_model_changed(ETableModel * model,ETree * tree)1491 et_table_model_changed (ETableModel *model,
1492 ETree *tree)
1493 {
1494 if (tree->priv->horizontal_scrolling)
1495 e_table_header_update_horizontal (tree->priv->header);
1496 }
1497
1498 static void
et_table_row_changed(ETableModel * table_model,gint row,ETree * tree)1499 et_table_row_changed (ETableModel *table_model,
1500 gint row,
1501 ETree *tree)
1502 {
1503 et_table_model_changed (table_model, tree);
1504 }
1505
1506 static void
et_table_cell_changed(ETableModel * table_model,gint view_col,gint row,ETree * tree)1507 et_table_cell_changed (ETableModel *table_model,
1508 gint view_col,
1509 gint row,
1510 ETree *tree)
1511 {
1512 et_table_model_changed (table_model, tree);
1513 }
1514
1515 static void
et_table_rows_deleted(ETableModel * table_model,gint row,gint count,ETree * tree)1516 et_table_rows_deleted (ETableModel *table_model,
1517 gint row,
1518 gint count,
1519 ETree *tree)
1520 {
1521 ETreeTableAdapter *adapter;
1522 ETreePath * node, * prev_node;
1523
1524 /* If the cursor is still valid after this deletion, we're done */
1525 if (e_selection_model_cursor_row (tree->priv->selection) >= 0
1526 || row == 0)
1527 return;
1528
1529 adapter = e_tree_get_table_adapter (tree);
1530 prev_node = e_tree_table_adapter_node_at_row (adapter, row - 1);
1531 node = e_tree_get_cursor (tree);
1532
1533 /* Check if the cursor is a child of the node directly before the
1534 * deleted region (implying that an expander was collapsed with
1535 * the cursor inside it) */
1536 while (node) {
1537 node = e_tree_model_node_get_parent (tree->priv->model, node);
1538 if (node == prev_node) {
1539 /* Set the cursor to the still-visible parent */
1540 e_tree_set_cursor (tree, prev_node);
1541 return;
1542 }
1543 }
1544 }
1545
1546 static void
e_tree_update_full_header_grouped_view(ETree * tree)1547 e_tree_update_full_header_grouped_view (ETree *tree)
1548 {
1549 gint ii, sz;
1550
1551 g_return_if_fail (E_IS_TREE (tree));
1552
1553 if (!tree->priv->full_header)
1554 return;
1555
1556 sz = e_table_header_count (tree->priv->full_header);
1557 for (ii = 0; ii < sz; ii++) {
1558 ETableCol *col;
1559
1560 col = e_table_header_get_column (tree->priv->full_header, ii);
1561 if (!col || !E_IS_CELL_TREE (col->ecell))
1562 continue;
1563
1564 e_cell_tree_set_grouped_view (E_CELL_TREE (col->ecell), tree->priv->grouped_view);
1565 }
1566 }
1567
1568 static void
et_connect_to_etta(ETree * tree)1569 et_connect_to_etta (ETree *tree)
1570 {
1571 tree->priv->table_model_change_id = g_signal_connect (
1572 tree->priv->etta, "model_changed",
1573 G_CALLBACK (et_table_model_changed), tree);
1574
1575 tree->priv->table_row_change_id = g_signal_connect (
1576 tree->priv->etta, "model_row_changed",
1577 G_CALLBACK (et_table_row_changed), tree);
1578
1579 tree->priv->table_cell_change_id = g_signal_connect (
1580 tree->priv->etta, "model_cell_changed",
1581 G_CALLBACK (et_table_cell_changed), tree);
1582
1583 tree->priv->table_rows_delete_id = g_signal_connect (
1584 tree->priv->etta, "model_rows_deleted",
1585 G_CALLBACK (et_table_rows_deleted), tree);
1586
1587 g_object_bind_property (tree, "sort-children-ascending",
1588 tree->priv->etta, "sort-children-ascending",
1589 G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE);
1590 }
1591
1592 static gboolean
et_real_construct(ETree * tree,ETreeModel * etm,ETableExtras * ete,ETableSpecification * specification,ETableState * state)1593 et_real_construct (ETree *tree,
1594 ETreeModel *etm,
1595 ETableExtras *ete,
1596 ETableSpecification *specification,
1597 ETableState *state)
1598 {
1599 GtkAdjustment *adjustment;
1600 GtkScrollable *scrollable;
1601 gint row = 0;
1602
1603 if (ete)
1604 g_object_ref (ete);
1605 else
1606 ete = e_table_extras_new ();
1607
1608 tree->priv->alternating_row_colors = specification->alternating_row_colors;
1609 tree->priv->horizontal_draw_grid = specification->horizontal_draw_grid;
1610 tree->priv->vertical_draw_grid = specification->vertical_draw_grid;
1611 tree->priv->draw_focus = specification->draw_focus;
1612 tree->priv->cursor_mode = specification->cursor_mode;
1613 tree->priv->full_header = e_table_spec_to_full_header (specification, ete);
1614
1615 e_tree_update_full_header_grouped_view (tree);
1616
1617 connect_header (tree, state);
1618
1619 tree->priv->horizontal_scrolling = specification->horizontal_scrolling;
1620
1621 tree->priv->model = etm;
1622 g_object_ref (etm);
1623
1624 tree->priv->etta = E_TREE_TABLE_ADAPTER (
1625 e_tree_table_adapter_new (
1626 tree->priv->model,
1627 tree->priv->sort_info,
1628 tree->priv->full_header));
1629
1630 et_connect_to_etta (tree);
1631
1632 g_object_set (
1633 tree->priv->selection,
1634 "model", tree->priv->model,
1635 "etta", tree->priv->etta,
1636 "selection_mode", specification->selection_mode,
1637 "cursor_mode", specification->cursor_mode,
1638 NULL);
1639
1640 g_signal_connect (
1641 tree->priv->selection, "selection_changed",
1642 G_CALLBACK (et_selection_model_selection_changed), tree);
1643 g_signal_connect (
1644 tree->priv->selection, "selection_row_changed",
1645 G_CALLBACK (et_selection_model_selection_row_changed), tree);
1646
1647 if (!specification->no_headers) {
1648 e_tree_setup_header (tree);
1649 }
1650 e_tree_setup_table (tree);
1651
1652 scrollable = GTK_SCROLLABLE (tree->priv->table_canvas);
1653
1654 adjustment = gtk_scrollable_get_vadjustment (scrollable);
1655 gtk_adjustment_set_step_increment (adjustment, 20);
1656
1657 adjustment = gtk_scrollable_get_hadjustment (scrollable);
1658 gtk_adjustment_set_step_increment (adjustment, 20);
1659
1660 if (!specification->no_headers) {
1661 /*
1662 * The header
1663 */
1664 gtk_table_attach (
1665 GTK_TABLE (tree),
1666 GTK_WIDGET (tree->priv->header_canvas),
1667 0, 1, 0 + row, 1 + row,
1668 GTK_FILL | GTK_EXPAND,
1669 GTK_FILL, 0, 0);
1670 row++;
1671 }
1672
1673 gtk_table_attach (
1674 GTK_TABLE (tree),
1675 GTK_WIDGET (tree->priv->table_canvas),
1676 0, 1, 0 + row, 1 + row,
1677 GTK_FILL | GTK_EXPAND,
1678 GTK_FILL | GTK_EXPAND,
1679 0, 0);
1680
1681 g_object_unref (ete);
1682
1683 return TRUE;
1684 }
1685
1686 /**
1687 * e_tree_construct:
1688 * @tree: The newly created #ETree object.
1689 * @etm: The model for this table.
1690 * @ete: An optional #ETableExtras. (%NULL is valid.)
1691 * @specification: an #ETableSpecification
1692 *
1693 * This is the internal implementation of e_tree_new() for use by
1694 * subclasses or language bindings. See e_tree_new() for details.
1695 *
1696 * Return value: %TRUE on success, %FALSE if an error occurred
1697 **/
1698 gboolean
e_tree_construct(ETree * tree,ETreeModel * etm,ETableExtras * ete,ETableSpecification * specification)1699 e_tree_construct (ETree *tree,
1700 ETreeModel *etm,
1701 ETableExtras *ete,
1702 ETableSpecification *specification)
1703 {
1704 ETableState *state;
1705
1706 g_return_val_if_fail (E_IS_TREE (tree), FALSE);
1707 g_return_val_if_fail (E_IS_TREE_MODEL (etm), FALSE);
1708 g_return_val_if_fail (ete == NULL || E_IS_TABLE_EXTRAS (ete), FALSE);
1709 g_return_val_if_fail (E_IS_TABLE_SPECIFICATION (specification), FALSE);
1710
1711 state = g_object_ref (specification->state);
1712
1713 et_real_construct (tree, etm, ete, specification, state);
1714
1715 tree->priv->spec = g_object_ref (specification);
1716 tree->priv->spec->allow_grouping = FALSE;
1717
1718 g_object_unref (state);
1719
1720 return TRUE;
1721 }
1722
1723 /**
1724 * e_tree_new:
1725 * @etm: The model for this tree
1726 * @ete: An optional #ETableExtras (%NULL is valid.)
1727 * @specification: an #ETableSpecification
1728 *
1729 * This function creates an #ETree from the given parameters. The
1730 * #ETreeModel is a tree model to be represented. The #ETableExtras
1731 * is an optional set of pixbufs, cells, and sorting functions to be
1732 * used when interpreting the spec. If you pass in %NULL it uses the
1733 * default #ETableExtras. (See e_table_extras_new()).
1734 *
1735 * @specification is the specification of the set of viewable columns and the
1736 * default sorting state and such. @state is an optional string specifying
1737 * the current sorting state and such.
1738 *
1739 * Return value:
1740 * The newly created #ETree or %NULL if there's an error.
1741 **/
1742 GtkWidget *
e_tree_new(ETreeModel * etm,ETableExtras * ete,ETableSpecification * specification)1743 e_tree_new (ETreeModel *etm,
1744 ETableExtras *ete,
1745 ETableSpecification *specification)
1746 {
1747 ETree *tree;
1748
1749 g_return_val_if_fail (E_IS_TREE_MODEL (etm), NULL);
1750 g_return_val_if_fail (ete == NULL || E_IS_TABLE_EXTRAS (ete), NULL);
1751 g_return_val_if_fail (E_IS_TABLE_SPECIFICATION (specification), NULL);
1752
1753 tree = g_object_new (E_TYPE_TREE, NULL);
1754
1755 if (!e_tree_construct (tree, etm, ete, specification)) {
1756 g_object_unref (tree);
1757 return NULL;
1758 }
1759
1760 return GTK_WIDGET (tree);
1761 }
1762
1763 void
e_tree_show_cursor_after_reflow(ETree * tree)1764 e_tree_show_cursor_after_reflow (ETree *tree)
1765 {
1766 g_return_if_fail (E_IS_TREE (tree));
1767
1768 tree->priv->show_cursor_after_reflow = TRUE;
1769 }
1770
1771 void
e_tree_set_cursor(ETree * tree,ETreePath path)1772 e_tree_set_cursor (ETree *tree,
1773 ETreePath path)
1774 {
1775 g_return_if_fail (E_IS_TREE (tree));
1776 g_return_if_fail (path != NULL);
1777
1778 e_tree_selection_model_select_single_path (
1779 E_TREE_SELECTION_MODEL (tree->priv->selection), path);
1780 e_tree_selection_model_change_cursor (
1781 E_TREE_SELECTION_MODEL (tree->priv->selection), path);
1782 }
1783
1784 ETreePath
e_tree_get_cursor(ETree * tree)1785 e_tree_get_cursor (ETree *tree)
1786 {
1787 return e_tree_selection_model_get_cursor (
1788 E_TREE_SELECTION_MODEL (tree->priv->selection));
1789 }
1790
1791 /* Standard functions */
1792 static void
et_foreach_recurse(ETreeModel * model,ETreePath path,ETreeForeachFunc callback,gpointer closure)1793 et_foreach_recurse (ETreeModel *model,
1794 ETreePath path,
1795 ETreeForeachFunc callback,
1796 gpointer closure)
1797 {
1798 ETreePath child;
1799
1800 callback (path, closure);
1801
1802 for (child = e_tree_model_node_get_first_child (E_TREE_MODEL (model), path);
1803 child;
1804 child = e_tree_model_node_get_next (E_TREE_MODEL (model), child)) {
1805 et_foreach_recurse (model, child, callback, closure);
1806 }
1807 }
1808
1809 void
e_tree_path_foreach(ETree * tree,ETreeForeachFunc callback,gpointer closure)1810 e_tree_path_foreach (ETree *tree,
1811 ETreeForeachFunc callback,
1812 gpointer closure)
1813 {
1814 ETreePath root;
1815
1816 g_return_if_fail (E_IS_TREE (tree));
1817
1818 root = e_tree_model_get_root (tree->priv->model);
1819
1820 if (root)
1821 et_foreach_recurse (tree->priv->model,
1822 root,
1823 callback,
1824 closure);
1825 }
1826
1827 static void
et_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)1828 et_get_property (GObject *object,
1829 guint property_id,
1830 GValue *value,
1831 GParamSpec *pspec)
1832 {
1833 ETree *tree = E_TREE (object);
1834
1835 switch (property_id) {
1836 case PROP_ETTA:
1837 g_value_set_object (value, tree->priv->etta);
1838 break;
1839
1840 case PROP_UNIFORM_ROW_HEIGHT:
1841 g_value_set_boolean (value, tree->priv->uniform_row_height);
1842 break;
1843
1844 case PROP_IS_EDITING:
1845 g_value_set_boolean (value, e_tree_is_editing (tree));
1846 break;
1847
1848 case PROP_ALWAYS_SEARCH:
1849 g_value_set_boolean (value, tree->priv->always_search);
1850 break;
1851
1852 case PROP_HADJUSTMENT:
1853 if (tree->priv->table_canvas)
1854 g_object_get_property (
1855 G_OBJECT (tree->priv->table_canvas),
1856 "hadjustment", value);
1857 else
1858 g_value_set_object (value, NULL);
1859 break;
1860
1861 case PROP_VADJUSTMENT:
1862 if (tree->priv->table_canvas)
1863 g_object_get_property (
1864 G_OBJECT (tree->priv->table_canvas),
1865 "vadjustment", value);
1866 else
1867 g_value_set_object (value, NULL);
1868 break;
1869
1870 case PROP_HSCROLL_POLICY:
1871 if (tree->priv->table_canvas)
1872 g_object_get_property (
1873 G_OBJECT (tree->priv->table_canvas),
1874 "hscroll-policy", value);
1875 else
1876 g_value_set_enum (value, 0);
1877 break;
1878
1879 case PROP_VSCROLL_POLICY:
1880 if (tree->priv->table_canvas)
1881 g_object_get_property (
1882 G_OBJECT (tree->priv->table_canvas),
1883 "vscroll-policy", value);
1884 else
1885 g_value_set_enum (value, 0);
1886 break;
1887
1888 case PROP_SORT_CHILDREN_ASCENDING:
1889 g_value_set_boolean (value, e_tree_get_sort_children_ascending (tree));
1890 break;
1891
1892 default:
1893 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1894 break;
1895 }
1896 }
1897
1898 typedef struct {
1899 gchar *arg;
1900 gboolean setting;
1901 } bool_closure;
1902
1903 static void
et_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)1904 et_set_property (GObject *object,
1905 guint property_id,
1906 const GValue *value,
1907 GParamSpec *pspec)
1908 {
1909 ETree *tree = E_TREE (object);
1910
1911 switch (property_id) {
1912 case PROP_LENGTH_THRESHOLD:
1913 tree->priv->length_threshold = g_value_get_int (value);
1914 if (tree->priv->item) {
1915 gnome_canvas_item_set (
1916 GNOME_CANVAS_ITEM (tree->priv->item),
1917 "length_threshold",
1918 tree->priv->length_threshold,
1919 NULL);
1920 }
1921 break;
1922
1923 case PROP_HORIZONTAL_DRAW_GRID:
1924 tree->priv->horizontal_draw_grid = g_value_get_boolean (value);
1925 if (tree->priv->item) {
1926 gnome_canvas_item_set (
1927 GNOME_CANVAS_ITEM (tree->priv->item),
1928 "horizontal_draw_grid",
1929 tree->priv->horizontal_draw_grid,
1930 NULL);
1931 }
1932 break;
1933
1934 case PROP_VERTICAL_DRAW_GRID:
1935 tree->priv->vertical_draw_grid = g_value_get_boolean (value);
1936 if (tree->priv->item) {
1937 gnome_canvas_item_set (
1938 GNOME_CANVAS_ITEM (tree->priv->item),
1939 "vertical_draw_grid",
1940 tree->priv->vertical_draw_grid,
1941 NULL);
1942 }
1943 break;
1944
1945 case PROP_DRAW_FOCUS:
1946 tree->priv->draw_focus = g_value_get_boolean (value);
1947 if (tree->priv->item) {
1948 gnome_canvas_item_set (
1949 GNOME_CANVAS_ITEM (tree->priv->item),
1950 "drawfocus",
1951 tree->priv->draw_focus,
1952 NULL);
1953 }
1954 break;
1955
1956 case PROP_UNIFORM_ROW_HEIGHT:
1957 tree->priv->uniform_row_height = g_value_get_boolean (value);
1958 if (tree->priv->item) {
1959 gnome_canvas_item_set (
1960 GNOME_CANVAS_ITEM (tree->priv->item),
1961 "uniform_row_height",
1962 tree->priv->uniform_row_height,
1963 NULL);
1964 }
1965 break;
1966
1967 case PROP_ALWAYS_SEARCH:
1968 if (tree->priv->always_search == g_value_get_boolean (value))
1969 return;
1970 tree->priv->always_search = g_value_get_boolean (value);
1971 clear_current_search_col (tree);
1972 break;
1973
1974 case PROP_HADJUSTMENT:
1975 if (tree->priv->table_canvas)
1976 g_object_set_property (
1977 G_OBJECT (tree->priv->table_canvas),
1978 "hadjustment", value);
1979 break;
1980
1981 case PROP_VADJUSTMENT:
1982 if (tree->priv->table_canvas)
1983 g_object_set_property (
1984 G_OBJECT (tree->priv->table_canvas),
1985 "vadjustment", value);
1986 break;
1987
1988 case PROP_HSCROLL_POLICY:
1989 if (tree->priv->table_canvas)
1990 g_object_set_property (
1991 G_OBJECT (tree->priv->table_canvas),
1992 "hscroll-policy", value);
1993 break;
1994
1995 case PROP_VSCROLL_POLICY:
1996 if (tree->priv->table_canvas)
1997 g_object_set_property (
1998 G_OBJECT (tree->priv->table_canvas),
1999 "vscroll-policy", value);
2000 break;
2001
2002 case PROP_SORT_CHILDREN_ASCENDING:
2003 e_tree_set_sort_children_ascending (tree, g_value_get_boolean (value));
2004 break;
2005 }
2006 }
2007
2008 /**
2009 * e_tree_get_model:
2010 * @tree: the ETree
2011 *
2012 * Returns the model upon which this ETree is based.
2013 *
2014 * Returns: the model
2015 **/
2016 ETreeModel *
e_tree_get_model(ETree * tree)2017 e_tree_get_model (ETree *tree)
2018 {
2019 g_return_val_if_fail (E_IS_TREE (tree), NULL);
2020
2021 return tree->priv->model;
2022 }
2023
2024 /**
2025 * e_tree_get_selection_model:
2026 * @tree: the ETree
2027 *
2028 * Returns the selection model of this ETree.
2029 *
2030 * Returns: the selection model
2031 **/
2032 ESelectionModel *
e_tree_get_selection_model(ETree * tree)2033 e_tree_get_selection_model (ETree *tree)
2034 {
2035 g_return_val_if_fail (E_IS_TREE (tree), NULL);
2036
2037 return tree->priv->selection;
2038 }
2039
2040 /**
2041 * e_tree_get_table_adapter:
2042 * @tree: the ETree
2043 *
2044 * Returns the table adapter this ETree uses.
2045 *
2046 * Returns: the model
2047 **/
2048 ETreeTableAdapter *
e_tree_get_table_adapter(ETree * tree)2049 e_tree_get_table_adapter (ETree *tree)
2050 {
2051 g_return_val_if_fail (E_IS_TREE (tree), NULL);
2052
2053 return tree->priv->etta;
2054 }
2055
2056 ETableItem *
e_tree_get_item(ETree * tree)2057 e_tree_get_item (ETree *tree)
2058 {
2059 g_return_val_if_fail (E_IS_TREE (tree), NULL);
2060
2061 return E_TABLE_ITEM (tree->priv->item);
2062 }
2063
2064 GnomeCanvasItem *
e_tree_get_header_item(ETree * tree)2065 e_tree_get_header_item (ETree *tree)
2066 {
2067 g_return_val_if_fail (E_IS_TREE (tree), NULL);
2068
2069 return tree->priv->header_item;
2070 }
2071
2072 struct _ETreeDragSourceSite
2073 {
2074 GdkModifierType start_button_mask;
2075 GtkTargetList *target_list; /* Targets for drag data */
2076 GdkDragAction actions; /* Possible actions */
2077 GdkPixbuf *pixbuf; /* Icon for drag data */
2078
2079 /* Stored button press information to detect drag beginning */
2080 gint state;
2081 gint x, y;
2082 gint row, col;
2083 };
2084
2085 typedef enum
2086 {
2087 GTK_DRAG_STATUS_DRAG,
2088 GTK_DRAG_STATUS_WAIT,
2089 GTK_DRAG_STATUS_DROP
2090 } GtkDragStatus;
2091
2092 typedef struct _GtkDragDestInfo GtkDragDestInfo;
2093 typedef struct _GtkDragSourceInfo GtkDragSourceInfo;
2094
2095 struct _GtkDragDestInfo
2096 {
2097 GtkWidget *widget; /* Widget in which drag is in */
2098 GdkDragContext *context; /* Drag context */
2099 GtkDragSourceInfo *proxy_source; /* Set if this is a proxy drag */
2100 GtkSelectionData *proxy_data; /* Set while retrieving proxied data */
2101 guint dropped : 1; /* Set after we receive a drop */
2102 guint32 proxy_drop_time; /* Timestamp for proxied drop */
2103 guint proxy_drop_wait : 1; /* Set if we are waiting for a
2104 * status reply before sending
2105 * a proxied drop on.
2106 */
2107 gint drop_x, drop_y; /* Position of drop */
2108 };
2109
2110 struct _GtkDragSourceInfo
2111 {
2112 GtkWidget *widget;
2113 GtkTargetList *target_list; /* Targets for drag data */
2114 GdkDragAction possible_actions; /* Actions allowed by source */
2115 GdkDragContext *context; /* drag context */
2116 GtkWidget *icon_window; /* Window for drag */
2117 GtkWidget *ipc_widget; /* GtkInvisible for grab, message passing */
2118 GdkCursor *cursor; /* Cursor for drag */
2119 gint hot_x, hot_y; /* Hot spot for drag */
2120 gint button; /* mouse button starting drag */
2121
2122 GtkDragStatus status; /* drag status */
2123 GdkEvent *last_event; /* motion event waiting for response */
2124
2125 gint start_x, start_y; /* Initial position */
2126 gint cur_x, cur_y; /* Current Position */
2127
2128 GList *selections; /* selections we've claimed */
2129
2130 GtkDragDestInfo *proxy_dest; /* Set if this is a proxy drag */
2131
2132 guint drop_timeout; /* Timeout for aborting drop */
2133 guint destroy_icon : 1; /* If true, destroy icon_window
2134 */
2135 };
2136
2137 /* Drag & drop stuff. */
2138
2139 /* Source side */
2140
2141 static gint
et_real_start_drag(ETree * tree,gint row,ETreePath path,gint col,GdkEvent * event)2142 et_real_start_drag (ETree *tree,
2143 gint row,
2144 ETreePath path,
2145 gint col,
2146 GdkEvent *event)
2147 {
2148 GtkDragSourceInfo *info;
2149 GdkDragContext *context;
2150 ETreeDragSourceSite *site;
2151
2152 if (tree->priv->do_drag) {
2153 site = tree->priv->site;
2154
2155 site->state = 0;
2156 context = e_tree_drag_begin (
2157 tree, row, col,
2158 site->target_list,
2159 site->actions,
2160 1, event);
2161
2162 if (context) {
2163 info = g_dataset_get_data (context, "gtk-info");
2164
2165 if (info && !info->icon_window) {
2166 if (site->pixbuf)
2167 gtk_drag_set_icon_pixbuf (
2168 context,
2169 site->pixbuf,
2170 -2, -2);
2171 else
2172 gtk_drag_set_icon_default (context);
2173 }
2174 }
2175 return TRUE;
2176 }
2177 return FALSE;
2178 }
2179
2180 void
e_tree_drag_source_set(ETree * tree,GdkModifierType start_button_mask,const GtkTargetEntry * targets,gint n_targets,GdkDragAction actions)2181 e_tree_drag_source_set (ETree *tree,
2182 GdkModifierType start_button_mask,
2183 const GtkTargetEntry *targets,
2184 gint n_targets,
2185 GdkDragAction actions)
2186 {
2187 ETreeDragSourceSite *site;
2188 GtkWidget *canvas;
2189
2190 g_return_if_fail (E_IS_TREE (tree));
2191
2192 canvas = GTK_WIDGET (tree->priv->table_canvas);
2193 site = tree->priv->site;
2194
2195 tree->priv->do_drag = TRUE;
2196
2197 gtk_widget_add_events (
2198 canvas,
2199 gtk_widget_get_events (canvas) |
2200 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
2201 GDK_BUTTON_MOTION_MASK | GDK_STRUCTURE_MASK);
2202
2203 if (site) {
2204 if (site->target_list)
2205 gtk_target_list_unref (site->target_list);
2206 } else {
2207 site = g_new0 (ETreeDragSourceSite, 1);
2208 tree->priv->site = site;
2209 }
2210
2211 site->start_button_mask = start_button_mask;
2212
2213 if (targets)
2214 site->target_list = gtk_target_list_new (targets, n_targets);
2215 else
2216 site->target_list = NULL;
2217
2218 site->actions = actions;
2219 }
2220
2221 void
e_tree_drag_source_unset(ETree * tree)2222 e_tree_drag_source_unset (ETree *tree)
2223 {
2224 ETreeDragSourceSite *site;
2225
2226 g_return_if_fail (E_IS_TREE (tree));
2227
2228 site = tree->priv->site;
2229
2230 if (site) {
2231 if (site->target_list)
2232 gtk_target_list_unref (site->target_list);
2233 g_free (site);
2234 tree->priv->site = NULL;
2235 }
2236 }
2237
2238 /* There probably should be functions for setting the targets
2239 * as a GtkTargetList
2240 */
2241
2242 GdkDragContext *
e_tree_drag_begin(ETree * tree,gint row,gint col,GtkTargetList * targets,GdkDragAction actions,gint button,GdkEvent * event)2243 e_tree_drag_begin (ETree *tree,
2244 gint row,
2245 gint col,
2246 GtkTargetList *targets,
2247 GdkDragAction actions,
2248 gint button,
2249 GdkEvent *event)
2250 {
2251 ETreePath path;
2252
2253 g_return_val_if_fail (E_IS_TREE (tree), NULL);
2254
2255 path = e_tree_table_adapter_node_at_row (tree->priv->etta, row);
2256
2257 tree->priv->drag_row = row;
2258 tree->priv->drag_path = path;
2259 tree->priv->drag_col = col;
2260
2261 return gtk_drag_begin (
2262 GTK_WIDGET (tree->priv->table_canvas),
2263 targets,
2264 actions,
2265 button,
2266 event);
2267 }
2268
2269 /**
2270 * e_tree_is_dragging:
2271 * @tree: An #ETree widget
2272 *
2273 * Returns whether is @tree in a drag&drop operation.
2274 **/
2275 gboolean
e_tree_is_dragging(ETree * tree)2276 e_tree_is_dragging (ETree *tree)
2277 {
2278 g_return_val_if_fail (E_IS_TREE (tree), FALSE);
2279
2280 return tree->priv->is_dragging;
2281 }
2282
2283 /**
2284 * e_tree_get_cell_at:
2285 * @tree: An ETree widget
2286 * @x: X coordinate for the pixel
2287 * @y: Y coordinate for the pixel
2288 * @row_return: Pointer to return the row value
2289 * @col_return: Pointer to return the column value
2290 *
2291 * Return the row and column for the cell in which the pixel at (@x, @y) is
2292 * contained.
2293 **/
2294 void
e_tree_get_cell_at(ETree * tree,gint x,gint y,gint * row_return,gint * col_return)2295 e_tree_get_cell_at (ETree *tree,
2296 gint x,
2297 gint y,
2298 gint *row_return,
2299 gint *col_return)
2300 {
2301 GtkAdjustment *adjustment;
2302 GtkScrollable *scrollable;
2303
2304 g_return_if_fail (E_IS_TREE (tree));
2305 g_return_if_fail (row_return != NULL);
2306 g_return_if_fail (col_return != NULL);
2307
2308 /* FIXME it would be nice if it could handle a NULL row_return or
2309 * col_return gracefully. */
2310
2311 if (row_return)
2312 *row_return = -1;
2313 if (col_return)
2314 *col_return = -1;
2315
2316 scrollable = GTK_SCROLLABLE (tree->priv->table_canvas);
2317
2318 adjustment = gtk_scrollable_get_hadjustment (scrollable);
2319 x += gtk_adjustment_get_value (adjustment);
2320
2321 adjustment = gtk_scrollable_get_vadjustment (scrollable);
2322 y += gtk_adjustment_get_value (adjustment);
2323
2324 e_table_item_compute_location (
2325 E_TABLE_ITEM (tree->priv->item),
2326 &x, &y, row_return, col_return);
2327 }
2328
2329 /**
2330 * e_tree_get_cell_geometry:
2331 * @tree: The tree.
2332 * @row: The row to get the geometry of.
2333 * @col: The col to get the geometry of.
2334 * @x_return: Returns the x coordinate of the upper right hand corner
2335 * of the cell with respect to the widget.
2336 * @y_return: Returns the y coordinate of the upper right hand corner
2337 * of the cell with respect to the widget.
2338 * @width_return: Returns the width of the cell.
2339 * @height_return: Returns the height of the cell.
2340 *
2341 * Computes the data about this cell.
2342 **/
2343 void
e_tree_get_cell_geometry(ETree * tree,gint row,gint col,gint * x_return,gint * y_return,gint * width_return,gint * height_return)2344 e_tree_get_cell_geometry (ETree *tree,
2345 gint row,
2346 gint col,
2347 gint *x_return,
2348 gint *y_return,
2349 gint *width_return,
2350 gint *height_return)
2351 {
2352 GtkAdjustment *adjustment;
2353 GtkScrollable *scrollable;
2354
2355 g_return_if_fail (E_IS_TREE (tree));
2356 g_return_if_fail (row >= 0);
2357 g_return_if_fail (col >= 0);
2358
2359 /* FIXME it would be nice if it could handle a NULL row_return or
2360 * col_return gracefully. */
2361
2362 e_table_item_get_cell_geometry (
2363 E_TABLE_ITEM (tree->priv->item),
2364 &row, &col, x_return, y_return,
2365 width_return, height_return);
2366
2367 scrollable = GTK_SCROLLABLE (tree->priv->table_canvas);
2368
2369 if (x_return) {
2370 adjustment = gtk_scrollable_get_hadjustment (scrollable);
2371 (*x_return) -= gtk_adjustment_get_value (adjustment);
2372 }
2373
2374 if (y_return) {
2375 adjustment = gtk_scrollable_get_vadjustment (scrollable);
2376 (*y_return) -= gtk_adjustment_get_value (adjustment);
2377 }
2378 }
2379
2380 static void
et_drag_begin(GtkWidget * widget,GdkDragContext * context,ETree * tree)2381 et_drag_begin (GtkWidget *widget,
2382 GdkDragContext *context,
2383 ETree *tree)
2384 {
2385 tree->priv->is_dragging = TRUE;
2386
2387 g_signal_emit (
2388 tree,
2389 signals[TREE_DRAG_BEGIN], 0,
2390 tree->priv->drag_row,
2391 tree->priv->drag_path,
2392 tree->priv->drag_col,
2393 context);
2394 }
2395
2396 static void
et_drag_end(GtkWidget * widget,GdkDragContext * context,ETree * tree)2397 et_drag_end (GtkWidget *widget,
2398 GdkDragContext *context,
2399 ETree *tree)
2400 {
2401 tree->priv->is_dragging = FALSE;
2402
2403 g_signal_emit (
2404 tree,
2405 signals[TREE_DRAG_END], 0,
2406 tree->priv->drag_row,
2407 tree->priv->drag_path,
2408 tree->priv->drag_col,
2409 context);
2410 }
2411
2412 static void
et_drag_data_get(GtkWidget * widget,GdkDragContext * context,GtkSelectionData * selection_data,guint info,guint time,ETree * tree)2413 et_drag_data_get (GtkWidget *widget,
2414 GdkDragContext *context,
2415 GtkSelectionData *selection_data,
2416 guint info,
2417 guint time,
2418 ETree *tree)
2419 {
2420 g_signal_emit (
2421 tree,
2422 signals[TREE_DRAG_DATA_GET], 0,
2423 tree->priv->drag_row,
2424 tree->priv->drag_path,
2425 tree->priv->drag_col,
2426 context,
2427 selection_data,
2428 info,
2429 time);
2430 }
2431
2432 static void
et_drag_data_delete(GtkWidget * widget,GdkDragContext * context,ETree * tree)2433 et_drag_data_delete (GtkWidget *widget,
2434 GdkDragContext *context,
2435 ETree *tree)
2436 {
2437 g_signal_emit (
2438 tree,
2439 signals[TREE_DRAG_DATA_DELETE], 0,
2440 tree->priv->drag_row,
2441 tree->priv->drag_path,
2442 tree->priv->drag_col,
2443 context);
2444 }
2445
2446 static gboolean
do_drag_motion(ETree * tree,GdkDragContext * context,gint x,gint y,guint time)2447 do_drag_motion (ETree *tree,
2448 GdkDragContext *context,
2449 gint x,
2450 gint y,
2451 guint time)
2452 {
2453 gboolean ret_val = FALSE;
2454 gint row, col;
2455 ETreePath path;
2456
2457 e_tree_get_cell_at (tree, x, y, &row, &col);
2458
2459 if (row != tree->priv->drop_row && col != tree->priv->drop_col) {
2460 g_signal_emit (
2461 tree, signals[TREE_DRAG_LEAVE], 0,
2462 tree->priv->drop_row,
2463 tree->priv->drop_path,
2464 tree->priv->drop_col,
2465 context,
2466 time);
2467 }
2468
2469 path = e_tree_table_adapter_node_at_row (tree->priv->etta, row);
2470
2471 tree->priv->drop_row = row;
2472 tree->priv->drop_path = path;
2473 tree->priv->drop_col = col;
2474 g_signal_emit (
2475 tree, signals[TREE_DRAG_MOTION], 0,
2476 tree->priv->drop_row,
2477 tree->priv->drop_path,
2478 tree->priv->drop_col,
2479 context,
2480 x, y,
2481 time,
2482 &ret_val);
2483
2484 return ret_val;
2485 }
2486
2487 static gboolean
scroll_timeout(gpointer data)2488 scroll_timeout (gpointer data)
2489 {
2490 ETree *tree = data;
2491 gint dx = 0, dy = 0;
2492 GtkAdjustment *adjustment;
2493 GtkScrollable *scrollable;
2494 gdouble old_h_value;
2495 gdouble new_h_value;
2496 gdouble old_v_value;
2497 gdouble new_v_value;
2498 gdouble page_size;
2499 gdouble lower;
2500 gdouble upper;
2501
2502 if (tree->priv->scroll_direction & ET_SCROLL_DOWN)
2503 dy += 20;
2504 if (tree->priv->scroll_direction & ET_SCROLL_UP)
2505 dy -= 20;
2506
2507 if (tree->priv->scroll_direction & ET_SCROLL_RIGHT)
2508 dx += 20;
2509 if (tree->priv->scroll_direction & ET_SCROLL_LEFT)
2510 dx -= 20;
2511
2512 scrollable = GTK_SCROLLABLE (tree->priv->table_canvas);
2513
2514 adjustment = gtk_scrollable_get_hadjustment (scrollable);
2515
2516 page_size = gtk_adjustment_get_page_size (adjustment);
2517 lower = gtk_adjustment_get_lower (adjustment);
2518 upper = gtk_adjustment_get_upper (adjustment);
2519
2520 old_h_value = gtk_adjustment_get_value (adjustment);
2521 new_h_value = CLAMP (old_h_value + dx, lower, upper - page_size);
2522
2523 gtk_adjustment_set_value (adjustment, new_h_value);
2524
2525 adjustment = gtk_scrollable_get_vadjustment (scrollable);
2526
2527 page_size = gtk_adjustment_get_page_size (adjustment);
2528 lower = gtk_adjustment_get_lower (adjustment);
2529 upper = gtk_adjustment_get_upper (adjustment);
2530
2531 old_v_value = gtk_adjustment_get_value (adjustment);
2532 new_v_value = CLAMP (old_v_value + dy, lower, upper - page_size);
2533
2534 gtk_adjustment_set_value (adjustment, new_v_value);
2535
2536 if (new_h_value != old_h_value || new_v_value != old_v_value)
2537 do_drag_motion (
2538 tree,
2539 tree->priv->last_drop_context,
2540 tree->priv->last_drop_x,
2541 tree->priv->last_drop_y,
2542 tree->priv->last_drop_time);
2543
2544 return TRUE;
2545 }
2546
2547 static void
scroll_on(ETree * tree,guint scroll_direction)2548 scroll_on (ETree *tree,
2549 guint scroll_direction)
2550 {
2551 if (tree->priv->scroll_idle_id == 0 ||
2552 scroll_direction != tree->priv->scroll_direction) {
2553 if (tree->priv->scroll_idle_id != 0)
2554 g_source_remove (tree->priv->scroll_idle_id);
2555 tree->priv->scroll_direction = scroll_direction;
2556 tree->priv->scroll_idle_id =
2557 e_named_timeout_add (100, scroll_timeout, tree);
2558 }
2559 }
2560
2561 static void
scroll_off(ETree * tree)2562 scroll_off (ETree *tree)
2563 {
2564 if (tree->priv->scroll_idle_id) {
2565 g_source_remove (tree->priv->scroll_idle_id);
2566 tree->priv->scroll_idle_id = 0;
2567 }
2568 }
2569
2570 static gboolean
hover_timeout(gpointer data)2571 hover_timeout (gpointer data)
2572 {
2573 ETree *tree = data;
2574 gint x = tree->priv->hover_x;
2575 gint y = tree->priv->hover_y;
2576 gint row, col;
2577 ETreePath path;
2578
2579 e_tree_get_cell_at (tree, x, y, &row, &col);
2580
2581 path = e_tree_table_adapter_node_at_row (tree->priv->etta, row);
2582 if (path && e_tree_model_node_is_expandable (tree->priv->model, path)) {
2583 if (!e_tree_table_adapter_node_is_expanded (tree->priv->etta, path)) {
2584 tree->priv->expanded_list = g_list_prepend (
2585 tree->priv->expanded_list,
2586 e_tree_model_get_save_id (
2587 tree->priv->model, path));
2588
2589 e_tree_table_adapter_node_set_expanded (
2590 tree->priv->etta, path, TRUE);
2591 }
2592 }
2593
2594 return TRUE;
2595 }
2596
2597 static void
hover_on(ETree * tree,gint x,gint y)2598 hover_on (ETree *tree,
2599 gint x,
2600 gint y)
2601 {
2602 tree->priv->hover_x = x;
2603 tree->priv->hover_y = y;
2604 if (tree->priv->hover_idle_id != 0)
2605 g_source_remove (tree->priv->hover_idle_id);
2606 tree->priv->hover_idle_id =
2607 e_named_timeout_add (500, hover_timeout, tree);
2608 }
2609
2610 static void
hover_off(ETree * tree)2611 hover_off (ETree *tree)
2612 {
2613 if (tree->priv->hover_idle_id) {
2614 g_source_remove (tree->priv->hover_idle_id);
2615 tree->priv->hover_idle_id = 0;
2616 }
2617 }
2618
2619 static void
collapse_drag(ETree * tree,ETreePath drop)2620 collapse_drag (ETree *tree,
2621 ETreePath drop)
2622 {
2623 GList *list;
2624
2625 /* We only want to leave open parents of the node dropped in.
2626 * Not the node itself. */
2627 if (drop) {
2628 drop = e_tree_model_node_get_parent (tree->priv->model, drop);
2629 }
2630
2631 for (list = tree->priv->expanded_list; list; list = list->next) {
2632 gchar *save_id = list->data;
2633 ETreePath path;
2634
2635 path = e_tree_model_get_node_by_id (tree->priv->model, save_id);
2636 if (path) {
2637 ETreePath search;
2638 gboolean found = FALSE;
2639
2640 for (search = drop; search;
2641 search = e_tree_model_node_get_parent (
2642 tree->priv->model, search)) {
2643 if (path == search) {
2644 found = TRUE;
2645 break;
2646 }
2647 }
2648
2649 if (!found)
2650 e_tree_table_adapter_node_set_expanded (
2651 tree->priv->etta, path, FALSE);
2652 }
2653 g_free (save_id);
2654 }
2655 g_list_free (tree->priv->expanded_list);
2656 tree->priv->expanded_list = NULL;
2657 }
2658
2659 static void
context_destroyed(gpointer data,GObject * ctx)2660 context_destroyed (gpointer data,
2661 GObject *ctx)
2662 {
2663 ETree *tree = data;
2664 if (tree->priv) {
2665 tree->priv->last_drop_x = 0;
2666 tree->priv->last_drop_y = 0;
2667 tree->priv->last_drop_time = 0;
2668 tree->priv->last_drop_context = NULL;
2669 collapse_drag (tree, NULL);
2670 scroll_off (tree);
2671 hover_off (tree);
2672 }
2673 g_object_unref (tree);
2674 }
2675
2676 static void
context_connect(ETree * tree,GdkDragContext * context)2677 context_connect (ETree *tree,
2678 GdkDragContext *context)
2679 {
2680 if (context == tree->priv->last_drop_context)
2681 return;
2682
2683 if (tree->priv->last_drop_context)
2684 g_object_weak_unref (
2685 G_OBJECT (tree->priv->last_drop_context),
2686 context_destroyed, tree);
2687 else
2688 g_object_ref (tree);
2689
2690 g_object_weak_ref (G_OBJECT (context), context_destroyed, tree);
2691 }
2692
2693 static void
et_drag_leave(GtkWidget * widget,GdkDragContext * context,guint time,ETree * tree)2694 et_drag_leave (GtkWidget *widget,
2695 GdkDragContext *context,
2696 guint time,
2697 ETree *tree)
2698 {
2699 g_signal_emit (
2700 tree,
2701 signals[TREE_DRAG_LEAVE], 0,
2702 tree->priv->drop_row,
2703 tree->priv->drop_path,
2704 tree->priv->drop_col,
2705 context,
2706 time);
2707 tree->priv->drop_row = -1;
2708 tree->priv->drop_col = -1;
2709
2710 scroll_off (tree);
2711 hover_off (tree);
2712 }
2713
2714 static gboolean
et_drag_motion(GtkWidget * widget,GdkDragContext * context,gint x,gint y,guint time,ETree * tree)2715 et_drag_motion (GtkWidget *widget,
2716 GdkDragContext *context,
2717 gint x,
2718 gint y,
2719 guint time,
2720 ETree *tree)
2721 {
2722 GtkAllocation allocation;
2723 gint ret_val;
2724 guint direction = 0;
2725
2726 tree->priv->last_drop_x = x;
2727 tree->priv->last_drop_y = y;
2728 tree->priv->last_drop_time = time;
2729 context_connect (tree, context);
2730 tree->priv->last_drop_context = context;
2731
2732 if (tree->priv->hover_idle_id != 0) {
2733 if (abs (tree->priv->hover_x - x) > 3 ||
2734 abs (tree->priv->hover_y - y) > 3) {
2735 hover_on (tree, x, y);
2736 }
2737 } else {
2738 hover_on (tree, x, y);
2739 }
2740
2741 ret_val = do_drag_motion (tree, context, x, y, time);
2742
2743 gtk_widget_get_allocation (widget, &allocation);
2744
2745 if (y < 20)
2746 direction |= ET_SCROLL_UP;
2747 if (y > allocation.height - 20)
2748 direction |= ET_SCROLL_DOWN;
2749 if (x < 20)
2750 direction |= ET_SCROLL_LEFT;
2751 if (x > allocation.width - 20)
2752 direction |= ET_SCROLL_RIGHT;
2753
2754 if (direction != 0)
2755 scroll_on (tree, direction);
2756 else
2757 scroll_off (tree);
2758
2759 return ret_val;
2760 }
2761
2762 static gboolean
et_drag_drop(GtkWidget * widget,GdkDragContext * context,gint x,gint y,guint time,ETree * tree)2763 et_drag_drop (GtkWidget *widget,
2764 GdkDragContext *context,
2765 gint x,
2766 gint y,
2767 guint time,
2768 ETree *tree)
2769 {
2770 gboolean ret_val = FALSE;
2771 gint row, col;
2772 ETreePath path;
2773
2774 e_tree_get_cell_at (tree, x, y, &row, &col);
2775
2776 path = e_tree_table_adapter_node_at_row (tree->priv->etta, row);
2777
2778 if (row != tree->priv->drop_row && col != tree->priv->drop_row) {
2779 g_signal_emit (
2780 tree, signals[TREE_DRAG_LEAVE], 0,
2781 tree->priv->drop_row,
2782 tree->priv->drop_path,
2783 tree->priv->drop_col,
2784 context,
2785 time);
2786 g_signal_emit (
2787 tree, signals[TREE_DRAG_MOTION], 0,
2788 row,
2789 path,
2790 col,
2791 context,
2792 x,
2793 y,
2794 time,
2795 &ret_val);
2796 }
2797 tree->priv->drop_row = row;
2798 tree->priv->drop_path = path;
2799 tree->priv->drop_col = col;
2800
2801 g_signal_emit (
2802 tree, signals[TREE_DRAG_DROP], 0,
2803 tree->priv->drop_row,
2804 tree->priv->drop_path,
2805 tree->priv->drop_col,
2806 context,
2807 x,
2808 y,
2809 time,
2810 &ret_val);
2811
2812 tree->priv->drop_row = -1;
2813 tree->priv->drop_path = NULL;
2814 tree->priv->drop_col = -1;
2815
2816 collapse_drag (tree, path);
2817
2818 scroll_off (tree);
2819 return ret_val;
2820 }
2821
2822 static void
et_drag_data_received(GtkWidget * widget,GdkDragContext * context,gint x,gint y,GtkSelectionData * selection_data,guint info,guint time,ETree * tree)2823 et_drag_data_received (GtkWidget *widget,
2824 GdkDragContext *context,
2825 gint x,
2826 gint y,
2827 GtkSelectionData *selection_data,
2828 guint info,
2829 guint time,
2830 ETree *tree)
2831 {
2832 gint row, col;
2833 ETreePath path;
2834
2835 e_tree_get_cell_at (tree, x, y, &row, &col);
2836
2837 path = e_tree_table_adapter_node_at_row (tree->priv->etta, row);
2838 g_signal_emit (
2839 tree, signals[TREE_DRAG_DATA_RECEIVED], 0,
2840 row,
2841 path,
2842 col,
2843 context,
2844 x,
2845 y,
2846 selection_data,
2847 info,
2848 time);
2849 }
2850
2851 static void
e_tree_class_init(ETreeClass * class)2852 e_tree_class_init (ETreeClass *class)
2853 {
2854 GObjectClass *object_class;
2855 GtkWidgetClass *widget_class;
2856
2857 g_type_class_add_private (class, sizeof (ETreePrivate));
2858
2859 object_class = G_OBJECT_CLASS (class);
2860 object_class->dispose = et_dispose;
2861 object_class->set_property = et_set_property;
2862 object_class->get_property = et_get_property;
2863
2864 widget_class = GTK_WIDGET_CLASS (class);
2865 widget_class->grab_focus = et_grab_focus;
2866 widget_class->unrealize = et_unrealize;
2867 widget_class->style_updated = et_canvas_style_updated;
2868 widget_class->focus = et_focus;
2869
2870 class->start_drag = et_real_start_drag;
2871
2872 signals[CURSOR_CHANGE] = g_signal_new (
2873 "cursor_change",
2874 G_OBJECT_CLASS_TYPE (object_class),
2875 G_SIGNAL_RUN_LAST,
2876 G_STRUCT_OFFSET (ETreeClass, cursor_change),
2877 NULL, NULL,
2878 e_marshal_VOID__INT_POINTER,
2879 G_TYPE_NONE, 2,
2880 G_TYPE_INT,
2881 G_TYPE_POINTER);
2882
2883 signals[CURSOR_ACTIVATED] = g_signal_new (
2884 "cursor_activated",
2885 G_OBJECT_CLASS_TYPE (object_class),
2886 G_SIGNAL_RUN_LAST,
2887 G_STRUCT_OFFSET (ETreeClass, cursor_activated),
2888 NULL, NULL,
2889 e_marshal_VOID__INT_POINTER,
2890 G_TYPE_NONE, 2,
2891 G_TYPE_INT,
2892 G_TYPE_POINTER);
2893
2894 signals[SELECTION_CHANGE] = g_signal_new (
2895 "selection_change",
2896 G_OBJECT_CLASS_TYPE (object_class),
2897 G_SIGNAL_RUN_LAST,
2898 G_STRUCT_OFFSET (ETreeClass, selection_change),
2899 NULL, NULL,
2900 g_cclosure_marshal_VOID__VOID,
2901 G_TYPE_NONE, 0);
2902
2903 signals[DOUBLE_CLICK] = g_signal_new (
2904 "double_click",
2905 G_OBJECT_CLASS_TYPE (object_class),
2906 G_SIGNAL_RUN_LAST,
2907 G_STRUCT_OFFSET (ETreeClass, double_click),
2908 NULL, NULL,
2909 e_marshal_VOID__INT_POINTER_INT_BOXED,
2910 G_TYPE_NONE, 4,
2911 G_TYPE_INT,
2912 G_TYPE_POINTER,
2913 G_TYPE_INT,
2914 GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
2915
2916 signals[RIGHT_CLICK] = g_signal_new (
2917 "right_click",
2918 G_OBJECT_CLASS_TYPE (object_class),
2919 G_SIGNAL_RUN_LAST,
2920 G_STRUCT_OFFSET (ETreeClass, right_click),
2921 g_signal_accumulator_true_handled, NULL,
2922 e_marshal_BOOLEAN__INT_POINTER_INT_BOXED,
2923 G_TYPE_BOOLEAN, 4,
2924 G_TYPE_INT,
2925 G_TYPE_POINTER,
2926 G_TYPE_INT,
2927 GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
2928
2929 signals[CLICK] = g_signal_new (
2930 "click",
2931 G_OBJECT_CLASS_TYPE (object_class),
2932 G_SIGNAL_RUN_LAST,
2933 G_STRUCT_OFFSET (ETreeClass, click),
2934 g_signal_accumulator_true_handled, NULL,
2935 e_marshal_BOOLEAN__INT_POINTER_INT_BOXED,
2936 G_TYPE_BOOLEAN, 4,
2937 G_TYPE_INT,
2938 G_TYPE_POINTER,
2939 G_TYPE_INT,
2940 GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
2941
2942 signals[KEY_PRESS] = g_signal_new (
2943 "key_press",
2944 G_OBJECT_CLASS_TYPE (object_class),
2945 G_SIGNAL_RUN_LAST,
2946 G_STRUCT_OFFSET (ETreeClass, key_press),
2947 g_signal_accumulator_true_handled, NULL,
2948 e_marshal_BOOLEAN__INT_POINTER_INT_BOXED,
2949 G_TYPE_BOOLEAN, 4,
2950 G_TYPE_INT,
2951 G_TYPE_POINTER,
2952 G_TYPE_INT,
2953 GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
2954
2955 signals[START_DRAG] = g_signal_new (
2956 "start_drag",
2957 G_OBJECT_CLASS_TYPE (object_class),
2958 G_SIGNAL_RUN_LAST,
2959 G_STRUCT_OFFSET (ETreeClass, start_drag),
2960 NULL, NULL,
2961 e_marshal_VOID__INT_POINTER_INT_BOXED,
2962 G_TYPE_NONE, 4,
2963 G_TYPE_INT,
2964 G_TYPE_POINTER,
2965 G_TYPE_INT,
2966 GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
2967
2968 signals[STATE_CHANGE] = g_signal_new (
2969 "state_change",
2970 G_OBJECT_CLASS_TYPE (object_class),
2971 G_SIGNAL_RUN_LAST,
2972 G_STRUCT_OFFSET (ETreeClass, state_change),
2973 NULL, NULL,
2974 g_cclosure_marshal_VOID__VOID,
2975 G_TYPE_NONE, 0);
2976
2977 signals[WHITE_SPACE_EVENT] = g_signal_new (
2978 "white_space_event",
2979 G_OBJECT_CLASS_TYPE (object_class),
2980 G_SIGNAL_RUN_LAST,
2981 G_STRUCT_OFFSET (ETreeClass, white_space_event),
2982 g_signal_accumulator_true_handled, NULL,
2983 e_marshal_BOOLEAN__POINTER,
2984 G_TYPE_BOOLEAN, 1,
2985 GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
2986
2987 signals[TREE_DRAG_BEGIN] = g_signal_new (
2988 "tree_drag_begin",
2989 G_OBJECT_CLASS_TYPE (object_class),
2990 G_SIGNAL_RUN_LAST,
2991 G_STRUCT_OFFSET (ETreeClass, tree_drag_begin),
2992 NULL, NULL,
2993 e_marshal_VOID__INT_POINTER_INT_BOXED,
2994 G_TYPE_NONE, 4,
2995 G_TYPE_INT,
2996 G_TYPE_POINTER,
2997 G_TYPE_INT,
2998 GDK_TYPE_DRAG_CONTEXT);
2999
3000 signals[TREE_DRAG_END] = g_signal_new (
3001 "tree_drag_end",
3002 G_OBJECT_CLASS_TYPE (object_class),
3003 G_SIGNAL_RUN_LAST,
3004 G_STRUCT_OFFSET (ETreeClass, tree_drag_end),
3005 NULL, NULL,
3006 e_marshal_VOID__INT_POINTER_INT_BOXED,
3007 G_TYPE_NONE, 4,
3008 G_TYPE_INT,
3009 G_TYPE_POINTER,
3010 G_TYPE_INT,
3011 GDK_TYPE_DRAG_CONTEXT);
3012
3013 signals[TREE_DRAG_DATA_GET] = g_signal_new (
3014 "tree_drag_data_get",
3015 G_OBJECT_CLASS_TYPE (object_class),
3016 G_SIGNAL_RUN_LAST,
3017 G_STRUCT_OFFSET (ETreeClass, tree_drag_data_get),
3018 NULL, NULL,
3019 e_marshal_VOID__INT_POINTER_INT_OBJECT_BOXED_UINT_UINT,
3020 G_TYPE_NONE, 7,
3021 G_TYPE_INT,
3022 G_TYPE_POINTER,
3023 G_TYPE_INT,
3024 GDK_TYPE_DRAG_CONTEXT,
3025 GTK_TYPE_SELECTION_DATA | G_SIGNAL_TYPE_STATIC_SCOPE,
3026 G_TYPE_UINT,
3027 G_TYPE_UINT);
3028
3029 signals[TREE_DRAG_DATA_DELETE] = g_signal_new (
3030 "tree_drag_data_delete",
3031 G_OBJECT_CLASS_TYPE (object_class),
3032 G_SIGNAL_RUN_LAST,
3033 G_STRUCT_OFFSET (ETreeClass, tree_drag_data_delete),
3034 NULL, NULL,
3035 e_marshal_VOID__INT_POINTER_INT_OBJECT,
3036 G_TYPE_NONE, 4,
3037 G_TYPE_INT,
3038 G_TYPE_POINTER,
3039 G_TYPE_INT,
3040 GDK_TYPE_DRAG_CONTEXT);
3041
3042 signals[TREE_DRAG_LEAVE] = g_signal_new (
3043 "tree_drag_leave",
3044 G_OBJECT_CLASS_TYPE (object_class),
3045 G_SIGNAL_RUN_LAST,
3046 G_STRUCT_OFFSET (ETreeClass, tree_drag_leave),
3047 NULL, NULL,
3048 e_marshal_VOID__INT_POINTER_INT_OBJECT_UINT,
3049 G_TYPE_NONE, 5,
3050 G_TYPE_INT,
3051 G_TYPE_POINTER,
3052 G_TYPE_INT,
3053 GDK_TYPE_DRAG_CONTEXT,
3054 G_TYPE_UINT);
3055
3056 signals[TREE_DRAG_MOTION] = g_signal_new (
3057 "tree_drag_motion",
3058 G_OBJECT_CLASS_TYPE (object_class),
3059 G_SIGNAL_RUN_LAST,
3060 G_STRUCT_OFFSET (ETreeClass, tree_drag_motion),
3061 NULL, NULL,
3062 e_marshal_BOOLEAN__INT_POINTER_INT_OBJECT_INT_INT_UINT,
3063 G_TYPE_BOOLEAN, 7,
3064 G_TYPE_INT,
3065 G_TYPE_POINTER,
3066 G_TYPE_INT,
3067 GDK_TYPE_DRAG_CONTEXT,
3068 G_TYPE_INT,
3069 G_TYPE_INT,
3070 G_TYPE_UINT);
3071
3072 signals[TREE_DRAG_DROP] = g_signal_new (
3073 "tree_drag_drop",
3074 G_OBJECT_CLASS_TYPE (object_class),
3075 G_SIGNAL_RUN_LAST,
3076 G_STRUCT_OFFSET (ETreeClass, tree_drag_drop),
3077 NULL, NULL,
3078 e_marshal_BOOLEAN__INT_POINTER_INT_OBJECT_INT_INT_UINT,
3079 G_TYPE_BOOLEAN, 7,
3080 G_TYPE_INT,
3081 G_TYPE_POINTER,
3082 G_TYPE_INT,
3083 GDK_TYPE_DRAG_CONTEXT,
3084 G_TYPE_INT,
3085 G_TYPE_INT,
3086 G_TYPE_UINT);
3087
3088 signals[TREE_DRAG_DATA_RECEIVED] = g_signal_new (
3089 "tree_drag_data_received",
3090 G_OBJECT_CLASS_TYPE (object_class),
3091 G_SIGNAL_RUN_LAST,
3092 G_STRUCT_OFFSET (ETreeClass, tree_drag_data_received),
3093 NULL, NULL,
3094 e_marshal_VOID__INT_POINTER_INT_OBJECT_INT_INT_BOXED_UINT_UINT,
3095 G_TYPE_NONE, 9,
3096 G_TYPE_INT,
3097 G_TYPE_POINTER,
3098 G_TYPE_INT,
3099 GDK_TYPE_DRAG_CONTEXT,
3100 G_TYPE_INT,
3101 G_TYPE_INT,
3102 GTK_TYPE_SELECTION_DATA,
3103 G_TYPE_UINT,
3104 G_TYPE_UINT);
3105
3106 g_object_class_install_property (
3107 object_class,
3108 PROP_LENGTH_THRESHOLD,
3109 g_param_spec_int (
3110 "length_threshold",
3111 "Length Threshold",
3112 "Length Threshold",
3113 0, G_MAXINT, 0,
3114 G_PARAM_WRITABLE));
3115
3116 g_object_class_install_property (
3117 object_class,
3118 PROP_HORIZONTAL_DRAW_GRID,
3119 g_param_spec_boolean (
3120 "horizontal_draw_grid",
3121 "Horizontal Draw Grid",
3122 "Horizontal Draw Grid",
3123 FALSE,
3124 G_PARAM_WRITABLE));
3125
3126 g_object_class_install_property (
3127 object_class,
3128 PROP_VERTICAL_DRAW_GRID,
3129 g_param_spec_boolean (
3130 "vertical_draw_grid",
3131 "Vertical Draw Grid",
3132 "Vertical Draw Grid",
3133 FALSE,
3134 G_PARAM_WRITABLE));
3135
3136 g_object_class_install_property (
3137 object_class,
3138 PROP_DRAW_FOCUS,
3139 g_param_spec_boolean (
3140 "drawfocus",
3141 "Draw focus",
3142 "Draw focus",
3143 FALSE,
3144 G_PARAM_WRITABLE));
3145
3146 g_object_class_install_property (
3147 object_class,
3148 PROP_ETTA,
3149 g_param_spec_object (
3150 "ETreeTableAdapter",
3151 "ETree table adapter",
3152 "ETree table adapter",
3153 E_TYPE_TREE_TABLE_ADAPTER,
3154 G_PARAM_READABLE));
3155
3156 g_object_class_install_property (
3157 object_class,
3158 PROP_UNIFORM_ROW_HEIGHT,
3159 g_param_spec_boolean (
3160 "uniform_row_height",
3161 "Uniform row height",
3162 "Uniform row height",
3163 FALSE,
3164 G_PARAM_READWRITE));
3165
3166 g_object_class_install_property (
3167 object_class,
3168 PROP_IS_EDITING,
3169 g_param_spec_boolean (
3170 "is-editing",
3171 "Whether is in an editing mode",
3172 "Whether is in an editing mode",
3173 FALSE,
3174 G_PARAM_READABLE));
3175
3176 g_object_class_install_property (
3177 object_class,
3178 PROP_ALWAYS_SEARCH,
3179 g_param_spec_boolean (
3180 "always_search",
3181 "Always search",
3182 "Always search",
3183 FALSE,
3184 G_PARAM_READWRITE));
3185
3186 g_object_class_install_property (
3187 object_class,
3188 PROP_SORT_CHILDREN_ASCENDING,
3189 g_param_spec_boolean (
3190 "sort-children-ascending",
3191 "Sort Children Ascending",
3192 "Always sort children tree nodes ascending",
3193 FALSE,
3194 G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
3195
3196 gtk_widget_class_install_style_property (
3197 widget_class,
3198 g_param_spec_int (
3199 "expander_size",
3200 "Expander Size",
3201 "Size of the expander arrow",
3202 0, G_MAXINT, 10,
3203 G_PARAM_READABLE));
3204
3205 gtk_widget_class_install_style_property (
3206 widget_class,
3207 g_param_spec_int (
3208 "vertical-spacing",
3209 "Vertical Row Spacing",
3210 "Vertical space between rows. "
3211 "It is added to top and to bottom of a row",
3212 0, G_MAXINT, 3,
3213 G_PARAM_READABLE |
3214 G_PARAM_STATIC_STRINGS));
3215
3216 gtk_widget_class_install_style_property (
3217 widget_class,
3218 g_param_spec_boolean (
3219 "alternating-row-colors",
3220 "Alternating Row Colors",
3221 "Whether to use alternating row colors",
3222 TRUE,
3223 G_PARAM_READABLE));
3224
3225 /* Scrollable interface */
3226 g_object_class_override_property (
3227 object_class, PROP_HADJUSTMENT, "hadjustment");
3228 g_object_class_override_property (
3229 object_class, PROP_VADJUSTMENT, "vadjustment");
3230 g_object_class_override_property (
3231 object_class, PROP_HSCROLL_POLICY, "hscroll-policy");
3232 g_object_class_override_property (
3233 object_class, PROP_VSCROLL_POLICY, "vscroll-policy");
3234
3235 gtk_widget_class_set_accessible_type (widget_class,
3236 GAL_A11Y_TYPE_E_TREE);
3237 }
3238
3239 static gboolean
e_tree_scrollable_get_border(GtkScrollable * scrollable,GtkBorder * border)3240 e_tree_scrollable_get_border (GtkScrollable *scrollable,
3241 GtkBorder *border)
3242 {
3243 ETree *tree;
3244 ETableHeaderItem *header_item;
3245
3246 g_return_val_if_fail (E_IS_TREE (scrollable), FALSE);
3247 g_return_val_if_fail (border != NULL, FALSE);
3248
3249 tree = E_TREE (scrollable);
3250 if (!tree->priv->header_item)
3251 return FALSE;
3252
3253 g_return_val_if_fail (E_IS_TABLE_HEADER_ITEM (tree->priv->header_item), FALSE);
3254
3255 header_item = E_TABLE_HEADER_ITEM (tree->priv->header_item);
3256
3257 border->top = header_item->height;
3258
3259 return TRUE;
3260 }
3261
3262 static void
e_tree_scrollable_init(GtkScrollableInterface * iface)3263 e_tree_scrollable_init (GtkScrollableInterface *iface)
3264 {
3265 iface->get_border = e_tree_scrollable_get_border;
3266 }
3267
3268 static void
tree_size_allocate(GtkWidget * widget,GtkAllocation * alloc,ETree * tree)3269 tree_size_allocate (GtkWidget *widget,
3270 GtkAllocation *alloc,
3271 ETree *tree)
3272 {
3273 gdouble width;
3274
3275 g_return_if_fail (E_IS_TREE (tree));
3276 g_return_if_fail (tree->priv->info_text != NULL);
3277
3278 gnome_canvas_get_scroll_region (
3279 GNOME_CANVAS (tree->priv->table_canvas),
3280 NULL, NULL, &width, NULL);
3281
3282 width -= 60.0;
3283
3284 g_object_set (
3285 tree->priv->info_text, "width", width,
3286 "clip_width", width, NULL);
3287 }
3288
3289 /**
3290 * e_tree_set_info_message:
3291 * @tree: #ETree instance
3292 * @info_message: Message to set. Can be NULL.
3293 *
3294 * Creates an info message in table area, or removes old.
3295 **/
3296 void
e_tree_set_info_message(ETree * tree,const gchar * info_message)3297 e_tree_set_info_message (ETree *tree,
3298 const gchar *info_message)
3299 {
3300 GtkAllocation allocation;
3301 GtkWidget *widget;
3302
3303 g_return_if_fail (E_IS_TREE (tree));
3304
3305 if (!tree->priv->info_text && (!info_message || !*info_message))
3306 return;
3307
3308 if (!info_message || !*info_message) {
3309 g_signal_handler_disconnect (tree, tree->priv->info_text_resize_id);
3310 g_object_run_dispose (G_OBJECT (tree->priv->info_text));
3311 tree->priv->info_text = NULL;
3312 return;
3313 }
3314
3315 widget = GTK_WIDGET (tree->priv->table_canvas);
3316 gtk_widget_get_allocation (widget, &allocation);
3317
3318 if (!tree->priv->info_text) {
3319 if (allocation.width > 60) {
3320 tree->priv->info_text = gnome_canvas_item_new (
3321 GNOME_CANVAS_GROUP (gnome_canvas_root (tree->priv->table_canvas)),
3322 e_text_get_type (),
3323 "line_wrap", TRUE,
3324 "clip", TRUE,
3325 "justification", GTK_JUSTIFY_LEFT,
3326 "text", info_message,
3327 "width", (gdouble) allocation.width - 60.0,
3328 "clip_width", (gdouble) allocation.width - 60.0,
3329 NULL);
3330
3331 e_canvas_item_move_absolute (tree->priv->info_text, 30, 30);
3332
3333 tree->priv->info_text_resize_id = g_signal_connect (
3334 tree, "size_allocate",
3335 G_CALLBACK (tree_size_allocate), tree);
3336 }
3337 } else
3338 gnome_canvas_item_set (tree->priv->info_text, "text", info_message, NULL);
3339 }
3340
3341 void
e_tree_freeze_state_change(ETree * tree)3342 e_tree_freeze_state_change (ETree *tree)
3343 {
3344 g_return_if_fail (E_IS_TREE (tree));
3345
3346 tree->priv->state_change_freeze++;
3347 if (tree->priv->state_change_freeze == 1)
3348 tree->priv->state_changed = FALSE;
3349
3350 g_return_if_fail (tree->priv->state_change_freeze != 0);
3351 }
3352
3353 void
e_tree_thaw_state_change(ETree * tree)3354 e_tree_thaw_state_change (ETree *tree)
3355 {
3356 g_return_if_fail (E_IS_TREE (tree));
3357 g_return_if_fail (tree->priv->state_change_freeze != 0);
3358
3359 tree->priv->state_change_freeze--;
3360 if (tree->priv->state_change_freeze == 0 && tree->priv->state_changed) {
3361 tree->priv->state_changed = FALSE;
3362 e_tree_state_change (tree);
3363 }
3364 }
3365
3366 gboolean
e_tree_is_editing(ETree * tree)3367 e_tree_is_editing (ETree *tree)
3368 {
3369 g_return_val_if_fail (E_IS_TREE (tree), FALSE);
3370
3371 return tree->priv->item && e_table_item_is_editing (E_TABLE_ITEM (tree->priv->item));
3372 }
3373
3374 void
e_tree_set_grouped_view(ETree * tree,gboolean grouped_view)3375 e_tree_set_grouped_view (ETree *tree,
3376 gboolean grouped_view)
3377 {
3378 g_return_if_fail (E_IS_TREE (tree));
3379
3380 if ((tree->priv->grouped_view ? 1 : 0) == (grouped_view ? 1 : 0))
3381 return;
3382
3383 tree->priv->grouped_view = grouped_view;
3384
3385 e_tree_update_full_header_grouped_view (tree);
3386 }
3387
3388 gboolean
e_tree_get_grouped_view(ETree * tree)3389 e_tree_get_grouped_view (ETree *tree)
3390 {
3391 g_return_val_if_fail (E_IS_TREE (tree), FALSE);
3392
3393 return tree->priv->grouped_view;
3394 }
3395
3396 gboolean
e_tree_get_sort_children_ascending(ETree * tree)3397 e_tree_get_sort_children_ascending (ETree *tree)
3398 {
3399 g_return_val_if_fail (E_IS_TREE (tree), FALSE);
3400
3401 return tree->priv->sort_children_ascending;
3402 }
3403
3404 void
e_tree_set_sort_children_ascending(ETree * tree,gboolean sort_children_ascending)3405 e_tree_set_sort_children_ascending (ETree *tree,
3406 gboolean sort_children_ascending)
3407 {
3408 g_return_if_fail (E_IS_TREE (tree));
3409
3410 if ((tree->priv->sort_children_ascending ? 1 : 0) == (sort_children_ascending ? 1 : 0))
3411 return;
3412
3413 tree->priv->sort_children_ascending = sort_children_ascending;
3414
3415 g_object_notify (G_OBJECT (tree), "sort-children-ascending");
3416 }
3417
3418 void
e_tree_customize_view(ETree * tree)3419 e_tree_customize_view (ETree *tree)
3420 {
3421 GnomeCanvasItem *header_item;
3422
3423 g_return_if_fail (E_IS_TREE (tree));
3424
3425 header_item = e_tree_get_header_item (tree);
3426
3427 if (header_item)
3428 e_table_header_item_customize_view (E_TABLE_HEADER_ITEM (header_item));
3429 }
3430