Updated Spanish translation
[evolution.git] / e-util / e-tree.c
blob19f83c53ed166fda0844a51cff812f4b3740d68f
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.
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.
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/>.
15 * Authors:
16 * Chris Lahey <clahey@ximian.com>
18 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
26 #include <stdlib.h>
27 #include <stdio.h>
28 #include <string.h>
30 #include <gtk/gtk.h>
31 #include <glib/gi18n.h>
32 #include <gdk/gdkkeysyms.h>
34 #include <libgnomecanvas/libgnomecanvas.h>
36 #include "e-canvas-background.h"
37 #include "e-canvas-utils.h"
38 #include "e-canvas.h"
39 #include "e-cell-tree.h"
40 #include "e-table-column-specification.h"
41 #include "e-table-header-item.h"
42 #include "e-table-header.h"
43 #include "e-table-item.h"
44 #include "e-table-sort-info.h"
45 #include "e-table-utils.h"
46 #include "e-text.h"
47 #include "e-tree-selection-model.h"
48 #include "e-tree-table-adapter.h"
49 #include "e-tree.h"
50 #include "e-misc-utils.h"
51 #include "gal-a11y-e-tree.h"
53 #define COLUMN_HEADER_HEIGHT 16
55 #define d(x)
57 #define E_TREE_GET_PRIVATE(obj) \
58 (G_TYPE_INSTANCE_GET_PRIVATE \
59 ((obj), E_TYPE_TREE, ETreePrivate))
61 typedef struct _ETreeDragSourceSite ETreeDragSourceSite;
63 enum {
64 CURSOR_CHANGE,
65 CURSOR_ACTIVATED,
66 SELECTION_CHANGE,
67 DOUBLE_CLICK,
68 RIGHT_CLICK,
69 CLICK,
70 KEY_PRESS,
71 START_DRAG,
72 STATE_CHANGE,
73 WHITE_SPACE_EVENT,
75 CUT_CLIPBOARD,
76 COPY_CLIPBOARD,
77 PASTE_CLIPBOARD,
78 SELECT_ALL,
80 TREE_DRAG_BEGIN,
81 TREE_DRAG_END,
82 TREE_DRAG_DATA_GET,
83 TREE_DRAG_DATA_DELETE,
85 TREE_DRAG_LEAVE,
86 TREE_DRAG_MOTION,
87 TREE_DRAG_DROP,
88 TREE_DRAG_DATA_RECEIVED,
90 LAST_SIGNAL
93 enum {
94 PROP_0,
95 PROP_LENGTH_THRESHOLD,
96 PROP_HORIZONTAL_DRAW_GRID,
97 PROP_VERTICAL_DRAW_GRID,
98 PROP_DRAW_FOCUS,
99 PROP_ETTA,
100 PROP_UNIFORM_ROW_HEIGHT,
101 PROP_IS_EDITING,
102 PROP_ALWAYS_SEARCH,
103 PROP_HADJUSTMENT,
104 PROP_VADJUSTMENT,
105 PROP_HSCROLL_POLICY,
106 PROP_VSCROLL_POLICY,
107 PROP_SORT_CHILDREN_ASCENDING
110 enum {
111 ET_SCROLL_UP = 1 << 0,
112 ET_SCROLL_DOWN = 1 << 1,
113 ET_SCROLL_LEFT = 1 << 2,
114 ET_SCROLL_RIGHT = 1 << 3
117 struct _ETreePrivate {
118 ETreeModel *model;
119 ETreeTableAdapter *etta;
121 ETableHeader *full_header, *header;
123 guint structure_change_id, expansion_change_id;
125 ETableSortInfo *sort_info;
127 guint sort_info_change_id, group_info_change_id;
129 ESelectionModel *selection;
130 ETableSpecification *spec;
132 ETableSearch *search;
134 ETableCol *current_search_col;
136 guint search_search_id;
137 guint search_accept_id;
139 gint reflow_idle_id;
140 gint scroll_idle_id;
141 gint hover_idle_id;
143 gboolean show_cursor_after_reflow;
145 gint table_model_change_id;
146 gint table_row_change_id;
147 gint table_cell_change_id;
148 gint table_rows_delete_id;
150 GnomeCanvasItem *info_text;
151 guint info_text_resize_id;
153 GnomeCanvas *header_canvas, *table_canvas;
155 GnomeCanvasItem *header_item, *root;
157 GnomeCanvasItem *white_item;
158 GnomeCanvasItem *item;
160 gint length_threshold;
162 GtkAdjustment *table_canvas_vadjustment;
165 * Configuration settings
167 guint alternating_row_colors : 1;
168 guint horizontal_draw_grid : 1;
169 guint vertical_draw_grid : 1;
170 guint draw_focus : 1;
171 guint row_selection_active : 1;
173 guint horizontal_scrolling : 1;
175 guint scroll_direction : 4;
177 guint do_drag : 1;
179 guint uniform_row_height : 1;
181 guint search_col_set : 1;
182 guint always_search : 1;
184 ECursorMode cursor_mode;
186 gint drop_row;
187 ETreePath drop_path;
188 gint drop_col;
190 GnomeCanvasItem *drop_highlight;
191 gint last_drop_x;
192 gint last_drop_y;
193 gint last_drop_time;
194 GdkDragContext *last_drop_context;
196 gint hover_x;
197 gint hover_y;
199 gint drag_row;
200 ETreePath drag_path;
201 gint drag_col;
202 ETreeDragSourceSite *site;
204 GList *expanded_list;
206 gboolean state_changed;
207 guint state_change_freeze;
209 gboolean is_dragging;
211 gboolean grouped_view;
212 gboolean sort_children_ascending;
215 static guint signals[LAST_SIGNAL];
217 static void et_grab_focus (GtkWidget *widget);
219 static void et_drag_begin (GtkWidget *widget,
220 GdkDragContext *context,
221 ETree *tree);
222 static void et_drag_end (GtkWidget *widget,
223 GdkDragContext *context,
224 ETree *tree);
225 static void et_drag_data_get (GtkWidget *widget,
226 GdkDragContext *context,
227 GtkSelectionData *selection_data,
228 guint info,
229 guint time,
230 ETree *tree);
231 static void et_drag_data_delete (GtkWidget *widget,
232 GdkDragContext *context,
233 ETree *tree);
235 static void et_drag_leave (GtkWidget *widget,
236 GdkDragContext *context,
237 guint time,
238 ETree *tree);
239 static gboolean et_drag_motion (GtkWidget *widget,
240 GdkDragContext *context,
241 gint x,
242 gint y,
243 guint time,
244 ETree *tree);
245 static gboolean et_drag_drop (GtkWidget *widget,
246 GdkDragContext *context,
247 gint x,
248 gint y,
249 guint time,
250 ETree *tree);
251 static void et_drag_data_received (GtkWidget *widget,
252 GdkDragContext *context,
253 gint x,
254 gint y,
255 GtkSelectionData *selection_data,
256 guint info,
257 guint time,
258 ETree *tree);
260 static void scroll_off (ETree *tree);
261 static void scroll_on (ETree *tree, guint scroll_direction);
262 static void hover_off (ETree *tree);
263 static void hover_on (ETree *tree, gint x, gint y);
264 static void context_destroyed (gpointer data, GObject *ctx);
266 static void e_tree_scrollable_init (GtkScrollableInterface *iface);
268 G_DEFINE_TYPE_WITH_CODE (ETree, e_tree, GTK_TYPE_TABLE,
269 G_IMPLEMENT_INTERFACE (GTK_TYPE_SCROLLABLE, e_tree_scrollable_init))
271 static void
272 tree_item_is_editing_changed_cb (ETableItem *item,
273 GParamSpec *param,
274 ETree *tree)
276 g_return_if_fail (E_IS_TREE (tree));
278 g_object_notify (G_OBJECT (tree), "is-editing");
281 static void
282 et_disconnect_from_etta (ETree *tree)
284 if (tree->priv->table_model_change_id != 0)
285 g_signal_handler_disconnect (
286 tree->priv->etta,
287 tree->priv->table_model_change_id);
288 if (tree->priv->table_row_change_id != 0)
289 g_signal_handler_disconnect (
290 tree->priv->etta,
291 tree->priv->table_row_change_id);
292 if (tree->priv->table_cell_change_id != 0)
293 g_signal_handler_disconnect (
294 tree->priv->etta,
295 tree->priv->table_cell_change_id);
296 if (tree->priv->table_rows_delete_id != 0)
297 g_signal_handler_disconnect (
298 tree->priv->etta,
299 tree->priv->table_rows_delete_id);
301 tree->priv->table_model_change_id = 0;
302 tree->priv->table_row_change_id = 0;
303 tree->priv->table_cell_change_id = 0;
304 tree->priv->table_rows_delete_id = 0;
307 static void
308 clear_current_search_col (ETree *tree)
310 tree->priv->search_col_set = FALSE;
313 static ETableCol *
314 current_search_col (ETree *tree)
316 if (!tree->priv->search_col_set) {
317 tree->priv->current_search_col =
318 e_table_util_calculate_current_search_col (
319 tree->priv->header,
320 tree->priv->full_header,
321 tree->priv->sort_info,
322 tree->priv->always_search);
323 tree->priv->search_col_set = TRUE;
326 return tree->priv->current_search_col;
329 static void
330 e_tree_state_change (ETree *tree)
332 if (tree->priv->state_change_freeze)
333 tree->priv->state_changed = TRUE;
334 else
335 g_signal_emit (tree, signals[STATE_CHANGE], 0);
338 static void
339 change_trigger (GObject *object,
340 ETree *tree)
342 e_tree_state_change (tree);
345 static void
346 search_col_change_trigger (GObject *object,
347 ETree *tree)
349 clear_current_search_col (tree);
350 e_tree_state_change (tree);
353 static void
354 disconnect_header (ETree *tree)
356 if (tree->priv->header == NULL)
357 return;
359 if (tree->priv->structure_change_id)
360 g_signal_handler_disconnect (
361 tree->priv->header,
362 tree->priv->structure_change_id);
363 if (tree->priv->expansion_change_id)
364 g_signal_handler_disconnect (
365 tree->priv->header,
366 tree->priv->expansion_change_id);
367 if (tree->priv->sort_info) {
368 if (tree->priv->sort_info_change_id)
369 g_signal_handler_disconnect (
370 tree->priv->sort_info,
371 tree->priv->sort_info_change_id);
372 if (tree->priv->group_info_change_id)
373 g_signal_handler_disconnect (
374 tree->priv->sort_info,
375 tree->priv->group_info_change_id);
377 g_object_unref (tree->priv->sort_info);
379 g_object_unref (tree->priv->header);
380 tree->priv->header = NULL;
381 tree->priv->sort_info = NULL;
384 static void
385 connect_header (ETree *tree,
386 ETableState *state)
388 GValue *val = g_new0 (GValue, 1);
390 if (tree->priv->header != NULL)
391 disconnect_header (tree);
393 tree->priv->header = e_table_state_to_header (
394 GTK_WIDGET (tree), tree->priv->full_header, state);
396 tree->priv->structure_change_id = g_signal_connect (
397 tree->priv->header, "structure_change",
398 G_CALLBACK (search_col_change_trigger), tree);
400 tree->priv->expansion_change_id = g_signal_connect (
401 tree->priv->header, "expansion_change",
402 G_CALLBACK (change_trigger), tree);
404 if (state->sort_info) {
405 tree->priv->sort_info = e_table_sort_info_duplicate (state->sort_info);
406 e_table_sort_info_set_can_group (tree->priv->sort_info, FALSE);
407 tree->priv->sort_info_change_id = g_signal_connect (
408 tree->priv->sort_info, "sort_info_changed",
409 G_CALLBACK (search_col_change_trigger), tree);
411 tree->priv->group_info_change_id = g_signal_connect (
412 tree->priv->sort_info, "group_info_changed",
413 G_CALLBACK (search_col_change_trigger), tree);
414 } else
415 tree->priv->sort_info = NULL;
417 g_value_init (val, G_TYPE_OBJECT);
418 g_value_set_object (val, tree->priv->sort_info);
419 g_object_set_property (G_OBJECT (tree->priv->header), "sort_info", val);
420 g_free (val);
423 static void
424 et_dispose (GObject *object)
426 ETreePrivate *priv;
428 priv = E_TREE_GET_PRIVATE (object);
430 if (priv->search != NULL) {
431 g_signal_handler_disconnect (
432 priv->search, priv->search_search_id);
433 g_signal_handler_disconnect (
434 priv->search, priv->search_accept_id);
435 g_object_unref (priv->search);
436 priv->search = NULL;
439 if (priv->reflow_idle_id > 0) {
440 g_source_remove (priv->reflow_idle_id);
441 priv->reflow_idle_id = 0;
444 scroll_off (E_TREE (object));
445 hover_off (E_TREE (object));
446 g_list_foreach (
447 priv->expanded_list,
448 (GFunc) g_free, NULL);
449 g_list_free (priv->expanded_list);
450 priv->expanded_list = NULL;
452 et_disconnect_from_etta (E_TREE (object));
454 if (priv->etta != NULL) {
455 g_object_unref (priv->etta);
456 priv->etta = NULL;
459 if (priv->model != NULL) {
460 g_object_unref (priv->model);
461 priv->model = NULL;
464 if (priv->full_header != NULL) {
465 g_object_unref (priv->full_header);
466 priv->full_header = NULL;
469 disconnect_header (E_TREE (object));
471 if (priv->selection != NULL) {
472 g_object_unref (priv->selection);
473 priv->selection = NULL;
476 if (priv->spec != NULL) {
477 g_object_unref (priv->spec);
478 priv->spec = NULL;
481 if (priv->header_canvas != NULL) {
482 gtk_widget_destroy (GTK_WIDGET (priv->header_canvas));
483 priv->header_canvas = NULL;
486 if (priv->site)
487 e_tree_drag_source_unset (E_TREE (object));
489 if (priv->last_drop_context != NULL) {
490 g_object_weak_unref (
491 G_OBJECT (priv->last_drop_context),
492 context_destroyed, object);
493 priv->last_drop_context = NULL;
496 if (priv->info_text != NULL) {
497 g_object_run_dispose (G_OBJECT (priv->info_text));
498 priv->info_text = NULL;
500 priv->info_text_resize_id = 0;
502 if (priv->table_canvas != NULL) {
503 g_signal_handlers_disconnect_by_data (priv->table_canvas, object);
504 gtk_widget_destroy (GTK_WIDGET (priv->table_canvas));
505 priv->table_canvas = NULL;
508 if (priv->table_canvas_vadjustment) {
509 g_signal_handlers_disconnect_by_data (priv->table_canvas_vadjustment, object);
510 g_clear_object (&priv->table_canvas_vadjustment);
513 /* do not unref it, it was owned by priv->table_canvas */
514 priv->item = NULL;
516 /* Chain up to parent's dispose() method. */
517 G_OBJECT_CLASS (e_tree_parent_class)->dispose (object);
520 static void
521 et_unrealize (GtkWidget *widget)
523 scroll_off (E_TREE (widget));
524 hover_off (E_TREE (widget));
526 if (GTK_WIDGET_CLASS (e_tree_parent_class)->unrealize)
527 GTK_WIDGET_CLASS (e_tree_parent_class)->unrealize (widget);
530 typedef struct {
531 ETree *tree;
532 gchar *string;
533 } SearchSearchStruct;
535 static gboolean
536 search_search_callback (ETreeModel *model,
537 ETreePath path,
538 gpointer data)
540 SearchSearchStruct *cb_data = data;
541 gconstpointer value;
542 ETableCol *col = current_search_col (cb_data->tree);
544 value = e_tree_model_value_at (
545 model, path,
546 cb_data->tree->priv->current_search_col->spec->model_col);
548 return col->search (value, cb_data->string);
551 static gboolean
552 et_search_search (ETableSearch *search,
553 gchar *string,
554 ETableSearchFlags flags,
555 ETree *tree)
557 ETreePath cursor;
558 ETreePath found;
559 SearchSearchStruct cb_data;
560 ETableCol *col = current_search_col (tree);
562 if (col == NULL)
563 return FALSE;
565 cb_data.tree = tree;
566 cb_data.string = string;
568 cursor = e_tree_get_cursor (tree);
570 if (cursor && (flags & E_TABLE_SEARCH_FLAGS_CHECK_CURSOR_FIRST)) {
571 gconstpointer value;
573 value = e_tree_model_value_at (
574 tree->priv->model, cursor, col->spec->model_col);
576 if (col->search (value, string)) {
577 return TRUE;
581 found = e_tree_model_node_find (
582 tree->priv->model, cursor, NULL,
583 search_search_callback, &cb_data);
584 if (found == NULL)
585 found = e_tree_model_node_find (
586 tree->priv->model, NULL, cursor,
587 search_search_callback, &cb_data);
589 if (found && found != cursor) {
590 gint model_row;
592 e_tree_table_adapter_show_node (tree->priv->etta, found);
593 model_row = e_tree_table_adapter_row_of_node (tree->priv->etta, found);
595 e_selection_model_select_as_key_press (
596 E_SELECTION_MODEL (tree->priv->selection),
597 model_row, col->spec->model_col,
598 GDK_CONTROL_MASK);
599 return TRUE;
600 } else if (cursor && !(flags & E_TABLE_SEARCH_FLAGS_CHECK_CURSOR_FIRST)) {
601 gconstpointer value;
603 value = e_tree_model_value_at (
604 tree->priv->model, cursor, col->spec->model_col);
606 return col->search (value, string);
607 } else
608 return FALSE;
611 static void
612 et_search_accept (ETableSearch *search,
613 ETree *tree)
615 ETableCol *col = current_search_col (tree);
616 gint cursor;
618 if (col == NULL)
619 return;
621 g_object_get (tree->priv->selection, "cursor_row", &cursor, NULL);
623 e_selection_model_select_as_key_press (
624 E_SELECTION_MODEL (tree->priv->selection),
625 cursor, col->spec->model_col, 0);
628 static void
629 e_tree_init (ETree *tree)
631 gtk_widget_set_can_focus (GTK_WIDGET (tree), TRUE);
633 gtk_table_set_homogeneous (GTK_TABLE (tree), FALSE);
635 tree->priv = E_TREE_GET_PRIVATE (tree);
637 tree->priv->alternating_row_colors = 1;
638 tree->priv->horizontal_draw_grid = 1;
639 tree->priv->vertical_draw_grid = 1;
640 tree->priv->draw_focus = 1;
641 tree->priv->cursor_mode = E_CURSOR_SIMPLE;
642 tree->priv->length_threshold = 200;
644 tree->priv->drop_row = -1;
645 tree->priv->drop_col = -1;
647 tree->priv->drag_row = -1;
648 tree->priv->drag_col = -1;
650 tree->priv->selection =
651 E_SELECTION_MODEL (e_tree_selection_model_new ());
653 tree->priv->search = e_table_search_new ();
655 tree->priv->search_search_id = g_signal_connect (
656 tree->priv->search, "search",
657 G_CALLBACK (et_search_search), tree);
659 tree->priv->search_accept_id = g_signal_connect (
660 tree->priv->search, "accept",
661 G_CALLBACK (et_search_accept), tree);
663 tree->priv->always_search = g_getenv ("GAL_ALWAYS_SEARCH") ? TRUE : FALSE;
665 tree->priv->state_changed = FALSE;
666 tree->priv->state_change_freeze = 0;
668 tree->priv->is_dragging = FALSE;
669 tree->priv->grouped_view = TRUE;
672 /* Grab_focus handler for the ETree */
673 static void
674 et_grab_focus (GtkWidget *widget)
676 ETree *tree;
678 tree = E_TREE (widget);
680 gtk_widget_grab_focus (GTK_WIDGET (tree->priv->table_canvas));
683 /* Focus handler for the ETree */
684 static gint
685 et_focus (GtkWidget *container,
686 GtkDirectionType direction)
688 ETree *tree;
690 tree = E_TREE (container);
692 if (gtk_container_get_focus_child (GTK_CONTAINER (container))) {
693 gtk_container_set_focus_child (GTK_CONTAINER (container), NULL);
694 return FALSE;
697 return gtk_widget_child_focus (
698 GTK_WIDGET (tree->priv->table_canvas), direction);
701 static void
702 set_header_canvas_width (ETree *tree)
704 gdouble oldwidth, oldheight, width;
706 if (!(tree->priv->header_item &&
707 tree->priv->header_canvas && tree->priv->table_canvas))
708 return;
710 gnome_canvas_get_scroll_region (
711 GNOME_CANVAS (tree->priv->table_canvas),
712 NULL, NULL, &width, NULL);
713 gnome_canvas_get_scroll_region (
714 GNOME_CANVAS (tree->priv->header_canvas),
715 NULL, NULL, &oldwidth, &oldheight);
717 if (oldwidth != width ||
718 oldheight != E_TABLE_HEADER_ITEM (tree->priv->header_item)->height - 1)
719 gnome_canvas_set_scroll_region (
720 GNOME_CANVAS (tree->priv->header_canvas),
721 0, 0, width, /* COLUMN_HEADER_HEIGHT - 1 */
722 E_TABLE_HEADER_ITEM (tree->priv->header_item)->height - 1);
726 static void
727 header_canvas_size_allocate (GtkWidget *widget,
728 GtkAllocation *alloc,
729 ETree *tree)
731 GtkAllocation allocation;
733 set_header_canvas_width (tree);
735 widget = GTK_WIDGET (tree->priv->header_canvas);
736 gtk_widget_get_allocation (widget, &allocation);
738 /* When the header item is created ->height == 0,
739 * as the font is only created when everything is realized.
740 * So we set the usize here as well, so that the size of the
741 * header is correct */
742 if (allocation.height != E_TABLE_HEADER_ITEM (tree->priv->header_item)->height)
743 gtk_widget_set_size_request (
744 widget, -1,
745 E_TABLE_HEADER_ITEM (tree->priv->header_item)->height);
748 static void
749 e_tree_setup_header (ETree *tree)
751 GtkWidget *widget;
752 gchar *pointer;
754 widget = e_canvas_new ();
755 gtk_style_context_add_class (
756 gtk_widget_get_style_context (widget), "table-header");
757 gtk_widget_set_can_focus (widget, FALSE);
758 tree->priv->header_canvas = GNOME_CANVAS (widget);
759 gtk_widget_show (widget);
761 pointer = g_strdup_printf ("%p", (gpointer) tree);
763 tree->priv->header_item = gnome_canvas_item_new (
764 gnome_canvas_root (tree->priv->header_canvas),
765 e_table_header_item_get_type (),
766 "ETableHeader", tree->priv->header,
767 "full_header", tree->priv->full_header,
768 "sort_info", tree->priv->sort_info,
769 "dnd_code", pointer,
770 "tree", tree,
771 NULL);
773 g_free (pointer);
775 g_signal_connect (
776 tree->priv->header_canvas, "size_allocate",
777 G_CALLBACK (header_canvas_size_allocate), tree);
779 gtk_widget_set_size_request (
780 GTK_WIDGET (tree->priv->header_canvas), -1,
781 E_TABLE_HEADER_ITEM (tree->priv->header_item)->height);
784 static void
785 scroll_to_cursor (ETree *tree)
787 ETreePath path;
788 GtkAdjustment *adjustment;
789 GtkScrollable *scrollable;
790 gint x, y, w, h;
791 gdouble page_size;
792 gdouble lower;
793 gdouble upper;
794 gdouble value;
796 path = e_tree_get_cursor (tree);
797 x = y = w = h = 0;
799 if (path != NULL) {
800 ETreeTableAdapter *adapter;
801 gint row;
802 gint col = 0;
804 adapter = e_tree_get_table_adapter (tree);
805 row = e_tree_table_adapter_row_of_node (adapter, path);
807 if (row >= 0)
808 e_table_item_get_cell_geometry (
809 E_TABLE_ITEM (tree->priv->item),
810 &row, &col, &x, &y, &w, &h);
813 e_table_item_cancel_scroll_to_cursor (E_TABLE_ITEM (tree->priv->item));
815 scrollable = GTK_SCROLLABLE (tree->priv->table_canvas);
816 adjustment = gtk_scrollable_get_vadjustment (scrollable);
818 page_size = gtk_adjustment_get_page_size (adjustment);
819 lower = gtk_adjustment_get_lower (adjustment);
820 upper = gtk_adjustment_get_upper (adjustment);
821 value = gtk_adjustment_get_value (adjustment);
823 if (y < value || y + h > value + page_size) {
824 value = CLAMP (y - page_size / 2, lower, upper - page_size);
825 gtk_adjustment_set_value (adjustment, value);
829 static gboolean
830 tree_canvas_reflow_idle (ETree *tree)
832 gdouble height, width;
833 gdouble oldheight, oldwidth;
834 GtkAllocation allocation;
835 GtkWidget *widget;
837 widget = GTK_WIDGET (tree->priv->table_canvas);
838 gtk_widget_get_allocation (widget, &allocation);
840 g_object_get (
841 tree->priv->item,
842 "height", &height, "width", &width, NULL);
844 height = MAX ((gint) height, allocation.height);
845 width = MAX ((gint) width, allocation.width);
847 /* I have no idea why this needs to be -1, but it works. */
848 gnome_canvas_get_scroll_region (
849 GNOME_CANVAS (tree->priv->table_canvas),
850 NULL, NULL, &oldwidth, &oldheight);
852 if (oldwidth != width - 1 ||
853 oldheight != height - 1) {
854 gnome_canvas_set_scroll_region (
855 GNOME_CANVAS (tree->priv->table_canvas),
856 0, 0, width - 1, height - 1);
857 set_header_canvas_width (tree);
860 tree->priv->reflow_idle_id = 0;
862 if (tree->priv->show_cursor_after_reflow) {
863 tree->priv->show_cursor_after_reflow = FALSE;
864 scroll_to_cursor (tree);
867 return FALSE;
870 static void
871 tree_canvas_size_allocate (GtkWidget *widget,
872 GtkAllocation *alloc,
873 ETree *tree)
875 gdouble width;
876 gdouble height;
877 GValue *val = g_new0 (GValue, 1);
878 g_value_init (val, G_TYPE_DOUBLE);
880 width = alloc->width;
881 g_value_set_double (val, width);
882 g_object_get (
883 tree->priv->item,
884 "height", &height,
885 NULL);
886 height = MAX ((gint) height, alloc->height);
888 g_object_set (
889 tree->priv->item,
890 "width", width,
891 NULL);
892 g_object_set_property (G_OBJECT (tree->priv->header), "width", val);
893 g_free (val);
895 if (tree->priv->reflow_idle_id)
896 g_source_remove (tree->priv->reflow_idle_id);
897 tree_canvas_reflow_idle (tree);
900 static void
901 tree_canvas_reflow (GnomeCanvas *canvas,
902 ETree *tree)
904 if (!tree->priv->reflow_idle_id)
905 tree->priv->reflow_idle_id = g_idle_add_full (
906 400, (GSourceFunc) tree_canvas_reflow_idle,
907 tree, NULL);
910 static void
911 item_cursor_change (ETableItem *eti,
912 gint row,
913 ETree *tree)
915 ETreePath path = e_tree_table_adapter_node_at_row (tree->priv->etta, row);
917 g_signal_emit (tree, signals[CURSOR_CHANGE], 0, row, path);
920 static void
921 item_cursor_activated (ETableItem *eti,
922 gint row,
923 ETree *tree)
925 ETreePath path = e_tree_table_adapter_node_at_row (tree->priv->etta, row);
927 g_signal_emit (tree, signals[CURSOR_ACTIVATED], 0, row, path);
930 static void
931 item_double_click (ETableItem *eti,
932 gint row,
933 gint col,
934 GdkEvent *event,
935 ETree *tree)
937 ETreePath path = e_tree_table_adapter_node_at_row (tree->priv->etta, row);
939 g_signal_emit (tree, signals[DOUBLE_CLICK], 0, row, path, col, event);
942 static gboolean
943 item_right_click (ETableItem *eti,
944 gint row,
945 gint col,
946 GdkEvent *event,
947 ETree *tree)
949 ETreePath path = e_tree_table_adapter_node_at_row (tree->priv->etta, row);
950 gboolean return_val = 0;
952 g_signal_emit (
953 tree, signals[RIGHT_CLICK], 0,
954 row, path, col, event, &return_val);
956 return return_val;
959 static gboolean
960 item_click (ETableItem *eti,
961 gint row,
962 gint col,
963 GdkEvent *event,
964 ETree *tree)
966 gboolean return_val = 0;
967 ETreePath path = e_tree_table_adapter_node_at_row (tree->priv->etta, row);
969 g_signal_emit (
970 tree, signals[CLICK], 0, row, path, col, event, &return_val);
972 return return_val;
975 static gint
976 item_key_press (ETableItem *eti,
977 gint row,
978 gint col,
979 GdkEvent *event,
980 ETree *tree)
982 gint return_val = 0;
983 GdkEventKey *key = (GdkEventKey *) event;
984 ETreePath path;
985 gint y, row_local, col_local;
986 GtkAdjustment *adjustment;
987 GtkScrollable *scrollable;
988 gdouble page_size;
989 gdouble upper;
990 gdouble value;
992 scrollable = GTK_SCROLLABLE (tree->priv->table_canvas);
993 adjustment = gtk_scrollable_get_vadjustment (scrollable);
995 page_size = gtk_adjustment_get_page_size (adjustment);
996 upper = gtk_adjustment_get_upper (adjustment);
997 value = gtk_adjustment_get_value (adjustment);
999 switch (key->keyval) {
1000 case GDK_KEY_Page_Down:
1001 case GDK_KEY_KP_Page_Down:
1002 y = CLAMP (value + (2 * page_size - 50), 0, upper);
1003 y -= value;
1004 e_tree_get_cell_at (tree, 30, y, &row_local, &col_local);
1006 if (row_local == -1)
1007 row_local = e_table_model_row_count (
1008 E_TABLE_MODEL (tree->priv->etta)) - 1;
1010 col_local = e_selection_model_cursor_col (
1011 E_SELECTION_MODEL (tree->priv->selection));
1012 e_selection_model_select_as_key_press (
1013 E_SELECTION_MODEL (tree->priv->selection),
1014 row_local, col_local, key->state);
1016 return_val = 1;
1017 break;
1018 case GDK_KEY_Page_Up:
1019 case GDK_KEY_KP_Page_Up:
1020 y = CLAMP (value - (page_size - 50), 0, upper);
1021 y -= value;
1022 e_tree_get_cell_at (tree, 30, y, &row_local, &col_local);
1024 if (row_local == -1)
1025 row_local = e_table_model_row_count (
1026 E_TABLE_MODEL (tree->priv->etta)) - 1;
1028 col_local = e_selection_model_cursor_col (
1029 E_SELECTION_MODEL (tree->priv->selection));
1030 e_selection_model_select_as_key_press (
1031 E_SELECTION_MODEL (tree->priv->selection),
1032 row_local, col_local, key->state);
1034 return_val = 1;
1035 break;
1036 case GDK_KEY_plus:
1037 case GDK_KEY_KP_Add:
1038 case GDK_KEY_Right:
1039 case GDK_KEY_KP_Right:
1040 /* Only allow if the Shift modifier is used.
1041 * eg. Ctrl-Equal shouldn't be handled. */
1042 if ((key->state & (GDK_SHIFT_MASK | GDK_LOCK_MASK |
1043 GDK_MOD1_MASK)) != GDK_SHIFT_MASK)
1044 break;
1045 if (row != -1) {
1046 path = e_tree_table_adapter_node_at_row (
1047 tree->priv->etta, row);
1048 if (path)
1049 e_tree_table_adapter_node_set_expanded (
1050 tree->priv->etta, path, TRUE);
1052 return_val = 1;
1053 break;
1054 case GDK_KEY_underscore:
1055 case GDK_KEY_KP_Subtract:
1056 case GDK_KEY_Left:
1057 case GDK_KEY_KP_Left:
1058 /* Only allow if the Shift modifier is used.
1059 * eg. Ctrl-Minus shouldn't be handled. */
1060 if ((key->state & (GDK_SHIFT_MASK | GDK_LOCK_MASK |
1061 GDK_MOD1_MASK)) != GDK_SHIFT_MASK)
1062 break;
1063 if (row != -1) {
1064 path = e_tree_table_adapter_node_at_row (
1065 tree->priv->etta, row);
1066 if (path)
1067 e_tree_table_adapter_node_set_expanded (
1068 tree->priv->etta, path, FALSE);
1070 return_val = 1;
1071 break;
1072 case GDK_KEY_BackSpace:
1073 if (e_table_search_backspace (tree->priv->search))
1074 return TRUE;
1075 /* Fallthrough */
1076 default:
1077 if ((key->state & ~(GDK_SHIFT_MASK | GDK_LOCK_MASK |
1078 GDK_MOD1_MASK | GDK_MOD2_MASK | GDK_MOD3_MASK |
1079 GDK_MOD4_MASK | GDK_MOD5_MASK)) == 0
1080 && ((key->keyval >= GDK_KEY_a && key->keyval <= GDK_KEY_z) ||
1081 (key->keyval >= GDK_KEY_A && key->keyval <= GDK_KEY_Z) ||
1082 (key->keyval >= GDK_KEY_0 && key->keyval <= GDK_KEY_9))) {
1083 e_table_search_input_character (tree->priv->search, key->keyval);
1085 path = e_tree_table_adapter_node_at_row (tree->priv->etta, row);
1086 g_signal_emit (
1087 tree,
1088 signals[KEY_PRESS], 0,
1089 row, path, col, event, &return_val);
1090 break;
1092 return return_val;
1095 static gint
1096 item_start_drag (ETableItem *eti,
1097 gint row,
1098 gint col,
1099 GdkEvent *event,
1100 ETree *tree)
1102 ETreePath path;
1103 gint return_val = 0;
1105 path = e_tree_table_adapter_node_at_row (tree->priv->etta, row);
1107 g_signal_emit (
1108 tree, signals[START_DRAG], 0,
1109 row, path, col, event, &return_val);
1111 return return_val;
1114 static void
1115 et_selection_model_selection_changed (ETableSelectionModel *etsm,
1116 ETree *tree)
1118 g_signal_emit (tree, signals[SELECTION_CHANGE], 0);
1121 static void
1122 et_selection_model_selection_row_changed (ETableSelectionModel *etsm,
1123 gint row,
1124 ETree *tree)
1126 g_signal_emit (tree, signals[SELECTION_CHANGE], 0);
1129 static void
1130 et_build_item (ETree *tree)
1132 gboolean alternating_row_colors;
1134 alternating_row_colors = tree->priv->alternating_row_colors;
1135 if (alternating_row_colors) {
1136 gboolean bvalue = TRUE;
1138 /* user can only disable this option, if it's enabled by the specification */
1139 gtk_widget_style_get (GTK_WIDGET (tree), "alternating-row-colors", &bvalue, NULL);
1141 alternating_row_colors = bvalue ? 1 : 0;
1144 tree->priv->item = gnome_canvas_item_new (
1145 GNOME_CANVAS_GROUP (
1146 gnome_canvas_root (tree->priv->table_canvas)),
1147 e_table_item_get_type (),
1148 "ETableHeader", tree->priv->header,
1149 "ETableModel", tree->priv->etta,
1150 "selection_model", tree->priv->selection,
1151 "alternating_row_colors", alternating_row_colors,
1152 "horizontal_draw_grid", tree->priv->horizontal_draw_grid,
1153 "vertical_draw_grid", tree->priv->vertical_draw_grid,
1154 "drawfocus", tree->priv->draw_focus,
1155 "cursor_mode", tree->priv->cursor_mode,
1156 "length_threshold", tree->priv->length_threshold,
1157 "uniform_row_height", tree->priv->uniform_row_height,
1158 NULL);
1160 g_signal_connect (
1161 tree->priv->item, "cursor_change",
1162 G_CALLBACK (item_cursor_change), tree);
1163 g_signal_connect (
1164 tree->priv->item, "cursor_activated",
1165 G_CALLBACK (item_cursor_activated), tree);
1166 g_signal_connect (
1167 tree->priv->item, "double_click",
1168 G_CALLBACK (item_double_click), tree);
1169 g_signal_connect (
1170 tree->priv->item, "right_click",
1171 G_CALLBACK (item_right_click), tree);
1172 g_signal_connect (
1173 tree->priv->item, "click",
1174 G_CALLBACK (item_click), tree);
1175 g_signal_connect (
1176 tree->priv->item, "key_press",
1177 G_CALLBACK (item_key_press), tree);
1178 g_signal_connect (
1179 tree->priv->item, "start_drag",
1180 G_CALLBACK (item_start_drag), tree);
1181 e_signal_connect_notify (
1182 tree->priv->item, "notify::is-editing",
1183 G_CALLBACK (tree_item_is_editing_changed_cb), tree);
1186 static void
1187 et_canvas_style_updated (GtkWidget *widget)
1189 GdkColor color;
1191 GTK_WIDGET_CLASS (e_tree_parent_class)->style_updated (widget);
1193 e_utils_get_theme_color_color (widget, "theme_base_color", E_UTILS_DEFAULT_THEME_BASE_COLOR, &color);
1195 gnome_canvas_item_set (
1196 E_TREE (widget)->priv->white_item,
1197 "fill_color_gdk", &color,
1198 NULL);
1201 static gboolean
1202 white_item_event (GnomeCanvasItem *white_item,
1203 GdkEvent *event,
1204 ETree *tree)
1206 gboolean return_val = 0;
1208 g_signal_emit (
1209 tree,
1210 signals[WHITE_SPACE_EVENT], 0,
1211 event, &return_val);
1213 if (!return_val && event && tree->priv->item) {
1214 guint event_button = 0;
1216 gdk_event_get_button (event, &event_button);
1218 if (event->type == GDK_BUTTON_PRESS && (event_button == 1 || event_button == 2)) {
1219 gnome_canvas_item_grab_focus (GNOME_CANVAS_ITEM (tree->priv->item));
1220 return_val = TRUE;
1224 return return_val;
1227 static gint
1228 et_canvas_root_event (GnomeCanvasItem *root,
1229 GdkEvent *event,
1230 ETree *tree)
1232 switch (event->type) {
1233 case GDK_BUTTON_PRESS:
1234 case GDK_2BUTTON_PRESS:
1235 case GDK_BUTTON_RELEASE:
1236 if (event->button.button != 4 && event->button.button != 5) {
1237 if (gtk_widget_has_focus (GTK_WIDGET (root->canvas))) {
1238 GnomeCanvasItem *item = GNOME_CANVAS (root->canvas)->focused_item;
1240 if (E_IS_TABLE_ITEM (item)) {
1241 e_table_item_leave_edit (E_TABLE_ITEM (item));
1242 return TRUE;
1246 break;
1247 default:
1248 break;
1251 return FALSE;
1254 /* Handler for focus events in the table_canvas; we have to repaint ourselves
1255 * and give the focus to some ETableItem.
1257 static gboolean
1258 table_canvas_focus_event_cb (GtkWidget *widget,
1259 GdkEventFocus *event,
1260 gpointer data)
1262 GnomeCanvas *canvas;
1263 ETree *tree;
1265 gtk_widget_queue_draw (widget);
1267 if (!event->in)
1268 return TRUE;
1270 canvas = GNOME_CANVAS (widget);
1271 tree = E_TREE (data);
1273 if (!canvas->focused_item ||
1274 (e_selection_model_cursor_row (tree->priv->selection) == -1)) {
1275 e_table_item_set_cursor (E_TABLE_ITEM (tree->priv->item), 0, 0);
1276 gnome_canvas_item_grab_focus (tree->priv->item);
1279 return TRUE;
1282 static void
1283 e_tree_table_canvas_scrolled_cb (GtkAdjustment *vadjustment,
1284 GParamSpec *param,
1285 ETree *tree)
1287 g_return_if_fail (E_IS_TREE (tree));
1289 if (tree->priv->item)
1290 e_table_item_cursor_scrolled (E_TABLE_ITEM (tree->priv->item));
1293 static void
1294 et_setup_table_canvas_vadjustment (ETree *tree)
1296 GtkAdjustment *vadjustment = NULL;
1298 g_return_if_fail (E_IS_TREE (tree));
1300 if (tree->priv->table_canvas_vadjustment) {
1301 g_signal_handlers_disconnect_by_data (tree->priv->table_canvas_vadjustment, tree);
1302 g_clear_object (&tree->priv->table_canvas_vadjustment);
1305 if (tree->priv->table_canvas)
1306 vadjustment = gtk_scrollable_get_vadjustment (
1307 GTK_SCROLLABLE (tree->priv->table_canvas));
1309 if (vadjustment) {
1310 tree->priv->table_canvas_vadjustment = g_object_ref (vadjustment);
1311 g_signal_connect (
1312 vadjustment, "notify::value",
1313 G_CALLBACK (e_tree_table_canvas_scrolled_cb), tree);
1317 static void
1318 e_tree_setup_table (ETree *tree)
1320 GtkWidget *widget;
1321 GdkColor color;
1323 tree->priv->table_canvas = GNOME_CANVAS (e_canvas_new ());
1324 g_signal_connect (
1325 tree->priv->table_canvas, "size_allocate",
1326 G_CALLBACK (tree_canvas_size_allocate), tree);
1327 g_signal_connect (
1328 tree->priv->table_canvas, "focus_in_event",
1329 G_CALLBACK (table_canvas_focus_event_cb), tree);
1330 g_signal_connect (
1331 tree->priv->table_canvas, "focus_out_event",
1332 G_CALLBACK (table_canvas_focus_event_cb), tree);
1334 g_signal_connect (
1335 tree->priv->table_canvas, "drag_begin",
1336 G_CALLBACK (et_drag_begin), tree);
1337 g_signal_connect (
1338 tree->priv->table_canvas, "drag_end",
1339 G_CALLBACK (et_drag_end), tree);
1340 g_signal_connect (
1341 tree->priv->table_canvas, "drag_data_get",
1342 G_CALLBACK (et_drag_data_get), tree);
1343 g_signal_connect (
1344 tree->priv->table_canvas, "drag_data_delete",
1345 G_CALLBACK (et_drag_data_delete), tree);
1346 g_signal_connect (
1347 tree, "drag_motion",
1348 G_CALLBACK (et_drag_motion), tree);
1349 g_signal_connect (
1350 tree, "drag_leave",
1351 G_CALLBACK (et_drag_leave), tree);
1352 g_signal_connect (
1353 tree, "drag_drop",
1354 G_CALLBACK (et_drag_drop), tree);
1355 g_signal_connect (
1356 tree, "drag_data_received",
1357 G_CALLBACK (et_drag_data_received), tree);
1359 g_signal_connect (
1360 tree->priv->table_canvas, "reflow",
1361 G_CALLBACK (tree_canvas_reflow), tree);
1363 et_setup_table_canvas_vadjustment (tree);
1364 g_signal_connect_swapped (
1365 tree->priv->table_canvas, "notify::vadjustment",
1366 G_CALLBACK (et_setup_table_canvas_vadjustment), tree);
1368 widget = GTK_WIDGET (tree->priv->table_canvas);
1370 gtk_widget_show (widget);
1372 e_utils_get_theme_color_color (widget, "theme_base_color", E_UTILS_DEFAULT_THEME_BASE_COLOR, &color);
1374 tree->priv->white_item = gnome_canvas_item_new (
1375 gnome_canvas_root (tree->priv->table_canvas),
1376 e_canvas_background_get_type (),
1377 "fill_color_gdk", &color,
1378 NULL);
1380 g_signal_connect (
1381 tree->priv->white_item, "event",
1382 G_CALLBACK (white_item_event), tree);
1383 g_signal_connect (
1384 gnome_canvas_root (tree->priv->table_canvas), "event",
1385 G_CALLBACK (et_canvas_root_event), tree);
1387 et_build_item (tree);
1390 void
1391 e_tree_set_state_object (ETree *tree,
1392 ETableState *state)
1394 GValue *val;
1395 GtkAllocation allocation;
1396 GtkWidget *widget;
1398 val = g_new0 (GValue, 1);
1399 g_value_init (val, G_TYPE_DOUBLE);
1401 connect_header (tree, state);
1403 widget = GTK_WIDGET (tree->priv->table_canvas);
1404 gtk_widget_get_allocation (widget, &allocation);
1406 g_value_set_double (val, (gdouble) allocation.width);
1407 g_object_set_property (G_OBJECT (tree->priv->header), "width", val);
1408 g_free (val);
1410 if (tree->priv->header_item)
1411 g_object_set (
1412 tree->priv->header_item,
1413 "ETableHeader", tree->priv->header,
1414 "sort_info", tree->priv->sort_info,
1415 NULL);
1417 if (tree->priv->item)
1418 g_object_set (
1419 tree->priv->item,
1420 "ETableHeader", tree->priv->header,
1421 NULL);
1423 if (tree->priv->etta)
1424 e_tree_table_adapter_set_sort_info (
1425 tree->priv->etta, tree->priv->sort_info);
1427 e_tree_state_change (tree);
1431 * e_tree_get_state_object:
1432 * @tree: #ETree object to act on
1434 * Builds an #ETableState corresponding to the current state of the
1435 * #ETree.
1437 * Return value:
1438 * The %ETableState object generated.
1440 ETableState *
1441 e_tree_get_state_object (ETree *tree)
1443 ETableState *state;
1444 GPtrArray *columns;
1445 gint full_col_count;
1446 gint i, j;
1448 columns = e_table_specification_ref_columns (tree->priv->spec);
1450 state = e_table_state_new (tree->priv->spec);
1452 g_clear_object (&state->sort_info);
1453 if (tree->priv->sort_info != NULL)
1454 state->sort_info = g_object_ref (tree->priv->sort_info);
1456 state->col_count = e_table_header_count (tree->priv->header);
1457 full_col_count = e_table_header_count (tree->priv->full_header);
1459 state->column_specs = g_new (
1460 ETableColumnSpecification *, state->col_count);
1461 state->expansions = g_new (gdouble, state->col_count);
1463 for (i = 0; i < state->col_count; i++) {
1464 ETableCol *col = e_table_header_get_column (tree->priv->header, i);
1465 state->column_specs[i] = NULL;
1466 for (j = 0; j < full_col_count; j++) {
1467 if (col->spec->model_col == e_table_header_index (tree->priv->full_header, j)) {
1468 state->column_specs[i] =
1469 g_object_ref (columns->pdata[j]);
1470 break;
1473 state->expansions[i] = col->expansion;
1476 g_ptr_array_unref (columns);
1478 return state;
1482 * e_tree_get_spec:
1483 * @tree: The #ETree to query
1485 * Returns the specification object.
1487 * Return value:
1489 ETableSpecification *
1490 e_tree_get_spec (ETree *tree)
1492 g_return_val_if_fail (E_IS_TREE (tree), NULL);
1494 return tree->priv->spec;
1497 static void
1498 et_table_model_changed (ETableModel *model,
1499 ETree *tree)
1501 if (tree->priv->horizontal_scrolling)
1502 e_table_header_update_horizontal (tree->priv->header);
1505 static void
1506 et_table_row_changed (ETableModel *table_model,
1507 gint row,
1508 ETree *tree)
1510 et_table_model_changed (table_model, tree);
1513 static void
1514 et_table_cell_changed (ETableModel *table_model,
1515 gint view_col,
1516 gint row,
1517 ETree *tree)
1519 et_table_model_changed (table_model, tree);
1522 static void
1523 et_table_rows_deleted (ETableModel *table_model,
1524 gint row,
1525 gint count,
1526 ETree *tree)
1528 ETreeTableAdapter *adapter;
1529 ETreePath * node, * prev_node;
1531 /* If the cursor is still valid after this deletion, we're done */
1532 if (e_selection_model_cursor_row (tree->priv->selection) >= 0
1533 || row == 0)
1534 return;
1536 adapter = e_tree_get_table_adapter (tree);
1537 prev_node = e_tree_table_adapter_node_at_row (adapter, row - 1);
1538 node = e_tree_get_cursor (tree);
1540 /* Check if the cursor is a child of the node directly before the
1541 * deleted region (implying that an expander was collapsed with
1542 * the cursor inside it) */
1543 while (node) {
1544 node = e_tree_model_node_get_parent (tree->priv->model, node);
1545 if (node == prev_node) {
1546 /* Set the cursor to the still-visible parent */
1547 e_tree_set_cursor (tree, prev_node);
1548 return;
1553 static void
1554 e_tree_update_full_header_grouped_view (ETree *tree)
1556 gint ii, sz;
1558 g_return_if_fail (E_IS_TREE (tree));
1560 if (!tree->priv->full_header)
1561 return;
1563 sz = e_table_header_count (tree->priv->full_header);
1564 for (ii = 0; ii < sz; ii++) {
1565 ETableCol *col;
1567 col = e_table_header_get_column (tree->priv->full_header, ii);
1568 if (!col || !E_IS_CELL_TREE (col->ecell))
1569 continue;
1571 e_cell_tree_set_grouped_view (E_CELL_TREE (col->ecell), tree->priv->grouped_view);
1575 static void
1576 et_connect_to_etta (ETree *tree)
1578 tree->priv->table_model_change_id = g_signal_connect (
1579 tree->priv->etta, "model_changed",
1580 G_CALLBACK (et_table_model_changed), tree);
1582 tree->priv->table_row_change_id = g_signal_connect (
1583 tree->priv->etta, "model_row_changed",
1584 G_CALLBACK (et_table_row_changed), tree);
1586 tree->priv->table_cell_change_id = g_signal_connect (
1587 tree->priv->etta, "model_cell_changed",
1588 G_CALLBACK (et_table_cell_changed), tree);
1590 tree->priv->table_rows_delete_id = g_signal_connect (
1591 tree->priv->etta, "model_rows_deleted",
1592 G_CALLBACK (et_table_rows_deleted), tree);
1594 g_object_bind_property (tree, "sort-children-ascending",
1595 tree->priv->etta, "sort-children-ascending",
1596 G_BINDING_DEFAULT);
1599 static gboolean
1600 et_real_construct (ETree *tree,
1601 ETreeModel *etm,
1602 ETableExtras *ete,
1603 ETableSpecification *specification,
1604 ETableState *state)
1606 GtkAdjustment *adjustment;
1607 GtkScrollable *scrollable;
1608 gint row = 0;
1610 if (ete)
1611 g_object_ref (ete);
1612 else
1613 ete = e_table_extras_new ();
1615 tree->priv->alternating_row_colors = specification->alternating_row_colors;
1616 tree->priv->horizontal_draw_grid = specification->horizontal_draw_grid;
1617 tree->priv->vertical_draw_grid = specification->vertical_draw_grid;
1618 tree->priv->draw_focus = specification->draw_focus;
1619 tree->priv->cursor_mode = specification->cursor_mode;
1620 tree->priv->full_header = e_table_spec_to_full_header (specification, ete);
1622 e_tree_update_full_header_grouped_view (tree);
1624 connect_header (tree, state);
1626 tree->priv->horizontal_scrolling = specification->horizontal_scrolling;
1628 tree->priv->model = etm;
1629 g_object_ref (etm);
1631 tree->priv->etta = E_TREE_TABLE_ADAPTER (
1632 e_tree_table_adapter_new (
1633 tree->priv->model,
1634 tree->priv->sort_info,
1635 tree->priv->full_header));
1637 et_connect_to_etta (tree);
1639 g_object_set (
1640 tree->priv->selection,
1641 "model", tree->priv->model,
1642 "etta", tree->priv->etta,
1643 "selection_mode", specification->selection_mode,
1644 "cursor_mode", specification->cursor_mode,
1645 NULL);
1647 g_signal_connect (
1648 tree->priv->selection, "selection_changed",
1649 G_CALLBACK (et_selection_model_selection_changed), tree);
1650 g_signal_connect (
1651 tree->priv->selection, "selection_row_changed",
1652 G_CALLBACK (et_selection_model_selection_row_changed), tree);
1654 if (!specification->no_headers) {
1655 e_tree_setup_header (tree);
1657 e_tree_setup_table (tree);
1659 scrollable = GTK_SCROLLABLE (tree->priv->table_canvas);
1661 adjustment = gtk_scrollable_get_vadjustment (scrollable);
1662 gtk_adjustment_set_step_increment (adjustment, 20);
1664 adjustment = gtk_scrollable_get_hadjustment (scrollable);
1665 gtk_adjustment_set_step_increment (adjustment, 20);
1667 if (!specification->no_headers) {
1669 * The header
1671 gtk_table_attach (
1672 GTK_TABLE (tree),
1673 GTK_WIDGET (tree->priv->header_canvas),
1674 0, 1, 0 + row, 1 + row,
1675 GTK_FILL | GTK_EXPAND,
1676 GTK_FILL, 0, 0);
1677 row++;
1680 gtk_table_attach (
1681 GTK_TABLE (tree),
1682 GTK_WIDGET (tree->priv->table_canvas),
1683 0, 1, 0 + row, 1 + row,
1684 GTK_FILL | GTK_EXPAND,
1685 GTK_FILL | GTK_EXPAND,
1686 0, 0);
1688 g_object_unref (ete);
1690 return TRUE;
1694 * e_tree_construct:
1695 * @tree: The newly created #ETree object.
1696 * @etm: The model for this table.
1697 * @ete: An optional #ETableExtras. (%NULL is valid.)
1698 * @specification: an #ETableSpecification
1700 * This is the internal implementation of e_tree_new() for use by
1701 * subclasses or language bindings. See e_tree_new() for details.
1703 * Return value: %TRUE on success, %FALSE if an error occurred
1705 gboolean
1706 e_tree_construct (ETree *tree,
1707 ETreeModel *etm,
1708 ETableExtras *ete,
1709 ETableSpecification *specification)
1711 ETableState *state;
1713 g_return_val_if_fail (E_IS_TREE (tree), FALSE);
1714 g_return_val_if_fail (E_IS_TREE_MODEL (etm), FALSE);
1715 g_return_val_if_fail (ete == NULL || E_IS_TABLE_EXTRAS (ete), FALSE);
1716 g_return_val_if_fail (E_IS_TABLE_SPECIFICATION (specification), FALSE);
1718 state = g_object_ref (specification->state);
1720 et_real_construct (tree, etm, ete, specification, state);
1722 tree->priv->spec = g_object_ref (specification);
1723 tree->priv->spec->allow_grouping = FALSE;
1725 g_object_unref (state);
1727 return TRUE;
1731 * e_tree_new:
1732 * @etm: The model for this tree
1733 * @ete: An optional #ETableExtras (%NULL is valid.)
1734 * @specification: an #ETableSpecification
1736 * This function creates an #ETree from the given parameters. The
1737 * #ETreeModel is a tree model to be represented. The #ETableExtras
1738 * is an optional set of pixbufs, cells, and sorting functions to be
1739 * used when interpreting the spec. If you pass in %NULL it uses the
1740 * default #ETableExtras. (See e_table_extras_new()).
1742 * @specification is the specification of the set of viewable columns and the
1743 * default sorting state and such. @state is an optional string specifying
1744 * the current sorting state and such.
1746 * Return value:
1747 * The newly created #ETree or %NULL if there's an error.
1749 GtkWidget *
1750 e_tree_new (ETreeModel *etm,
1751 ETableExtras *ete,
1752 ETableSpecification *specification)
1754 ETree *tree;
1756 g_return_val_if_fail (E_IS_TREE_MODEL (etm), NULL);
1757 g_return_val_if_fail (ete == NULL || E_IS_TABLE_EXTRAS (ete), NULL);
1758 g_return_val_if_fail (E_IS_TABLE_SPECIFICATION (specification), NULL);
1760 tree = g_object_new (E_TYPE_TREE, NULL);
1762 if (!e_tree_construct (tree, etm, ete, specification)) {
1763 g_object_unref (tree);
1764 return NULL;
1767 return GTK_WIDGET (tree);
1770 void
1771 e_tree_show_cursor_after_reflow (ETree *tree)
1773 g_return_if_fail (E_IS_TREE (tree));
1775 tree->priv->show_cursor_after_reflow = TRUE;
1778 void
1779 e_tree_set_cursor (ETree *tree,
1780 ETreePath path)
1782 g_return_if_fail (E_IS_TREE (tree));
1783 g_return_if_fail (path != NULL);
1785 e_tree_selection_model_select_single_path (
1786 E_TREE_SELECTION_MODEL (tree->priv->selection), path);
1787 e_tree_selection_model_change_cursor (
1788 E_TREE_SELECTION_MODEL (tree->priv->selection), path);
1791 ETreePath
1792 e_tree_get_cursor (ETree *tree)
1794 return e_tree_selection_model_get_cursor (
1795 E_TREE_SELECTION_MODEL (tree->priv->selection));
1798 /* Standard functions */
1799 static void
1800 et_foreach_recurse (ETreeModel *model,
1801 ETreePath path,
1802 ETreeForeachFunc callback,
1803 gpointer closure)
1805 ETreePath child;
1807 callback (path, closure);
1809 child = e_tree_model_node_get_first_child (E_TREE_MODEL (model), path);
1810 for (; child; child = e_tree_model_node_get_next (E_TREE_MODEL (model), child))
1811 if (child)
1812 et_foreach_recurse (model, child, callback, closure);
1815 void
1816 e_tree_path_foreach (ETree *tree,
1817 ETreeForeachFunc callback,
1818 gpointer closure)
1820 ETreePath root;
1822 g_return_if_fail (E_IS_TREE (tree));
1824 root = e_tree_model_get_root (tree->priv->model);
1826 if (root)
1827 et_foreach_recurse (tree->priv->model,
1828 root,
1829 callback,
1830 closure);
1833 static void
1834 et_get_property (GObject *object,
1835 guint property_id,
1836 GValue *value,
1837 GParamSpec *pspec)
1839 ETree *tree = E_TREE (object);
1841 switch (property_id) {
1842 case PROP_ETTA:
1843 g_value_set_object (value, tree->priv->etta);
1844 break;
1846 case PROP_UNIFORM_ROW_HEIGHT:
1847 g_value_set_boolean (value, tree->priv->uniform_row_height);
1848 break;
1850 case PROP_IS_EDITING:
1851 g_value_set_boolean (value, e_tree_is_editing (tree));
1852 break;
1854 case PROP_ALWAYS_SEARCH:
1855 g_value_set_boolean (value, tree->priv->always_search);
1856 break;
1858 case PROP_HADJUSTMENT:
1859 if (tree->priv->table_canvas)
1860 g_object_get_property (
1861 G_OBJECT (tree->priv->table_canvas),
1862 "hadjustment", value);
1863 else
1864 g_value_set_object (value, NULL);
1865 break;
1867 case PROP_VADJUSTMENT:
1868 if (tree->priv->table_canvas)
1869 g_object_get_property (
1870 G_OBJECT (tree->priv->table_canvas),
1871 "vadjustment", value);
1872 else
1873 g_value_set_object (value, NULL);
1874 break;
1876 case PROP_HSCROLL_POLICY:
1877 if (tree->priv->table_canvas)
1878 g_object_get_property (
1879 G_OBJECT (tree->priv->table_canvas),
1880 "hscroll-policy", value);
1881 else
1882 g_value_set_enum (value, 0);
1883 break;
1885 case PROP_VSCROLL_POLICY:
1886 if (tree->priv->table_canvas)
1887 g_object_get_property (
1888 G_OBJECT (tree->priv->table_canvas),
1889 "vscroll-policy", value);
1890 else
1891 g_value_set_enum (value, 0);
1892 break;
1894 case PROP_SORT_CHILDREN_ASCENDING:
1895 g_value_set_boolean (value, e_tree_get_sort_children_ascending (tree));
1896 break;
1898 default:
1899 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1900 break;
1904 typedef struct {
1905 gchar *arg;
1906 gboolean setting;
1907 } bool_closure;
1909 static void
1910 et_set_property (GObject *object,
1911 guint property_id,
1912 const GValue *value,
1913 GParamSpec *pspec)
1915 ETree *tree = E_TREE (object);
1917 switch (property_id) {
1918 case PROP_LENGTH_THRESHOLD:
1919 tree->priv->length_threshold = g_value_get_int (value);
1920 if (tree->priv->item) {
1921 gnome_canvas_item_set (
1922 GNOME_CANVAS_ITEM (tree->priv->item),
1923 "length_threshold",
1924 tree->priv->length_threshold,
1925 NULL);
1927 break;
1929 case PROP_HORIZONTAL_DRAW_GRID:
1930 tree->priv->horizontal_draw_grid = g_value_get_boolean (value);
1931 if (tree->priv->item) {
1932 gnome_canvas_item_set (
1933 GNOME_CANVAS_ITEM (tree->priv->item),
1934 "horizontal_draw_grid",
1935 tree->priv->horizontal_draw_grid,
1936 NULL);
1938 break;
1940 case PROP_VERTICAL_DRAW_GRID:
1941 tree->priv->vertical_draw_grid = g_value_get_boolean (value);
1942 if (tree->priv->item) {
1943 gnome_canvas_item_set (
1944 GNOME_CANVAS_ITEM (tree->priv->item),
1945 "vertical_draw_grid",
1946 tree->priv->vertical_draw_grid,
1947 NULL);
1949 break;
1951 case PROP_DRAW_FOCUS:
1952 tree->priv->draw_focus = g_value_get_boolean (value);
1953 if (tree->priv->item) {
1954 gnome_canvas_item_set (
1955 GNOME_CANVAS_ITEM (tree->priv->item),
1956 "drawfocus",
1957 tree->priv->draw_focus,
1958 NULL);
1960 break;
1962 case PROP_UNIFORM_ROW_HEIGHT:
1963 tree->priv->uniform_row_height = g_value_get_boolean (value);
1964 if (tree->priv->item) {
1965 gnome_canvas_item_set (
1966 GNOME_CANVAS_ITEM (tree->priv->item),
1967 "uniform_row_height",
1968 tree->priv->uniform_row_height,
1969 NULL);
1971 break;
1973 case PROP_ALWAYS_SEARCH:
1974 if (tree->priv->always_search == g_value_get_boolean (value))
1975 return;
1976 tree->priv->always_search = g_value_get_boolean (value);
1977 clear_current_search_col (tree);
1978 break;
1980 case PROP_HADJUSTMENT:
1981 if (tree->priv->table_canvas)
1982 g_object_set_property (
1983 G_OBJECT (tree->priv->table_canvas),
1984 "hadjustment", value);
1985 break;
1987 case PROP_VADJUSTMENT:
1988 if (tree->priv->table_canvas)
1989 g_object_set_property (
1990 G_OBJECT (tree->priv->table_canvas),
1991 "vadjustment", value);
1992 break;
1994 case PROP_HSCROLL_POLICY:
1995 if (tree->priv->table_canvas)
1996 g_object_set_property (
1997 G_OBJECT (tree->priv->table_canvas),
1998 "hscroll-policy", value);
1999 break;
2001 case PROP_VSCROLL_POLICY:
2002 if (tree->priv->table_canvas)
2003 g_object_set_property (
2004 G_OBJECT (tree->priv->table_canvas),
2005 "vscroll-policy", value);
2006 break;
2008 case PROP_SORT_CHILDREN_ASCENDING:
2009 e_tree_set_sort_children_ascending (tree, g_value_get_boolean (value));
2010 break;
2015 * e_tree_get_model:
2016 * @tree: the ETree
2018 * Returns the model upon which this ETree is based.
2020 * Returns: the model
2022 ETreeModel *
2023 e_tree_get_model (ETree *tree)
2025 g_return_val_if_fail (E_IS_TREE (tree), NULL);
2027 return tree->priv->model;
2031 * e_tree_get_selection_model:
2032 * @tree: the ETree
2034 * Returns the selection model of this ETree.
2036 * Returns: the selection model
2038 ESelectionModel *
2039 e_tree_get_selection_model (ETree *tree)
2041 g_return_val_if_fail (E_IS_TREE (tree), NULL);
2043 return tree->priv->selection;
2047 * e_tree_get_table_adapter:
2048 * @tree: the ETree
2050 * Returns the table adapter this ETree uses.
2052 * Returns: the model
2054 ETreeTableAdapter *
2055 e_tree_get_table_adapter (ETree *tree)
2057 g_return_val_if_fail (E_IS_TREE (tree), NULL);
2059 return tree->priv->etta;
2062 ETableItem *
2063 e_tree_get_item (ETree *tree)
2065 g_return_val_if_fail (E_IS_TREE (tree), NULL);
2067 return E_TABLE_ITEM (tree->priv->item);
2070 GnomeCanvasItem *
2071 e_tree_get_header_item (ETree *tree)
2073 g_return_val_if_fail (E_IS_TREE (tree), NULL);
2075 return tree->priv->header_item;
2078 struct _ETreeDragSourceSite
2080 GdkModifierType start_button_mask;
2081 GtkTargetList *target_list; /* Targets for drag data */
2082 GdkDragAction actions; /* Possible actions */
2083 GdkPixbuf *pixbuf; /* Icon for drag data */
2085 /* Stored button press information to detect drag beginning */
2086 gint state;
2087 gint x, y;
2088 gint row, col;
2091 typedef enum
2093 GTK_DRAG_STATUS_DRAG,
2094 GTK_DRAG_STATUS_WAIT,
2095 GTK_DRAG_STATUS_DROP
2096 } GtkDragStatus;
2098 typedef struct _GtkDragDestInfo GtkDragDestInfo;
2099 typedef struct _GtkDragSourceInfo GtkDragSourceInfo;
2101 struct _GtkDragDestInfo
2103 GtkWidget *widget; /* Widget in which drag is in */
2104 GdkDragContext *context; /* Drag context */
2105 GtkDragSourceInfo *proxy_source; /* Set if this is a proxy drag */
2106 GtkSelectionData *proxy_data; /* Set while retrieving proxied data */
2107 guint dropped : 1; /* Set after we receive a drop */
2108 guint32 proxy_drop_time; /* Timestamp for proxied drop */
2109 guint proxy_drop_wait : 1; /* Set if we are waiting for a
2110 * status reply before sending
2111 * a proxied drop on.
2113 gint drop_x, drop_y; /* Position of drop */
2116 struct _GtkDragSourceInfo
2118 GtkWidget *widget;
2119 GtkTargetList *target_list; /* Targets for drag data */
2120 GdkDragAction possible_actions; /* Actions allowed by source */
2121 GdkDragContext *context; /* drag context */
2122 GtkWidget *icon_window; /* Window for drag */
2123 GtkWidget *ipc_widget; /* GtkInvisible for grab, message passing */
2124 GdkCursor *cursor; /* Cursor for drag */
2125 gint hot_x, hot_y; /* Hot spot for drag */
2126 gint button; /* mouse button starting drag */
2128 GtkDragStatus status; /* drag status */
2129 GdkEvent *last_event; /* motion event waiting for response */
2131 gint start_x, start_y; /* Initial position */
2132 gint cur_x, cur_y; /* Current Position */
2134 GList *selections; /* selections we've claimed */
2136 GtkDragDestInfo *proxy_dest; /* Set if this is a proxy drag */
2138 guint drop_timeout; /* Timeout for aborting drop */
2139 guint destroy_icon : 1; /* If true, destroy icon_window
2143 /* Drag & drop stuff. */
2145 /* Source side */
2147 static gint
2148 et_real_start_drag (ETree *tree,
2149 gint row,
2150 ETreePath path,
2151 gint col,
2152 GdkEvent *event)
2154 GtkDragSourceInfo *info;
2155 GdkDragContext *context;
2156 ETreeDragSourceSite *site;
2158 if (tree->priv->do_drag) {
2159 site = tree->priv->site;
2161 site->state = 0;
2162 context = e_tree_drag_begin (
2163 tree, row, col,
2164 site->target_list,
2165 site->actions,
2166 1, event);
2168 if (context) {
2169 info = g_dataset_get_data (context, "gtk-info");
2171 if (info && !info->icon_window) {
2172 if (site->pixbuf)
2173 gtk_drag_set_icon_pixbuf (
2174 context,
2175 site->pixbuf,
2176 -2, -2);
2177 else
2178 gtk_drag_set_icon_default (context);
2181 return TRUE;
2183 return FALSE;
2186 void
2187 e_tree_drag_source_set (ETree *tree,
2188 GdkModifierType start_button_mask,
2189 const GtkTargetEntry *targets,
2190 gint n_targets,
2191 GdkDragAction actions)
2193 ETreeDragSourceSite *site;
2194 GtkWidget *canvas;
2196 g_return_if_fail (E_IS_TREE (tree));
2198 canvas = GTK_WIDGET (tree->priv->table_canvas);
2199 site = tree->priv->site;
2201 tree->priv->do_drag = TRUE;
2203 gtk_widget_add_events (
2204 canvas,
2205 gtk_widget_get_events (canvas) |
2206 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
2207 GDK_BUTTON_MOTION_MASK | GDK_STRUCTURE_MASK);
2209 if (site) {
2210 if (site->target_list)
2211 gtk_target_list_unref (site->target_list);
2212 } else {
2213 site = g_new0 (ETreeDragSourceSite, 1);
2214 tree->priv->site = site;
2217 site->start_button_mask = start_button_mask;
2219 if (targets)
2220 site->target_list = gtk_target_list_new (targets, n_targets);
2221 else
2222 site->target_list = NULL;
2224 site->actions = actions;
2227 void
2228 e_tree_drag_source_unset (ETree *tree)
2230 ETreeDragSourceSite *site;
2232 g_return_if_fail (E_IS_TREE (tree));
2234 site = tree->priv->site;
2236 if (site) {
2237 if (site->target_list)
2238 gtk_target_list_unref (site->target_list);
2239 g_free (site);
2240 tree->priv->site = NULL;
2244 /* There probably should be functions for setting the targets
2245 * as a GtkTargetList
2248 GdkDragContext *
2249 e_tree_drag_begin (ETree *tree,
2250 gint row,
2251 gint col,
2252 GtkTargetList *targets,
2253 GdkDragAction actions,
2254 gint button,
2255 GdkEvent *event)
2257 ETreePath path;
2259 g_return_val_if_fail (E_IS_TREE (tree), NULL);
2261 path = e_tree_table_adapter_node_at_row (tree->priv->etta, row);
2263 tree->priv->drag_row = row;
2264 tree->priv->drag_path = path;
2265 tree->priv->drag_col = col;
2267 return gtk_drag_begin (
2268 GTK_WIDGET (tree->priv->table_canvas),
2269 targets,
2270 actions,
2271 button,
2272 event);
2276 * e_tree_is_dragging:
2277 * @tree: An #ETree widget
2279 * Returns whether is @tree in a drag&drop operation.
2281 gboolean
2282 e_tree_is_dragging (ETree *tree)
2284 g_return_val_if_fail (E_IS_TREE (tree), FALSE);
2286 return tree->priv->is_dragging;
2290 * e_tree_get_cell_at:
2291 * @tree: An ETree widget
2292 * @x: X coordinate for the pixel
2293 * @y: Y coordinate for the pixel
2294 * @row_return: Pointer to return the row value
2295 * @col_return: Pointer to return the column value
2297 * Return the row and column for the cell in which the pixel at (@x, @y) is
2298 * contained.
2300 void
2301 e_tree_get_cell_at (ETree *tree,
2302 gint x,
2303 gint y,
2304 gint *row_return,
2305 gint *col_return)
2307 GtkAdjustment *adjustment;
2308 GtkScrollable *scrollable;
2310 g_return_if_fail (E_IS_TREE (tree));
2311 g_return_if_fail (row_return != NULL);
2312 g_return_if_fail (col_return != NULL);
2314 /* FIXME it would be nice if it could handle a NULL row_return or
2315 * col_return gracefully. */
2317 if (row_return)
2318 *row_return = -1;
2319 if (col_return)
2320 *col_return = -1;
2322 scrollable = GTK_SCROLLABLE (tree->priv->table_canvas);
2324 adjustment = gtk_scrollable_get_hadjustment (scrollable);
2325 x += gtk_adjustment_get_value (adjustment);
2327 adjustment = gtk_scrollable_get_vadjustment (scrollable);
2328 y += gtk_adjustment_get_value (adjustment);
2330 e_table_item_compute_location (
2331 E_TABLE_ITEM (tree->priv->item),
2332 &x, &y, row_return, col_return);
2336 * e_tree_get_cell_geometry:
2337 * @tree: The tree.
2338 * @row: The row to get the geometry of.
2339 * @col: The col to get the geometry of.
2340 * @x_return: Returns the x coordinate of the upper right hand corner
2341 * of the cell with respect to the widget.
2342 * @y_return: Returns the y coordinate of the upper right hand corner
2343 * of the cell with respect to the widget.
2344 * @width_return: Returns the width of the cell.
2345 * @height_return: Returns the height of the cell.
2347 * Computes the data about this cell.
2349 void
2350 e_tree_get_cell_geometry (ETree *tree,
2351 gint row,
2352 gint col,
2353 gint *x_return,
2354 gint *y_return,
2355 gint *width_return,
2356 gint *height_return)
2358 GtkAdjustment *adjustment;
2359 GtkScrollable *scrollable;
2361 g_return_if_fail (E_IS_TREE (tree));
2362 g_return_if_fail (row >= 0);
2363 g_return_if_fail (col >= 0);
2365 /* FIXME it would be nice if it could handle a NULL row_return or
2366 * col_return gracefully. */
2368 e_table_item_get_cell_geometry (
2369 E_TABLE_ITEM (tree->priv->item),
2370 &row, &col, x_return, y_return,
2371 width_return, height_return);
2373 scrollable = GTK_SCROLLABLE (tree->priv->table_canvas);
2375 if (x_return) {
2376 adjustment = gtk_scrollable_get_hadjustment (scrollable);
2377 (*x_return) -= gtk_adjustment_get_value (adjustment);
2380 if (y_return) {
2381 adjustment = gtk_scrollable_get_vadjustment (scrollable);
2382 (*y_return) -= gtk_adjustment_get_value (adjustment);
2386 static void
2387 et_drag_begin (GtkWidget *widget,
2388 GdkDragContext *context,
2389 ETree *tree)
2391 tree->priv->is_dragging = TRUE;
2393 g_signal_emit (
2394 tree,
2395 signals[TREE_DRAG_BEGIN], 0,
2396 tree->priv->drag_row,
2397 tree->priv->drag_path,
2398 tree->priv->drag_col,
2399 context);
2402 static void
2403 et_drag_end (GtkWidget *widget,
2404 GdkDragContext *context,
2405 ETree *tree)
2407 tree->priv->is_dragging = FALSE;
2409 g_signal_emit (
2410 tree,
2411 signals[TREE_DRAG_END], 0,
2412 tree->priv->drag_row,
2413 tree->priv->drag_path,
2414 tree->priv->drag_col,
2415 context);
2418 static void
2419 et_drag_data_get (GtkWidget *widget,
2420 GdkDragContext *context,
2421 GtkSelectionData *selection_data,
2422 guint info,
2423 guint time,
2424 ETree *tree)
2426 g_signal_emit (
2427 tree,
2428 signals[TREE_DRAG_DATA_GET], 0,
2429 tree->priv->drag_row,
2430 tree->priv->drag_path,
2431 tree->priv->drag_col,
2432 context,
2433 selection_data,
2434 info,
2435 time);
2438 static void
2439 et_drag_data_delete (GtkWidget *widget,
2440 GdkDragContext *context,
2441 ETree *tree)
2443 g_signal_emit (
2444 tree,
2445 signals[TREE_DRAG_DATA_DELETE], 0,
2446 tree->priv->drag_row,
2447 tree->priv->drag_path,
2448 tree->priv->drag_col,
2449 context);
2452 static gboolean
2453 do_drag_motion (ETree *tree,
2454 GdkDragContext *context,
2455 gint x,
2456 gint y,
2457 guint time)
2459 gboolean ret_val = FALSE;
2460 gint row, col;
2461 ETreePath path;
2463 e_tree_get_cell_at (tree, x, y, &row, &col);
2465 if (row != tree->priv->drop_row && col != tree->priv->drop_col) {
2466 g_signal_emit (
2467 tree, signals[TREE_DRAG_LEAVE], 0,
2468 tree->priv->drop_row,
2469 tree->priv->drop_path,
2470 tree->priv->drop_col,
2471 context,
2472 time);
2475 path = e_tree_table_adapter_node_at_row (tree->priv->etta, row);
2477 tree->priv->drop_row = row;
2478 tree->priv->drop_path = path;
2479 tree->priv->drop_col = col;
2480 g_signal_emit (
2481 tree, signals[TREE_DRAG_MOTION], 0,
2482 tree->priv->drop_row,
2483 tree->priv->drop_path,
2484 tree->priv->drop_col,
2485 context,
2486 x, y,
2487 time,
2488 &ret_val);
2490 return ret_val;
2493 static gboolean
2494 scroll_timeout (gpointer data)
2496 ETree *tree = data;
2497 gint dx = 0, dy = 0;
2498 GtkAdjustment *adjustment;
2499 GtkScrollable *scrollable;
2500 gdouble old_h_value;
2501 gdouble new_h_value;
2502 gdouble old_v_value;
2503 gdouble new_v_value;
2504 gdouble page_size;
2505 gdouble lower;
2506 gdouble upper;
2508 if (tree->priv->scroll_direction & ET_SCROLL_DOWN)
2509 dy += 20;
2510 if (tree->priv->scroll_direction & ET_SCROLL_UP)
2511 dy -= 20;
2513 if (tree->priv->scroll_direction & ET_SCROLL_RIGHT)
2514 dx += 20;
2515 if (tree->priv->scroll_direction & ET_SCROLL_LEFT)
2516 dx -= 20;
2518 scrollable = GTK_SCROLLABLE (tree->priv->table_canvas);
2520 adjustment = gtk_scrollable_get_hadjustment (scrollable);
2522 page_size = gtk_adjustment_get_page_size (adjustment);
2523 lower = gtk_adjustment_get_lower (adjustment);
2524 upper = gtk_adjustment_get_upper (adjustment);
2526 old_h_value = gtk_adjustment_get_value (adjustment);
2527 new_h_value = CLAMP (old_h_value + dx, lower, upper - page_size);
2529 gtk_adjustment_set_value (adjustment, new_h_value);
2531 adjustment = gtk_scrollable_get_vadjustment (scrollable);
2533 page_size = gtk_adjustment_get_page_size (adjustment);
2534 lower = gtk_adjustment_get_lower (adjustment);
2535 upper = gtk_adjustment_get_upper (adjustment);
2537 old_v_value = gtk_adjustment_get_value (adjustment);
2538 new_v_value = CLAMP (old_v_value + dy, lower, upper - page_size);
2540 gtk_adjustment_set_value (adjustment, new_v_value);
2542 if (new_h_value != old_h_value || new_v_value != old_v_value)
2543 do_drag_motion (
2544 tree,
2545 tree->priv->last_drop_context,
2546 tree->priv->last_drop_x,
2547 tree->priv->last_drop_y,
2548 tree->priv->last_drop_time);
2550 return TRUE;
2553 static void
2554 scroll_on (ETree *tree,
2555 guint scroll_direction)
2557 if (tree->priv->scroll_idle_id == 0 ||
2558 scroll_direction != tree->priv->scroll_direction) {
2559 if (tree->priv->scroll_idle_id != 0)
2560 g_source_remove (tree->priv->scroll_idle_id);
2561 tree->priv->scroll_direction = scroll_direction;
2562 tree->priv->scroll_idle_id =
2563 e_named_timeout_add (100, scroll_timeout, tree);
2567 static void
2568 scroll_off (ETree *tree)
2570 if (tree->priv->scroll_idle_id) {
2571 g_source_remove (tree->priv->scroll_idle_id);
2572 tree->priv->scroll_idle_id = 0;
2576 static gboolean
2577 hover_timeout (gpointer data)
2579 ETree *tree = data;
2580 gint x = tree->priv->hover_x;
2581 gint y = tree->priv->hover_y;
2582 gint row, col;
2583 ETreePath path;
2585 e_tree_get_cell_at (tree, x, y, &row, &col);
2587 path = e_tree_table_adapter_node_at_row (tree->priv->etta, row);
2588 if (path && e_tree_model_node_is_expandable (tree->priv->model, path)) {
2589 if (!e_tree_table_adapter_node_is_expanded (tree->priv->etta, path)) {
2590 tree->priv->expanded_list = g_list_prepend (
2591 tree->priv->expanded_list,
2592 e_tree_model_get_save_id (
2593 tree->priv->model, path));
2595 e_tree_table_adapter_node_set_expanded (
2596 tree->priv->etta, path, TRUE);
2600 return TRUE;
2603 static void
2604 hover_on (ETree *tree,
2605 gint x,
2606 gint y)
2608 tree->priv->hover_x = x;
2609 tree->priv->hover_y = y;
2610 if (tree->priv->hover_idle_id != 0)
2611 g_source_remove (tree->priv->hover_idle_id);
2612 tree->priv->hover_idle_id =
2613 e_named_timeout_add (500, hover_timeout, tree);
2616 static void
2617 hover_off (ETree *tree)
2619 if (tree->priv->hover_idle_id) {
2620 g_source_remove (tree->priv->hover_idle_id);
2621 tree->priv->hover_idle_id = 0;
2625 static void
2626 collapse_drag (ETree *tree,
2627 ETreePath drop)
2629 GList *list;
2631 /* We only want to leave open parents of the node dropped in.
2632 * Not the node itself. */
2633 if (drop) {
2634 drop = e_tree_model_node_get_parent (tree->priv->model, drop);
2637 for (list = tree->priv->expanded_list; list; list = list->next) {
2638 gchar *save_id = list->data;
2639 ETreePath path;
2641 path = e_tree_model_get_node_by_id (tree->priv->model, save_id);
2642 if (path) {
2643 ETreePath search;
2644 gboolean found = FALSE;
2646 for (search = drop; search;
2647 search = e_tree_model_node_get_parent (
2648 tree->priv->model, search)) {
2649 if (path == search) {
2650 found = TRUE;
2651 break;
2655 if (!found)
2656 e_tree_table_adapter_node_set_expanded (
2657 tree->priv->etta, path, FALSE);
2659 g_free (save_id);
2661 g_list_free (tree->priv->expanded_list);
2662 tree->priv->expanded_list = NULL;
2665 static void
2666 context_destroyed (gpointer data,
2667 GObject *ctx)
2669 ETree *tree = data;
2670 if (tree->priv) {
2671 tree->priv->last_drop_x = 0;
2672 tree->priv->last_drop_y = 0;
2673 tree->priv->last_drop_time = 0;
2674 tree->priv->last_drop_context = NULL;
2675 collapse_drag (tree, NULL);
2676 scroll_off (tree);
2677 hover_off (tree);
2679 g_object_unref (tree);
2682 static void
2683 context_connect (ETree *tree,
2684 GdkDragContext *context)
2686 if (context == tree->priv->last_drop_context)
2687 return;
2689 if (tree->priv->last_drop_context)
2690 g_object_weak_unref (
2691 G_OBJECT (tree->priv->last_drop_context),
2692 context_destroyed, tree);
2693 else
2694 g_object_ref (tree);
2696 g_object_weak_ref (G_OBJECT (context), context_destroyed, tree);
2699 static void
2700 et_drag_leave (GtkWidget *widget,
2701 GdkDragContext *context,
2702 guint time,
2703 ETree *tree)
2705 g_signal_emit (
2706 tree,
2707 signals[TREE_DRAG_LEAVE], 0,
2708 tree->priv->drop_row,
2709 tree->priv->drop_path,
2710 tree->priv->drop_col,
2711 context,
2712 time);
2713 tree->priv->drop_row = -1;
2714 tree->priv->drop_col = -1;
2716 scroll_off (tree);
2717 hover_off (tree);
2720 static gboolean
2721 et_drag_motion (GtkWidget *widget,
2722 GdkDragContext *context,
2723 gint x,
2724 gint y,
2725 guint time,
2726 ETree *tree)
2728 GtkAllocation allocation;
2729 gint ret_val;
2730 guint direction = 0;
2732 tree->priv->last_drop_x = x;
2733 tree->priv->last_drop_y = y;
2734 tree->priv->last_drop_time = time;
2735 context_connect (tree, context);
2736 tree->priv->last_drop_context = context;
2738 if (tree->priv->hover_idle_id != 0) {
2739 if (abs (tree->priv->hover_x - x) > 3 ||
2740 abs (tree->priv->hover_y - y) > 3) {
2741 hover_on (tree, x, y);
2743 } else {
2744 hover_on (tree, x, y);
2747 ret_val = do_drag_motion (tree, context, x, y, time);
2749 gtk_widget_get_allocation (widget, &allocation);
2751 if (y < 20)
2752 direction |= ET_SCROLL_UP;
2753 if (y > allocation.height - 20)
2754 direction |= ET_SCROLL_DOWN;
2755 if (x < 20)
2756 direction |= ET_SCROLL_LEFT;
2757 if (x > allocation.width - 20)
2758 direction |= ET_SCROLL_RIGHT;
2760 if (direction != 0)
2761 scroll_on (tree, direction);
2762 else
2763 scroll_off (tree);
2765 return ret_val;
2768 static gboolean
2769 et_drag_drop (GtkWidget *widget,
2770 GdkDragContext *context,
2771 gint x,
2772 gint y,
2773 guint time,
2774 ETree *tree)
2776 gboolean ret_val = FALSE;
2777 gint row, col;
2778 ETreePath path;
2780 e_tree_get_cell_at (tree, x, y, &row, &col);
2782 path = e_tree_table_adapter_node_at_row (tree->priv->etta, row);
2784 if (row != tree->priv->drop_row && col != tree->priv->drop_row) {
2785 g_signal_emit (
2786 tree, signals[TREE_DRAG_LEAVE], 0,
2787 tree->priv->drop_row,
2788 tree->priv->drop_path,
2789 tree->priv->drop_col,
2790 context,
2791 time);
2792 g_signal_emit (
2793 tree, signals[TREE_DRAG_MOTION], 0,
2794 row,
2795 path,
2796 col,
2797 context,
2800 time,
2801 &ret_val);
2803 tree->priv->drop_row = row;
2804 tree->priv->drop_path = path;
2805 tree->priv->drop_col = col;
2807 g_signal_emit (
2808 tree, signals[TREE_DRAG_DROP], 0,
2809 tree->priv->drop_row,
2810 tree->priv->drop_path,
2811 tree->priv->drop_col,
2812 context,
2815 time,
2816 &ret_val);
2818 tree->priv->drop_row = -1;
2819 tree->priv->drop_path = NULL;
2820 tree->priv->drop_col = -1;
2822 collapse_drag (tree, path);
2824 scroll_off (tree);
2825 return ret_val;
2828 static void
2829 et_drag_data_received (GtkWidget *widget,
2830 GdkDragContext *context,
2831 gint x,
2832 gint y,
2833 GtkSelectionData *selection_data,
2834 guint info,
2835 guint time,
2836 ETree *tree)
2838 gint row, col;
2839 ETreePath path;
2841 e_tree_get_cell_at (tree, x, y, &row, &col);
2843 path = e_tree_table_adapter_node_at_row (tree->priv->etta, row);
2844 g_signal_emit (
2845 tree, signals[TREE_DRAG_DATA_RECEIVED], 0,
2846 row,
2847 path,
2848 col,
2849 context,
2852 selection_data,
2853 info,
2854 time);
2857 static void
2858 e_tree_class_init (ETreeClass *class)
2860 GObjectClass *object_class;
2861 GtkWidgetClass *widget_class;
2863 g_type_class_add_private (class, sizeof (ETreePrivate));
2865 object_class = G_OBJECT_CLASS (class);
2866 object_class->dispose = et_dispose;
2867 object_class->set_property = et_set_property;
2868 object_class->get_property = et_get_property;
2870 widget_class = GTK_WIDGET_CLASS (class);
2871 widget_class->grab_focus = et_grab_focus;
2872 widget_class->unrealize = et_unrealize;
2873 widget_class->style_updated = et_canvas_style_updated;
2874 widget_class->focus = et_focus;
2876 class->start_drag = et_real_start_drag;
2878 signals[CURSOR_CHANGE] = g_signal_new (
2879 "cursor_change",
2880 G_OBJECT_CLASS_TYPE (object_class),
2881 G_SIGNAL_RUN_LAST,
2882 G_STRUCT_OFFSET (ETreeClass, cursor_change),
2883 NULL, NULL,
2884 e_marshal_NONE__INT_POINTER,
2885 G_TYPE_NONE, 2,
2886 G_TYPE_INT,
2887 G_TYPE_POINTER);
2889 signals[CURSOR_ACTIVATED] = g_signal_new (
2890 "cursor_activated",
2891 G_OBJECT_CLASS_TYPE (object_class),
2892 G_SIGNAL_RUN_LAST,
2893 G_STRUCT_OFFSET (ETreeClass, cursor_activated),
2894 NULL, NULL,
2895 e_marshal_NONE__INT_POINTER,
2896 G_TYPE_NONE, 2,
2897 G_TYPE_INT,
2898 G_TYPE_POINTER);
2900 signals[SELECTION_CHANGE] = g_signal_new (
2901 "selection_change",
2902 G_OBJECT_CLASS_TYPE (object_class),
2903 G_SIGNAL_RUN_LAST,
2904 G_STRUCT_OFFSET (ETreeClass, selection_change),
2905 NULL, NULL,
2906 g_cclosure_marshal_VOID__VOID,
2907 G_TYPE_NONE, 0);
2909 signals[DOUBLE_CLICK] = g_signal_new (
2910 "double_click",
2911 G_OBJECT_CLASS_TYPE (object_class),
2912 G_SIGNAL_RUN_LAST,
2913 G_STRUCT_OFFSET (ETreeClass, double_click),
2914 NULL, NULL,
2915 e_marshal_NONE__INT_POINTER_INT_BOXED,
2916 G_TYPE_NONE, 4,
2917 G_TYPE_INT,
2918 G_TYPE_POINTER,
2919 G_TYPE_INT,
2920 GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
2922 signals[RIGHT_CLICK] = g_signal_new (
2923 "right_click",
2924 G_OBJECT_CLASS_TYPE (object_class),
2925 G_SIGNAL_RUN_LAST,
2926 G_STRUCT_OFFSET (ETreeClass, right_click),
2927 g_signal_accumulator_true_handled, NULL,
2928 e_marshal_BOOLEAN__INT_POINTER_INT_BOXED,
2929 G_TYPE_BOOLEAN, 4,
2930 G_TYPE_INT,
2931 G_TYPE_POINTER,
2932 G_TYPE_INT,
2933 GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
2935 signals[CLICK] = g_signal_new (
2936 "click",
2937 G_OBJECT_CLASS_TYPE (object_class),
2938 G_SIGNAL_RUN_LAST,
2939 G_STRUCT_OFFSET (ETreeClass, click),
2940 g_signal_accumulator_true_handled, NULL,
2941 e_marshal_BOOLEAN__INT_POINTER_INT_BOXED,
2942 G_TYPE_BOOLEAN, 4,
2943 G_TYPE_INT,
2944 G_TYPE_POINTER,
2945 G_TYPE_INT,
2946 GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
2948 signals[KEY_PRESS] = g_signal_new (
2949 "key_press",
2950 G_OBJECT_CLASS_TYPE (object_class),
2951 G_SIGNAL_RUN_LAST,
2952 G_STRUCT_OFFSET (ETreeClass, key_press),
2953 g_signal_accumulator_true_handled, NULL,
2954 e_marshal_BOOLEAN__INT_POINTER_INT_BOXED,
2955 G_TYPE_BOOLEAN, 4,
2956 G_TYPE_INT,
2957 G_TYPE_POINTER,
2958 G_TYPE_INT,
2959 GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
2961 signals[START_DRAG] = g_signal_new (
2962 "start_drag",
2963 G_OBJECT_CLASS_TYPE (object_class),
2964 G_SIGNAL_RUN_LAST,
2965 G_STRUCT_OFFSET (ETreeClass, start_drag),
2966 NULL, NULL,
2967 e_marshal_NONE__INT_POINTER_INT_BOXED,
2968 G_TYPE_NONE, 4,
2969 G_TYPE_INT,
2970 G_TYPE_POINTER,
2971 G_TYPE_INT,
2972 GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
2974 signals[STATE_CHANGE] = g_signal_new (
2975 "state_change",
2976 G_OBJECT_CLASS_TYPE (object_class),
2977 G_SIGNAL_RUN_LAST,
2978 G_STRUCT_OFFSET (ETreeClass, state_change),
2979 NULL, NULL,
2980 g_cclosure_marshal_VOID__VOID,
2981 G_TYPE_NONE, 0);
2983 signals[WHITE_SPACE_EVENT] = g_signal_new (
2984 "white_space_event",
2985 G_OBJECT_CLASS_TYPE (object_class),
2986 G_SIGNAL_RUN_LAST,
2987 G_STRUCT_OFFSET (ETreeClass, white_space_event),
2988 g_signal_accumulator_true_handled, NULL,
2989 e_marshal_BOOLEAN__POINTER,
2990 G_TYPE_BOOLEAN, 1,
2991 GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
2993 signals[TREE_DRAG_BEGIN] = g_signal_new (
2994 "tree_drag_begin",
2995 G_OBJECT_CLASS_TYPE (object_class),
2996 G_SIGNAL_RUN_LAST,
2997 G_STRUCT_OFFSET (ETreeClass, tree_drag_begin),
2998 NULL, NULL,
2999 e_marshal_NONE__INT_POINTER_INT_BOXED,
3000 G_TYPE_NONE, 4,
3001 G_TYPE_INT,
3002 G_TYPE_POINTER,
3003 G_TYPE_INT,
3004 GDK_TYPE_DRAG_CONTEXT);
3006 signals[TREE_DRAG_END] = g_signal_new (
3007 "tree_drag_end",
3008 G_OBJECT_CLASS_TYPE (object_class),
3009 G_SIGNAL_RUN_LAST,
3010 G_STRUCT_OFFSET (ETreeClass, tree_drag_end),
3011 NULL, NULL,
3012 e_marshal_NONE__INT_POINTER_INT_BOXED,
3013 G_TYPE_NONE, 4,
3014 G_TYPE_INT,
3015 G_TYPE_POINTER,
3016 G_TYPE_INT,
3017 GDK_TYPE_DRAG_CONTEXT);
3019 signals[TREE_DRAG_DATA_GET] = g_signal_new (
3020 "tree_drag_data_get",
3021 G_OBJECT_CLASS_TYPE (object_class),
3022 G_SIGNAL_RUN_LAST,
3023 G_STRUCT_OFFSET (ETreeClass, tree_drag_data_get),
3024 NULL, NULL,
3025 e_marshal_NONE__INT_POINTER_INT_OBJECT_BOXED_UINT_UINT,
3026 G_TYPE_NONE, 7,
3027 G_TYPE_INT,
3028 G_TYPE_POINTER,
3029 G_TYPE_INT,
3030 GDK_TYPE_DRAG_CONTEXT,
3031 GTK_TYPE_SELECTION_DATA | G_SIGNAL_TYPE_STATIC_SCOPE,
3032 G_TYPE_UINT,
3033 G_TYPE_UINT);
3035 signals[TREE_DRAG_DATA_DELETE] = g_signal_new (
3036 "tree_drag_data_delete",
3037 G_OBJECT_CLASS_TYPE (object_class),
3038 G_SIGNAL_RUN_LAST,
3039 G_STRUCT_OFFSET (ETreeClass, tree_drag_data_delete),
3040 NULL, NULL,
3041 e_marshal_NONE__INT_POINTER_INT_OBJECT,
3042 G_TYPE_NONE, 4,
3043 G_TYPE_INT,
3044 G_TYPE_POINTER,
3045 G_TYPE_INT,
3046 GDK_TYPE_DRAG_CONTEXT);
3048 signals[TREE_DRAG_LEAVE] = g_signal_new (
3049 "tree_drag_leave",
3050 G_OBJECT_CLASS_TYPE (object_class),
3051 G_SIGNAL_RUN_LAST,
3052 G_STRUCT_OFFSET (ETreeClass, tree_drag_leave),
3053 NULL, NULL,
3054 e_marshal_NONE__INT_POINTER_INT_OBJECT_UINT,
3055 G_TYPE_NONE, 5,
3056 G_TYPE_INT,
3057 G_TYPE_POINTER,
3058 G_TYPE_INT,
3059 GDK_TYPE_DRAG_CONTEXT,
3060 G_TYPE_UINT);
3062 signals[TREE_DRAG_MOTION] = g_signal_new (
3063 "tree_drag_motion",
3064 G_OBJECT_CLASS_TYPE (object_class),
3065 G_SIGNAL_RUN_LAST,
3066 G_STRUCT_OFFSET (ETreeClass, tree_drag_motion),
3067 NULL, NULL,
3068 e_marshal_BOOLEAN__INT_POINTER_INT_OBJECT_INT_INT_UINT,
3069 G_TYPE_BOOLEAN, 7,
3070 G_TYPE_INT,
3071 G_TYPE_POINTER,
3072 G_TYPE_INT,
3073 GDK_TYPE_DRAG_CONTEXT,
3074 G_TYPE_INT,
3075 G_TYPE_INT,
3076 G_TYPE_UINT);
3078 signals[TREE_DRAG_DROP] = g_signal_new (
3079 "tree_drag_drop",
3080 G_OBJECT_CLASS_TYPE (object_class),
3081 G_SIGNAL_RUN_LAST,
3082 G_STRUCT_OFFSET (ETreeClass, tree_drag_drop),
3083 NULL, NULL,
3084 e_marshal_BOOLEAN__INT_POINTER_INT_OBJECT_INT_INT_UINT,
3085 G_TYPE_BOOLEAN, 7,
3086 G_TYPE_INT,
3087 G_TYPE_POINTER,
3088 G_TYPE_INT,
3089 GDK_TYPE_DRAG_CONTEXT,
3090 G_TYPE_INT,
3091 G_TYPE_INT,
3092 G_TYPE_UINT);
3094 signals[TREE_DRAG_DATA_RECEIVED] = g_signal_new (
3095 "tree_drag_data_received",
3096 G_OBJECT_CLASS_TYPE (object_class),
3097 G_SIGNAL_RUN_LAST,
3098 G_STRUCT_OFFSET (ETreeClass, tree_drag_data_received),
3099 NULL, NULL,
3100 e_marshal_NONE__INT_POINTER_INT_OBJECT_INT_INT_BOXED_UINT_UINT,
3101 G_TYPE_NONE, 9,
3102 G_TYPE_INT,
3103 G_TYPE_POINTER,
3104 G_TYPE_INT,
3105 GDK_TYPE_DRAG_CONTEXT,
3106 G_TYPE_INT,
3107 G_TYPE_INT,
3108 GTK_TYPE_SELECTION_DATA,
3109 G_TYPE_UINT,
3110 G_TYPE_UINT);
3112 g_object_class_install_property (
3113 object_class,
3114 PROP_LENGTH_THRESHOLD,
3115 g_param_spec_int (
3116 "length_threshold",
3117 "Length Threshold",
3118 "Length Threshold",
3119 0, G_MAXINT, 0,
3120 G_PARAM_WRITABLE));
3122 g_object_class_install_property (
3123 object_class,
3124 PROP_HORIZONTAL_DRAW_GRID,
3125 g_param_spec_boolean (
3126 "horizontal_draw_grid",
3127 "Horizontal Draw Grid",
3128 "Horizontal Draw Grid",
3129 FALSE,
3130 G_PARAM_WRITABLE));
3132 g_object_class_install_property (
3133 object_class,
3134 PROP_VERTICAL_DRAW_GRID,
3135 g_param_spec_boolean (
3136 "vertical_draw_grid",
3137 "Vertical Draw Grid",
3138 "Vertical Draw Grid",
3139 FALSE,
3140 G_PARAM_WRITABLE));
3142 g_object_class_install_property (
3143 object_class,
3144 PROP_DRAW_FOCUS,
3145 g_param_spec_boolean (
3146 "drawfocus",
3147 "Draw focus",
3148 "Draw focus",
3149 FALSE,
3150 G_PARAM_WRITABLE));
3152 g_object_class_install_property (
3153 object_class,
3154 PROP_ETTA,
3155 g_param_spec_object (
3156 "ETreeTableAdapter",
3157 "ETree table adapter",
3158 "ETree table adapter",
3159 E_TYPE_TREE_TABLE_ADAPTER,
3160 G_PARAM_READABLE));
3162 g_object_class_install_property (
3163 object_class,
3164 PROP_UNIFORM_ROW_HEIGHT,
3165 g_param_spec_boolean (
3166 "uniform_row_height",
3167 "Uniform row height",
3168 "Uniform row height",
3169 FALSE,
3170 G_PARAM_READWRITE));
3172 g_object_class_install_property (
3173 object_class,
3174 PROP_IS_EDITING,
3175 g_param_spec_boolean (
3176 "is-editing",
3177 "Whether is in an editing mode",
3178 "Whether is in an editing mode",
3179 FALSE,
3180 G_PARAM_READABLE));
3182 g_object_class_install_property (
3183 object_class,
3184 PROP_ALWAYS_SEARCH,
3185 g_param_spec_boolean (
3186 "always_search",
3187 "Always search",
3188 "Always search",
3189 FALSE,
3190 G_PARAM_READWRITE));
3192 g_object_class_install_property (
3193 object_class,
3194 PROP_SORT_CHILDREN_ASCENDING,
3195 g_param_spec_boolean (
3196 "sort-children-ascending",
3197 "Sort Children Ascending",
3198 "Always sort children tree nodes ascending",
3199 FALSE,
3200 G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
3202 gtk_widget_class_install_style_property (
3203 widget_class,
3204 g_param_spec_int (
3205 "expander_size",
3206 "Expander Size",
3207 "Size of the expander arrow",
3208 0, G_MAXINT, 10,
3209 G_PARAM_READABLE));
3211 gtk_widget_class_install_style_property (
3212 widget_class,
3213 g_param_spec_int (
3214 "vertical-spacing",
3215 "Vertical Row Spacing",
3216 "Vertical space between rows. "
3217 "It is added to top and to bottom of a row",
3218 0, G_MAXINT, 3,
3219 G_PARAM_READABLE |
3220 G_PARAM_STATIC_STRINGS));
3222 gtk_widget_class_install_style_property (
3223 widget_class,
3224 g_param_spec_boolean (
3225 "alternating-row-colors",
3226 "Alternating Row Colors",
3227 "Whether to use alternating row colors",
3228 TRUE,
3229 G_PARAM_READABLE));
3231 /* Scrollable interface */
3232 g_object_class_override_property (
3233 object_class, PROP_HADJUSTMENT, "hadjustment");
3234 g_object_class_override_property (
3235 object_class, PROP_VADJUSTMENT, "vadjustment");
3236 g_object_class_override_property (
3237 object_class, PROP_HSCROLL_POLICY, "hscroll-policy");
3238 g_object_class_override_property (
3239 object_class, PROP_VSCROLL_POLICY, "vscroll-policy");
3241 gtk_widget_class_set_accessible_type (widget_class,
3242 GAL_A11Y_TYPE_E_TREE);
3245 #if GTK_CHECK_VERSION (3, 15, 9)
3246 static gboolean
3247 e_tree_scrollable_get_border (GtkScrollable *scrollable,
3248 GtkBorder *border)
3250 ETree *tree;
3251 ETableHeaderItem *header_item;
3253 g_return_val_if_fail (E_IS_TREE (scrollable), FALSE);
3254 g_return_val_if_fail (border != NULL, FALSE);
3256 tree = E_TREE (scrollable);
3257 if (!tree->priv->header_item)
3258 return FALSE;
3260 g_return_val_if_fail (E_IS_TABLE_HEADER_ITEM (tree->priv->header_item), FALSE);
3262 header_item = E_TABLE_HEADER_ITEM (tree->priv->header_item);
3264 border->top = header_item->height;
3266 return TRUE;
3268 #endif
3270 static void
3271 e_tree_scrollable_init (GtkScrollableInterface *iface)
3273 #if GTK_CHECK_VERSION (3, 15, 9)
3274 iface->get_border = e_tree_scrollable_get_border;
3275 #endif
3278 static void
3279 tree_size_allocate (GtkWidget *widget,
3280 GtkAllocation *alloc,
3281 ETree *tree)
3283 gdouble width;
3285 g_return_if_fail (E_IS_TREE (tree));
3286 g_return_if_fail (tree->priv->info_text != NULL);
3288 gnome_canvas_get_scroll_region (
3289 GNOME_CANVAS (tree->priv->table_canvas),
3290 NULL, NULL, &width, NULL);
3292 width -= 60.0;
3294 g_object_set (
3295 tree->priv->info_text, "width", width,
3296 "clip_width", width, NULL);
3300 * e_tree_set_info_message:
3301 * @tree: #ETree instance
3302 * @info_message: Message to set. Can be NULL.
3304 * Creates an info message in table area, or removes old.
3306 void
3307 e_tree_set_info_message (ETree *tree,
3308 const gchar *info_message)
3310 GtkAllocation allocation;
3311 GtkWidget *widget;
3313 g_return_if_fail (E_IS_TREE (tree));
3315 if (!tree->priv->info_text && (!info_message || !*info_message))
3316 return;
3318 if (!info_message || !*info_message) {
3319 g_signal_handler_disconnect (tree, tree->priv->info_text_resize_id);
3320 g_object_run_dispose (G_OBJECT (tree->priv->info_text));
3321 tree->priv->info_text = NULL;
3322 return;
3325 widget = GTK_WIDGET (tree->priv->table_canvas);
3326 gtk_widget_get_allocation (widget, &allocation);
3328 if (!tree->priv->info_text) {
3329 if (allocation.width > 60) {
3330 tree->priv->info_text = gnome_canvas_item_new (
3331 GNOME_CANVAS_GROUP (gnome_canvas_root (tree->priv->table_canvas)),
3332 e_text_get_type (),
3333 "line_wrap", TRUE,
3334 "clip", TRUE,
3335 "justification", GTK_JUSTIFY_LEFT,
3336 "text", info_message,
3337 "width", (gdouble) allocation.width - 60.0,
3338 "clip_width", (gdouble) allocation.width - 60.0,
3339 NULL);
3341 e_canvas_item_move_absolute (tree->priv->info_text, 30, 30);
3343 tree->priv->info_text_resize_id = g_signal_connect (
3344 tree, "size_allocate",
3345 G_CALLBACK (tree_size_allocate), tree);
3347 } else
3348 gnome_canvas_item_set (tree->priv->info_text, "text", info_message, NULL);
3351 void
3352 e_tree_freeze_state_change (ETree *tree)
3354 g_return_if_fail (E_IS_TREE (tree));
3356 tree->priv->state_change_freeze++;
3357 if (tree->priv->state_change_freeze == 1)
3358 tree->priv->state_changed = FALSE;
3360 g_return_if_fail (tree->priv->state_change_freeze != 0);
3363 void
3364 e_tree_thaw_state_change (ETree *tree)
3366 g_return_if_fail (E_IS_TREE (tree));
3367 g_return_if_fail (tree->priv->state_change_freeze != 0);
3369 tree->priv->state_change_freeze--;
3370 if (tree->priv->state_change_freeze == 0 && tree->priv->state_changed) {
3371 tree->priv->state_changed = FALSE;
3372 e_tree_state_change (tree);
3376 gboolean
3377 e_tree_is_editing (ETree *tree)
3379 g_return_val_if_fail (E_IS_TREE (tree), FALSE);
3381 return tree->priv->item && e_table_item_is_editing (E_TABLE_ITEM (tree->priv->item));
3384 void
3385 e_tree_set_grouped_view (ETree *tree,
3386 gboolean grouped_view)
3388 g_return_if_fail (E_IS_TREE (tree));
3390 if ((tree->priv->grouped_view ? 1 : 0) == (grouped_view ? 1 : 0))
3391 return;
3393 tree->priv->grouped_view = grouped_view;
3395 e_tree_update_full_header_grouped_view (tree);
3398 gboolean
3399 e_tree_get_grouped_view (ETree *tree)
3401 g_return_val_if_fail (E_IS_TREE (tree), FALSE);
3403 return tree->priv->grouped_view;
3406 gboolean
3407 e_tree_get_sort_children_ascending (ETree *tree)
3409 g_return_val_if_fail (E_IS_TREE (tree), FALSE);
3411 return tree->priv->sort_children_ascending;
3414 void
3415 e_tree_set_sort_children_ascending (ETree *tree,
3416 gboolean sort_children_ascending)
3418 g_return_if_fail (E_IS_TREE (tree));
3420 if ((tree->priv->sort_children_ascending ? 1 : 0) == (sort_children_ascending ? 1 : 0))
3421 return;
3423 tree->priv->sort_children_ascending = sort_children_ascending;
3425 g_object_notify (G_OBJECT (tree), "sort-children-ascending");