No issues found
1 /*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU Lesser General Public
4 * License as published by the Free Software Foundation; either
5 * version 2 of the License, or (at your option) version 3.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10 * Lesser General Public License for more details.
11 *
12 * You should have received a copy of the GNU Lesser General Public
13 * License along with the program; if not, see <http://www.gnu.org/licenses/>
14 *
15 *
16 * Authors:
17 * Chris Lahey <clahey@ximian.com>
18 * Chris Toshok <toshok@ximian.com>
19 *
20 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
21 *
22 */
23
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27
28 #include <stdlib.h>
29 #include <string.h>
30
31 #include <glib/gstdio.h>
32 #include <libxml/tree.h>
33 #include <libxml/parser.h>
34
35 #include <libedataserver/libedataserver.h>
36
37 #include "e-util/e-util.h"
38 #include "libevolution-utils/e-xml-utils.h"
39
40 #include "e-table-sorting-utils.h"
41 #include "e-tree-table-adapter.h"
42
43 #define E_TREE_TABLE_ADAPTER_GET_PRIVATE(obj) \
44 (G_TYPE_INSTANCE_GET_PRIVATE \
45 ((obj), E_TYPE_TREE_TABLE_ADAPTER, ETreeTableAdapterPrivate))
46
47 /* workaround for avoiding API breakage */
48 #define etta_get_type e_tree_table_adapter_get_type
49 G_DEFINE_TYPE (ETreeTableAdapter, etta, E_TYPE_TABLE_MODEL)
50 #define d(x)
51
52 #define INCREMENT_AMOUNT 100
53
54 enum {
55 SORTING_CHANGED,
56 LAST_SIGNAL
57 };
58
59 static guint signals[LAST_SIGNAL] = { 0, };
60
61 typedef struct {
62 ETreePath path;
63 guint32 num_visible_children;
64 guint32 index;
65
66 guint expanded : 1;
67 guint expandable : 1;
68 guint expandable_set : 1;
69 } node_t;
70
71 struct _ETreeTableAdapterPrivate {
72 ETreeModel *source;
73 ETableSortInfo *sort_info;
74 ETableHeader *header;
75
76 gint n_map;
77 gint n_vals_allocated;
78 node_t **map_table;
79 GHashTable *nodes;
80 GNode *root;
81
82 guint root_visible : 1;
83 guint remap_needed : 1;
84
85 gint last_access;
86
87 gint pre_change_id;
88 gint no_change_id;
89 gint rebuilt_id;
90 gint node_changed_id;
91 gint node_data_changed_id;
92 gint node_col_changed_id;
93 gint node_inserted_id;
94 gint node_removed_id;
95 gint node_request_collapse_id;
96 gint sort_info_changed_id;
97
98 guint resort_idle_id;
99
100 gint force_expanded_state; /* use this instead of model's default if not 0; <0 ... collapse, >0 ... expand */
101 };
102
103 static void etta_sort_info_changed (ETableSortInfo *sort_info, ETreeTableAdapter *etta);
104
105 static GNode *
106 lookup_gnode (ETreeTableAdapter *etta,
107 ETreePath path)
108 {
109 GNode *gnode;
110
111 if (!path)
112 return NULL;
113
114 gnode = g_hash_table_lookup (etta->priv->nodes, path);
115
116 return gnode;
117 }
118
119 static void
120 resize_map (ETreeTableAdapter *etta,
121 gint size)
122 {
123 if (size > etta->priv->n_vals_allocated) {
124 etta->priv->n_vals_allocated = MAX (etta->priv->n_vals_allocated + INCREMENT_AMOUNT, size);
125 etta->priv->map_table = g_renew (node_t *, etta->priv->map_table, etta->priv->n_vals_allocated);
126 }
127
128 etta->priv->n_map = size;
129 }
130
131 static void
132 move_map_elements (ETreeTableAdapter *etta,
133 gint to,
134 gint from,
135 gint count)
136 {
137 if (count <= 0 || from >= etta->priv->n_map)
138 return;
139 memmove (etta->priv->map_table + to, etta->priv->map_table + from, count * sizeof (node_t *));
140 etta->priv->remap_needed = TRUE;
141 }
142
143 static gint
144 fill_map (ETreeTableAdapter *etta,
145 gint index,
146 GNode *gnode)
147 {
148 GNode *p;
149
150 if ((gnode != etta->priv->root) || etta->priv->root_visible)
151 etta->priv->map_table[index++] = gnode->data;
152
153 for (p = gnode->children; p; p = p->next)
154 index = fill_map (etta, index, p);
155
156 etta->priv->remap_needed = TRUE;
157 return index;
158 }
159
160 static void
161 remap_indices (ETreeTableAdapter *etta)
162 {
163 gint i;
164 for (i = 0; i < etta->priv->n_map; i++)
165 etta->priv->map_table[i]->index = i;
166 etta->priv->remap_needed = FALSE;
167 }
168
169 static node_t *
170 get_node (ETreeTableAdapter *etta,
171 ETreePath path)
172 {
173 GNode *gnode = lookup_gnode (etta, path);
174
175 if (!gnode)
176 return NULL;
177
178 return (node_t *) gnode->data;
179 }
180
181 static void
182 resort_node (ETreeTableAdapter *etta,
183 GNode *gnode,
184 gboolean recurse)
185 {
186 node_t *node = (node_t *) gnode->data;
187 ETreePath *paths, path;
188 GNode *prev, *curr;
189 gint i, count;
190 gboolean sort_needed;
191
192 if (node->num_visible_children == 0)
193 return;
194
195 sort_needed = etta->priv->sort_info && e_table_sort_info_sorting_get_count (etta->priv->sort_info) > 0;
196
197 for (i = 0, path = e_tree_model_node_get_first_child (etta->priv->source, node->path); path;
198 path = e_tree_model_node_get_next (etta->priv->source, path), i++);
199
200 count = i;
201 if (count <= 1)
202 return;
203
204 paths = g_new0 (ETreePath, count);
205
206 for (i = 0, path = e_tree_model_node_get_first_child (etta->priv->source, node->path); path;
207 path = e_tree_model_node_get_next (etta->priv->source, path), i++)
208 paths[i] = path;
209
210 if (count > 1 && sort_needed)
211 e_table_sorting_utils_tree_sort (etta->priv->source, etta->priv->sort_info, etta->priv->header, paths, count);
212
213 prev = NULL;
214 for (i = 0; i < count; i++) {
215 curr = lookup_gnode (etta, paths[i]);
216 if (!curr)
217 continue;
218
219 if (prev)
220 prev->next = curr;
221 else
222 gnode->children = curr;
223
224 curr->prev = prev;
225 curr->next = NULL;
226 prev = curr;
227 if (recurse)
228 resort_node (etta, curr, recurse);
229 }
230
231 g_free (paths);
232 }
233
234 static gint
235 get_row (ETreeTableAdapter *etta,
236 ETreePath path)
237 {
238 node_t *node = get_node (etta, path);
239 if (!node)
240 return -1;
241
242 if (etta->priv->remap_needed)
243 remap_indices (etta);
244
245 return node->index;
246 }
247
248 static ETreePath
249 get_path (ETreeTableAdapter *etta,
250 gint row)
251 {
252 if (row == -1 && etta->priv->n_map > 0)
253 row = etta->priv->n_map - 1;
254 else if (row < 0 || row >= etta->priv->n_map)
255 return NULL;
256
257 return etta->priv->map_table[row]->path;
258 }
259
260 static void
261 kill_gnode (GNode *node,
262 ETreeTableAdapter *etta)
263 {
264 g_hash_table_remove (etta->priv->nodes, ((node_t *) node->data)->path);
265
266 while (node->children) {
267 GNode *next = node->children->next;
268 kill_gnode (node->children, etta);
269 node->children = next;
270 }
271
272 g_free (node->data);
273 if (node == etta->priv->root)
274 etta->priv->root = NULL;
275 g_node_destroy (node);
276 }
277
278 static void
279 update_child_counts (GNode *gnode,
280 gint delta)
281 {
282 while (gnode) {
283 node_t *node = (node_t *) gnode->data;
284 node->num_visible_children += delta;
285 gnode = gnode->parent;
286 }
287 }
288
289 static gint
290 delete_children (ETreeTableAdapter *etta,
291 GNode *gnode)
292 {
293 node_t *node = (node_t *) gnode->data;
294 gint to_remove = node ? node->num_visible_children : 0;
295
296 if (to_remove == 0)
297 return 0;
298
299 while (gnode->children) {
300 GNode *next = gnode->children->next;
301 kill_gnode (gnode->children, etta);
302 gnode->children = next;
303 }
304
305 return to_remove;
306 }
307
308 static void
309 delete_node (ETreeTableAdapter *etta,
310 ETreePath parent,
311 ETreePath path)
312 {
313 gint to_remove = 1;
314 gint parent_row = get_row (etta, parent);
315 gint row = get_row (etta, path);
316 GNode *gnode = lookup_gnode (etta, path);
317 GNode *parent_gnode = lookup_gnode (etta, parent);
318
319 e_table_model_pre_change (E_TABLE_MODEL (etta));
320
321 if (row == -1) {
322 e_table_model_no_change (E_TABLE_MODEL (etta));
323 return;
324 }
325
326 to_remove += delete_children (etta, gnode);
327 kill_gnode (gnode, etta);
328
329 move_map_elements (etta, row, row + to_remove, etta->priv->n_map - row - to_remove);
330 resize_map (etta, etta->priv->n_map - to_remove);
331
332 if (parent_gnode != NULL) {
333 node_t *parent_node = parent_gnode->data;
334 gboolean expandable = e_tree_model_node_is_expandable (etta->priv->source, parent);
335
336 update_child_counts (parent_gnode, - to_remove);
337 if (parent_node->expandable != expandable) {
338 e_table_model_pre_change (E_TABLE_MODEL (etta));
339 parent_node->expandable = expandable;
340 e_table_model_row_changed (E_TABLE_MODEL (etta), parent_row);
341 }
342
343 resort_node (etta, parent_gnode, FALSE);
344 }
345
346 e_table_model_rows_deleted (E_TABLE_MODEL (etta), row, to_remove);
347 }
348
349 static GNode *
350 create_gnode (ETreeTableAdapter *etta,
351 ETreePath path)
352 {
353 GNode *gnode;
354 node_t *node;
355
356 node = g_new0 (node_t, 1);
357 node->path = path;
358 node->index = -1;
359 node->expanded = etta->priv->force_expanded_state == 0 ? e_tree_model_get_expanded_default (etta->priv->source) : etta->priv->force_expanded_state > 0;
360 node->expandable = e_tree_model_node_is_expandable (etta->priv->source, path);
361 node->expandable_set = 1;
362 node->num_visible_children = 0;
363 gnode = g_node_new (node);
364 g_hash_table_insert (etta->priv->nodes, path, gnode);
365 return gnode;
366 }
367
368 static gint
369 insert_children (ETreeTableAdapter *etta,
370 GNode *gnode)
371 {
372 ETreePath path, tmp;
373 gint count = 0;
374 gint pos = 0;
375
376 path = ((node_t *) gnode->data)->path;
377 for (tmp = e_tree_model_node_get_first_child (etta->priv->source, path);
378 tmp;
379 tmp = e_tree_model_node_get_next (etta->priv->source, tmp), pos++) {
380 GNode *child = create_gnode (etta, tmp);
381 node_t *node = (node_t *) child->data;
382 if (node->expanded)
383 node->num_visible_children = insert_children (etta, child);
384 g_node_prepend (gnode, child);
385 count += node->num_visible_children + 1;
386 }
387 g_node_reverse_children (gnode);
388 return count;
389 }
390
391 static void
392 generate_tree (ETreeTableAdapter *etta,
393 ETreePath path)
394 {
395 GNode *gnode;
396 node_t *node;
397 gint size;
398
399 e_table_model_pre_change (E_TABLE_MODEL (etta));
400
401 g_return_if_fail (e_tree_model_node_is_root (etta->priv->source, path));
402
403 if (etta->priv->root)
404 kill_gnode (etta->priv->root, etta);
405 resize_map (etta, 0);
406
407 gnode = create_gnode (etta, path);
408 node = (node_t *) gnode->data;
409 node->expanded = TRUE;
410 node->num_visible_children = insert_children (etta, gnode);
411 if (etta->priv->sort_info && e_table_sort_info_sorting_get_count (etta->priv->sort_info) > 0)
412 resort_node (etta, gnode, TRUE);
413
414 etta->priv->root = gnode;
415 size = etta->priv->root_visible ? node->num_visible_children + 1 : node->num_visible_children;
416 resize_map (etta, size);
417 fill_map (etta, 0, gnode);
418 e_table_model_changed (E_TABLE_MODEL (etta));
419 }
420
421 static void
422 insert_node (ETreeTableAdapter *etta,
423 ETreePath parent,
424 ETreePath path)
425 {
426 GNode *gnode, *parent_gnode;
427 node_t *node, *parent_node;
428 gboolean expandable;
429 gint size, row;
430
431 e_table_model_pre_change (E_TABLE_MODEL (etta));
432
433 if (get_node (etta, path)) {
434 e_table_model_no_change (E_TABLE_MODEL (etta));
435 return;
436 }
437
438 parent_gnode = lookup_gnode (etta, parent);
439 if (!parent_gnode) {
440 ETreePath grandparent = e_tree_model_node_get_parent (etta->priv->source, parent);
441 if (e_tree_model_node_is_root (etta->priv->source, parent))
442 generate_tree (etta, parent);
443 else
444 insert_node (etta, grandparent, parent);
445 e_table_model_changed (E_TABLE_MODEL (etta));
446 return;
447 }
448
449 parent_node = (node_t *) parent_gnode->data;
450
451 if (parent_gnode != etta->priv->root) {
452 expandable = e_tree_model_node_is_expandable (etta->priv->source, parent);
453 if (parent_node->expandable != expandable) {
454 e_table_model_pre_change (E_TABLE_MODEL (etta));
455 parent_node->expandable = expandable;
456 parent_node->expandable_set = 1;
457 e_table_model_row_changed (E_TABLE_MODEL (etta), parent_node->index);
458 }
459 }
460
461 if (!e_tree_table_adapter_node_is_expanded (etta, parent)) {
462 e_table_model_no_change (E_TABLE_MODEL (etta));
463 return;
464 }
465
466 gnode = create_gnode (etta, path);
467 node = (node_t *) gnode->data;
468
469 if (node->expanded)
470 node->num_visible_children = insert_children (etta, gnode);
471
472 g_node_append (parent_gnode, gnode);
473 update_child_counts (parent_gnode, node->num_visible_children + 1);
474 resort_node (etta, parent_gnode, FALSE);
475 resort_node (etta, gnode, TRUE);
476
477 size = node->num_visible_children + 1;
478 resize_map (etta, etta->priv->n_map + size);
479 if (parent_gnode == etta->priv->root)
480 row = 0;
481 else {
482 gint new_size = parent_node->num_visible_children + 1;
483 gint old_size = new_size - size;
484 row = parent_node->index;
485 move_map_elements (etta, row + new_size, row + old_size, etta->priv->n_map - row - new_size);
486 }
487 fill_map (etta, row, parent_gnode);
488 e_table_model_rows_inserted (E_TABLE_MODEL (etta), get_row (etta, path), size);
489 }
490
491 typedef struct {
492 GSList *paths;
493 gboolean expanded;
494 } check_expanded_closure;
495
496 static gboolean
497 check_expanded (GNode *gnode,
498 gpointer data)
499 {
500 check_expanded_closure *closure = (check_expanded_closure *) data;
501 node_t *node = (node_t *) gnode->data;
502
503 if (node->expanded != closure->expanded)
504 closure->paths = g_slist_prepend (closure->paths, node->path);
505
506 return FALSE;
507 }
508
509 static void
510 update_node (ETreeTableAdapter *etta,
511 ETreePath path)
512 {
513 check_expanded_closure closure;
514 ETreePath parent = e_tree_model_node_get_parent (etta->priv->source, path);
515 GNode *gnode = lookup_gnode (etta, path);
516 GSList *l;
517
518 closure.expanded = e_tree_model_get_expanded_default (etta->priv->source);
519 closure.paths = NULL;
520
521 if (gnode)
522 g_node_traverse (gnode, G_POST_ORDER, G_TRAVERSE_ALL, -1, check_expanded, &closure);
523
524 if (e_tree_model_node_is_root (etta->priv->source, path))
525 generate_tree (etta, path);
526 else {
527 delete_node (etta, parent, path);
528 insert_node (etta, parent, path);
529 }
530
531 for (l = closure.paths; l; l = l->next)
532 if (lookup_gnode (etta, l->data))
533 e_tree_table_adapter_node_set_expanded (etta, l->data, !closure.expanded);
534
535 g_slist_free (closure.paths);
536 }
537
538 static void
539 etta_finalize (GObject *object)
540 {
541 ETreeTableAdapterPrivate *priv;
542
543 priv = E_TREE_TABLE_ADAPTER_GET_PRIVATE (object);
544
545 if (priv->resort_idle_id) {
546 g_source_remove (priv->resort_idle_id);
547 priv->resort_idle_id = 0;
548 }
549
550 if (priv->root) {
551 kill_gnode (priv->root, E_TREE_TABLE_ADAPTER (object));
552 priv->root = NULL;
553 }
554
555 g_hash_table_destroy (priv->nodes);
556
557 g_free (priv->map_table);
558
559 /* Chain up to parent's finalize() method. */
560 G_OBJECT_CLASS (etta_parent_class)->finalize (object);
561 }
562
563 static void
564 etta_dispose (GObject *object)
565 {
566 ETreeTableAdapterPrivate *priv;
567
568 priv = E_TREE_TABLE_ADAPTER_GET_PRIVATE (object);
569
570 if (priv->sort_info) {
571 g_signal_handler_disconnect (
572 priv->sort_info, priv->sort_info_changed_id);
573 g_object_unref (priv->sort_info);
574 priv->sort_info = NULL;
575 }
576
577 if (priv->header) {
578 g_object_unref (priv->header);
579 priv->header = NULL;
580 }
581
582 if (priv->source) {
583 g_signal_handler_disconnect (
584 priv->source, priv->pre_change_id);
585 g_signal_handler_disconnect (
586 priv->source, priv->no_change_id);
587 g_signal_handler_disconnect (
588 priv->source, priv->rebuilt_id);
589 g_signal_handler_disconnect (
590 priv->source, priv->node_changed_id);
591 g_signal_handler_disconnect (
592 priv->source, priv->node_data_changed_id);
593 g_signal_handler_disconnect (
594 priv->source, priv->node_col_changed_id);
595 g_signal_handler_disconnect (
596 priv->source, priv->node_inserted_id);
597 g_signal_handler_disconnect (
598 priv->source, priv->node_removed_id);
599 g_signal_handler_disconnect (
600 priv->source, priv->node_request_collapse_id);
601
602 g_object_unref (priv->source);
603 priv->source = NULL;
604 }
605
606 /* Chain up to parent's dispose() method. */
607 G_OBJECT_CLASS (etta_parent_class)->dispose (object);
608 }
609
610 static gint
611 etta_column_count (ETableModel *etm)
612 {
613 ETreeTableAdapter *etta = (ETreeTableAdapter *) etm;
614
615 return e_tree_model_column_count (etta->priv->source);
616 }
617
618 static gboolean
619 etta_has_save_id (ETableModel *etm)
620 {
621 ETreeTableAdapter *etta = (ETreeTableAdapter *) etm;
622
623 return e_tree_model_has_save_id (etta->priv->source);
624 }
625
626 static gchar *
627 etta_get_save_id (ETableModel *etm,
628 gint row)
629 {
630 ETreeTableAdapter *etta = (ETreeTableAdapter *) etm;
631
632 return e_tree_model_get_save_id (etta->priv->source, get_path (etta, row));
633 }
634
635 static gboolean
636 etta_has_change_pending (ETableModel *etm)
637 {
638 ETreeTableAdapter *etta = (ETreeTableAdapter *) etm;
639
640 return e_tree_model_has_change_pending (etta->priv->source);
641 }
642
643 static gint
644 etta_row_count (ETableModel *etm)
645 {
646 ETreeTableAdapter *etta = (ETreeTableAdapter *) etm;
647
648 return etta->priv->n_map;
649 }
650
651 static gpointer
652 etta_value_at (ETableModel *etm,
653 gint col,
654 gint row)
655 {
656 ETreeTableAdapter *etta = (ETreeTableAdapter *) etm;
657
658 switch (col) {
659 case -1:
660 if (row == -1)
661 return NULL;
662 return get_path (etta, row);
663 case -2:
664 return etta->priv->source;
665 case -3:
666 return etta;
667 default:
668 return e_tree_model_value_at (etta->priv->source, get_path (etta, row), col);
669 }
670 }
671
672 static void
673 etta_set_value_at (ETableModel *etm,
674 gint col,
675 gint row,
676 gconstpointer val)
677 {
678 ETreeTableAdapter *etta = (ETreeTableAdapter *) etm;
679
680 e_tree_model_set_value_at (etta->priv->source, get_path (etta, row), col, val);
681 }
682
683 static gboolean
684 etta_is_cell_editable (ETableModel *etm,
685 gint col,
686 gint row)
687 {
688 ETreeTableAdapter *etta = (ETreeTableAdapter *) etm;
689
690 return e_tree_model_node_is_editable (etta->priv->source, get_path (etta, row), col);
691 }
692
693 static void
694 etta_append_row (ETableModel *etm,
695 ETableModel *source,
696 gint row)
697 {
698 }
699
700 static gpointer
701 etta_duplicate_value (ETableModel *etm,
702 gint col,
703 gconstpointer value)
704 {
705 ETreeTableAdapter *etta = (ETreeTableAdapter *) etm;
706
707 return e_tree_model_duplicate_value (etta->priv->source, col, value);
708 }
709
710 static void
711 etta_free_value (ETableModel *etm,
712 gint col,
713 gpointer value)
714 {
715 ETreeTableAdapter *etta = (ETreeTableAdapter *) etm;
716
717 e_tree_model_free_value (etta->priv->source, col, value);
718 }
719
720 static gpointer
721 etta_initialize_value (ETableModel *etm,
722 gint col)
723 {
724 ETreeTableAdapter *etta = (ETreeTableAdapter *) etm;
725
726 return e_tree_model_initialize_value (etta->priv->source, col);
727 }
728
729 static gboolean
730 etta_value_is_empty (ETableModel *etm,
731 gint col,
732 gconstpointer value)
733 {
734 ETreeTableAdapter *etta = (ETreeTableAdapter *) etm;
735
736 return e_tree_model_value_is_empty (etta->priv->source, col, value);
737 }
738
739 static gchar *
740 etta_value_to_string (ETableModel *etm,
741 gint col,
742 gconstpointer value)
743 {
744 ETreeTableAdapter *etta = (ETreeTableAdapter *) etm;
745
746 return e_tree_model_value_to_string (etta->priv->source, col, value);
747 }
748
749 static void
750 etta_class_init (ETreeTableAdapterClass *class)
751 {
752 GObjectClass *object_class;
753 ETableModelClass *table_model_class;
754
755 g_type_class_add_private (class, sizeof (ETreeTableAdapterPrivate));
756
757 object_class = G_OBJECT_CLASS (class);
758 object_class->dispose = etta_dispose;
759 object_class->finalize = etta_finalize;
760
761 table_model_class = E_TABLE_MODEL_CLASS (class);
762 table_model_class->column_count = etta_column_count;
763 table_model_class->row_count = etta_row_count;
764 table_model_class->append_row = etta_append_row;
765
766 table_model_class->value_at = etta_value_at;
767 table_model_class->set_value_at = etta_set_value_at;
768 table_model_class->is_cell_editable = etta_is_cell_editable;
769
770 table_model_class->has_save_id = etta_has_save_id;
771 table_model_class->get_save_id = etta_get_save_id;
772
773 table_model_class->has_change_pending = etta_has_change_pending;
774 table_model_class->duplicate_value = etta_duplicate_value;
775 table_model_class->free_value = etta_free_value;
776 table_model_class->initialize_value = etta_initialize_value;
777 table_model_class->value_is_empty = etta_value_is_empty;
778 table_model_class->value_to_string = etta_value_to_string;
779
780 class->sorting_changed = NULL;
781
782 signals[SORTING_CHANGED] = g_signal_new (
783 "sorting_changed",
784 G_OBJECT_CLASS_TYPE (object_class),
785 G_SIGNAL_RUN_LAST,
786 G_STRUCT_OFFSET (ETreeTableAdapterClass, sorting_changed),
787 NULL, NULL,
788 e_marshal_BOOLEAN__NONE,
789 G_TYPE_BOOLEAN, 0,
790 G_TYPE_NONE);
791 }
792
793 static void
794 etta_init (ETreeTableAdapter *etta)
795 {
796 etta->priv = E_TREE_TABLE_ADAPTER_GET_PRIVATE (etta);
797
798 etta->priv->root_visible = TRUE;
799 etta->priv->remap_needed = TRUE;
800 }
801
802 static void
803 etta_proxy_pre_change (ETreeModel *etm,
804 ETreeTableAdapter *etta)
805 {
806 e_table_model_pre_change (E_TABLE_MODEL (etta));
807 }
808
809 static void
810 etta_proxy_no_change (ETreeModel *etm,
811 ETreeTableAdapter *etta)
812 {
813 e_table_model_no_change (E_TABLE_MODEL (etta));
814 }
815
816 static void
817 etta_proxy_rebuilt (ETreeModel *etm,
818 ETreeTableAdapter *etta)
819 {
820 if (!etta->priv->root)
821 return;
822 kill_gnode (etta->priv->root, etta);
823 etta->priv->root = NULL;
824 g_hash_table_destroy (etta->priv->nodes);
825 etta->priv->nodes = g_hash_table_new (NULL, NULL);
826 }
827
828 static gboolean
829 resort_model (ETreeTableAdapter *etta)
830 {
831 etta_sort_info_changed (NULL, etta);
832 etta->priv->resort_idle_id = 0;
833 return FALSE;
834 }
835
836 static void
837 etta_proxy_node_changed (ETreeModel *etm,
838 ETreePath path,
839 ETreeTableAdapter *etta)
840 {
841 update_node (etta, path);
842 e_table_model_changed (E_TABLE_MODEL (etta));
843
844 /* FIXME: Really it shouldnt be required. But a lot of thread
845 * which were supposed to be present in the list is way below
846 */
847 if (!etta->priv->resort_idle_id)
848 etta->priv->resort_idle_id = g_idle_add ((GSourceFunc) resort_model, etta);
849 }
850
851 static void
852 etta_proxy_node_data_changed (ETreeModel *etm,
853 ETreePath path,
854 ETreeTableAdapter *etta)
855 {
856 gint row = get_row (etta, path);
857
858 if (row == -1) {
859 e_table_model_no_change (E_TABLE_MODEL (etta));
860 return;
861 }
862
863 e_table_model_row_changed (E_TABLE_MODEL (etta), row);
864 }
865
866 static void
867 etta_proxy_node_col_changed (ETreeModel *etm,
868 ETreePath path,
869 gint col,
870 ETreeTableAdapter *etta)
871 {
872 gint row = get_row (etta, path);
873
874 if (row == -1) {
875 e_table_model_no_change (E_TABLE_MODEL (etta));
876 return;
877 }
878
879 e_table_model_cell_changed (E_TABLE_MODEL (etta), col, row);
880 }
881
882 static void
883 etta_proxy_node_inserted (ETreeModel *etm,
884 ETreePath parent,
885 ETreePath child,
886 ETreeTableAdapter *etta)
887 {
888 if (e_tree_model_node_is_root (etm, child))
889 generate_tree (etta, child);
890 else
891 insert_node (etta, parent, child);
892
893 e_table_model_changed (E_TABLE_MODEL (etta));
894 }
895
896 static void
897 etta_proxy_node_removed (ETreeModel *etm,
898 ETreePath parent,
899 ETreePath child,
900 gint old_position,
901 ETreeTableAdapter *etta)
902 {
903 delete_node (etta, parent, child);
904 e_table_model_changed (E_TABLE_MODEL (etta));
905 }
906
907 static void
908 etta_proxy_node_request_collapse (ETreeModel *etm,
909 ETreePath node,
910 ETreeTableAdapter *etta)
911 {
912 e_tree_table_adapter_node_set_expanded (etta, node, FALSE);
913 }
914
915 static void
916 etta_sort_info_changed (ETableSortInfo *sort_info,
917 ETreeTableAdapter *etta)
918 {
919 if (!etta->priv->root)
920 return;
921
922 /* the function is called also internally, with sort_info = NULL,
923 * thus skip those in signal emit */
924 if (sort_info) {
925 gboolean handled = FALSE;
926
927 g_signal_emit (etta, signals[SORTING_CHANGED], 0, &handled);
928
929 if (handled)
930 return;
931 }
932
933 e_table_model_pre_change (E_TABLE_MODEL (etta));
934 resort_node (etta, etta->priv->root, TRUE);
935 fill_map (etta, 0, etta->priv->root);
936 e_table_model_changed (E_TABLE_MODEL (etta));
937 }
938
939 ETableModel *
940 e_tree_table_adapter_construct (ETreeTableAdapter *etta,
941 ETreeModel *source,
942 ETableSortInfo *sort_info,
943 ETableHeader *header)
944 {
945 ETreePath root;
946
947 etta->priv->source = source;
948 g_object_ref (source);
949
950 etta->priv->sort_info = sort_info;
951 if (sort_info) {
952 g_object_ref (sort_info);
953 etta->priv->sort_info_changed_id = g_signal_connect (
954 sort_info, "sort_info_changed",
955 G_CALLBACK (etta_sort_info_changed), etta);
956 }
957
958 etta->priv->header = header;
959 if (header)
960 g_object_ref (header);
961
962 etta->priv->nodes = g_hash_table_new (NULL, NULL);
963
964 root = e_tree_model_get_root (source);
965
966 if (root)
967 generate_tree (etta, root);
968
969 etta->priv->pre_change_id = g_signal_connect (
970 source, "pre_change",
971 G_CALLBACK (etta_proxy_pre_change), etta);
972 etta->priv->no_change_id = g_signal_connect (
973 source, "no_change",
974 G_CALLBACK (etta_proxy_no_change), etta);
975 etta->priv->rebuilt_id = g_signal_connect (
976 source, "rebuilt",
977 G_CALLBACK (etta_proxy_rebuilt), etta);
978 etta->priv->node_changed_id = g_signal_connect (
979 source, "node_changed",
980 G_CALLBACK (etta_proxy_node_changed), etta);
981 etta->priv->node_data_changed_id = g_signal_connect (
982 source, "node_data_changed",
983 G_CALLBACK (etta_proxy_node_data_changed), etta);
984 etta->priv->node_col_changed_id = g_signal_connect (
985 source, "node_col_changed",
986 G_CALLBACK (etta_proxy_node_col_changed), etta);
987 etta->priv->node_inserted_id = g_signal_connect (
988 source, "node_inserted",
989 G_CALLBACK (etta_proxy_node_inserted), etta);
990 etta->priv->node_removed_id = g_signal_connect (
991 source, "node_removed",
992 G_CALLBACK (etta_proxy_node_removed), etta);
993 etta->priv->node_request_collapse_id = g_signal_connect (
994 source, "node_request_collapse",
995 G_CALLBACK (etta_proxy_node_request_collapse), etta);
996
997 return E_TABLE_MODEL (etta);
998 }
999
1000 ETableModel *
1001 e_tree_table_adapter_new (ETreeModel *source,
1002 ETableSortInfo *sort_info,
1003 ETableHeader *header)
1004 {
1005 ETreeTableAdapter *etta = g_object_new (E_TYPE_TREE_TABLE_ADAPTER, NULL);
1006
1007 e_tree_table_adapter_construct (etta, source, sort_info, header);
1008
1009 return (ETableModel *) etta;
1010 }
1011
1012 typedef struct {
1013 xmlNode *root;
1014 gboolean expanded_default;
1015 ETreeModel *model;
1016 } TreeAndRoot;
1017
1018 static void
1019 save_expanded_state_func (gpointer keyp,
1020 gpointer value,
1021 gpointer data)
1022 {
1023 ETreePath path = keyp;
1024 node_t *node = ((GNode *) value)->data;
1025 TreeAndRoot *tar = data;
1026 xmlNode *xmlnode;
1027
1028 if (node->expanded != tar->expanded_default) {
1029 gchar *save_id = e_tree_model_get_save_id (tar->model, path);
1030 xmlnode = xmlNewChild (tar->root, NULL, (const guchar *)"node", NULL);
1031 e_xml_set_string_prop_by_name (xmlnode, (const guchar *)"id", save_id);
1032 g_free (save_id);
1033 }
1034 }
1035
1036 xmlDoc *
1037 e_tree_table_adapter_save_expanded_state_xml (ETreeTableAdapter *etta)
1038 {
1039 TreeAndRoot tar;
1040 xmlDocPtr doc;
1041 xmlNode *root;
1042
1043 g_return_val_if_fail (etta != NULL, NULL);
1044
1045 doc = xmlNewDoc ((const guchar *)"1.0");
1046 root = xmlNewDocNode (doc, NULL, (const guchar *)"expanded_state", NULL);
1047 xmlDocSetRootElement (doc, root);
1048
1049 tar.model = etta->priv->source;
1050 tar.root = root;
1051 tar.expanded_default = e_tree_model_get_expanded_default (etta->priv->source);
1052
1053 e_xml_set_integer_prop_by_name (root, (const guchar *)"vers", 2);
1054 e_xml_set_bool_prop_by_name (root, (const guchar *)"default", tar.expanded_default);
1055
1056 g_hash_table_foreach (etta->priv->nodes, save_expanded_state_func, &tar);
1057
1058 return doc;
1059 }
1060
1061 void
1062 e_tree_table_adapter_save_expanded_state (ETreeTableAdapter *etta,
1063 const gchar *filename)
1064 {
1065 xmlDoc *doc;
1066
1067 g_return_if_fail (etta != NULL);
1068
1069 doc = e_tree_table_adapter_save_expanded_state_xml (etta);
1070 if (doc) {
1071 e_xml_save_file (filename, doc);
1072 xmlFreeDoc (doc);
1073 }
1074 }
1075
1076 static xmlDoc *
1077 open_file (ETreeTableAdapter *etta,
1078 const gchar *filename)
1079 {
1080 xmlDoc *doc;
1081 xmlNode *root;
1082 gint vers;
1083 gboolean model_default, saved_default;
1084
1085 if (!g_file_test (filename, G_FILE_TEST_EXISTS))
1086 return NULL;
1087
1088 #ifdef G_OS_WIN32
1089 {
1090 gchar *locale_filename = g_win32_locale_filename_from_utf8 (filename);
1091 doc = xmlParseFile (locale_filename);
1092 g_free (locale_filename);
1093 }
1094 #else
1095 doc = xmlParseFile (filename);
1096 #endif
1097
1098 if (!doc)
1099 return NULL;
1100
1101 root = xmlDocGetRootElement (doc);
1102 if (root == NULL || strcmp ((gchar *) root->name, "expanded_state")) {
1103 xmlFreeDoc (doc);
1104 return NULL;
1105 }
1106
1107 vers = e_xml_get_integer_prop_by_name_with_default (root, (const guchar *)"vers", 0);
1108 if (vers > 2) {
1109 xmlFreeDoc (doc);
1110 return NULL;
1111 }
1112 model_default = e_tree_model_get_expanded_default (etta->priv->source);
1113 saved_default = e_xml_get_bool_prop_by_name_with_default (root, (const guchar *)"default", !model_default);
1114 if (saved_default != model_default) {
1115 xmlFreeDoc (doc);
1116 return NULL;
1117 }
1118
1119 return doc;
1120 }
1121
1122 /* state: <0 ... collapse; 0 ... use default; >0 ... expand */
1123 void
1124 e_tree_table_adapter_force_expanded_state (ETreeTableAdapter *etta,
1125 gint state)
1126 {
1127 g_return_if_fail (etta != NULL);
1128
1129 etta->priv->force_expanded_state = state;
1130 }
1131
1132 void
1133 e_tree_table_adapter_load_expanded_state_xml (ETreeTableAdapter *etta,
1134 xmlDoc *doc)
1135 {
1136 xmlNode *root, *child;
1137 gboolean model_default;
1138 gboolean file_default = FALSE;
1139
1140 g_return_if_fail (etta != NULL);
1141 g_return_if_fail (doc != NULL);
1142
1143 root = xmlDocGetRootElement (doc);
1144
1145 e_table_model_pre_change (E_TABLE_MODEL (etta));
1146
1147 model_default = e_tree_model_get_expanded_default (etta->priv->source);
1148
1149 if (!strcmp ((gchar *) root->name, "expanded_state")) {
1150 gchar *state;
1151
1152 state = e_xml_get_string_prop_by_name_with_default (root, (const guchar *)"default", "");
1153
1154 if (state[0] == 't')
1155 file_default = TRUE;
1156 else
1157 file_default = FALSE; /* Even unspecified we'll consider as false */
1158
1159 g_free (state);
1160 }
1161
1162 /* Incase the default is changed, lets forget the changes and stick to default */
1163
1164 if (file_default != model_default) {
1165 xmlFreeDoc (doc);
1166 return;
1167 }
1168
1169 for (child = root->xmlChildrenNode; child; child = child->next) {
1170 gchar *id;
1171 ETreePath path;
1172
1173 if (strcmp ((gchar *) child->name, "node")) {
1174 d (g_warning ("unknown node '%s' in %s", child->name, filename));
1175 continue;
1176 }
1177
1178 id = e_xml_get_string_prop_by_name_with_default (child, (const guchar *)"id", "");
1179
1180 if (!strcmp (id, "")) {
1181 g_free (id);
1182 continue;
1183 }
1184
1185 path = e_tree_model_get_node_by_id (etta->priv->source, id);
1186 if (path)
1187 e_tree_table_adapter_node_set_expanded (etta, path, !model_default);
1188
1189 g_free (id);
1190 }
1191
1192 e_table_model_changed (E_TABLE_MODEL (etta));
1193 }
1194
1195 void
1196 e_tree_table_adapter_load_expanded_state (ETreeTableAdapter *etta,
1197 const gchar *filename)
1198 {
1199 xmlDoc *doc;
1200
1201 g_return_if_fail (etta != NULL);
1202
1203 doc = open_file (etta, filename);
1204 if (!doc)
1205 return;
1206
1207 e_tree_table_adapter_load_expanded_state_xml (etta, doc);
1208
1209 xmlFreeDoc (doc);
1210 }
1211
1212 void
1213 e_tree_table_adapter_root_node_set_visible (ETreeTableAdapter *etta,
1214 gboolean visible)
1215 {
1216 gint size;
1217
1218 g_return_if_fail (etta != NULL);
1219
1220 if (etta->priv->root_visible == visible)
1221 return;
1222
1223 e_table_model_pre_change (E_TABLE_MODEL (etta));
1224
1225 etta->priv->root_visible = visible;
1226 if (!visible) {
1227 ETreePath root = e_tree_model_get_root (etta->priv->source);
1228 if (root)
1229 e_tree_table_adapter_node_set_expanded (etta, root, TRUE);
1230 }
1231 size = (visible ? 1 : 0) + (etta->priv->root ? ((node_t *) etta->priv->root->data)->num_visible_children : 0);
1232 resize_map (etta, size);
1233 if (etta->priv->root)
1234 fill_map (etta, 0, etta->priv->root);
1235 e_table_model_changed (E_TABLE_MODEL (etta));
1236 }
1237
1238 void
1239 e_tree_table_adapter_node_set_expanded (ETreeTableAdapter *etta,
1240 ETreePath path,
1241 gboolean expanded)
1242 {
1243 GNode *gnode = lookup_gnode (etta, path);
1244 node_t *node;
1245 gint row;
1246
1247 if (!expanded && (!gnode || (e_tree_model_node_is_root (etta->priv->source, path) && !etta->priv->root_visible)))
1248 return;
1249
1250 if (!gnode && expanded) {
1251 ETreePath parent = e_tree_model_node_get_parent (etta->priv->source, path);
1252 g_return_if_fail (parent != NULL);
1253 e_tree_table_adapter_node_set_expanded (etta, parent, expanded);
1254 gnode = lookup_gnode (etta, path);
1255 }
1256 g_return_if_fail (gnode != NULL);
1257
1258 node = (node_t *) gnode->data;
1259
1260 if (expanded == node->expanded)
1261 return;
1262
1263 node->expanded = expanded;
1264
1265 row = get_row (etta, path);
1266 if (row == -1)
1267 return;
1268
1269 e_table_model_pre_change (E_TABLE_MODEL (etta));
1270 e_table_model_pre_change (E_TABLE_MODEL (etta));
1271 e_table_model_row_changed (E_TABLE_MODEL (etta), row);
1272
1273 if (expanded) {
1274 gint num_children = insert_children (etta, gnode);
1275 update_child_counts (gnode, num_children);
1276 if (etta->priv->sort_info && e_table_sort_info_sorting_get_count (etta->priv->sort_info) > 0)
1277 resort_node (etta, gnode, TRUE);
1278 resize_map (etta, etta->priv->n_map + num_children);
1279 move_map_elements (etta, row + 1 + num_children, row + 1, etta->priv->n_map - row - 1 - num_children);
1280 fill_map (etta, row, gnode);
1281 if (num_children != 0) {
1282 e_table_model_rows_inserted (E_TABLE_MODEL (etta), row + 1, num_children);
1283 } else
1284 e_table_model_no_change (E_TABLE_MODEL (etta));
1285 } else {
1286 gint num_children = delete_children (etta, gnode);
1287 if (num_children == 0) {
1288 e_table_model_no_change (E_TABLE_MODEL (etta));
1289 return;
1290 }
1291 move_map_elements (etta, row + 1, row + 1 + num_children, etta->priv->n_map - row - 1 - num_children);
1292 update_child_counts (gnode, - num_children);
1293 resize_map (etta, etta->priv->n_map - num_children);
1294 e_table_model_rows_deleted (E_TABLE_MODEL (etta), row + 1, num_children);
1295 }
1296 }
1297
1298 void
1299 e_tree_table_adapter_node_set_expanded_recurse (ETreeTableAdapter *etta,
1300 ETreePath path,
1301 gboolean expanded)
1302 {
1303 ETreePath children;
1304
1305 e_tree_table_adapter_node_set_expanded (etta, path, expanded);
1306
1307 for (children = e_tree_model_node_get_first_child (etta->priv->source, path);
1308 children;
1309 children = e_tree_model_node_get_next (etta->priv->source, children)) {
1310 e_tree_table_adapter_node_set_expanded_recurse (etta, children, expanded);
1311 }
1312 }
1313
1314 ETreePath
1315 e_tree_table_adapter_node_at_row (ETreeTableAdapter *etta,
1316 gint row)
1317 {
1318 return get_path (etta, row);
1319 }
1320
1321 gint
1322 e_tree_table_adapter_row_of_node (ETreeTableAdapter *etta,
1323 ETreePath path)
1324 {
1325 return get_row (etta, path);
1326 }
1327
1328 gboolean
1329 e_tree_table_adapter_root_node_is_visible (ETreeTableAdapter *etta)
1330 {
1331 return etta->priv->root_visible;
1332 }
1333
1334 void
1335 e_tree_table_adapter_show_node (ETreeTableAdapter *etta,
1336 ETreePath path)
1337 {
1338 ETreePath parent;
1339
1340 parent = e_tree_model_node_get_parent (etta->priv->source, path);
1341
1342 while (parent) {
1343 e_tree_table_adapter_node_set_expanded (etta, parent, TRUE);
1344 parent = e_tree_model_node_get_parent (etta->priv->source, parent);
1345 }
1346 }
1347
1348 gboolean
1349 e_tree_table_adapter_node_is_expanded (ETreeTableAdapter *etta,
1350 ETreePath path)
1351 {
1352 node_t *node = get_node (etta, path);
1353 if (!e_tree_model_node_is_expandable (etta->priv->source, path) || !node)
1354 return FALSE;
1355
1356 return node->expanded;
1357 }
1358
1359 void
1360 e_tree_table_adapter_set_sort_info (ETreeTableAdapter *etta,
1361 ETableSortInfo *sort_info)
1362 {
1363 if (etta->priv->sort_info) {
1364 g_signal_handler_disconnect (
1365 etta->priv->sort_info,
1366 etta->priv->sort_info_changed_id);
1367 g_object_unref (etta->priv->sort_info);
1368 }
1369
1370 etta->priv->sort_info = sort_info;
1371 if (sort_info) {
1372 g_object_ref (sort_info);
1373 etta->priv->sort_info_changed_id = g_signal_connect (
1374 sort_info, "sort_info_changed",
1375 G_CALLBACK (etta_sort_info_changed), etta);
1376 }
1377
1378 if (!etta->priv->root)
1379 return;
1380
1381 e_table_model_pre_change (E_TABLE_MODEL (etta));
1382 resort_node (etta, etta->priv->root, TRUE);
1383 fill_map (etta, 0, etta->priv->root);
1384 e_table_model_changed (E_TABLE_MODEL (etta));
1385 }
1386
1387 ETableSortInfo *
1388 e_tree_table_adapter_get_sort_info (ETreeTableAdapter *etta)
1389 {
1390 g_return_val_if_fail (etta != NULL, NULL);
1391
1392 return etta->priv->sort_info;
1393 }
1394
1395 ETableHeader *
1396 e_tree_table_adapter_get_header (ETreeTableAdapter *etta)
1397 {
1398 g_return_val_if_fail (etta != NULL, NULL);
1399
1400 return etta->priv->header;
1401 }
1402
1403 ETreePath
1404 e_tree_table_adapter_node_get_next (ETreeTableAdapter *etta,
1405 ETreePath path)
1406 {
1407 GNode *node = lookup_gnode (etta, path);
1408
1409 if (node && node->next)
1410 return ((node_t *) node->next->data)->path;
1411
1412 return NULL;
1413 }