diff options
Diffstat (limited to 'widgets/table/e-tree-table-adapter.c')
-rw-r--r-- | widgets/table/e-tree-table-adapter.c | 974 |
1 files changed, 974 insertions, 0 deletions
diff --git a/widgets/table/e-tree-table-adapter.c b/widgets/table/e-tree-table-adapter.c new file mode 100644 index 0000000000..481deb8eb5 --- /dev/null +++ b/widgets/table/e-tree-table-adapter.c @@ -0,0 +1,974 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * e-tree-table-adapter.c: Implements a table that contains a subset of another table. + * + * Author: + * Chris Lahey <clahey@ximian.com> + * Chris Toshok <toshok@ximian.com> + * + * (C) 2000, 2001 Ximian, Inc. + */ +#include <config.h> +#include <stdlib.h> +#include <string.h> +#include <gtk/gtksignal.h> +#include "gal/util/e-util.h" +#include "gal/util/e-xml-utils.h" +#include "e-tree-table-adapter.h" +#include "gnome-xml/tree.h" +#include "gnome-xml/parser.h" + +#define PARENT_TYPE E_TABLE_MODEL_TYPE +#define d(x) + +#define INCREMENT_AMOUNT 100 + +static ETableModelClass *parent_class; + + +struct ETreeTableAdapterPriv { + ETreeModel *source; + int n_map; + int n_vals_allocated; + ETreePath *map_table; + GHashTable *attributes; + + guint root_visible : 1; + + int last_access; + + int tree_model_pre_change_id; + int tree_model_node_changed_id; + int tree_model_node_data_changed_id; + int tree_model_node_col_changed_id; + int tree_model_node_inserted_id; + int tree_model_node_removed_id; +}; + +typedef struct ETreeTableAdapterNode { + guint expanded : 1; + guint expandable : 1; + guint expandable_set : 1; + + /* parent/child/sibling pointers */ + guint32 num_visible_children; +} ETreeTableAdapterNode; + +static ETreeTableAdapterNode * +find_node(ETreeTableAdapter *adapter, ETreePath path) +{ + ETreeTableAdapterNode *node; + + if (path == NULL) + return NULL; + + if (e_tree_model_has_save_id(adapter->priv->source)) { + char *save_id; + save_id = e_tree_model_get_save_id(adapter->priv->source, path); + node = g_hash_table_lookup(adapter->priv->attributes, save_id); + g_free(save_id); + } else { + node = g_hash_table_lookup(adapter->priv->attributes, path); + } + if (node && !node->expandable_set) { + node->expandable = e_tree_model_node_is_expandable(adapter->priv->source, path); + node->expandable_set = 1; + } + + return node; +} + +static ETreeTableAdapterNode * +find_or_create_node(ETreeTableAdapter *etta, ETreePath path) +{ + ETreeTableAdapterNode *node; + + node = find_node(etta, path); + + if (!node) { + node = g_new(ETreeTableAdapterNode, 1); + if (e_tree_model_node_is_root(etta->priv->source, path)) + node->expanded = TRUE; + else + node->expanded = e_tree_model_get_expanded_default(etta->priv->source); + node->expandable = e_tree_model_node_is_expandable(etta->priv->source, path); + node->expandable_set = 1; + node->num_visible_children = 0; + + if (e_tree_model_has_save_id(etta->priv->source)) { + char *save_id; + save_id = e_tree_model_get_save_id(etta->priv->source, path); + g_hash_table_insert(etta->priv->attributes, save_id, node); + } else { + g_hash_table_insert(etta->priv->attributes, path, node); + } + } + + return node; +} + +static void +add_expanded_node(ETreeTableAdapter *etta, char *save_id, gboolean expanded) +{ + ETreeTableAdapterNode *node; + + node = g_hash_table_lookup(etta->priv->attributes, save_id); + + if (node) { + node->expandable_set = 0; + node->expanded = expanded; + return; + } + + node = g_new(ETreeTableAdapterNode, 1); + + node->expanded = expanded; + node->expandable = 0; + node->expandable_set = 0; + node->num_visible_children = 0; + + g_hash_table_insert(etta->priv->attributes, save_id, node); +} + +static void +etta_expand_to(ETreeTableAdapter *etta, int size) +{ + if (size > etta->priv->n_vals_allocated) { + etta->priv->n_vals_allocated = MAX(etta->priv->n_vals_allocated + INCREMENT_AMOUNT, size); + etta->priv->map_table = g_renew (ETreePath, etta->priv->map_table, etta->priv->n_vals_allocated); + } + +} + +static void +etta_update_parent_child_counts(ETreeTableAdapter *etta, ETreePath path, int change) +{ + for (path = e_tree_model_node_get_parent(etta->priv->source, path); + path; + path = e_tree_model_node_get_parent(etta->priv->source, path)) { + ETreeTableAdapterNode *node = find_or_create_node(etta, path); + node->num_visible_children += change; + } + etta->priv->n_map += change; +} + +static int +find_next_node(ETreeTableAdapter *adapter, int row) +{ + ETreePath path = adapter->priv->map_table[row]; + if (path) { + ETreePath next_sibling = e_tree_model_node_get_next(adapter->priv->source, path); + ETreeTableAdapterNode *current = find_node (adapter, path); + if (next_sibling) + return row + (current ? current->num_visible_children : 0) + 1; + else + return -1; + } else + return -1; +} + +static int +find_first_child_node(ETreeTableAdapter *adapter, int row) +{ + if (row != -1) { + ETreePath path = adapter->priv->map_table[row]; + ETreePath first_child = e_tree_model_node_get_first_child(adapter->priv->source, path); + ETreeTableAdapterNode *current = find_node (adapter, path); + if (first_child && current && current->expanded) + return row + 1; + else + return -1; + } else + return 0; +} + +static int +find_child_row_num(ETreeTableAdapter *etta, int row, ETreePath path) +{ + row = find_first_child_node(etta, row); + + while (row != -1 && path != etta->priv->map_table[row]) { + row = find_next_node(etta, row); + } + + return row; +} + +static int +find_row_num(ETreeTableAdapter *etta, ETreePath path) +{ + int depth; + ETreePath *sequence; + int i; + int row; + + if (etta->priv->map_table == NULL) + return -1; + + if (path == NULL) + return -1; + + if (etta->priv->last_access != -1 && etta->priv->map_table[etta->priv->last_access] == path) + return etta->priv->last_access; + + depth = e_tree_model_node_depth(etta->priv->source, path); + + sequence = g_new(ETreePath, depth + 1); + + sequence[0] = path; + + for (i = 0; i < depth; i++) { + ETreeTableAdapterNode *node; + + sequence[i + 1] = e_tree_model_node_get_parent(etta->priv->source, sequence[i]); + + node = find_node(etta, sequence[i + 1]); + if (! ((node && node->expanded) || e_tree_model_get_expanded_default(etta->priv->source))) { + g_free(sequence); + return -1; + } + } + + row = 0; + + for (i = depth; i >= 0; i --) { + while (row != -1 && sequence[i] != etta->priv->map_table[row]) { + row = find_next_node(etta, row); + } + if (row == -1) + break; + if (i == 0) + break; + row = find_first_child_node(etta, row); + } + g_free (sequence); + + etta->priv->last_access = row; + return row; +} + +static int +array_size_from_path(ETreeTableAdapter *etta, ETreePath path) +{ + int size = 1; + + ETreeTableAdapterNode *node = NULL; + + if (e_tree_model_node_is_expandable(etta->priv->source, path)) + node = find_or_create_node(etta, path); + + if (node && node->expanded) { + ETreePath children; + + for (children = e_tree_model_node_get_first_child(etta->priv->source, path); + children; + children = e_tree_model_node_get_next(etta->priv->source, children)) { + size += array_size_from_path(etta, children); + } + } + + return size; +} + +static int +fill_array_from_path(ETreeTableAdapter *etta, ETreePath *array, ETreePath path) +{ + ETreeTableAdapterNode *node = NULL; + int index = 0; + + array[index] = path; + + index ++; + + if (e_tree_model_node_is_expandable(etta->priv->source, path)) + node = find_or_create_node(etta, path); + else + node = find_node(etta, path); + + if (node && node->expanded) { + ETreePath children; + + for (children = e_tree_model_node_get_first_child(etta->priv->source, path); + children; + children = e_tree_model_node_get_next(etta->priv->source, children)) { + index += fill_array_from_path(etta, array + index, children); + } + } + + if (node) + node->num_visible_children = index - 1; + + return index; +} + +static void +free_string (gpointer key, gpointer value, gpointer data) +{ + g_free(key); +} + +static void +etta_destroy (GtkObject *object) +{ + ETreeTableAdapter *etta = E_TREE_TABLE_ADAPTER (object); + + if (etta->priv->source && e_tree_model_has_save_id(etta->priv->source)) { + g_hash_table_foreach(etta->priv->attributes, free_string, NULL); + } + g_hash_table_destroy (etta->priv->attributes); + + if (etta->priv->source) { + gtk_signal_disconnect (GTK_OBJECT (etta->priv->source), + etta->priv->tree_model_pre_change_id); + gtk_signal_disconnect (GTK_OBJECT (etta->priv->source), + etta->priv->tree_model_node_changed_id); + gtk_signal_disconnect (GTK_OBJECT (etta->priv->source), + etta->priv->tree_model_node_data_changed_id); + gtk_signal_disconnect (GTK_OBJECT (etta->priv->source), + etta->priv->tree_model_node_col_changed_id); + gtk_signal_disconnect (GTK_OBJECT (etta->priv->source), + etta->priv->tree_model_node_inserted_id); + gtk_signal_disconnect (GTK_OBJECT (etta->priv->source), + etta->priv->tree_model_node_removed_id); + + gtk_object_unref (GTK_OBJECT (etta->priv->source)); + etta->priv->source = NULL; + + etta->priv->tree_model_pre_change_id = 0; + etta->priv->tree_model_node_changed_id = 0; + etta->priv->tree_model_node_data_changed_id = 0; + etta->priv->tree_model_node_col_changed_id = 0; + etta->priv->tree_model_node_inserted_id = 0; + etta->priv->tree_model_node_removed_id = 0; + } + + g_free (etta->priv->map_table); + + g_free (etta->priv); + + GTK_OBJECT_CLASS (parent_class)->destroy (object); +} + +static int +etta_column_count (ETableModel *etm) +{ + ETreeTableAdapter *etta = (ETreeTableAdapter *)etm; + + return e_tree_model_column_count (etta->priv->source); +} + +static gboolean +etta_has_save_id (ETableModel *etm) +{ + ETreeTableAdapter *etta = (ETreeTableAdapter *)etm; + + return e_tree_model_has_save_id (etta->priv->source); +} + +static gchar * +etta_get_save_id (ETableModel *etm, int row) +{ + ETreeTableAdapter *etta = (ETreeTableAdapter *)etm; + + if (etta->priv->root_visible) + return e_tree_model_get_save_id (etta->priv->source, etta->priv->map_table [row]); + else + return e_tree_model_get_save_id (etta->priv->source, etta->priv->map_table [row + 1]); +} + +static int +etta_row_count (ETableModel *etm) +{ + ETreeTableAdapter *etta = (ETreeTableAdapter *)etm; + + if (etta->priv->root_visible) + return etta->priv->n_map; + else { + if (etta->priv->n_map > 0) + return etta->priv->n_map - 1; + else + return 0; + } +} + +static void * +etta_value_at (ETableModel *etm, int col, int row) +{ + ETreeTableAdapter *etta = (ETreeTableAdapter *)etm; + + etta->priv->last_access = row; + d(g_print("g) Setting last_access to %d\n", row)); + + switch (col) { + case -1: + if (etta->priv->root_visible) + return etta->priv->map_table [row]; + else + return etta->priv->map_table [row + 1]; + case -2: + return etta->priv->source; + case -3: + return etta; + default: + if (etta->priv->root_visible) + return e_tree_model_value_at (etta->priv->source, etta->priv->map_table [row], col); + else + return e_tree_model_value_at (etta->priv->source, etta->priv->map_table [row + 1], col); + } +} + +static void +etta_set_value_at (ETableModel *etm, int col, int row, const void *val) +{ + ETreeTableAdapter *etta = (ETreeTableAdapter *)etm; + + etta->priv->last_access = row; + d(g_print("h) Setting last_access to %d\n", row)); + if (etta->priv->root_visible) + e_tree_model_set_value_at (etta->priv->source, etta->priv->map_table [row], col, val); + else + e_tree_model_set_value_at (etta->priv->source, etta->priv->map_table [row + 1], col, val); +} + +static gboolean +etta_is_cell_editable (ETableModel *etm, int col, int row) +{ + ETreeTableAdapter *etta = (ETreeTableAdapter *)etm; + + if (etta->priv->root_visible) + return e_tree_model_node_is_editable (etta->priv->source, etta->priv->map_table [row], col); + else + return e_tree_model_node_is_editable (etta->priv->source, etta->priv->map_table [row + 1], col); +} + +static void +etta_append_row (ETableModel *etm, ETableModel *source, int row) +{ +#if 0 + ETreeTableAdapter *etta = (ETreeTableAdapter *)etm; + e_table_model_append_row (etta->priv->source, source, row); +#endif +} + +static void * +etta_duplicate_value (ETableModel *etm, int col, const void *value) +{ + ETreeTableAdapter *etta = (ETreeTableAdapter *)etm; + + return e_tree_model_duplicate_value (etta->priv->source, col, value); +} + +static void +etta_free_value (ETableModel *etm, int col, void *value) +{ + ETreeTableAdapter *etta = (ETreeTableAdapter *)etm; + + e_tree_model_free_value (etta->priv->source, col, value); +} + +static void * +etta_initialize_value (ETableModel *etm, int col) +{ + ETreeTableAdapter *etta = (ETreeTableAdapter *)etm; + + return e_tree_model_initialize_value (etta->priv->source, col); +} + +static gboolean +etta_value_is_empty (ETableModel *etm, int col, const void *value) +{ + ETreeTableAdapter *etta = (ETreeTableAdapter *)etm; + + return e_tree_model_value_is_empty (etta->priv->source, col, value); +} + +static char * +etta_value_to_string (ETableModel *etm, int col, const void *value) +{ + ETreeTableAdapter *etta = (ETreeTableAdapter *)etm; + + return e_tree_model_value_to_string (etta->priv->source, col, value); +} + +static void +etta_class_init (ETreeTableAdapterClass *klass) +{ + ETableModelClass *table_class = (ETableModelClass *) klass; + GtkObjectClass *object_class = (GtkObjectClass *) klass; + + parent_class = gtk_type_class (PARENT_TYPE); + + object_class->destroy = etta_destroy; + + table_class->column_count = etta_column_count; + table_class->has_save_id = etta_has_save_id; + table_class->get_save_id = etta_get_save_id; + table_class->row_count = etta_row_count; + table_class->value_at = etta_value_at; + table_class->set_value_at = etta_set_value_at; + table_class->is_cell_editable = etta_is_cell_editable; + table_class->append_row = etta_append_row; + table_class->duplicate_value = etta_duplicate_value; + table_class->free_value = etta_free_value; + table_class->initialize_value = etta_initialize_value; + table_class->value_is_empty = etta_value_is_empty; + table_class->value_to_string = etta_value_to_string; +} + +static void +etta_init (ETreeTableAdapter *etta) +{ + etta->priv = g_new(ETreeTableAdapterPriv, 1); + + etta->priv->last_access = 0; + etta->priv->map_table = NULL; + etta->priv->n_map = 0; + etta->priv->n_vals_allocated = 0; + + etta->priv->root_visible = TRUE; + + etta->priv->attributes = NULL; +} + +E_MAKE_TYPE(e_tree_table_adapter, "ETreeTableAdapter", ETreeTableAdapter, etta_class_init, etta_init, PARENT_TYPE); + +static void +etta_proxy_pre_change (ETreeModel *etm, ETreeTableAdapter *etta) +{ + e_table_model_pre_change(E_TABLE_MODEL(etta)); +} + +static void +etta_proxy_node_changed (ETreeModel *etm, ETreePath path, ETreeTableAdapter *etta) +{ + if (e_tree_model_node_is_root(etm, path)) { + int size; + + size = array_size_from_path(etta, path); + etta_expand_to(etta, size); + etta->priv->n_map = size; + fill_array_from_path(etta, etta->priv->map_table, path); + } else { + int row = find_row_num(etta, path); + int size; + int old_size; + + if (row == -1) + return; + + size = array_size_from_path(etta, path); + if (e_tree_model_node_is_expandable(etta->priv->source, path)) { + ETreeTableAdapterNode *node = find_or_create_node(etta, path); + old_size = node->num_visible_children + 1; + } else { + ETreeTableAdapterNode *node = find_node(etta, path); + if (node) + old_size = node->num_visible_children + 1; + else + old_size = 1; + } + + etta_expand_to(etta, etta->priv->n_map + size - old_size); + + memmove(etta->priv->map_table + row + size, + etta->priv->map_table + row + old_size, + (etta->priv->n_map - row - old_size) * sizeof (ETreePath)); + fill_array_from_path(etta, etta->priv->map_table + row, path); + etta_update_parent_child_counts(etta, path, size - old_size); + } + + e_table_model_changed(E_TABLE_MODEL(etta)); +} + +static void +etta_proxy_node_data_changed (ETreeModel *etm, ETreePath path, ETreeTableAdapter *etta) +{ + int row = find_row_num(etta, path); + if (row != -1) { + if (etta->priv->root_visible) + e_table_model_row_changed(E_TABLE_MODEL(etta), row); + else if (row != 0) + e_table_model_row_changed(E_TABLE_MODEL(etta), row - 1); + } +} + +static void +etta_proxy_node_col_changed (ETreeModel *etm, ETreePath path, int col, ETreeTableAdapter *etta) +{ + int row = find_row_num(etta, path); + if (row != -1) { + if (etta->priv->root_visible) + e_table_model_cell_changed(E_TABLE_MODEL(etta), col, row); + else if (row != 0) + e_table_model_cell_changed(E_TABLE_MODEL(etta), col, row - 1); + } +} + +static void +etta_proxy_node_inserted (ETreeModel *etm, ETreePath parent, ETreePath child, ETreeTableAdapter *etta) +{ + int row; + + if (e_tree_model_node_is_root(etm, child)) { + row = 0; + } else { + ETreePath children; + int parent_row; + ETreeTableAdapterNode *parent_node; + + parent_row = find_row_num(etta, parent); + if (parent_row == -1) + return; + + parent_node = find_or_create_node(etta, parent); + if (parent_node->expandable != e_tree_model_node_is_expandable(etta->priv->source, parent)) { + parent_node->expandable = e_tree_model_node_is_expandable(etta->priv->source, parent); + if (etta->priv->root_visible) + e_table_model_row_changed(E_TABLE_MODEL(etta), parent_row); + else if (parent_row != 0) + e_table_model_row_changed(E_TABLE_MODEL(etta), parent_row - 1); + } + if (!parent_node->expanded) + return; + + row = find_first_child_node(etta, parent_row); + children = e_tree_model_node_get_first_child(etta->priv->source, parent); + + while (row != -1 && children != NULL && children == etta->priv->map_table[row]) { + children = e_tree_model_node_get_next(etta->priv->source, children); + row = find_next_node(etta, row); + } + } + + if (row != -1) { + int size; + + size = array_size_from_path(etta, child); + + etta_expand_to(etta, etta->priv->n_map + size); + + memmove(etta->priv->map_table + row + size, + etta->priv->map_table + row, + (etta->priv->n_map - row) * sizeof (ETreePath)); + + fill_array_from_path(etta, etta->priv->map_table + row, child); + etta_update_parent_child_counts(etta, child, size); + + if (etta->priv->root_visible) + e_table_model_rows_inserted(E_TABLE_MODEL(etta), row, size); + else if (row != 0) + e_table_model_rows_inserted(E_TABLE_MODEL(etta), row - 1, size); + else + e_table_model_rows_inserted(E_TABLE_MODEL(etta), 0, size - 1); + } +} + +static void +etta_proxy_node_removed (ETableModel *etm, ETreePath parent, ETreePath child, ETreeTableAdapter *etta) +{ + int parent_row = find_row_num(etta, parent); + int row = find_child_row_num(etta, parent_row, child); + ETreeTableAdapterNode *parent_node = find_node(etta, parent); + if (parent_row != -1 && parent_node) { + if (parent_node->expandable != e_tree_model_node_is_expandable(etta->priv->source, parent)) { + parent_node->expandable = e_tree_model_node_is_expandable(etta->priv->source, parent); + if (etta->priv->root_visible) + e_table_model_row_changed(E_TABLE_MODEL(etta), parent_row); + else if (parent_row != 0) + e_table_model_row_changed(E_TABLE_MODEL(etta), parent_row - 1); + } + } + if (row != -1) { + ETreeTableAdapterNode *node = find_node(etta, child); + int to_remove = (node ? node->num_visible_children : 0) + 1; + + memmove(etta->priv->map_table + row, + etta->priv->map_table + row + to_remove, + (etta->priv->n_map - row - to_remove) * sizeof (ETreePath)); + + if (parent_node) + parent_node->num_visible_children -= to_remove; + if (parent) + etta_update_parent_child_counts(etta, parent, - to_remove); + + if (etta->priv->root_visible) + e_table_model_rows_deleted(E_TABLE_MODEL(etta), row, to_remove); + else if (row != 0) + e_table_model_rows_deleted(E_TABLE_MODEL(etta), row - 1, to_remove); + else + e_table_model_rows_deleted(E_TABLE_MODEL(etta), 0, to_remove - 1); + } +} + +ETableModel * +e_tree_table_adapter_construct (ETreeTableAdapter *etta, ETreeModel *source) +{ + ETreePath root; + + etta->priv->source = source; + gtk_object_ref (GTK_OBJECT (source)); + + if (e_tree_model_has_save_id(source)) + etta->priv->attributes = g_hash_table_new(g_str_hash, g_str_equal); + else + etta->priv->attributes = g_hash_table_new(NULL, NULL); + + root = e_tree_model_get_root (source); + + if (root) { + etta->priv->n_map = array_size_from_path(etta, root); + etta->priv->n_vals_allocated = etta->priv->n_map; + etta->priv->map_table = g_new(ETreePath, etta->priv->n_map); + fill_array_from_path(etta, etta->priv->map_table, root); + } + + etta->priv->tree_model_pre_change_id = gtk_signal_connect (GTK_OBJECT (source), "pre_change", + GTK_SIGNAL_FUNC (etta_proxy_pre_change), etta); + etta->priv->tree_model_node_changed_id = gtk_signal_connect (GTK_OBJECT (source), "node_changed", + GTK_SIGNAL_FUNC (etta_proxy_node_changed), etta); + etta->priv->tree_model_node_data_changed_id = gtk_signal_connect (GTK_OBJECT (source), "node_data_changed", + GTK_SIGNAL_FUNC (etta_proxy_node_data_changed), etta); + etta->priv->tree_model_node_col_changed_id = gtk_signal_connect (GTK_OBJECT (source), "node_col_changed", + GTK_SIGNAL_FUNC (etta_proxy_node_col_changed), etta); + etta->priv->tree_model_node_inserted_id = gtk_signal_connect (GTK_OBJECT (source), "node_inserted", + GTK_SIGNAL_FUNC (etta_proxy_node_inserted), etta); + etta->priv->tree_model_node_removed_id = gtk_signal_connect (GTK_OBJECT (source), "node_removed", + GTK_SIGNAL_FUNC (etta_proxy_node_removed), etta); + + return E_TABLE_MODEL (etta); +} + +ETableModel * +e_tree_table_adapter_new (ETreeModel *source) +{ + ETreeTableAdapter *etta = gtk_type_new (E_TREE_TABLE_ADAPTER_TYPE); + + e_tree_table_adapter_construct (etta, source); + + return (ETableModel *) etta; +} + +typedef struct { + xmlNode *root; + ETreeModel *tree; +} TreeAndRoot; + +static void +save_expanded_state_func (gpointer keyp, gpointer value, gpointer data) +{ + gchar *key = keyp; + ETreeTableAdapterNode *node = value; + TreeAndRoot *tar = data; + xmlNode *root = tar->root; + ETreeModel *etm = tar->tree; + xmlNode *xmlnode; + + if (node->expanded != e_tree_model_get_expanded_default(etm)) { + xmlnode = xmlNewChild (root, NULL, "node", NULL); + e_xml_set_string_prop_by_name(xmlnode, "id", key); + } +} + +void +e_tree_table_adapter_save_expanded_state (ETreeTableAdapter *etta, const char *filename) +{ + xmlDoc *doc; + xmlNode *root; + ETreeTableAdapterPriv *priv; + TreeAndRoot tar; + + g_return_if_fail(etta != NULL); + + priv = etta->priv; + + doc = xmlNewDoc ((xmlChar*) "1.0"); + root = xmlNewDocNode (doc, NULL, + (xmlChar *) "expanded_state", + NULL); + xmlDocSetRootElement (doc, root); + + e_xml_set_integer_prop_by_name(root, "vers", 1); + + tar.root = root; + tar.tree = etta->priv->source; + + g_hash_table_foreach (priv->attributes, + save_expanded_state_func, + &tar); + + xmlSaveFile (filename, doc); + + xmlFreeDoc (doc); +} + +void +e_tree_table_adapter_load_expanded_state (ETreeTableAdapter *etta, const char *filename) +{ + ETreeTableAdapterPriv *priv; + xmlDoc *doc; + xmlNode *root; + xmlNode *child; + int vers; + + g_return_if_fail(etta != NULL); + + priv = etta->priv; + + doc = xmlParseFile (filename); + if (!doc) + return; + + root = xmlDocGetRootElement (doc); + if (root == NULL || strcmp (root->name, "expanded_state")) { + xmlFreeDoc (doc); + return; + } + + vers = e_xml_get_integer_prop_by_name_with_default(root, "vers", 0); + if (vers != 1) { + xmlFreeDoc (doc); + return; + } + + for (child = root->childs; child; child = child->next) { + char *id; + + if (strcmp (child->name, "node")) { + d(g_warning ("unknown node '%s' in %s", child->name, filename)); + continue; + } + + id = e_xml_get_string_prop_by_name_with_default (child, "id", ""); + + if (!strcmp(id, "")) { + g_free(id); + return; + } + + add_expanded_node(etta, id, !e_tree_model_get_expanded_default(etta->priv->source)); + } + + xmlFreeDoc (doc); +} + +void e_tree_table_adapter_root_node_set_visible (ETreeTableAdapter *etta, gboolean visible) +{ + if (etta->priv->root_visible == visible) + return; + + e_table_model_pre_change (E_TABLE_MODEL(etta)); + + etta->priv->root_visible = visible; + if (!visible) { + ETreePath root = e_tree_model_get_root(etta->priv->source); + if (root) + e_tree_table_adapter_node_set_expanded(etta, root, TRUE); + } + e_table_model_changed(E_TABLE_MODEL(etta)); +} + +void e_tree_table_adapter_node_set_expanded (ETreeTableAdapter *etta, ETreePath path, gboolean expanded) +{ + ETreeTableAdapterNode *node; + int row; + + node = find_or_create_node(etta, path); + row = find_row_num(etta, path); + + if (expanded != node->expanded) { + e_table_model_pre_change (E_TABLE_MODEL(etta)); + + node->expanded = expanded; + + if (row != -1) { + if (etta->priv->root_visible) + e_table_model_row_changed(E_TABLE_MODEL(etta), row); + else if (row != 0) + e_table_model_row_changed(E_TABLE_MODEL(etta), row - 1); + + if (expanded) { + int num_children = array_size_from_path(etta, path) - 1; + etta_expand_to(etta, etta->priv->n_map + num_children); + memmove(etta->priv->map_table + row + 1 + num_children, + etta->priv->map_table + row + 1, + (etta->priv->n_map - row - 1) * sizeof (ETreePath)); + fill_array_from_path(etta, etta->priv->map_table + row, path); + etta_update_parent_child_counts(etta, path, num_children); + if (num_children != 0) { + if (etta->priv->root_visible) + e_table_model_rows_inserted(E_TABLE_MODEL(etta), row + 1, num_children); + else + e_table_model_rows_inserted(E_TABLE_MODEL(etta), row, num_children); + } + } else { + int num_children = node->num_visible_children; + memmove(etta->priv->map_table + row + 1, + etta->priv->map_table + row + 1 + num_children, + (etta->priv->n_map - row - 1 - num_children) * sizeof (ETreePath)); + node->num_visible_children = 0; + etta_update_parent_child_counts(etta, path, - num_children); + if (num_children != 0) { + if (etta->priv->root_visible) + e_table_model_rows_deleted(E_TABLE_MODEL(etta), row + 1, num_children); + else + e_table_model_rows_deleted(E_TABLE_MODEL(etta), row, num_children); + } + } + } + } +} + +void e_tree_table_adapter_node_set_expanded_recurse (ETreeTableAdapter *etta, ETreePath path, gboolean expanded) +{ + ETreePath children; + + e_tree_table_adapter_node_set_expanded(etta, path, expanded); + + for (children = e_tree_model_node_get_first_child(etta->priv->source, path); + children; + children = e_tree_model_node_get_next(etta->priv->source, children)) { + e_tree_table_adapter_node_set_expanded_recurse(etta, children, expanded); + } +} + +ETreePath e_tree_table_adapter_node_at_row (ETreeTableAdapter *etta, int row) +{ + if (etta->priv->root_visible) + return etta->priv->map_table[row]; + else + return etta->priv->map_table[row + 1]; +} + +int e_tree_table_adapter_row_of_node (ETreeTableAdapter *etta, ETreePath path) +{ + if (etta->priv->root_visible) + return find_row_num(etta, path); + else + return find_row_num(etta, path) - 1; +} + +gboolean e_tree_table_adapter_root_node_is_visible(ETreeTableAdapter *etta) +{ + return etta->priv->root_visible; +} + +void e_tree_table_adapter_show_node (ETreeTableAdapter *etta, ETreePath path) +{ + ETreePath parent; + + parent = e_tree_model_node_get_parent(etta->priv->source, path); + + if (parent) { + e_tree_table_adapter_node_set_expanded(etta, parent, TRUE); + e_tree_table_adapter_show_node(etta, parent); + } +} + +gboolean e_tree_table_adapter_node_is_expanded (ETreeTableAdapter *etta, ETreePath path) +{ + if (e_tree_model_node_is_expandable(etta->priv->source, path)) { + ETreeTableAdapterNode *node = find_or_create_node(etta, path); + return node->expanded; + } else + return FALSE; +} |