aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--widgets/table/e-tree-model.c156
1 files changed, 117 insertions, 39 deletions
diff --git a/widgets/table/e-tree-model.c b/widgets/table/e-tree-model.c
index 6e8c7856dd..e3c4f138cd 100644
--- a/widgets/table/e-tree-model.c
+++ b/widgets/table/e-tree-model.c
@@ -41,6 +41,7 @@ struct ETreeModelPriv {
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 {
@@ -172,6 +173,24 @@ e_tree_model_node_traverse (ETreeModel *model, ETreePath *path, ETreePathFunc fu
}
+/* 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
@@ -691,6 +710,9 @@ e_tree_model_construct (ETreeModel *etree)
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 *
@@ -896,7 +918,7 @@ e_tree_model_node_insert (ETreeModel *tree_model,
e_tree_path_insert (parent_path, position, new_path);
if (e_tree_model_node_is_visible (tree_model, new_path)) {
- int parent_row;
+ int parent_row, child_offset = 0;
ETreePath *n;
/* we need to iterate back up to the root, incrementing the number of visible
@@ -905,9 +927,22 @@ e_tree_model_node_insert (ETreeModel *tree_model,
n->visible_descendents ++;
}
- /* finally, insert a row into the table */
- if (position == -1)
+ /* 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) {
@@ -917,9 +952,11 @@ e_tree_model_node_insert (ETreeModel *tree_model,
}
priv->row_array = g_array_insert_val (priv->row_array,
- parent_row + position + 1, new_path);
+ parent_row + position + 1 + child_offset, new_path);
- e_table_model_row_inserted (E_TABLE_MODEL(tree_model), parent_row + position + 1);
+ /* 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)
@@ -973,11 +1010,25 @@ e_tree_model_node_insert_before (ETreeModel *etree,
return e_tree_model_node_insert (etree, parent, position, node_data);
}
-static gboolean
-child_remove (ETreeModel *model, ETreePath *node, gpointer data)
+/* just blows away child data, doesn't take into account unlinking/etc */
+static void
+child_free(ETreeModel *etree, ETreePath *node)
{
- e_tree_model_node_remove (model, node);
- return FALSE;
+ 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
@@ -986,49 +1037,73 @@ 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;
- /* remove children */
- e_tree_model_node_traverse (etree, path, child_remove, etree);
-
- /* clean up the display */
+ /* work out what range of visible rows to remove */
if (parent) {
- if (e_tree_model_node_is_visible (etree, path)) {
- int row = e_tree_model_row_of_node (etree, path);
- priv->row_array = g_array_remove_index (priv->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) {
- parent->visible_descendents --;
- }
+ 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) {
+ } else if (path == priv->root) {
priv->root = NULL;
if (priv->root_visible) {
- priv->row_array = g_array_remove_index (priv->row_array, 0);
- e_table_model_row_deleted (E_TABLE_MODEL (etree), 0);
+ base = 0;
+ visible = path->visible_descendents + 1;
+ } else {
+ base = 0;
+ visible = path->visible_descendents;
}
- }
- else {
- /* XXX invalid path */
+ } else {
+ g_warning("Trying to remove invalid path %p", path);
return NULL;
}
- /* unlink the node */
+ /* 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);
- /* now free up the storage from that path */
- if (path->save_id)
- g_hash_table_remove (priv->expanded_state, path->save_id);
- g_free (path->save_id);
- g_chunk_free (path, priv->node_chunk);
+ /*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)
{
@@ -1036,7 +1111,9 @@ add_visible_descendents_to_array (ETreeModel *etm, ETreePath *node, int *row, in
/* add a row for this node */
priv->row_array = g_array_insert_val (priv->row_array, (*row), node);
- e_table_model_row_inserted (E_TABLE_MODEL (etm), (*row)++);
+ 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
@@ -1283,6 +1360,7 @@ e_tree_model_node_sort (ETreeModel *tree_model,
g_free (sort_info);
- e_table_model_changed (E_TABLE_MODEL (tree_model));
+ if (!priv->pre_change)
+ e_table_model_changed (E_TABLE_MODEL (tree_model));
}