X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=gtk%2Fgtktreemodelfilter.c;h=9d2b89587c52d5b9dc1018d0f8508559359c2c81;hb=ef1da5f6c2450fc5f7c7de4a17114cc7507a41ad;hp=fd9241279ac4ba161c951286e49f4c47bbe6e32e;hpb=c09cc89317d222e54e98d4e2e9f2792de13897ec;p=~andy%2Fgtk diff --git a/gtk/gtktreemodelfilter.c b/gtk/gtktreemodelfilter.c index fd9241279..9d2b89587 100644 --- a/gtk/gtktreemodelfilter.c +++ b/gtk/gtktreemodelfilter.c @@ -22,8 +22,8 @@ #include "gtktreemodelfilter.h" #include "gtkintl.h" #include "gtktreednd.h" -#include "gtkalias.h" #include "gtkprivate.h" +#include "gtkalias.h" #include /* ITER FORMAT: @@ -37,6 +37,23 @@ * child model. */ +/* A few notes: + * There are three model/views involved, so there are two mappings: + * * this model -> child model: mapped via offset in FilterElt. + * * this model -> parent model (or view): mapped via the array index + * of FilterElt. + * + * Note that there are two kinds of paths relative to the filter model + * (those generated from the array indices): paths taking non-visible + * nodes into account, and paths which don't. Paths which take + * non-visible nodes into account should only be used internally and + * NEVER be passed along with a signal emission. + * + * The filter model has a reference on every node that is not in the root + * level and has a parent with ref_count > 1. Exception is a virtual root + * level; all nodes in the virtual root level are referenced too. + */ + typedef struct _FilterElt FilterElt; typedef struct _FilterLevel FilterLevel; @@ -54,6 +71,7 @@ struct _FilterLevel { GArray *array; gint ref_count; + gint visible_nodes; FilterElt *parent_elt; FilterLevel *parent_level; @@ -69,8 +87,6 @@ struct _GtkTreeModelFilterPrivate GtkTreeModel *child_model; gint zero_ref_count; - guint root_level_visible; - GtkTreePath *virtual_root; GtkTreeModelFilterVisibleFunc visible_func; @@ -81,13 +97,16 @@ struct _GtkTreeModelFilterPrivate GType *modify_types; GtkTreeModelFilterModifyFunc modify_func; gpointer modify_data; - gpointer modify_destroy; + GtkDestroyNotify modify_destroy; gint visible_column; gboolean visible_method_set; gboolean modify_func_set; + gboolean in_row_deleted; + gboolean virtual_root_deleted; + /* signal ids */ guint changed_id; guint inserted_id; @@ -111,8 +130,6 @@ enum #define FILTER_LEVEL(filter_level) ((FilterLevel *)filter_level) /* general code (object/interface init, properties, etc) */ -static void gtk_tree_model_filter_init (GtkTreeModelFilter *filter); -static void gtk_tree_model_filter_class_init (GtkTreeModelFilterClass *filter_class); static void gtk_tree_model_filter_tree_model_init (GtkTreeModelIface *iface); static void gtk_tree_model_filter_drag_source_init (GtkTreeDragSourceIface *iface); static void gtk_tree_model_filter_finalize (GObject *object); @@ -152,6 +169,9 @@ static GtkTreeModelFlags gtk_tree_model_filter_get_flags ( static gint gtk_tree_model_filter_get_n_columns (GtkTreeModel *model); static GType gtk_tree_model_filter_get_column_type (GtkTreeModel *model, gint index); +static gboolean gtk_tree_model_filter_get_iter_full (GtkTreeModel *model, + GtkTreeIter *iter, + GtkTreePath *path); static gboolean gtk_tree_model_filter_get_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreePath *path); @@ -194,7 +214,9 @@ static gboolean gtk_tree_model_filter_drag_data_delete (GtkTr /* private functions */ static void gtk_tree_model_filter_build_level (GtkTreeModelFilter *filter, FilterLevel *parent_level, - FilterElt *parent_elt); + FilterElt *parent_elt, + gboolean emit_inserted); + static void gtk_tree_model_filter_free_level (GtkTreeModelFilter *filter, FilterLevel *filter_level); @@ -220,6 +242,10 @@ static void gtk_tree_model_filter_real_unref_node (GtkTr static void gtk_tree_model_filter_set_model (GtkTreeModelFilter *filter, GtkTreeModel *child_model); +static void gtk_tree_model_filter_ref_path (GtkTreeModelFilter *filter, + GtkTreePath *path); +static void gtk_tree_model_filter_unref_path (GtkTreeModelFilter *filter, + GtkTreePath *path); static void gtk_tree_model_filter_set_root (GtkTreeModelFilter *filter, GtkTreePath *root); @@ -228,13 +254,19 @@ static GtkTreePath *gtk_real_tree_model_filter_convert_child_path_to_path (GtkTr gboolean build_levels, gboolean fetch_children); +static FilterElt *gtk_tree_model_filter_get_nth (GtkTreeModelFilter *filter, + FilterLevel *level, + int n); +static FilterElt *gtk_tree_model_filter_get_nth_visible (GtkTreeModelFilter *filter, + FilterLevel *level, + int n); + static FilterElt *gtk_tree_model_filter_fetch_child (GtkTreeModelFilter *filter, FilterLevel *level, gint offset, gint *index); static void gtk_tree_model_filter_remove_node (GtkTreeModelFilter *filter, - GtkTreeIter *iter, - gboolean emit_signal); + GtkTreeIter *iter); static void gtk_tree_model_filter_update_children (GtkTreeModelFilter *filter, FilterLevel *level, FilterElt *elt); @@ -243,57 +275,11 @@ static FilterElt *bsearch_elt_with_offset (GArra gint *index); -static GObjectClass *parent_class = NULL; - -GType -gtk_tree_model_filter_get_type (void) -{ - static GType tree_model_filter_type = 0; - - if (!tree_model_filter_type) - { - static const GTypeInfo tree_model_filter_info = - { - sizeof (GtkTreeModelFilterClass), - NULL, /* base_init */ - NULL, /* base_finalize */ - (GClassInitFunc) gtk_tree_model_filter_class_init, - NULL, /* class_finalize */ - NULL, /* class_data */ - sizeof (GtkTreeModelFilter), - 0, /* n_preallocs */ - (GInstanceInitFunc) gtk_tree_model_filter_init - }; - - static const GInterfaceInfo tree_model_info = - { - (GInterfaceInitFunc) gtk_tree_model_filter_tree_model_init, - NULL, - NULL - }; - - static const GInterfaceInfo drag_source_info = - { - (GInterfaceInitFunc) gtk_tree_model_filter_drag_source_init, - NULL, - NULL - }; - - tree_model_filter_type = g_type_register_static (G_TYPE_OBJECT, - g_intern_static_string ("GtkTreeModelFilter"), - &tree_model_filter_info, 0); - - g_type_add_interface_static (tree_model_filter_type, - GTK_TYPE_TREE_MODEL, - &tree_model_info); - - g_type_add_interface_static (tree_model_filter_type, - GTK_TYPE_TREE_DRAG_SOURCE, - &drag_source_info); - } - - return tree_model_filter_type; -} +G_DEFINE_TYPE_WITH_CODE (GtkTreeModelFilter, gtk_tree_model_filter, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_MODEL, + gtk_tree_model_filter_tree_model_init) + G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_SOURCE, + gtk_tree_model_filter_drag_source_init)) static void gtk_tree_model_filter_init (GtkTreeModelFilter *filter) @@ -304,6 +290,8 @@ gtk_tree_model_filter_init (GtkTreeModelFilter *filter) filter->priv->zero_ref_count = 0; filter->priv->visible_method_set = FALSE; filter->priv->modify_func_set = FALSE; + filter->priv->in_row_deleted = FALSE; + filter->priv->virtual_root_deleted = FALSE; } static void @@ -312,7 +300,6 @@ gtk_tree_model_filter_class_init (GtkTreeModelFilterClass *filter_class) GObjectClass *object_class; object_class = (GObjectClass *) filter_class; - parent_class = g_type_class_peek_parent (filter_class); object_class->set_property = gtk_tree_model_filter_set_property; object_class->get_property = gtk_tree_model_filter_get_property; @@ -374,6 +361,12 @@ gtk_tree_model_filter_finalize (GObject *object) { GtkTreeModelFilter *filter = (GtkTreeModelFilter *) object; + if (filter->priv->virtual_root && !filter->priv->virtual_root_deleted) + { + gtk_tree_model_filter_unref_path (filter, filter->priv->virtual_root); + filter->priv->virtual_root_deleted = TRUE; + } + gtk_tree_model_filter_set_model (filter, NULL); if (filter->priv->virtual_root) @@ -382,11 +375,16 @@ gtk_tree_model_filter_finalize (GObject *object) if (filter->priv->root) gtk_tree_model_filter_free_level (filter, filter->priv->root); - if (filter->priv->modify_types) - g_free (filter->priv->modify_types); + g_free (filter->priv->modify_types); + + if (filter->priv->modify_destroy) + filter->priv->modify_destroy (filter->priv->modify_data); + + if (filter->priv->visible_destroy) + filter->priv->visible_destroy (filter->priv->visible_data); /* must chain up */ - parent_class->finalize (object); + G_OBJECT_CLASS (gtk_tree_model_filter_parent_class)->finalize (object); } static void @@ -438,9 +436,11 @@ gtk_tree_model_filter_get_property (GObject *object, static void gtk_tree_model_filter_build_level (GtkTreeModelFilter *filter, FilterLevel *parent_level, - FilterElt *parent_elt) + FilterElt *parent_elt, + gboolean emit_inserted) { GtkTreeIter iter; + GtkTreeIter first_node; GtkTreeIter root; FilterLevel *new_level; gint length = 0; @@ -448,6 +448,9 @@ gtk_tree_model_filter_build_level (GtkTreeModelFilter *filter, g_assert (filter->priv->child_model != NULL); + if (filter->priv->in_row_deleted) + return; + if (!parent_level) { if (filter->priv->virtual_root) @@ -495,6 +498,7 @@ gtk_tree_model_filter_build_level (GtkTreeModelFilter *filter, sizeof (FilterElt), length); new_level->ref_count = 0; + new_level->visible_nodes = 0; new_level->parent_elt = parent_elt; new_level->parent_level = parent_level; @@ -511,12 +515,12 @@ gtk_tree_model_filter_build_level (GtkTreeModelFilter *filter, parent_elt = parent_level->parent_elt; parent_level = parent_level->parent_level; } - filter->priv->zero_ref_count++; + if (new_level != filter->priv->root) + filter->priv->zero_ref_count++; i = 0; - if (!new_level->parent_level) - filter->priv->root_level_visible = 0; + first_node = iter; do { @@ -534,13 +538,63 @@ gtk_tree_model_filter_build_level (GtkTreeModelFilter *filter, filter_elt.iter = iter; g_array_append_val (new_level->array, filter_elt); + new_level->visible_nodes++; + + if (new_level->parent_level || filter->priv->virtual_root) + { + GtkTreeIter f_iter; + + f_iter.stamp = filter->priv->stamp; + f_iter.user_data = new_level; + f_iter.user_data2 = &(g_array_index (new_level->array, FilterElt, new_level->array->len - 1)); + + gtk_tree_model_filter_ref_node (GTK_TREE_MODEL (filter), &f_iter); - if (!new_level->parent_level) - filter->priv->root_level_visible++; + if (emit_inserted) + { + GtkTreePath *f_path; + + f_path = gtk_tree_model_get_path (GTK_TREE_MODEL (filter), + &f_iter); + gtk_tree_model_row_inserted (GTK_TREE_MODEL (filter), + f_path, &f_iter); + gtk_tree_path_free (f_path); + } + } } i++; } while (gtk_tree_model_iter_next (filter->priv->child_model, &iter)); + + if (new_level->array->len == 0 + && (new_level != filter->priv->root || filter->priv->virtual_root)) + { + /* If none of the nodes are visible, we will just pull in the + * first node of the level and keep a reference on it. We need this + * to make sure that we get all signals for this level. + */ + FilterElt filter_elt; + GtkTreeIter f_iter; + + filter_elt.offset = 0; + filter_elt.zero_ref_count = 0; + filter_elt.ref_count = 0; + filter_elt.children = NULL; + filter_elt.visible = FALSE; + + if (GTK_TREE_MODEL_FILTER_CACHE_CHILD_ITERS (filter)) + filter_elt.iter = first_node; + + g_array_append_val (new_level->array, filter_elt); + + f_iter.stamp = filter->priv->stamp; + f_iter.user_data = new_level; + f_iter.user_data2 = &(g_array_index (new_level->array, FilterElt, new_level->array->len - 1)); + + gtk_tree_model_filter_ref_node (GTK_TREE_MODEL (filter), &f_iter); + } + else if (new_level->array->len == 0) + gtk_tree_model_filter_free_level (filter, new_level); } static void @@ -551,36 +605,41 @@ gtk_tree_model_filter_free_level (GtkTreeModelFilter *filter, g_assert (filter_level); + for (i = 0; i < filter_level->array->len; i++) + { + if (g_array_index (filter_level->array, FilterElt, i).children) + gtk_tree_model_filter_free_level (filter, + FILTER_LEVEL (g_array_index (filter_level->array, FilterElt, i).children)); + + if (filter_level->parent_level || filter->priv->virtual_root) + { + GtkTreeIter f_iter; + + f_iter.stamp = filter->priv->stamp; + f_iter.user_data = filter_level; + f_iter.user_data2 = &(g_array_index (filter_level->array, FilterElt, i)); + + gtk_tree_model_filter_unref_node (GTK_TREE_MODEL (filter), &f_iter); + } + } + if (filter_level->ref_count == 0) { FilterLevel *parent_level = filter_level->parent_level; FilterElt *parent_elt = filter_level->parent_elt; - do + while (parent_level) { - if (parent_elt) - parent_elt->zero_ref_count--; + parent_elt->zero_ref_count--; - if (parent_level) - { - parent_elt = parent_level->parent_elt; - parent_level = parent_level->parent_level; - } + parent_elt = parent_level->parent_elt; + parent_level = parent_level->parent_level; } - while (parent_level); - filter->priv->zero_ref_count--; - } - for (i = 0; i < filter_level->array->len; i++) - { - if (g_array_index (filter_level->array, FilterElt, i).children) - gtk_tree_model_filter_free_level (filter, - FILTER_LEVEL (g_array_index (filter_level->array, FilterElt, i).children)); + if (filter_level != filter->priv->root) + filter->priv->zero_ref_count--; } - if (!filter_level->parent_level) - filter->priv->root_level_visible = 0; - if (filter_level->parent_elt) filter_level->parent_elt->children = NULL; else @@ -593,6 +652,7 @@ gtk_tree_model_filter_free_level (GtkTreeModelFilter *filter, filter_level = NULL; } +/* Creates paths suitable for accessing the child model. */ static GtkTreePath * gtk_tree_model_filter_elt_get_path (FilterLevel *level, FilterElt *elt, @@ -708,7 +768,7 @@ gtk_tree_model_filter_visible (GtkTreeModelFilter *filter, return FALSE; } - /* no filter thing set, so always visible */ + /* no visible function set, so always visible */ return TRUE; } @@ -733,6 +793,45 @@ gtk_tree_model_filter_clear_cache_helper (GtkTreeModelFilter *filter, } } +static FilterElt * +gtk_tree_model_filter_get_nth (GtkTreeModelFilter *filter, + FilterLevel *level, + int n) +{ + if (level->array->len <= n) + return NULL; + + return &g_array_index (level->array, FilterElt, n); +} + +static FilterElt * +gtk_tree_model_filter_get_nth_visible (GtkTreeModelFilter *filter, + FilterLevel *level, + int n) +{ + int i = 0; + FilterElt *elt; + + if (level->visible_nodes <= n) + return NULL; + + elt = FILTER_ELT (level->array->data); + while (!elt->visible) + elt++; + + while (i < n) + { + if (elt->visible) + i++; + elt++; + } + + while (!elt->visible) + elt++; + + return elt; +} + static FilterElt * gtk_tree_model_filter_fetch_child (GtkTreeModelFilter *filter, FilterLevel *level, @@ -828,77 +927,78 @@ gtk_tree_model_filter_fetch_child (GtkTreeModelFilter *filter, g_array_insert_val (level->array, i, elt); *index = i; - if (i > 0) - i--; - - for ( ; i < level->array->len; i++) + for (i = 0; i < level->array->len; i++) { FilterElt *e = &(g_array_index (level->array, FilterElt, i)); if (e->children) e->children->parent_elt = e; } + c_iter.stamp = filter->priv->stamp; + c_iter.user_data = level; + c_iter.user_data2 = &g_array_index (level->array, FilterElt, *index); + + if (level->parent_level || filter->priv->virtual_root) + gtk_tree_model_filter_ref_node (GTK_TREE_MODEL (filter), &c_iter); + return &g_array_index (level->array, FilterElt, *index); } static void gtk_tree_model_filter_remove_node (GtkTreeModelFilter *filter, - GtkTreeIter *iter, - gboolean emit_signal) + GtkTreeIter *iter) { FilterElt *elt, *parent; FilterLevel *level, *parent_level; - gint offset, i, length, level_refcount; + gint i, length; - /* FIXME: this function is very ugly. I need to rethink and - * rewrite it someday. - */ + gboolean emit_child_toggled = FALSE; level = FILTER_LEVEL (iter->user_data); elt = FILTER_ELT (iter->user_data2); parent = level->parent_elt; parent_level = level->parent_level; + length = level->array->len; - offset = elt->offset; - /* ref counting */ - while (elt->ref_count > 0) - gtk_tree_model_filter_real_unref_node (GTK_TREE_MODEL (filter), - iter, FALSE); + /* we distinguish a couple of cases: + * - root level, length > 1: emit row-deleted and remove. + * - root level, length == 1: emit row-deleted and keep in cache. + * - level, length == 1: parent->ref_count > 1: emit row-deleted and keep. + * - level, length > 1: emit row-deleted and remove. + * - else, remove level. + * + * if level != root level and visible nodes == 0, emit row-has-child-toggled. + */ - level_refcount = level->ref_count; + if (level != filter->priv->root && level->visible_nodes == 0) + emit_child_toggled = TRUE; - /* do the ref counting first! this touches the stamp */ - if (emit_signal) + if (length > 1) { GtkTreePath *path; + FilterElt *tmp; + + /* we emit row-deleted, and remove the node from the cache. + */ path = gtk_tree_model_get_path (GTK_TREE_MODEL (filter), iter); + elt->visible = FALSE; gtk_tree_model_filter_increment_stamp (filter); + iter->stamp = filter->priv->stamp; gtk_tree_model_row_deleted (GTK_TREE_MODEL (filter), path); gtk_tree_path_free (path); - } - if ((length == 1 || level_refcount == 0) && - emit_signal && iter->user_data != filter->priv->root) - { - /* above code destroyed the level */ - goto emit_has_child_toggled; - } - - if (length == 1) - { - /* kill the level */ - gtk_tree_model_filter_free_level (filter, level); + while (elt->ref_count > 1) + gtk_tree_model_filter_real_unref_node (GTK_TREE_MODEL (filter), + iter, FALSE); - if (!filter->priv->root) - /* we killed the root */ - return; - } - else - { - FilterElt *tmp; + if (parent_level || filter->priv->virtual_root) + gtk_tree_model_filter_unref_node (GTK_TREE_MODEL (filter), iter); + else if (elt->ref_count > 0) + gtk_tree_model_filter_real_unref_node (GTK_TREE_MODEL (filter), + iter, FALSE); /* remove the node */ tmp = bsearch_elt_with_offset (level->array, elt->offset, &i); @@ -919,16 +1019,43 @@ gtk_tree_model_filter_remove_node (GtkTreeModelFilter *filter, } } } + else if ((length == 1 && parent && parent->ref_count > 1) + || (length == 1 && level == filter->priv->root)) + { + GtkTreePath *path; + + /* we emit row-deleted, but keep the node in the cache and + * referenced. + */ + + path = gtk_tree_model_get_path (GTK_TREE_MODEL (filter), iter); + elt->visible = FALSE; + gtk_tree_model_filter_increment_stamp (filter); + gtk_tree_model_row_deleted (GTK_TREE_MODEL (filter), path); + gtk_tree_path_free (path); + } + else + { + GtkTreePath *path; -emit_has_child_toggled: - /* children are being handled first, so we can check it this way - * - * yes this if-statement is ugly - */ - if ((parent && parent->children && parent->children->array->len <= 1) || - (length == 1 && emit_signal && iter->user_data != filter->priv->root)) + /* blow level away */ + + path = gtk_tree_model_get_path (GTK_TREE_MODEL (filter), iter); + elt->visible = FALSE; + gtk_tree_model_filter_increment_stamp (filter); + iter->stamp = filter->priv->stamp; + gtk_tree_model_row_deleted (GTK_TREE_MODEL (filter), path); + gtk_tree_path_free (path); + + while (elt->ref_count > 1) + gtk_tree_model_filter_real_unref_node (GTK_TREE_MODEL (filter), + iter, FALSE); + + gtk_tree_model_filter_free_level (filter, level); + } + + if (emit_child_toggled) { - /* latest child has been removed, level has been destroyed */ GtkTreeIter piter; GtkTreePath *ppath; @@ -1074,7 +1201,8 @@ gtk_tree_model_filter_row_changed (GtkTreeModel *c_model, if (path) { - gtk_tree_model_get_iter (GTK_TREE_MODEL (filter), &iter, path); + gtk_tree_model_filter_get_iter_full (GTK_TREE_MODEL (filter), + &iter, path); current_state = FILTER_ELT (iter.user_data2)->visible; } else @@ -1087,22 +1215,21 @@ gtk_tree_model_filter_row_changed (GtkTreeModel *c_model, if (current_state == TRUE && requested_state == FALSE) { /* get rid of this node */ - gtk_tree_model_get_iter (GTK_TREE_MODEL (filter), &iter, path); - level = FILTER_LEVEL (iter.user_data); + level->visible_nodes--; - if (!level->parent_level) - filter->priv->root_level_visible--; - - gtk_tree_model_filter_remove_node (filter, &iter, TRUE); + gtk_tree_model_filter_remove_node (filter, &iter); goto done; } if (current_state == TRUE && requested_state == TRUE) { - /* progate the signal */ - gtk_tree_model_get_iter (GTK_TREE_MODEL (filter), &iter, path); + /* propagate the signal; also get a path taking only visible + * nodes into account. + */ + gtk_tree_path_free (path); + path = gtk_tree_model_get_path (GTK_TREE_MODEL (filter), &iter); gtk_tree_model_row_changed (GTK_TREE_MODEL (filter), path, &iter); level = FILTER_LEVEL (iter.user_data); @@ -1126,7 +1253,7 @@ gtk_tree_model_filter_row_changed (GtkTreeModel *c_model, gint i; FilterLevel *root; - gtk_tree_model_filter_build_level (filter, NULL, NULL); + gtk_tree_model_filter_build_level (filter, NULL, NULL, FALSE); root = FILTER_LEVEL (filter->priv->root); @@ -1134,7 +1261,7 @@ gtk_tree_model_filter_row_changed (GtkTreeModel *c_model, { for (i = 0; i < root->array->len; i++) g_array_index (root->array, FilterElt, i).visible = FALSE; - filter->priv->root_level_visible = 0; + root->visible_nodes = 0; } } @@ -1150,21 +1277,29 @@ gtk_tree_model_filter_row_changed (GtkTreeModel *c_model, /* parent is probably being filtered out */ goto done; - gtk_tree_model_get_iter (GTK_TREE_MODEL (filter), &iter, path); + gtk_tree_model_filter_get_iter_full (GTK_TREE_MODEL (filter), &iter, path); level = FILTER_LEVEL (iter.user_data); elt = FILTER_ELT (iter.user_data2); - elt->visible = TRUE; + /* elt->visible can be TRUE at this point if it was pulled in above */ + if (!elt->visible) + { + elt->visible = TRUE; + level->visible_nodes++; + } - if (!level->parent_level) - filter->priv->root_level_visible++; + if ((level->parent_elt && level->parent_elt->visible) || !level->parent_elt) + { + /* visibility changed -- reget path */ + gtk_tree_path_free (path); + path = gtk_tree_model_get_path (GTK_TREE_MODEL (filter), &iter); - /* update stamp */ - gtk_tree_model_row_inserted (GTK_TREE_MODEL (filter), path, &iter); + gtk_tree_model_row_inserted (GTK_TREE_MODEL (filter), path, &iter); - if (gtk_tree_model_iter_children (c_model, &children, c_iter)) - gtk_tree_model_filter_update_children (filter, level, elt); + if (gtk_tree_model_iter_children (c_model, &children, c_iter)) + gtk_tree_model_filter_update_children (filter, level, elt); + } done: if (path) @@ -1191,7 +1326,7 @@ gtk_tree_model_filter_row_inserted (GtkTreeModel *c_model, FilterLevel *level; FilterLevel *parent_level; - gint i = 0, offset, index = -1; + gint i = 0, offset; gboolean free_c_path = FALSE; @@ -1218,22 +1353,34 @@ gtk_tree_model_filter_row_inserted (GtkTreeModel *c_model, { gint level; gint *v_indices, *c_indices; + gboolean common_prefix = TRUE; level = gtk_tree_path_get_depth (c_path) - 1; v_indices = gtk_tree_path_get_indices (filter->priv->virtual_root); c_indices = gtk_tree_path_get_indices (c_path); - if (v_indices[level] >= c_indices[level]) + for (i = 0; i < level; i++) + if (v_indices[i] != c_indices[i]) + { + common_prefix = FALSE; + break; + } + + if (common_prefix && v_indices[level] >= c_indices[level]) (v_indices[level])++; } } if (!filter->priv->root) { - gtk_tree_model_filter_build_level (filter, NULL, NULL); - /* that already put the inserted iter in the level */ + /* build level will pull in the new child */ + gtk_tree_model_filter_build_level (filter, NULL, NULL, FALSE); - goto done_and_emit; + if (filter->priv->root + && FILTER_LEVEL (filter->priv->root)->visible_nodes) + goto done_and_emit; + else + goto done; } parent_level = level = FILTER_LEVEL (filter->priv->root); @@ -1243,7 +1390,7 @@ gtk_tree_model_filter_row_inserted (GtkTreeModel *c_model, { real_path = gtk_tree_model_filter_remove_root (c_path, filter->priv->virtual_root); - /* not our kiddo */ + /* not our child */ if (!real_path) goto done; } @@ -1333,11 +1480,20 @@ gtk_tree_model_filter_row_inserted (GtkTreeModel *c_model, if (g_array_index (level->array, FilterElt, i).offset > offset) break; + level->visible_nodes++; + g_array_insert_val (level->array, i, felt); - index = i; - if (!level->parent_level) - filter->priv->root_level_visible++; + if (level->parent_level || filter->priv->virtual_root) + { + GtkTreeIter f_iter; + + f_iter.stamp = filter->priv->stamp; + f_iter.user_data = level; + f_iter.user_data2 = &g_array_index (level->array, FilterElt, i); + + gtk_tree_model_filter_ref_node (GTK_TREE_MODEL (filter), &f_iter); + } } /* another iteration to update the references of children to parents. */ @@ -1365,7 +1521,12 @@ done_and_emit: gtk_tree_model_filter_increment_stamp (filter); - gtk_tree_model_get_iter (GTK_TREE_MODEL (data), &iter, path); + gtk_tree_model_filter_get_iter_full (GTK_TREE_MODEL (data), &iter, path); + + /* get a path taking only visible nodes into account */ + gtk_tree_path_free (path); + path = gtk_tree_model_get_path (GTK_TREE_MODEL (data), &iter); + gtk_tree_model_row_inserted (GTK_TREE_MODEL (data), path, &iter); gtk_tree_path_free (path); @@ -1387,10 +1548,20 @@ gtk_tree_model_filter_row_has_child_toggled (GtkTreeModel *c_model, GtkTreeModelFilter *filter = GTK_TREE_MODEL_FILTER (data); GtkTreePath *path; GtkTreeIter iter; + FilterLevel *level; + FilterElt *elt; g_return_if_fail (c_path != NULL && c_iter != NULL); - /* FIXME: does this code work? */ + /* If we get row-has-child-toggled on the virtual root, and there is + * no root level; try to build it now. + */ + if (filter->priv->virtual_root && !filter->priv->root + && !gtk_tree_path_compare (c_path, filter->priv->virtual_root)) + { + gtk_tree_model_filter_build_level (filter, NULL, NULL, TRUE); + return; + } if (!gtk_tree_model_filter_visible (filter, c_iter)) return; @@ -1402,10 +1573,25 @@ gtk_tree_model_filter_row_has_child_toggled (GtkTreeModel *c_model, if (!path) return; - gtk_tree_model_get_iter (GTK_TREE_MODEL (data), &iter, path); - gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (data), path, &iter); + gtk_tree_model_filter_get_iter_full (GTK_TREE_MODEL (data), &iter, path); gtk_tree_path_free (path); + + level = FILTER_LEVEL (iter.user_data); + elt = FILTER_ELT (iter.user_data2); + + g_assert (elt->visible); + + /* If this node is referenced and has children, build the level so we + * can monitor it for changes. + */ + if (elt->ref_count > 1 && gtk_tree_model_iter_has_child (c_model, c_iter)) + gtk_tree_model_filter_build_level (filter, level, elt, TRUE); + + /* get a path taking only visible nodes into account */ + path = gtk_tree_model_get_path (GTK_TREE_MODEL (data), &iter); + gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (data), path, &iter); + gtk_tree_path_free (path); } static void @@ -1416,10 +1602,10 @@ gtk_tree_model_filter_row_deleted (GtkTreeModel *c_model, GtkTreeModelFilter *filter = GTK_TREE_MODEL_FILTER (data); GtkTreePath *path; GtkTreeIter iter; - FilterElt *elt; - FilterLevel *level; + FilterElt *elt, *parent = NULL; + FilterLevel *level, *parent_level = NULL; + gboolean emit_child_toggled = FALSE; gint offset; - gboolean emit_signal = TRUE; gint i; g_return_if_fail (c_path != NULL); @@ -1433,6 +1619,9 @@ gtk_tree_model_filter_row_deleted (GtkTreeModel *c_model, GtkTreePath *path; FilterLevel *level = FILTER_LEVEL (filter->priv->root); + gtk_tree_model_filter_unref_path (filter, filter->priv->virtual_root); + filter->priv->virtual_root_deleted = TRUE; + if (!level) return; @@ -1446,7 +1635,7 @@ gtk_tree_model_filter_row_deleted (GtkTreeModel *c_model, path = gtk_tree_path_new (); gtk_tree_path_append_index (path, 0); - for (i = 0; i < level->array->len; i++) + for (i = 0; i < level->visible_nodes; i++) gtk_tree_model_row_deleted (GTK_TREE_MODEL (data), path); gtk_tree_path_free (path); @@ -1463,12 +1652,20 @@ gtk_tree_model_filter_row_deleted (GtkTreeModel *c_model, { gint level; gint *v_indices, *c_indices; + gboolean common_prefix = TRUE; level = gtk_tree_path_get_depth (c_path) - 1; v_indices = gtk_tree_path_get_indices (filter->priv->virtual_root); c_indices = gtk_tree_path_get_indices (c_path); - if (v_indices[level] > c_indices[level]) + for (i = 0; i < level; i++) + if (v_indices[i] != c_indices[i]) + { + common_prefix = FALSE; + break; + } + + if (common_prefix && v_indices[level] > c_indices[level]) (v_indices[level])--; } } @@ -1480,7 +1677,10 @@ gtk_tree_model_filter_row_deleted (GtkTreeModel *c_model, if (!path) { - /* fixup the offsets */ + /* The node deleted in the child model is not visible in the + * filter model. We will not emit a signal, just fixup the offsets + * of the other nodes. + */ GtkTreePath *real_path; if (!filter->priv->root) @@ -1503,6 +1703,7 @@ gtk_tree_model_filter_row_deleted (GtkTreeModel *c_model, i = 0; if (gtk_tree_path_get_depth (real_path) - 1 >= 1) { + /* find the level where the deletion occurred */ while (i < gtk_tree_path_get_depth (real_path) - 1) { gint j; @@ -1536,10 +1737,7 @@ gtk_tree_model_filter_row_deleted (GtkTreeModel *c_model, if (!level) return; - /* we need: - * - the offset of the removed item - * - the level - */ + /* decrease offset of all nodes following the deleted node */ for (i = 0; i < level->array->len; i++) { elt = &g_array_index (level->array, FilterElt, i); @@ -1552,35 +1750,41 @@ gtk_tree_model_filter_row_deleted (GtkTreeModel *c_model, return; } - gtk_tree_model_get_iter (GTK_TREE_MODEL (data), &iter, path); + /* a node was deleted, which was in our cache */ + gtk_tree_model_filter_get_iter_full (GTK_TREE_MODEL (data), &iter, path); level = FILTER_LEVEL (iter.user_data); elt = FILTER_ELT (iter.user_data2); offset = elt->offset; - if (!level->parent_level && elt->visible) - filter->priv->root_level_visible--; - - if (emit_signal) + if (elt->visible) { - if (level->ref_count == 0 && level != filter->priv->root) - { - gtk_tree_model_filter_increment_stamp (filter); - gtk_tree_model_row_deleted (GTK_TREE_MODEL (data), path); + /* get a path taking only visible nodes into account */ + gtk_tree_path_free (path); + path = gtk_tree_model_get_path (GTK_TREE_MODEL (filter), &iter); - gtk_tree_path_free (path); - return; + level->visible_nodes--; + + if (level->visible_nodes == 0) + { + emit_child_toggled = TRUE; + parent_level = level->parent_level; + parent = level->parent_elt; } + /* emit row_deleted */ gtk_tree_model_filter_increment_stamp (filter); gtk_tree_model_row_deleted (GTK_TREE_MODEL (data), path); iter.stamp = filter->priv->stamp; - - while (elt->ref_count > 0) - gtk_tree_model_filter_real_unref_node (GTK_TREE_MODEL (data), &iter, - FALSE); } + /* The filter model's reference on the child node is released + * below. + */ + while (elt->ref_count > 1) + gtk_tree_model_filter_real_unref_node (GTK_TREE_MODEL (data), &iter, + FALSE); + if (level->array->len == 1) { /* kill level */ @@ -1590,6 +1794,13 @@ gtk_tree_model_filter_row_deleted (GtkTreeModel *c_model, { FilterElt *tmp; + /* release the filter model's reference on the node */ + if (level->parent_level || filter->priv->virtual_root) + gtk_tree_model_filter_unref_node (GTK_TREE_MODEL (filter), &iter); + else if (elt->ref_count > 0) + gtk_tree_model_filter_real_unref_node (GTK_TREE_MODEL (data), &iter, + FALSE); + /* remove the row */ tmp = bsearch_elt_with_offset (level->array, elt->offset, &i); @@ -1607,6 +1818,27 @@ gtk_tree_model_filter_row_deleted (GtkTreeModel *c_model, } } + if (emit_child_toggled && parent_level) + { + GtkTreeIter iter; + GtkTreePath *path; + + iter.stamp = filter->priv->stamp; + iter.user_data = parent_level; + iter.user_data2 = parent; + + /* We set in_row_deleted to TRUE to avoid a level build triggered + * by row-has-child-toggled (parent model could call iter_has_child + * for example). + */ + filter->priv->in_row_deleted = TRUE; + path = gtk_tree_model_get_path (GTK_TREE_MODEL (filter), &iter); + gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (filter), + path, &iter); + gtk_tree_path_free (path); + filter->priv->in_row_deleted = FALSE; + } + gtk_tree_path_free (path); } @@ -1634,9 +1866,6 @@ gtk_tree_model_filter_rows_reordered (GtkTreeModel *c_model, if (c_path == NULL || gtk_tree_path_get_indices (c_path) == NULL) { - if (!filter->priv->root) - return; - length = gtk_tree_model_iter_n_children (c_model, NULL); if (filter->priv->virtual_root) @@ -1664,8 +1893,7 @@ gtk_tree_model_filter_rows_reordered (GtkTreeModel *c_model, /* virtual root anchor reordering */ if (filter->priv->virtual_root && - gtk_tree_path_get_depth (c_path) < - gtk_tree_path_get_depth (filter->priv->virtual_root)) + gtk_tree_path_is_ancestor (c_path, filter->priv->virtual_root)) { gint new_pos = -1; gint length; @@ -1696,6 +1924,7 @@ gtk_tree_model_filter_rows_reordered (GtkTreeModel *c_model, c_path, FALSE, FALSE); + if (!path && filter->priv->virtual_root && gtk_tree_path_compare (c_path, filter->priv->virtual_root)) return; @@ -1714,7 +1943,8 @@ gtk_tree_model_filter_rows_reordered (GtkTreeModel *c_model, } else { - gtk_tree_model_get_iter (GTK_TREE_MODEL (data), &iter, path); + gtk_tree_model_filter_get_iter_full (GTK_TREE_MODEL (data), + &iter, path); level = FILTER_LEVEL (iter.user_data); elt = FILTER_ELT (iter.user_data2); @@ -1732,7 +1962,7 @@ gtk_tree_model_filter_rows_reordered (GtkTreeModel *c_model, } } - if (level->array->len < 1) + if (!level || level->array->len < 1) { gtk_tree_path_free (path); return; @@ -1786,8 +2016,14 @@ gtk_tree_model_filter_rows_reordered (GtkTreeModel *c_model, gtk_tree_model_rows_reordered (GTK_TREE_MODEL (data), path, NULL, tmp_array); else - gtk_tree_model_rows_reordered (GTK_TREE_MODEL (data), path, &iter, - tmp_array); + { + /* get a path taking only visible nodes into account */ + gtk_tree_path_free (path); + path = gtk_tree_model_get_path (GTK_TREE_MODEL (data), &iter); + + gtk_tree_model_rows_reordered (GTK_TREE_MODEL (data), path, &iter, + tmp_array); + } /* done */ g_free (tmp_array); @@ -1853,6 +2089,65 @@ gtk_tree_model_filter_get_column_type (GtkTreeModel *model, return gtk_tree_model_get_column_type (filter->priv->child_model, index); } +/* A special case of _get_iter; this function can also get iters which + * are not visible. These iters should ONLY be passed internally, never + * pass those along with a signal emission. + */ +static gboolean +gtk_tree_model_filter_get_iter_full (GtkTreeModel *model, + GtkTreeIter *iter, + GtkTreePath *path) +{ + GtkTreeModelFilter *filter = (GtkTreeModelFilter *)model; + gint *indices; + FilterLevel *level; + FilterElt *elt; + gint depth, i; + g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (model), FALSE); + g_return_val_if_fail (filter->priv->child_model != NULL, FALSE); + + indices = gtk_tree_path_get_indices (path); + + if (filter->priv->root == NULL) + gtk_tree_model_filter_build_level (filter, NULL, NULL, FALSE); + level = FILTER_LEVEL (filter->priv->root); + + depth = gtk_tree_path_get_depth (path); + if (!depth) + { + iter->stamp = 0; + return FALSE; + } + + for (i = 0; i < depth - 1; i++) + { + if (!level || indices[i] >= level->array->len) + { + return FALSE; + } + + elt = gtk_tree_model_filter_get_nth (filter, level, indices[i]); + + if (!elt->children) + gtk_tree_model_filter_build_level (filter, level, elt, FALSE); + level = elt->children; + } + + if (!level || indices[i] >= level->array->len) + { + iter->stamp = 0; + return FALSE; + } + + iter->stamp = filter->priv->stamp; + iter->user_data = level; + + elt = gtk_tree_model_filter_get_nth (filter, level, indices[depth - 1]); + iter->user_data2 = elt; + + return TRUE; +} + static gboolean gtk_tree_model_filter_get_iter (GtkTreeModel *model, GtkTreeIter *iter, @@ -1861,15 +2156,15 @@ gtk_tree_model_filter_get_iter (GtkTreeModel *model, GtkTreeModelFilter *filter = (GtkTreeModelFilter *)model; gint *indices; FilterLevel *level; + FilterElt *elt; gint depth, i; - g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (model), FALSE); g_return_val_if_fail (filter->priv->child_model != NULL, FALSE); indices = gtk_tree_path_get_indices (path); if (filter->priv->root == NULL) - gtk_tree_model_filter_build_level (filter, NULL, NULL); + gtk_tree_model_filter_build_level (filter, NULL, NULL, FALSE); level = FILTER_LEVEL (filter->priv->root); depth = gtk_tree_path_get_depth (path); @@ -1881,20 +2176,19 @@ gtk_tree_model_filter_get_iter (GtkTreeModel *model, for (i = 0; i < depth - 1; i++) { - if (!level || indices[i] >= level->array->len) + if (!level || indices[i] >= level->visible_nodes) { return FALSE; } - if (!g_array_index (level->array, FilterElt, indices[i]).children) - gtk_tree_model_filter_build_level (filter, level, - &g_array_index (level->array, - FilterElt, - indices[i])); - level = g_array_index (level->array, FilterElt, indices[i]).children; + elt = gtk_tree_model_filter_get_nth_visible (filter, level, indices[i]); + + if (!elt->children) + gtk_tree_model_filter_build_level (filter, level, elt, FALSE); + level = elt->children; } - if (!level || indices[i] >= level->array->len) + if (!level || indices[i] >= level->visible_nodes) { iter->stamp = 0; return FALSE; @@ -1902,8 +2196,10 @@ gtk_tree_model_filter_get_iter (GtkTreeModel *model, iter->stamp = filter->priv->stamp; iter->user_data = level; - iter->user_data2 = &g_array_index (level->array, FilterElt, - indices[depth - 1]); + + elt = gtk_tree_model_filter_get_nth_visible (filter, level, + indices[depth - 1]); + iter->user_data2 = elt; return TRUE; } @@ -1924,10 +2220,23 @@ gtk_tree_model_filter_get_path (GtkTreeModel *model, level = iter->user_data; elt = iter->user_data2; + if (!elt->visible) + return NULL; + while (level) { - gtk_tree_path_prepend_index (retval, - elt - FILTER_ELT (level->array->data)); + int i = 0, index = 0; + + while (&g_array_index (level->array, FilterElt, i) != elt) + { + if (g_array_index (level->array, FilterElt, i).visible) + index++; + i++; + + g_assert (i < level->array->len); + } + + gtk_tree_path_prepend_index (retval, index); elt = level->parent_elt; level = level->parent_level; } @@ -1971,6 +2280,7 @@ static gboolean gtk_tree_model_filter_iter_next (GtkTreeModel *model, GtkTreeIter *iter) { + int i; FilterLevel *level; FilterElt *elt; @@ -1981,15 +2291,24 @@ gtk_tree_model_filter_iter_next (GtkTreeModel *model, level = iter->user_data; elt = iter->user_data2; - if (elt - FILTER_ELT (level->array->data) >= level->array->len - 1) + i = elt - FILTER_ELT (level->array->data); + + while (i < level->array->len - 1) { - iter->stamp = 0; - return FALSE; + i++; + elt++; + + if (elt->visible) + { + iter->user_data2 = elt; + return TRUE; + } } - iter->user_data2 = elt + 1; + /* no next visible iter */ + iter->stamp = 0; - return TRUE; + return FALSE; } static gboolean @@ -2008,35 +2327,74 @@ gtk_tree_model_filter_iter_children (GtkTreeModel *model, if (!parent) { + int i = 0; + if (!filter->priv->root) - gtk_tree_model_filter_build_level (filter, NULL, NULL); + gtk_tree_model_filter_build_level (filter, NULL, NULL, FALSE); if (!filter->priv->root) return FALSE; level = filter->priv->root; + + if (!level->visible_nodes) + return FALSE; + iter->stamp = filter->priv->stamp; iter->user_data = level; - iter->user_data2 = level->array->data; + + while (i < level->array->len) + { + if (!g_array_index (level->array, FilterElt, i).visible) + { + i++; + continue; + } + + iter->user_data2 = &g_array_index (level->array, FilterElt, i); + return TRUE; + } + + iter->stamp = 0; + return FALSE; } else { + int i = 0; + if (FILTER_ELT (parent->user_data2)->children == NULL) gtk_tree_model_filter_build_level (filter, FILTER_LEVEL (parent->user_data), - FILTER_ELT (parent->user_data2)); + FILTER_ELT (parent->user_data2), + FALSE); if (FILTER_ELT (parent->user_data2)->children == NULL) return FALSE; - /* empty array? */ - if (FILTER_ELT (parent->user_data2)->children->array->len <= 0) + if (FILTER_ELT (parent->user_data2)->children->visible_nodes <= 0) return FALSE; iter->stamp = filter->priv->stamp; iter->user_data = FILTER_ELT (parent->user_data2)->children; - iter->user_data2 = FILTER_LEVEL (iter->user_data)->array->data; + + level = FILTER_LEVEL (iter->user_data); + + while (i < level->array->len) + { + if (!g_array_index (level->array, FilterElt, i).visible) + { + i++; + continue; + } + + iter->user_data2 = &g_array_index (level->array, FilterElt, i); + return TRUE; + } + + iter->stamp = 0; + return FALSE; } - return TRUE; + iter->stamp = 0; + return FALSE; } static gboolean @@ -2056,18 +2414,18 @@ gtk_tree_model_filter_iter_has_child (GtkTreeModel *model, gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (model), &child_iter, iter); elt = FILTER_ELT (iter->user_data2); + if (!elt->visible) + return FALSE; + /* we need to build the level to check if not all children are filtered * out */ if (!elt->children && gtk_tree_model_iter_has_child (filter->priv->child_model, &child_iter)) gtk_tree_model_filter_build_level (filter, FILTER_LEVEL (iter->user_data), - elt); + elt, FALSE); - /* FIXME: we should prolly count the visible nodes here, just like in - * _iter_n_children. - */ - if (elt->children && elt->children->array->len > 0) + if (elt->children && elt->children->visible_nodes > 0) return TRUE; return FALSE; @@ -2089,34 +2447,29 @@ gtk_tree_model_filter_iter_n_children (GtkTreeModel *model, if (!iter) { if (!filter->priv->root) - gtk_tree_model_filter_build_level (filter, NULL, NULL); + gtk_tree_model_filter_build_level (filter, NULL, NULL, FALSE); + + if (filter->priv->root) + return FILTER_LEVEL (filter->priv->root)->visible_nodes; - /* count visible nodes */ - return filter->priv->root_level_visible; + return 0; } elt = FILTER_ELT (iter->user_data2); + + if (!elt->visible) + return 0; + gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (model), &child_iter, iter); if (!elt->children && gtk_tree_model_iter_has_child (filter->priv->child_model, &child_iter)) gtk_tree_model_filter_build_level (filter, FILTER_LEVEL (iter->user_data), - elt); - - if (elt->children && elt->children->array->len) - { - int i = 0; - int count = 0; - GArray *a = elt->children->array; - - /* count visible nodes */ - for (i = 0; i < a->len; i++) - if (g_array_index (a, FilterElt, i).visible) - count++; + elt, FALSE); - return count; - } + if (elt->children) + return elt->children->visible_nodes; return 0; } @@ -2127,6 +2480,7 @@ gtk_tree_model_filter_iter_nth_child (GtkTreeModel *model, GtkTreeIter *parent, gint n) { + FilterElt *elt; FilterLevel *level; GtkTreeIter children; @@ -2134,7 +2488,7 @@ gtk_tree_model_filter_iter_nth_child (GtkTreeModel *model, if (parent) g_return_val_if_fail (GTK_TREE_MODEL_FILTER (model)->priv->stamp == parent->stamp, FALSE); - /* use this instead of has_Child to force us to build the level, if needed */ + /* use this instead of has_child to force us to build the level, if needed */ if (gtk_tree_model_filter_iter_children (model, &children, parent) == FALSE) { iter->stamp = 0; @@ -2142,15 +2496,20 @@ gtk_tree_model_filter_iter_nth_child (GtkTreeModel *model, } level = children.user_data; - if (n >= level->array->len) + elt = FILTER_ELT (level->array->data); + + if (n >= level->visible_nodes) { iter->stamp = 0; return FALSE; } + elt = gtk_tree_model_filter_get_nth_visible (GTK_TREE_MODEL_FILTER (model), + level, n); + iter->stamp = GTK_TREE_MODEL_FILTER (model)->priv->stamp; iter->user_data = level; - iter->user_data2 = &g_array_index (level->array, FilterElt, n); + iter->user_data2 = elt; return TRUE; } @@ -2209,19 +2568,16 @@ gtk_tree_model_filter_ref_node (GtkTreeModel *model, FilterElt *parent_elt = level->parent_elt; /* we were at zero -- time to decrease the zero_ref_count val */ - do + while (parent_level) { - if (parent_elt) - parent_elt->zero_ref_count--; + parent_elt->zero_ref_count--; - if (parent_level) - { - parent_elt = parent_level->parent_elt; - parent_level = parent_level->parent_level; - } + parent_elt = parent_level->parent_elt; + parent_level = parent_level->parent_level; } - while (parent_level); - filter->priv->zero_ref_count--; + + if (filter->priv->root != level) + filter->priv->zero_ref_count--; } } @@ -2272,7 +2628,9 @@ gtk_tree_model_filter_real_unref_node (GtkTreeModel *model, parent_elt = parent_level->parent_elt; parent_level = parent_level->parent_level; } - filter->priv->zero_ref_count++; + + if (filter->priv->root != level) + filter->priv->zero_ref_count++; } } @@ -2359,7 +2717,8 @@ gtk_tree_model_filter_set_model (GtkTreeModelFilter *filter, filter->priv->root = NULL; g_object_unref (filter->priv->child_model); filter->priv->visible_column = -1; - /* FIXME: destroy more crack here? the funcs? */ + + /* FIXME: do we need to destroy more here? */ } filter->priv->child_model = child_model; @@ -2393,6 +2752,48 @@ gtk_tree_model_filter_set_model (GtkTreeModelFilter *filter, } } +static void +gtk_tree_model_filter_ref_path (GtkTreeModelFilter *filter, + GtkTreePath *path) +{ + int len; + GtkTreePath *p; + + len = gtk_tree_path_get_depth (path); + p = gtk_tree_path_copy (path); + while (len--) + { + GtkTreeIter iter; + + gtk_tree_model_get_iter (GTK_TREE_MODEL (filter->priv->child_model), &iter, p); + gtk_tree_model_ref_node (GTK_TREE_MODEL (filter->priv->child_model), &iter); + gtk_tree_path_up (p); + } + + gtk_tree_path_free (p); +} + +static void +gtk_tree_model_filter_unref_path (GtkTreeModelFilter *filter, + GtkTreePath *path) +{ + int len; + GtkTreePath *p; + + len = gtk_tree_path_get_depth (path); + p = gtk_tree_path_copy (path); + while (len--) + { + GtkTreeIter iter; + + gtk_tree_model_get_iter (GTK_TREE_MODEL (filter->priv->child_model), &iter, p); + gtk_tree_model_unref_node (GTK_TREE_MODEL (filter->priv->child_model), &iter); + gtk_tree_path_up (p); + } + + gtk_tree_path_free (p); +} + static void gtk_tree_model_filter_set_root (GtkTreeModelFilter *filter, GtkTreePath *root) @@ -2424,6 +2825,7 @@ gtk_tree_model_filter_new (GtkTreeModel *child_model, GtkTreePath *root) { GtkTreeModel *retval; + GtkTreeModelFilter *filter; g_return_val_if_fail (GTK_IS_TREE_MODEL (child_model), NULL); @@ -2432,6 +2834,13 @@ gtk_tree_model_filter_new (GtkTreeModel *child_model, "virtual-root", root, NULL); + filter = GTK_TREE_MODEL_FILTER (retval); + if (filter->priv->virtual_root) + { + gtk_tree_model_filter_ref_path (filter, filter->priv->virtual_root); + filter->priv->virtual_root_deleted = FALSE; + } + return retval; } @@ -2579,34 +2988,43 @@ gtk_tree_model_filter_set_visible_column (GtkTreeModelFilter *filter, * @child_iter: A valid #GtkTreeIter pointing to a row on the child model. * * Sets @filter_iter to point to the row in @filter that corresponds to the - * row pointed at by @child_iter. + * row pointed at by @child_iter. If @filter_iter was not set, %FALSE is + * returned. + * + * Return value: %TRUE, if @filter_iter was set, i.e. if @child_iter is a + * valid iterator pointing to a visible row in child model. * * Since: 2.4 */ -void +gboolean gtk_tree_model_filter_convert_child_iter_to_iter (GtkTreeModelFilter *filter, GtkTreeIter *filter_iter, GtkTreeIter *child_iter) { + gboolean ret; GtkTreePath *child_path, *path; - g_return_if_fail (GTK_IS_TREE_MODEL_FILTER (filter)); - g_return_if_fail (filter->priv->child_model != NULL); - g_return_if_fail (filter_iter != NULL); - g_return_if_fail (child_iter != NULL); + g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (filter), FALSE); + g_return_val_if_fail (filter->priv->child_model != NULL, FALSE); + g_return_val_if_fail (filter_iter != NULL, FALSE); + g_return_val_if_fail (child_iter != NULL, FALSE); filter_iter->stamp = 0; child_path = gtk_tree_model_get_path (filter->priv->child_model, child_iter); - g_return_if_fail (child_path != NULL); + g_return_val_if_fail (child_path != NULL, FALSE); path = gtk_tree_model_filter_convert_child_path_to_path (filter, child_path); gtk_tree_path_free (child_path); - g_return_if_fail (path != NULL); - gtk_tree_model_get_iter (GTK_TREE_MODEL (filter), filter_iter, path); + if (!path) + return FALSE; + + ret = gtk_tree_model_get_iter (GTK_TREE_MODEL (filter), filter_iter, path); gtk_tree_path_free (path); + + return ret; } /** @@ -2646,6 +3064,7 @@ gtk_tree_model_filter_convert_iter_to_child_iter (GtkTreeModelFilter *filter, } } +/* The path returned can only be used internally in the filter model. */ static GtkTreePath * gtk_real_tree_model_filter_convert_child_path_to_path (GtkTreeModelFilter *filter, GtkTreePath *child_path, @@ -2676,7 +3095,7 @@ gtk_real_tree_model_filter_convert_child_path_to_path (GtkTreeModelFilter *filte child_indices = gtk_tree_path_get_indices (real_path); if (filter->priv->root == NULL && build_levels) - gtk_tree_model_filter_build_level (filter, NULL, NULL); + gtk_tree_model_filter_build_level (filter, NULL, NULL, FALSE); level = FILTER_LEVEL (filter->priv->root); for (i = 0; i < gtk_tree_path_get_depth (real_path); i++) @@ -2696,7 +3115,7 @@ gtk_real_tree_model_filter_convert_child_path_to_path (GtkTreeModelFilter *filte { gtk_tree_path_append_index (retval, j); if (!tmp->children && build_levels) - gtk_tree_model_filter_build_level (filter, level, tmp); + gtk_tree_model_filter_build_level (filter, level, tmp, FALSE); level = tmp->children; found_child = TRUE; } @@ -2718,7 +3137,7 @@ gtk_real_tree_model_filter_convert_child_path_to_path (GtkTreeModelFilter *filte gtk_tree_path_append_index (retval, j); if (!tmp->children && build_levels) - gtk_tree_model_filter_build_level (filter, level, tmp); + gtk_tree_model_filter_build_level (filter, level, tmp, FALSE); level = tmp->children; found_child = TRUE; } @@ -2743,7 +3162,8 @@ gtk_real_tree_model_filter_convert_child_path_to_path (GtkTreeModelFilter *filte * Converts @child_path to a path relative to @filter. That is, @child_path * points to a path in the child model. The rerturned path will point to the * same row in the filtered model. If @child_path isn't a valid path on the - * child model, then %NULL is returned. + * child model or points to a row which is not visible in @filter, then %NULL + * is returned. * * Return value: A newly allocated #GtkTreePath, or %NULL. * @@ -2753,11 +3173,29 @@ GtkTreePath * gtk_tree_model_filter_convert_child_path_to_path (GtkTreeModelFilter *filter, GtkTreePath *child_path) { + GtkTreeIter iter; + GtkTreePath *path; + /* this function does the sanity checks */ - return gtk_real_tree_model_filter_convert_child_path_to_path (filter, + path = gtk_real_tree_model_filter_convert_child_path_to_path (filter, child_path, TRUE, TRUE); + + if (!path) + return NULL; + + /* get a new path which only takes visible nodes into account. + * -- if this gives any performance issues, we can write a special + * version of convert_child_path_to_path immediately returning + * a visible-nodes-only path. + */ + gtk_tree_model_filter_get_iter_full (GTK_TREE_MODEL (filter), &iter, path); + + gtk_tree_path_free (path); + path = gtk_tree_model_get_path (GTK_TREE_MODEL (filter), &iter); + + return path; } /** @@ -2791,30 +3229,33 @@ gtk_tree_model_filter_convert_path_to_child_path (GtkTreeModelFilter *filter, retval = gtk_tree_path_new (); filter_indices = gtk_tree_path_get_indices (filter_path); if (!filter->priv->root) - gtk_tree_model_filter_build_level (filter, NULL, NULL); + gtk_tree_model_filter_build_level (filter, NULL, NULL, FALSE); level = FILTER_LEVEL (filter->priv->root); for (i = 0; i < gtk_tree_path_get_depth (filter_path); i++) { - gint count = filter_indices[i]; + FilterElt *elt; - if (!level || level->array->len <= filter_indices[i]) + if (!level || level->visible_nodes <= filter_indices[i]) { gtk_tree_path_free (retval); return NULL; } - if (g_array_index (level->array, FilterElt, count).children == NULL) - gtk_tree_model_filter_build_level (filter, level, &g_array_index (level->array, FilterElt, count)); + elt = gtk_tree_model_filter_get_nth_visible (filter, level, + filter_indices[i]); + + if (elt->children == NULL) + gtk_tree_model_filter_build_level (filter, level, elt, FALSE); - if (!level || level->array->len <= filter_indices[i]) + if (!level || level->visible_nodes <= filter_indices[i]) { gtk_tree_path_free (retval); return NULL; } - gtk_tree_path_append_index (retval, g_array_index (level->array, FilterElt, count).offset); - level = g_array_index (level->array, FilterElt, count).children; + gtk_tree_path_append_index (retval, elt->offset); + level = elt->children; } /* apply vroot */ @@ -2874,7 +3315,7 @@ gtk_tree_model_filter_refilter (GtkTreeModelFilter *filter) * gtk_tree_model_ref_node(). This might be useful if the child model * being filtered is static (and doesn't change often) and there has been * a lot of unreffed access to nodes. As a side effect of this function, - * all unreffed itters will be invalid. + * all unreffed iters will be invalid. * * Since: 2.4 */ @@ -2883,7 +3324,7 @@ gtk_tree_model_filter_clear_cache (GtkTreeModelFilter *filter) { g_return_if_fail (GTK_IS_TREE_MODEL_FILTER (filter)); - if (filter->priv->zero_ref_count) + if (filter->priv->zero_ref_count > 0) gtk_tree_model_filter_clear_cache_helper (filter, FILTER_LEVEL (filter->priv->root)); }