aboutsummaryrefslogtreecommitdiffstats
path: root/widgets/table/e-tree-model.c
diff options
context:
space:
mode:
Diffstat (limited to 'widgets/table/e-tree-model.c')
-rw-r--r--widgets/table/e-tree-model.c549
1 files changed, 459 insertions, 90 deletions
diff --git a/widgets/table/e-tree-model.c b/widgets/table/e-tree-model.c
index d67e59f3f5..c11cb58831 100644
--- a/widgets/table/e-tree-model.c
+++ b/widgets/table/e-tree-model.c
@@ -20,98 +20,222 @@
static ETableModel *e_tree_model_parent_class;
+typedef struct {
+ gboolean expanded;
+ guint visible_descendents;
+ gpointer node_data;
+} ENode;
+
+enum {
+ NODE_CHANGED,
+ NODE_INSERTED,
+ NODE_REMOVED,
+ LAST_SIGNAL
+};
+
+static guint e_tree_model_signals [LAST_SIGNAL] = {0, };
+
+static void add_visible_descendents_to_array (ETreeModel *etm, GNode *gnode, int *row, int *count);
+
+
/* virtual methods */
-static ETreePath*
-etree_get_root (ETreeModel *etm)
+static void
+etree_destroy (GtkObject *object)
{
- /* shouldn't be called */
- g_assert(0);
- return NULL;
+ /* XXX lots of stuff to free here */
}
-static void*
-etree_value_at (ETreeModel *etm, ETreePath* node, int col)
+static ETreePath*
+etree_get_root (ETreeModel *etm)
{
- /* shouldn't be called */
- g_assert(0);
- return NULL;
+ return etm->root;
}
-static void
-etree_set_value_at (ETreeModel *etm, ETreePath* node, int col, const void *val)
+static ETreePath*
+etree_get_parent (ETreeModel *etm, ETreePath *path)
{
- /* shouldn't be called */
- g_assert(0);
+ g_return_val_if_fail (path, NULL);
+
+ return path->parent;
}
-static gboolean
-etree_is_editable (ETreeModel *etm, ETreePath* node, int col)
+static ETreePath*
+etree_get_next (ETreeModel *etm, ETreePath *node)
{
- /* shouldn't be called */
- g_assert(0);
- return FALSE;
+ g_return_val_if_fail (node, NULL);
+
+ return g_node_next_sibling(node);
}
-static gboolean
-etree_is_expanded (ETreeModel *etm, ETreePath* node)
+static ETreePath*
+etree_get_prev (ETreeModel *etm, ETreePath *node)
{
- /* shouldn't be called */
- g_assert(0);
- return FALSE;
+ g_return_val_if_fail (node, NULL);
+
+ return g_node_prev_sibling (node);
}
static guint
etree_get_children (ETreeModel *etm, ETreePath* node, ETreePath ***paths)
{
- /* shouldn't be called */
- g_assert(0);
- return 0;
+ guint n_children;
+
+ g_return_val_if_fail (node, 0);
+
+ n_children = g_node_n_children (node);
+
+ if (paths) {
+ int i;
+ (*paths) = g_malloc (sizeof (ETreePath*) * n_children);
+ for (i = 0; i < n_children; i ++) {
+ (*paths)[i] = g_node_nth_child (node, i);
+ }
+ }
+
+ return n_children;
}
-static void
-etree_release_paths (ETreeModel *etm, ETreePath **paths, guint num_paths)
+static gboolean
+etree_is_expanded (ETreeModel *etm, ETreePath* node)
{
- /* shouldn't be called */
- g_assert(0);
+ g_return_val_if_fail (node && node->data, FALSE);
+
+ return ((ENode*)node->data)->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 (!((ENode*)node->data)->expanded)
+ return FALSE;
+ }
+
+ return TRUE;
}
static void
etree_set_expanded (ETreeModel *etm, ETreePath* node, gboolean expanded)
{
- /* shouldn't be called */
- g_assert(0);
+ GNode *child;
+ ENode *enode;
+ int row;
+
+ g_return_if_fail (node && node->data);
+
+ enode = ((ENode*)node->data);
+
+ if (enode->expanded == expanded)
+ return;
+
+ enode->expanded = expanded;
+
+ row = e_tree_model_row_of_node (etm, node) + 1;
+
+ if (expanded) {
+ GNode *parent;
+
+ if (e_tree_model_node_is_visible (etm, node)) {
+ enode->visible_descendents = 0;
+ for (child = g_node_first_child (node); child;
+ child = g_node_next_sibling (child)) {
+ add_visible_descendents_to_array (etm, child, &row, &enode->visible_descendents);
+ }
+ }
+ /* now iterate back up the tree, adding to our
+ ancestors' visible descendents */
+
+ for (parent = node->parent; parent; parent = parent->parent) {
+ ENode *parent_enode = (ENode*)parent->data;
+ parent_enode->visible_descendents += enode->visible_descendents;
+ }
+ }
+ else {
+ int i;
+ GNode *parent;
+
+ if (e_tree_model_node_is_visible (etm, node)) {
+ for (i = 0; i < enode->visible_descendents; i ++) {
+ etm->row_array = g_array_remove_index (etm->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) {
+ ENode *parent_enode = (ENode*)parent->data;
+
+ parent_enode->visible_descendents -= enode->visible_descendents;
+ }
+
+ enode->visible_descendents = 0;
+ }
}
+/* fairly naive implementation */
static void
-etree_destroy (GtkObject *object)
+etree_set_expanded_recurse (ETreeModel *etm, ETreePath* node, gboolean expanded)
{
+ ETreePath **paths;
+ guint num_children;
+ int i;
+
+ e_tree_model_node_set_expanded (etm, node, expanded);
+
+ num_children = e_tree_model_node_get_children (etm, node, &paths);
+ if (num_children) {
+ for (i = 0; i < num_children; i ++) {
+ e_tree_model_node_set_expanded_recurse (etm, paths[i], expanded);
+ }
+
+ g_free (paths);
+ }
}
-guint
-e_tree_model_node_num_visible_descendents (ETreeModel *etm, ETreePath *node)
+static ETreePath *
+etree_node_at_row (ETreeModel *etree, int row)
{
- int count = 1;
- if (e_tree_model_node_is_expanded (etm, node)) {
- ETreePath **paths;
- int i;
- int num_paths;
+ GNode *node = g_array_index (etree->row_array, GNode*, row);
- num_paths = e_tree_model_node_get_children (etm, node, &paths);
+ return node;
+}
- for (i = 0; i < num_paths; i ++)
- count += e_tree_model_node_num_visible_descendents(etm, paths[i]);
+
+/* ETable analogs */
+static void*
+etree_value_at (ETreeModel *etm, ETreePath* node, int col)
+{
+ /* shouldn't be called */
+ g_assert (0);
+ return NULL;
+}
- e_tree_model_release_paths (etm, paths, num_paths);
- }
+static void
+etree_set_value_at (ETreeModel *etm, ETreePath* node, int col, const void *val)
+{
+ /* shouldn't be called */
+ g_assert (0);
+}
- return count;
+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)
{
- return e_tree_model_node_num_visible_descendents (E_TREE_MODEL (etm), e_tree_model_get_root (E_TREE_MODEL (etm)));
+ ETreeModel *tree = E_TREE_MODEL (etm);
+ return tree->row_array->len;
}
static void *
@@ -155,6 +279,7 @@ etable_is_cell_editable (ETableModel *etm, int col, int row)
return et_class->is_editable (etree, node, col);
}
+
static void
e_tree_model_class_init (GtkObjectClass *klass)
{
@@ -165,11 +290,38 @@ e_tree_model_class_init (GtkObjectClass *klass)
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);
+
+ 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;
@@ -178,19 +330,68 @@ e_tree_model_class_init (GtkObjectClass *klass)
#endif
tree_class->get_root = etree_get_root;
+ tree_class->get_prev = etree_get_prev;
+ tree_class->get_next = etree_get_next;
+ tree_class->get_parent = etree_get_parent;
+
tree_class->value_at = etree_value_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->release_paths = etree_release_paths;
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;
}
E_MAKE_TYPE(e_tree_model, "ETreeModel", ETreeModel, e_tree_model_class_init, NULL, PARENT_TYPE)
+
/* signals */
+void
+e_tree_model_node_changed (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_CHANGED]);
+}
+
+void
+e_tree_model_node_inserted (ETreeModel *tree_model,
+ ETreePath *parent_node,
+ ETreePath *inserted_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_INSERTED],
+ parent_node, inserted_node);
+}
+
+void
+e_tree_model_node_removed (ETreeModel *tree_model, ETreePath *parent_node, ETreePath *removed_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_REMOVED],
+ parent_node, removed_node);
+}
+
+
+void
+e_tree_model_construct (ETreeModel *etree)
+{
+ etree->root = NULL;
+ etree->root_visible = TRUE;
+ etree->row_array = g_array_new (FALSE, FALSE, sizeof(GNode*));
+}
ETreeModel *
e_tree_model_new ()
@@ -202,52 +403,56 @@ e_tree_model_new ()
return et;
}
-static ETreePath *
-e_tree_model_node_at_row_1 (ETreeModel *etree, int *row, ETreePath *node)
+ETreePath *
+e_tree_model_get_root (ETreeModel *etree)
{
- ETreePath *ret = NULL;
-
- if (*row == 0)
- ret = node;
- else if (e_tree_model_node_is_expanded (etree, node)) {
- int num_children;
- int i;
- ETreePath **paths;
+ return ETM_CLASS(etree)->get_root(etree);
+}
- num_children = e_tree_model_node_get_children (etree, node, &paths);
+ETreePath *
+e_tree_model_node_at_row (ETreeModel *etree, int row)
+{
+ return ETM_CLASS(etree)->node_at_row (etree, row);
+}
- for (i = 0; i < num_children; i ++) {
- ETreePath *p;
+int
+e_tree_model_row_of_node (ETreeModel *etree, ETreePath *node)
+{
+ int i;
- (*row) --;
+ for (i = 0; i < etree->row_array->len; i ++)
+ if (g_array_index (etree->row_array, GNode*, i) == node)
+ return i;
- p = e_tree_model_node_at_row_1 (etree, row, paths[i]);
+ return -1;
+}
- if (p) {
- ret = p;
- break;
+void
+e_tree_model_root_node_set_visible (ETreeModel *etm, gboolean visible)
+{
+ if (visible != etm->root_visible) {
+ etm->root_visible = visible;
+ if (etm->root) {
+ if (visible) {
+ etm->row_array = g_array_insert_val (etm->row_array, 0, etm->root);
}
+ else {
+ ETreePath *root_path = e_tree_model_get_root (etm);
+ etm->row_array = g_array_remove_index (etm->row_array, 0);
+ e_tree_model_node_set_expanded (etm, root_path, TRUE);
+ }
+
+ e_table_model_changed (E_TABLE_MODEL (etm));
}
-
- /* XXX need to find why this release is causing problems */
- /* e_tree_model_release_paths (etree, paths, num_children); */
}
-
- return ret;
}
-ETreePath *
-e_tree_model_get_root (ETreeModel *etree)
+gboolean
+e_tree_model_root_node_is_visible (ETreeModel *etm)
{
- return ETM_CLASS(etree)->get_root(etree);
+ return etm->root_visible;
}
-ETreePath *
-e_tree_model_node_at_row (ETreeModel *etree, int row)
-{
- /* XXX icky, perform a depth first search of the tree. we need this optimized sorely */
- return e_tree_model_node_at_row_1 (etree, &row, ETM_CLASS(etree)->get_root(etree));
-}
ETreePath *
e_tree_model_node_get_next (ETreeModel *etree, ETreePath *node)
@@ -264,18 +469,13 @@ e_tree_model_node_get_prev (ETreeModel *etree, ETreePath *node)
guint
e_tree_model_node_depth (ETreeModel *etree, ETreePath *path)
{
- return g_list_length (path) - 1;
+ return g_node_depth (path) - 1;
}
ETreePath *
e_tree_model_node_get_parent (ETreeModel *etree, ETreePath *path)
{
- g_return_val_if_fail (path, NULL);
-
- if (path->next == NULL)
- return NULL;
- else
- return g_list_copy (path->next);
+ return ETM_CLASS(etree)->get_parent(etree, path);
}
gboolean
@@ -296,21 +496,190 @@ 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)
+{
+ ENode *enode = (ENode*)node->data;
+
+ return enode->visible_descendents;
+}
+
+gpointer
+e_tree_model_node_get_data (ETreeModel *etm, ETreePath *node)
+{
+ ENode *enode;
+
+ g_return_val_if_fail (node && node->data, NULL);
+
+ enode = (ENode*)node->data;
+
+ return enode->node_data;
+}
+
void
-e_tree_model_release_paths (ETreeModel *etree, ETreePath **paths, guint num_paths)
+e_tree_model_node_set_data (ETreeModel *etm, ETreePath *node, gpointer node_data)
{
- ETM_CLASS(etree)->release_paths (etree, paths, num_paths);
+ ENode *enode;
+
+ g_return_if_fail (node && node->data);
+
+ enode = (ENode*)node->data;
+
+ enode->node_data = node_data;
}
+ETreePath*
+e_tree_model_node_insert (ETreeModel *tree_model,
+ ETreePath *parent_path,
+ int position,
+ gpointer node_data)
+{
+ ENode *node;
+ ETreePath *new_path;
+
+ g_return_val_if_fail (parent_path != NULL || tree_model->root == NULL, NULL);
+
+ node = g_new0 (ENode, 1);
+
+ node->expanded = FALSE;
+ node->node_data = node_data;
+
+ if (parent_path != NULL) {
+
+ new_path = g_node_new (node);
+
+ g_node_insert (parent_path, position, new_path);
+
+ if (e_tree_model_node_is_visible (tree_model, new_path)) {
+ int parent_row;
+ GNode *node;
+
+ /* we need to iterate back up to the root, incrementing the number of visible
+ descendents */
+ for (node = parent_path; node; node = node->parent) {
+ ENode *parent_enode = (ENode*)node->data;
+
+ parent_enode->visible_descendents ++;
+ }
+
+ /* finally, insert a row into the table */
+ parent_row = e_tree_model_row_of_node (tree_model, parent_path);
+
+ tree_model->row_array = g_array_insert_val (tree_model->row_array,
+ parent_row + position + 1, new_path);
+ }
+ }
+ else {
+ tree_model->root = g_node_new (node);
+ if (tree_model->root_visible)
+ tree_model->row_array = g_array_insert_val (tree_model->row_array, 0, tree_model->root);
+ new_path = tree_model->root;
+ }
+
+ /* FIXME: find out the number of descendents that will be visible,
+ and call insert_row that many times. */
+ e_table_model_changed (E_TABLE_MODEL(tree_model));
+
+ return new_path;
+}
+
+ETreePath *
+e_tree_model_node_insert_before (ETreeModel *etree, ETreePath *parent,
+ ETreePath *sibling, gpointer node_data)
+{
+ return e_tree_model_node_insert (etree, parent,
+ g_node_child_position (parent, sibling),
+ node_data);
+}
+
+gpointer
+e_tree_model_node_remove (ETreeModel *etree, ETreePath *path)
+{
+ GNode *parent = path->parent;
+ ENode *enode = (ENode*)path->data;
+ gpointer ret = enode->node_data;
+
+ g_return_val_if_fail (!g_node_first_child(path), NULL);
+
+ /* clean up the display */
+ if (parent) {
+ if (e_tree_model_node_is_visible (etree, path)) {
+ int row = e_tree_model_row_of_node (etree, path);
+ etree->row_array = g_array_remove_index (etree->row_array, row);
+ e_table_model_row_deleted (E_TABLE_MODEL (etree), row);
+
+ /* we need to iterate back up to the root, incrementing the number of visible
+ descendents */
+ for (; parent; parent = parent->parent) {
+ ENode *parent_enode = (ENode*)parent->data;
+
+ parent_enode->visible_descendents --;
+ }
+ }
+ }
+ else if (path == etree->root) {
+ etree->root = NULL;
+ if (etree->root_visible) {
+ etree->row_array = g_array_remove_index (etree->row_array, 0);
+ e_table_model_row_deleted (E_TABLE_MODEL (etree), 0);
+ }
+ }
+ else {
+ /* XXX invalid path */
+ return NULL;
+ }
+
+
+ /* now free up the storage from that path */
+ g_node_destroy (path);
+ g_free (enode);
+
+ return ret;
+}
+
+
+static void
+add_visible_descendents_to_array (ETreeModel *etm, GNode *gnode, int *row, int *count)
+{
+ GNode *child;
+ ENode *enode;
+
+ /* add a row for this node */
+ etm->row_array = g_array_insert_val (etm->row_array, (*row), gnode);
+ e_table_model_row_inserted (E_TABLE_MODEL (etm), (*row)++);
+ (*count) ++;
+
+ /* then loop over its children, calling this routine for each
+ of them */
+ enode = (ENode*)gnode->data;
+ if (enode->expanded) {
+ for (child = g_node_first_child (gnode); child;
+ child = g_node_next_sibling (child)) {
+ add_visible_descendents_to_array (etm, child, row, count);
+ }
+ }
+}