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