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
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/>.
16 * Chris Lahey <clahey@ximian.com>
18 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
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"
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"
47 #include "e-tree-selection-model.h"
48 #include "e-tree-table-adapter.h"
50 #include "e-misc-utils.h"
51 #include "gal-a11y-e-tree.h"
53 #define COLUMN_HEADER_HEIGHT 16
57 #define E_TREE_GET_PRIVATE(obj) \
58 (G_TYPE_INSTANCE_GET_PRIVATE \
59 ((obj), E_TYPE_TREE, ETreePrivate))
61 typedef struct _ETreeDragSourceSite ETreeDragSourceSite
;
83 TREE_DRAG_DATA_DELETE
,
88 TREE_DRAG_DATA_RECEIVED
,
95 PROP_LENGTH_THRESHOLD
,
96 PROP_HORIZONTAL_DRAW_GRID
,
97 PROP_VERTICAL_DRAW_GRID
,
100 PROP_UNIFORM_ROW_HEIGHT
,
107 PROP_SORT_CHILDREN_ASCENDING
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
{
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
;
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;
179 guint uniform_row_height
: 1;
181 guint search_col_set
: 1;
182 guint always_search
: 1;
184 ECursorMode cursor_mode
;
190 GnomeCanvasItem
*drop_highlight
;
194 GdkDragContext
*last_drop_context
;
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
,
222 static void et_drag_end (GtkWidget
*widget
,
223 GdkDragContext
*context
,
225 static void et_drag_data_get (GtkWidget
*widget
,
226 GdkDragContext
*context
,
227 GtkSelectionData
*selection_data
,
231 static void et_drag_data_delete (GtkWidget
*widget
,
232 GdkDragContext
*context
,
235 static void et_drag_leave (GtkWidget
*widget
,
236 GdkDragContext
*context
,
239 static gboolean
et_drag_motion (GtkWidget
*widget
,
240 GdkDragContext
*context
,
245 static gboolean
et_drag_drop (GtkWidget
*widget
,
246 GdkDragContext
*context
,
251 static void et_drag_data_received (GtkWidget
*widget
,
252 GdkDragContext
*context
,
255 GtkSelectionData
*selection_data
,
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
))
272 tree_item_is_editing_changed_cb (ETableItem
*item
,
276 g_return_if_fail (E_IS_TREE (tree
));
278 g_object_notify (G_OBJECT (tree
), "is-editing");
282 et_disconnect_from_etta (ETree
*tree
)
284 if (tree
->priv
->table_model_change_id
!= 0)
285 g_signal_handler_disconnect (
287 tree
->priv
->table_model_change_id
);
288 if (tree
->priv
->table_row_change_id
!= 0)
289 g_signal_handler_disconnect (
291 tree
->priv
->table_row_change_id
);
292 if (tree
->priv
->table_cell_change_id
!= 0)
293 g_signal_handler_disconnect (
295 tree
->priv
->table_cell_change_id
);
296 if (tree
->priv
->table_rows_delete_id
!= 0)
297 g_signal_handler_disconnect (
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;
308 clear_current_search_col (ETree
*tree
)
310 tree
->priv
->search_col_set
= FALSE
;
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 (
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
;
330 e_tree_state_change (ETree
*tree
)
332 if (tree
->priv
->state_change_freeze
)
333 tree
->priv
->state_changed
= TRUE
;
335 g_signal_emit (tree
, signals
[STATE_CHANGE
], 0);
339 change_trigger (GObject
*object
,
342 e_tree_state_change (tree
);
346 search_col_change_trigger (GObject
*object
,
349 clear_current_search_col (tree
);
350 e_tree_state_change (tree
);
354 disconnect_header (ETree
*tree
)
356 if (tree
->priv
->header
== NULL
)
359 if (tree
->priv
->structure_change_id
)
360 g_signal_handler_disconnect (
362 tree
->priv
->structure_change_id
);
363 if (tree
->priv
->expansion_change_id
)
364 g_signal_handler_disconnect (
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
;
385 connect_header (ETree
*tree
,
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
);
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
);
424 et_dispose (GObject
*object
)
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
);
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
));
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
);
459 if (priv
->model
!= NULL
) {
460 g_object_unref (priv
->model
);
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
);
481 if (priv
->header_canvas
!= NULL
) {
482 gtk_widget_destroy (GTK_WIDGET (priv
->header_canvas
));
483 priv
->header_canvas
= NULL
;
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 */
516 /* Chain up to parent's dispose() method. */
517 G_OBJECT_CLASS (e_tree_parent_class
)->dispose (object
);
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
);
533 } SearchSearchStruct
;
536 search_search_callback (ETreeModel
*model
,
540 SearchSearchStruct
*cb_data
= data
;
542 ETableCol
*col
= current_search_col (cb_data
->tree
);
544 value
= e_tree_model_value_at (
546 cb_data
->tree
->priv
->current_search_col
->spec
->model_col
);
548 return col
->search (value
, cb_data
->string
);
552 et_search_search (ETableSearch
*search
,
554 ETableSearchFlags flags
,
559 SearchSearchStruct cb_data
;
560 ETableCol
*col
= current_search_col (tree
);
566 cb_data
.string
= string
;
568 cursor
= e_tree_get_cursor (tree
);
570 if (cursor
&& (flags
& E_TABLE_SEARCH_FLAGS_CHECK_CURSOR_FIRST
)) {
573 value
= e_tree_model_value_at (
574 tree
->priv
->model
, cursor
, col
->spec
->model_col
);
576 if (col
->search (value
, string
)) {
581 found
= e_tree_model_node_find (
582 tree
->priv
->model
, cursor
, NULL
,
583 search_search_callback
, &cb_data
);
585 found
= e_tree_model_node_find (
586 tree
->priv
->model
, NULL
, cursor
,
587 search_search_callback
, &cb_data
);
589 if (found
&& found
!= cursor
) {
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
,
600 } else if (cursor
&& !(flags
& E_TABLE_SEARCH_FLAGS_CHECK_CURSOR_FIRST
)) {
603 value
= e_tree_model_value_at (
604 tree
->priv
->model
, cursor
, col
->spec
->model_col
);
606 return col
->search (value
, string
);
612 et_search_accept (ETableSearch
*search
,
615 ETableCol
*col
= current_search_col (tree
);
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);
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 */
674 et_grab_focus (GtkWidget
*widget
)
678 tree
= E_TREE (widget
);
680 gtk_widget_grab_focus (GTK_WIDGET (tree
->priv
->table_canvas
));
683 /* Focus handler for the ETree */
685 et_focus (GtkWidget
*container
,
686 GtkDirectionType direction
)
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
);
697 return gtk_widget_child_focus (
698 GTK_WIDGET (tree
->priv
->table_canvas
), direction
);
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
))
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);
727 header_canvas_size_allocate (GtkWidget
*widget
,
728 GtkAllocation
*alloc
,
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 (
745 E_TABLE_HEADER_ITEM (tree
->priv
->header_item
)->height
);
749 e_tree_setup_header (ETree
*tree
)
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
,
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
);
785 scroll_to_cursor (ETree
*tree
)
788 GtkAdjustment
*adjustment
;
789 GtkScrollable
*scrollable
;
796 path
= e_tree_get_cursor (tree
);
800 ETreeTableAdapter
*adapter
;
804 adapter
= e_tree_get_table_adapter (tree
);
805 row
= e_tree_table_adapter_row_of_node (adapter
, path
);
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
);
830 tree_canvas_reflow_idle (ETree
*tree
)
832 gdouble height
, width
;
833 gdouble oldheight
, oldwidth
;
834 GtkAllocation allocation
;
837 widget
= GTK_WIDGET (tree
->priv
->table_canvas
);
838 gtk_widget_get_allocation (widget
, &allocation
);
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
);
871 tree_canvas_size_allocate (GtkWidget
*widget
,
872 GtkAllocation
*alloc
,
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
);
886 height
= MAX ((gint
) height
, alloc
->height
);
892 g_object_set_property (G_OBJECT (tree
->priv
->header
), "width", val
);
895 if (tree
->priv
->reflow_idle_id
)
896 g_source_remove (tree
->priv
->reflow_idle_id
);
897 tree_canvas_reflow_idle (tree
);
901 tree_canvas_reflow (GnomeCanvas
*canvas
,
904 if (!tree
->priv
->reflow_idle_id
)
905 tree
->priv
->reflow_idle_id
= g_idle_add_full (
906 400, (GSourceFunc
) tree_canvas_reflow_idle
,
911 item_cursor_change (ETableItem
*eti
,
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
);
921 item_cursor_activated (ETableItem
*eti
,
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
);
931 item_double_click (ETableItem
*eti
,
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
);
943 item_right_click (ETableItem
*eti
,
949 ETreePath path
= e_tree_table_adapter_node_at_row (tree
->priv
->etta
, row
);
950 gboolean return_val
= 0;
953 tree
, signals
[RIGHT_CLICK
], 0,
954 row
, path
, col
, event
, &return_val
);
960 item_click (ETableItem
*eti
,
966 gboolean return_val
= 0;
967 ETreePath path
= e_tree_table_adapter_node_at_row (tree
->priv
->etta
, row
);
970 tree
, signals
[CLICK
], 0, row
, path
, col
, event
, &return_val
);
976 item_key_press (ETableItem
*eti
,
983 GdkEventKey
*key
= (GdkEventKey
*) event
;
985 gint y
, row_local
, col_local
;
986 GtkAdjustment
*adjustment
;
987 GtkScrollable
*scrollable
;
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
);
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
);
1018 case GDK_KEY_Page_Up
:
1019 case GDK_KEY_KP_Page_Up
:
1020 y
= CLAMP (value
- (page_size
- 50), 0, upper
);
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
);
1037 case GDK_KEY_KP_Add
:
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
)
1046 path
= e_tree_table_adapter_node_at_row (
1047 tree
->priv
->etta
, row
);
1049 e_tree_table_adapter_node_set_expanded (
1050 tree
->priv
->etta
, path
, TRUE
);
1054 case GDK_KEY_underscore
:
1055 case GDK_KEY_KP_Subtract
:
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
)
1064 path
= e_tree_table_adapter_node_at_row (
1065 tree
->priv
->etta
, row
);
1067 e_tree_table_adapter_node_set_expanded (
1068 tree
->priv
->etta
, path
, FALSE
);
1072 case GDK_KEY_BackSpace
:
1073 if (e_table_search_backspace (tree
->priv
->search
))
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
);
1088 signals
[KEY_PRESS
], 0,
1089 row
, path
, col
, event
, &return_val
);
1096 item_start_drag (ETableItem
*eti
,
1103 gint return_val
= 0;
1105 path
= e_tree_table_adapter_node_at_row (tree
->priv
->etta
, row
);
1108 tree
, signals
[START_DRAG
], 0,
1109 row
, path
, col
, event
, &return_val
);
1115 et_selection_model_selection_changed (ETableSelectionModel
*etsm
,
1118 g_signal_emit (tree
, signals
[SELECTION_CHANGE
], 0);
1122 et_selection_model_selection_row_changed (ETableSelectionModel
*etsm
,
1126 g_signal_emit (tree
, signals
[SELECTION_CHANGE
], 0);
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
,
1161 tree
->priv
->item
, "cursor_change",
1162 G_CALLBACK (item_cursor_change
), tree
);
1164 tree
->priv
->item
, "cursor_activated",
1165 G_CALLBACK (item_cursor_activated
), tree
);
1167 tree
->priv
->item
, "double_click",
1168 G_CALLBACK (item_double_click
), tree
);
1170 tree
->priv
->item
, "right_click",
1171 G_CALLBACK (item_right_click
), tree
);
1173 tree
->priv
->item
, "click",
1174 G_CALLBACK (item_click
), tree
);
1176 tree
->priv
->item
, "key_press",
1177 G_CALLBACK (item_key_press
), tree
);
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
);
1187 et_canvas_style_updated (GtkWidget
*widget
)
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
,
1202 white_item_event (GnomeCanvasItem
*white_item
,
1206 gboolean return_val
= 0;
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
));
1228 et_canvas_root_event (GnomeCanvasItem
*root
,
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
));
1254 /* Handler for focus events in the table_canvas; we have to repaint ourselves
1255 * and give the focus to some ETableItem.
1258 table_canvas_focus_event_cb (GtkWidget
*widget
,
1259 GdkEventFocus
*event
,
1262 GnomeCanvas
*canvas
;
1265 gtk_widget_queue_draw (widget
);
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
);
1283 e_tree_table_canvas_scrolled_cb (GtkAdjustment
*vadjustment
,
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
));
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
));
1310 tree
->priv
->table_canvas_vadjustment
= g_object_ref (vadjustment
);
1312 vadjustment
, "notify::value",
1313 G_CALLBACK (e_tree_table_canvas_scrolled_cb
), tree
);
1318 e_tree_setup_table (ETree
*tree
)
1323 tree
->priv
->table_canvas
= GNOME_CANVAS (e_canvas_new ());
1325 tree
->priv
->table_canvas
, "size_allocate",
1326 G_CALLBACK (tree_canvas_size_allocate
), tree
);
1328 tree
->priv
->table_canvas
, "focus_in_event",
1329 G_CALLBACK (table_canvas_focus_event_cb
), tree
);
1331 tree
->priv
->table_canvas
, "focus_out_event",
1332 G_CALLBACK (table_canvas_focus_event_cb
), tree
);
1335 tree
->priv
->table_canvas
, "drag_begin",
1336 G_CALLBACK (et_drag_begin
), tree
);
1338 tree
->priv
->table_canvas
, "drag_end",
1339 G_CALLBACK (et_drag_end
), tree
);
1341 tree
->priv
->table_canvas
, "drag_data_get",
1342 G_CALLBACK (et_drag_data_get
), tree
);
1344 tree
->priv
->table_canvas
, "drag_data_delete",
1345 G_CALLBACK (et_drag_data_delete
), tree
);
1347 tree
, "drag_motion",
1348 G_CALLBACK (et_drag_motion
), tree
);
1351 G_CALLBACK (et_drag_leave
), tree
);
1354 G_CALLBACK (et_drag_drop
), tree
);
1356 tree
, "drag_data_received",
1357 G_CALLBACK (et_drag_data_received
), tree
);
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
,
1381 tree
->priv
->white_item
, "event",
1382 G_CALLBACK (white_item_event
), tree
);
1384 gnome_canvas_root (tree
->priv
->table_canvas
), "event",
1385 G_CALLBACK (et_canvas_root_event
), tree
);
1387 et_build_item (tree
);
1391 e_tree_set_state_object (ETree
*tree
,
1395 GtkAllocation allocation
;
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
);
1410 if (tree
->priv
->header_item
)
1412 tree
->priv
->header_item
,
1413 "ETableHeader", tree
->priv
->header
,
1414 "sort_info", tree
->priv
->sort_info
,
1417 if (tree
->priv
->item
)
1420 "ETableHeader", tree
->priv
->header
,
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
1438 * The %ETableState object generated.
1441 e_tree_get_state_object (ETree
*tree
)
1445 gint full_col_count
;
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
]);
1473 state
->expansions
[i
] = col
->expansion
;
1476 g_ptr_array_unref (columns
);
1483 * @tree: The #ETree to query
1485 * Returns the specification object.
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
;
1498 et_table_model_changed (ETableModel
*model
,
1501 if (tree
->priv
->horizontal_scrolling
)
1502 e_table_header_update_horizontal (tree
->priv
->header
);
1506 et_table_row_changed (ETableModel
*table_model
,
1510 et_table_model_changed (table_model
, tree
);
1514 et_table_cell_changed (ETableModel
*table_model
,
1519 et_table_model_changed (table_model
, tree
);
1523 et_table_rows_deleted (ETableModel
*table_model
,
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
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) */
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
);
1554 e_tree_update_full_header_grouped_view (ETree
*tree
)
1558 g_return_if_fail (E_IS_TREE (tree
));
1560 if (!tree
->priv
->full_header
)
1563 sz
= e_table_header_count (tree
->priv
->full_header
);
1564 for (ii
= 0; ii
< sz
; ii
++) {
1567 col
= e_table_header_get_column (tree
->priv
->full_header
, ii
);
1568 if (!col
|| !E_IS_CELL_TREE (col
->ecell
))
1571 e_cell_tree_set_grouped_view (E_CELL_TREE (col
->ecell
), tree
->priv
->grouped_view
);
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",
1600 et_real_construct (ETree
*tree
,
1603 ETableSpecification
*specification
,
1606 GtkAdjustment
*adjustment
;
1607 GtkScrollable
*scrollable
;
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
;
1631 tree
->priv
->etta
= E_TREE_TABLE_ADAPTER (
1632 e_tree_table_adapter_new (
1634 tree
->priv
->sort_info
,
1635 tree
->priv
->full_header
));
1637 et_connect_to_etta (tree
);
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
,
1648 tree
->priv
->selection
, "selection_changed",
1649 G_CALLBACK (et_selection_model_selection_changed
), tree
);
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
) {
1673 GTK_WIDGET (tree
->priv
->header_canvas
),
1674 0, 1, 0 + row
, 1 + row
,
1675 GTK_FILL
| GTK_EXPAND
,
1682 GTK_WIDGET (tree
->priv
->table_canvas
),
1683 0, 1, 0 + row
, 1 + row
,
1684 GTK_FILL
| GTK_EXPAND
,
1685 GTK_FILL
| GTK_EXPAND
,
1688 g_object_unref (ete
);
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
1706 e_tree_construct (ETree
*tree
,
1709 ETableSpecification
*specification
)
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
);
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.
1747 * The newly created #ETree or %NULL if there's an error.
1750 e_tree_new (ETreeModel
*etm
,
1752 ETableSpecification
*specification
)
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
);
1767 return GTK_WIDGET (tree
);
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
;
1779 e_tree_set_cursor (ETree
*tree
,
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
);
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 */
1800 et_foreach_recurse (ETreeModel
*model
,
1802 ETreeForeachFunc callback
,
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
))
1812 et_foreach_recurse (model
, child
, callback
, closure
);
1816 e_tree_path_foreach (ETree
*tree
,
1817 ETreeForeachFunc callback
,
1822 g_return_if_fail (E_IS_TREE (tree
));
1824 root
= e_tree_model_get_root (tree
->priv
->model
);
1827 et_foreach_recurse (tree
->priv
->model
,
1834 et_get_property (GObject
*object
,
1839 ETree
*tree
= E_TREE (object
);
1841 switch (property_id
) {
1843 g_value_set_object (value
, tree
->priv
->etta
);
1846 case PROP_UNIFORM_ROW_HEIGHT
:
1847 g_value_set_boolean (value
, tree
->priv
->uniform_row_height
);
1850 case PROP_IS_EDITING
:
1851 g_value_set_boolean (value
, e_tree_is_editing (tree
));
1854 case PROP_ALWAYS_SEARCH
:
1855 g_value_set_boolean (value
, tree
->priv
->always_search
);
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
);
1864 g_value_set_object (value
, NULL
);
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
);
1873 g_value_set_object (value
, NULL
);
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
);
1882 g_value_set_enum (value
, 0);
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
);
1891 g_value_set_enum (value
, 0);
1894 case PROP_SORT_CHILDREN_ASCENDING
:
1895 g_value_set_boolean (value
, e_tree_get_sort_children_ascending (tree
));
1899 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
1910 et_set_property (GObject
*object
,
1912 const GValue
*value
,
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
),
1924 tree
->priv
->length_threshold
,
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
,
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
,
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
),
1957 tree
->priv
->draw_focus
,
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
,
1973 case PROP_ALWAYS_SEARCH
:
1974 if (tree
->priv
->always_search
== g_value_get_boolean (value
))
1976 tree
->priv
->always_search
= g_value_get_boolean (value
);
1977 clear_current_search_col (tree
);
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
);
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
);
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
);
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
);
2008 case PROP_SORT_CHILDREN_ASCENDING
:
2009 e_tree_set_sort_children_ascending (tree
, g_value_get_boolean (value
));
2018 * Returns the model upon which this ETree is based.
2020 * Returns: the model
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:
2034 * Returns the selection model of this ETree.
2036 * Returns: the selection model
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:
2050 * Returns the table adapter this ETree uses.
2052 * Returns: the model
2055 e_tree_get_table_adapter (ETree
*tree
)
2057 g_return_val_if_fail (E_IS_TREE (tree
), NULL
);
2059 return tree
->priv
->etta
;
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
);
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 */
2093 GTK_DRAG_STATUS_DRAG
,
2094 GTK_DRAG_STATUS_WAIT
,
2095 GTK_DRAG_STATUS_DROP
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
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. */
2148 et_real_start_drag (ETree
*tree
,
2154 GtkDragSourceInfo
*info
;
2155 GdkDragContext
*context
;
2156 ETreeDragSourceSite
*site
;
2158 if (tree
->priv
->do_drag
) {
2159 site
= tree
->priv
->site
;
2162 context
= e_tree_drag_begin (
2169 info
= g_dataset_get_data (context
, "gtk-info");
2171 if (info
&& !info
->icon_window
) {
2173 gtk_drag_set_icon_pixbuf (
2178 gtk_drag_set_icon_default (context
);
2187 e_tree_drag_source_set (ETree
*tree
,
2188 GdkModifierType start_button_mask
,
2189 const GtkTargetEntry
*targets
,
2191 GdkDragAction actions
)
2193 ETreeDragSourceSite
*site
;
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 (
2205 gtk_widget_get_events (canvas
) |
2206 GDK_BUTTON_PRESS_MASK
| GDK_BUTTON_RELEASE_MASK
|
2207 GDK_BUTTON_MOTION_MASK
| GDK_STRUCTURE_MASK
);
2210 if (site
->target_list
)
2211 gtk_target_list_unref (site
->target_list
);
2213 site
= g_new0 (ETreeDragSourceSite
, 1);
2214 tree
->priv
->site
= site
;
2217 site
->start_button_mask
= start_button_mask
;
2220 site
->target_list
= gtk_target_list_new (targets
, n_targets
);
2222 site
->target_list
= NULL
;
2224 site
->actions
= actions
;
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
;
2237 if (site
->target_list
)
2238 gtk_target_list_unref (site
->target_list
);
2240 tree
->priv
->site
= NULL
;
2244 /* There probably should be functions for setting the targets
2245 * as a GtkTargetList
2249 e_tree_drag_begin (ETree
*tree
,
2252 GtkTargetList
*targets
,
2253 GdkDragAction actions
,
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
),
2276 * e_tree_is_dragging:
2277 * @tree: An #ETree widget
2279 * Returns whether is @tree in a drag&drop operation.
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
2301 e_tree_get_cell_at (ETree
*tree
,
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. */
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:
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.
2350 e_tree_get_cell_geometry (ETree
*tree
,
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
);
2376 adjustment
= gtk_scrollable_get_hadjustment (scrollable
);
2377 (*x_return
) -= gtk_adjustment_get_value (adjustment
);
2381 adjustment
= gtk_scrollable_get_vadjustment (scrollable
);
2382 (*y_return
) -= gtk_adjustment_get_value (adjustment
);
2387 et_drag_begin (GtkWidget
*widget
,
2388 GdkDragContext
*context
,
2391 tree
->priv
->is_dragging
= TRUE
;
2395 signals
[TREE_DRAG_BEGIN
], 0,
2396 tree
->priv
->drag_row
,
2397 tree
->priv
->drag_path
,
2398 tree
->priv
->drag_col
,
2403 et_drag_end (GtkWidget
*widget
,
2404 GdkDragContext
*context
,
2407 tree
->priv
->is_dragging
= FALSE
;
2411 signals
[TREE_DRAG_END
], 0,
2412 tree
->priv
->drag_row
,
2413 tree
->priv
->drag_path
,
2414 tree
->priv
->drag_col
,
2419 et_drag_data_get (GtkWidget
*widget
,
2420 GdkDragContext
*context
,
2421 GtkSelectionData
*selection_data
,
2428 signals
[TREE_DRAG_DATA_GET
], 0,
2429 tree
->priv
->drag_row
,
2430 tree
->priv
->drag_path
,
2431 tree
->priv
->drag_col
,
2439 et_drag_data_delete (GtkWidget
*widget
,
2440 GdkDragContext
*context
,
2445 signals
[TREE_DRAG_DATA_DELETE
], 0,
2446 tree
->priv
->drag_row
,
2447 tree
->priv
->drag_path
,
2448 tree
->priv
->drag_col
,
2453 do_drag_motion (ETree
*tree
,
2454 GdkDragContext
*context
,
2459 gboolean ret_val
= FALSE
;
2463 e_tree_get_cell_at (tree
, x
, y
, &row
, &col
);
2465 if (row
!= tree
->priv
->drop_row
&& col
!= tree
->priv
->drop_col
) {
2467 tree
, signals
[TREE_DRAG_LEAVE
], 0,
2468 tree
->priv
->drop_row
,
2469 tree
->priv
->drop_path
,
2470 tree
->priv
->drop_col
,
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
;
2481 tree
, signals
[TREE_DRAG_MOTION
], 0,
2482 tree
->priv
->drop_row
,
2483 tree
->priv
->drop_path
,
2484 tree
->priv
->drop_col
,
2494 scroll_timeout (gpointer 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
;
2508 if (tree
->priv
->scroll_direction
& ET_SCROLL_DOWN
)
2510 if (tree
->priv
->scroll_direction
& ET_SCROLL_UP
)
2513 if (tree
->priv
->scroll_direction
& ET_SCROLL_RIGHT
)
2515 if (tree
->priv
->scroll_direction
& ET_SCROLL_LEFT
)
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
)
2545 tree
->priv
->last_drop_context
,
2546 tree
->priv
->last_drop_x
,
2547 tree
->priv
->last_drop_y
,
2548 tree
->priv
->last_drop_time
);
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
);
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;
2577 hover_timeout (gpointer data
)
2580 gint x
= tree
->priv
->hover_x
;
2581 gint y
= tree
->priv
->hover_y
;
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
);
2604 hover_on (ETree
*tree
,
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
);
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;
2626 collapse_drag (ETree
*tree
,
2631 /* We only want to leave open parents of the node dropped in.
2632 * Not the node itself. */
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
;
2641 path
= e_tree_model_get_node_by_id (tree
->priv
->model
, save_id
);
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
) {
2656 e_tree_table_adapter_node_set_expanded (
2657 tree
->priv
->etta
, path
, FALSE
);
2661 g_list_free (tree
->priv
->expanded_list
);
2662 tree
->priv
->expanded_list
= NULL
;
2666 context_destroyed (gpointer data
,
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
);
2679 g_object_unref (tree
);
2683 context_connect (ETree
*tree
,
2684 GdkDragContext
*context
)
2686 if (context
== tree
->priv
->last_drop_context
)
2689 if (tree
->priv
->last_drop_context
)
2690 g_object_weak_unref (
2691 G_OBJECT (tree
->priv
->last_drop_context
),
2692 context_destroyed
, tree
);
2694 g_object_ref (tree
);
2696 g_object_weak_ref (G_OBJECT (context
), context_destroyed
, tree
);
2700 et_drag_leave (GtkWidget
*widget
,
2701 GdkDragContext
*context
,
2707 signals
[TREE_DRAG_LEAVE
], 0,
2708 tree
->priv
->drop_row
,
2709 tree
->priv
->drop_path
,
2710 tree
->priv
->drop_col
,
2713 tree
->priv
->drop_row
= -1;
2714 tree
->priv
->drop_col
= -1;
2721 et_drag_motion (GtkWidget
*widget
,
2722 GdkDragContext
*context
,
2728 GtkAllocation allocation
;
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
);
2744 hover_on (tree
, x
, y
);
2747 ret_val
= do_drag_motion (tree
, context
, x
, y
, time
);
2749 gtk_widget_get_allocation (widget
, &allocation
);
2752 direction
|= ET_SCROLL_UP
;
2753 if (y
> allocation
.height
- 20)
2754 direction
|= ET_SCROLL_DOWN
;
2756 direction
|= ET_SCROLL_LEFT
;
2757 if (x
> allocation
.width
- 20)
2758 direction
|= ET_SCROLL_RIGHT
;
2761 scroll_on (tree
, direction
);
2769 et_drag_drop (GtkWidget
*widget
,
2770 GdkDragContext
*context
,
2776 gboolean ret_val
= FALSE
;
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
) {
2786 tree
, signals
[TREE_DRAG_LEAVE
], 0,
2787 tree
->priv
->drop_row
,
2788 tree
->priv
->drop_path
,
2789 tree
->priv
->drop_col
,
2793 tree
, signals
[TREE_DRAG_MOTION
], 0,
2803 tree
->priv
->drop_row
= row
;
2804 tree
->priv
->drop_path
= path
;
2805 tree
->priv
->drop_col
= col
;
2808 tree
, signals
[TREE_DRAG_DROP
], 0,
2809 tree
->priv
->drop_row
,
2810 tree
->priv
->drop_path
,
2811 tree
->priv
->drop_col
,
2818 tree
->priv
->drop_row
= -1;
2819 tree
->priv
->drop_path
= NULL
;
2820 tree
->priv
->drop_col
= -1;
2822 collapse_drag (tree
, path
);
2829 et_drag_data_received (GtkWidget
*widget
,
2830 GdkDragContext
*context
,
2833 GtkSelectionData
*selection_data
,
2841 e_tree_get_cell_at (tree
, x
, y
, &row
, &col
);
2843 path
= e_tree_table_adapter_node_at_row (tree
->priv
->etta
, row
);
2845 tree
, signals
[TREE_DRAG_DATA_RECEIVED
], 0,
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 (
2880 G_OBJECT_CLASS_TYPE (object_class
),
2882 G_STRUCT_OFFSET (ETreeClass
, cursor_change
),
2884 e_marshal_NONE__INT_POINTER
,
2889 signals
[CURSOR_ACTIVATED
] = g_signal_new (
2891 G_OBJECT_CLASS_TYPE (object_class
),
2893 G_STRUCT_OFFSET (ETreeClass
, cursor_activated
),
2895 e_marshal_NONE__INT_POINTER
,
2900 signals
[SELECTION_CHANGE
] = g_signal_new (
2902 G_OBJECT_CLASS_TYPE (object_class
),
2904 G_STRUCT_OFFSET (ETreeClass
, selection_change
),
2906 g_cclosure_marshal_VOID__VOID
,
2909 signals
[DOUBLE_CLICK
] = g_signal_new (
2911 G_OBJECT_CLASS_TYPE (object_class
),
2913 G_STRUCT_OFFSET (ETreeClass
, double_click
),
2915 e_marshal_NONE__INT_POINTER_INT_BOXED
,
2920 GDK_TYPE_EVENT
| G_SIGNAL_TYPE_STATIC_SCOPE
);
2922 signals
[RIGHT_CLICK
] = g_signal_new (
2924 G_OBJECT_CLASS_TYPE (object_class
),
2926 G_STRUCT_OFFSET (ETreeClass
, right_click
),
2927 g_signal_accumulator_true_handled
, NULL
,
2928 e_marshal_BOOLEAN__INT_POINTER_INT_BOXED
,
2933 GDK_TYPE_EVENT
| G_SIGNAL_TYPE_STATIC_SCOPE
);
2935 signals
[CLICK
] = g_signal_new (
2937 G_OBJECT_CLASS_TYPE (object_class
),
2939 G_STRUCT_OFFSET (ETreeClass
, click
),
2940 g_signal_accumulator_true_handled
, NULL
,
2941 e_marshal_BOOLEAN__INT_POINTER_INT_BOXED
,
2946 GDK_TYPE_EVENT
| G_SIGNAL_TYPE_STATIC_SCOPE
);
2948 signals
[KEY_PRESS
] = g_signal_new (
2950 G_OBJECT_CLASS_TYPE (object_class
),
2952 G_STRUCT_OFFSET (ETreeClass
, key_press
),
2953 g_signal_accumulator_true_handled
, NULL
,
2954 e_marshal_BOOLEAN__INT_POINTER_INT_BOXED
,
2959 GDK_TYPE_EVENT
| G_SIGNAL_TYPE_STATIC_SCOPE
);
2961 signals
[START_DRAG
] = g_signal_new (
2963 G_OBJECT_CLASS_TYPE (object_class
),
2965 G_STRUCT_OFFSET (ETreeClass
, start_drag
),
2967 e_marshal_NONE__INT_POINTER_INT_BOXED
,
2972 GDK_TYPE_EVENT
| G_SIGNAL_TYPE_STATIC_SCOPE
);
2974 signals
[STATE_CHANGE
] = g_signal_new (
2976 G_OBJECT_CLASS_TYPE (object_class
),
2978 G_STRUCT_OFFSET (ETreeClass
, state_change
),
2980 g_cclosure_marshal_VOID__VOID
,
2983 signals
[WHITE_SPACE_EVENT
] = g_signal_new (
2984 "white_space_event",
2985 G_OBJECT_CLASS_TYPE (object_class
),
2987 G_STRUCT_OFFSET (ETreeClass
, white_space_event
),
2988 g_signal_accumulator_true_handled
, NULL
,
2989 e_marshal_BOOLEAN__POINTER
,
2991 GDK_TYPE_EVENT
| G_SIGNAL_TYPE_STATIC_SCOPE
);
2993 signals
[TREE_DRAG_BEGIN
] = g_signal_new (
2995 G_OBJECT_CLASS_TYPE (object_class
),
2997 G_STRUCT_OFFSET (ETreeClass
, tree_drag_begin
),
2999 e_marshal_NONE__INT_POINTER_INT_BOXED
,
3004 GDK_TYPE_DRAG_CONTEXT
);
3006 signals
[TREE_DRAG_END
] = g_signal_new (
3008 G_OBJECT_CLASS_TYPE (object_class
),
3010 G_STRUCT_OFFSET (ETreeClass
, tree_drag_end
),
3012 e_marshal_NONE__INT_POINTER_INT_BOXED
,
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
),
3023 G_STRUCT_OFFSET (ETreeClass
, tree_drag_data_get
),
3025 e_marshal_NONE__INT_POINTER_INT_OBJECT_BOXED_UINT_UINT
,
3030 GDK_TYPE_DRAG_CONTEXT
,
3031 GTK_TYPE_SELECTION_DATA
| G_SIGNAL_TYPE_STATIC_SCOPE
,
3035 signals
[TREE_DRAG_DATA_DELETE
] = g_signal_new (
3036 "tree_drag_data_delete",
3037 G_OBJECT_CLASS_TYPE (object_class
),
3039 G_STRUCT_OFFSET (ETreeClass
, tree_drag_data_delete
),
3041 e_marshal_NONE__INT_POINTER_INT_OBJECT
,
3046 GDK_TYPE_DRAG_CONTEXT
);
3048 signals
[TREE_DRAG_LEAVE
] = g_signal_new (
3050 G_OBJECT_CLASS_TYPE (object_class
),
3052 G_STRUCT_OFFSET (ETreeClass
, tree_drag_leave
),
3054 e_marshal_NONE__INT_POINTER_INT_OBJECT_UINT
,
3059 GDK_TYPE_DRAG_CONTEXT
,
3062 signals
[TREE_DRAG_MOTION
] = g_signal_new (
3064 G_OBJECT_CLASS_TYPE (object_class
),
3066 G_STRUCT_OFFSET (ETreeClass
, tree_drag_motion
),
3068 e_marshal_BOOLEAN__INT_POINTER_INT_OBJECT_INT_INT_UINT
,
3073 GDK_TYPE_DRAG_CONTEXT
,
3078 signals
[TREE_DRAG_DROP
] = g_signal_new (
3080 G_OBJECT_CLASS_TYPE (object_class
),
3082 G_STRUCT_OFFSET (ETreeClass
, tree_drag_drop
),
3084 e_marshal_BOOLEAN__INT_POINTER_INT_OBJECT_INT_INT_UINT
,
3089 GDK_TYPE_DRAG_CONTEXT
,
3094 signals
[TREE_DRAG_DATA_RECEIVED
] = g_signal_new (
3095 "tree_drag_data_received",
3096 G_OBJECT_CLASS_TYPE (object_class
),
3098 G_STRUCT_OFFSET (ETreeClass
, tree_drag_data_received
),
3100 e_marshal_NONE__INT_POINTER_INT_OBJECT_INT_INT_BOXED_UINT_UINT
,
3105 GDK_TYPE_DRAG_CONTEXT
,
3108 GTK_TYPE_SELECTION_DATA
,
3112 g_object_class_install_property (
3114 PROP_LENGTH_THRESHOLD
,
3122 g_object_class_install_property (
3124 PROP_HORIZONTAL_DRAW_GRID
,
3125 g_param_spec_boolean (
3126 "horizontal_draw_grid",
3127 "Horizontal Draw Grid",
3128 "Horizontal Draw Grid",
3132 g_object_class_install_property (
3134 PROP_VERTICAL_DRAW_GRID
,
3135 g_param_spec_boolean (
3136 "vertical_draw_grid",
3137 "Vertical Draw Grid",
3138 "Vertical Draw Grid",
3142 g_object_class_install_property (
3145 g_param_spec_boolean (
3152 g_object_class_install_property (
3155 g_param_spec_object (
3156 "ETreeTableAdapter",
3157 "ETree table adapter",
3158 "ETree table adapter",
3159 E_TYPE_TREE_TABLE_ADAPTER
,
3162 g_object_class_install_property (
3164 PROP_UNIFORM_ROW_HEIGHT
,
3165 g_param_spec_boolean (
3166 "uniform_row_height",
3167 "Uniform row height",
3168 "Uniform row height",
3170 G_PARAM_READWRITE
));
3172 g_object_class_install_property (
3175 g_param_spec_boolean (
3177 "Whether is in an editing mode",
3178 "Whether is in an editing mode",
3182 g_object_class_install_property (
3185 g_param_spec_boolean (
3190 G_PARAM_READWRITE
));
3192 g_object_class_install_property (
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",
3200 G_PARAM_READWRITE
| G_PARAM_CONSTRUCT
));
3202 gtk_widget_class_install_style_property (
3207 "Size of the expander arrow",
3211 gtk_widget_class_install_style_property (
3215 "Vertical Row Spacing",
3216 "Vertical space between rows. "
3217 "It is added to top and to bottom of a row",
3220 G_PARAM_STATIC_STRINGS
));
3222 gtk_widget_class_install_style_property (
3224 g_param_spec_boolean (
3225 "alternating-row-colors",
3226 "Alternating Row Colors",
3227 "Whether to use alternating row colors",
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)
3247 e_tree_scrollable_get_border (GtkScrollable
*scrollable
,
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
)
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
;
3271 e_tree_scrollable_init (GtkScrollableInterface
*iface
)
3273 #if GTK_CHECK_VERSION (3, 15, 9)
3274 iface
->get_border
= e_tree_scrollable_get_border
;
3279 tree_size_allocate (GtkWidget
*widget
,
3280 GtkAllocation
*alloc
,
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
);
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.
3307 e_tree_set_info_message (ETree
*tree
,
3308 const gchar
*info_message
)
3310 GtkAllocation allocation
;
3313 g_return_if_fail (E_IS_TREE (tree
));
3315 if (!tree
->priv
->info_text
&& (!info_message
|| !*info_message
))
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
;
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
)),
3335 "justification", GTK_JUSTIFY_LEFT
,
3336 "text", info_message
,
3337 "width", (gdouble
) allocation
.width
- 60.0,
3338 "clip_width", (gdouble
) allocation
.width
- 60.0,
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
);
3348 gnome_canvas_item_set (tree
->priv
->info_text
, "text", info_message
, NULL
);
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);
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
);
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
));
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))
3393 tree
->priv
->grouped_view
= grouped_view
;
3395 e_tree_update_full_header_grouped_view (tree
);
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
;
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
;
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))
3423 tree
->priv
->sort_children_ascending
= sort_children_ascending
;
3425 g_object_notify (G_OBJECT (tree
), "sort-children-ascending");