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