diff options
Diffstat (limited to 'widgets/table/e-tree-model.c')
-rw-r--r-- | widgets/table/e-tree-model.c | 1366 |
1 files changed, 0 insertions, 1366 deletions
diff --git a/widgets/table/e-tree-model.c b/widgets/table/e-tree-model.c deleted file mode 100644 index e3c4f138cd..0000000000 --- a/widgets/table/e-tree-model.c +++ /dev/null @@ -1,1366 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* - * e-tree-model.c: a Tree Model - * - * Author: - * Chris Toshok (toshok@helixcode.com) - * - * Adapted from the gtree code and ETableModel. - * - * (C) 2000 Helix Code, Inc. - */ -#include <config.h> - -#include <stdio.h> -#include <errno.h> -#include <unistd.h> -#include <fcntl.h> - -#include <gnome-xml/parser.h> -#include <gnome-xml/xmlmemory.h> - -#include <gtk/gtksignal.h> -#include <stdlib.h> -#include "gal/util/e-util.h" -#include "gal/util/e-xml-utils.h" -#include "e-tree-model.h" - -#define ETM_CLASS(e) ((ETreeModelClass *)((GtkObject *)e)->klass) - -#define PARENT_TYPE E_TABLE_MODEL_TYPE - -#define TREEPATH_CHUNK_AREA_SIZE (30 * sizeof (ETreePath)) - -static ETableModel *e_tree_model_parent_class; - -struct ETreeModelPriv { - GMemChunk *node_chunk; - ETreePath *root; - gboolean root_visible; - GArray *row_array; /* used in the mapping between ETable and our tree */ - GHashTable *expanded_state; /* used for loading/saving expanded state */ - GString *sort_group; /* for caching the last sort group info */ - gboolean expanded_default; /* whether nodes are created expanded or collapsed by default */ - gboolean pre_change; /* has there been a pre-change event on us? */ -}; - -struct ETreePath { - gboolean expanded; - gboolean expanded_set; - guint visible_descendents; - char *save_id; - ETreePathCompareFunc compare; - gpointer node_data; - - /* parent/child/sibling pointers */ - ETreePath *parent; - ETreePath *next_sibling; - ETreePath *prev_sibling; - ETreePath *first_child; - ETreePath *last_child; - guint32 num_children; -}; - -enum { - NODE_CHANGED, - NODE_INSERTED, - NODE_REMOVED, - NODE_COLLAPSED, - NODE_EXPANDED, - LAST_SIGNAL -}; - -static guint e_tree_model_signals [LAST_SIGNAL] = {0, }; - -static void add_visible_descendents_to_array (ETreeModel *etm, ETreePath *node, int *row, int *count); - - -/* ETreePath functions */ - -static int -e_tree_path_depth (ETreePath *path) -{ - int depth = 0; - while (path) { - depth ++; - path = path->parent; - } - return depth; -} - -static void -e_tree_path_insert (ETreePath *parent, int position, ETreePath *child) -{ - g_return_if_fail (position <= parent->num_children || position == -1); - - child->parent = parent; - - if (parent->first_child == NULL) - parent->first_child = child; - - if (position == -1 || position == parent->num_children) { - child->prev_sibling = parent->last_child; - if (parent->last_child) - parent->last_child->next_sibling = child; - parent->last_child = child; - } - else { - ETreePath *c; - for (c = parent->first_child; c; c = c->next_sibling) { - if (position == 0) { - child->next_sibling = c; - child->prev_sibling = c->prev_sibling; - - if (child->next_sibling) - child->next_sibling->prev_sibling = child; - if (child->prev_sibling) - child->prev_sibling->next_sibling = child; - - if (parent->first_child == c) - parent->first_child = child; - break; - } - position --; - } - } - - parent->num_children++; -} - -static void -e_tree_path_unlink (ETreePath *path) -{ - ETreePath *parent = path->parent; - - /* unlink first/last child if applicable */ - if (parent) { - if (path == parent->first_child) - parent->first_child = path->next_sibling; - if (path == parent->last_child) - parent->last_child = path->prev_sibling; - - parent->num_children --; - } - - /* unlink prev/next sibling links */ - if (path->next_sibling) - path->next_sibling->prev_sibling = path->prev_sibling; - if (path->prev_sibling) - path->prev_sibling->next_sibling = path->next_sibling; - - path->parent = NULL; - path->next_sibling = NULL; - path->prev_sibling = NULL; -} - -void -e_tree_model_node_traverse (ETreeModel *model, ETreePath *path, ETreePathFunc func, gpointer data) -{ - ETreePath *child; - - g_return_if_fail (path); - - child = path->first_child; - - while (child) { - ETreePath *next_child = child->next_sibling; - e_tree_model_node_traverse (model, child, func, data); - if (func (model, child, data) == TRUE) - return; - - child = next_child; - } -} - - -/* signal handlers */ -static void -etree_pre_change(GtkObject *object, ETreeModel *etm) -{ - ETreeModelPriv *priv = etm->priv; - - priv->pre_change = TRUE; -} - -static void -etree_changed(GtkObject *object, ETreeModel *etm) -{ - ETreeModelPriv *priv = etm->priv; - - priv->pre_change = FALSE; -} - - -/* virtual methods */ - -static void -etree_destroy (GtkObject *object) -{ - ETreeModel *etree = E_TREE_MODEL (object); - ETreeModelPriv *priv = etree->priv; - - /* XXX lots of stuff to free here */ - g_array_free (priv->row_array, TRUE); - g_hash_table_destroy (priv->expanded_state); - - g_string_free(priv->sort_group, TRUE); - - g_free (priv); - - GTK_OBJECT_CLASS (e_tree_model_parent_class)->destroy (object); -} - -static ETreePath* -etree_get_root (ETreeModel *etm) -{ - ETreeModelPriv *priv = etm->priv; - return priv->root; -} - -static ETreePath* -etree_get_parent (ETreeModel *etm, ETreePath *path) -{ - g_return_val_if_fail (path, NULL); - - return path->parent; -} - -static ETreePath* -etree_get_next (ETreeModel *etm, ETreePath *node) -{ - g_return_val_if_fail (node, NULL); - - return node->next_sibling; -} - -static ETreePath* -etree_get_prev (ETreeModel *etm, ETreePath *node) -{ - g_return_val_if_fail (node, NULL); - - return node->prev_sibling; -} - -static ETreePath* -etree_get_first_child (ETreeModel *etm, ETreePath *node) -{ - g_return_val_if_fail (node, NULL); - - return node->first_child; -} - -static ETreePath* -etree_get_last_child (ETreeModel *etm, ETreePath *node) -{ - g_return_val_if_fail (node, NULL); - - return node->last_child; -} - -static guint -etree_get_children (ETreeModel *etm, ETreePath* node, ETreePath ***paths) -{ - guint n_children; - - g_return_val_if_fail (node, 0); - - n_children = node->num_children; - - if (paths) { - ETreePath *p; - int i = 0; - (*paths) = g_malloc (sizeof (ETreePath*) * n_children); - for (p = node->first_child; p; p = p->next_sibling) { - (*paths)[i++] = p; - } - } - - return n_children; -} - -static gboolean -etree_is_expanded (ETreeModel *etm, ETreePath* node) -{ - g_return_val_if_fail (node, FALSE); - - return node->expanded; -} - -static gboolean -etree_is_visible (ETreeModel *etm, ETreePath* node) -{ - g_return_val_if_fail (node, FALSE); - - for (node = node->parent; node; node = node->parent) { - if (!node->expanded) - return FALSE; - } - - return TRUE; -} - -static void -etree_set_expanded (ETreeModel *etm, ETreePath* node, gboolean expanded) -{ - ETreeModelPriv *priv = etm->priv; - ETreePath *child; - int row; - - g_return_if_fail (node); - - node->expanded_set = TRUE; - - if (node->expanded == expanded) - return; - - if (expanded) { - gboolean allow_expand = TRUE; - e_tree_model_node_expanded (etm, node, &allow_expand); - if (!allow_expand) - return; - } - - node->expanded = expanded; - if (node->save_id) { - g_hash_table_insert (priv->expanded_state, node->save_id, (gpointer)expanded); - } - - /* if the node wasn't visible at present */ - if ((row = e_tree_model_row_of_node (etm, node)) == -1) { - if (!expanded) { - e_tree_model_node_collapsed (etm, node); - } - return; - } - - row++; - - if (expanded) { - ETreePath *parent; - - if (e_tree_model_node_is_visible (etm, node)) { - node->visible_descendents = 0; - for (child = node->first_child; child; - child = child->next_sibling) { - add_visible_descendents_to_array (etm, child, &row, &node->visible_descendents); - } - } - /* now iterate back up the tree, adding to our - ancestors' visible descendents */ - - for (parent = node->parent; parent; parent = parent->parent) { - parent->visible_descendents += node->visible_descendents; - } - } - else { - int i; - ETreePath *parent; - - if (e_tree_model_node_is_visible (etm, node)) { - for (i = 0; i < node->visible_descendents; i ++) { - priv->row_array = g_array_remove_index (priv->row_array, row); - e_table_model_row_deleted (E_TABLE_MODEL (etm), row); - } - } - /* now iterate back up the tree, subtracting from our - ancestors' visible descendents */ - - for (parent = node->parent; parent; parent = parent->parent) { - parent->visible_descendents -= node->visible_descendents; - } - - node->visible_descendents = 0; - - e_tree_model_node_collapsed (etm, node); - } -} - -void -e_tree_model_set_expanded_default (ETreeModel *etree, - gboolean expanded) -{ - ETreeModelPriv *priv = etree->priv; - - priv->expanded_default = expanded; -} - -/* fairly naive implementation */ -static void -etree_set_expanded_recurse (ETreeModel *etm, ETreePath* node, gboolean expanded) -{ - ETreePath *child; - - e_tree_model_node_set_expanded (etm, node, expanded); - - for (child = node->first_child; child; child = child->next_sibling) - e_tree_model_node_set_expanded_recurse (etm, child, expanded); -} - -static ETreePath * -etree_node_at_row (ETreeModel *etree, int row) -{ - ETreeModelPriv *priv = etree->priv; - - g_return_val_if_fail (row < priv->row_array->len, NULL); - - return g_array_index (priv->row_array, ETreePath*, row); -} - - -/* ETable analogs */ -static void* -etree_value_at (ETreeModel *etm, ETreePath* node, int col) -{ - /* shouldn't be called */ - g_assert (0); - return NULL; -} - -static GdkPixbuf* -etree_icon_at (ETreeModel *etm, ETreePath* node) -{ - /* shouldn't be called */ - g_assert (0); - return NULL; -} - -static void -etree_set_value_at (ETreeModel *etm, ETreePath* node, int col, const void *val) -{ - /* shouldn't be called */ - g_assert (0); -} - -static gboolean -etree_is_editable (ETreeModel *etm, ETreePath* node, int col) -{ - /* shouldn't be called */ - g_assert(0); - return FALSE; -} - - -/* ETable virtual functions we map */ -static int -etable_row_count (ETableModel *etm) -{ - ETreeModel *tree = E_TREE_MODEL (etm); - ETreeModelPriv *priv = tree->priv; - return priv->row_array->len; -} - -static void * -etable_value_at (ETableModel *etm, int col, int row) -{ - ETreeModel *etree = E_TREE_MODEL(etm); - ETreeModelClass *et_class = ETM_CLASS(etm); - ETreePath* node = e_tree_model_node_at_row (etree, row); - - g_return_val_if_fail (node, NULL); - - if (col == -1) - return node; - else if (col == -2) - return etm; - else - return et_class->value_at (etree, node, col); -} - -static void -etable_set_value_at (ETableModel *etm, int col, int row, const void *val) -{ - ETreeModel *etree = E_TREE_MODEL(etm); - ETreeModelClass *et_class = ETM_CLASS(etm); - ETreePath* node = e_tree_model_node_at_row (etree, row); - - g_return_if_fail (node); - - et_class->set_value_at (etree, node, col, val); -} - -static gboolean -etable_is_cell_editable (ETableModel *etm, int col, int row) -{ - ETreeModel *etree = E_TREE_MODEL(etm); - ETreeModelClass *et_class = ETM_CLASS(etm); - ETreePath* node = e_tree_model_node_at_row (etree, row); - - g_return_val_if_fail (node, FALSE); - - return et_class->is_editable (etree, node, col); -} - -static void -build_sort_group(GString *out, ETreePath *node) -{ - if (node->parent) { - build_sort_group(out, node->parent); - } - g_string_sprintfa(out, "/%p", node); -} - -static const char * -etable_row_sort_group(ETableModel *etm, int row) -{ - ETreeModel *etree = E_TREE_MODEL(etm); - ETreeModelPriv *priv = etree->priv; - ETreePath* node = e_tree_model_node_at_row (etree, row); - - g_return_val_if_fail (node, ""); - - g_string_truncate(priv->sort_group, 0); - if (node) - build_sort_group(priv->sort_group, node); - - return priv->sort_group->str; -} - -static gboolean -etable_has_sort_group(ETableModel *etm) -{ - /* could optimise for the flat &/or unexpanded tree case */ - return TRUE; -} - - -static void -e_tree_model_class_init (GtkObjectClass *klass) -{ - ETableModelClass *table_class = (ETableModelClass *) klass; - ETreeModelClass *tree_class = (ETreeModelClass *) klass; - - e_tree_model_parent_class = gtk_type_class (PARENT_TYPE); - - klass->destroy = etree_destroy; - - e_tree_model_signals [NODE_CHANGED] = - gtk_signal_new ("node_changed", - GTK_RUN_LAST, - klass->type, - GTK_SIGNAL_OFFSET (ETreeModelClass, node_changed), - gtk_marshal_NONE__POINTER, - GTK_TYPE_NONE, 1, GTK_TYPE_POINTER); - - e_tree_model_signals [NODE_INSERTED] = - gtk_signal_new ("node_inserted", - GTK_RUN_LAST, - klass->type, - GTK_SIGNAL_OFFSET (ETreeModelClass, node_inserted), - gtk_marshal_NONE__POINTER_POINTER, - GTK_TYPE_NONE, 2, GTK_TYPE_POINTER, GTK_TYPE_POINTER); - - e_tree_model_signals [NODE_REMOVED] = - gtk_signal_new ("node_removed", - GTK_RUN_LAST, - klass->type, - GTK_SIGNAL_OFFSET (ETreeModelClass, node_removed), - gtk_marshal_NONE__POINTER_POINTER, - GTK_TYPE_NONE, 2, GTK_TYPE_POINTER, GTK_TYPE_POINTER); - - e_tree_model_signals [NODE_COLLAPSED] = - gtk_signal_new ("node_collapsed", - GTK_RUN_LAST, - klass->type, - GTK_SIGNAL_OFFSET (ETreeModelClass, node_collapsed), - gtk_marshal_NONE__POINTER, - GTK_TYPE_NONE, 1, GTK_TYPE_POINTER); - - e_tree_model_signals [NODE_EXPANDED] = - gtk_signal_new ("node_expanded", - GTK_RUN_LAST, - klass->type, - GTK_SIGNAL_OFFSET (ETreeModelClass, node_expanded), - gtk_marshal_NONE__POINTER_POINTER, - GTK_TYPE_NONE, 2, GTK_TYPE_POINTER, GTK_TYPE_POINTER); - - gtk_object_class_add_signals (klass, e_tree_model_signals, LAST_SIGNAL); - - table_class->row_count = etable_row_count; - table_class->value_at = etable_value_at; - table_class->set_value_at = etable_set_value_at; - table_class->is_cell_editable = etable_is_cell_editable; -#if 0 - /* XX need to pass these through */ - table_class->duplicate_value = etable_duplicate_value; - table_class->free_value = etable_free_value; - table_class->initialize_value = etable_initialize_value; - table_class->value_is_empty = etable_value_is_empty; - table_class->value_to_string = etable_value_to_string; - table_class->thaw = etable_thaw; -#endif - - table_class->row_sort_group = etable_row_sort_group; - table_class->has_sort_group = etable_has_sort_group; - - tree_class->get_root = etree_get_root; - tree_class->get_prev = etree_get_prev; - tree_class->get_next = etree_get_next; - tree_class->get_first_child = etree_get_first_child; - tree_class->get_last_child = etree_get_last_child; - tree_class->get_parent = etree_get_parent; - - tree_class->value_at = etree_value_at; - tree_class->icon_at = etree_icon_at; - tree_class->set_value_at = etree_set_value_at; - tree_class->is_editable = etree_is_editable; - - tree_class->get_children = etree_get_children; - tree_class->is_expanded = etree_is_expanded; - tree_class->is_visible = etree_is_visible; - tree_class->set_expanded = etree_set_expanded; - tree_class->set_expanded_recurse = etree_set_expanded_recurse; - tree_class->node_at_row = etree_node_at_row; -} - -static void -e_tree_init (GtkObject *object) -{ - ETreeModel *etree = (ETreeModel *)object; - e_tree_model_construct (etree); -} - -E_MAKE_TYPE(e_tree_model, "ETreeModel", ETreeModel, e_tree_model_class_init, e_tree_init, PARENT_TYPE) - - -/* signals */ -void -e_tree_model_node_changed (ETreeModel *tree_model, ETreePath *node) -{ - int row; - g_return_if_fail (tree_model != NULL); - g_return_if_fail (E_IS_TREE_MODEL (tree_model)); - - row = e_tree_model_row_of_node (tree_model, node); - if (row != -1) - e_table_model_row_changed (E_TABLE_MODEL (tree_model), row); - - gtk_signal_emit (GTK_OBJECT (tree_model), - e_tree_model_signals [NODE_CHANGED]); -} - -void -e_tree_model_node_inserted (ETreeModel *tree_model, - ETreePath *parent_node, - ETreePath *inserted_node) -{ - int row; - g_return_if_fail (tree_model != NULL); - g_return_if_fail (E_IS_TREE_MODEL (tree_model)); - - row = e_tree_model_row_of_node (tree_model, inserted_node); - if (row != -1) - e_table_model_row_inserted (E_TABLE_MODEL (tree_model), row); - - gtk_signal_emit (GTK_OBJECT (tree_model), - e_tree_model_signals [NODE_INSERTED], - parent_node, inserted_node); -} - -void -e_tree_model_node_removed (ETreeModel *tree_model, ETreePath *parent_node, ETreePath *removed_node) -{ - int row; - - g_return_if_fail (tree_model != NULL); - g_return_if_fail (E_IS_TREE_MODEL (tree_model)); - - row = e_tree_model_row_of_node (tree_model, removed_node); - if (row != -1) - e_table_model_row_deleted (E_TABLE_MODEL (tree_model), row); - - gtk_signal_emit (GTK_OBJECT (tree_model), - e_tree_model_signals [NODE_REMOVED], - parent_node, removed_node); -} - -void -e_tree_model_node_collapsed (ETreeModel *tree_model, ETreePath *node) -{ - g_return_if_fail (tree_model != NULL); - g_return_if_fail (E_IS_TREE_MODEL (tree_model)); - - gtk_signal_emit (GTK_OBJECT (tree_model), - e_tree_model_signals [NODE_COLLAPSED], - node); -} - -void -e_tree_model_node_expanded (ETreeModel *tree_model, ETreePath *node, gboolean *allow_expand) -{ - g_return_if_fail (tree_model != NULL); - g_return_if_fail (E_IS_TREE_MODEL (tree_model)); - - gtk_signal_emit (GTK_OBJECT (tree_model), - e_tree_model_signals [NODE_EXPANDED], - node, allow_expand); -} - - - -void -e_tree_model_construct (ETreeModel *etree) -{ - ETreeModelPriv *priv = g_new0 (ETreeModelPriv, 1); - - etree->priv = priv; - - priv->node_chunk = g_mem_chunk_create (ETreePath, TREEPATH_CHUNK_AREA_SIZE, G_ALLOC_AND_FREE); - priv->root = NULL; - priv->root_visible = TRUE; - priv->row_array = g_array_new (FALSE, FALSE, sizeof(ETreePath*)); - priv->expanded_state = g_hash_table_new (g_str_hash, g_str_equal); - priv->sort_group = g_string_new(""); - - gtk_signal_connect((GtkObject *)etree, "model_pre_change", etree_pre_change, etree); - gtk_signal_connect((GtkObject *)etree, "model_changed", etree_changed, etree); -} - -ETreeModel * -e_tree_model_new () -{ - ETreeModel *et; - - et = gtk_type_new (e_tree_model_get_type ()); - - return et; -} - -ETreePath * -e_tree_model_get_root (ETreeModel *etree) -{ - return ETM_CLASS(etree)->get_root(etree); -} - -ETreePath * -e_tree_model_node_at_row (ETreeModel *etree, int row) -{ - return ETM_CLASS(etree)->node_at_row (etree, row); -} - -GdkPixbuf * -e_tree_model_icon_of_node (ETreeModel *etree, ETreePath *path) -{ - return ETM_CLASS(etree)->icon_at (etree, path); -} - -int -e_tree_model_row_of_node (ETreeModel *etree, ETreePath *node) -{ - ETreeModelPriv *priv = etree->priv; - int i; - - for (i = 0; i < priv->row_array->len; i ++) - if (g_array_index (priv->row_array, ETreePath*, i) == node) - return i; - - return -1; -} - -void -e_tree_model_root_node_set_visible (ETreeModel *etm, gboolean visible) -{ - ETreeModelPriv *priv = etm->priv; - - if (visible != priv->root_visible) { - priv->root_visible = visible; - if (priv->root) { - if (visible) { - priv->row_array = g_array_insert_val (priv->row_array, 0, priv->root); - } - else { - ETreePath *root_path = e_tree_model_get_root (etm); - e_tree_model_node_set_expanded (etm, root_path, TRUE); - priv->row_array = g_array_remove_index (priv->row_array, 0); - } - - e_table_model_changed (E_TABLE_MODEL (etm)); - } - } -} - -gboolean -e_tree_model_root_node_is_visible (ETreeModel *etm) -{ - ETreeModelPriv *priv = etm->priv; - return priv->root_visible; -} - -ETreePath * -e_tree_model_node_get_first_child (ETreeModel *etree, ETreePath *node) -{ - return ETM_CLASS(etree)->get_first_child(etree, node); -} - -ETreePath * -e_tree_model_node_get_last_child (ETreeModel *etree, ETreePath *node) -{ - return ETM_CLASS(etree)->get_last_child(etree, node); -} - - -ETreePath * -e_tree_model_node_get_next (ETreeModel *etree, ETreePath *node) -{ - return ETM_CLASS(etree)->get_next(etree, node); -} - -ETreePath * -e_tree_model_node_get_prev (ETreeModel *etree, ETreePath *node) -{ - return ETM_CLASS(etree)->get_prev(etree, node); -} - -guint -e_tree_model_node_depth (ETreeModel *etree, ETreePath *path) -{ - return e_tree_path_depth (path) - 1; -} - -ETreePath * -e_tree_model_node_get_parent (ETreeModel *etree, ETreePath *path) -{ - return ETM_CLASS(etree)->get_parent(etree, path); -} - -gboolean -e_tree_model_node_is_root (ETreeModel *etree, ETreePath *path) -{ - return (e_tree_model_node_depth (etree, path) == 0); -} - -gboolean -e_tree_model_node_is_expandable (ETreeModel *etree, ETreePath *path) -{ - return (e_tree_model_node_get_children (etree, path, NULL) > 0); -} - -gboolean -e_tree_model_node_is_expanded (ETreeModel *etree, ETreePath *path) -{ - return ETM_CLASS(etree)->is_expanded (etree, path); -} - -gboolean -e_tree_model_node_is_visible (ETreeModel *etree, ETreePath *path) -{ - return ETM_CLASS(etree)->is_visible (etree, path); -} - -void -e_tree_model_node_set_expanded (ETreeModel *etree, ETreePath *path, gboolean expanded) -{ - ETM_CLASS(etree)->set_expanded (etree, path, expanded); -} - -void -e_tree_model_node_set_expanded_recurse (ETreeModel *etree, ETreePath *path, gboolean expanded) -{ - ETM_CLASS(etree)->set_expanded_recurse (etree, path, expanded); -} - -guint -e_tree_model_node_get_children (ETreeModel *etree, ETreePath *path, ETreePath ***paths) -{ - return ETM_CLASS(etree)->get_children (etree, path, paths); -} - -guint -e_tree_model_node_num_visible_descendents (ETreeModel *etm, ETreePath *node) -{ - return node->visible_descendents; -} - -gpointer -e_tree_model_node_get_data (ETreeModel *etm, ETreePath *node) -{ - g_return_val_if_fail (node, NULL); - - return node->node_data; -} - -void -e_tree_model_node_set_data (ETreeModel *etm, ETreePath *node, gpointer node_data) -{ - g_return_if_fail (node); - - node->node_data = node_data; -} - -ETreePath* -e_tree_model_node_insert (ETreeModel *tree_model, - ETreePath *parent_path, - int position, - gpointer node_data) -{ - ETreeModelPriv *priv; - ETreePath *new_path; - - priv = tree_model->priv; - - g_return_val_if_fail (parent_path != NULL || priv->root == NULL, NULL); - - priv = tree_model->priv; - - new_path = g_chunk_new0 (ETreePath, priv->node_chunk); - - new_path->expanded = FALSE; - new_path->node_data = node_data; - - if (parent_path != NULL) { - - if (parent_path->first_child == NULL - && !parent_path->expanded_set) { - e_tree_model_node_set_expanded (tree_model, - parent_path, - priv->expanded_default); - } - - e_tree_path_insert (parent_path, position, new_path); - - if (e_tree_model_node_is_visible (tree_model, new_path)) { - int parent_row, child_offset = 0; - ETreePath *n; - - /* we need to iterate back up to the root, incrementing the number of visible - descendents */ - for (n = parent_path; n; n = n->parent) { - n->visible_descendents ++; - } - - /* determine if we are inserting at the end of this parent */ - if (position == -1 || position == parent_path->num_children) { - position = e_tree_model_node_num_visible_descendents (tree_model, parent_path) - 1; - } else { - /* if we're not inserting at the end of the array, position is the child node we're - inserting at, not the absolute row position - need to count expanded nodes before it too */ - int i = position; - - n = e_tree_model_node_get_first_child(tree_model, parent_path); - while (n != NULL && i > 0) { - child_offset += n->visible_descendents; - n = n->next_sibling; - i--; - } - } - - - /* if the parent is the root, no need to search for its position since it aint there */ - if (parent_path->parent == NULL) { - parent_row = -1; - } else { - parent_row = e_tree_model_row_of_node (tree_model, parent_path); - } - - priv->row_array = g_array_insert_val (priv->row_array, - parent_row + position + 1 + child_offset, new_path); - - /* only do this if we know a changed signal isn't coming later on */ - if (!priv->pre_change) - e_table_model_row_inserted (E_TABLE_MODEL(tree_model), parent_row + position + 1 + child_offset); - } - - if (parent_path->compare) - e_tree_model_node_sort (tree_model, parent_path); - } - else { - priv->root = new_path; - if (priv->root_visible) { - priv->row_array = g_array_insert_val (priv->row_array, 0, priv->root); - e_table_model_row_inserted (E_TABLE_MODEL (tree_model), 0); - } - else { - /* need to mark the new node as expanded or - we'll never see it's children */ - new_path->expanded = TRUE; - new_path->expanded_set = TRUE; - } - } - - return new_path; -} - -ETreePath* -e_tree_model_node_insert_id (ETreeModel *tree_model, - ETreePath *parent_path, - int position, - gpointer node_data, - const char *save_id) -{ - ETreePath *path = e_tree_model_node_insert (tree_model, parent_path, position, node_data); - - e_tree_model_node_set_save_id (tree_model, path, save_id); - - return path; -} - - -ETreePath * -e_tree_model_node_insert_before (ETreeModel *etree, - ETreePath *parent, - ETreePath *sibling, - gpointer node_data) -{ - ETreePath *child; - int position = 0; - for (child = parent->first_child; child; child = child->next_sibling) { - if (child == sibling) - break; - position ++; - } - return e_tree_model_node_insert (etree, parent, position, node_data); -} - -/* just blows away child data, doesn't take into account unlinking/etc */ -static void -child_free(ETreeModel *etree, ETreePath *node) -{ - ETreePath *child, *next; - ETreeModelPriv *priv = etree->priv; - - child = node->first_child; - while (child) { - next = child->next_sibling; - child_free(etree, child); - child = next; - } - - if (node->save_id) { - g_hash_table_remove(priv->expanded_state, node->save_id); - g_free(node->save_id); - } - g_chunk_free(node, priv->node_chunk); -} - -gpointer -e_tree_model_node_remove (ETreeModel *etree, ETreePath *path) -{ - ETreeModelPriv *priv = etree->priv; - ETreePath *parent = path->parent; - gpointer ret = path->node_data; - int row, visible = 0, base = 0; - gboolean dochanged; - - /* work out what range of visible rows to remove */ - if (parent) { - if (e_tree_model_node_is_visible(etree, path)) { - base = e_tree_model_row_of_node(etree, path); - visible = path->visible_descendents + 1; - } - } else if (path == priv->root) { - priv->root = NULL; - if (priv->root_visible) { - base = 0; - visible = path->visible_descendents + 1; - } else { - base = 0; - visible = path->visible_descendents; - } - } else { - g_warning("Trying to remove invalid path %p", path); - return NULL; - } - - /* unlink this node - we only have to unlink the root node being removed, - since the others are only references from this node */ - e_tree_path_unlink (path); - - /*printf("removing %d nodes from position %d\n", visible, base);*/ - - if (visible > 0) { - /* fix up the parent visible counts */ - for (; parent; parent = parent->parent) { - parent->visible_descendents -= visible; - } - - /* if we have a lot of nodes to remove, then we dont row_deleted each one */ - /* could probably be tuned, but this'll do, since its normally only when we - remove the whole lot do we really care */ - dochanged = (visible > 1000) || (visible > (priv->row_array->len / 4)); - - /* and physically remove them */ - if (visible == priv->row_array->len) { - g_array_set_size(priv->row_array, 0); - } else { - memmove(&g_array_index(priv->row_array, ETreePath *, base), - &g_array_index(priv->row_array, ETreePath *, base+visible), - (visible) * sizeof(ETreePath *)); - g_array_set_size(priv->row_array, priv->row_array->len - visible); - } - - /* tell the system we've removed (these) nodes */ - if (!priv->pre_change) { - if (dochanged) { - e_table_model_changed(E_TABLE_MODEL(etree)); - } else { - for (row=visible-1;row>=0;row--) { - e_table_model_row_deleted(E_TABLE_MODEL(etree), row+base); - } - } - } - } - - child_free(etree, path); - - return ret; -} - -static void -add_visible_descendents_to_array (ETreeModel *etm, ETreePath *node, int *row, int *count) -{ - ETreeModelPriv *priv = etm->priv; - - /* add a row for this node */ - priv->row_array = g_array_insert_val (priv->row_array, (*row), node); - if (!priv->pre_change) - e_table_model_row_inserted (E_TABLE_MODEL (etm), (*row)); - (*row) ++; - (*count) ++; - - /* then loop over its children, calling this routine for each - of them */ - if (node->expanded) { - ETreePath *child; - for (child = node->first_child; child; - child = child->next_sibling) { - add_visible_descendents_to_array (etm, child, row, count); - } - } -} - -static void -save_expanded_state_func (char *key, gboolean expanded, gpointer user_data) -{ - if (expanded) { - xmlNode *root = (xmlNode*)user_data; - xmlNode *node_root = xmlNewNode (NULL, (xmlChar*) "node"); - - xmlAddChild (root, node_root); - xmlNewChild (node_root, NULL, (xmlChar *) "id", (xmlChar*) key); - } -} - -gboolean -e_tree_model_save_expanded_state (ETreeModel *etm, const char *filename) -{ - xmlDoc *doc; - xmlNode *root; - int fd, rv; - xmlChar *buf; - int buf_size; - ETreeModelPriv *priv = etm->priv; - - doc = xmlNewDoc ((xmlChar*) "1.0"); - root = xmlNewDocNode (doc, NULL, - (xmlChar *) "expanded_state", - NULL); - xmlDocSetRootElement (doc, root); - - g_hash_table_foreach (priv->expanded_state, - (GHFunc)save_expanded_state_func, - root); - - fd = open (filename, O_CREAT | O_TRUNC | O_WRONLY, 0777); - - xmlDocDumpMemory (doc, &buf, &buf_size); - - if (buf == NULL) { - g_error ("Failed to write %s: xmlBufferCreate() == NULL", filename); - return FALSE; - } - - rv = write (fd, buf, buf_size); - xmlFree (buf); - close (fd); - - if (0 > rv) { - g_error ("Failed to write new %s: %d\n", filename, errno); - unlink (filename); - return FALSE; - } - - return TRUE; -} - -static char * -get_string_value (xmlNode *node, - const char *name) -{ - xmlNode *p; - xmlChar *xml_string; - char *retval; - - p = e_xml_get_child_by_name (node, (xmlChar *) name); - if (p == NULL) - return NULL; - - p = e_xml_get_child_by_name (p, (xmlChar *) "text"); - if (p == NULL) /* there's no text between the tags, return the empty string */ - return g_strdup(""); - - xml_string = xmlNodeListGetString (node->doc, p, 1); - retval = g_strdup ((char *) xml_string); - xmlFree (xml_string); - - return retval; -} - -gboolean -e_tree_model_load_expanded_state (ETreeModel *etm, const char *filename) -{ - ETreeModelPriv *priv = etm->priv; - xmlDoc *doc; - xmlNode *root; - xmlNode *child; - - doc = xmlParseFile (filename); - if (!doc) - return FALSE; - - root = xmlDocGetRootElement (doc); - if (root == NULL || strcmp (root->name, "expanded_state")) { - xmlFreeDoc (doc); - return FALSE; - } - - for (child = root->childs; child; child = child->next) { - char *id; - - if (strcmp (child->name, "node")) { - g_warning ("unknown node '%s' in %s", child->name, filename); - continue; - } - - id = get_string_value (child, "id"); - - g_hash_table_insert (priv->expanded_state, id, (gpointer)TRUE); - } - - xmlFreeDoc (doc); - - return TRUE; -} - - -void -e_tree_model_node_set_save_id (ETreeModel *etm, ETreePath *node, const char *id) -{ - char *key; - gboolean expanded_state; - ETreeModelPriv *priv; - - g_return_if_fail (E_TREE_MODEL (etm)); - g_return_if_fail (node); - - priv = etm->priv; - - if (g_hash_table_lookup_extended (priv->expanded_state, - id, (gpointer*)&key, (gpointer*)&expanded_state)) { - - e_tree_model_node_set_expanded (etm, node, - expanded_state); - - /* important that this comes after the e_tree_model_node_set_expanded */ - node->save_id = key; - } - else { - node->save_id = g_strdup (id); - - g_hash_table_insert (priv->expanded_state, node->save_id, (gpointer)node->expanded); - } -} - - - -void -e_tree_model_node_set_compare_function (ETreeModel *tree_model, - ETreePath *node, - ETreePathCompareFunc compare) -{ - gboolean need_sort; - - g_return_if_fail (E_TREE_MODEL (tree_model)); - g_return_if_fail (node); - - need_sort = (compare != node->compare); - - node->compare = compare; - - if (need_sort) - e_tree_model_node_sort (tree_model, node); -} - -typedef struct { - ETreeModel *model; - ETreePath *path; - ETreePathCompareFunc compare; - gboolean was_expanded; -} ETreeSortInfo; - -static gint -e_tree_model_node_compare (ETreeSortInfo *info1, ETreeSortInfo *info2) -{ - return info1->compare (info1->model, info1->path, info2->path); -} - -void -e_tree_model_node_sort (ETreeModel *tree_model, - ETreePath *node) -{ - int num_nodes = node->num_children; - ETreeSortInfo *sort_info; - int i; - int child_index; - gboolean node_expanded = e_tree_model_node_is_expanded (tree_model, node); - ETreeModelPriv *priv = tree_model->priv;; - - g_return_if_fail (E_TREE_MODEL (tree_model)); - g_return_if_fail (node); - - g_return_if_fail (node->compare); - - if (num_nodes == 0) - return; - - sort_info = g_new (ETreeSortInfo, num_nodes); - - child_index = e_tree_model_row_of_node (tree_model, node) + 1; - - /* collect our info and remove the children */ - for (i = 0; i < num_nodes; i ++) { - sort_info[i].path = node->first_child; - sort_info[i].was_expanded = e_tree_model_node_is_expanded (tree_model, sort_info[i].path); - sort_info[i].model = tree_model; - sort_info[i].compare = node->compare; - - e_tree_model_node_set_expanded(tree_model, sort_info[i].path, FALSE); - if (node_expanded) - priv->row_array = g_array_remove_index (priv->row_array, child_index); - e_tree_path_unlink (sort_info[i].path); - } - - /* sort things */ - qsort (sort_info, num_nodes, sizeof(ETreeSortInfo), (GCompareFunc)e_tree_model_node_compare); - - /* reinsert the children nodes into the tree in the sorted order */ - for (i = 0; i < num_nodes; i ++) { - e_tree_path_insert (node, i, sort_info[i].path); - if (node_expanded) - priv->row_array = g_array_insert_val (priv->row_array, child_index + i, - sort_info[i].path); - } - - /* make another pass expanding the children as needed. - - XXX this used to be in the loop above, but a recent change - (either here or in the etable code) causes an assertion and - a crash */ - for (i = 0; i < num_nodes; i ++) { - e_tree_model_node_set_expanded (tree_model, sort_info[i].path, sort_info[i].was_expanded); - } - - g_free (sort_info); - - if (!priv->pre_change) - e_table_model_changed (E_TABLE_MODEL (tree_model)); -} - |