aboutsummaryrefslogtreecommitdiffstats
path: root/e-util/e-tree-memory.c
diff options
context:
space:
mode:
Diffstat (limited to 'e-util/e-tree-memory.c')
-rw-r--r--e-util/e-tree-memory.c743
1 files changed, 743 insertions, 0 deletions
diff --git a/e-util/e-tree-memory.c b/e-util/e-tree-memory.c
new file mode 100644
index 0000000000..0af5d27b31
--- /dev/null
+++ b/e-util/e-tree-memory.c
@@ -0,0 +1,743 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ *
+ * Authors:
+ * Chris Lahey <clahey@ximian.com>
+ * Chris Toshok <toshok@ximian.com>
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-tree-memory.h"
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdlib.h>
+
+#include <libxml/parser.h>
+#include <libxml/xmlmemory.h>
+
+#include "e-xml-utils.h"
+
+#define E_TREE_MEMORY_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_TREE_MEMORY, ETreeMemoryPrivate))
+
+G_DEFINE_TYPE (ETreeMemory, e_tree_memory, E_TYPE_TREE_MODEL)
+
+enum {
+ FILL_IN_CHILDREN,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0, };
+
+typedef struct ETreeMemoryPath ETreeMemoryPath;
+
+struct ETreeMemoryPath {
+ gpointer node_data;
+
+ guint children_computed : 1;
+
+ /* parent/child/sibling pointers */
+ ETreeMemoryPath *parent;
+ ETreeMemoryPath *next_sibling;
+ ETreeMemoryPath *prev_sibling;
+ ETreeMemoryPath *first_child;
+ ETreeMemoryPath *last_child;
+
+ gint num_children;
+};
+
+struct _ETreeMemoryPrivate {
+ ETreeMemoryPath *root;
+
+ /* whether nodes are created expanded
+ * or collapsed by default */
+ gboolean expanded_default;
+
+ gint frozen;
+ GFunc destroy_func;
+ gpointer destroy_user_data;
+};
+
+/* ETreeMemoryPath functions */
+
+static inline void
+check_children (ETreeMemory *memory,
+ ETreePath node)
+{
+ ETreeMemoryPath *path = node;
+ if (!path->children_computed) {
+ g_signal_emit (memory, signals[FILL_IN_CHILDREN], 0, node);
+ path->children_computed = TRUE;
+ }
+}
+
+static gint
+e_tree_memory_path_depth (ETreeMemoryPath *path)
+{
+ gint 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,
+ gint 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)
+{
+ ETreeMemoryPrivate *priv = etmm->priv;
+
+ if (priv->frozen == 0)
+ 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)
+{
+ ETreeMemoryPrivate *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_dispose (GObject *object)
+{
+ ETreeMemoryPrivate *priv;
+
+ priv = E_TREE_MEMORY_GET_PRIVATE (object);
+
+ if (priv->root)
+ e_tree_memory_node_remove (
+ E_TREE_MEMORY (object), priv->root);
+
+ G_OBJECT_CLASS (e_tree_memory_parent_class)->dispose (object);
+}
+
+static ETreePath
+etmm_get_root (ETreeModel *etm)
+{
+ ETreeMemoryPrivate *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;
+
+ check_children (E_TREE_MEMORY (etm), node);
+ return path->first_child;
+}
+
+static ETreePath
+etmm_get_last_child (ETreeModel *etm,
+ ETreePath node)
+{
+ ETreeMemoryPath *path = node;
+
+ check_children (E_TREE_MEMORY (etm), 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;
+
+ check_children (E_TREE_MEMORY (etm), node);
+ return path->first_child != NULL;
+}
+
+static guint
+etmm_get_children (ETreeModel *etm,
+ ETreePath node,
+ ETreePath **nodes)
+{
+ ETreeMemoryPath *path = node;
+ guint n_children;
+
+ check_children (E_TREE_MEMORY (etm), node);
+
+ n_children = path->num_children;
+
+ if (nodes) {
+ ETreeMemoryPath *p;
+ gint i = 0;
+
+ (*nodes) = g_new (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);
+ ETreeMemoryPrivate *priv = etmm->priv;
+
+ return priv->expanded_default;
+}
+
+static void
+etmm_clear_children_computed (ETreeMemoryPath *path)
+{
+ for (path = path->first_child; path; path = path->next_sibling) {
+ path->children_computed = FALSE;
+ etmm_clear_children_computed (path);
+ }
+}
+
+static void
+etmm_node_request_collapse (ETreeModel *etm,
+ ETreePath node)
+{
+ ETreeModelClass *parent_class;
+
+ if (node)
+ etmm_clear_children_computed (node);
+
+ parent_class = E_TREE_MODEL_CLASS (e_tree_memory_parent_class);
+
+ if (parent_class->node_request_collapse != NULL)
+ parent_class->node_request_collapse (etm, node);
+}
+
+static void
+e_tree_memory_class_init (ETreeMemoryClass *class)
+{
+ GObjectClass *object_class;
+ ETreeModelClass *tree_model_class;
+
+ g_type_class_add_private (class, sizeof (ETreeMemoryPrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->dispose = etmm_dispose;
+
+ tree_model_class = E_TREE_MODEL_CLASS (class);
+ tree_model_class->get_root = etmm_get_root;
+ tree_model_class->get_prev = etmm_get_prev;
+ tree_model_class->get_next = etmm_get_next;
+ tree_model_class->get_first_child = etmm_get_first_child;
+ tree_model_class->get_last_child = etmm_get_last_child;
+ tree_model_class->get_parent = etmm_get_parent;
+
+ tree_model_class->is_root = etmm_is_root;
+ tree_model_class->is_expandable = etmm_is_expandable;
+ tree_model_class->get_children = etmm_get_children;
+ tree_model_class->depth = etmm_depth;
+ tree_model_class->get_expanded_default = etmm_get_expanded_default;
+
+ tree_model_class->node_request_collapse = etmm_node_request_collapse;
+
+ class->fill_in_children = NULL;
+
+ signals[FILL_IN_CHILDREN] = g_signal_new (
+ "fill_in_children",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (ETreeMemoryClass, fill_in_children),
+ (GSignalAccumulator) NULL, NULL,
+ g_cclosure_marshal_VOID__POINTER,
+ G_TYPE_NONE, 1,
+ G_TYPE_POINTER);
+}
+
+static void
+e_tree_memory_init (ETreeMemory *etmm)
+{
+ etmm->priv = E_TREE_MEMORY_GET_PRIVATE (etmm);
+}
+
+/**
+ * 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)
+{
+ return g_object_new (E_TYPE_TREE_MEMORY, NULL);
+}
+
+/**
+ * e_tree_memory_set_expanded_default
+ *
+ * Sets the state of nodes to be append to a thread.
+ * They will either be expanded or collapsed, according to
+ * the value of @expanded.
+ */
+void
+e_tree_memory_set_expanded_default (ETreeMemory *etree,
+ gboolean expanded)
+{
+ g_return_if_fail (etree != NULL);
+
+ 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,
+ gint position,
+ gpointer node_data)
+{
+ ETreeMemoryPrivate *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_slice_new0 (ETreeMemoryPath);
+
+ new_path->node_data = node_data;
+ new_path->children_computed = FALSE;
+
+ 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,
+ gint position,
+ gpointer node_data,
+ gchar *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;
+ gint 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_slice_free (ETreeMemoryPath, node);
+}
+
+/**
+ * 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;
+ gint 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;
+
+ if (!etree->priv->frozen)
+ e_tree_model_node_deleted (E_TREE_MODEL (etree), path);
+
+ return ret;
+}
+
+typedef struct {
+ ETreeMemory *memory;
+ gpointer closure;
+ ETreeMemorySortCallback callback;
+} MemoryAndClosure;
+
+static gint
+sort_callback (gconstpointer data1,
+ gconstpointer 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;
+ gint count;
+ gint i;
+ ETreeMemoryPath *path = node;
+ MemoryAndClosure mac;
+ ETreeMemoryPath *last;
+
+ e_tree_model_pre_change (E_TREE_MODEL (etmm));
+
+ 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;
+
+ g_qsort_with_data (
+ 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;
+}