X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;ds=sidebyside;f=gtk%2Fgtktreemodelfilter.c;h=53f0d57e5b3a2e8fd0b0fb69bd228e4923abeb65;hb=f71cc8fcafaac561fbe50ecad32c146b614fb6ec;hp=15aca9ae214bc40736c8995a0c9ced0c803a410b;hpb=7cda670d127f50507d896b842a428fcb18eef367;p=~andy%2Fgtk diff --git a/gtk/gtktreemodelfilter.c b/gtk/gtktreemodelfilter.c index 15aca9ae2..53f0d57e5 100644 --- a/gtk/gtktreemodelfilter.c +++ b/gtk/gtktreemodelfilter.c @@ -18,8 +18,12 @@ * Boston, MA 02111-1307, USA. */ +#include #include "gtktreemodelfilter.h" #include "gtkintl.h" +#include "gtktreednd.h" +#include "gtkprivate.h" +#include "gtkalias.h" #include /* ITER FORMAT: @@ -33,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; @@ -50,6 +71,7 @@ struct _FilterLevel { GArray *array; gint ref_count; + gint visible_nodes; FilterElt *parent_elt; FilterLevel *parent_level; @@ -65,8 +87,6 @@ struct _GtkTreeModelFilterPrivate GtkTreeModel *child_model; gint zero_ref_count; - guint root_level_visible; - GtkTreePath *virtual_root; GtkTreeModelFilterVisibleFunc visible_func; @@ -77,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; @@ -107,9 +130,8 @@ 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); static void gtk_tree_model_filter_set_property (GObject *object, guint prop_id, @@ -143,10 +165,13 @@ static void gtk_tree_model_filter_rows_reordered (GtkTr gpointer data); /* GtkTreeModel interface */ -static guint gtk_tree_model_filter_get_flags (GtkTreeModel *model); +static GtkTreeModelFlags gtk_tree_model_filter_get_flags (GtkTreeModel *model); 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); @@ -177,11 +202,21 @@ static void gtk_tree_model_filter_ref_node (GtkTr static void gtk_tree_model_filter_unref_node (GtkTreeModel *model, GtkTreeIter *iter); +/* TreeDragSource interface */ +static gboolean gtk_tree_model_filter_row_draggable (GtkTreeDragSource *drag_source, + GtkTreePath *path); +static gboolean gtk_tree_model_filter_drag_data_get (GtkTreeDragSource *drag_source, + GtkTreePath *path, + GtkSelectionData *selection_data); +static gboolean gtk_tree_model_filter_drag_data_delete (GtkTreeDragSource *drag_source, + GtkTreePath *path); /* 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); @@ -207,22 +242,32 @@ 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); static GtkTreePath *gtk_real_tree_model_filter_convert_child_path_to_path (GtkTreeModelFilter *filter, GtkTreePath *child_path, gboolean build_levels, - gboolean fetch_childs); + 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); -static void gtk_tree_model_filter_update_childs (GtkTreeModelFilter *filter, + GtkTreeIter *iter); +static void gtk_tree_model_filter_update_children (GtkTreeModelFilter *filter, FilterLevel *level, FilterElt *elt); static FilterElt *bsearch_elt_with_offset (GArray *array, @@ -230,46 +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 - }; - - tree_model_filter_type = g_type_register_static (G_TYPE_OBJECT, - "GtkTreeModelFilter", - &tree_model_filter_info, 0); - - g_type_add_interface_static (tree_model_filter_type, - GTK_TYPE_TREE_MODEL, - &tree_model_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) @@ -280,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 @@ -288,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; @@ -300,19 +311,19 @@ gtk_tree_model_filter_class_init (GtkTreeModelFilterClass *filter_class) */ g_object_class_install_property (object_class, PROP_CHILD_MODEL, - g_param_spec_object ("child_model", + g_param_spec_object ("child-model", ("The child model"), ("The model for the filtermodel to filter"), GTK_TYPE_TREE_MODEL, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_VIRTUAL_ROOT, - g_param_spec_boxed ("virtual_root", + g_param_spec_boxed ("virtual-root", ("The virtual root"), ("The virtual root (relative to the child model) for this filtermodel"), GTK_TYPE_TREE_PATH, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_type_class_add_private (object_class, sizeof (GtkTreeModelFilterPrivate)); } @@ -336,12 +347,26 @@ gtk_tree_model_filter_tree_model_init (GtkTreeModelIface *iface) iface->unref_node = gtk_tree_model_filter_unref_node; } +static void +gtk_tree_model_filter_drag_source_init (GtkTreeDragSourceIface *iface) +{ + iface->row_draggable = gtk_tree_model_filter_row_draggable; + iface->drag_data_delete = gtk_tree_model_filter_drag_data_delete; + iface->drag_data_get = gtk_tree_model_filter_drag_data_get; +} + static void 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) @@ -352,9 +377,15 @@ gtk_tree_model_filter_finalize (GObject *object) if (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 @@ -406,9 +437,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; @@ -416,6 +449,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) @@ -463,6 +499,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; @@ -479,12 +516,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 { @@ -502,13 +539,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; - if (!new_level->parent_level) - filter->priv->root_level_visible++; + 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 (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 @@ -519,36 +606,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 @@ -561,6 +653,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, @@ -654,9 +747,10 @@ gtk_tree_model_filter_visible (GtkTreeModelFilter *filter, { if (filter->priv->visible_func) { - return (filter->priv->visible_func (filter->priv->child_model, - child_iter, - filter->priv->visible_data)); + return filter->priv->visible_func (filter->priv->child_model, + child_iter, + filter->priv->visible_data) + ? TRUE : FALSE; } else if (filter->priv->visible_column >= 0) { @@ -675,7 +769,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; } @@ -700,6 +794,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, @@ -795,74 +928,78 @@ gtk_tree_model_filter_fetch_child (GtkTreeModelFilter *filter, g_array_insert_val (level->array, i, elt); *index = i; - for (i = MAX (--i, 0); 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); @@ -871,7 +1008,8 @@ gtk_tree_model_filter_remove_node (GtkTreeModelFilter *filter, { g_array_remove_index (level->array, i); - for (i = MAX (--i, 0); i < level->array->len; i++) + i--; + for (i = MAX (i, 0); i < level->array->len; i++) { /* NOTE: here we do *not* decrease offsets, because the node was * not removed from the child model @@ -882,16 +1020,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; -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)) + /* 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; + + /* 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; @@ -908,9 +1073,9 @@ emit_has_child_toggled: } static void -gtk_tree_model_filter_update_childs (GtkTreeModelFilter *filter, - FilterLevel *level, - FilterElt *elt) +gtk_tree_model_filter_update_children (GtkTreeModelFilter *filter, + FilterLevel *level, + FilterElt *elt) { GtkTreeIter c_iter; GtkTreeIter iter; @@ -963,7 +1128,7 @@ bsearch_elt_with_offset (GArray *array, return NULL; } - while (start != end) + do { middle = (start + end) / 2; @@ -976,6 +1141,7 @@ bsearch_elt_with_offset (GArray *array, else break; } + while (start != end); if (elt->offset == offset) { @@ -995,7 +1161,7 @@ gtk_tree_model_filter_row_changed (GtkTreeModel *c_model, { GtkTreeModelFilter *filter = GTK_TREE_MODEL_FILTER (data); GtkTreeIter iter; - GtkTreeIter childs; + GtkTreeIter children; GtkTreeIter real_c_iter; GtkTreePath *path = NULL; @@ -1036,7 +1202,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 @@ -1049,29 +1216,29 @@ 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); - gtk_tree_model_filter_remove_node (filter, &iter, TRUE); - 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); 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); elt = FILTER_ELT (iter.user_data2); - /* and update the childs */ - if (gtk_tree_model_iter_children (c_model, &childs, &real_c_iter)) - gtk_tree_model_filter_update_childs (filter, level, elt); + /* and update the children */ + if (gtk_tree_model_iter_children (c_model, &children, &real_c_iter)) + gtk_tree_model_filter_update_children (filter, level, elt); goto done; } @@ -1087,7 +1254,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); @@ -1095,34 +1262,45 @@ 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; } } + gtk_tree_model_filter_increment_stamp (filter); + if (!path) path = gtk_real_tree_model_filter_convert_child_path_to_path (filter, c_path, TRUE, TRUE); - g_return_if_fail (path != NULL); + if (!path) + /* parent is probably being filtered out */ + goto done; - gtk_tree_model_filter_increment_stamp (filter); - 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, &childs, c_iter)) - gtk_tree_model_filter_update_childs (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) @@ -1149,7 +1327,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; @@ -1176,22 +1354,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); @@ -1201,7 +1391,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; } @@ -1291,14 +1481,23 @@ 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 childs to parents. */ + /* another iteration to update the references of children to parents. */ for (i = 0; i < level->array->len; i++) { FilterElt *e = &g_array_index (level->array, FilterElt, i); @@ -1323,7 +1522,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); @@ -1345,10 +1549,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; @@ -1360,10 +1574,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 @@ -1374,10 +1603,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); @@ -1391,6 +1620,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; @@ -1404,7 +1636,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); @@ -1421,12 +1653,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])--; } } @@ -1438,7 +1678,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) @@ -1461,6 +1704,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; @@ -1494,10 +1738,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); @@ -1510,35 +1751,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 */ @@ -1548,13 +1795,21 @@ 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); offset = tmp->offset; g_array_remove_index (level->array, i); - for (i = MAX (--i, 0); i < level->array->len; i++) + i--; + for (i = MAX (i, 0); i < level->array->len; i++) { elt = &g_array_index (level->array, FilterElt, i); if (elt->offset > offset) @@ -1564,6 +1819,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); } @@ -1591,9 +1867,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) @@ -1621,8 +1894,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; @@ -1653,6 +1925,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; @@ -1671,7 +1944,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); @@ -1689,8 +1963,11 @@ gtk_tree_model_filter_rows_reordered (GtkTreeModel *c_model, } } - if (level->array->len < 1) - return; + if (!level || level->array->len < 1) + { + gtk_tree_path_free (path); + return; + } /* NOTE: we do not bail out here if level->array->len < 2 like * GtkTreeModelSort does. This because we do some special tricky @@ -1740,8 +2017,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); @@ -1749,10 +2032,18 @@ gtk_tree_model_filter_rows_reordered (GtkTreeModel *c_model, } /* TreeModelIface implementation */ -static guint +static GtkTreeModelFlags gtk_tree_model_filter_get_flags (GtkTreeModel *model) { + GtkTreeModelFlags flags; + g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (model), 0); + g_return_val_if_fail (GTK_TREE_MODEL_FILTER (model)->priv->child_model != NULL, 0); + + flags = gtk_tree_model_get_flags (GTK_TREE_MODEL_FILTER (model)->priv->child_model); + + if ((flags & GTK_TREE_MODEL_LIST_ONLY) == GTK_TREE_MODEL_LIST_ONLY) + return GTK_TREE_MODEL_LIST_ONLY; return 0; } @@ -1799,6 +2090,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, @@ -1807,15 +2157,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); @@ -1827,20 +2177,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; @@ -1848,8 +2197,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; } @@ -1870,10 +2221,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; } @@ -1917,6 +2281,7 @@ static gboolean gtk_tree_model_filter_iter_next (GtkTreeModel *model, GtkTreeIter *iter) { + int i; FilterLevel *level; FilterElt *elt; @@ -1927,15 +2292,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 @@ -1954,35 +2328,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 @@ -2002,18 +2415,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; @@ -2035,34 +2448,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); - /* count visible nodes */ - return filter->priv->root_level_visible; + if (filter->priv->root) + return FILTER_LEVEL (filter->priv->root)->visible_nodes; + + 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); + elt, FALSE); - 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++; - - return count; - } + if (elt->children) + return elt->children->visible_nodes; return 0; } @@ -2073,6 +2481,7 @@ gtk_tree_model_filter_iter_nth_child (GtkTreeModel *model, GtkTreeIter *parent, gint n) { + FilterElt *elt; FilterLevel *level; GtkTreeIter children; @@ -2080,7 +2489,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; @@ -2088,15 +2497,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; } @@ -2155,19 +2569,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--; } } @@ -2218,10 +2629,68 @@ 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++; } } +/* TreeDragSource interface implementation */ +static gboolean +gtk_tree_model_filter_row_draggable (GtkTreeDragSource *drag_source, + GtkTreePath *path) +{ + GtkTreeModelFilter *tree_model_filter = (GtkTreeModelFilter *)drag_source; + GtkTreePath *child_path; + gboolean draggable; + + g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (drag_source), FALSE); + g_return_val_if_fail (path != NULL, FALSE); + + child_path = gtk_tree_model_filter_convert_path_to_child_path (tree_model_filter, path); + draggable = gtk_tree_drag_source_row_draggable (GTK_TREE_DRAG_SOURCE (tree_model_filter->priv->child_model), child_path); + gtk_tree_path_free (child_path); + + return draggable; +} + +static gboolean +gtk_tree_model_filter_drag_data_get (GtkTreeDragSource *drag_source, + GtkTreePath *path, + GtkSelectionData *selection_data) +{ + GtkTreeModelFilter *tree_model_filter = (GtkTreeModelFilter *)drag_source; + GtkTreePath *child_path; + gboolean gotten; + + g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (drag_source), FALSE); + g_return_val_if_fail (path != NULL, FALSE); + + child_path = gtk_tree_model_filter_convert_path_to_child_path (tree_model_filter, path); + gotten = gtk_tree_drag_source_drag_data_get (GTK_TREE_DRAG_SOURCE (tree_model_filter->priv->child_model), child_path, selection_data); + gtk_tree_path_free (child_path); + + return gotten; +} + +static gboolean +gtk_tree_model_filter_drag_data_delete (GtkTreeDragSource *drag_source, + GtkTreePath *path) +{ + GtkTreeModelFilter *tree_model_filter = (GtkTreeModelFilter *)drag_source; + GtkTreePath *child_path; + gboolean deleted; + + g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (drag_source), FALSE); + g_return_val_if_fail (path != NULL, FALSE); + + child_path = gtk_tree_model_filter_convert_path_to_child_path (tree_model_filter, path); + deleted = gtk_tree_drag_source_drag_data_delete (GTK_TREE_DRAG_SOURCE (tree_model_filter->priv->child_model), child_path); + gtk_tree_path_free (child_path); + + return deleted; +} + /* bits and pieces */ static void gtk_tree_model_filter_set_model (GtkTreeModelFilter *filter, @@ -2231,15 +2700,15 @@ gtk_tree_model_filter_set_model (GtkTreeModelFilter *filter, if (filter->priv->child_model) { - g_signal_handler_disconnect (G_OBJECT (filter->priv->child_model), + g_signal_handler_disconnect (filter->priv->child_model, filter->priv->changed_id); - g_signal_handler_disconnect (G_OBJECT (filter->priv->child_model), + g_signal_handler_disconnect (filter->priv->child_model, filter->priv->inserted_id); - g_signal_handler_disconnect (G_OBJECT (filter->priv->child_model), + g_signal_handler_disconnect (filter->priv->child_model, filter->priv->has_child_toggled_id); - g_signal_handler_disconnect (G_OBJECT (filter->priv->child_model), + g_signal_handler_disconnect (filter->priv->child_model, filter->priv->deleted_id); - g_signal_handler_disconnect (G_OBJECT (filter->priv->child_model), + g_signal_handler_disconnect (filter->priv->child_model, filter->priv->reordered_id); /* reset our state */ @@ -2247,16 +2716,17 @@ gtk_tree_model_filter_set_model (GtkTreeModelFilter *filter, gtk_tree_model_filter_free_level (filter, filter->priv->root); filter->priv->root = NULL; - g_object_unref (G_OBJECT (filter->priv->child_model)); + 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; if (child_model) { - g_object_ref (G_OBJECT (filter->priv->child_model)); + g_object_ref (filter->priv->child_model); filter->priv->changed_id = g_signal_connect (child_model, "row_changed", G_CALLBACK (gtk_tree_model_filter_row_changed), @@ -2283,6 +2753,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) @@ -2314,14 +2826,21 @@ 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); - retval = GTK_TREE_MODEL (g_object_new (gtk_tree_model_filter_get_type (), NULL)); + retval = g_object_new (GTK_TYPE_TREE_MODEL_FILTER, + "child-model", child_model, + "virtual-root", root, + NULL); - gtk_tree_model_filter_set_model (GTK_TREE_MODEL_FILTER (retval), - child_model); - gtk_tree_model_filter_set_root (GTK_TREE_MODEL_FILTER (retval), root); + 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; } @@ -2355,6 +2874,11 @@ gtk_tree_model_filter_get_model (GtkTreeModelFilter *filter) * function should return %TRUE if the given row should be visible and * %FALSE otherwise. * + * If the condition calculated by the function changes over time (e.g. because + * it depends on some global parameters), you must call + * gtk_tree_model_filter_refilter() to keep the visibility information of + * the model uptodate. + * * Since: 2.4 */ void @@ -2387,12 +2911,17 @@ gtk_tree_model_filter_set_visible_func (GtkTreeModelFilter *filter, * @filter: A #GtkTreeModelFilter. * @n_columns: The number of columns in the filter model. * @types: The #GTypes of the columns. - * @func: A #GtkTreeModelFilterModifyFunc, or %NULL. + * @func: A #GtkTreeModelFilterModifyFunc * @data: User data to pass to the modify function, or %NULL. * @destroy: Destroy notifier of @data, or %NULL. * - * Sets the @filter to have @n_columns columns with @types. If @func - * is not %NULL, it will set @func to be the modify function of @filter. + * With the @n_columns and @types parameters, you give an array of column + * types for this model (which will be exposed to the parent model/view). + * The @func, @data and @destroy parameters are for specifying the modify + * function. The modify function will get called for each + * data access, the goal of the modify function is to return the data which + * should be displayed at the location specified using the parameters of the + * modify function. * * Since: 2.4 */ @@ -2460,34 +2989,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; } /** @@ -2527,11 +3065,12 @@ 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, gboolean build_levels, - gboolean fetch_childs) + gboolean fetch_children) { gint *child_indices; GtkTreePath *retval; @@ -2557,7 +3096,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++) @@ -2577,12 +3116,12 @@ 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; } - if (!found_child && fetch_childs) + if (!found_child && fetch_children) { tmp = gtk_tree_model_filter_fetch_child (filter, level, child_indices[i], @@ -2599,11 +3138,11 @@ 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; } - else if (!found_child && !fetch_childs) + else if (!found_child && !fetch_children) { /* no path */ gtk_tree_path_free (real_path); @@ -2624,7 +3163,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. * @@ -2634,11 +3174,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; } /** @@ -2672,30 +3230,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 */ @@ -2755,7 +3316,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 */ @@ -2764,7 +3325,10 @@ 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)); } + +#define __GTK_TREE_MODEL_FILTER_C__ +#include "gtkaliasdef.c"