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