Updated Spanish translation
[evolution.git] / e-util / e-tree-table-adapter.c
blob6fa3974653b210b5a56da0f15454a8a3572de762
1 /*
2 * This program is free software; you can redistribute it and/or modify it
3 * under the terms of the GNU Lesser General Public License as published by
4 * the Free Software Foundation.
6 * This program is distributed in the hope that it will be useful, but
7 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
8 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
9 * for more details.
11 * You should have received a copy of the GNU Lesser General Public License
12 * along with this program; if not, see <http://www.gnu.org/licenses/>.
15 * Authors:
16 * Chris Lahey <clahey@ximian.com>
17 * Chris Toshok <toshok@ximian.com>
19 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
23 #include "e-tree-table-adapter.h"
25 #include <stdlib.h>
26 #include <string.h>
28 #include <glib/gstdio.h>
30 #include <libxml/tree.h>
31 #include <libxml/parser.h>
33 #include <libedataserver/libedataserver.h>
35 #include "e-marshal.h"
36 #include "e-table-sorting-utils.h"
37 #include "e-xml-utils.h"
39 #define E_TREE_TABLE_ADAPTER_GET_PRIVATE(obj) \
40 (G_TYPE_INSTANCE_GET_PRIVATE \
41 ((obj), E_TYPE_TREE_TABLE_ADAPTER, ETreeTableAdapterPrivate))
43 #define d(x)
45 #define INCREMENT_AMOUNT 100
47 typedef struct {
48 ETreePath path;
49 guint32 num_visible_children;
50 guint32 index;
52 guint expanded : 1;
53 guint expandable : 1;
54 guint expandable_set : 1;
55 } node_t;
57 struct _ETreeTableAdapterPrivate {
58 ETreeModel *source_model;
59 gulong pre_change_handler_id;
60 gulong rebuilt_handler_id;
61 gulong node_changed_handler_id;
62 gulong node_data_changed_handler_id;
63 gulong node_inserted_handler_id;
64 gulong node_removed_handler_id;
66 ETableSortInfo *sort_info;
67 gulong sort_info_changed_handler_id;
68 ETableSortInfo *children_sort_info;
69 gboolean sort_children_ascending;
71 ETableHeader *header;
73 gint n_map;
74 gint n_vals_allocated;
75 node_t **map_table;
76 GHashTable *nodes;
77 GNode *root;
79 guint root_visible : 1;
80 guint remap_needed : 1;
82 gint last_access;
84 guint resort_idle_id;
86 gint force_expanded_state; /* use this instead of model's default if not 0; <0 ... collapse, >0 ... expand */
89 enum {
90 PROP_0,
91 PROP_HEADER,
92 PROP_SORT_INFO,
93 PROP_SOURCE_MODEL,
94 PROP_SORT_CHILDREN_ASCENDING
97 enum {
98 SORTING_CHANGED,
99 LAST_SIGNAL
102 /* Forward Declarations */
103 static void e_tree_table_adapter_table_model_init
104 (ETableModelInterface *iface);
106 static guint signals[LAST_SIGNAL];
108 G_DEFINE_TYPE_WITH_CODE (
109 ETreeTableAdapter,
110 e_tree_table_adapter,
111 G_TYPE_OBJECT,
112 G_IMPLEMENT_INTERFACE (
113 E_TYPE_TABLE_MODEL,
114 e_tree_table_adapter_table_model_init))
116 static GNode *
117 lookup_gnode (ETreeTableAdapter *etta,
118 ETreePath path)
120 GNode *gnode;
122 if (!path)
123 return NULL;
125 gnode = g_hash_table_lookup (etta->priv->nodes, path);
127 return gnode;
130 static void
131 resize_map (ETreeTableAdapter *etta,
132 gint size)
134 if (size > etta->priv->n_vals_allocated) {
135 etta->priv->n_vals_allocated = MAX (etta->priv->n_vals_allocated + INCREMENT_AMOUNT, size);
136 etta->priv->map_table = g_renew (node_t *, etta->priv->map_table, etta->priv->n_vals_allocated);
139 etta->priv->n_map = size;
142 static void
143 move_map_elements (ETreeTableAdapter *etta,
144 gint to,
145 gint from,
146 gint count)
148 if (count <= 0 || from >= etta->priv->n_map)
149 return;
150 memmove (etta->priv->map_table + to, etta->priv->map_table + from, count * sizeof (node_t *));
151 etta->priv->remap_needed = TRUE;
154 static gint
155 fill_map (ETreeTableAdapter *etta,
156 gint index,
157 GNode *gnode)
159 GNode *p;
161 if ((gnode != etta->priv->root) || etta->priv->root_visible)
162 etta->priv->map_table[index++] = gnode->data;
164 for (p = gnode->children; p; p = p->next)
165 index = fill_map (etta, index, p);
167 etta->priv->remap_needed = TRUE;
168 return index;
171 static void
172 remap_indices (ETreeTableAdapter *etta)
174 gint i;
175 for (i = 0; i < etta->priv->n_map; i++)
176 etta->priv->map_table[i]->index = i;
177 etta->priv->remap_needed = FALSE;
180 static node_t *
181 get_node (ETreeTableAdapter *etta,
182 ETreePath path)
184 GNode *gnode = lookup_gnode (etta, path);
186 if (!gnode)
187 return NULL;
189 return (node_t *) gnode->data;
192 static void
193 resort_node (ETreeTableAdapter *etta,
194 GNode *gnode,
195 gboolean recurse)
197 node_t *node = (node_t *) gnode->data;
198 ETreePath *paths, path;
199 GNode *prev, *curr;
200 gint i, count;
201 gboolean sort_needed;
203 g_return_if_fail (node != NULL);
205 if (node->num_visible_children == 0)
206 return;
208 sort_needed = etta->priv->sort_info && e_table_sort_info_sorting_get_count (etta->priv->sort_info) > 0;
210 for (i = 0, path = e_tree_model_node_get_first_child (etta->priv->source_model, node->path); path;
211 path = e_tree_model_node_get_next (etta->priv->source_model, path), i++);
213 count = i;
214 if (count <= 1)
215 return;
217 paths = g_new0 (ETreePath, count);
219 for (i = 0, path = e_tree_model_node_get_first_child (etta->priv->source_model, node->path); path;
220 path = e_tree_model_node_get_next (etta->priv->source_model, path), i++)
221 paths[i] = path;
223 if (count > 1 && sort_needed) {
224 ETableSortInfo *use_sort_info;
226 use_sort_info = etta->priv->sort_info;
228 if (etta->priv->sort_children_ascending && gnode->parent) {
229 if (!etta->priv->children_sort_info) {
230 gint len;
232 etta->priv->children_sort_info = e_table_sort_info_duplicate (etta->priv->sort_info);
234 len = e_table_sort_info_sorting_get_count (etta->priv->children_sort_info);
236 for (i = 0; i < len; i++) {
237 ETableColumnSpecification *spec;
238 GtkSortType sort_type;
240 spec = e_table_sort_info_sorting_get_nth (etta->priv->children_sort_info, i, &sort_type);
241 if (spec) {
242 if (sort_type == GTK_SORT_DESCENDING)
243 e_table_sort_info_sorting_set_nth (etta->priv->children_sort_info, i, spec, GTK_SORT_ASCENDING);
248 use_sort_info = etta->priv->children_sort_info;
251 e_table_sorting_utils_tree_sort (etta->priv->source_model, use_sort_info, etta->priv->header, paths, count);
254 prev = NULL;
255 for (i = 0; i < count; i++) {
256 curr = lookup_gnode (etta, paths[i]);
257 if (!curr)
258 continue;
260 if (prev)
261 prev->next = curr;
262 else
263 gnode->children = curr;
265 curr->prev = prev;
266 curr->next = NULL;
267 prev = curr;
268 if (recurse)
269 resort_node (etta, curr, recurse);
272 g_free (paths);
275 static void
276 kill_gnode (GNode *node,
277 ETreeTableAdapter *etta)
279 g_hash_table_remove (etta->priv->nodes, ((node_t *) node->data)->path);
281 while (node->children) {
282 GNode *next = node->children->next;
283 kill_gnode (node->children, etta);
284 node->children = next;
287 g_free (node->data);
288 if (node == etta->priv->root)
289 etta->priv->root = NULL;
290 g_node_destroy (node);
293 static void
294 update_child_counts (GNode *gnode,
295 gint delta)
297 while (gnode) {
298 node_t *node = (node_t *) gnode->data;
299 node->num_visible_children += delta;
300 gnode = gnode->parent;
304 static gint
305 delete_children (ETreeTableAdapter *etta,
306 GNode *gnode)
308 node_t *node = (node_t *) gnode->data;
309 gint to_remove = node ? node->num_visible_children : 0;
311 if (to_remove == 0)
312 return 0;
314 while (gnode->children) {
315 GNode *next = gnode->children->next;
316 kill_gnode (gnode->children, etta);
317 gnode->children = next;
320 return to_remove;
323 static void
324 delete_node (ETreeTableAdapter *etta,
325 ETreePath parent,
326 ETreePath path)
328 gint to_remove = 1;
329 gint parent_row = e_tree_table_adapter_row_of_node (etta, parent);
330 gint row = e_tree_table_adapter_row_of_node (etta, path);
331 GNode *gnode = lookup_gnode (etta, path);
332 GNode *parent_gnode = lookup_gnode (etta, parent);
334 e_table_model_pre_change (E_TABLE_MODEL (etta));
336 if (row == -1) {
337 e_table_model_no_change (E_TABLE_MODEL (etta));
338 return;
341 to_remove += delete_children (etta, gnode);
342 kill_gnode (gnode, etta);
344 move_map_elements (etta, row, row + to_remove, etta->priv->n_map - row - to_remove);
345 resize_map (etta, etta->priv->n_map - to_remove);
347 if (parent_gnode != NULL) {
348 node_t *parent_node = parent_gnode->data;
349 gboolean expandable = e_tree_model_node_is_expandable (etta->priv->source_model, parent);
351 update_child_counts (parent_gnode, - to_remove);
352 if (parent_node->expandable != expandable) {
353 e_table_model_pre_change (E_TABLE_MODEL (etta));
354 parent_node->expandable = expandable;
355 e_table_model_row_changed (E_TABLE_MODEL (etta), parent_row);
358 resort_node (etta, parent_gnode, FALSE);
361 e_table_model_rows_deleted (E_TABLE_MODEL (etta), row, to_remove);
364 static GNode *
365 create_gnode (ETreeTableAdapter *etta,
366 ETreePath path)
368 GNode *gnode;
369 node_t *node;
371 node = g_new0 (node_t, 1);
372 node->path = path;
373 node->index = -1;
374 node->expanded = etta->priv->force_expanded_state == 0 ? e_tree_model_get_expanded_default (etta->priv->source_model) : etta->priv->force_expanded_state > 0;
375 node->expandable = e_tree_model_node_is_expandable (etta->priv->source_model, path);
376 node->expandable_set = 1;
377 node->num_visible_children = 0;
378 gnode = g_node_new (node);
379 g_hash_table_insert (etta->priv->nodes, path, gnode);
380 return gnode;
383 static gint
384 insert_children (ETreeTableAdapter *etta,
385 GNode *gnode)
387 ETreePath path, tmp;
388 gint count = 0;
389 gint pos = 0;
391 path = ((node_t *) gnode->data)->path;
392 for (tmp = e_tree_model_node_get_first_child (etta->priv->source_model, path);
393 tmp;
394 tmp = e_tree_model_node_get_next (etta->priv->source_model, tmp), pos++) {
395 GNode *child = create_gnode (etta, tmp);
396 node_t *node = (node_t *) child->data;
397 if (node->expanded)
398 node->num_visible_children = insert_children (etta, child);
399 g_node_prepend (gnode, child);
400 count += node->num_visible_children + 1;
402 g_node_reverse_children (gnode);
403 return count;
406 static void
407 generate_tree (ETreeTableAdapter *etta,
408 ETreePath path)
410 GNode *gnode;
411 node_t *node;
412 gint size;
414 e_table_model_pre_change (E_TABLE_MODEL (etta));
416 g_return_if_fail (e_tree_model_node_is_root (etta->priv->source_model, path));
418 if (etta->priv->root)
419 kill_gnode (etta->priv->root, etta);
420 resize_map (etta, 0);
422 gnode = create_gnode (etta, path);
423 node = (node_t *) gnode->data;
424 node->expanded = TRUE;
425 node->num_visible_children = insert_children (etta, gnode);
426 if (etta->priv->sort_info && e_table_sort_info_sorting_get_count (etta->priv->sort_info) > 0)
427 resort_node (etta, gnode, TRUE);
429 etta->priv->root = gnode;
430 size = etta->priv->root_visible ? node->num_visible_children + 1 : node->num_visible_children;
431 resize_map (etta, size);
432 fill_map (etta, 0, gnode);
433 e_table_model_changed (E_TABLE_MODEL (etta));
436 static void
437 insert_node (ETreeTableAdapter *etta,
438 ETreePath parent,
439 ETreePath path)
441 GNode *gnode, *parent_gnode;
442 node_t *node, *parent_node;
443 gboolean expandable;
444 gint size, row;
446 e_table_model_pre_change (E_TABLE_MODEL (etta));
448 if (get_node (etta, path)) {
449 e_table_model_no_change (E_TABLE_MODEL (etta));
450 return;
453 parent_gnode = lookup_gnode (etta, parent);
454 if (!parent_gnode) {
455 ETreePath grandparent = e_tree_model_node_get_parent (etta->priv->source_model, parent);
456 if (e_tree_model_node_is_root (etta->priv->source_model, parent))
457 generate_tree (etta, parent);
458 else
459 insert_node (etta, grandparent, parent);
460 e_table_model_changed (E_TABLE_MODEL (etta));
461 return;
464 parent_node = (node_t *) parent_gnode->data;
466 if (parent_gnode != etta->priv->root) {
467 expandable = e_tree_model_node_is_expandable (etta->priv->source_model, parent);
468 if (parent_node->expandable != expandable) {
469 e_table_model_pre_change (E_TABLE_MODEL (etta));
470 parent_node->expandable = expandable;
471 parent_node->expandable_set = 1;
472 e_table_model_row_changed (E_TABLE_MODEL (etta), parent_node->index);
476 if (!e_tree_table_adapter_node_is_expanded (etta, parent)) {
477 e_table_model_no_change (E_TABLE_MODEL (etta));
478 return;
481 gnode = create_gnode (etta, path);
482 node = (node_t *) gnode->data;
484 if (node->expanded)
485 node->num_visible_children = insert_children (etta, gnode);
487 g_node_append (parent_gnode, gnode);
488 update_child_counts (parent_gnode, node->num_visible_children + 1);
489 resort_node (etta, parent_gnode, FALSE);
490 resort_node (etta, gnode, TRUE);
492 size = node->num_visible_children + 1;
493 resize_map (etta, etta->priv->n_map + size);
494 if (parent_gnode == etta->priv->root)
495 row = 0;
496 else {
497 gint new_size = parent_node->num_visible_children + 1;
498 gint old_size = new_size - size;
499 row = parent_node->index;
500 move_map_elements (etta, row + new_size, row + old_size, etta->priv->n_map - row - new_size);
502 fill_map (etta, row, parent_gnode);
503 e_table_model_rows_inserted (
504 E_TABLE_MODEL (etta),
505 e_tree_table_adapter_row_of_node (etta, path), size);
508 typedef struct {
509 GSList *paths;
510 gboolean expanded;
511 } check_expanded_closure;
513 static gboolean
514 check_expanded (GNode *gnode,
515 gpointer data)
517 check_expanded_closure *closure = (check_expanded_closure *) data;
518 node_t *node = (node_t *) gnode->data;
520 if (node->expanded != closure->expanded)
521 closure->paths = g_slist_prepend (closure->paths, node->path);
523 return FALSE;
526 static void
527 update_node (ETreeTableAdapter *etta,
528 ETreePath path)
530 check_expanded_closure closure;
531 ETreePath parent = e_tree_model_node_get_parent (etta->priv->source_model, path);
532 GNode *gnode = lookup_gnode (etta, path);
533 GSList *l;
535 closure.expanded = e_tree_model_get_expanded_default (etta->priv->source_model);
536 closure.paths = NULL;
538 if (gnode)
539 g_node_traverse (gnode, G_POST_ORDER, G_TRAVERSE_ALL, -1, check_expanded, &closure);
541 if (e_tree_model_node_is_root (etta->priv->source_model, path))
542 generate_tree (etta, path);
543 else {
544 delete_node (etta, parent, path);
545 insert_node (etta, parent, path);
548 for (l = closure.paths; l; l = l->next)
549 if (lookup_gnode (etta, l->data))
550 e_tree_table_adapter_node_set_expanded (etta, l->data, !closure.expanded);
552 g_slist_free (closure.paths);
555 static void
556 tree_table_adapter_sort_info_changed_cb (ETableSortInfo *sort_info,
557 ETreeTableAdapter *etta)
559 g_clear_object (&etta->priv->children_sort_info);
561 if (!etta->priv->root)
562 return;
564 /* the function is called also internally, with sort_info = NULL,
565 * thus skip those in signal emit */
566 if (sort_info) {
567 gboolean handled = FALSE;
569 g_signal_emit (etta, signals[SORTING_CHANGED], 0, &handled);
571 if (handled)
572 return;
575 e_table_model_pre_change (E_TABLE_MODEL (etta));
576 resort_node (etta, etta->priv->root, TRUE);
577 fill_map (etta, 0, etta->priv->root);
578 e_table_model_changed (E_TABLE_MODEL (etta));
581 static void
582 tree_table_adapter_source_model_pre_change_cb (ETreeModel *source_model,
583 ETreeTableAdapter *etta)
585 e_table_model_pre_change (E_TABLE_MODEL (etta));
588 static void
589 tree_table_adapter_source_model_rebuilt_cb (ETreeModel *source_model,
590 ETreeTableAdapter *etta)
592 if (!etta->priv->root)
593 return;
595 kill_gnode (etta->priv->root, etta);
596 etta->priv->root = NULL;
598 g_hash_table_remove_all (etta->priv->nodes);
601 static gboolean
602 tree_table_adapter_resort_model_idle_cb (gpointer user_data)
604 ETreeTableAdapter *etta;
606 etta = E_TREE_TABLE_ADAPTER (user_data);
607 tree_table_adapter_sort_info_changed_cb (NULL, etta);
608 etta->priv->resort_idle_id = 0;
610 return FALSE;
613 static void
614 tree_table_adapter_source_model_node_changed_cb (ETreeModel *source_model,
615 ETreePath path,
616 ETreeTableAdapter *etta)
618 update_node (etta, path);
619 e_table_model_changed (E_TABLE_MODEL (etta));
621 /* FIXME: Really it shouldnt be required. But a lot of thread
622 * which were supposed to be present in the list is way below
624 if (etta->priv->resort_idle_id == 0)
625 etta->priv->resort_idle_id = g_idle_add (
626 tree_table_adapter_resort_model_idle_cb, etta);
629 static void
630 tree_table_adapter_source_model_node_data_changed_cb (ETreeModel *source_model,
631 ETreePath path,
632 ETreeTableAdapter *etta)
634 gint row = e_tree_table_adapter_row_of_node (etta, path);
636 if (row == -1) {
637 e_table_model_no_change (E_TABLE_MODEL (etta));
638 return;
641 e_table_model_row_changed (E_TABLE_MODEL (etta), row);
644 static void
645 tree_table_adapter_source_model_node_inserted_cb (ETreeModel *etm,
646 ETreePath parent,
647 ETreePath child,
648 ETreeTableAdapter *etta)
650 if (e_tree_model_node_is_root (etm, child))
651 generate_tree (etta, child);
652 else
653 insert_node (etta, parent, child);
655 e_table_model_changed (E_TABLE_MODEL (etta));
658 static void
659 tree_table_adapter_source_model_node_removed_cb (ETreeModel *etm,
660 ETreePath parent,
661 ETreePath child,
662 gint old_position,
663 ETreeTableAdapter *etta)
665 delete_node (etta, parent, child);
666 e_table_model_changed (E_TABLE_MODEL (etta));
669 static void
670 tree_table_adapter_set_header (ETreeTableAdapter *etta,
671 ETableHeader *header)
673 if (header == NULL)
674 return;
676 g_return_if_fail (E_IS_TABLE_HEADER (header));
677 g_return_if_fail (etta->priv->header == NULL);
679 etta->priv->header = g_object_ref (header);
682 static void
683 tree_table_adapter_set_source_model (ETreeTableAdapter *etta,
684 ETreeModel *source_model)
686 g_return_if_fail (E_IS_TREE_MODEL (source_model));
687 g_return_if_fail (etta->priv->source_model == NULL);
689 etta->priv->source_model = g_object_ref (source_model);
692 static void
693 tree_table_adapter_set_property (GObject *object,
694 guint property_id,
695 const GValue *value,
696 GParamSpec *pspec)
698 switch (property_id) {
699 case PROP_HEADER:
700 tree_table_adapter_set_header (
701 E_TREE_TABLE_ADAPTER (object),
702 g_value_get_object (value));
703 return;
705 case PROP_SORT_INFO:
706 e_tree_table_adapter_set_sort_info (
707 E_TREE_TABLE_ADAPTER (object),
708 g_value_get_object (value));
709 return;
711 case PROP_SOURCE_MODEL:
712 tree_table_adapter_set_source_model (
713 E_TREE_TABLE_ADAPTER (object),
714 g_value_get_object (value));
715 return;
717 case PROP_SORT_CHILDREN_ASCENDING:
718 e_tree_table_adapter_set_sort_children_ascending (
719 E_TREE_TABLE_ADAPTER (object),
720 g_value_get_boolean (value));
721 return;
724 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
727 static void
728 tree_table_adapter_get_property (GObject *object,
729 guint property_id,
730 GValue *value,
731 GParamSpec *pspec)
733 switch (property_id) {
734 case PROP_HEADER:
735 g_value_set_object (
736 value,
737 e_tree_table_adapter_get_header (
738 E_TREE_TABLE_ADAPTER (object)));
739 return;
741 case PROP_SORT_INFO:
742 g_value_set_object (
743 value,
744 e_tree_table_adapter_get_sort_info (
745 E_TREE_TABLE_ADAPTER (object)));
746 return;
748 case PROP_SOURCE_MODEL:
749 g_value_set_object (
750 value,
751 e_tree_table_adapter_get_source_model (
752 E_TREE_TABLE_ADAPTER (object)));
753 return;
755 case PROP_SORT_CHILDREN_ASCENDING:
756 g_value_set_boolean (
757 value,
758 e_tree_table_adapter_get_sort_children_ascending (
759 E_TREE_TABLE_ADAPTER (object)));
760 return;
763 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
766 static void
767 tree_table_adapter_dispose (GObject *object)
769 ETreeTableAdapterPrivate *priv;
771 priv = E_TREE_TABLE_ADAPTER_GET_PRIVATE (object);
773 if (priv->pre_change_handler_id > 0) {
774 g_signal_handler_disconnect (
775 priv->source_model,
776 priv->pre_change_handler_id);
777 priv->pre_change_handler_id = 0;
780 if (priv->rebuilt_handler_id > 0) {
781 g_signal_handler_disconnect (
782 priv->source_model,
783 priv->rebuilt_handler_id);
784 priv->rebuilt_handler_id = 0;
787 if (priv->node_changed_handler_id > 0) {
788 g_signal_handler_disconnect (
789 priv->source_model,
790 priv->node_changed_handler_id);
791 priv->node_changed_handler_id = 0;
794 if (priv->node_data_changed_handler_id > 0) {
795 g_signal_handler_disconnect (
796 priv->source_model,
797 priv->node_data_changed_handler_id);
798 priv->node_data_changed_handler_id = 0;
801 if (priv->node_inserted_handler_id > 0) {
802 g_signal_handler_disconnect (
803 priv->source_model,
804 priv->node_inserted_handler_id);
805 priv->node_inserted_handler_id = 0;
808 if (priv->node_removed_handler_id > 0) {
809 g_signal_handler_disconnect (
810 priv->source_model,
811 priv->node_removed_handler_id);
812 priv->node_removed_handler_id = 0;
815 if (priv->sort_info_changed_handler_id > 0) {
816 g_signal_handler_disconnect (
817 priv->sort_info,
818 priv->sort_info_changed_handler_id);
819 priv->sort_info_changed_handler_id = 0;
822 g_clear_object (&priv->source_model);
823 g_clear_object (&priv->sort_info);
824 g_clear_object (&priv->children_sort_info);
825 g_clear_object (&priv->header);
827 /* Chain up to parent's dispose() method. */
828 G_OBJECT_CLASS (e_tree_table_adapter_parent_class)->dispose (object);
831 static void
832 tree_table_adapter_finalize (GObject *object)
834 ETreeTableAdapterPrivate *priv;
836 priv = E_TREE_TABLE_ADAPTER_GET_PRIVATE (object);
838 if (priv->resort_idle_id) {
839 g_source_remove (priv->resort_idle_id);
840 priv->resort_idle_id = 0;
843 if (priv->root) {
844 kill_gnode (priv->root, E_TREE_TABLE_ADAPTER (object));
845 priv->root = NULL;
848 g_hash_table_destroy (priv->nodes);
850 g_free (priv->map_table);
852 /* Chain up to parent's finalize() method. */
853 G_OBJECT_CLASS (e_tree_table_adapter_parent_class)->finalize (object);
856 static void
857 tree_table_adapter_constructed (GObject *object)
859 ETreeTableAdapter *etta;
860 ETreeModel *source_model;
861 ETreePath root;
862 gulong handler_id;
864 etta = E_TREE_TABLE_ADAPTER (object);
866 /* Chain up to parent's constructed() method. */
867 G_OBJECT_CLASS (e_tree_table_adapter_parent_class)->constructed (object);
869 source_model = e_tree_table_adapter_get_source_model (etta);
871 root = e_tree_model_get_root (source_model);
872 if (root != NULL)
873 generate_tree (etta, root);
875 handler_id = g_signal_connect (
876 source_model, "pre_change",
877 G_CALLBACK (tree_table_adapter_source_model_pre_change_cb),
878 etta);
879 etta->priv->pre_change_handler_id = handler_id;
881 handler_id = g_signal_connect (
882 source_model, "rebuilt",
883 G_CALLBACK (tree_table_adapter_source_model_rebuilt_cb),
884 etta);
885 etta->priv->rebuilt_handler_id = handler_id;
887 handler_id = g_signal_connect (
888 source_model, "node_changed",
889 G_CALLBACK (tree_table_adapter_source_model_node_changed_cb),
890 etta);
891 etta->priv->node_changed_handler_id = handler_id;
893 handler_id = g_signal_connect (
894 source_model, "node_data_changed",
895 G_CALLBACK (tree_table_adapter_source_model_node_data_changed_cb),
896 etta);
897 etta->priv->node_data_changed_handler_id = handler_id;
899 handler_id = g_signal_connect (
900 source_model, "node_inserted",
901 G_CALLBACK (tree_table_adapter_source_model_node_inserted_cb),
902 etta);
903 etta->priv->node_inserted_handler_id = handler_id;
905 handler_id = g_signal_connect (
906 source_model, "node_removed",
907 G_CALLBACK (tree_table_adapter_source_model_node_removed_cb),
908 etta);
909 etta->priv->node_removed_handler_id = handler_id;
912 static gint
913 tree_table_adapter_column_count (ETableModel *etm)
915 ETreeTableAdapter *etta = (ETreeTableAdapter *) etm;
917 return e_tree_model_column_count (etta->priv->source_model);
920 static gboolean
921 tree_table_adapter_has_save_id (ETableModel *etm)
923 return TRUE;
926 static gchar *
927 tree_table_adapter_get_save_id (ETableModel *etm,
928 gint row)
930 ETreeTableAdapter *etta = (ETreeTableAdapter *) etm;
932 return e_tree_model_get_save_id (
933 etta->priv->source_model,
934 e_tree_table_adapter_node_at_row (etta, row));
937 static gint
938 tree_table_adapter_row_count (ETableModel *etm)
940 ETreeTableAdapter *etta = (ETreeTableAdapter *) etm;
942 return etta->priv->n_map;
945 static gpointer
946 tree_table_adapter_value_at (ETableModel *etm,
947 gint col,
948 gint row)
950 ETreeTableAdapter *etta = (ETreeTableAdapter *) etm;
952 switch (col) {
953 case -1:
954 if (row == -1)
955 return NULL;
956 return e_tree_table_adapter_node_at_row (etta, row);
957 case -2:
958 return etta->priv->source_model;
959 case -3:
960 return etta;
961 default:
962 return e_tree_model_value_at (
963 etta->priv->source_model,
964 e_tree_table_adapter_node_at_row (etta, row), col);
968 static void
969 tree_table_adapter_set_value_at (ETableModel *etm,
970 gint col,
971 gint row,
972 gconstpointer val)
974 g_warn_if_reached ();
977 static gboolean
978 tree_table_adapter_is_cell_editable (ETableModel *etm,
979 gint col,
980 gint row)
982 return FALSE;
985 static void
986 tree_table_adapter_append_row (ETableModel *etm,
987 ETableModel *source,
988 gint row)
992 static gpointer
993 tree_table_adapter_duplicate_value (ETableModel *etm,
994 gint col,
995 gconstpointer value)
997 ETreeTableAdapter *etta = (ETreeTableAdapter *) etm;
999 return e_tree_model_duplicate_value (etta->priv->source_model, col, value);
1002 static void
1003 tree_table_adapter_free_value (ETableModel *etm,
1004 gint col,
1005 gpointer value)
1007 ETreeTableAdapter *etta = (ETreeTableAdapter *) etm;
1009 e_tree_model_free_value (etta->priv->source_model, col, value);
1012 static gpointer
1013 tree_table_adapter_initialize_value (ETableModel *etm,
1014 gint col)
1016 ETreeTableAdapter *etta = (ETreeTableAdapter *) etm;
1018 return e_tree_model_initialize_value (etta->priv->source_model, col);
1021 static gboolean
1022 tree_table_adapter_value_is_empty (ETableModel *etm,
1023 gint col,
1024 gconstpointer value)
1026 ETreeTableAdapter *etta = (ETreeTableAdapter *) etm;
1028 return e_tree_model_value_is_empty (etta->priv->source_model, col, value);
1031 static gchar *
1032 tree_table_adapter_value_to_string (ETableModel *etm,
1033 gint col,
1034 gconstpointer value)
1036 ETreeTableAdapter *etta = (ETreeTableAdapter *) etm;
1038 return e_tree_model_value_to_string (etta->priv->source_model, col, value);
1041 static void
1042 e_tree_table_adapter_class_init (ETreeTableAdapterClass *class)
1044 GObjectClass *object_class;
1046 g_type_class_add_private (class, sizeof (ETreeTableAdapterPrivate));
1048 object_class = G_OBJECT_CLASS (class);
1049 object_class->set_property = tree_table_adapter_set_property;
1050 object_class->get_property = tree_table_adapter_get_property;
1051 object_class->dispose = tree_table_adapter_dispose;
1052 object_class->finalize = tree_table_adapter_finalize;
1053 object_class->constructed = tree_table_adapter_constructed;
1055 g_object_class_install_property (
1056 object_class,
1057 PROP_HEADER,
1058 g_param_spec_object (
1059 "header",
1060 "Header",
1061 NULL,
1062 E_TYPE_TABLE_HEADER,
1063 G_PARAM_READWRITE |
1064 G_PARAM_CONSTRUCT_ONLY |
1065 G_PARAM_STATIC_STRINGS));
1067 g_object_class_install_property (
1068 object_class,
1069 PROP_SORT_INFO,
1070 g_param_spec_object (
1071 "sort-info",
1072 "Sort Info",
1073 NULL,
1074 E_TYPE_TABLE_SORT_INFO,
1075 G_PARAM_READWRITE |
1076 G_PARAM_CONSTRUCT |
1077 G_PARAM_STATIC_STRINGS));
1079 g_object_class_install_property (
1080 object_class,
1081 PROP_SOURCE_MODEL,
1082 g_param_spec_object (
1083 "source-model",
1084 "Source Model",
1085 NULL,
1086 E_TYPE_TREE_MODEL,
1087 G_PARAM_READWRITE |
1088 G_PARAM_CONSTRUCT_ONLY |
1089 G_PARAM_STATIC_STRINGS));
1091 g_object_class_install_property (
1092 object_class,
1093 PROP_SORT_CHILDREN_ASCENDING,
1094 g_param_spec_boolean (
1095 "sort-children-ascending",
1096 "Sort Children Ascending",
1097 NULL,
1098 FALSE,
1099 G_PARAM_READWRITE |
1100 G_PARAM_CONSTRUCT |
1101 G_PARAM_STATIC_STRINGS));
1103 signals[SORTING_CHANGED] = g_signal_new (
1104 "sorting_changed",
1105 G_OBJECT_CLASS_TYPE (object_class),
1106 G_SIGNAL_RUN_LAST,
1107 G_STRUCT_OFFSET (ETreeTableAdapterClass, sorting_changed),
1108 NULL, NULL,
1109 e_marshal_BOOLEAN__NONE,
1110 G_TYPE_BOOLEAN, 0,
1111 G_TYPE_NONE);
1114 static void
1115 e_tree_table_adapter_table_model_init (ETableModelInterface *iface)
1117 iface->column_count = tree_table_adapter_column_count;
1118 iface->row_count = tree_table_adapter_row_count;
1119 iface->append_row = tree_table_adapter_append_row;
1121 iface->value_at = tree_table_adapter_value_at;
1122 iface->set_value_at = tree_table_adapter_set_value_at;
1123 iface->is_cell_editable = tree_table_adapter_is_cell_editable;
1125 iface->has_save_id = tree_table_adapter_has_save_id;
1126 iface->get_save_id = tree_table_adapter_get_save_id;
1128 iface->duplicate_value = tree_table_adapter_duplicate_value;
1129 iface->free_value = tree_table_adapter_free_value;
1130 iface->initialize_value = tree_table_adapter_initialize_value;
1131 iface->value_is_empty = tree_table_adapter_value_is_empty;
1132 iface->value_to_string = tree_table_adapter_value_to_string;
1135 static void
1136 e_tree_table_adapter_init (ETreeTableAdapter *etta)
1138 etta->priv = E_TREE_TABLE_ADAPTER_GET_PRIVATE (etta);
1140 etta->priv->nodes = g_hash_table_new (NULL, NULL);
1142 etta->priv->root_visible = TRUE;
1143 etta->priv->remap_needed = TRUE;
1146 ETableModel *
1147 e_tree_table_adapter_new (ETreeModel *source_model,
1148 ETableSortInfo *sort_info,
1149 ETableHeader *header)
1151 g_return_val_if_fail (E_IS_TREE_MODEL (source_model), NULL);
1153 if (sort_info != NULL)
1154 g_return_val_if_fail (E_IS_TABLE_SORT_INFO (sort_info), NULL);
1156 if (header != NULL)
1157 g_return_val_if_fail (E_IS_TABLE_HEADER (header), NULL);
1159 return g_object_new (
1160 E_TYPE_TREE_TABLE_ADAPTER,
1161 "source-model", source_model,
1162 "sort-info", sort_info,
1163 "header", header,
1164 NULL);
1167 ETableHeader *
1168 e_tree_table_adapter_get_header (ETreeTableAdapter *etta)
1170 g_return_val_if_fail (E_IS_TREE_TABLE_ADAPTER (etta), NULL);
1172 return etta->priv->header;
1175 ETableSortInfo *
1176 e_tree_table_adapter_get_sort_info (ETreeTableAdapter *etta)
1178 g_return_val_if_fail (E_IS_TREE_TABLE_ADAPTER (etta), NULL);
1180 return etta->priv->sort_info;
1183 void
1184 e_tree_table_adapter_set_sort_info (ETreeTableAdapter *etta,
1185 ETableSortInfo *sort_info)
1187 g_return_if_fail (E_IS_TREE_TABLE_ADAPTER (etta));
1189 if (sort_info != NULL) {
1190 g_return_if_fail (E_IS_TABLE_SORT_INFO (sort_info));
1191 g_object_ref (sort_info);
1194 if (etta->priv->sort_info != NULL) {
1195 g_signal_handler_disconnect (
1196 etta->priv->sort_info,
1197 etta->priv->sort_info_changed_handler_id);
1198 etta->priv->sort_info_changed_handler_id = 0;
1200 g_clear_object (&etta->priv->sort_info);
1203 etta->priv->sort_info = sort_info;
1205 if (etta->priv->sort_info != NULL) {
1206 gulong handler_id;
1208 handler_id = g_signal_connect (
1209 etta->priv->sort_info, "sort_info_changed",
1210 G_CALLBACK (tree_table_adapter_sort_info_changed_cb),
1211 etta);
1212 etta->priv->sort_info_changed_handler_id = handler_id;
1215 g_clear_object (&etta->priv->children_sort_info);
1217 g_object_notify (G_OBJECT (etta), "sort-info");
1219 if (etta->priv->root == NULL)
1220 return;
1222 e_table_model_pre_change (E_TABLE_MODEL (etta));
1223 resort_node (etta, etta->priv->root, TRUE);
1224 fill_map (etta, 0, etta->priv->root);
1225 e_table_model_changed (E_TABLE_MODEL (etta));
1228 gboolean
1229 e_tree_table_adapter_get_sort_children_ascending (ETreeTableAdapter *etta)
1231 g_return_val_if_fail (E_IS_TREE_TABLE_ADAPTER (etta), FALSE);
1233 return etta->priv->sort_children_ascending;
1236 void
1237 e_tree_table_adapter_set_sort_children_ascending (ETreeTableAdapter *etta,
1238 gboolean sort_children_ascending)
1240 g_return_if_fail (E_IS_TREE_TABLE_ADAPTER (etta));
1242 if ((etta->priv->sort_children_ascending ? 1 : 0) == (sort_children_ascending ? 1 : 0))
1243 return;
1245 etta->priv->sort_children_ascending = sort_children_ascending;
1246 g_clear_object (&etta->priv->children_sort_info);
1248 g_object_notify (G_OBJECT (etta), "sort-children-ascending");
1250 if (!etta->priv->root)
1251 return;
1253 e_table_model_pre_change (E_TABLE_MODEL (etta));
1254 resort_node (etta, etta->priv->root, TRUE);
1255 fill_map (etta, 0, etta->priv->root);
1256 e_table_model_changed (E_TABLE_MODEL (etta));
1259 ETreeModel *
1260 e_tree_table_adapter_get_source_model (ETreeTableAdapter *etta)
1262 g_return_val_if_fail (E_IS_TREE_TABLE_ADAPTER (etta), NULL);
1264 return etta->priv->source_model;
1267 typedef struct {
1268 xmlNode *root;
1269 gboolean expanded_default;
1270 ETreeModel *model;
1271 } TreeAndRoot;
1273 static void
1274 save_expanded_state_func (gpointer keyp,
1275 gpointer value,
1276 gpointer data)
1278 ETreePath path = keyp;
1279 node_t *node = ((GNode *) value)->data;
1280 TreeAndRoot *tar = data;
1281 xmlNode *xmlnode;
1283 if (node->expanded != tar->expanded_default) {
1284 gchar *save_id = e_tree_model_get_save_id (tar->model, path);
1285 xmlnode = xmlNewChild (tar->root, NULL, (const guchar *)"node", NULL);
1286 e_xml_set_string_prop_by_name (xmlnode, (const guchar *)"id", save_id);
1287 g_free (save_id);
1291 xmlDoc *
1292 e_tree_table_adapter_save_expanded_state_xml (ETreeTableAdapter *etta)
1294 TreeAndRoot tar;
1295 xmlDocPtr doc;
1296 xmlNode *root;
1298 g_return_val_if_fail (E_IS_TREE_TABLE_ADAPTER (etta), NULL);
1300 doc = xmlNewDoc ((const guchar *)"1.0");
1301 root = xmlNewDocNode (doc, NULL, (const guchar *)"expanded_state", NULL);
1302 xmlDocSetRootElement (doc, root);
1304 tar.model = etta->priv->source_model;
1305 tar.root = root;
1306 tar.expanded_default = e_tree_model_get_expanded_default (etta->priv->source_model);
1308 e_xml_set_integer_prop_by_name (root, (const guchar *)"vers", 2);
1309 e_xml_set_bool_prop_by_name (root, (const guchar *)"default", tar.expanded_default);
1311 g_hash_table_foreach (etta->priv->nodes, save_expanded_state_func, &tar);
1313 return doc;
1316 void
1317 e_tree_table_adapter_save_expanded_state (ETreeTableAdapter *etta,
1318 const gchar *filename)
1320 xmlDoc *doc;
1322 g_return_if_fail (E_IS_TREE_TABLE_ADAPTER (etta));
1324 doc = e_tree_table_adapter_save_expanded_state_xml (etta);
1325 if (doc) {
1326 e_xml_save_file (filename, doc);
1327 xmlFreeDoc (doc);
1331 static xmlDoc *
1332 open_file (ETreeTableAdapter *etta,
1333 const gchar *filename)
1335 xmlDoc *doc;
1336 xmlNode *root;
1337 gint vers;
1338 gboolean model_default, saved_default;
1340 if (!g_file_test (filename, G_FILE_TEST_EXISTS))
1341 return NULL;
1343 #ifdef G_OS_WIN32
1345 gchar *locale_filename = g_win32_locale_filename_from_utf8 (filename);
1346 doc = xmlParseFile (locale_filename);
1347 g_free (locale_filename);
1349 #else
1350 doc = xmlParseFile (filename);
1351 #endif
1353 if (!doc)
1354 return NULL;
1356 root = xmlDocGetRootElement (doc);
1357 if (root == NULL || strcmp ((gchar *) root->name, "expanded_state")) {
1358 xmlFreeDoc (doc);
1359 return NULL;
1362 vers = e_xml_get_integer_prop_by_name_with_default (root, (const guchar *)"vers", 0);
1363 if (vers > 2) {
1364 xmlFreeDoc (doc);
1365 return NULL;
1367 model_default = e_tree_model_get_expanded_default (etta->priv->source_model);
1368 saved_default = e_xml_get_bool_prop_by_name_with_default (root, (const guchar *)"default", !model_default);
1369 if (saved_default != model_default) {
1370 xmlFreeDoc (doc);
1371 return NULL;
1374 return doc;
1377 /* state: <0 ... collapse; 0 ... use default; >0 ... expand */
1378 void
1379 e_tree_table_adapter_force_expanded_state (ETreeTableAdapter *etta,
1380 gint state)
1382 g_return_if_fail (E_IS_TREE_TABLE_ADAPTER (etta));
1384 etta->priv->force_expanded_state = state;
1387 void
1388 e_tree_table_adapter_load_expanded_state_xml (ETreeTableAdapter *etta,
1389 xmlDoc *doc)
1391 xmlNode *root, *child;
1392 gboolean model_default;
1393 gboolean file_default = FALSE;
1395 g_return_if_fail (E_IS_TREE_TABLE_ADAPTER (etta));
1396 g_return_if_fail (doc != NULL);
1398 root = xmlDocGetRootElement (doc);
1400 e_table_model_pre_change (E_TABLE_MODEL (etta));
1402 model_default = e_tree_model_get_expanded_default (etta->priv->source_model);
1404 if (!strcmp ((gchar *) root->name, "expanded_state")) {
1405 gchar *state;
1407 state = e_xml_get_string_prop_by_name_with_default (root, (const guchar *)"default", "");
1409 if (state[0] == 't')
1410 file_default = TRUE;
1411 else
1412 file_default = FALSE; /* Even unspecified we'll consider as false */
1414 g_free (state);
1417 /* Incase the default is changed, lets forget the changes and stick to default */
1419 if (file_default != model_default) {
1420 xmlFreeDoc (doc);
1421 return;
1424 for (child = root->xmlChildrenNode; child; child = child->next) {
1425 gchar *id;
1426 ETreePath path;
1428 if (strcmp ((gchar *) child->name, "node")) {
1429 d (g_warning ("unknown node '%s' in %s", child->name, filename));
1430 continue;
1433 id = e_xml_get_string_prop_by_name_with_default (child, (const guchar *)"id", "");
1435 if (!strcmp (id, "")) {
1436 g_free (id);
1437 continue;
1440 path = e_tree_model_get_node_by_id (etta->priv->source_model, id);
1441 if (path)
1442 e_tree_table_adapter_node_set_expanded (etta, path, !model_default);
1444 g_free (id);
1447 e_table_model_changed (E_TABLE_MODEL (etta));
1450 void
1451 e_tree_table_adapter_load_expanded_state (ETreeTableAdapter *etta,
1452 const gchar *filename)
1454 xmlDoc *doc;
1456 g_return_if_fail (E_IS_TREE_TABLE_ADAPTER (etta));
1458 doc = open_file (etta, filename);
1459 if (!doc)
1460 return;
1462 e_tree_table_adapter_load_expanded_state_xml (etta, doc);
1464 xmlFreeDoc (doc);
1467 void
1468 e_tree_table_adapter_root_node_set_visible (ETreeTableAdapter *etta,
1469 gboolean visible)
1471 gint size;
1473 g_return_if_fail (E_IS_TREE_TABLE_ADAPTER (etta));
1475 if (etta->priv->root_visible == visible)
1476 return;
1478 e_table_model_pre_change (E_TABLE_MODEL (etta));
1480 etta->priv->root_visible = visible;
1481 if (!visible) {
1482 ETreePath root = e_tree_model_get_root (etta->priv->source_model);
1483 if (root)
1484 e_tree_table_adapter_node_set_expanded (etta, root, TRUE);
1486 size = (visible ? 1 : 0) + (etta->priv->root ? ((node_t *) etta->priv->root->data)->num_visible_children : 0);
1487 resize_map (etta, size);
1488 if (etta->priv->root)
1489 fill_map (etta, 0, etta->priv->root);
1490 e_table_model_changed (E_TABLE_MODEL (etta));
1493 void
1494 e_tree_table_adapter_node_set_expanded (ETreeTableAdapter *etta,
1495 ETreePath path,
1496 gboolean expanded)
1498 GNode *gnode;
1499 node_t *node;
1500 gint row;
1502 g_return_if_fail (E_IS_TREE_TABLE_ADAPTER (etta));
1504 gnode = lookup_gnode (etta, path);
1506 if (!expanded && (!gnode || (e_tree_model_node_is_root (etta->priv->source_model, path) && !etta->priv->root_visible)))
1507 return;
1509 if (!gnode && expanded) {
1510 ETreePath parent = e_tree_model_node_get_parent (etta->priv->source_model, path);
1511 g_return_if_fail (parent != NULL);
1512 e_tree_table_adapter_node_set_expanded (etta, parent, expanded);
1513 gnode = lookup_gnode (etta, path);
1515 g_return_if_fail (gnode != NULL);
1517 node = (node_t *) gnode->data;
1519 if (expanded == node->expanded)
1520 return;
1522 node->expanded = expanded;
1524 row = e_tree_table_adapter_row_of_node (etta, path);
1525 if (row == -1)
1526 return;
1528 e_table_model_pre_change (E_TABLE_MODEL (etta));
1529 e_table_model_pre_change (E_TABLE_MODEL (etta));
1530 e_table_model_row_changed (E_TABLE_MODEL (etta), row);
1532 if (expanded) {
1533 gint num_children = insert_children (etta, gnode);
1534 update_child_counts (gnode, num_children);
1535 if (etta->priv->sort_info && e_table_sort_info_sorting_get_count (etta->priv->sort_info) > 0)
1536 resort_node (etta, gnode, TRUE);
1537 resize_map (etta, etta->priv->n_map + num_children);
1538 move_map_elements (etta, row + 1 + num_children, row + 1, etta->priv->n_map - row - 1 - num_children);
1539 fill_map (etta, row, gnode);
1540 if (num_children != 0) {
1541 e_table_model_rows_inserted (E_TABLE_MODEL (etta), row + 1, num_children);
1542 } else
1543 e_table_model_no_change (E_TABLE_MODEL (etta));
1544 } else {
1545 gint num_children = delete_children (etta, gnode);
1546 if (num_children == 0) {
1547 e_table_model_no_change (E_TABLE_MODEL (etta));
1548 return;
1550 move_map_elements (etta, row + 1, row + 1 + num_children, etta->priv->n_map - row - 1 - num_children);
1551 update_child_counts (gnode, - num_children);
1552 resize_map (etta, etta->priv->n_map - num_children);
1553 e_table_model_rows_deleted (E_TABLE_MODEL (etta), row + 1, num_children);
1557 void
1558 e_tree_table_adapter_node_set_expanded_recurse (ETreeTableAdapter *etta,
1559 ETreePath path,
1560 gboolean expanded)
1562 ETreePath children;
1564 g_return_if_fail (E_IS_TREE_TABLE_ADAPTER (etta));
1566 e_tree_table_adapter_node_set_expanded (etta, path, expanded);
1568 for (children = e_tree_model_node_get_first_child (etta->priv->source_model, path);
1569 children;
1570 children = e_tree_model_node_get_next (etta->priv->source_model, children)) {
1571 e_tree_table_adapter_node_set_expanded_recurse (etta, children, expanded);
1575 ETreePath
1576 e_tree_table_adapter_node_at_row (ETreeTableAdapter *etta,
1577 gint row)
1579 g_return_val_if_fail (E_IS_TREE_TABLE_ADAPTER (etta), NULL);
1581 if (row == -1 && etta->priv->n_map > 0)
1582 row = etta->priv->n_map - 1;
1583 else if (row < 0 || row >= etta->priv->n_map)
1584 return NULL;
1586 return etta->priv->map_table[row]->path;
1589 gint
1590 e_tree_table_adapter_row_of_node (ETreeTableAdapter *etta,
1591 ETreePath path)
1593 node_t *node;
1595 g_return_val_if_fail (E_IS_TREE_TABLE_ADAPTER (etta), -1);
1597 node = get_node (etta, path);
1598 if (node == NULL)
1599 return -1;
1601 if (etta->priv->remap_needed)
1602 remap_indices (etta);
1604 return node->index;
1607 gboolean
1608 e_tree_table_adapter_root_node_is_visible (ETreeTableAdapter *etta)
1610 g_return_val_if_fail (E_IS_TREE_TABLE_ADAPTER (etta), FALSE);
1612 return etta->priv->root_visible;
1615 void
1616 e_tree_table_adapter_show_node (ETreeTableAdapter *etta,
1617 ETreePath path)
1619 ETreePath parent;
1621 g_return_if_fail (E_IS_TREE_TABLE_ADAPTER (etta));
1623 parent = e_tree_model_node_get_parent (etta->priv->source_model, path);
1625 while (parent) {
1626 e_tree_table_adapter_node_set_expanded (etta, parent, TRUE);
1627 parent = e_tree_model_node_get_parent (etta->priv->source_model, parent);
1631 gboolean
1632 e_tree_table_adapter_node_is_expanded (ETreeTableAdapter *etta,
1633 ETreePath path)
1635 node_t *node;
1637 g_return_val_if_fail (E_IS_TREE_TABLE_ADAPTER (etta), FALSE);
1639 node = get_node (etta, path);
1640 if (!e_tree_model_node_is_expandable (etta->priv->source_model, path) || !node)
1641 return FALSE;
1643 return node->expanded;
1646 ETreePath
1647 e_tree_table_adapter_node_get_next (ETreeTableAdapter *etta,
1648 ETreePath path)
1650 GNode *node;
1652 g_return_val_if_fail (E_IS_TREE_TABLE_ADAPTER (etta), NULL);
1654 node = lookup_gnode (etta, path);
1656 if (node && node->next)
1657 return ((node_t *) node->next->data)->path;
1659 return NULL;