1 /*
2 * This program is free software; you can redistribute it and/or modify it
3 * under the terms of the GNU Lesser General Public License as published by
4 * the Free Software Foundation.
5 *
6 * This program is distributed in the hope that it will be useful, but
7 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
8 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
9 * for more details.
10 *
11 * You should have received a copy of the GNU Lesser General Public License
12 * along with this program; if not, see <http://www.gnu.org/licenses/>.
13 *
14 *
15 * Authors:
16 * Chris Lahey <clahey@ximian.com>
17 * Chris Toshok <toshok@ximian.com>
18 *
19 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
20 *
21 */
22
23 #include "e-tree-table-adapter.h"
24
25 #include <stdlib.h>
26 #include <string.h>
27
28 #include <glib/gstdio.h>
29
30 #include <libxml/tree.h>
31 #include <libxml/parser.h>
32
33 #include <libedataserver/libedataserver.h>
34
35 #include "e-marshal.h"
36 #include "e-table-sorting-utils.h"
37 #include "e-xml-utils.h"
38
39 #define E_TREE_TABLE_ADAPTER_GET_PRIVATE(obj) \
40 (G_TYPE_INSTANCE_GET_PRIVATE \
41 ((obj), E_TYPE_TREE_TABLE_ADAPTER, ETreeTableAdapterPrivate))
42
43 #define d(x)
44
45 #define INCREMENT_AMOUNT 100
46
47 typedef struct {
48 ETreePath path;
49 guint32 num_visible_children;
50 guint32 index;
51
52 guint expanded : 1;
53 guint expandable : 1;
54 guint expandable_set : 1;
55 } node_t;
56
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;
65
66 ETableSortInfo *sort_info;
67 gulong sort_info_changed_handler_id;
68 ETableSortInfo *children_sort_info;
69 gboolean sort_children_ascending;
70
71 ETableHeader *header;
72
73 gint n_map;
74 gint n_vals_allocated;
75 node_t **map_table;
76 GHashTable *nodes;
77 GNode *root;
78
79 guint root_visible : 1;
80 guint remap_needed : 1;
81
82 gint last_access;
83
84 guint resort_idle_id;
85
86 gint force_expanded_state; /* use this instead of model's default if not 0; <0 ... collapse, >0 ... expand */
87 };
88
89 enum {
90 PROP_0,
91 PROP_HEADER,
92 PROP_SORT_INFO,
93 PROP_SOURCE_MODEL,
94 PROP_SORT_CHILDREN_ASCENDING
95 };
96
97 enum {
98 SORTING_CHANGED,
99 LAST_SIGNAL
100 };
101
102 /* Forward Declarations */
103 static void e_tree_table_adapter_table_model_init
104 (ETableModelInterface *iface);
105
106 static guint signals[LAST_SIGNAL];
107
G_DEFINE_TYPE_WITH_CODE(ETreeTableAdapter,e_tree_table_adapter,G_TYPE_OBJECT,G_IMPLEMENT_INTERFACE (E_TYPE_TABLE_MODEL,e_tree_table_adapter_table_model_init))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))
115
116 static GNode *
117 lookup_gnode (ETreeTableAdapter *etta,
118 ETreePath path)
119 {
120 GNode *gnode;
121
122 if (!path)
123 return NULL;
124
125 gnode = g_hash_table_lookup (etta->priv->nodes, path);
126
127 return gnode;
128 }
129
130 static void
resize_map(ETreeTableAdapter * etta,gint size)131 resize_map (ETreeTableAdapter *etta,
132 gint size)
133 {
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);
137 }
138
139 etta->priv->n_map = size;
140 }
141
142 static void
move_map_elements(ETreeTableAdapter * etta,gint to,gint from,gint count)143 move_map_elements (ETreeTableAdapter *etta,
144 gint to,
145 gint from,
146 gint count)
147 {
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;
152 }
153
154 static gint
fill_map(ETreeTableAdapter * etta,gint index,GNode * gnode)155 fill_map (ETreeTableAdapter *etta,
156 gint index,
157 GNode *gnode)
158 {
159 GNode *p;
160
161 if ((gnode != etta->priv->root) || etta->priv->root_visible)
162 etta->priv->map_table[index++] = gnode->data;
163
164 for (p = gnode->children; p; p = p->next)
165 index = fill_map (etta, index, p);
166
167 etta->priv->remap_needed = TRUE;
168 return index;
169 }
170
171 static void
remap_indices(ETreeTableAdapter * etta)172 remap_indices (ETreeTableAdapter *etta)
173 {
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;
178 }
179
180 static node_t *
get_node(ETreeTableAdapter * etta,ETreePath path)181 get_node (ETreeTableAdapter *etta,
182 ETreePath path)
183 {
184 GNode *gnode = lookup_gnode (etta, path);
185
186 if (!gnode)
187 return NULL;
188
189 return (node_t *) gnode->data;
190 }
191
192 static void
resort_node(ETreeTableAdapter * etta,GNode * gnode,gboolean recurse)193 resort_node (ETreeTableAdapter *etta,
194 GNode *gnode,
195 gboolean recurse)
196 {
197 node_t *node = (node_t *) gnode->data;
198 ETreePath *paths, path;
199 GNode *prev, *curr;
200 gint i, count;
201 gboolean sort_needed;
202
203 g_return_if_fail (node != NULL);
204
205 if (node->num_visible_children == 0)
206 return;
207
208 sort_needed = etta->priv->sort_info && e_table_sort_info_sorting_get_count (etta->priv->sort_info) > 0;
209
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++);
212
213 count = i;
214 if (count <= 1)
215 return;
216
217 paths = g_new0 (ETreePath, count);
218
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;
222
223 if (count > 1 && sort_needed) {
224 ETableSortInfo *use_sort_info;
225
226 use_sort_info = etta->priv->sort_info;
227
228 if (etta->priv->sort_children_ascending && gnode->parent) {
229 if (!etta->priv->children_sort_info) {
230 gint len;
231
232 etta->priv->children_sort_info = e_table_sort_info_duplicate (etta->priv->sort_info);
233
234 len = e_table_sort_info_sorting_get_count (etta->priv->children_sort_info);
235
236 for (i = 0; i < len; i++) {
237 ETableColumnSpecification *spec;
238 GtkSortType sort_type;
239
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);
244 }
245 }
246 }
247
248 use_sort_info = etta->priv->children_sort_info;
249 }
250
251 e_table_sorting_utils_tree_sort (etta->priv->source_model, use_sort_info, etta->priv->header, paths, count);
252 }
253
254 prev = NULL;
255 for (i = 0; i < count; i++) {
256 curr = lookup_gnode (etta, paths[i]);
257 if (!curr)
258 continue;
259
260 if (prev)
261 prev->next = curr;
262 else
263 gnode->children = curr;
264
265 curr->prev = prev;
266 curr->next = NULL;
267 prev = curr;
268 if (recurse)
269 resort_node (etta, curr, recurse);
270 }
271
272 g_free (paths);
273 }
274
275 static void
kill_gnode(GNode * node,ETreeTableAdapter * etta)276 kill_gnode (GNode *node,
277 ETreeTableAdapter *etta)
278 {
279 g_hash_table_remove (etta->priv->nodes, ((node_t *) node->data)->path);
280
281 while (node->children) {
282 GNode *next = node->children->next;
283 kill_gnode (node->children, etta);
284 node->children = next;
285 }
286
287 g_free (node->data);
288 if (node == etta->priv->root)
289 etta->priv->root = NULL;
290 g_node_destroy (node);
291 }
292
293 static void
update_child_counts(GNode * gnode,gint delta)294 update_child_counts (GNode *gnode,
295 gint delta)
296 {
297 while (gnode) {
298 node_t *node = (node_t *) gnode->data;
299 node->num_visible_children += delta;
300 gnode = gnode->parent;
301 }
302 }
303
304 static gint
delete_children(ETreeTableAdapter * etta,GNode * gnode)305 delete_children (ETreeTableAdapter *etta,
306 GNode *gnode)
307 {
308 node_t *node = (node_t *) gnode->data;
309 gint to_remove = node ? node->num_visible_children : 0;
310
311 if (to_remove == 0)
312 return 0;
313
314 while (gnode->children) {
315 GNode *next = gnode->children->next;
316 kill_gnode (gnode->children, etta);
317 gnode->children = next;
318 }
319
320 return to_remove;
321 }
322
323 static void
delete_node(ETreeTableAdapter * etta,ETreePath parent,ETreePath path)324 delete_node (ETreeTableAdapter *etta,
325 ETreePath parent,
326 ETreePath path)
327 {
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);
333
334 e_table_model_pre_change (E_TABLE_MODEL (etta));
335
336 if (row == -1) {
337 e_table_model_no_change (E_TABLE_MODEL (etta));
338 return;
339 }
340
341 to_remove += delete_children (etta, gnode);
342 kill_gnode (gnode, etta);
343
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);
346
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);
350
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);
356 }
357
358 resort_node (etta, parent_gnode, FALSE);
359 }
360
361 e_table_model_rows_deleted (E_TABLE_MODEL (etta), row, to_remove);
362 }
363
364 static GNode *
create_gnode(ETreeTableAdapter * etta,ETreePath path)365 create_gnode (ETreeTableAdapter *etta,
366 ETreePath path)
367 {
368 GNode *gnode;
369 node_t *node;
370
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;
381 }
382
383 static gint
insert_children(ETreeTableAdapter * etta,GNode * gnode)384 insert_children (ETreeTableAdapter *etta,
385 GNode *gnode)
386 {
387 ETreePath path, tmp;
388 gint count = 0;
389 gint pos = 0;
390
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;
401 }
402 g_node_reverse_children (gnode);
403 return count;
404 }
405
406 static void
generate_tree(ETreeTableAdapter * etta,ETreePath path)407 generate_tree (ETreeTableAdapter *etta,
408 ETreePath path)
409 {
410 GNode *gnode;
411 node_t *node;
412 gint size;
413
414 e_table_model_pre_change (E_TABLE_MODEL (etta));
415
416 g_return_if_fail (e_tree_model_node_is_root (etta->priv->source_model, path));
417
418 if (etta->priv->root)
419 kill_gnode (etta->priv->root, etta);
420 resize_map (etta, 0);
421
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);
428
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));
434 }
435
436 static void
insert_node(ETreeTableAdapter * etta,ETreePath parent,ETreePath path)437 insert_node (ETreeTableAdapter *etta,
438 ETreePath parent,
439 ETreePath path)
440 {
441 GNode *gnode, *parent_gnode;
442 node_t *node, *parent_node;
443 gboolean expandable;
444 gint size, row;
445
446 e_table_model_pre_change (E_TABLE_MODEL (etta));
447
448 if (get_node (etta, path)) {
449 e_table_model_no_change (E_TABLE_MODEL (etta));
450 return;
451 }
452
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;
462 }
463
464 parent_node = (node_t *) parent_gnode->data;
465
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);
473 }
474 }
475
476 if (!e_tree_table_adapter_node_is_expanded (etta, parent)) {
477 e_table_model_no_change (E_TABLE_MODEL (etta));
478 return;
479 }
480
481 gnode = create_gnode (etta, path);
482 node = (node_t *) gnode->data;
483
484 if (node->expanded)
485 node->num_visible_children = insert_children (etta, gnode);
486
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);
491
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);
501 }
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);
506 }
507
508 typedef struct {
509 GSList *paths;
510 gboolean expanded;
511 } check_expanded_closure;
512
513 static gboolean
check_expanded(GNode * gnode,gpointer data)514 check_expanded (GNode *gnode,
515 gpointer data)
516 {
517 check_expanded_closure *closure = (check_expanded_closure *) data;
518 node_t *node = (node_t *) gnode->data;
519
520 if (node->expanded != closure->expanded)
521 closure->paths = g_slist_prepend (closure->paths, node->path);
522
523 return FALSE;
524 }
525
526 static void
update_node(ETreeTableAdapter * etta,ETreePath path)527 update_node (ETreeTableAdapter *etta,
528 ETreePath path)
529 {
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;
534
535 closure.expanded = e_tree_model_get_expanded_default (etta->priv->source_model);
536 closure.paths = NULL;
537
538 if (gnode)
539 g_node_traverse (gnode, G_POST_ORDER, G_TRAVERSE_ALL, -1, check_expanded, &closure);
540
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);
546 }
547
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);
551
552 g_slist_free (closure.paths);
553 }
554
555 static void
tree_table_adapter_sort_info_changed_cb(ETableSortInfo * sort_info,ETreeTableAdapter * etta)556 tree_table_adapter_sort_info_changed_cb (ETableSortInfo *sort_info,
557 ETreeTableAdapter *etta)
558 {
559 g_clear_object (&etta->priv->children_sort_info);
560
561 if (!etta->priv->root)
562 return;
563
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;
568
569 g_signal_emit (etta, signals[SORTING_CHANGED], 0, &handled);
570
571 if (handled)
572 return;
573 }
574
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));
579 }
580
581 static void
tree_table_adapter_source_model_pre_change_cb(ETreeModel * source_model,ETreeTableAdapter * etta)582 tree_table_adapter_source_model_pre_change_cb (ETreeModel *source_model,
583 ETreeTableAdapter *etta)
584 {
585 e_table_model_pre_change (E_TABLE_MODEL (etta));
586 }
587
588 static void
tree_table_adapter_source_model_rebuilt_cb(ETreeModel * source_model,ETreeTableAdapter * etta)589 tree_table_adapter_source_model_rebuilt_cb (ETreeModel *source_model,
590 ETreeTableAdapter *etta)
591 {
592 if (!etta->priv->root)
593 return;
594
595 kill_gnode (etta->priv->root, etta);
596 etta->priv->root = NULL;
597
598 g_hash_table_remove_all (etta->priv->nodes);
599 }
600
601 static gboolean
tree_table_adapter_resort_model_idle_cb(gpointer user_data)602 tree_table_adapter_resort_model_idle_cb (gpointer user_data)
603 {
604 ETreeTableAdapter *etta;
605
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;
609
610 return FALSE;
611 }
612
613 static void
tree_table_adapter_source_model_node_changed_cb(ETreeModel * source_model,ETreePath path,ETreeTableAdapter * etta)614 tree_table_adapter_source_model_node_changed_cb (ETreeModel *source_model,
615 ETreePath path,
616 ETreeTableAdapter *etta)
617 {
618 update_node (etta, path);
619 e_table_model_changed (E_TABLE_MODEL (etta));
620
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
623 */
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);
627 }
628
629 static void
tree_table_adapter_source_model_node_data_changed_cb(ETreeModel * source_model,ETreePath path,ETreeTableAdapter * etta)630 tree_table_adapter_source_model_node_data_changed_cb (ETreeModel *source_model,
631 ETreePath path,
632 ETreeTableAdapter *etta)
633 {
634 gint row = e_tree_table_adapter_row_of_node (etta, path);
635
636 if (row == -1) {
637 e_table_model_no_change (E_TABLE_MODEL (etta));
638 return;
639 }
640
641 e_table_model_row_changed (E_TABLE_MODEL (etta), row);
642 }
643
644 static void
tree_table_adapter_source_model_node_inserted_cb(ETreeModel * etm,ETreePath parent,ETreePath child,ETreeTableAdapter * etta)645 tree_table_adapter_source_model_node_inserted_cb (ETreeModel *etm,
646 ETreePath parent,
647 ETreePath child,
648 ETreeTableAdapter *etta)
649 {
650 if (e_tree_model_node_is_root (etm, child))
651 generate_tree (etta, child);
652 else
653 insert_node (etta, parent, child);
654
655 e_table_model_changed (E_TABLE_MODEL (etta));
656 }
657
658 static void
tree_table_adapter_source_model_node_removed_cb(ETreeModel * etm,ETreePath parent,ETreePath child,gint old_position,ETreeTableAdapter * etta)659 tree_table_adapter_source_model_node_removed_cb (ETreeModel *etm,
660 ETreePath parent,
661 ETreePath child,
662 gint old_position,
663 ETreeTableAdapter *etta)
664 {
665 delete_node (etta, parent, child);
666 e_table_model_changed (E_TABLE_MODEL (etta));
667 }
668
669 static void
tree_table_adapter_set_header(ETreeTableAdapter * etta,ETableHeader * header)670 tree_table_adapter_set_header (ETreeTableAdapter *etta,
671 ETableHeader *header)
672 {
673 if (header == NULL)
674 return;
675
676 g_return_if_fail (E_IS_TABLE_HEADER (header));
677 g_return_if_fail (etta->priv->header == NULL);
678
679 etta->priv->header = g_object_ref (header);
680 }
681
682 static void
tree_table_adapter_set_source_model(ETreeTableAdapter * etta,ETreeModel * source_model)683 tree_table_adapter_set_source_model (ETreeTableAdapter *etta,
684 ETreeModel *source_model)
685 {
686 g_return_if_fail (E_IS_TREE_MODEL (source_model));
687 g_return_if_fail (etta->priv->source_model == NULL);
688
689 etta->priv->source_model = g_object_ref (source_model);
690 }
691
692 static void
tree_table_adapter_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)693 tree_table_adapter_set_property (GObject *object,
694 guint property_id,
695 const GValue *value,
696 GParamSpec *pspec)
697 {
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;
704
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;
710
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;
716
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;
722 }
723
724 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
725 }
726
727 static void
tree_table_adapter_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)728 tree_table_adapter_get_property (GObject *object,
729 guint property_id,
730 GValue *value,
731 GParamSpec *pspec)
732 {
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;
740
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;
747
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;
754
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;
761 }
762
763 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
764 }
765
766 static void
tree_table_adapter_dispose(GObject * object)767 tree_table_adapter_dispose (GObject *object)
768 {
769 ETreeTableAdapterPrivate *priv;
770
771 priv = E_TREE_TABLE_ADAPTER_GET_PRIVATE (object);
772
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;
778 }
779
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;
785 }
786
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;
792 }
793
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;
799 }
800
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;
806 }
807
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;
813 }
814
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;
820 }
821
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);
826
827 /* Chain up to parent's dispose() method. */
828 G_OBJECT_CLASS (e_tree_table_adapter_parent_class)->dispose (object);
829 }
830
831 static void
tree_table_adapter_finalize(GObject * object)832 tree_table_adapter_finalize (GObject *object)
833 {
834 ETreeTableAdapterPrivate *priv;
835
836 priv = E_TREE_TABLE_ADAPTER_GET_PRIVATE (object);
837
838 if (priv->resort_idle_id) {
839 g_source_remove (priv->resort_idle_id);
840 priv->resort_idle_id = 0;
841 }
842
843 if (priv->root) {
844 kill_gnode (priv->root, E_TREE_TABLE_ADAPTER (object));
845 priv->root = NULL;
846 }
847
848 g_hash_table_destroy (priv->nodes);
849
850 g_free (priv->map_table);
851
852 /* Chain up to parent's finalize() method. */
853 G_OBJECT_CLASS (e_tree_table_adapter_parent_class)->finalize (object);
854 }
855
856 static void
tree_table_adapter_constructed(GObject * object)857 tree_table_adapter_constructed (GObject *object)
858 {
859 ETreeTableAdapter *etta;
860 ETreeModel *source_model;
861 ETreePath root;
862 gulong handler_id;
863
864 etta = E_TREE_TABLE_ADAPTER (object);
865
866 /* Chain up to parent's constructed() method. */
867 G_OBJECT_CLASS (e_tree_table_adapter_parent_class)->constructed (object);
868
869 source_model = e_tree_table_adapter_get_source_model (etta);
870
871 root = e_tree_model_get_root (source_model);
872 if (root != NULL)
873 generate_tree (etta, root);
874
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;
880
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;
886
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;
892
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;
898
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;
904
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;
910 }
911
912 static gint
tree_table_adapter_column_count(ETableModel * etm)913 tree_table_adapter_column_count (ETableModel *etm)
914 {
915 ETreeTableAdapter *etta = (ETreeTableAdapter *) etm;
916
917 return e_tree_model_column_count (etta->priv->source_model);
918 }
919
920 static gboolean
tree_table_adapter_has_save_id(ETableModel * etm)921 tree_table_adapter_has_save_id (ETableModel *etm)
922 {
923 return TRUE;
924 }
925
926 static gchar *
tree_table_adapter_get_save_id(ETableModel * etm,gint row)927 tree_table_adapter_get_save_id (ETableModel *etm,
928 gint row)
929 {
930 ETreeTableAdapter *etta = (ETreeTableAdapter *) etm;
931
932 return e_tree_model_get_save_id (
933 etta->priv->source_model,
934 e_tree_table_adapter_node_at_row (etta, row));
935 }
936
937 static gint
tree_table_adapter_row_count(ETableModel * etm)938 tree_table_adapter_row_count (ETableModel *etm)
939 {
940 ETreeTableAdapter *etta = (ETreeTableAdapter *) etm;
941
942 return etta->priv->n_map;
943 }
944
945 static gpointer
tree_table_adapter_value_at(ETableModel * etm,gint col,gint row)946 tree_table_adapter_value_at (ETableModel *etm,
947 gint col,
948 gint row)
949 {
950 ETreeTableAdapter *etta = (ETreeTableAdapter *) etm;
951 ETreePath path;
952
953 switch (col) {
954 case -1:
955 if (row == -1)
956 return NULL;
957 return e_tree_table_adapter_node_at_row (etta, row);
958 case -2:
959 return etta->priv->source_model;
960 case -3:
961 return etta;
962 default:
963 path = e_tree_table_adapter_node_at_row (etta, row);
964 if (!path)
965 return NULL;
966
967 return e_tree_model_value_at (etta->priv->source_model, path, col);
968 }
969 }
970
971 static void
tree_table_adapter_set_value_at(ETableModel * etm,gint col,gint row,gconstpointer val)972 tree_table_adapter_set_value_at (ETableModel *etm,
973 gint col,
974 gint row,
975 gconstpointer val)
976 {
977 g_warn_if_reached ();
978 }
979
980 static gboolean
tree_table_adapter_is_cell_editable(ETableModel * etm,gint col,gint row)981 tree_table_adapter_is_cell_editable (ETableModel *etm,
982 gint col,
983 gint row)
984 {
985 return FALSE;
986 }
987
988 static void
tree_table_adapter_append_row(ETableModel * etm,ETableModel * source,gint row)989 tree_table_adapter_append_row (ETableModel *etm,
990 ETableModel *source,
991 gint row)
992 {
993 }
994
995 static gpointer
tree_table_adapter_duplicate_value(ETableModel * etm,gint col,gconstpointer value)996 tree_table_adapter_duplicate_value (ETableModel *etm,
997 gint col,
998 gconstpointer value)
999 {
1000 ETreeTableAdapter *etta = (ETreeTableAdapter *) etm;
1001
1002 return e_tree_model_duplicate_value (etta->priv->source_model, col, value);
1003 }
1004
1005 static void
tree_table_adapter_free_value(ETableModel * etm,gint col,gpointer value)1006 tree_table_adapter_free_value (ETableModel *etm,
1007 gint col,
1008 gpointer value)
1009 {
1010 ETreeTableAdapter *etta = (ETreeTableAdapter *) etm;
1011
1012 e_tree_model_free_value (etta->priv->source_model, col, value);
1013 }
1014
1015 static gpointer
tree_table_adapter_initialize_value(ETableModel * etm,gint col)1016 tree_table_adapter_initialize_value (ETableModel *etm,
1017 gint col)
1018 {
1019 ETreeTableAdapter *etta = (ETreeTableAdapter *) etm;
1020
1021 return e_tree_model_initialize_value (etta->priv->source_model, col);
1022 }
1023
1024 static gboolean
tree_table_adapter_value_is_empty(ETableModel * etm,gint col,gconstpointer value)1025 tree_table_adapter_value_is_empty (ETableModel *etm,
1026 gint col,
1027 gconstpointer value)
1028 {
1029 ETreeTableAdapter *etta = (ETreeTableAdapter *) etm;
1030
1031 return e_tree_model_value_is_empty (etta->priv->source_model, col, value);
1032 }
1033
1034 static gchar *
tree_table_adapter_value_to_string(ETableModel * etm,gint col,gconstpointer value)1035 tree_table_adapter_value_to_string (ETableModel *etm,
1036 gint col,
1037 gconstpointer value)
1038 {
1039 ETreeTableAdapter *etta = (ETreeTableAdapter *) etm;
1040
1041 return e_tree_model_value_to_string (etta->priv->source_model, col, value);
1042 }
1043
1044 static void
e_tree_table_adapter_class_init(ETreeTableAdapterClass * class)1045 e_tree_table_adapter_class_init (ETreeTableAdapterClass *class)
1046 {
1047 GObjectClass *object_class;
1048
1049 g_type_class_add_private (class, sizeof (ETreeTableAdapterPrivate));
1050
1051 object_class = G_OBJECT_CLASS (class);
1052 object_class->set_property = tree_table_adapter_set_property;
1053 object_class->get_property = tree_table_adapter_get_property;
1054 object_class->dispose = tree_table_adapter_dispose;
1055 object_class->finalize = tree_table_adapter_finalize;
1056 object_class->constructed = tree_table_adapter_constructed;
1057
1058 g_object_class_install_property (
1059 object_class,
1060 PROP_HEADER,
1061 g_param_spec_object (
1062 "header",
1063 "Header",
1064 NULL,
1065 E_TYPE_TABLE_HEADER,
1066 G_PARAM_READWRITE |
1067 G_PARAM_CONSTRUCT_ONLY |
1068 G_PARAM_STATIC_STRINGS));
1069
1070 g_object_class_install_property (
1071 object_class,
1072 PROP_SORT_INFO,
1073 g_param_spec_object (
1074 "sort-info",
1075 "Sort Info",
1076 NULL,
1077 E_TYPE_TABLE_SORT_INFO,
1078 G_PARAM_READWRITE |
1079 G_PARAM_CONSTRUCT |
1080 G_PARAM_STATIC_STRINGS));
1081
1082 g_object_class_install_property (
1083 object_class,
1084 PROP_SOURCE_MODEL,
1085 g_param_spec_object (
1086 "source-model",
1087 "Source Model",
1088 NULL,
1089 E_TYPE_TREE_MODEL,
1090 G_PARAM_READWRITE |
1091 G_PARAM_CONSTRUCT_ONLY |
1092 G_PARAM_STATIC_STRINGS));
1093
1094 g_object_class_install_property (
1095 object_class,
1096 PROP_SORT_CHILDREN_ASCENDING,
1097 g_param_spec_boolean (
1098 "sort-children-ascending",
1099 "Sort Children Ascending",
1100 NULL,
1101 FALSE,
1102 G_PARAM_READWRITE |
1103 G_PARAM_CONSTRUCT |
1104 G_PARAM_STATIC_STRINGS));
1105
1106 signals[SORTING_CHANGED] = g_signal_new (
1107 "sorting_changed",
1108 G_OBJECT_CLASS_TYPE (object_class),
1109 G_SIGNAL_RUN_LAST,
1110 G_STRUCT_OFFSET (ETreeTableAdapterClass, sorting_changed),
1111 NULL, NULL,
1112 e_marshal_BOOLEAN__VOID,
1113 G_TYPE_BOOLEAN, 0,
1114 G_TYPE_NONE);
1115 }
1116
1117 static void
e_tree_table_adapter_table_model_init(ETableModelInterface * iface)1118 e_tree_table_adapter_table_model_init (ETableModelInterface *iface)
1119 {
1120 iface->column_count = tree_table_adapter_column_count;
1121 iface->row_count = tree_table_adapter_row_count;
1122 iface->append_row = tree_table_adapter_append_row;
1123
1124 iface->value_at = tree_table_adapter_value_at;
1125 iface->set_value_at = tree_table_adapter_set_value_at;
1126 iface->is_cell_editable = tree_table_adapter_is_cell_editable;
1127
1128 iface->has_save_id = tree_table_adapter_has_save_id;
1129 iface->get_save_id = tree_table_adapter_get_save_id;
1130
1131 iface->duplicate_value = tree_table_adapter_duplicate_value;
1132 iface->free_value = tree_table_adapter_free_value;
1133 iface->initialize_value = tree_table_adapter_initialize_value;
1134 iface->value_is_empty = tree_table_adapter_value_is_empty;
1135 iface->value_to_string = tree_table_adapter_value_to_string;
1136 }
1137
1138 static void
e_tree_table_adapter_init(ETreeTableAdapter * etta)1139 e_tree_table_adapter_init (ETreeTableAdapter *etta)
1140 {
1141 etta->priv = E_TREE_TABLE_ADAPTER_GET_PRIVATE (etta);
1142
1143 etta->priv->nodes = g_hash_table_new (NULL, NULL);
1144
1145 etta->priv->root_visible = TRUE;
1146 etta->priv->remap_needed = TRUE;
1147 }
1148
1149 ETableModel *
e_tree_table_adapter_new(ETreeModel * source_model,ETableSortInfo * sort_info,ETableHeader * header)1150 e_tree_table_adapter_new (ETreeModel *source_model,
1151 ETableSortInfo *sort_info,
1152 ETableHeader *header)
1153 {
1154 g_return_val_if_fail (E_IS_TREE_MODEL (source_model), NULL);
1155
1156 if (sort_info != NULL)
1157 g_return_val_if_fail (E_IS_TABLE_SORT_INFO (sort_info), NULL);
1158
1159 if (header != NULL)
1160 g_return_val_if_fail (E_IS_TABLE_HEADER (header), NULL);
1161
1162 return g_object_new (
1163 E_TYPE_TREE_TABLE_ADAPTER,
1164 "source-model", source_model,
1165 "sort-info", sort_info,
1166 "header", header,
1167 NULL);
1168 }
1169
1170 ETableHeader *
e_tree_table_adapter_get_header(ETreeTableAdapter * etta)1171 e_tree_table_adapter_get_header (ETreeTableAdapter *etta)
1172 {
1173 g_return_val_if_fail (E_IS_TREE_TABLE_ADAPTER (etta), NULL);
1174
1175 return etta->priv->header;
1176 }
1177
1178 ETableSortInfo *
e_tree_table_adapter_get_sort_info(ETreeTableAdapter * etta)1179 e_tree_table_adapter_get_sort_info (ETreeTableAdapter *etta)
1180 {
1181 g_return_val_if_fail (E_IS_TREE_TABLE_ADAPTER (etta), NULL);
1182
1183 return etta->priv->sort_info;
1184 }
1185
1186 void
e_tree_table_adapter_set_sort_info(ETreeTableAdapter * etta,ETableSortInfo * sort_info)1187 e_tree_table_adapter_set_sort_info (ETreeTableAdapter *etta,
1188 ETableSortInfo *sort_info)
1189 {
1190 g_return_if_fail (E_IS_TREE_TABLE_ADAPTER (etta));
1191
1192 if (sort_info != NULL) {
1193 g_return_if_fail (E_IS_TABLE_SORT_INFO (sort_info));
1194 g_object_ref (sort_info);
1195 }
1196
1197 if (etta->priv->sort_info != NULL) {
1198 g_signal_handler_disconnect (
1199 etta->priv->sort_info,
1200 etta->priv->sort_info_changed_handler_id);
1201 etta->priv->sort_info_changed_handler_id = 0;
1202
1203 g_clear_object (&etta->priv->sort_info);
1204 }
1205
1206 etta->priv->sort_info = sort_info;
1207
1208 if (etta->priv->sort_info != NULL) {
1209 gulong handler_id;
1210
1211 handler_id = g_signal_connect (
1212 etta->priv->sort_info, "sort_info_changed",
1213 G_CALLBACK (tree_table_adapter_sort_info_changed_cb),
1214 etta);
1215 etta->priv->sort_info_changed_handler_id = handler_id;
1216 }
1217
1218 g_clear_object (&etta->priv->children_sort_info);
1219
1220 g_object_notify (G_OBJECT (etta), "sort-info");
1221
1222 if (etta->priv->root == NULL)
1223 return;
1224
1225 e_table_model_pre_change (E_TABLE_MODEL (etta));
1226 resort_node (etta, etta->priv->root, TRUE);
1227 fill_map (etta, 0, etta->priv->root);
1228 e_table_model_changed (E_TABLE_MODEL (etta));
1229 }
1230
1231 gboolean
e_tree_table_adapter_get_sort_children_ascending(ETreeTableAdapter * etta)1232 e_tree_table_adapter_get_sort_children_ascending (ETreeTableAdapter *etta)
1233 {
1234 g_return_val_if_fail (E_IS_TREE_TABLE_ADAPTER (etta), FALSE);
1235
1236 return etta->priv->sort_children_ascending;
1237 }
1238
1239 void
e_tree_table_adapter_set_sort_children_ascending(ETreeTableAdapter * etta,gboolean sort_children_ascending)1240 e_tree_table_adapter_set_sort_children_ascending (ETreeTableAdapter *etta,
1241 gboolean sort_children_ascending)
1242 {
1243 g_return_if_fail (E_IS_TREE_TABLE_ADAPTER (etta));
1244
1245 if ((etta->priv->sort_children_ascending ? 1 : 0) == (sort_children_ascending ? 1 : 0))
1246 return;
1247
1248 etta->priv->sort_children_ascending = sort_children_ascending;
1249 g_clear_object (&etta->priv->children_sort_info);
1250
1251 g_object_notify (G_OBJECT (etta), "sort-children-ascending");
1252
1253 if (!etta->priv->root)
1254 return;
1255
1256 e_table_model_pre_change (E_TABLE_MODEL (etta));
1257 resort_node (etta, etta->priv->root, TRUE);
1258 fill_map (etta, 0, etta->priv->root);
1259 e_table_model_changed (E_TABLE_MODEL (etta));
1260 }
1261
1262 ETreeModel *
e_tree_table_adapter_get_source_model(ETreeTableAdapter * etta)1263 e_tree_table_adapter_get_source_model (ETreeTableAdapter *etta)
1264 {
1265 g_return_val_if_fail (E_IS_TREE_TABLE_ADAPTER (etta), NULL);
1266
1267 return etta->priv->source_model;
1268 }
1269
1270 typedef struct {
1271 xmlNode *root;
1272 gboolean expanded_default;
1273 ETreeModel *model;
1274 } TreeAndRoot;
1275
1276 static void
save_expanded_state_func(gpointer keyp,gpointer value,gpointer data)1277 save_expanded_state_func (gpointer keyp,
1278 gpointer value,
1279 gpointer data)
1280 {
1281 ETreePath path = keyp;
1282 node_t *node = ((GNode *) value)->data;
1283 TreeAndRoot *tar = data;
1284 xmlNode *xmlnode;
1285
1286 if (node->expanded != tar->expanded_default) {
1287 gchar *save_id = e_tree_model_get_save_id (tar->model, path);
1288 xmlnode = xmlNewChild (tar->root, NULL, (const guchar *)"node", NULL);
1289 e_xml_set_string_prop_by_name (xmlnode, (const guchar *)"id", save_id);
1290 g_free (save_id);
1291 }
1292 }
1293
1294 xmlDoc *
e_tree_table_adapter_save_expanded_state_xml(ETreeTableAdapter * etta)1295 e_tree_table_adapter_save_expanded_state_xml (ETreeTableAdapter *etta)
1296 {
1297 TreeAndRoot tar;
1298 xmlDocPtr doc;
1299 xmlNode *root;
1300
1301 g_return_val_if_fail (E_IS_TREE_TABLE_ADAPTER (etta), NULL);
1302
1303 doc = xmlNewDoc ((const guchar *)"1.0");
1304 root = xmlNewDocNode (doc, NULL, (const guchar *)"expanded_state", NULL);
1305 xmlDocSetRootElement (doc, root);
1306
1307 tar.model = etta->priv->source_model;
1308 tar.root = root;
1309 tar.expanded_default = e_tree_model_get_expanded_default (etta->priv->source_model);
1310
1311 e_xml_set_integer_prop_by_name (root, (const guchar *)"vers", 2);
1312 e_xml_set_bool_prop_by_name (root, (const guchar *)"default", tar.expanded_default);
1313
1314 g_hash_table_foreach (etta->priv->nodes, save_expanded_state_func, &tar);
1315
1316 return doc;
1317 }
1318
1319 void
e_tree_table_adapter_save_expanded_state(ETreeTableAdapter * etta,const gchar * filename)1320 e_tree_table_adapter_save_expanded_state (ETreeTableAdapter *etta,
1321 const gchar *filename)
1322 {
1323 xmlDoc *doc;
1324
1325 g_return_if_fail (E_IS_TREE_TABLE_ADAPTER (etta));
1326
1327 doc = e_tree_table_adapter_save_expanded_state_xml (etta);
1328 if (doc) {
1329 e_xml_save_file (filename, doc);
1330 xmlFreeDoc (doc);
1331 }
1332 }
1333
1334 static xmlDoc *
open_file(ETreeTableAdapter * etta,const gchar * filename)1335 open_file (ETreeTableAdapter *etta,
1336 const gchar *filename)
1337 {
1338 xmlDoc *doc;
1339 xmlNode *root;
1340 gint vers;
1341 gboolean model_default, saved_default;
1342
1343 if (!g_file_test (filename, G_FILE_TEST_EXISTS))
1344 return NULL;
1345
1346 #ifdef G_OS_WIN32
1347 {
1348 gchar *locale_filename = g_win32_locale_filename_from_utf8 (filename);
1349 doc = xmlParseFile (locale_filename);
1350 g_free (locale_filename);
1351 }
1352 #else
1353 doc = xmlParseFile (filename);
1354 #endif
1355
1356 if (!doc)
1357 return NULL;
1358
1359 root = xmlDocGetRootElement (doc);
1360 if (root == NULL || strcmp ((gchar *) root->name, "expanded_state")) {
1361 xmlFreeDoc (doc);
1362 return NULL;
1363 }
1364
1365 vers = e_xml_get_integer_prop_by_name_with_default (root, (const guchar *)"vers", 0);
1366 if (vers > 2) {
1367 xmlFreeDoc (doc);
1368 return NULL;
1369 }
1370 model_default = e_tree_model_get_expanded_default (etta->priv->source_model);
1371 saved_default = e_xml_get_bool_prop_by_name_with_default (root, (const guchar *)"default", !model_default);
1372 if (saved_default != model_default) {
1373 xmlFreeDoc (doc);
1374 return NULL;
1375 }
1376
1377 return doc;
1378 }
1379
1380 /* state: <0 ... collapse; 0 ... use default; >0 ... expand */
1381 void
e_tree_table_adapter_force_expanded_state(ETreeTableAdapter * etta,gint state)1382 e_tree_table_adapter_force_expanded_state (ETreeTableAdapter *etta,
1383 gint state)
1384 {
1385 g_return_if_fail (E_IS_TREE_TABLE_ADAPTER (etta));
1386
1387 etta->priv->force_expanded_state = state;
1388 }
1389
1390 void
e_tree_table_adapter_load_expanded_state_xml(ETreeTableAdapter * etta,xmlDoc * doc)1391 e_tree_table_adapter_load_expanded_state_xml (ETreeTableAdapter *etta,
1392 xmlDoc *doc)
1393 {
1394 xmlNode *root, *child;
1395 gboolean model_default;
1396 gboolean file_default = FALSE;
1397
1398 g_return_if_fail (E_IS_TREE_TABLE_ADAPTER (etta));
1399 g_return_if_fail (doc != NULL);
1400
1401 root = xmlDocGetRootElement (doc);
1402
1403 e_table_model_pre_change (E_TABLE_MODEL (etta));
1404
1405 model_default = e_tree_model_get_expanded_default (etta->priv->source_model);
1406
1407 if (!strcmp ((gchar *) root->name, "expanded_state")) {
1408 gchar *state;
1409
1410 state = e_xml_get_string_prop_by_name_with_default (root, (const guchar *)"default", "");
1411
1412 if (state[0] == 't')
1413 file_default = TRUE;
1414 else
1415 file_default = FALSE; /* Even unspecified we'll consider as false */
1416
1417 g_free (state);
1418 }
1419
1420 /* Incase the default is changed, lets forget the changes and stick to default */
1421
1422 if (file_default != model_default)
1423 return;
1424
1425 for (child = root->xmlChildrenNode; child; child = child->next) {
1426 gchar *id;
1427 ETreePath path;
1428
1429 if (strcmp ((gchar *) child->name, "node")) {
1430 d (g_warning ("unknown node '%s' in %s", child->name, filename));
1431 continue;
1432 }
1433
1434 id = e_xml_get_string_prop_by_name_with_default (child, (const guchar *)"id", "");
1435
1436 if (!strcmp (id, "")) {
1437 g_free (id);
1438 continue;
1439 }
1440
1441 path = e_tree_model_get_node_by_id (etta->priv->source_model, id);
1442 if (path)
1443 e_tree_table_adapter_node_set_expanded (etta, path, !model_default);
1444
1445 g_free (id);
1446 }
1447
1448 e_table_model_changed (E_TABLE_MODEL (etta));
1449 }
1450
1451 void
e_tree_table_adapter_load_expanded_state(ETreeTableAdapter * etta,const gchar * filename)1452 e_tree_table_adapter_load_expanded_state (ETreeTableAdapter *etta,
1453 const gchar *filename)
1454 {
1455 xmlDoc *doc;
1456
1457 g_return_if_fail (E_IS_TREE_TABLE_ADAPTER (etta));
1458
1459 doc = open_file (etta, filename);
1460 if (!doc)
1461 return;
1462
1463 e_tree_table_adapter_load_expanded_state_xml (etta, doc);
1464
1465 xmlFreeDoc (doc);
1466 }
1467
1468 void
e_tree_table_adapter_root_node_set_visible(ETreeTableAdapter * etta,gboolean visible)1469 e_tree_table_adapter_root_node_set_visible (ETreeTableAdapter *etta,
1470 gboolean visible)
1471 {
1472 gint size;
1473
1474 g_return_if_fail (E_IS_TREE_TABLE_ADAPTER (etta));
1475
1476 if (etta->priv->root_visible == visible)
1477 return;
1478
1479 e_table_model_pre_change (E_TABLE_MODEL (etta));
1480
1481 etta->priv->root_visible = visible;
1482 if (!visible) {
1483 ETreePath root = e_tree_model_get_root (etta->priv->source_model);
1484 if (root)
1485 e_tree_table_adapter_node_set_expanded (etta, root, TRUE);
1486 }
1487 size = (visible ? 1 : 0) + (etta->priv->root ? ((node_t *) etta->priv->root->data)->num_visible_children : 0);
1488 resize_map (etta, size);
1489 if (etta->priv->root)
1490 fill_map (etta, 0, etta->priv->root);
1491 e_table_model_changed (E_TABLE_MODEL (etta));
1492 }
1493
1494 void
e_tree_table_adapter_node_set_expanded(ETreeTableAdapter * etta,ETreePath path,gboolean expanded)1495 e_tree_table_adapter_node_set_expanded (ETreeTableAdapter *etta,
1496 ETreePath path,
1497 gboolean expanded)
1498 {
1499 GNode *gnode;
1500 node_t *node;
1501 gint row;
1502
1503 g_return_if_fail (E_IS_TREE_TABLE_ADAPTER (etta));
1504
1505 gnode = lookup_gnode (etta, path);
1506
1507 if (!expanded && (!gnode || (e_tree_model_node_is_root (etta->priv->source_model, path) && !etta->priv->root_visible)))
1508 return;
1509
1510 if (!gnode && expanded) {
1511 ETreePath parent = e_tree_model_node_get_parent (etta->priv->source_model, path);
1512 g_return_if_fail (parent != NULL);
1513 e_tree_table_adapter_node_set_expanded (etta, parent, expanded);
1514 gnode = lookup_gnode (etta, path);
1515 }
1516 g_return_if_fail (gnode != NULL);
1517
1518 node = (node_t *) gnode->data;
1519
1520 if (expanded == node->expanded)
1521 return;
1522
1523 node->expanded = expanded;
1524
1525 row = e_tree_table_adapter_row_of_node (etta, path);
1526 if (row == -1)
1527 return;
1528
1529 e_table_model_pre_change (E_TABLE_MODEL (etta));
1530 e_table_model_pre_change (E_TABLE_MODEL (etta));
1531 e_table_model_row_changed (E_TABLE_MODEL (etta), row);
1532
1533 if (expanded) {
1534 gint num_children = insert_children (etta, gnode);
1535 update_child_counts (gnode, num_children);
1536 if (etta->priv->sort_info && e_table_sort_info_sorting_get_count (etta->priv->sort_info) > 0)
1537 resort_node (etta, gnode, TRUE);
1538 resize_map (etta, etta->priv->n_map + num_children);
1539 move_map_elements (etta, row + 1 + num_children, row + 1, etta->priv->n_map - row - 1 - num_children);
1540 fill_map (etta, row, gnode);
1541 if (num_children != 0) {
1542 e_table_model_rows_inserted (E_TABLE_MODEL (etta), row + 1, num_children);
1543 } else
1544 e_table_model_no_change (E_TABLE_MODEL (etta));
1545 } else {
1546 gint num_children = delete_children (etta, gnode);
1547 if (num_children == 0) {
1548 e_table_model_no_change (E_TABLE_MODEL (etta));
1549 return;
1550 }
1551 move_map_elements (etta, row + 1, row + 1 + num_children, etta->priv->n_map - row - 1 - num_children);
1552 update_child_counts (gnode, - num_children);
1553 resize_map (etta, etta->priv->n_map - num_children);
1554 e_table_model_rows_deleted (E_TABLE_MODEL (etta), row + 1, num_children);
1555 }
1556 }
1557
1558 void
e_tree_table_adapter_node_set_expanded_recurse(ETreeTableAdapter * etta,ETreePath path,gboolean expanded)1559 e_tree_table_adapter_node_set_expanded_recurse (ETreeTableAdapter *etta,
1560 ETreePath path,
1561 gboolean expanded)
1562 {
1563 ETreePath children;
1564
1565 g_return_if_fail (E_IS_TREE_TABLE_ADAPTER (etta));
1566
1567 e_tree_table_adapter_node_set_expanded (etta, path, expanded);
1568
1569 for (children = e_tree_model_node_get_first_child (etta->priv->source_model, path);
1570 children;
1571 children = e_tree_model_node_get_next (etta->priv->source_model, children)) {
1572 e_tree_table_adapter_node_set_expanded_recurse (etta, children, expanded);
1573 }
1574 }
1575
1576 ETreePath
e_tree_table_adapter_node_at_row(ETreeTableAdapter * etta,gint row)1577 e_tree_table_adapter_node_at_row (ETreeTableAdapter *etta,
1578 gint row)
1579 {
1580 g_return_val_if_fail (E_IS_TREE_TABLE_ADAPTER (etta), NULL);
1581
1582 if (row == -1 && etta->priv->n_map > 0)
1583 row = etta->priv->n_map - 1;
1584 else if (row < 0 || row >= etta->priv->n_map)
1585 return NULL;
1586
1587 return etta->priv->map_table[row]->path;
1588 }
1589
1590 gint
e_tree_table_adapter_row_of_node(ETreeTableAdapter * etta,ETreePath path)1591 e_tree_table_adapter_row_of_node (ETreeTableAdapter *etta,
1592 ETreePath path)
1593 {
1594 node_t *node;
1595
1596 g_return_val_if_fail (E_IS_TREE_TABLE_ADAPTER (etta), -1);
1597
1598 node = get_node (etta, path);
1599 if (node == NULL)
1600 return -1;
1601
1602 if (etta->priv->remap_needed)
1603 remap_indices (etta);
1604
1605 return node->index;
1606 }
1607
1608 gboolean
e_tree_table_adapter_root_node_is_visible(ETreeTableAdapter * etta)1609 e_tree_table_adapter_root_node_is_visible (ETreeTableAdapter *etta)
1610 {
1611 g_return_val_if_fail (E_IS_TREE_TABLE_ADAPTER (etta), FALSE);
1612
1613 return etta->priv->root_visible;
1614 }
1615
1616 void
e_tree_table_adapter_show_node(ETreeTableAdapter * etta,ETreePath path)1617 e_tree_table_adapter_show_node (ETreeTableAdapter *etta,
1618 ETreePath path)
1619 {
1620 ETreePath parent;
1621
1622 g_return_if_fail (E_IS_TREE_TABLE_ADAPTER (etta));
1623
1624 parent = e_tree_model_node_get_parent (etta->priv->source_model, path);
1625
1626 while (parent) {
1627 e_tree_table_adapter_node_set_expanded (etta, parent, TRUE);
1628 parent = e_tree_model_node_get_parent (etta->priv->source_model, parent);
1629 }
1630 }
1631
1632 gboolean
e_tree_table_adapter_node_is_expanded(ETreeTableAdapter * etta,ETreePath path)1633 e_tree_table_adapter_node_is_expanded (ETreeTableAdapter *etta,
1634 ETreePath path)
1635 {
1636 node_t *node;
1637
1638 g_return_val_if_fail (E_IS_TREE_TABLE_ADAPTER (etta), FALSE);
1639
1640 node = get_node (etta, path);
1641 if (!e_tree_model_node_is_expandable (etta->priv->source_model, path) || !node)
1642 return FALSE;
1643
1644 return node->expanded;
1645 }
1646
1647 ETreePath
e_tree_table_adapter_node_get_next(ETreeTableAdapter * etta,ETreePath path)1648 e_tree_table_adapter_node_get_next (ETreeTableAdapter *etta,
1649 ETreePath path)
1650 {
1651 GNode *node;
1652
1653 g_return_val_if_fail (E_IS_TREE_TABLE_ADAPTER (etta), NULL);
1654
1655 node = lookup_gnode (etta, path);
1656
1657 if (node && node->next)
1658 return ((node_t *) node->next->data)->path;
1659
1660 return NULL;
1661 }
1662
1663 void
e_tree_table_adapter_clear_nodes_silent(ETreeTableAdapter * etta)1664 e_tree_table_adapter_clear_nodes_silent (ETreeTableAdapter *etta)
1665 {
1666 g_return_if_fail (E_IS_TREE_TABLE_ADAPTER (etta));
1667
1668 if (etta->priv->root)
1669 kill_gnode (etta->priv->root, etta);
1670 resize_map (etta, 0);
1671 }
1672