/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ /* * e-tree-memory.c: a Tree Model implementation that the programmer builds in memory. * * Author: * Chris Toshok (toshok@ximian.com) * Chris Lahey * * Adapted from the gtree code and ETableModel. * * (C) 2000, 2001 Ximian, Inc. */ #include #include #include #include #include #include #include #include #include #include "gal/util/e-util.h" #include "gal/util/e-xml-utils.h" #include "e-tree-memory.h" #define PARENT_TYPE E_TREE_MODEL_TYPE #define TREEPATH_CHUNK_AREA_SIZE (30 * sizeof (ETreeMemoryPath)) static ETreeModel *parent_class; static GMemChunk *node_chunk; typedef struct ETreeMemoryPath ETreeMemoryPath; struct ETreeMemoryPath { gpointer node_data; /* parent/child/sibling pointers */ ETreeMemoryPath *parent; ETreeMemoryPath *next_sibling; ETreeMemoryPath *prev_sibling; ETreeMemoryPath *first_child; ETreeMemoryPath *last_child; gint num_children; }; struct ETreeMemoryPriv { ETreeMemoryPath *root; gboolean expanded_default; /* whether nodes are created expanded or collapsed by default */ gint frozen; GFunc destroy_func; gpointer destroy_user_data; }; /* ETreeMemoryPath functions */ static int e_tree_memory_path_depth (ETreeMemoryPath *path) { int depth = 0; g_return_val_if_fail(path != NULL, -1); for ( path = path->parent; path; path = path->parent) depth ++; return depth; } static void e_tree_memory_path_insert (ETreeMemoryPath *parent, int position, ETreeMemoryPath *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 { ETreeMemoryPath *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 (ETreeMemoryPath *path) { ETreeMemoryPath *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; } /** * e_tree_memory_freeze: * @etmm: the ETreeModel to freeze. * * This function prepares an ETreeModel for a period of much change. * All signals regarding changes to the tree are deferred until we * thaw the tree. * **/ void e_tree_memory_freeze(ETreeMemory *etmm) { ETreeMemoryPriv *priv = etmm->priv; e_tree_model_pre_change(E_TREE_MODEL(etmm)); priv->frozen ++; } /** * e_tree_memory_thaw: * @etmm: the ETreeMemory to thaw. * * This function thaws an ETreeMemory. All the defered signals can add * up to a lot, we don't know - so we just emit a model_changed * signal. * **/ void e_tree_memory_thaw(ETreeMemory *etmm) { ETreeMemoryPriv *priv = etmm->priv; if (priv->frozen > 0) priv->frozen --; if (priv->frozen == 0) { e_tree_model_node_changed(E_TREE_MODEL(etmm), priv->root); } } /* virtual methods */ static void etmm_destroy (GtkObject *object) { ETreeMemory *etmm = E_TREE_MEMORY (object); ETreeMemoryPriv *priv = etmm->priv; /* XXX lots of stuff to free here */ if (priv->root) e_tree_memory_node_remove (etmm, priv->root); g_free (priv); GTK_OBJECT_CLASS (parent_class)->destroy (object); } static ETreePath etmm_get_root (ETreeModel *etm) { ETreeMemoryPriv *priv = E_TREE_MEMORY(etm)->priv; return priv->root; } static ETreePath etmm_get_parent (ETreeModel *etm, ETreePath node) { ETreeMemoryPath *path = node; return path->parent; } static ETreePath etmm_get_first_child (ETreeModel *etm, ETreePath node) { ETreeMemoryPath *path = node; return path->first_child; } static ETreePath etmm_get_last_child (ETreeModel *etm, ETreePath node) { ETreeMemoryPath *path = node; return path->last_child; } static ETreePath etmm_get_next (ETreeModel *etm, ETreePath node) { ETreeMemoryPath *path = node; return path->next_sibling; } static ETreePath etmm_get_prev (ETreeModel *etm, ETreePath node) { ETreeMemoryPath *path = node; return path->prev_sibling; } static gboolean etmm_is_root (ETreeModel *etm, ETreePath node) { ETreeMemoryPath *path = node; return e_tree_memory_path_depth (path) == 0; } static gboolean etmm_is_expandable (ETreeModel *etm, ETreePath node) { ETreeMemoryPath *path = node; return path->first_child != NULL; } static guint etmm_get_children (ETreeModel *etm, ETreePath node, ETreePath **nodes) { ETreeMemoryPath *path = node; guint n_children; n_children = path->num_children; if (nodes) { ETreeMemoryPath *p; int i = 0; (*nodes) = g_malloc (sizeof (ETreePath) * n_children); for (p = path->first_child; p; p = p->next_sibling) { (*nodes)[i++] = p; } } return n_children; } static guint etmm_depth (ETreeModel *etm, ETreePath path) { return e_tree_memory_path_depth(path); } static gboolean etmm_get_expanded_default (ETreeModel *etm) { ETreeMemory *etmm = E_TREE_MEMORY (etm); ETreeMemoryPriv *priv = etmm->priv; return priv->expanded_default; } static void e_tree_memory_class_init (GtkObjectClass *klass) { ETreeModelClass *tree_class = (ETreeModelClass *) klass; parent_class = gtk_type_class (PARENT_TYPE); node_chunk = g_mem_chunk_create (ETreeMemoryPath, TREEPATH_CHUNK_AREA_SIZE, G_ALLOC_AND_FREE); klass->destroy = etmm_destroy; tree_class->get_root = etmm_get_root; tree_class->get_prev = etmm_get_prev; tree_class->get_next = etmm_get_next; tree_class->get_first_child = etmm_get_first_child; tree_class->get_last_child = etmm_get_last_child; tree_class->get_parent = etmm_get_parent; tree_class->is_root = etmm_is_root; tree_class->is_expandable = etmm_is_expandable; tree_class->get_children = etmm_get_children; tree_class->depth = etmm_depth; tree_class->get_expanded_default = etmm_get_expanded_default; } static void e_tree_memory_init (GtkObject *object) { ETreeMemory *etmm = (ETreeMemory *)object; ETreeMemoryPriv *priv; priv = g_new0 (ETreeMemoryPriv, 1); etmm->priv = priv; priv->root = NULL; priv->frozen = 0; priv->expanded_default = 0; priv->destroy_func = NULL; priv->destroy_user_data = NULL; } E_MAKE_TYPE(e_tree_memory, "ETreeMemory", ETreeMemory, e_tree_memory_class_init, e_tree_memory_init, PARENT_TYPE) /** * e_tree_memory_construct: * @etree: * * **/ void e_tree_memory_construct (ETreeMemory *etmm) { } /** * e_tree_memory_new * * XXX docs here. * * return values: a newly constructed ETreeMemory. */ ETreeMemory * e_tree_memory_new (void) { ETreeMemory *etmm; etmm = gtk_type_new (e_tree_memory_get_type ()); e_tree_memory_construct(etmm); return etmm; } void e_tree_memory_set_expanded_default (ETreeMemory *etree, gboolean expanded) { etree->priv->expanded_default = expanded; } /** * e_tree_memory_node_get_data: * @etmm: * @node: * * * * Return value: **/ gpointer e_tree_memory_node_get_data (ETreeMemory *etmm, ETreePath node) { ETreeMemoryPath *path = node; g_return_val_if_fail (path, NULL); return path->node_data; } /** * e_tree_memory_node_set_data: * @etmm: * @node: * @node_data: * * **/ void e_tree_memory_node_set_data (ETreeMemory *etmm, ETreePath node, gpointer node_data) { ETreeMemoryPath *path = node; g_return_if_fail (path); path->node_data = node_data; } /** * e_tree_memory_node_insert: * @tree_model: * @parent_path: * @position: * @node_data: * * * * Return value: **/ ETreePath e_tree_memory_node_insert (ETreeMemory *tree_model, ETreePath parent_node, int position, gpointer node_data) { ETreeMemoryPriv *priv; ETreeMemoryPath *new_path; ETreeMemoryPath *parent_path = parent_node; g_return_val_if_fail(tree_model != NULL, NULL); priv = tree_model->priv; g_return_val_if_fail (parent_path != NULL || priv->root == NULL, NULL); priv = tree_model->priv; if (!tree_model->priv->frozen) e_tree_model_pre_change(E_TREE_MODEL(tree_model)); new_path = g_chunk_new0 (ETreeMemoryPath, node_chunk); new_path->node_data = node_data; if (parent_path != NULL) { e_tree_memory_path_insert (parent_path, position, new_path); if (!tree_model->priv->frozen) e_tree_model_node_inserted (E_TREE_MODEL(tree_model), parent_path, new_path); } else { priv->root = new_path; if (!tree_model->priv->frozen) e_tree_model_node_changed(E_TREE_MODEL(tree_model), new_path); } return new_path; } ETreePath e_tree_memory_node_insert_id (ETreeMemory *etree, ETreePath parent, int position, gpointer node_data, char *id) { return e_tree_memory_node_insert(etree, parent, position, node_data); } /** * e_tree_memory_node_insert_before: * @etree: * @parent: * @sibling: * @node_data: * * * * Return value: **/ ETreePath e_tree_memory_node_insert_before (ETreeMemory *etree, ETreePath parent, ETreePath sibling, gpointer node_data) { ETreeMemoryPath *child; ETreeMemoryPath *parent_path = parent; ETreeMemoryPath *sibling_path = sibling; int position = 0; g_return_val_if_fail(etree != NULL, NULL); if (sibling != NULL) { for (child = parent_path->first_child; child; child = child->next_sibling) { if (child == sibling_path) break; position ++; } } else position = parent_path->num_children; return e_tree_memory_node_insert (etree, parent, position, node_data); } /* just blows away child data, doesn't take into account unlinking/etc */ static void child_free(ETreeMemory *etree, ETreeMemoryPath *node) { ETreeMemoryPath *child, *next; child = node->first_child; while (child) { next = child->next_sibling; child_free(etree, child); child = next; } if (etree->priv->destroy_func) { etree->priv->destroy_func (node->node_data, etree->priv->destroy_user_data); } g_chunk_free(node, node_chunk); } /** * e_tree_memory_node_remove: * @etree: * @path: * * * * Return value: **/ gpointer e_tree_memory_node_remove (ETreeMemory *etree, ETreePath node) { ETreeMemoryPath *path = node; ETreeMemoryPath *parent = path->parent; ETreeMemoryPath *sibling; gpointer ret = path->node_data; int old_position = 0; g_return_val_if_fail(etree != NULL, NULL); if (!etree->priv->frozen) { e_tree_model_pre_change(E_TREE_MODEL(etree)); for (old_position = 0, sibling = path; sibling; old_position++, sibling = sibling->prev_sibling) /* Empty intentionally*/; old_position --; } /* 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 (!etree->priv->frozen) e_tree_model_node_removed(E_TREE_MODEL(etree), parent, path, old_position); child_free(etree, path); if (path == etree->priv->root) etree->priv->root = NULL; return ret; } typedef struct { ETreeMemory *memory; gpointer closure; ETreeMemorySortCallback callback; } MemoryAndClosure; static int sort_callback(const void *data1, const void *data2, gpointer user_data) { ETreePath path1 = *(ETreePath *)data1; ETreePath path2 = *(ETreePath *)data2; MemoryAndClosure *mac = user_data; return (*mac->callback) (mac->memory, path1, path2, mac->closure); } void e_tree_memory_sort_node (ETreeMemory *etmm, ETreePath node, ETreeMemorySortCallback callback, gpointer user_data) { ETreeMemoryPath **children; ETreeMemoryPath *child; int count; int i; ETreeMemoryPath *path = node; MemoryAndClosure mac; ETreeMemoryPath *last; i = 0; for (child = path->first_child; child; child = child->next_sibling) i++; children = g_new(ETreeMemoryPath *, i); count = i; for (child = path->first_child, i = 0; child; child = child->next_sibling, i++) { children[i] = child; } mac.memory = etmm; mac.closure = user_data; mac.callback = callback; e_sort (children, count, sizeof (ETreeMemoryPath *), sort_callback, &mac); path->first_child = NULL; last = NULL; for (i = 0; i < count; i++) { children[i]->prev_sibling = last; if (last) last->next_sibling = children[i]; else path->first_child = children[i]; last = children[i]; } if (last) last->next_sibling = NULL; path->last_child = last; g_free(children); e_tree_model_node_changed(E_TREE_MODEL(etmm), node); } void e_tree_memory_set_node_destroy_func (ETreeMemory *etmm, GFunc destroy_func, gpointer user_data) { etmm->priv->destroy_func = destroy_func; etmm->priv->destroy_user_data = user_data; }