evolution-3.6.4/widgets/table/e-tree-table-adapter.c

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 }