/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ /* * e-tree-table-adapter.c * Copyright 2000, 2001, Ximian, Inc. * * Authors: * Chris Lahey * Chris Toshok * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License, version 2, as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include "gal/util/e-util.h" #include "gal/util/e-xml-utils.h" #include "e-tree-table-adapter.h" #include "e-table-sorting-utils.h" #define PARENT_TYPE E_TABLE_MODEL_TYPE #define d(x) #define INCREMENT_AMOUNT 100 static ETableModelClass *parent_class; typedef struct { ETreePath path; guint32 num_visible_children; guint32 index; guint expanded : 1; guint expandable : 1; guint expandable_set : 1; } node_t; struct ETreeTableAdapterPriv { ETreeModel *source; ETableSortInfo *sort_info; ETableHeader *header; int n_map; int n_vals_allocated; node_t **map_table; GHashTable *nodes; GNode *root; guint root_visible : 1; guint remap_needed : 1; int last_access; int pre_change_id; int no_change_id; int node_changed_id; int node_data_changed_id; int node_col_changed_id; int node_inserted_id; int node_removed_id; int node_request_collapse_id; int sort_info_changed_id; }; static GNode * lookup_gnode(ETreeTableAdapter *etta, ETreePath path) { GNode *gnode; if (!path) return NULL; gnode = g_hash_table_lookup(etta->priv->nodes, path); return gnode; } static void resize_map(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 (node_t *, etta->priv->map_table, etta->priv->n_vals_allocated); } etta->priv->n_map = size; } static void move_map_elements(ETreeTableAdapter *etta, int to, int from, int count) { if (count <= 0 || from >= etta->priv->n_map) return; memmove(etta->priv->map_table + to, etta->priv->map_table + from, count * sizeof (node_t *)); etta->priv->remap_needed = TRUE; } static gint fill_map(ETreeTableAdapter *etta, gint index, GNode *gnode) { GNode *p; if ((gnode != etta->priv->root) || etta->priv->root_visible) etta->priv->map_table[index++] = gnode->data; for (p = gnode->children; p; p = p->next) index = fill_map(etta, index, p); etta->priv->remap_needed = TRUE; return index; } static void remap_indices(ETreeTableAdapter *etta) { int i; for (i = 0; i < etta->priv->n_map; i++) etta->priv->map_table[i]->index = i; etta->priv->remap_needed = FALSE; } static node_t * get_node(ETreeTableAdapter *etta, ETreePath path) { GNode *gnode = lookup_gnode(etta, path); if (!gnode) return NULL; return (node_t *)gnode->data; } static void resort_node(ETreeTableAdapter *etta, GNode *gnode, gboolean recurse) { node_t *node = (node_t *)gnode->data; ETreePath *paths, path; GNode *prev, *curr; int i, count; gboolean sort_needed; if (node->num_visible_children == 0) return; sort_needed = etta->priv->sort_info && e_table_sort_info_sorting_get_count (etta->priv->sort_info) > 0; for (i = 0, path = e_tree_model_node_get_first_child(etta->priv->source, node->path); path; path = e_tree_model_node_get_next(etta->priv->source, path), i++); count = i; if (count <= 1) return; paths = g_new0(ETreePath, count); for (i = 0, path = e_tree_model_node_get_first_child(etta->priv->source, node->path); path; path = e_tree_model_node_get_next(etta->priv->source, path), i++) paths[i] = path; if (count > 1 && sort_needed) e_table_sorting_utils_tree_sort(etta->priv->source, etta->priv->sort_info, etta->priv->header, paths, count); prev = NULL; for (i = 0; i < count; i++) { curr = lookup_gnode(etta, paths[i]); if (!curr) continue; if (prev) prev->next = curr; else gnode->children = curr; curr->prev = prev; curr->next = NULL; prev = curr; if (recurse) resort_node(etta, curr, recurse); } g_free(paths); } static gint get_row(ETreeTableAdapter *etta, ETreePath path) { node_t *node = get_node(etta, path); if (!node) return -1; if (etta->priv->remap_needed) remap_indices(etta); return node->index; } static ETreePath get_path (ETreeTableAdapter *etta, int row) { if (row == -1 && etta->priv->n_map > 0) row = etta->priv->n_map - 1; else if (row < 0 || row >= etta->priv->n_map) return NULL; return etta->priv->map_table [row]->path; } static void kill_gnode(GNode *node, ETreeTableAdapter *etta) { g_hash_table_remove(etta->priv->nodes, ((node_t *)node->data)->path); while (node->children) { GNode *next = node->children->next; kill_gnode(node->children, etta); node->children = next; } g_free(node->data); if (node == etta->priv->root) etta->priv->root = NULL; g_node_destroy(node); } static void update_child_counts(GNode *gnode, gint delta) { while (gnode) { node_t *node = (node_t *) gnode->data; node->num_visible_children += delta; gnode = gnode->parent; } } static int delete_children(ETreeTableAdapter *etta, GNode *gnode) { node_t *node = (node_t *)gnode->data; int to_remove = node ? node->num_visible_children : 0; if (to_remove == 0) return 0; while (gnode->children) { GNode *next = gnode->children->next; kill_gnode(gnode->children, etta); gnode->children = next; } return to_remove; } static void delete_node(ETreeTableAdapter *etta, ETreePath parent, ETreePath path) { int to_remove = 1; int parent_row = get_row(etta, parent); int row = get_row(etta, path); GNode *gnode = lookup_gnode(etta, path); GNode *parent_gnode = lookup_gnode(etta, parent); e_table_model_pre_change(E_TABLE_MODEL(etta)); if (row == -1) { e_table_model_no_change(E_TABLE_MODEL(etta)); return; } to_remove += delete_children(etta, gnode); kill_gnode(gnode, etta); move_map_elements(etta, row, row + to_remove, etta->priv->n_map - row - to_remove); resize_map(etta, etta->priv->n_map - to_remove); if (parent_gnode != NULL) { node_t *parent_node = parent_gnode->data; gboolean expandable = e_tree_model_node_is_expandable(etta->priv->source, parent); update_child_counts(parent_gnode, - to_remove); if (parent_node->expandable != expandable) { e_table_model_pre_change(E_TABLE_MODEL(etta)); parent_node->expandable = expandable; e_table_model_row_changed(E_TABLE_MODEL(etta), parent_row); } resort_node (etta, parent_gnode, FALSE); } e_table_model_rows_deleted(E_TABLE_MODEL(etta), row, to_remove); } static GNode * create_gnode(ETreeTableAdapter *etta, ETreePath path) { GNode *gnode; node_t *node; node = g_new0(node_t, 1); node->path = path; node->index = -1; 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; gnode = g_node_new(node); g_hash_table_insert(etta->priv->nodes, path, gnode); return gnode; } static gint insert_children(ETreeTableAdapter *etta, GNode *gnode) { ETreePath path, tmp; int count = 0; int pos = 0; path = ((node_t *)gnode->data)->path; for (tmp = e_tree_model_node_get_first_child(etta->priv->source, path); tmp; tmp = e_tree_model_node_get_next(etta->priv->source, tmp), pos++) { GNode *child = create_gnode(etta, tmp); node_t *node = (node_t *) child->data; if (node->expanded) node->num_visible_children = insert_children(etta, child); g_node_prepend(gnode, child); count += node->num_visible_children + 1; } g_node_reverse_children(gnode); return count; } static void generate_tree(ETreeTableAdapter *etta, ETreePath path) { GNode *gnode; node_t *node; int size; e_table_model_pre_change(E_TABLE_MODEL(etta)); g_assert(e_tree_model_node_is_root(etta->priv->source, path)); if (etta->priv->root) kill_gnode(etta->priv->root, etta); resize_map(etta, 0); gnode = create_gnode(etta, path); node = (node_t *) gnode->data; node->expanded = TRUE; node->num_visible_children = insert_children(etta, gnode); if (etta->priv->sort_info && e_table_sort_info_sorting_get_count(etta->priv->sort_info) > 0) resort_node(etta, gnode, TRUE); etta->priv->root = gnode; size = etta->priv->root_visible ? node->num_visible_children + 1 : node->num_visible_children; resize_map(etta, size); fill_map(etta, 0, gnode); e_table_model_changed(E_TABLE_MODEL(etta)); } static void insert_node(ETreeTableAdapter *etta, ETreePath parent, ETreePath path) { GNode *gnode, *parent_gnode; node_t *node, *parent_node; gboolean expandable; int size, row; e_table_model_pre_change(E_TABLE_MODEL(etta)); if (get_node(etta, path)) { e_table_model_no_change(E_TABLE_MODEL(etta)); return; } parent_gnode = lookup_gnode(etta, parent); if (!parent_gnode) { ETreePath grandparent = e_tree_model_node_get_parent(etta->priv->source, parent); if (e_tree_model_node_is_root(etta->priv->source, parent)) generate_tree(etta, parent); else insert_node(etta, grandparent, parent); return; } parent_node = (node_t *) parent_gnode->data; if (parent_gnode != etta->priv->root) { expandable = e_tree_model_node_is_expandable(etta->priv->source, parent); if (parent_node->expandable != expandable) { e_table_model_pre_change(E_TABLE_MODEL(etta)); parent_node->expandable = expandable; parent_node->expandable_set = 1; e_table_model_row_changed(E_TABLE_MODEL(etta), parent_node->index); } } if (!e_tree_table_adapter_node_is_expanded (etta, parent)) { e_table_model_no_change(E_TABLE_MODEL(etta)); return; } gnode = create_gnode(etta, path); node = (node_t *) gnode->data; if (node->expanded) node->num_visible_children = insert_children(etta, gnode); g_node_append(parent_gnode, gnode); update_child_counts(parent_gnode, node->num_visible_children + 1); resort_node(etta, parent_gnode, FALSE); resort_node(etta, gnode, TRUE); size = node->num_visible_children + 1; resize_map(etta, etta->priv->n_map + size); if (parent_gnode == etta->priv->root) row = 0; else { gint new_size = parent_node->num_visible_children + 1; gint old_size = new_size - size; row = parent_node->index; move_map_elements(etta, row + new_size, row + old_size, etta->priv->n_map - row - new_size); } fill_map(etta, row, parent_gnode); e_table_model_rows_inserted(E_TABLE_MODEL(etta), get_row(etta, path), size); } typedef struct { GSList *paths; gboolean expanded; } check_expanded_closure; static gboolean check_expanded(GNode *gnode, gpointer data) { check_expanded_closure *closure = (check_expanded_closure *) data; node_t *node = (node_t *) gnode->data; if (node->expanded != closure->expanded) closure->paths = g_slist_prepend(closure->paths, node->path); return FALSE; } static void update_node(ETreeTableAdapter *etta, ETreePath path) { check_expanded_closure closure; ETreePath parent = e_tree_model_node_get_parent(etta->priv->source, path); GNode *gnode = lookup_gnode(etta, path); GSList *l; closure.expanded = e_tree_model_get_expanded_default (etta->priv->source); closure.paths = NULL; if (gnode) g_node_traverse(gnode, G_IN_ORDER, G_TRAVERSE_ALL, -1, check_expanded, &closure); if (e_tree_model_node_is_root(etta->priv->source, path)) generate_tree(etta, path); else { delete_node(etta, parent, path); insert_node(etta, parent, path); } for (l = closure.paths; l; l = l->next) e_tree_table_adapter_node_set_expanded (etta, l->data, !closure.expanded); g_slist_free(closure.paths); } static void etta_finalize (GObject *object) { ETreeTableAdapter *etta = E_TREE_TABLE_ADAPTER (object); if (etta->priv->root) { kill_gnode(etta->priv->root, etta); etta->priv->root = NULL; } g_hash_table_destroy (etta->priv->nodes); g_free (etta->priv->map_table); g_free (etta->priv); G_OBJECT_CLASS (parent_class)->finalize (object); } static void etta_dispose (GObject *object) { ETreeTableAdapter *etta = E_TREE_TABLE_ADAPTER (object); if (etta->priv->sort_info) { g_signal_handler_disconnect(G_OBJECT (etta->priv->sort_info), etta->priv->sort_info_changed_id); g_object_unref(etta->priv->sort_info); etta->priv->sort_info = NULL; } if (etta->priv->header) { g_object_unref(etta->priv->header); etta->priv->header = NULL; } if (etta->priv->source) { g_signal_handler_disconnect (G_OBJECT (etta->priv->source), etta->priv->pre_change_id); g_signal_handler_disconnect (G_OBJECT (etta->priv->source), etta->priv->no_change_id); g_signal_handler_disconnect (G_OBJECT (etta->priv->source), etta->priv->node_changed_id); g_signal_handler_disconnect (G_OBJECT (etta->priv->source), etta->priv->node_data_changed_id); g_signal_handler_disconnect (G_OBJECT (etta->priv->source), etta->priv->node_col_changed_id); g_signal_handler_disconnect (G_OBJECT (etta->priv->source), etta->priv->node_inserted_id); g_signal_handler_disconnect (G_OBJECT (etta->priv->source), etta->priv->node_removed_id); g_signal_handler_disconnect (G_OBJECT (etta->priv->source), etta->priv->node_request_collapse_id); g_object_unref (etta->priv->source); etta->priv->source = NULL; } G_OBJECT_CLASS (parent_class)->dispose (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; return e_tree_model_get_save_id (etta->priv->source, get_path(etta, row)); } static gboolean etta_has_change_pending (ETableModel *etm) { ETreeTableAdapter *etta = (ETreeTableAdapter *)etm; return e_tree_model_has_change_pending (etta->priv->source); } static int etta_row_count (ETableModel *etm) { ETreeTableAdapter *etta = (ETreeTableAdapter *)etm; return etta->priv->n_map; } static void * etta_value_at (ETableModel *etm, int col, int row) { ETreeTableAdapter *etta = (ETreeTableAdapter *)etm; switch (col) { case -1: if (row == -1) return NULL; return get_path (etta, row); case -2: return etta->priv->source; case -3: return etta; default: return e_tree_model_value_at (etta->priv->source, get_path (etta, row), col); } } static void etta_set_value_at (ETableModel *etm, int col, int row, const void *val) { ETreeTableAdapter *etta = (ETreeTableAdapter *)etm; e_tree_model_set_value_at (etta->priv->source, get_path (etta, row), col, val); } static gboolean etta_is_cell_editable (ETableModel *etm, int col, int row) { ETreeTableAdapter *etta = (ETreeTableAdapter *)etm; return e_tree_model_node_is_editable (etta->priv->source, get_path (etta, row), col); } static void etta_append_row (ETableModel *etm, ETableModel *source, int row) { } 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; GObjectClass *object_class = (GObjectClass *) klass; parent_class = g_type_class_peek_parent (klass); object_class->dispose = etta_dispose; object_class->finalize = etta_finalize; table_class->column_count = etta_column_count; table_class->row_count = etta_row_count; table_class->append_row = etta_append_row; 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->has_save_id = etta_has_save_id; table_class->get_save_id = etta_get_save_id; table_class->has_change_pending = etta_has_change_pending; 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->source = NULL; etta->priv->sort_info = NULL; etta->priv->n_map = 0; etta->priv->n_vals_allocated = 0; etta->priv->map_table = NULL; etta->priv->nodes = NULL; etta->priv->root = NULL; etta->priv->root_visible = TRUE; etta->priv->remap_needed = TRUE; etta->priv->pre_change_id = 0; etta->priv->no_change_id = 0; etta->priv->node_changed_id = 0; etta->priv->node_data_changed_id = 0; etta->priv->node_col_changed_id = 0; etta->priv->node_inserted_id = 0; etta->priv->node_removed_id = 0; etta->priv->node_request_collapse_id = 0; } 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_no_change (ETreeModel *etm, ETreeTableAdapter *etta) { e_table_model_no_change(E_TABLE_MODEL(etta)); } static void etta_proxy_node_changed (ETreeModel *etm, ETreePath path, ETreeTableAdapter *etta) { update_node(etta, path); e_table_model_changed(E_TABLE_MODEL(etta)); } static void etta_proxy_node_data_changed (ETreeModel *etm, ETreePath path, ETreeTableAdapter *etta) { int row = get_row(etta, path); if (row == -1) { e_table_model_no_change(E_TABLE_MODEL(etta)); return; } e_table_model_row_changed(E_TABLE_MODEL(etta), row); } static void etta_proxy_node_col_changed (ETreeModel *etm, ETreePath path, int col, ETreeTableAdapter *etta) { int row = get_row(etta, path); if (row == -1) { e_table_model_no_change(E_TABLE_MODEL(etta)); return; } e_table_model_cell_changed(E_TABLE_MODEL(etta), col, row); } static void etta_proxy_node_inserted (ETreeModel *etm, ETreePath parent, ETreePath child, ETreeTableAdapter *etta) { if (e_tree_model_node_is_root(etm, child)) generate_tree(etta, child); else insert_node(etta, parent, child); } static void etta_proxy_node_removed (ETreeModel *etm, ETreePath parent, ETreePath child, int old_position, ETreeTableAdapter *etta) { delete_node(etta, parent, child); } static void etta_proxy_node_request_collapse (ETreeModel *etm, ETreePath node, ETreeTableAdapter *etta) { e_tree_table_adapter_node_set_expanded(etta, node, FALSE); } static void etta_sort_info_changed (ETableSortInfo *sort_info, ETreeTableAdapter *etta) { e_table_model_pre_change(E_TABLE_MODEL(etta)); resort_node(etta, etta->priv->root, TRUE); fill_map(etta, 0, etta->priv->root); e_table_model_changed(E_TABLE_MODEL(etta)); } ETableModel * e_tree_table_adapter_construct (ETreeTableAdapter *etta, ETreeModel *source, ETableSortInfo *sort_info, ETableHeader *header) { ETreePath root; etta->priv->source = source; g_object_ref (source); etta->priv->sort_info = sort_info; if (sort_info) { g_object_ref(sort_info); etta->priv->sort_info_changed_id = g_signal_connect (G_OBJECT (sort_info), "sort_info_changed", G_CALLBACK (etta_sort_info_changed), etta); } etta->priv->header = header; if (header) g_object_ref(header); etta->priv->nodes = g_hash_table_new(NULL, NULL); root = e_tree_model_get_root (source); if (root) generate_tree(etta, root); etta->priv->pre_change_id = g_signal_connect(G_OBJECT(source), "pre_change", G_CALLBACK (etta_proxy_pre_change), etta); etta->priv->no_change_id = g_signal_connect (G_OBJECT (source), "no_change", G_CALLBACK (etta_proxy_no_change), etta); etta->priv->node_changed_id = g_signal_connect (G_OBJECT (source), "node_changed", G_CALLBACK (etta_proxy_node_changed), etta); etta->priv->node_data_changed_id = g_signal_connect (G_OBJECT (source), "node_data_changed", G_CALLBACK (etta_proxy_node_data_changed), etta); etta->priv->node_col_changed_id = g_signal_connect (G_OBJECT (source), "node_col_changed", G_CALLBACK (etta_proxy_node_col_changed), etta); etta->priv->node_inserted_id = g_signal_connect (G_OBJECT (source), "node_inserted", G_CALLBACK (etta_proxy_node_inserted), etta); etta->priv->node_removed_id = g_signal_connect (G_OBJECT (source), "node_removed", G_CALLBACK (etta_proxy_node_removed), etta); etta->priv->node_request_collapse_id = g_signal_connect (G_OBJECT (source), "node_request_collapse", G_CALLBACK (etta_proxy_node_request_collapse), etta); return E_TABLE_MODEL (etta); } ETableModel * e_tree_table_adapter_new (ETreeModel *source, ETableSortInfo *sort_info, ETableHeader *header) { ETreeTableAdapter *etta = g_object_new (E_TREE_TABLE_ADAPTER_TYPE, NULL); e_tree_table_adapter_construct (etta, source, sort_info, header); return (ETableModel *) etta; } typedef struct { xmlNode *root; gboolean expanded_default; ETreeModel *model; } TreeAndRoot; static void save_expanded_state_func (gpointer keyp, gpointer value, gpointer data) { ETreePath path = keyp; node_t *node = ((GNode *)value)->data; TreeAndRoot *tar = data; xmlNode *xmlnode; if (node->expanded != tar->expanded_default) { gchar *save_id = e_tree_model_get_save_id(tar->model, path); xmlnode = xmlNewChild (tar->root, NULL, "node", NULL); e_xml_set_string_prop_by_name(xmlnode, "id", save_id); g_free(save_id); } } void e_tree_table_adapter_save_expanded_state (ETreeTableAdapter *etta, const char *filename) { TreeAndRoot tar; xmlDocPtr doc; xmlNode *root; g_return_if_fail(etta != NULL); doc = xmlNewDoc ("1.0"); root = xmlNewDocNode (doc, NULL, (xmlChar *) "expanded_state", NULL); xmlDocSetRootElement (doc, root); tar.model = etta->priv->source; tar.root = root; tar.expanded_default = e_tree_model_get_expanded_default(etta->priv->source); e_xml_set_integer_prop_by_name (root, "vers", 2); e_xml_set_bool_prop_by_name (root, "default", tar.expanded_default); g_hash_table_foreach (etta->priv->nodes, save_expanded_state_func, &tar); e_xml_save_file (filename, doc); xmlFreeDoc (doc); } static xmlDoc * open_file (ETreeTableAdapter *etta, const char *filename) { xmlDoc *doc; xmlNode *root; int vers; gboolean model_default, saved_default; doc = xmlParseFile (filename); if (!doc) return NULL; root = xmlDocGetRootElement (doc); if (root == NULL || strcmp (root->name, "expanded_state")) { xmlFreeDoc (doc); return NULL; } vers = e_xml_get_integer_prop_by_name_with_default (root, "vers", 0); if (vers > 2) { xmlFreeDoc (doc); return NULL; } model_default = e_tree_model_get_expanded_default (etta->priv->source); saved_default = e_xml_get_bool_prop_by_name_with_default (root, "default", !model_default); if (saved_default != model_default) { xmlFreeDoc (doc); return NULL; } return doc; } void e_tree_table_adapter_load_expanded_state (ETreeTableAdapter *etta, const char *filename) { xmlDoc *doc; xmlNode *root, *child; gboolean model_default; g_return_if_fail(etta != NULL); doc = open_file(etta, filename); if (!doc) return; root = xmlDocGetRootElement (doc); e_table_model_pre_change(E_TABLE_MODEL(etta)); model_default = e_tree_model_get_expanded_default(etta->priv->source); for (child = root->xmlChildrenNode; child; child = child->next) { char *id; ETreePath path; 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); continue; } path = e_tree_model_get_node_by_id(etta->priv->source, id); if (path) e_tree_table_adapter_node_set_expanded(etta, path, !model_default); } xmlFreeDoc (doc); e_table_model_changed (E_TABLE_MODEL (etta)); } void e_tree_table_adapter_root_node_set_visible (ETreeTableAdapter *etta, gboolean visible) { int size; 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); } size = (visible ? 1 : 0) + (etta->priv->root ? ((node_t *)etta->priv->root->data)->num_visible_children : 0); resize_map(etta, size); if (etta->priv->root) fill_map(etta, 0, etta->priv->root); e_table_model_changed(E_TABLE_MODEL(etta)); } void e_tree_table_adapter_node_set_expanded (ETreeTableAdapter *etta, ETreePath path, gboolean expanded) { GNode *gnode = lookup_gnode(etta, path); node_t *node; int row; if (!expanded && e_tree_model_node_is_root (etta->priv->source, path) && !etta->priv->root_visible) return; if (!gnode && expanded) { ETreePath parent = e_tree_model_node_get_parent(etta->priv->source, path); g_return_if_fail(parent != NULL); e_tree_table_adapter_node_set_expanded(etta, parent, expanded); gnode = lookup_gnode(etta, path); } g_return_if_fail(gnode != NULL); node = (node_t *) gnode->data; if (expanded == node->expanded) return; node->expanded = expanded; row = get_row(etta, path); if (row == -1) return; e_table_model_pre_change (E_TABLE_MODEL(etta)); e_table_model_pre_change (E_TABLE_MODEL(etta)); e_table_model_row_changed(E_TABLE_MODEL(etta), row); if (expanded) { int num_children = insert_children(etta, gnode); update_child_counts(gnode, num_children); if (etta->priv->sort_info && e_table_sort_info_sorting_get_count(etta->priv->sort_info) > 0) resort_node(etta, gnode, TRUE); resize_map(etta, etta->priv->n_map + num_children); move_map_elements(etta, row + 1 + num_children, row + 1, etta->priv->n_map - row - 1 - num_children); fill_map(etta, row, gnode); if (num_children != 0) { e_table_model_rows_inserted(E_TABLE_MODEL(etta), row + 1, num_children); } else e_table_model_no_change(E_TABLE_MODEL(etta)); } else { int num_children = delete_children(etta, gnode); if (num_children == 0) { e_table_model_no_change(E_TABLE_MODEL(etta)); return; } move_map_elements(etta, row + 1, row + 1 + num_children, etta->priv->n_map - row - 1 - num_children); update_child_counts(gnode, - num_children); resize_map(etta, etta->priv->n_map - num_children); e_table_model_rows_deleted(E_TABLE_MODEL(etta), row + 1, 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) { return get_path(etta, row); } int e_tree_table_adapter_row_of_node (ETreeTableAdapter *etta, ETreePath path) { return get_row(etta, path); } 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); while (parent) { e_tree_table_adapter_node_set_expanded(etta, parent, TRUE); parent = e_tree_model_node_get_parent(etta->priv->source, parent); } } gboolean e_tree_table_adapter_node_is_expanded (ETreeTableAdapter *etta, ETreePath path) { node_t *node = get_node(etta, path); if (!e_tree_model_node_is_expandable (etta->priv->source, path) || !node) return FALSE; return node->expanded; } void e_tree_table_adapter_set_sort_info (ETreeTableAdapter *etta, ETableSortInfo *sort_info) { if (etta->priv->sort_info) { g_signal_handler_disconnect(G_OBJECT(etta->priv->sort_info), etta->priv->sort_info_changed_id); g_object_unref(etta->priv->sort_info); } etta->priv->sort_info = sort_info; if (sort_info) { g_object_ref(sort_info); etta->priv->sort_info_changed_id = g_signal_connect(G_OBJECT(sort_info), "sort_info_changed", G_CALLBACK(etta_sort_info_changed), etta); } if (!etta->priv->root) return; e_table_model_pre_change(E_TABLE_MODEL(etta)); resort_node(etta, etta->priv->root, TRUE); fill_map(etta, 0, etta->priv->root); e_table_model_changed(E_TABLE_MODEL(etta)); }