Updated Hungarian translation
[evolution.git] / e-util / e-tree-table-adapter.c
bloba62e4f38f4b722b725656831ce47c99a2a7355b4
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;
69 ETableHeader *header;
71 gint n_map;
72 gint n_vals_allocated;
73 node_t **map_table;
74 GHashTable *nodes;
75 GNode *root;
77 guint root_visible : 1;
78 guint remap_needed : 1;
80 gint last_access;
82 guint resort_idle_id;
84 gint force_expanded_state; /* use this instead of model's default if not 0; <0 ... collapse, >0 ... expand */
87 enum {
88 PROP_0,
89 PROP_HEADER,
90 PROP_SORT_INFO,
91 PROP_SOURCE_MODEL
94 enum {
95 SORTING_CHANGED,
96 LAST_SIGNAL
99 /* Forward Declarations */
100 static void e_tree_table_adapter_table_model_init
101 (ETableModelInterface *iface);
103 static guint signals[LAST_SIGNAL];
105 G_DEFINE_TYPE_WITH_CODE (
106 ETreeTableAdapter,
107 e_tree_table_adapter,
108 G_TYPE_OBJECT,
109 G_IMPLEMENT_INTERFACE (
110 E_TYPE_TABLE_MODEL,
111 e_tree_table_adapter_table_model_init))
113 static GNode *
114 lookup_gnode (ETreeTableAdapter *etta,
115 ETreePath path)
117 GNode *gnode;
119 if (!path)
120 return NULL;
122 gnode = g_hash_table_lookup (etta->priv->nodes, path);
124 return gnode;
127 static void
128 resize_map (ETreeTableAdapter *etta,
129 gint size)
131 if (size > etta->priv->n_vals_allocated) {
132 etta->priv->n_vals_allocated = MAX (etta->priv->n_vals_allocated + INCREMENT_AMOUNT, size);
133 etta->priv->map_table = g_renew (node_t *, etta->priv->map_table, etta->priv->n_vals_allocated);
136 etta->priv->n_map = size;
139 static void
140 move_map_elements (ETreeTableAdapter *etta,
141 gint to,
142 gint from,
143 gint count)
145 if (count <= 0 || from >= etta->priv->n_map)
146 return;
147 memmove (etta->priv->map_table + to, etta->priv->map_table + from, count * sizeof (node_t *));
148 etta->priv->remap_needed = TRUE;
151 static gint
152 fill_map (ETreeTableAdapter *etta,
153 gint index,
154 GNode *gnode)
156 GNode *p;
158 if ((gnode != etta->priv->root) || etta->priv->root_visible)
159 etta->priv->map_table[index++] = gnode->data;
161 for (p = gnode->children; p; p = p->next)
162 index = fill_map (etta, index, p);
164 etta->priv->remap_needed = TRUE;
165 return index;
168 static void
169 remap_indices (ETreeTableAdapter *etta)
171 gint i;
172 for (i = 0; i < etta->priv->n_map; i++)
173 etta->priv->map_table[i]->index = i;
174 etta->priv->remap_needed = FALSE;
177 static node_t *
178 get_node (ETreeTableAdapter *etta,
179 ETreePath path)
181 GNode *gnode = lookup_gnode (etta, path);
183 if (!gnode)
184 return NULL;
186 return (node_t *) gnode->data;
189 static void
190 resort_node (ETreeTableAdapter *etta,
191 GNode *gnode,
192 gboolean recurse)
194 node_t *node = (node_t *) gnode->data;
195 ETreePath *paths, path;
196 GNode *prev, *curr;
197 gint i, count;
198 gboolean sort_needed;
200 if (node->num_visible_children == 0)
201 return;
203 sort_needed = etta->priv->sort_info && e_table_sort_info_sorting_get_count (etta->priv->sort_info) > 0;
205 for (i = 0, path = e_tree_model_node_get_first_child (etta->priv->source_model, node->path); path;
206 path = e_tree_model_node_get_next (etta->priv->source_model, path), i++);
208 count = i;
209 if (count <= 1)
210 return;
212 paths = g_new0 (ETreePath, count);
214 for (i = 0, path = e_tree_model_node_get_first_child (etta->priv->source_model, node->path); path;
215 path = e_tree_model_node_get_next (etta->priv->source_model, path), i++)
216 paths[i] = path;
218 if (count > 1 && sort_needed)
219 e_table_sorting_utils_tree_sort (etta->priv->source_model, etta->priv->sort_info, etta->priv->header, paths, count);
221 prev = NULL;
222 for (i = 0; i < count; i++) {
223 curr = lookup_gnode (etta, paths[i]);
224 if (!curr)
225 continue;
227 if (prev)
228 prev->next = curr;
229 else
230 gnode->children = curr;
232 curr->prev = prev;
233 curr->next = NULL;
234 prev = curr;
235 if (recurse)
236 resort_node (etta, curr, recurse);
239 g_free (paths);
242 static void
243 kill_gnode (GNode *node,
244 ETreeTableAdapter *etta)
246 g_hash_table_remove (etta->priv->nodes, ((node_t *) node->data)->path);
248 while (node->children) {
249 GNode *next = node->children->next;
250 kill_gnode (node->children, etta);
251 node->children = next;
254 g_free (node->data);
255 if (node == etta->priv->root)
256 etta->priv->root = NULL;
257 g_node_destroy (node);
260 static void
261 update_child_counts (GNode *gnode,
262 gint delta)
264 while (gnode) {
265 node_t *node = (node_t *) gnode->data;
266 node->num_visible_children += delta;
267 gnode = gnode->parent;
271 static gint
272 delete_children (ETreeTableAdapter *etta,
273 GNode *gnode)
275 node_t *node = (node_t *) gnode->data;
276 gint to_remove = node ? node->num_visible_children : 0;
278 if (to_remove == 0)
279 return 0;
281 while (gnode->children) {
282 GNode *next = gnode->children->next;
283 kill_gnode (gnode->children, etta);
284 gnode->children = next;
287 return to_remove;
290 static void
291 delete_node (ETreeTableAdapter *etta,
292 ETreePath parent,
293 ETreePath path)
295 gint to_remove = 1;
296 gint parent_row = e_tree_table_adapter_row_of_node (etta, parent);
297 gint row = e_tree_table_adapter_row_of_node (etta, path);
298 GNode *gnode = lookup_gnode (etta, path);
299 GNode *parent_gnode = lookup_gnode (etta, parent);
301 e_table_model_pre_change (E_TABLE_MODEL (etta));
303 if (row == -1) {
304 e_table_model_no_change (E_TABLE_MODEL (etta));
305 return;
308 to_remove += delete_children (etta, gnode);
309 kill_gnode (gnode, etta);
311 move_map_elements (etta, row, row + to_remove, etta->priv->n_map - row - to_remove);
312 resize_map (etta, etta->priv->n_map - to_remove);
314 if (parent_gnode != NULL) {
315 node_t *parent_node = parent_gnode->data;
316 gboolean expandable = e_tree_model_node_is_expandable (etta->priv->source_model, parent);
318 update_child_counts (parent_gnode, - to_remove);
319 if (parent_node->expandable != expandable) {
320 e_table_model_pre_change (E_TABLE_MODEL (etta));
321 parent_node->expandable = expandable;
322 e_table_model_row_changed (E_TABLE_MODEL (etta), parent_row);
325 resort_node (etta, parent_gnode, FALSE);
328 e_table_model_rows_deleted (E_TABLE_MODEL (etta), row, to_remove);
331 static GNode *
332 create_gnode (ETreeTableAdapter *etta,
333 ETreePath path)
335 GNode *gnode;
336 node_t *node;
338 node = g_new0 (node_t, 1);
339 node->path = path;
340 node->index = -1;
341 node->expanded = etta->priv->force_expanded_state == 0 ? e_tree_model_get_expanded_default (etta->priv->source_model) : etta->priv->force_expanded_state > 0;
342 node->expandable = e_tree_model_node_is_expandable (etta->priv->source_model, path);
343 node->expandable_set = 1;
344 node->num_visible_children = 0;
345 gnode = g_node_new (node);
346 g_hash_table_insert (etta->priv->nodes, path, gnode);
347 return gnode;
350 static gint
351 insert_children (ETreeTableAdapter *etta,
352 GNode *gnode)
354 ETreePath path, tmp;
355 gint count = 0;
356 gint pos = 0;
358 path = ((node_t *) gnode->data)->path;
359 for (tmp = e_tree_model_node_get_first_child (etta->priv->source_model, path);
360 tmp;
361 tmp = e_tree_model_node_get_next (etta->priv->source_model, tmp), pos++) {
362 GNode *child = create_gnode (etta, tmp);
363 node_t *node = (node_t *) child->data;
364 if (node->expanded)
365 node->num_visible_children = insert_children (etta, child);
366 g_node_prepend (gnode, child);
367 count += node->num_visible_children + 1;
369 g_node_reverse_children (gnode);
370 return count;
373 static void
374 generate_tree (ETreeTableAdapter *etta,
375 ETreePath path)
377 GNode *gnode;
378 node_t *node;
379 gint size;
381 e_table_model_pre_change (E_TABLE_MODEL (etta));
383 g_return_if_fail (e_tree_model_node_is_root (etta->priv->source_model, path));
385 if (etta->priv->root)
386 kill_gnode (etta->priv->root, etta);
387 resize_map (etta, 0);
389 gnode = create_gnode (etta, path);
390 node = (node_t *) gnode->data;
391 node->expanded = TRUE;
392 node->num_visible_children = insert_children (etta, gnode);
393 if (etta->priv->sort_info && e_table_sort_info_sorting_get_count (etta->priv->sort_info) > 0)
394 resort_node (etta, gnode, TRUE);
396 etta->priv->root = gnode;
397 size = etta->priv->root_visible ? node->num_visible_children + 1 : node->num_visible_children;
398 resize_map (etta, size);
399 fill_map (etta, 0, gnode);
400 e_table_model_changed (E_TABLE_MODEL (etta));
403 static void
404 insert_node (ETreeTableAdapter *etta,
405 ETreePath parent,
406 ETreePath path)
408 GNode *gnode, *parent_gnode;
409 node_t *node, *parent_node;
410 gboolean expandable;
411 gint size, row;
413 e_table_model_pre_change (E_TABLE_MODEL (etta));
415 if (get_node (etta, path)) {
416 e_table_model_no_change (E_TABLE_MODEL (etta));
417 return;
420 parent_gnode = lookup_gnode (etta, parent);
421 if (!parent_gnode) {
422 ETreePath grandparent = e_tree_model_node_get_parent (etta->priv->source_model, parent);
423 if (e_tree_model_node_is_root (etta->priv->source_model, parent))
424 generate_tree (etta, parent);
425 else
426 insert_node (etta, grandparent, parent);
427 e_table_model_changed (E_TABLE_MODEL (etta));
428 return;
431 parent_node = (node_t *) parent_gnode->data;
433 if (parent_gnode != etta->priv->root) {
434 expandable = e_tree_model_node_is_expandable (etta->priv->source_model, parent);
435 if (parent_node->expandable != expandable) {
436 e_table_model_pre_change (E_TABLE_MODEL (etta));
437 parent_node->expandable = expandable;
438 parent_node->expandable_set = 1;
439 e_table_model_row_changed (E_TABLE_MODEL (etta), parent_node->index);
443 if (!e_tree_table_adapter_node_is_expanded (etta, parent)) {
444 e_table_model_no_change (E_TABLE_MODEL (etta));
445 return;
448 gnode = create_gnode (etta, path);
449 node = (node_t *) gnode->data;
451 if (node->expanded)
452 node->num_visible_children = insert_children (etta, gnode);
454 g_node_append (parent_gnode, gnode);
455 update_child_counts (parent_gnode, node->num_visible_children + 1);
456 resort_node (etta, parent_gnode, FALSE);
457 resort_node (etta, gnode, TRUE);
459 size = node->num_visible_children + 1;
460 resize_map (etta, etta->priv->n_map + size);
461 if (parent_gnode == etta->priv->root)
462 row = 0;
463 else {
464 gint new_size = parent_node->num_visible_children + 1;
465 gint old_size = new_size - size;
466 row = parent_node->index;
467 move_map_elements (etta, row + new_size, row + old_size, etta->priv->n_map - row - new_size);
469 fill_map (etta, row, parent_gnode);
470 e_table_model_rows_inserted (
471 E_TABLE_MODEL (etta),
472 e_tree_table_adapter_row_of_node (etta, path), size);
475 typedef struct {
476 GSList *paths;
477 gboolean expanded;
478 } check_expanded_closure;
480 static gboolean
481 check_expanded (GNode *gnode,
482 gpointer data)
484 check_expanded_closure *closure = (check_expanded_closure *) data;
485 node_t *node = (node_t *) gnode->data;
487 if (node->expanded != closure->expanded)
488 closure->paths = g_slist_prepend (closure->paths, node->path);
490 return FALSE;
493 static void
494 update_node (ETreeTableAdapter *etta,
495 ETreePath path)
497 check_expanded_closure closure;
498 ETreePath parent = e_tree_model_node_get_parent (etta->priv->source_model, path);
499 GNode *gnode = lookup_gnode (etta, path);
500 GSList *l;
502 closure.expanded = e_tree_model_get_expanded_default (etta->priv->source_model);
503 closure.paths = NULL;
505 if (gnode)
506 g_node_traverse (gnode, G_POST_ORDER, G_TRAVERSE_ALL, -1, check_expanded, &closure);
508 if (e_tree_model_node_is_root (etta->priv->source_model, path))
509 generate_tree (etta, path);
510 else {
511 delete_node (etta, parent, path);
512 insert_node (etta, parent, path);
515 for (l = closure.paths; l; l = l->next)
516 if (lookup_gnode (etta, l->data))
517 e_tree_table_adapter_node_set_expanded (etta, l->data, !closure.expanded);
519 g_slist_free (closure.paths);
522 static void
523 tree_table_adapter_sort_info_changed_cb (ETableSortInfo *sort_info,
524 ETreeTableAdapter *etta)
526 if (!etta->priv->root)
527 return;
529 /* the function is called also internally, with sort_info = NULL,
530 * thus skip those in signal emit */
531 if (sort_info) {
532 gboolean handled = FALSE;
534 g_signal_emit (etta, signals[SORTING_CHANGED], 0, &handled);
536 if (handled)
537 return;
540 e_table_model_pre_change (E_TABLE_MODEL (etta));
541 resort_node (etta, etta->priv->root, TRUE);
542 fill_map (etta, 0, etta->priv->root);
543 e_table_model_changed (E_TABLE_MODEL (etta));
546 static void
547 tree_table_adapter_source_model_pre_change_cb (ETreeModel *source_model,
548 ETreeTableAdapter *etta)
550 e_table_model_pre_change (E_TABLE_MODEL (etta));
553 static void
554 tree_table_adapter_source_model_rebuilt_cb (ETreeModel *source_model,
555 ETreeTableAdapter *etta)
557 if (!etta->priv->root)
558 return;
560 kill_gnode (etta->priv->root, etta);
561 etta->priv->root = NULL;
563 g_hash_table_remove_all (etta->priv->nodes);
566 static gboolean
567 tree_table_adapter_resort_model_idle_cb (gpointer user_data)
569 ETreeTableAdapter *etta;
571 etta = E_TREE_TABLE_ADAPTER (user_data);
572 tree_table_adapter_sort_info_changed_cb (NULL, etta);
573 etta->priv->resort_idle_id = 0;
575 return FALSE;
578 static void
579 tree_table_adapter_source_model_node_changed_cb (ETreeModel *source_model,
580 ETreePath path,
581 ETreeTableAdapter *etta)
583 update_node (etta, path);
584 e_table_model_changed (E_TABLE_MODEL (etta));
586 /* FIXME: Really it shouldnt be required. But a lot of thread
587 * which were supposed to be present in the list is way below
589 if (etta->priv->resort_idle_id == 0)
590 etta->priv->resort_idle_id = g_idle_add (
591 tree_table_adapter_resort_model_idle_cb, etta);
594 static void
595 tree_table_adapter_source_model_node_data_changed_cb (ETreeModel *source_model,
596 ETreePath path,
597 ETreeTableAdapter *etta)
599 gint row = e_tree_table_adapter_row_of_node (etta, path);
601 if (row == -1) {
602 e_table_model_no_change (E_TABLE_MODEL (etta));
603 return;
606 e_table_model_row_changed (E_TABLE_MODEL (etta), row);
609 static void
610 tree_table_adapter_source_model_node_inserted_cb (ETreeModel *etm,
611 ETreePath parent,
612 ETreePath child,
613 ETreeTableAdapter *etta)
615 if (e_tree_model_node_is_root (etm, child))
616 generate_tree (etta, child);
617 else
618 insert_node (etta, parent, child);
620 e_table_model_changed (E_TABLE_MODEL (etta));
623 static void
624 tree_table_adapter_source_model_node_removed_cb (ETreeModel *etm,
625 ETreePath parent,
626 ETreePath child,
627 gint old_position,
628 ETreeTableAdapter *etta)
630 delete_node (etta, parent, child);
631 e_table_model_changed (E_TABLE_MODEL (etta));
634 static void
635 tree_table_adapter_set_header (ETreeTableAdapter *etta,
636 ETableHeader *header)
638 if (header == NULL)
639 return;
641 g_return_if_fail (E_IS_TABLE_HEADER (header));
642 g_return_if_fail (etta->priv->header == NULL);
644 etta->priv->header = g_object_ref (header);
647 static void
648 tree_table_adapter_set_source_model (ETreeTableAdapter *etta,
649 ETreeModel *source_model)
651 g_return_if_fail (E_IS_TREE_MODEL (source_model));
652 g_return_if_fail (etta->priv->source_model == NULL);
654 etta->priv->source_model = g_object_ref (source_model);
657 static void
658 tree_table_adapter_set_property (GObject *object,
659 guint property_id,
660 const GValue *value,
661 GParamSpec *pspec)
663 switch (property_id) {
664 case PROP_HEADER:
665 tree_table_adapter_set_header (
666 E_TREE_TABLE_ADAPTER (object),
667 g_value_get_object (value));
668 return;
670 case PROP_SORT_INFO:
671 e_tree_table_adapter_set_sort_info (
672 E_TREE_TABLE_ADAPTER (object),
673 g_value_get_object (value));
674 return;
676 case PROP_SOURCE_MODEL:
677 tree_table_adapter_set_source_model (
678 E_TREE_TABLE_ADAPTER (object),
679 g_value_get_object (value));
680 return;
683 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
686 static void
687 tree_table_adapter_get_property (GObject *object,
688 guint property_id,
689 GValue *value,
690 GParamSpec *pspec)
692 switch (property_id) {
693 case PROP_HEADER:
694 g_value_set_object (
695 value,
696 e_tree_table_adapter_get_header (
697 E_TREE_TABLE_ADAPTER (object)));
698 return;
700 case PROP_SORT_INFO:
701 g_value_set_object (
702 value,
703 e_tree_table_adapter_get_sort_info (
704 E_TREE_TABLE_ADAPTER (object)));
705 return;
707 case PROP_SOURCE_MODEL:
708 g_value_set_object (
709 value,
710 e_tree_table_adapter_get_source_model (
711 E_TREE_TABLE_ADAPTER (object)));
712 return;
715 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
718 static void
719 tree_table_adapter_dispose (GObject *object)
721 ETreeTableAdapterPrivate *priv;
723 priv = E_TREE_TABLE_ADAPTER_GET_PRIVATE (object);
725 if (priv->pre_change_handler_id > 0) {
726 g_signal_handler_disconnect (
727 priv->source_model,
728 priv->pre_change_handler_id);
729 priv->pre_change_handler_id = 0;
732 if (priv->rebuilt_handler_id > 0) {
733 g_signal_handler_disconnect (
734 priv->source_model,
735 priv->rebuilt_handler_id);
736 priv->rebuilt_handler_id = 0;
739 if (priv->node_changed_handler_id > 0) {
740 g_signal_handler_disconnect (
741 priv->source_model,
742 priv->node_changed_handler_id);
743 priv->node_changed_handler_id = 0;
746 if (priv->node_data_changed_handler_id > 0) {
747 g_signal_handler_disconnect (
748 priv->source_model,
749 priv->node_data_changed_handler_id);
750 priv->node_data_changed_handler_id = 0;
753 if (priv->node_inserted_handler_id > 0) {
754 g_signal_handler_disconnect (
755 priv->source_model,
756 priv->node_inserted_handler_id);
757 priv->node_inserted_handler_id = 0;
760 if (priv->node_removed_handler_id > 0) {
761 g_signal_handler_disconnect (
762 priv->source_model,
763 priv->node_removed_handler_id);
764 priv->node_removed_handler_id = 0;
767 if (priv->sort_info_changed_handler_id > 0) {
768 g_signal_handler_disconnect (
769 priv->sort_info,
770 priv->sort_info_changed_handler_id);
771 priv->sort_info_changed_handler_id = 0;
774 g_clear_object (&priv->source_model);
775 g_clear_object (&priv->sort_info);
776 g_clear_object (&priv->header);
778 /* Chain up to parent's dispose() method. */
779 G_OBJECT_CLASS (e_tree_table_adapter_parent_class)->dispose (object);
782 static void
783 tree_table_adapter_finalize (GObject *object)
785 ETreeTableAdapterPrivate *priv;
787 priv = E_TREE_TABLE_ADAPTER_GET_PRIVATE (object);
789 if (priv->resort_idle_id) {
790 g_source_remove (priv->resort_idle_id);
791 priv->resort_idle_id = 0;
794 if (priv->root) {
795 kill_gnode (priv->root, E_TREE_TABLE_ADAPTER (object));
796 priv->root = NULL;
799 g_hash_table_destroy (priv->nodes);
801 g_free (priv->map_table);
803 /* Chain up to parent's finalize() method. */
804 G_OBJECT_CLASS (e_tree_table_adapter_parent_class)->finalize (object);
807 static void
808 tree_table_adapter_constructed (GObject *object)
810 ETreeTableAdapter *etta;
811 ETreeModel *source_model;
812 ETreePath root;
813 gulong handler_id;
815 etta = E_TREE_TABLE_ADAPTER (object);
817 /* Chain up to parent's constructed() method. */
818 G_OBJECT_CLASS (e_tree_table_adapter_parent_class)->constructed (object);
820 source_model = e_tree_table_adapter_get_source_model (etta);
822 root = e_tree_model_get_root (source_model);
823 if (root != NULL)
824 generate_tree (etta, root);
826 handler_id = g_signal_connect (
827 source_model, "pre_change",
828 G_CALLBACK (tree_table_adapter_source_model_pre_change_cb),
829 etta);
830 etta->priv->pre_change_handler_id = handler_id;
832 handler_id = g_signal_connect (
833 source_model, "rebuilt",
834 G_CALLBACK (tree_table_adapter_source_model_rebuilt_cb),
835 etta);
836 etta->priv->rebuilt_handler_id = handler_id;
838 handler_id = g_signal_connect (
839 source_model, "node_changed",
840 G_CALLBACK (tree_table_adapter_source_model_node_changed_cb),
841 etta);
842 etta->priv->node_changed_handler_id = handler_id;
844 handler_id = g_signal_connect (
845 source_model, "node_data_changed",
846 G_CALLBACK (tree_table_adapter_source_model_node_data_changed_cb),
847 etta);
848 etta->priv->node_data_changed_handler_id = handler_id;
850 handler_id = g_signal_connect (
851 source_model, "node_inserted",
852 G_CALLBACK (tree_table_adapter_source_model_node_inserted_cb),
853 etta);
854 etta->priv->node_inserted_handler_id = handler_id;
856 handler_id = g_signal_connect (
857 source_model, "node_removed",
858 G_CALLBACK (tree_table_adapter_source_model_node_removed_cb),
859 etta);
860 etta->priv->node_removed_handler_id = handler_id;
863 static gint
864 tree_table_adapter_column_count (ETableModel *etm)
866 ETreeTableAdapter *etta = (ETreeTableAdapter *) etm;
868 return e_tree_model_column_count (etta->priv->source_model);
871 static gboolean
872 tree_table_adapter_has_save_id (ETableModel *etm)
874 return TRUE;
877 static gchar *
878 tree_table_adapter_get_save_id (ETableModel *etm,
879 gint row)
881 ETreeTableAdapter *etta = (ETreeTableAdapter *) etm;
883 return e_tree_model_get_save_id (
884 etta->priv->source_model,
885 e_tree_table_adapter_node_at_row (etta, row));
888 static gint
889 tree_table_adapter_row_count (ETableModel *etm)
891 ETreeTableAdapter *etta = (ETreeTableAdapter *) etm;
893 return etta->priv->n_map;
896 static gpointer
897 tree_table_adapter_value_at (ETableModel *etm,
898 gint col,
899 gint row)
901 ETreeTableAdapter *etta = (ETreeTableAdapter *) etm;
903 switch (col) {
904 case -1:
905 if (row == -1)
906 return NULL;
907 return e_tree_table_adapter_node_at_row (etta, row);
908 case -2:
909 return etta->priv->source_model;
910 case -3:
911 return etta;
912 default:
913 return e_tree_model_value_at (
914 etta->priv->source_model,
915 e_tree_table_adapter_node_at_row (etta, row), col);
919 static void
920 tree_table_adapter_set_value_at (ETableModel *etm,
921 gint col,
922 gint row,
923 gconstpointer val)
925 g_warn_if_reached ();
928 static gboolean
929 tree_table_adapter_is_cell_editable (ETableModel *etm,
930 gint col,
931 gint row)
933 return FALSE;
936 static void
937 tree_table_adapter_append_row (ETableModel *etm,
938 ETableModel *source,
939 gint row)
943 static gpointer
944 tree_table_adapter_duplicate_value (ETableModel *etm,
945 gint col,
946 gconstpointer value)
948 ETreeTableAdapter *etta = (ETreeTableAdapter *) etm;
950 return e_tree_model_duplicate_value (etta->priv->source_model, col, value);
953 static void
954 tree_table_adapter_free_value (ETableModel *etm,
955 gint col,
956 gpointer value)
958 ETreeTableAdapter *etta = (ETreeTableAdapter *) etm;
960 e_tree_model_free_value (etta->priv->source_model, col, value);
963 static gpointer
964 tree_table_adapter_initialize_value (ETableModel *etm,
965 gint col)
967 ETreeTableAdapter *etta = (ETreeTableAdapter *) etm;
969 return e_tree_model_initialize_value (etta->priv->source_model, col);
972 static gboolean
973 tree_table_adapter_value_is_empty (ETableModel *etm,
974 gint col,
975 gconstpointer value)
977 ETreeTableAdapter *etta = (ETreeTableAdapter *) etm;
979 return e_tree_model_value_is_empty (etta->priv->source_model, col, value);
982 static gchar *
983 tree_table_adapter_value_to_string (ETableModel *etm,
984 gint col,
985 gconstpointer value)
987 ETreeTableAdapter *etta = (ETreeTableAdapter *) etm;
989 return e_tree_model_value_to_string (etta->priv->source_model, col, value);
992 static void
993 e_tree_table_adapter_class_init (ETreeTableAdapterClass *class)
995 GObjectClass *object_class;
997 g_type_class_add_private (class, sizeof (ETreeTableAdapterPrivate));
999 object_class = G_OBJECT_CLASS (class);
1000 object_class->set_property = tree_table_adapter_set_property;
1001 object_class->get_property = tree_table_adapter_get_property;
1002 object_class->dispose = tree_table_adapter_dispose;
1003 object_class->finalize = tree_table_adapter_finalize;
1004 object_class->constructed = tree_table_adapter_constructed;
1006 g_object_class_install_property (
1007 object_class,
1008 PROP_HEADER,
1009 g_param_spec_object (
1010 "header",
1011 "Header",
1012 NULL,
1013 E_TYPE_TABLE_HEADER,
1014 G_PARAM_READWRITE |
1015 G_PARAM_CONSTRUCT_ONLY |
1016 G_PARAM_STATIC_STRINGS));
1018 g_object_class_install_property (
1019 object_class,
1020 PROP_SORT_INFO,
1021 g_param_spec_object (
1022 "sort-info",
1023 "Sort Info",
1024 NULL,
1025 E_TYPE_TABLE_SORT_INFO,
1026 G_PARAM_READWRITE |
1027 G_PARAM_CONSTRUCT |
1028 G_PARAM_STATIC_STRINGS));
1030 g_object_class_install_property (
1031 object_class,
1032 PROP_SOURCE_MODEL,
1033 g_param_spec_object (
1034 "source-model",
1035 "Source Model",
1036 NULL,
1037 E_TYPE_TREE_MODEL,
1038 G_PARAM_READWRITE |
1039 G_PARAM_CONSTRUCT_ONLY |
1040 G_PARAM_STATIC_STRINGS));
1042 signals[SORTING_CHANGED] = g_signal_new (
1043 "sorting_changed",
1044 G_OBJECT_CLASS_TYPE (object_class),
1045 G_SIGNAL_RUN_LAST,
1046 G_STRUCT_OFFSET (ETreeTableAdapterClass, sorting_changed),
1047 NULL, NULL,
1048 e_marshal_BOOLEAN__NONE,
1049 G_TYPE_BOOLEAN, 0,
1050 G_TYPE_NONE);
1053 static void
1054 e_tree_table_adapter_table_model_init (ETableModelInterface *iface)
1056 iface->column_count = tree_table_adapter_column_count;
1057 iface->row_count = tree_table_adapter_row_count;
1058 iface->append_row = tree_table_adapter_append_row;
1060 iface->value_at = tree_table_adapter_value_at;
1061 iface->set_value_at = tree_table_adapter_set_value_at;
1062 iface->is_cell_editable = tree_table_adapter_is_cell_editable;
1064 iface->has_save_id = tree_table_adapter_has_save_id;
1065 iface->get_save_id = tree_table_adapter_get_save_id;
1067 iface->duplicate_value = tree_table_adapter_duplicate_value;
1068 iface->free_value = tree_table_adapter_free_value;
1069 iface->initialize_value = tree_table_adapter_initialize_value;
1070 iface->value_is_empty = tree_table_adapter_value_is_empty;
1071 iface->value_to_string = tree_table_adapter_value_to_string;
1074 static void
1075 e_tree_table_adapter_init (ETreeTableAdapter *etta)
1077 etta->priv = E_TREE_TABLE_ADAPTER_GET_PRIVATE (etta);
1079 etta->priv->nodes = g_hash_table_new (NULL, NULL);
1081 etta->priv->root_visible = TRUE;
1082 etta->priv->remap_needed = TRUE;
1085 ETableModel *
1086 e_tree_table_adapter_new (ETreeModel *source_model,
1087 ETableSortInfo *sort_info,
1088 ETableHeader *header)
1090 g_return_val_if_fail (E_IS_TREE_MODEL (source_model), NULL);
1092 if (sort_info != NULL)
1093 g_return_val_if_fail (E_IS_TABLE_SORT_INFO (sort_info), NULL);
1095 if (header != NULL)
1096 g_return_val_if_fail (E_IS_TABLE_HEADER (header), NULL);
1098 return g_object_new (
1099 E_TYPE_TREE_TABLE_ADAPTER,
1100 "source-model", source_model,
1101 "sort-info", sort_info,
1102 "header", header,
1103 NULL);
1106 ETableHeader *
1107 e_tree_table_adapter_get_header (ETreeTableAdapter *etta)
1109 g_return_val_if_fail (E_IS_TREE_TABLE_ADAPTER (etta), NULL);
1111 return etta->priv->header;
1114 ETableSortInfo *
1115 e_tree_table_adapter_get_sort_info (ETreeTableAdapter *etta)
1117 g_return_val_if_fail (E_IS_TREE_TABLE_ADAPTER (etta), NULL);
1119 return etta->priv->sort_info;
1122 void
1123 e_tree_table_adapter_set_sort_info (ETreeTableAdapter *etta,
1124 ETableSortInfo *sort_info)
1126 g_return_if_fail (E_IS_TREE_TABLE_ADAPTER (etta));
1128 if (sort_info != NULL) {
1129 g_return_if_fail (E_IS_TABLE_SORT_INFO (sort_info));
1130 g_object_ref (sort_info);
1133 if (etta->priv->sort_info != NULL) {
1134 g_signal_handler_disconnect (
1135 etta->priv->sort_info,
1136 etta->priv->sort_info_changed_handler_id);
1137 etta->priv->sort_info_changed_handler_id = 0;
1139 g_clear_object (&etta->priv->sort_info);
1142 etta->priv->sort_info = sort_info;
1144 if (etta->priv->sort_info != NULL) {
1145 gulong handler_id;
1147 handler_id = g_signal_connect (
1148 etta->priv->sort_info, "sort_info_changed",
1149 G_CALLBACK (tree_table_adapter_sort_info_changed_cb),
1150 etta);
1151 etta->priv->sort_info_changed_handler_id = handler_id;
1154 g_object_notify (G_OBJECT (etta), "sort-info");
1156 if (etta->priv->root == NULL)
1157 return;
1159 e_table_model_pre_change (E_TABLE_MODEL (etta));
1160 resort_node (etta, etta->priv->root, TRUE);
1161 fill_map (etta, 0, etta->priv->root);
1162 e_table_model_changed (E_TABLE_MODEL (etta));
1165 ETreeModel *
1166 e_tree_table_adapter_get_source_model (ETreeTableAdapter *etta)
1168 g_return_val_if_fail (E_IS_TREE_TABLE_ADAPTER (etta), NULL);
1170 return etta->priv->source_model;
1173 typedef struct {
1174 xmlNode *root;
1175 gboolean expanded_default;
1176 ETreeModel *model;
1177 } TreeAndRoot;
1179 static void
1180 save_expanded_state_func (gpointer keyp,
1181 gpointer value,
1182 gpointer data)
1184 ETreePath path = keyp;
1185 node_t *node = ((GNode *) value)->data;
1186 TreeAndRoot *tar = data;
1187 xmlNode *xmlnode;
1189 if (node->expanded != tar->expanded_default) {
1190 gchar *save_id = e_tree_model_get_save_id (tar->model, path);
1191 xmlnode = xmlNewChild (tar->root, NULL, (const guchar *)"node", NULL);
1192 e_xml_set_string_prop_by_name (xmlnode, (const guchar *)"id", save_id);
1193 g_free (save_id);
1197 xmlDoc *
1198 e_tree_table_adapter_save_expanded_state_xml (ETreeTableAdapter *etta)
1200 TreeAndRoot tar;
1201 xmlDocPtr doc;
1202 xmlNode *root;
1204 g_return_val_if_fail (E_IS_TREE_TABLE_ADAPTER (etta), NULL);
1206 doc = xmlNewDoc ((const guchar *)"1.0");
1207 root = xmlNewDocNode (doc, NULL, (const guchar *)"expanded_state", NULL);
1208 xmlDocSetRootElement (doc, root);
1210 tar.model = etta->priv->source_model;
1211 tar.root = root;
1212 tar.expanded_default = e_tree_model_get_expanded_default (etta->priv->source_model);
1214 e_xml_set_integer_prop_by_name (root, (const guchar *)"vers", 2);
1215 e_xml_set_bool_prop_by_name (root, (const guchar *)"default", tar.expanded_default);
1217 g_hash_table_foreach (etta->priv->nodes, save_expanded_state_func, &tar);
1219 return doc;
1222 void
1223 e_tree_table_adapter_save_expanded_state (ETreeTableAdapter *etta,
1224 const gchar *filename)
1226 xmlDoc *doc;
1228 g_return_if_fail (E_IS_TREE_TABLE_ADAPTER (etta));
1230 doc = e_tree_table_adapter_save_expanded_state_xml (etta);
1231 if (doc) {
1232 e_xml_save_file (filename, doc);
1233 xmlFreeDoc (doc);
1237 static xmlDoc *
1238 open_file (ETreeTableAdapter *etta,
1239 const gchar *filename)
1241 xmlDoc *doc;
1242 xmlNode *root;
1243 gint vers;
1244 gboolean model_default, saved_default;
1246 if (!g_file_test (filename, G_FILE_TEST_EXISTS))
1247 return NULL;
1249 #ifdef G_OS_WIN32
1251 gchar *locale_filename = g_win32_locale_filename_from_utf8 (filename);
1252 doc = xmlParseFile (locale_filename);
1253 g_free (locale_filename);
1255 #else
1256 doc = xmlParseFile (filename);
1257 #endif
1259 if (!doc)
1260 return NULL;
1262 root = xmlDocGetRootElement (doc);
1263 if (root == NULL || strcmp ((gchar *) root->name, "expanded_state")) {
1264 xmlFreeDoc (doc);
1265 return NULL;
1268 vers = e_xml_get_integer_prop_by_name_with_default (root, (const guchar *)"vers", 0);
1269 if (vers > 2) {
1270 xmlFreeDoc (doc);
1271 return NULL;
1273 model_default = e_tree_model_get_expanded_default (etta->priv->source_model);
1274 saved_default = e_xml_get_bool_prop_by_name_with_default (root, (const guchar *)"default", !model_default);
1275 if (saved_default != model_default) {
1276 xmlFreeDoc (doc);
1277 return NULL;
1280 return doc;
1283 /* state: <0 ... collapse; 0 ... use default; >0 ... expand */
1284 void
1285 e_tree_table_adapter_force_expanded_state (ETreeTableAdapter *etta,
1286 gint state)
1288 g_return_if_fail (E_IS_TREE_TABLE_ADAPTER (etta));
1290 etta->priv->force_expanded_state = state;
1293 void
1294 e_tree_table_adapter_load_expanded_state_xml (ETreeTableAdapter *etta,
1295 xmlDoc *doc)
1297 xmlNode *root, *child;
1298 gboolean model_default;
1299 gboolean file_default = FALSE;
1301 g_return_if_fail (E_IS_TREE_TABLE_ADAPTER (etta));
1302 g_return_if_fail (doc != NULL);
1304 root = xmlDocGetRootElement (doc);
1306 e_table_model_pre_change (E_TABLE_MODEL (etta));
1308 model_default = e_tree_model_get_expanded_default (etta->priv->source_model);
1310 if (!strcmp ((gchar *) root->name, "expanded_state")) {
1311 gchar *state;
1313 state = e_xml_get_string_prop_by_name_with_default (root, (const guchar *)"default", "");
1315 if (state[0] == 't')
1316 file_default = TRUE;
1317 else
1318 file_default = FALSE; /* Even unspecified we'll consider as false */
1320 g_free (state);
1323 /* Incase the default is changed, lets forget the changes and stick to default */
1325 if (file_default != model_default) {
1326 xmlFreeDoc (doc);
1327 return;
1330 for (child = root->xmlChildrenNode; child; child = child->next) {
1331 gchar *id;
1332 ETreePath path;
1334 if (strcmp ((gchar *) child->name, "node")) {
1335 d (g_warning ("unknown node '%s' in %s", child->name, filename));
1336 continue;
1339 id = e_xml_get_string_prop_by_name_with_default (child, (const guchar *)"id", "");
1341 if (!strcmp (id, "")) {
1342 g_free (id);
1343 continue;
1346 path = e_tree_model_get_node_by_id (etta->priv->source_model, id);
1347 if (path)
1348 e_tree_table_adapter_node_set_expanded (etta, path, !model_default);
1350 g_free (id);
1353 e_table_model_changed (E_TABLE_MODEL (etta));
1356 void
1357 e_tree_table_adapter_load_expanded_state (ETreeTableAdapter *etta,
1358 const gchar *filename)
1360 xmlDoc *doc;
1362 g_return_if_fail (E_IS_TREE_TABLE_ADAPTER (etta));
1364 doc = open_file (etta, filename);
1365 if (!doc)
1366 return;
1368 e_tree_table_adapter_load_expanded_state_xml (etta, doc);
1370 xmlFreeDoc (doc);
1373 void
1374 e_tree_table_adapter_root_node_set_visible (ETreeTableAdapter *etta,
1375 gboolean visible)
1377 gint size;
1379 g_return_if_fail (E_IS_TREE_TABLE_ADAPTER (etta));
1381 if (etta->priv->root_visible == visible)
1382 return;
1384 e_table_model_pre_change (E_TABLE_MODEL (etta));
1386 etta->priv->root_visible = visible;
1387 if (!visible) {
1388 ETreePath root = e_tree_model_get_root (etta->priv->source_model);
1389 if (root)
1390 e_tree_table_adapter_node_set_expanded (etta, root, TRUE);
1392 size = (visible ? 1 : 0) + (etta->priv->root ? ((node_t *) etta->priv->root->data)->num_visible_children : 0);
1393 resize_map (etta, size);
1394 if (etta->priv->root)
1395 fill_map (etta, 0, etta->priv->root);
1396 e_table_model_changed (E_TABLE_MODEL (etta));
1399 void
1400 e_tree_table_adapter_node_set_expanded (ETreeTableAdapter *etta,
1401 ETreePath path,
1402 gboolean expanded)
1404 GNode *gnode;
1405 node_t *node;
1406 gint row;
1408 g_return_if_fail (E_IS_TREE_TABLE_ADAPTER (etta));
1410 gnode = lookup_gnode (etta, path);
1412 if (!expanded && (!gnode || (e_tree_model_node_is_root (etta->priv->source_model, path) && !etta->priv->root_visible)))
1413 return;
1415 if (!gnode && expanded) {
1416 ETreePath parent = e_tree_model_node_get_parent (etta->priv->source_model, path);
1417 g_return_if_fail (parent != NULL);
1418 e_tree_table_adapter_node_set_expanded (etta, parent, expanded);
1419 gnode = lookup_gnode (etta, path);
1421 g_return_if_fail (gnode != NULL);
1423 node = (node_t *) gnode->data;
1425 if (expanded == node->expanded)
1426 return;
1428 node->expanded = expanded;
1430 row = e_tree_table_adapter_row_of_node (etta, path);
1431 if (row == -1)
1432 return;
1434 e_table_model_pre_change (E_TABLE_MODEL (etta));
1435 e_table_model_pre_change (E_TABLE_MODEL (etta));
1436 e_table_model_row_changed (E_TABLE_MODEL (etta), row);
1438 if (expanded) {
1439 gint num_children = insert_children (etta, gnode);
1440 update_child_counts (gnode, num_children);
1441 if (etta->priv->sort_info && e_table_sort_info_sorting_get_count (etta->priv->sort_info) > 0)
1442 resort_node (etta, gnode, TRUE);
1443 resize_map (etta, etta->priv->n_map + num_children);
1444 move_map_elements (etta, row + 1 + num_children, row + 1, etta->priv->n_map - row - 1 - num_children);
1445 fill_map (etta, row, gnode);
1446 if (num_children != 0) {
1447 e_table_model_rows_inserted (E_TABLE_MODEL (etta), row + 1, num_children);
1448 } else
1449 e_table_model_no_change (E_TABLE_MODEL (etta));
1450 } else {
1451 gint num_children = delete_children (etta, gnode);
1452 if (num_children == 0) {
1453 e_table_model_no_change (E_TABLE_MODEL (etta));
1454 return;
1456 move_map_elements (etta, row + 1, row + 1 + num_children, etta->priv->n_map - row - 1 - num_children);
1457 update_child_counts (gnode, - num_children);
1458 resize_map (etta, etta->priv->n_map - num_children);
1459 e_table_model_rows_deleted (E_TABLE_MODEL (etta), row + 1, num_children);
1463 void
1464 e_tree_table_adapter_node_set_expanded_recurse (ETreeTableAdapter *etta,
1465 ETreePath path,
1466 gboolean expanded)
1468 ETreePath children;
1470 g_return_if_fail (E_IS_TREE_TABLE_ADAPTER (etta));
1472 e_tree_table_adapter_node_set_expanded (etta, path, expanded);
1474 for (children = e_tree_model_node_get_first_child (etta->priv->source_model, path);
1475 children;
1476 children = e_tree_model_node_get_next (etta->priv->source_model, children)) {
1477 e_tree_table_adapter_node_set_expanded_recurse (etta, children, expanded);
1481 ETreePath
1482 e_tree_table_adapter_node_at_row (ETreeTableAdapter *etta,
1483 gint row)
1485 g_return_val_if_fail (E_IS_TREE_TABLE_ADAPTER (etta), NULL);
1487 if (row == -1 && etta->priv->n_map > 0)
1488 row = etta->priv->n_map - 1;
1489 else if (row < 0 || row >= etta->priv->n_map)
1490 return NULL;
1492 return etta->priv->map_table[row]->path;
1495 gint
1496 e_tree_table_adapter_row_of_node (ETreeTableAdapter *etta,
1497 ETreePath path)
1499 node_t *node;
1501 g_return_val_if_fail (E_IS_TREE_TABLE_ADAPTER (etta), -1);
1503 node = get_node (etta, path);
1504 if (node == NULL)
1505 return -1;
1507 if (etta->priv->remap_needed)
1508 remap_indices (etta);
1510 return node->index;
1513 gboolean
1514 e_tree_table_adapter_root_node_is_visible (ETreeTableAdapter *etta)
1516 g_return_val_if_fail (E_IS_TREE_TABLE_ADAPTER (etta), FALSE);
1518 return etta->priv->root_visible;
1521 void
1522 e_tree_table_adapter_show_node (ETreeTableAdapter *etta,
1523 ETreePath path)
1525 ETreePath parent;
1527 g_return_if_fail (E_IS_TREE_TABLE_ADAPTER (etta));
1529 parent = e_tree_model_node_get_parent (etta->priv->source_model, path);
1531 while (parent) {
1532 e_tree_table_adapter_node_set_expanded (etta, parent, TRUE);
1533 parent = e_tree_model_node_get_parent (etta->priv->source_model, parent);
1537 gboolean
1538 e_tree_table_adapter_node_is_expanded (ETreeTableAdapter *etta,
1539 ETreePath path)
1541 node_t *node;
1543 g_return_val_if_fail (E_IS_TREE_TABLE_ADAPTER (etta), FALSE);
1545 node = get_node (etta, path);
1546 if (!e_tree_model_node_is_expandable (etta->priv->source_model, path) || !node)
1547 return FALSE;
1549 return node->expanded;
1552 ETreePath
1553 e_tree_table_adapter_node_get_next (ETreeTableAdapter *etta,
1554 ETreePath path)
1556 GNode *node;
1558 g_return_val_if_fail (E_IS_TREE_TABLE_ADAPTER (etta), NULL);
1560 node = lookup_gnode (etta, path);
1562 if (node && node->next)
1563 return ((node_t *) node->next->data)->path;
1565 return NULL;