aboutsummaryrefslogtreecommitdiffstats
path: root/lib/widgets/eggtreemodelfilter.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/widgets/eggtreemodelfilter.c')
-rw-r--r--lib/widgets/eggtreemodelfilter.c2560
1 files changed, 2560 insertions, 0 deletions
diff --git a/lib/widgets/eggtreemodelfilter.c b/lib/widgets/eggtreemodelfilter.c
new file mode 100644
index 000000000..b3b31a46d
--- /dev/null
+++ b/lib/widgets/eggtreemodelfilter.c
@@ -0,0 +1,2560 @@
+/* eggtreemodelfilter.c
+ * Copyright (C) 2000,2001 Red Hat, Inc., Jonathan Blandford <jrb@redhat.com>
+ * Copyright (C) 2001,2002 Kristian Rietveld <kris@gtk.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "eggtreemodelfilter.h"
+#include <gtk/gtksignal.h>
+#include <string.h>
+
+/****** NOTE NOTE NOTE WARNING WARNING ******
+ *
+ * This is *unstable* code. Don't use it in any project. This warning
+ * will be removed when this treemodel works.
+ */
+
+/*#define VERBOSE 1*/
+
+/* removed this when you add support for i18n */
+#define _
+
+/* ITER FORMAT:
+ *
+ * iter->stamp = filter->stamp
+ * iter->user_data = FilterLevel
+ * iter->user_data2 = FilterElt
+ */
+
+/* all paths, iters, etc prefixed with c_ are paths, iters, etc relative to the
+ * child model.
+ */
+
+typedef struct _FilterElt FilterElt;
+typedef struct _FilterLevel FilterLevel;
+
+struct _FilterElt
+{
+ GtkTreeIter iter;
+ FilterLevel *children;
+ gint offset;
+ gint ref_count;
+ gint zero_ref_count;
+ gboolean visible;
+};
+
+struct _FilterLevel
+{
+ GArray *array;
+ gint ref_count;
+
+ FilterElt *parent_elt;
+ FilterLevel *parent_level;
+};
+
+/* properties */
+enum
+{
+ PROP_0,
+ PROP_CHILD_MODEL,
+ PROP_VIRTUAL_ROOT
+};
+
+#define EGG_TREE_MODEL_FILTER_CACHE_CHILD_ITERS(filter) \
+ (((EggTreeModelFilter *)filter)->child_flags & GTK_TREE_MODEL_ITERS_PERSIST)
+
+#define FILTER_ELT(filter_elt) ((FilterElt *)filter_elt)
+#define FILTER_LEVEL(filter_level) ((FilterLevel *)filter_level)
+
+/* general code (object/interface init, properties, etc) */
+static void egg_tree_model_filter_init (EggTreeModelFilter *filter);
+static void egg_tree_model_filter_class_init (EggTreeModelFilterClass *filter_class);
+static void egg_tree_model_filter_tree_model_init (GtkTreeModelIface *iface);
+static void egg_tree_model_filter_finalize (GObject *object);
+static void egg_tree_model_filter_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void egg_tree_model_filter_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+/* signal handlers */
+static void egg_tree_model_filter_row_changed (GtkTreeModel *c_model,
+ GtkTreePath *c_path,
+ GtkTreeIter *c_iter,
+ gpointer data);
+static void egg_tree_model_filter_row_inserted (GtkTreeModel *c_model,
+ GtkTreePath *c_path,
+ GtkTreeIter *c_iter,
+ gpointer data);
+static void egg_tree_model_filter_row_has_child_toggled (GtkTreeModel *c_model,
+ GtkTreePath *c_path,
+ GtkTreeIter *c_iter,
+ gpointer data);
+static void egg_tree_model_filter_row_deleted (GtkTreeModel *c_model,
+ GtkTreePath *c_path,
+ gpointer data);
+static void egg_tree_model_filter_rows_reordered (GtkTreeModel *c_model,
+ GtkTreePath *c_path,
+ GtkTreeIter *c_iter,
+ gint *new_order,
+ gpointer data);
+
+/* GtkTreeModel interface */
+static guint egg_tree_model_filter_get_flags (GtkTreeModel *model);
+static gint egg_tree_model_filter_get_n_columns (GtkTreeModel *model);
+static GType egg_tree_model_filter_get_column_type (GtkTreeModel *model,
+ gint index);
+static gboolean egg_tree_model_filter_get_iter (GtkTreeModel *model,
+ GtkTreeIter *iter,
+ GtkTreePath *path);
+static GtkTreePath *egg_tree_model_filter_get_path (GtkTreeModel *model,
+ GtkTreeIter *iter);
+static void egg_tree_model_filter_get_value (GtkTreeModel *model,
+ GtkTreeIter *iter,
+ gint column,
+ GValue *value);
+static gboolean egg_tree_model_filter_iter_next (GtkTreeModel *model,
+ GtkTreeIter *iter);
+static gboolean egg_tree_model_filter_iter_children (GtkTreeModel *model,
+ GtkTreeIter *iter,
+ GtkTreeIter *parent);
+static gboolean egg_tree_model_filter_iter_has_child (GtkTreeModel *model,
+ GtkTreeIter *iter);
+static gint egg_tree_model_filter_iter_n_children (GtkTreeModel *model,
+ GtkTreeIter *iter);
+static gboolean egg_tree_model_filter_iter_nth_child (GtkTreeModel *model,
+ GtkTreeIter *iter,
+ GtkTreeIter *parent,
+ gint n);
+static gboolean egg_tree_model_filter_iter_parent (GtkTreeModel *model,
+ GtkTreeIter *iter,
+ GtkTreeIter *child);
+static void egg_tree_model_filter_ref_node (GtkTreeModel *model,
+ GtkTreeIter *iter);
+static void egg_tree_model_filter_unref_node (GtkTreeModel *model,
+ GtkTreeIter *iter);
+
+
+
+/* private functions */
+static void egg_tree_model_filter_build_level (EggTreeModelFilter *filter,
+ FilterLevel *parent_level,
+ FilterElt *parent_elt);
+static void egg_tree_model_filter_free_level (EggTreeModelFilter *filter,
+ FilterLevel *filter_level);
+
+static GtkTreePath *egg_tree_model_filter_elt_get_path (FilterLevel *level,
+ FilterElt *elt,
+ GtkTreePath *root);
+
+static GtkTreePath *egg_tree_model_filter_add_root (GtkTreePath *src,
+ GtkTreePath *root);
+static GtkTreePath *egg_tree_model_filter_remove_root (GtkTreePath *src,
+ GtkTreePath *root);
+
+static void egg_tree_model_filter_increment_stamp (EggTreeModelFilter *filter);
+
+static gboolean egg_tree_model_filter_visible (EggTreeModelFilter *filter,
+ GtkTreeIter *child_iter);
+static void egg_tree_model_filter_clear_cache_helper (EggTreeModelFilter *filter,
+ FilterLevel *level);
+
+static void egg_tree_model_filter_real_unref_node (GtkTreeModel *model,
+ GtkTreeIter *iter,
+ gboolean propagate_unref);
+
+static void egg_tree_model_filter_set_model (EggTreeModelFilter *filter,
+ GtkTreeModel *child_model);
+static void egg_tree_model_filter_set_root (EggTreeModelFilter *filter,
+ GtkTreePath *root);
+
+static GtkTreePath *egg_real_tree_model_filter_convert_child_path_to_path (EggTreeModelFilter *filter,
+ GtkTreePath *child_path,
+ gboolean build_levels,
+ gboolean fetch_childs);
+
+static gboolean egg_tree_model_filter_fetch_child (EggTreeModelFilter *filter,
+ FilterLevel *level,
+ gint offset);
+static void egg_tree_model_filter_remove_node (EggTreeModelFilter *filter,
+ GtkTreeIter *iter,
+ gboolean emit_signal);
+static void egg_tree_model_filter_update_childs (EggTreeModelFilter *filter,
+ FilterLevel *level,
+ FilterElt *elt);
+
+
+static GObjectClass *parent_class = NULL;
+
+GType
+egg_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 (EggTreeModelFilterClass),
+ NULL, /* base_init */
+ NULL, /* base_finalize */
+ (GClassInitFunc) egg_tree_model_filter_class_init,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof (EggTreeModelFilter),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) egg_tree_model_filter_init
+ };
+
+ static const GInterfaceInfo tree_model_info =
+ {
+ (GInterfaceInitFunc) egg_tree_model_filter_tree_model_init,
+ NULL,
+ NULL
+ };
+
+ tree_model_filter_type = g_type_register_static (G_TYPE_OBJECT,
+ "EggTreeModelFilter",
+ &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;
+}
+
+static void
+egg_tree_model_filter_init (EggTreeModelFilter *filter)
+{
+ filter->visible_column = -1;
+ filter->zero_ref_count = 0;
+ filter->visible_method_set = FALSE;
+ filter->modify_func_set = FALSE;
+}
+
+static void
+egg_tree_model_filter_class_init (EggTreeModelFilterClass *filter_class)
+{
+ GObjectClass *object_class;
+
+ object_class = (GObjectClass *) filter_class;
+ parent_class = g_type_class_peek_parent (filter_class);
+
+ object_class->set_property = egg_tree_model_filter_set_property;
+ object_class->get_property = egg_tree_model_filter_get_property;
+
+ object_class->finalize = egg_tree_model_filter_finalize;
+
+ /* Properties -- FIXME: write a better description ... */
+ g_object_class_install_property (object_class,
+ PROP_CHILD_MODEL,
+ g_param_spec_object ("child_model",
+ "The child model",
+ "The model for the TreeModelFilter to filter",
+ GTK_TYPE_TREE_MODEL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+ g_object_class_install_property (object_class,
+ PROP_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));
+}
+
+static void
+egg_tree_model_filter_tree_model_init (GtkTreeModelIface *iface)
+{
+ iface->get_flags = egg_tree_model_filter_get_flags;
+ iface->get_n_columns = egg_tree_model_filter_get_n_columns;
+ iface->get_column_type = egg_tree_model_filter_get_column_type;
+ iface->get_iter = egg_tree_model_filter_get_iter;
+ iface->get_path = egg_tree_model_filter_get_path;
+ iface->get_value = egg_tree_model_filter_get_value;
+ iface->iter_next = egg_tree_model_filter_iter_next;
+ iface->iter_children = egg_tree_model_filter_iter_children;
+ iface->iter_has_child = egg_tree_model_filter_iter_has_child;
+ iface->iter_n_children = egg_tree_model_filter_iter_n_children;
+ iface->iter_nth_child = egg_tree_model_filter_iter_nth_child;
+ iface->iter_parent = egg_tree_model_filter_iter_parent;
+ iface->ref_node = egg_tree_model_filter_ref_node;
+ iface->unref_node = egg_tree_model_filter_unref_node;
+}
+
+
+static void
+egg_tree_model_filter_finalize (GObject *object)
+{
+ EggTreeModelFilter *filter = (EggTreeModelFilter *) object;
+
+ egg_tree_model_filter_set_model (filter, NULL);
+
+ if (filter->virtual_root)
+ gtk_tree_path_free (filter->virtual_root);
+
+ if (filter->root)
+ egg_tree_model_filter_free_level (filter, filter->root);
+
+ if (filter->modify_types)
+ g_free (filter->modify_types);
+
+ /* must chain up */
+ parent_class->finalize (object);
+}
+
+static void
+egg_tree_model_filter_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ EggTreeModelFilter *filter = EGG_TREE_MODEL_FILTER (object);
+
+ switch (prop_id)
+ {
+ case PROP_CHILD_MODEL:
+ egg_tree_model_filter_set_model (filter, g_value_get_object (value));
+ break;
+ case PROP_VIRTUAL_ROOT:
+ egg_tree_model_filter_set_root (filter, g_value_get_boxed (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+egg_tree_model_filter_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ EggTreeModelFilter *filter = EGG_TREE_MODEL_FILTER (object);
+
+ switch (prop_id)
+ {
+ case PROP_CHILD_MODEL:
+ g_value_set_object (value, filter->child_model);
+ break;
+ case PROP_VIRTUAL_ROOT:
+ g_value_set_boxed (value, filter->virtual_root);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+/* helpers */
+
+static void
+egg_tree_model_filter_build_level (EggTreeModelFilter *filter,
+ FilterLevel *parent_level,
+ FilterElt *parent_elt)
+{
+ GtkTreeIter iter;
+ GtkTreeIter root;
+ FilterLevel *new_level;
+ gint length = 0;
+ gint i;
+
+ g_assert (filter->child_model != NULL);
+
+ if (!parent_level)
+ {
+ if (filter->virtual_root)
+ {
+ if (gtk_tree_model_get_iter (filter->child_model, &root, filter->virtual_root) == FALSE)
+ return;
+ length = gtk_tree_model_iter_n_children (filter->child_model, &root);
+
+#ifdef VERBOSE
+ g_print ("-- vroot %d children\n", length);
+#endif
+
+ if (gtk_tree_model_iter_children (filter->child_model, &iter, &root) == FALSE)
+ return;
+ }
+ else
+ {
+ if (!gtk_tree_model_get_iter_first (filter->child_model, &iter))
+ return;
+ length = gtk_tree_model_iter_n_children (filter->child_model, NULL);
+ }
+ }
+ else
+ {
+ GtkTreeIter parent_iter;
+ GtkTreeIter child_parent_iter;
+
+ parent_iter.stamp = filter->stamp;
+ parent_iter.user_data = parent_level;
+ parent_iter.user_data2 = parent_elt;
+
+ egg_tree_model_filter_convert_iter_to_child_iter (filter,
+ &child_parent_iter,
+ &parent_iter);
+ if (gtk_tree_model_iter_children (filter->child_model, &iter, &child_parent_iter) == FALSE)
+ return;
+
+ /* stamp may have changed */
+ egg_tree_model_filter_convert_iter_to_child_iter (filter,
+ &child_parent_iter,
+ &parent_iter);
+ length = gtk_tree_model_iter_n_children (filter->child_model, &child_parent_iter);
+ }
+
+ g_return_if_fail (length > 0);
+
+#ifdef VERBOSE
+ g_print ("-- building new level with %d childs\n", length);
+#endif
+
+ new_level = g_new (FilterLevel, 1);
+ new_level->array = g_array_sized_new (FALSE, FALSE,
+ sizeof (FilterElt),
+ length);
+ new_level->ref_count = 0;
+ new_level->parent_elt = parent_elt;
+ new_level->parent_level = parent_level;
+
+ if (parent_elt)
+ parent_elt->children = new_level;
+ else
+ filter->root = new_level;
+
+ /* increase the count of zero ref_counts */
+ while (parent_level)
+ {
+ parent_elt->zero_ref_count++;
+
+ parent_elt = parent_level->parent_elt;
+ parent_level = parent_level->parent_level;
+ }
+ filter->zero_ref_count++;
+
+#ifdef VERBOSE
+ g_print ("_build_level: zero ref count on filter is now %d\n",
+ filter->zero_ref_count);
+#endif
+
+ i = 0;
+ do
+ {
+ if (egg_tree_model_filter_visible (filter, &iter))
+ {
+ FilterElt filter_elt;
+
+ filter_elt.offset = i;
+ filter_elt.zero_ref_count = 0;
+ filter_elt.ref_count = 0;
+ filter_elt.children = NULL;
+ filter_elt.visible = TRUE;
+
+ if (EGG_TREE_MODEL_FILTER_CACHE_CHILD_ITERS (filter))
+ filter_elt.iter = iter;
+
+ g_array_append_val (new_level->array, filter_elt);
+ }
+ i++;
+ }
+ while (gtk_tree_model_iter_next (filter->child_model, &iter));
+}
+
+static void
+egg_tree_model_filter_free_level (EggTreeModelFilter *filter,
+ FilterLevel *filter_level)
+{
+ gint i;
+
+ g_assert (filter_level);
+
+ if (filter_level->ref_count == 0)
+ {
+ FilterLevel *parent_level = filter_level->parent_level;
+ FilterElt *parent_elt = filter_level->parent_elt;
+
+ do
+ {
+ if (parent_elt)
+ parent_elt->zero_ref_count--;
+
+ if (parent_level)
+ {
+ parent_elt = parent_level->parent_elt;
+ parent_level = parent_level->parent_level;
+ }
+ }
+ while (parent_level);
+ filter->zero_ref_count--;
+ }
+
+#ifdef VERBOSE
+ g_print ("freeing level\n");
+ g_print ("zero ref count is %d\n", filter->zero_ref_count);
+#endif
+
+ for (i = 0; i < filter_level->array->len; i++)
+ {
+ if (g_array_index (filter_level->array, FilterElt, i).children)
+ egg_tree_model_filter_free_level (filter,
+ FILTER_LEVEL (g_array_index (filter_level->array, FilterElt, i).children));
+ }
+
+ if (filter_level->parent_elt)
+ filter_level->parent_elt->children = NULL;
+ else
+ filter->root = NULL;
+
+ g_array_free (filter_level->array, TRUE);
+ filter_level->array = NULL;
+
+ g_free (filter_level);
+ filter_level = NULL;
+}
+
+static GtkTreePath *
+egg_tree_model_filter_elt_get_path (FilterLevel *level,
+ FilterElt *elt,
+ GtkTreePath *root)
+{
+ FilterLevel *walker = level;
+ FilterElt *walker2 = elt;
+ GtkTreePath *path;
+ GtkTreePath *real_path;
+
+ g_return_val_if_fail (level != NULL, NULL);
+ g_return_val_if_fail (elt != NULL, NULL);
+
+ path = gtk_tree_path_new ();
+
+ while (walker)
+ {
+ gtk_tree_path_prepend_index (path, walker2->offset);
+
+ walker2 = walker->parent_elt;
+ walker = walker->parent_level;
+ }
+
+ if (root)
+ {
+ real_path = gtk_tree_path_copy (root);
+
+ egg_tree_model_filter_add_root (real_path, path);
+ gtk_tree_path_free (path);
+ return real_path;
+ }
+
+ return path;
+}
+
+static GtkTreePath *
+egg_tree_model_filter_add_root (GtkTreePath *src,
+ GtkTreePath *root)
+{
+ GtkTreePath *retval;
+ gint i;
+
+ retval = gtk_tree_path_copy (root);
+
+ for (i = 0; i < gtk_tree_path_get_depth (src); i++)
+ gtk_tree_path_append_index (retval, gtk_tree_path_get_indices (src)[i]);
+
+ return retval;
+}
+
+static GtkTreePath *
+egg_tree_model_filter_remove_root (GtkTreePath *src,
+ GtkTreePath *root)
+{
+ GtkTreePath *retval;
+ gint i;
+ gint depth;
+ gint *indices;
+
+ if (gtk_tree_path_get_depth (src) <= gtk_tree_path_get_depth (root))
+ return NULL;
+
+ depth = gtk_tree_path_get_depth (src);
+ indices = gtk_tree_path_get_indices (src);
+
+ for (i = 0; i < gtk_tree_path_get_depth (root); i++)
+ if (indices[i] != gtk_tree_path_get_indices (root)[i])
+ return NULL;
+
+ retval = gtk_tree_path_new ();
+
+ for (; i < depth; i++)
+ gtk_tree_path_append_index (retval, indices[i]);
+
+ return retval;
+}
+
+static void
+egg_tree_model_filter_increment_stamp (EggTreeModelFilter *filter)
+{
+ do
+ {
+ filter->stamp++;
+ }
+ while (filter->stamp == 0);
+
+ egg_tree_model_filter_clear_cache (filter);
+}
+
+static gboolean
+egg_tree_model_filter_visible (EggTreeModelFilter *filter,
+ GtkTreeIter *child_iter)
+{
+ if (filter->visible_func)
+ {
+ return (filter->visible_func (filter->child_model,
+ child_iter,
+ filter->visible_data));
+ }
+ else if (filter->visible_column >= 0)
+ {
+ GValue val = {0, };
+
+ gtk_tree_model_get_value (filter->child_model, child_iter,
+ filter->visible_column, &val);
+
+ if (g_value_get_boolean (&val))
+ {
+ g_value_unset (&val);
+ return TRUE;
+ }
+
+ g_value_unset (&val);
+ return FALSE;
+ }
+
+ /* no filter thing set, so always visible */
+ return TRUE;
+}
+
+static void
+egg_tree_model_filter_clear_cache_helper (EggTreeModelFilter *filter,
+ FilterLevel *level)
+{
+ gint i;
+
+ g_assert (level);
+
+ for (i = 0; i < level->array->len; i++)
+ {
+ if (g_array_index (level->array, FilterElt, i).zero_ref_count > 0)
+ egg_tree_model_filter_clear_cache_helper (filter, g_array_index (level->array, FilterElt, i).children);
+ }
+
+ if (level->ref_count == 0 && level != filter->root)
+ {
+ egg_tree_model_filter_free_level (filter, level);
+ return;
+ }
+}
+
+static gboolean
+egg_tree_model_filter_fetch_child (EggTreeModelFilter *filter,
+ FilterLevel *level,
+ gint offset)
+{
+ gint i = 0;
+ gint len;
+ GtkTreePath *c_path = NULL;
+ GtkTreeIter c_iter;
+ GtkTreePath *c_parent_path = NULL;
+ GtkTreeIter c_parent_iter;
+ FilterElt elt;
+
+#ifdef VERBOSE
+ g_print ("_fetch_child: for offset %d\n", offset);
+#endif
+
+ /* check if child exists and is visible */
+ if (level->parent_elt)
+ {
+ c_parent_path =
+ egg_tree_model_filter_elt_get_path (level->parent_level,
+ level->parent_elt,
+ filter->virtual_root);
+ if (!c_parent_path)
+ return FALSE;
+ }
+ else
+ {
+ if (filter->virtual_root)
+ c_parent_path = gtk_tree_path_copy (filter->virtual_root);
+ else
+ c_parent_path = NULL;
+ }
+
+ if (c_parent_path)
+ {
+ gtk_tree_model_get_iter (filter->child_model,
+ &c_parent_iter,
+ c_parent_path);
+ len = gtk_tree_model_iter_n_children (filter->child_model,
+ &c_parent_iter);
+
+ c_path = gtk_tree_path_copy (c_parent_path);
+ gtk_tree_path_free (c_parent_path);
+ }
+ else
+ {
+ len = gtk_tree_model_iter_n_children (filter->child_model, NULL);
+ c_path = gtk_tree_path_new ();
+ }
+
+ gtk_tree_path_append_index (c_path, offset);
+ gtk_tree_model_get_iter (filter->child_model, &c_iter, c_path);
+ gtk_tree_path_free (c_path);
+
+ if (offset >= len || !egg_tree_model_filter_visible (filter, &c_iter))
+ return FALSE;
+
+ /* add child */
+ elt.offset = offset;
+ elt.zero_ref_count = 0;
+ elt.ref_count = 0;
+ elt.children = NULL;
+ /* visibility should be FALSE as we don't emit row_inserted */
+ elt.visible = FALSE;
+
+ if (EGG_TREE_MODEL_FILTER_CACHE_CHILD_ITERS (filter))
+ elt.iter = c_iter;
+
+ /* find index */
+ for (i = 0; i < level->array->len; i++)
+ if (g_array_index (level->array, FilterElt, i).offset > offset)
+ break;
+
+ g_array_insert_val (level->array, i, elt);
+
+ 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;
+ }
+
+ return TRUE;
+}
+
+static void
+egg_tree_model_filter_remove_node (EggTreeModelFilter *filter,
+ GtkTreeIter *iter,
+ gboolean emit_signal)
+{
+ FilterElt *elt, *parent;
+ FilterLevel *level, *parent_level;
+ gint offset, i, length, level_refcount;
+
+ /* FIXME: this function is very ugly. I need to rethink and
+ * rewrite it someday.
+ */
+
+ 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;
+
+#ifdef VERBOSE
+ g_print ("|___ removing node\n");
+#endif
+
+ /* ref counting */
+ while (elt->ref_count > 0)
+ egg_tree_model_filter_real_unref_node (GTK_TREE_MODEL (filter),
+ iter, FALSE);
+
+ level_refcount = level->ref_count;
+
+ /* do the ref counting first! this touches the stamp */
+ if (emit_signal)
+ {
+ GtkTreePath *path;
+
+ path = gtk_tree_model_get_path (GTK_TREE_MODEL (filter), iter);
+ egg_tree_model_filter_increment_stamp (filter);
+ 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->root)
+ {
+ /* above code destroyed the level */
+ goto emit_has_child_toggled;
+ }
+
+ if (length == 1)
+ {
+ /* kill the level */
+#ifdef VERBOSE
+ g_print ("killing level ...\n");
+#endif
+ egg_tree_model_filter_free_level (filter, level);
+
+ if (!filter->root)
+ /* we killed the root */
+ return;
+ }
+ else
+ {
+#ifdef VERBOSE
+ g_print ("removing the node...\n");
+#endif
+
+ /* remove the node */
+ for (i = 0; i < level->array->len; i++)
+ if (elt->offset == g_array_index (level->array, FilterElt, i).offset)
+ break;
+
+ g_array_remove_index (level->array, i);
+
+ for (i = 0; i < level->array->len; i++)
+ {
+ /* NOTE: here we do *not* decrease offsets, because the node was
+ * not removed from the child model
+ */
+ elt = &g_array_index (level->array, FilterElt, i);
+ if (elt->children)
+ elt->children->parent_elt = elt;
+ }
+ }
+
+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->root))
+ {
+ /* latest child has been removed, level has been destroyed */
+ GtkTreeIter piter;
+ GtkTreePath *ppath;
+
+ piter.stamp = filter->stamp;
+ piter.user_data = parent_level;
+ piter.user_data2 = parent;
+
+ ppath = gtk_tree_model_get_path (GTK_TREE_MODEL (filter), &piter);
+
+#ifdef VERBOSE
+ g_print ("emitting has_child_toggled (by _filter_remove)\n");
+#endif
+
+ gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (filter),
+ ppath, &piter);
+ gtk_tree_path_free (ppath);
+ }
+}
+
+static void
+egg_tree_model_filter_update_childs (EggTreeModelFilter *filter,
+ FilterLevel *level,
+ FilterElt *elt)
+{
+ GtkTreeIter c_iter;
+ GtkTreeIter iter;
+
+#ifdef VERBOSE
+ g_print ("~~ a node came back, search childs\n");
+#endif
+
+ if (!elt->visible)
+ {
+#ifdef VERBOSE
+ g_print (" + given elt not visible -- bailing out\n");
+#endif
+ return;
+ }
+
+ iter.stamp = filter->stamp;
+ iter.user_data = level;
+ iter.user_data2 = elt;
+
+ egg_tree_model_filter_convert_iter_to_child_iter (filter, &c_iter, &iter);
+
+ if (gtk_tree_model_iter_has_child (filter->child_model, &c_iter))
+ {
+ GtkTreePath *path = gtk_tree_model_get_path (GTK_TREE_MODEL (filter),
+ &iter);
+ gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (filter),
+ path,
+ &iter);
+ if (path)
+ gtk_tree_path_free (path);
+ }
+}
+
+/* TreeModel signals */
+static void
+egg_tree_model_filter_row_changed (GtkTreeModel *c_model,
+ GtkTreePath *c_path,
+ GtkTreeIter *c_iter,
+ gpointer data)
+{
+ EggTreeModelFilter *filter = EGG_TREE_MODEL_FILTER (data);
+ GtkTreeIter iter;
+ GtkTreeIter real_c_iter;
+ GtkTreePath *path;
+
+ FilterElt *elt;
+ FilterLevel *level;
+ gint offset;
+
+ gboolean new;
+ gboolean free_c_path = FALSE;
+
+ g_return_if_fail (c_path != NULL || c_iter != NULL);
+
+ if (!c_path)
+ {
+ c_path = gtk_tree_model_get_path (c_model, c_iter);
+ free_c_path = TRUE;
+ }
+
+ if (c_iter)
+ real_c_iter = *c_iter;
+ else
+ gtk_tree_model_get_iter (c_model, &real_c_iter, c_path);
+
+ if (!filter->root)
+ {
+ gint i;
+ FilterLevel *root;
+
+ /* build root level */
+ egg_tree_model_filter_build_level (filter, NULL, NULL);
+
+ root = FILTER_LEVEL (filter->root);
+
+ /* FIXME:
+ * we set the visibilities to FALSE here, so we ever emit
+ * a row_inserted. maybe it's even better to emit row_inserted
+ * here, not sure.
+ */
+ if (root)
+ for (i = 0; i < root->array->len; i++)
+ g_array_index (root->array, FilterElt, i).visible = FALSE;
+ }
+
+ path = egg_real_tree_model_filter_convert_child_path_to_path (filter,
+ c_path,
+ FALSE,
+ TRUE);
+ if (!path)
+ goto done;
+
+ gtk_tree_model_get_iter (GTK_TREE_MODEL (filter), &iter, path);
+
+ level = FILTER_LEVEL (iter.user_data);
+ elt = FILTER_ELT (iter.user_data2);
+ offset = elt->offset;
+ new = egg_tree_model_filter_visible (filter, c_iter);
+
+ if (elt->visible == TRUE && new == FALSE)
+ {
+#ifdef VERBOSE
+ g_print ("visible to false -> delete row\n");
+#endif
+ egg_tree_model_filter_remove_node (filter, &iter, TRUE);
+ }
+ else if (elt->visible == FALSE && new == TRUE)
+ {
+ GtkTreeIter childs;
+
+#ifdef VERBOSE
+ g_print ("visible to true -> insert row\n");
+#endif
+
+ elt->visible = TRUE;
+
+ egg_tree_model_filter_increment_stamp (filter);
+ /* update stamp */
+ gtk_tree_model_get_iter (GTK_TREE_MODEL (filter), &iter, path);
+ gtk_tree_model_row_inserted (GTK_TREE_MODEL (filter), path, &iter);
+
+ if (gtk_tree_model_iter_children (c_model, &childs, c_iter))
+ egg_tree_model_filter_update_childs (filter, level, elt);
+ }
+ else if (elt->visible == FALSE && new == FALSE)
+ {
+#ifdef VERBOSE
+ g_print ("remove node in silence\n");
+#endif
+ egg_tree_model_filter_remove_node (filter, &iter, FALSE);
+ }
+ else
+ {
+ GtkTreeIter childs;
+
+#ifdef VERBOSE
+ g_print ("no change in visibility -- pass row_changed\n");
+#endif
+
+ gtk_tree_model_row_changed (GTK_TREE_MODEL (filter), path, &iter);
+
+ if (gtk_tree_model_iter_children (c_model, &childs, c_iter) &&
+ elt->visible)
+ egg_tree_model_filter_update_childs (filter, level, elt);
+ }
+
+ gtk_tree_path_free (path);
+
+done:
+ if (free_c_path)
+ gtk_tree_path_free (c_path);
+}
+
+static void
+egg_tree_model_filter_row_inserted (GtkTreeModel *c_model,
+ GtkTreePath *c_path,
+ GtkTreeIter *c_iter,
+ gpointer data)
+{
+ EggTreeModelFilter *filter = EGG_TREE_MODEL_FILTER (data);
+ GtkTreePath *path;
+ GtkTreePath *real_path;
+ GtkTreeIter iter;
+
+ GtkTreeIter real_c_iter;
+
+ FilterElt *elt;
+ FilterLevel *level;
+ FilterLevel *parent_level;
+
+ gint i = 0, offset, index = -1;
+
+ gboolean free_c_path = FALSE;
+
+ g_return_if_fail (c_path != NULL || c_iter != NULL);
+
+ if (!c_path)
+ {
+ c_path = gtk_tree_model_get_path (c_model, c_iter);
+ free_c_path = TRUE;
+ }
+
+ if (c_iter)
+ real_c_iter = *c_iter;
+ else
+ gtk_tree_model_get_iter (c_model, &real_c_iter, c_path);
+
+ /* the row has already been inserted. so we need to fixup the
+ * virtual root here first
+ */
+ if (filter->virtual_root)
+ {
+ if (gtk_tree_path_get_depth (filter->virtual_root) >=
+ gtk_tree_path_get_depth (c_path))
+ {
+ gint level;
+ gint *v_indices, *c_indices;
+
+ level = gtk_tree_path_get_depth (c_path) - 1;
+ v_indices = gtk_tree_path_get_indices (filter->virtual_root);
+ c_indices = gtk_tree_path_get_indices (c_path);
+
+ if (v_indices[level] >= c_indices[level])
+ (v_indices[level])++;
+ }
+ }
+
+ if (!filter->root)
+ {
+ egg_tree_model_filter_build_level (filter, NULL, NULL);
+ /* that already put the inserted iter in the level */
+
+ goto done_and_emit;
+ }
+
+ parent_level = level = FILTER_LEVEL (filter->root);
+
+ /* subtract virtual root if necessary */
+ if (filter->virtual_root)
+ {
+ real_path = egg_tree_model_filter_remove_root (c_path,
+ filter->virtual_root);
+ /* not our kiddo */
+ if (!real_path)
+ goto done;
+ }
+ else
+ real_path = gtk_tree_path_copy (c_path);
+
+ if (gtk_tree_path_get_depth (real_path) - 1 >= 1)
+ {
+ /* find the parent level */
+ while (i < gtk_tree_path_get_depth (real_path) - 1)
+ {
+ gint j;
+
+ if (!level)
+ /* we don't cover this signal */
+ goto done;
+
+ elt = NULL;
+ for (j = 0; j < level->array->len; j++)
+ if (g_array_index (level->array, FilterElt, j).offset ==
+ gtk_tree_path_get_indices (real_path)[i])
+ {
+ elt = &g_array_index (level->array, FilterElt, j);
+ break;
+ }
+
+ if (!elt)
+ /* parent is probably being filtered out */
+ goto done;
+
+ if (!elt->children)
+ {
+ GtkTreePath *tmppath;
+ GtkTreeIter tmpiter;
+
+ tmpiter.stamp = filter->stamp;
+ tmpiter.user_data = level;
+ tmpiter.user_data2 = elt;
+
+ tmppath = gtk_tree_model_get_path (GTK_TREE_MODEL (data),
+ &tmpiter);
+
+ if (tmppath)
+ {
+ gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (data),
+ tmppath, &tmpiter);
+ gtk_tree_path_free (tmppath);
+ }
+
+ /* not covering this signal */
+ goto done;
+ }
+
+ level = elt->children;
+ parent_level = level;
+ i++;
+ }
+ }
+
+ if (!parent_level)
+ goto done;
+
+ /* let's try to insert the value */
+ offset = gtk_tree_path_get_indices (real_path)[gtk_tree_path_get_depth (real_path) - 1];
+
+ /* only insert when visible */
+ if (egg_tree_model_filter_visible (filter, &real_c_iter))
+ {
+ FilterElt felt;
+
+ if (EGG_TREE_MODEL_FILTER_CACHE_CHILD_ITERS (filter))
+ felt.iter = real_c_iter;
+ felt.offset = offset;
+ felt.zero_ref_count = 0;
+ felt.ref_count = 0;
+ felt.visible = TRUE;
+ felt.children = NULL;
+
+ for (i = 0; i < level->array->len; i++)
+ if (g_array_index (level->array, FilterElt, i).offset > offset)
+ break;
+
+ g_array_insert_val (level->array, i, felt);
+ index = i;
+ }
+
+ /* update the offsets, yes if we didn't insert the node above, there will
+ * be a gap here. This will be filled with the node (via fetch_child) when
+ * it becomes visible
+ */
+ for (i = 0; i < level->array->len; i++)
+ {
+ FilterElt *e = &g_array_index (level->array, FilterElt, i);
+ if ((e->offset >= offset) && i != index)
+ e->offset++;
+ if (e->children)
+ e->children->parent_elt = e;
+ }
+
+ /* don't emit the signal if we aren't visible */
+ if (!egg_tree_model_filter_visible (filter, &real_c_iter))
+ goto done;
+
+done_and_emit:
+ /* NOTE: pass c_path here and NOT real_path. This function does
+ * root subtraction itself
+ */
+ path = egg_real_tree_model_filter_convert_child_path_to_path (filter,
+ c_path,
+ FALSE, TRUE);
+
+ if (!path)
+ return;
+
+ egg_tree_model_filter_increment_stamp (filter);
+
+ gtk_tree_model_get_iter (GTK_TREE_MODEL (data), &iter, path);
+ gtk_tree_model_row_inserted (GTK_TREE_MODEL (data), path, &iter);
+
+#ifdef VERBOSE
+ g_print ("inserted row with offset %d\n", index);
+#endif
+
+done:
+ if (free_c_path)
+ gtk_tree_path_free (c_path);
+}
+
+static void
+egg_tree_model_filter_row_has_child_toggled (GtkTreeModel *c_model,
+ GtkTreePath *c_path,
+ GtkTreeIter *c_iter,
+ gpointer data)
+{
+ EggTreeModelFilter *filter = EGG_TREE_MODEL_FILTER (data);
+ GtkTreePath *path;
+ GtkTreeIter iter;
+
+ g_return_if_fail (c_path != NULL && c_iter != NULL);
+
+ /* FIXME: does this code work? */
+
+ if (!egg_tree_model_filter_visible (filter, c_iter))
+ return;
+
+ path = egg_real_tree_model_filter_convert_child_path_to_path (filter,
+ c_path,
+ FALSE,
+ TRUE);
+ 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_path_free (path);
+}
+
+static void
+egg_tree_model_filter_row_deleted (GtkTreeModel *c_model,
+ GtkTreePath *c_path,
+ gpointer data)
+{
+ EggTreeModelFilter *filter = EGG_TREE_MODEL_FILTER (data);
+ GtkTreePath *path;
+ GtkTreeIter iter;
+ FilterElt *elt;
+ FilterLevel *level;
+ gint offset;
+ gboolean emit_signal = TRUE;
+ gint i;
+
+ g_return_if_fail (c_path != NULL);
+
+ /* special case the deletion of an ancestor of the virtual root */
+ if (filter->virtual_root &&
+ (gtk_tree_path_is_ancestor (c_path, filter->virtual_root) ||
+ !gtk_tree_path_compare (c_path, filter->virtual_root)))
+ {
+ gint i;
+ GtkTreePath *path;
+ FilterLevel *level = FILTER_LEVEL (filter->root);
+
+ if (!level)
+ return;
+
+ /* remove everything in the filter model
+ *
+ * For now, we just iterate over the root level and emit a
+ * row_deleted for each FilterElt. Not sure if this is correct.
+ */
+
+ egg_tree_model_filter_increment_stamp (filter);
+ path = gtk_tree_path_new ();
+ gtk_tree_path_append_index (path, 0);
+
+ for (i = 0; i < level->array->len; i++)
+ gtk_tree_model_row_deleted (GTK_TREE_MODEL (data), path);
+
+ gtk_tree_path_free (path);
+ egg_tree_model_filter_free_level (filter, filter->root);
+
+ return;
+ }
+
+ /* fixup virtual root */
+ if (filter->virtual_root)
+ {
+ if (gtk_tree_path_get_depth (filter->virtual_root) >=
+ gtk_tree_path_get_depth (c_path))
+ {
+ gint level;
+ gint *v_indices, *c_indices;
+
+ level = gtk_tree_path_get_depth (c_path) - 1;
+ v_indices = gtk_tree_path_get_indices (filter->virtual_root);
+ c_indices = gtk_tree_path_get_indices (c_path);
+
+ if (v_indices[level] > c_indices[level])
+ (v_indices[level])--;
+ }
+ }
+
+ path = egg_real_tree_model_filter_convert_child_path_to_path (filter,
+ c_path,
+ FALSE,
+ FALSE);
+ if (!path)
+ {
+ path = egg_real_tree_model_filter_convert_child_path_to_path (filter,
+ c_path,
+ FALSE,
+ TRUE);
+
+ if (!path)
+ {
+ /* fixup the offsets */
+ GtkTreePath *real_path;
+
+ if (!filter->root)
+ return;
+
+ level = FILTER_LEVEL (filter->root);
+
+ /* subtract vroot if necessary */
+ if (filter->virtual_root)
+ {
+ real_path = egg_tree_model_filter_remove_root (c_path,
+ filter->virtual_root);
+ /* we don't handle this */
+ if (!real_path)
+ return;
+ }
+ else
+ real_path = gtk_tree_path_copy (c_path);
+
+ i = 0;
+ if (gtk_tree_path_get_depth (real_path) - 1 >= 1)
+ {
+ while (i < gtk_tree_path_get_depth (real_path) - 1)
+ {
+ gint j;
+
+ if (!level)
+ {
+ /* we don't cover this */
+ gtk_tree_path_free (real_path);
+ return;
+ }
+
+ elt = NULL;
+ for (j = 0; j < level->array->len; j++)
+ if (g_array_index (level->array, FilterElt, j).offset ==
+ gtk_tree_path_get_indices (real_path)[i])
+ {
+ elt = &g_array_index (level->array, FilterElt, j);
+ break;
+ }
+
+ if (!elt || !elt->children)
+ {
+ /* parent is filtered out, so no level */
+ gtk_tree_path_free (real_path);
+ return;
+ }
+
+ level = elt->children;
+ i++;
+ }
+ }
+
+ offset = gtk_tree_path_get_indices (real_path)[gtk_tree_path_get_depth (real_path) - 1];
+ gtk_tree_path_free (real_path);
+
+ if (!level)
+ return;
+
+ /* we need:
+ * - the offset of the removed item
+ * - the level
+ */
+ for (i = 0; i < level->array->len; i++)
+ {
+ elt = &g_array_index (level->array, FilterElt, i);
+ if (elt->offset > offset)
+ elt->offset--;
+ if (elt->children)
+ elt->children->parent_elt = elt;
+ }
+
+ return;
+ }
+
+ emit_signal = FALSE;
+ }
+
+ gtk_tree_model_get_iter (GTK_TREE_MODEL (data), &iter, path);
+
+ level = FILTER_LEVEL (iter.user_data);
+ elt = FILTER_ELT (iter.user_data2);
+ offset = elt->offset;
+
+ if (emit_signal)
+ {
+ if (level->ref_count == 0 && level != filter->root)
+ {
+ egg_tree_model_filter_increment_stamp (filter);
+ gtk_tree_model_row_deleted (GTK_TREE_MODEL (data), path);
+
+ gtk_tree_path_free (path);
+ return;
+ }
+
+ egg_tree_model_filter_increment_stamp (filter);
+ gtk_tree_model_row_deleted (GTK_TREE_MODEL (data), path);
+ iter.stamp = filter->stamp;
+
+ while (elt->ref_count > 0)
+ egg_tree_model_filter_real_unref_node (GTK_TREE_MODEL (data), &iter,
+ FALSE);
+ }
+
+ if (level->array->len == 1)
+ {
+ /* kill level */
+ egg_tree_model_filter_free_level (filter, level);
+ }
+ else
+ {
+ /* remove the row */
+ for (i = 0; i < level->array->len; i++)
+ if (elt->offset == g_array_index (level->array, FilterElt, i).offset)
+ break;
+
+ offset = g_array_index (level->array, FilterElt, i).offset;
+ g_array_remove_index (level->array, i);
+
+ for (i = 0; i < level->array->len; i++)
+ {
+ elt = &g_array_index (level->array, FilterElt, i);
+ if (elt->offset > offset)
+ elt->offset--;
+ if (elt->children)
+ elt->children->parent_elt = elt;
+ }
+ }
+
+ gtk_tree_path_free (path);
+}
+
+static void
+egg_tree_model_filter_rows_reordered (GtkTreeModel *c_model,
+ GtkTreePath *c_path,
+ GtkTreeIter *c_iter,
+ gint *new_order,
+ gpointer data)
+{
+ FilterElt *elt;
+ FilterLevel *level;
+ EggTreeModelFilter *filter = EGG_TREE_MODEL_FILTER (data);
+
+ GtkTreePath *path;
+ GtkTreeIter iter;
+
+ gint *tmp_array;
+ gint i, j, elt_count;
+ gint length;
+
+ GArray *new_array;
+
+ g_return_if_fail (new_order != NULL);
+
+#ifdef VERBOSE
+ g_print ("filter: reordering\n");
+#endif
+
+ if (c_path == NULL || gtk_tree_path_get_indices (c_path) == NULL)
+ {
+ if (!filter->root)
+ return;
+
+ length = gtk_tree_model_iter_n_children (c_model, NULL);
+
+ if (filter->virtual_root)
+ {
+ gint new_pos = -1;
+
+ /* reorder root level of path */
+ for (i = 0; i < length; i++)
+ if (new_order[i] == gtk_tree_path_get_indices (filter->virtual_root)[0])
+ new_pos = i;
+
+ if (new_pos < 0)
+ return;
+
+ gtk_tree_path_get_indices (filter->virtual_root)[0] = new_pos;
+ return;
+ }
+
+ path = gtk_tree_path_new ();
+ level = FILTER_LEVEL (filter->root);
+ }
+ else
+ {
+ GtkTreeIter child_iter;
+
+ /* virtual root anchor reordering */
+ if (filter->virtual_root &&
+ gtk_tree_path_get_depth (c_path) <
+ gtk_tree_path_get_depth (filter->virtual_root))
+ {
+ gint new_pos = -1;
+ gint length;
+ gint level;
+ GtkTreeIter real_c_iter;
+
+ level = gtk_tree_path_get_depth (c_path);
+
+ if (c_iter)
+ real_c_iter = *c_iter;
+ else
+ gtk_tree_model_get_iter (c_model, &real_c_iter, c_path);
+
+ length = gtk_tree_model_iter_n_children (c_model, &real_c_iter);
+
+ for (i = 0; i < length; i++)
+ if (new_order[i] == gtk_tree_path_get_indices (filter->virtual_root)[level])
+ new_pos = i;
+
+ if (new_pos < 0)
+ return;
+
+ gtk_tree_path_get_indices (filter->virtual_root)[level] = new_pos;
+ return;
+ }
+
+ path = egg_real_tree_model_filter_convert_child_path_to_path (filter,
+ c_path,
+ FALSE,
+ FALSE);
+ if (!path && filter->virtual_root &&
+ gtk_tree_path_compare (c_path, filter->virtual_root))
+ return;
+
+ if (!path && !filter->virtual_root)
+ return;
+
+ if (!path)
+ {
+ /* root level mode */
+ if (!c_iter)
+ gtk_tree_model_get_iter (c_model, c_iter, c_path);
+ length = gtk_tree_model_iter_n_children (c_model, c_iter);
+ path = gtk_tree_path_new ();
+ level = FILTER_LEVEL (filter->root);
+ }
+ else
+ {
+ gtk_tree_model_get_iter (GTK_TREE_MODEL (data), &iter, path);
+
+ level = FILTER_LEVEL (iter.user_data);
+ elt = FILTER_ELT (iter.user_data2);
+
+ if (!elt->children)
+ {
+ gtk_tree_path_free (path);
+ return;
+ }
+
+ level = elt->children;
+
+ egg_tree_model_filter_convert_iter_to_child_iter (EGG_TREE_MODEL_FILTER (filter), &child_iter, &iter);
+ length = gtk_tree_model_iter_n_children (c_model, &child_iter);
+ }
+ }
+
+ if (level->array->len < 1)
+ return;
+
+ /* NOTE: we do not bail out here if level->array->len < 2 like
+ * GtkTreeModelSort does. This because we do some special tricky
+ * reordering.
+ */
+
+ /* construct a new array */
+ new_array = g_array_sized_new (FALSE, FALSE, sizeof (FilterElt),
+ level->array->len);
+ tmp_array = g_new (gint, level->array->len);
+
+ for (i = 0, elt_count = 0; i < length; i++)
+ {
+ FilterElt *e = NULL;
+ gint old_offset = -1;
+
+ for (j = 0; j < level->array->len; j++)
+ if (g_array_index (level->array, FilterElt, j).offset == new_order[i])
+ {
+ e = &g_array_index (level->array, FilterElt, j);
+ old_offset = j;
+ break;
+ }
+
+ if (!e)
+ continue;
+
+ tmp_array[elt_count] = old_offset;
+ g_array_append_val (new_array, *e);
+ g_array_index (new_array, FilterElt, elt_count).offset = i;
+ elt_count++;
+ }
+
+ g_array_free (level->array, TRUE);
+ level->array = new_array;
+
+ /* fix up stuff */
+ 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;
+ }
+
+ /* emit rows_reordered */
+ if (!gtk_tree_path_get_indices (path))
+ 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);
+
+ /* done */
+ g_free (tmp_array);
+ gtk_tree_path_free (path);
+}
+
+/* TreeModelIface implementation */
+static guint
+egg_tree_model_filter_get_flags (GtkTreeModel *model)
+{
+ g_return_val_if_fail (EGG_IS_TREE_MODEL_FILTER (model), 0);
+
+ return 0;
+}
+
+static gint
+egg_tree_model_filter_get_n_columns (GtkTreeModel *model)
+{
+ EggTreeModelFilter *filter = (EggTreeModelFilter *)model;
+
+ g_return_val_if_fail (EGG_IS_TREE_MODEL_FILTER (model), 0);
+ g_return_val_if_fail (filter->child_model != NULL, 0);
+
+ if (filter->child_model == NULL)
+ return 0;
+
+ /* so we can't modify the modify func after this ... */
+ filter->modify_func_set = TRUE;
+
+ if (filter->modify_n_columns > 0)
+ return filter->modify_n_columns;
+
+ return gtk_tree_model_get_n_columns (filter->child_model);
+}
+
+static GType
+egg_tree_model_filter_get_column_type (GtkTreeModel *model,
+ gint index)
+{
+ EggTreeModelFilter *filter = (EggTreeModelFilter *)model;
+
+ g_return_val_if_fail (EGG_IS_TREE_MODEL_FILTER (model), G_TYPE_INVALID);
+ g_return_val_if_fail (filter->child_model != NULL, G_TYPE_INVALID);
+
+ /* so we can't modify the modify func after this ... */
+ filter->modify_func_set = TRUE;
+
+ if (filter->modify_types)
+ {
+ g_return_val_if_fail (index < filter->modify_n_columns, G_TYPE_INVALID);
+
+ return filter->modify_types[index];
+ }
+
+ return gtk_tree_model_get_column_type (filter->child_model, index);
+}
+
+static gboolean
+egg_tree_model_filter_get_iter (GtkTreeModel *model,
+ GtkTreeIter *iter,
+ GtkTreePath *path)
+{
+ EggTreeModelFilter *filter = (EggTreeModelFilter *)model;
+ gint *indices;
+ FilterLevel *level;
+ gint depth, i;
+
+ g_return_val_if_fail (EGG_IS_TREE_MODEL_FILTER (model), FALSE);
+ g_return_val_if_fail (filter->child_model != NULL, FALSE);
+
+ indices = gtk_tree_path_get_indices (path);
+
+ if (filter->root == NULL)
+ egg_tree_model_filter_build_level (filter, NULL, NULL);
+ level = FILTER_LEVEL (filter->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;
+ }
+
+ if (!g_array_index (level->array, FilterElt, indices[i]).children)
+ egg_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;
+ }
+
+ if (!level || indices[i] >= level->array->len)
+ {
+ iter->stamp = 0;
+ return FALSE;
+ }
+
+ iter->stamp = filter->stamp;
+ iter->user_data = level;
+ iter->user_data2 = &g_array_index (level->array, FilterElt,
+ indices[depth - 1]);
+
+ return TRUE;
+}
+
+static GtkTreePath *
+egg_tree_model_filter_get_path (GtkTreeModel *model,
+ GtkTreeIter *iter)
+{
+ GtkTreePath *retval;
+ FilterLevel *level;
+ FilterElt *elt;
+
+ g_return_val_if_fail (EGG_IS_TREE_MODEL_FILTER (model), NULL);
+ g_return_val_if_fail (EGG_TREE_MODEL_FILTER (model)->child_model != NULL, NULL);
+ g_return_val_if_fail (EGG_TREE_MODEL_FILTER (model)->stamp == iter->stamp, NULL);
+
+ retval = gtk_tree_path_new ();
+ level = iter->user_data;
+ elt = iter->user_data2;
+
+ while (level)
+ {
+ gtk_tree_path_prepend_index (retval,
+ elt - FILTER_ELT (level->array->data));
+ elt = level->parent_elt;
+ level = level->parent_level;
+ }
+
+ return retval;
+}
+
+static void
+egg_tree_model_filter_get_value (GtkTreeModel *model,
+ GtkTreeIter *iter,
+ gint column,
+ GValue *value)
+{
+ GtkTreeIter child_iter;
+ EggTreeModelFilter *filter = EGG_TREE_MODEL_FILTER (model);
+
+ g_return_if_fail (EGG_IS_TREE_MODEL_FILTER (model));
+ g_return_if_fail (EGG_TREE_MODEL_FILTER (model)->child_model != NULL);
+ g_return_if_fail (EGG_TREE_MODEL_FILTER (model)->stamp == iter->stamp);
+
+ if (filter->modify_func)
+ {
+ g_return_if_fail (column < filter->modify_n_columns);
+
+ g_value_init (value, filter->modify_types[column]);
+ filter->modify_func (model,
+ iter,
+ value,
+ column,
+ filter->modify_data);
+
+ return;
+ }
+
+ egg_tree_model_filter_convert_iter_to_child_iter (EGG_TREE_MODEL_FILTER (model), &child_iter, iter);
+ gtk_tree_model_get_value (EGG_TREE_MODEL_FILTER (model)->child_model,
+ &child_iter, column, value);
+}
+
+static gboolean
+egg_tree_model_filter_iter_next (GtkTreeModel *model,
+ GtkTreeIter *iter)
+{
+ FilterLevel *level;
+ FilterElt *elt;
+
+ g_return_val_if_fail (EGG_IS_TREE_MODEL_FILTER (model), FALSE);
+ g_return_val_if_fail (EGG_TREE_MODEL_FILTER (model)->child_model != NULL, FALSE);
+ g_return_val_if_fail (EGG_TREE_MODEL_FILTER (model)->stamp == iter->stamp, FALSE);
+
+ level = iter->user_data;
+ elt = iter->user_data2;
+
+ if (elt - FILTER_ELT (level->array->data) >= level->array->len - 1)
+ {
+ iter->stamp = 0;
+ return FALSE;
+ }
+
+ iter->user_data2 = elt + 1;
+
+ return TRUE;
+}
+
+static gboolean
+egg_tree_model_filter_iter_children (GtkTreeModel *model,
+ GtkTreeIter *iter,
+ GtkTreeIter *parent)
+{
+ EggTreeModelFilter *filter = (EggTreeModelFilter *)model;
+ FilterLevel *level;
+
+ iter->stamp = 0;
+ g_return_val_if_fail (EGG_IS_TREE_MODEL_FILTER (model), FALSE);
+ g_return_val_if_fail (filter->child_model != NULL, FALSE);
+ if (parent)
+ g_return_val_if_fail (filter->stamp == parent->stamp, FALSE);
+
+ if (!parent)
+ {
+ if (!filter->root)
+ egg_tree_model_filter_build_level (filter, NULL, NULL);
+ if (!filter->root)
+ return FALSE;
+
+ level = filter->root;
+ iter->stamp = filter->stamp;
+ iter->user_data = level;
+ iter->user_data2 = level->array->data;
+ }
+ else
+ {
+ if (FILTER_ELT (parent->user_data2)->children == NULL)
+ egg_tree_model_filter_build_level (filter,
+ FILTER_LEVEL (parent->user_data),
+ FILTER_ELT (parent->user_data2));
+ if (FILTER_ELT (parent->user_data2)->children == NULL)
+ return FALSE;
+
+ iter->stamp = filter->stamp;
+ iter->user_data = FILTER_ELT (parent->user_data2)->children;
+ iter->user_data2 = FILTER_LEVEL (iter->user_data)->array->data;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+egg_tree_model_filter_iter_has_child (GtkTreeModel *model,
+ GtkTreeIter *iter)
+{
+ GtkTreeIter child_iter;
+ EggTreeModelFilter *filter = (EggTreeModelFilter *)model;
+ FilterElt *elt;
+
+ g_return_val_if_fail (EGG_IS_TREE_MODEL_FILTER (model), FALSE);
+ g_return_val_if_fail (filter->child_model != NULL, FALSE);
+ g_return_val_if_fail (filter->stamp == iter->stamp, FALSE);
+
+ filter = EGG_TREE_MODEL_FILTER (model);
+
+ egg_tree_model_filter_convert_iter_to_child_iter (EGG_TREE_MODEL_FILTER (model), &child_iter, iter);
+ elt = FILTER_ELT (iter->user_data2);
+
+ /* 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->child_model, &child_iter))
+ egg_tree_model_filter_build_level (filter, FILTER_LEVEL (iter->user_data),
+ elt);
+
+ if (elt->children && elt->children->array->len > 0)
+ return TRUE;
+
+ return FALSE;
+}
+
+static gint
+egg_tree_model_filter_iter_n_children (GtkTreeModel *model,
+ GtkTreeIter *iter)
+{
+ GtkTreeIter child_iter;
+ EggTreeModelFilter *filter = (EggTreeModelFilter *)model;
+ FilterElt *elt;
+
+ g_return_val_if_fail (EGG_IS_TREE_MODEL_FILTER (model), 0);
+ g_return_val_if_fail (filter->child_model != NULL, 0);
+ if (iter)
+ g_return_val_if_fail (filter->stamp == iter->stamp, 0);
+
+ if (!iter)
+ {
+ int i = 0;
+ int count = 0;
+ GArray *a;
+
+ if (!filter->root)
+ egg_tree_model_filter_build_level (filter, NULL, NULL);
+
+ a = FILTER_LEVEL (filter->root)->array;
+
+ /* count visible nodes */
+
+ for (i = 0; i < a->len; i++)
+ if (g_array_index (a, FilterElt, i).visible)
+ count++;
+
+ return count;
+ }
+
+ elt = FILTER_ELT (iter->user_data2);
+ egg_tree_model_filter_convert_iter_to_child_iter (EGG_TREE_MODEL_FILTER (model), &child_iter, iter);
+
+ if (!elt->children &&
+ gtk_tree_model_iter_has_child (filter->child_model, &child_iter))
+ egg_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++;
+
+ return count;
+ }
+
+ return 0;
+}
+
+static gboolean
+egg_tree_model_filter_iter_nth_child (GtkTreeModel *model,
+ GtkTreeIter *iter,
+ GtkTreeIter *parent,
+ gint n)
+{
+ FilterLevel *level;
+ GtkTreeIter children;
+
+ g_return_val_if_fail (EGG_IS_TREE_MODEL_FILTER (model), FALSE);
+ if (parent)
+ g_return_val_if_fail (EGG_TREE_MODEL_FILTER (model)->stamp == parent->stamp, FALSE);
+
+ /* use this instead of has_Child to force us to build the level, if needed */
+ if (egg_tree_model_filter_iter_children (model, &children, parent) == FALSE)
+ {
+ iter->stamp = 0;
+ return FALSE;
+ }
+
+ level = children.user_data;
+ if (n >= level->array->len)
+ {
+ iter->stamp = 0;
+ return FALSE;
+ }
+
+ iter->stamp = EGG_TREE_MODEL_FILTER (model)->stamp;
+ iter->user_data = level;
+ iter->user_data2 = &g_array_index (level->array, FilterElt, n);
+
+ return TRUE;
+}
+
+static gboolean
+egg_tree_model_filter_iter_parent (GtkTreeModel *model,
+ GtkTreeIter *iter,
+ GtkTreeIter *child)
+{
+ FilterLevel *level;
+
+ iter->stamp = 0;
+ g_return_val_if_fail (EGG_IS_TREE_MODEL_FILTER (model), FALSE);
+ g_return_val_if_fail (EGG_TREE_MODEL_FILTER (model)->child_model != NULL, FALSE);
+ g_return_val_if_fail (EGG_TREE_MODEL_FILTER (model)->stamp == child->stamp, FALSE);
+
+ level = child->user_data;
+
+ if (level->parent_level)
+ {
+ iter->stamp = EGG_TREE_MODEL_FILTER (model)->stamp;
+ iter->user_data = level->parent_level;
+ iter->user_data2 = level->parent_elt;
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+egg_tree_model_filter_ref_node (GtkTreeModel *model,
+ GtkTreeIter *iter)
+{
+ EggTreeModelFilter *filter = (EggTreeModelFilter *)model;
+ GtkTreeIter child_iter;
+ FilterLevel *level;
+ FilterElt *elt;
+
+ g_return_if_fail (EGG_IS_TREE_MODEL_FILTER (model));
+ g_return_if_fail (EGG_TREE_MODEL_FILTER (model)->child_model != NULL);
+ g_return_if_fail (EGG_TREE_MODEL_FILTER (model)->stamp == iter->stamp);
+
+ egg_tree_model_filter_convert_iter_to_child_iter (EGG_TREE_MODEL_FILTER (model), &child_iter, iter);
+
+ gtk_tree_model_ref_node (filter->child_model, &child_iter);
+
+ level = iter->user_data;
+ elt = iter->user_data2;
+
+ elt->ref_count++;
+ level->ref_count++;
+ if (level->ref_count == 1)
+ {
+ FilterLevel *parent_level = level->parent_level;
+ FilterElt *parent_elt = level->parent_elt;
+
+ /* we were at zero -- time to decrease the zero_ref_count val */
+ do
+ {
+ if (parent_elt)
+ parent_elt->zero_ref_count--;
+
+ if (parent_level)
+ {
+ parent_elt = parent_level->parent_elt;
+ parent_level = parent_level->parent_level;
+ }
+ }
+ while (parent_level);
+ filter->zero_ref_count--;
+ }
+
+#ifdef VERBOSE
+ g_print ("reffed %p\n", iter->user_data2);
+ g_print ("zero ref count is now %d\n", filter->zero_ref_count);
+ if (filter->zero_ref_count < 0)
+ g_warning ("zero_ref_count < 0.");
+#endif
+}
+
+static void
+egg_tree_model_filter_unref_node (GtkTreeModel *model,
+ GtkTreeIter *iter)
+{
+ egg_tree_model_filter_real_unref_node (model, iter, TRUE);
+}
+
+static void
+egg_tree_model_filter_real_unref_node (GtkTreeModel *model,
+ GtkTreeIter *iter,
+ gboolean propagate_unref)
+{
+ EggTreeModelFilter *filter = (EggTreeModelFilter *)model;
+ FilterLevel *level;
+ FilterElt *elt;
+
+ g_return_if_fail (EGG_IS_TREE_MODEL_FILTER (model));
+ g_return_if_fail (filter->child_model != NULL);
+ g_return_if_fail (filter->stamp == iter->stamp);
+
+ if (propagate_unref)
+ {
+ GtkTreeIter child_iter;
+ egg_tree_model_filter_convert_iter_to_child_iter (EGG_TREE_MODEL_FILTER (model), &child_iter, iter);
+ gtk_tree_model_unref_node (filter->child_model, &child_iter);
+ }
+
+ level = iter->user_data;
+ elt = iter->user_data2;
+
+ g_return_if_fail (elt->ref_count > 0);
+
+ elt->ref_count--;
+ level->ref_count--;
+ if (level->ref_count == 0)
+ {
+ FilterLevel *parent_level = level->parent_level;
+ FilterElt *parent_elt = level->parent_elt;
+
+ /* we are at zero -- time to increase the zero_ref_count val */
+ while (parent_level)
+ {
+ parent_elt->zero_ref_count++;
+
+ parent_elt = parent_level->parent_elt;
+ parent_level = parent_level->parent_level;
+ }
+ filter->zero_ref_count++;
+ }
+
+#ifdef VERBOSE
+ g_print ("unreffed %p\n", iter->user_data2);
+ g_print ("zero ref count is now %d\n", filter->zero_ref_count);
+#endif
+}
+
+/* bits and pieces */
+static void
+egg_tree_model_filter_set_model (EggTreeModelFilter *filter,
+ GtkTreeModel *child_model)
+{
+ g_return_if_fail (EGG_IS_TREE_MODEL_FILTER (filter));
+
+ if (filter->child_model)
+ {
+ g_signal_handler_disconnect (G_OBJECT (filter->child_model),
+ filter->changed_id);
+ g_signal_handler_disconnect (G_OBJECT (filter->child_model),
+ filter->inserted_id);
+ g_signal_handler_disconnect (G_OBJECT (filter->child_model),
+ filter->has_child_toggled_id);
+ g_signal_handler_disconnect (G_OBJECT (filter->child_model),
+ filter->deleted_id);
+ g_signal_handler_disconnect (G_OBJECT (filter->child_model),
+ filter->reordered_id);
+
+ /* reset our state */
+ if (filter->root)
+ egg_tree_model_filter_free_level (filter, filter->root);
+
+ filter->root = NULL;
+ g_object_unref (G_OBJECT (filter->child_model));
+ filter->visible_column = -1;
+ /* FIXME: destroy more crack here? the funcs? */
+ }
+
+ filter->child_model = child_model;
+
+ if (child_model)
+ {
+ g_object_ref (G_OBJECT (filter->child_model));
+ filter->changed_id =
+ g_signal_connect (child_model, "row_changed",
+ G_CALLBACK (egg_tree_model_filter_row_changed),
+ filter);
+ filter->inserted_id =
+ g_signal_connect (child_model, "row_inserted",
+ G_CALLBACK (egg_tree_model_filter_row_inserted),
+ filter);
+ filter->has_child_toggled_id =
+ g_signal_connect (child_model, "row_has_child_toggled",
+ G_CALLBACK (egg_tree_model_filter_row_has_child_toggled),
+ filter);
+ filter->deleted_id =
+ g_signal_connect (child_model, "row_deleted",
+ G_CALLBACK (egg_tree_model_filter_row_deleted),
+ filter);
+ filter->reordered_id =
+ g_signal_connect (child_model, "rows_reordered",
+ G_CALLBACK (egg_tree_model_filter_rows_reordered),
+ filter);
+
+ filter->child_flags = gtk_tree_model_get_flags (child_model);
+ filter->stamp = g_random_int ();
+ }
+}
+
+static void
+egg_tree_model_filter_set_root (EggTreeModelFilter *filter,
+ GtkTreePath *root)
+{
+ g_return_if_fail (EGG_IS_TREE_MODEL_FILTER (filter));
+
+ if (!root)
+ filter->virtual_root = NULL;
+ else
+ filter->virtual_root = gtk_tree_path_copy (root);
+}
+
+/* public API */
+
+GtkTreeModel *
+egg_tree_model_filter_new (GtkTreeModel *child_model,
+ GtkTreePath *root)
+{
+ GtkTreeModel *retval;
+
+ g_return_val_if_fail (GTK_IS_TREE_MODEL (child_model), NULL);
+
+ retval = GTK_TREE_MODEL (g_object_new (egg_tree_model_filter_get_type (), NULL));
+
+ egg_tree_model_filter_set_model (EGG_TREE_MODEL_FILTER (retval),
+ child_model);
+ egg_tree_model_filter_set_root (EGG_TREE_MODEL_FILTER (retval), root);
+
+ return retval;
+}
+
+GtkTreeModel *
+egg_tree_model_filter_get_model (EggTreeModelFilter *filter)
+{
+ g_return_val_if_fail (EGG_IS_TREE_MODEL_FILTER (filter), NULL);
+
+ return filter->child_model;
+}
+
+void
+egg_tree_model_filter_set_visible_func (EggTreeModelFilter *filter,
+ EggTreeModelFilterVisibleFunc func,
+ gpointer data,
+ GtkDestroyNotify destroy)
+{
+ g_return_if_fail (EGG_IS_TREE_MODEL_FILTER (filter));
+ g_return_if_fail (func != NULL);
+ g_return_if_fail (filter->visible_method_set == FALSE);
+
+ if (filter->visible_func)
+ {
+ GtkDestroyNotify d = filter->visible_destroy;
+
+ filter->visible_destroy = NULL;
+ d (filter->visible_data);
+ }
+
+ filter->visible_func = func;
+ filter->visible_data = data;
+ filter->visible_destroy = destroy;
+
+ filter->visible_method_set = TRUE;
+}
+
+void
+egg_tree_model_filter_set_modify_func (EggTreeModelFilter *filter,
+ gint n_columns,
+ GType *types,
+ EggTreeModelFilterModifyFunc func,
+ gpointer data,
+ GtkDestroyNotify destroy)
+{
+ g_return_if_fail (EGG_IS_TREE_MODEL_FILTER (filter));
+ g_return_if_fail (func != NULL);
+ g_return_if_fail (filter->modify_func_set == FALSE);
+
+ if (filter->modify_destroy)
+ {
+ GtkDestroyNotify d = filter->modify_destroy;
+
+ filter->modify_destroy = NULL;
+ d (filter->modify_data);
+ }
+
+ filter->modify_n_columns = n_columns;
+ filter->modify_types = g_new0 (GType, n_columns);
+ memcpy (filter->modify_types, types, sizeof (GType) * n_columns);
+ filter->modify_func = func;
+ filter->modify_data = data;
+ filter->modify_destroy = destroy;
+
+ filter->modify_func_set = TRUE;
+}
+
+void
+egg_tree_model_filter_set_visible_column (EggTreeModelFilter *filter,
+ gint column)
+{
+ g_return_if_fail (EGG_IS_TREE_MODEL_FILTER (filter));
+ g_return_if_fail (column >= 0);
+ g_return_if_fail (filter->visible_method_set == FALSE);
+
+ filter->visible_column = column;
+
+ filter->visible_method_set = TRUE;
+}
+
+/* conversion */
+void
+egg_tree_model_filter_convert_child_iter_to_iter (EggTreeModelFilter *filter,
+ GtkTreeIter *filter_iter,
+ GtkTreeIter *child_iter)
+{
+ GtkTreePath *child_path, *path;
+
+ g_return_if_fail (EGG_IS_TREE_MODEL_FILTER (filter));
+ g_return_if_fail (filter->child_model != NULL);
+ g_return_if_fail (filter_iter != NULL);
+ g_return_if_fail (child_iter != NULL);
+
+ filter_iter->stamp = 0;
+
+ child_path = gtk_tree_model_get_path (filter->child_model, child_iter);
+ g_return_if_fail (child_path != NULL);
+
+ path = egg_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);
+ gtk_tree_path_free (path);
+}
+
+void
+egg_tree_model_filter_convert_iter_to_child_iter (EggTreeModelFilter *filter,
+ GtkTreeIter *child_iter,
+ GtkTreeIter *filter_iter)
+{
+ g_return_if_fail (EGG_IS_TREE_MODEL_FILTER (filter));
+ g_return_if_fail (filter->child_model != NULL);
+ g_return_if_fail (child_iter != NULL);
+ g_return_if_fail (filter_iter != NULL);
+ g_return_if_fail (filter_iter->stamp == filter->stamp);
+
+ if (EGG_TREE_MODEL_FILTER_CACHE_CHILD_ITERS (filter))
+ {
+ *child_iter = FILTER_ELT (filter_iter->user_data2)->iter;
+ }
+ else
+ {
+ GtkTreePath *path;
+
+ path = egg_tree_model_filter_elt_get_path (filter_iter->user_data,
+ filter_iter->user_data2,
+ NULL);
+ gtk_tree_model_get_iter (filter->child_model, child_iter, path);
+ gtk_tree_path_free (path);
+ }
+}
+
+static GtkTreePath *
+egg_real_tree_model_filter_convert_child_path_to_path (EggTreeModelFilter *filter,
+ GtkTreePath *child_path,
+ gboolean build_levels,
+ gboolean fetch_childs)
+{
+ gint *child_indices;
+ GtkTreePath *retval;
+ GtkTreePath *real_path;
+ FilterLevel *level;
+ gint i;
+
+ g_return_val_if_fail (EGG_IS_TREE_MODEL_FILTER (filter), NULL);
+ g_return_val_if_fail (filter->child_model != NULL, NULL);
+ g_return_val_if_fail (child_path != NULL, NULL);
+
+ if (!filter->virtual_root)
+ real_path = gtk_tree_path_copy (child_path);
+ else
+ real_path = egg_tree_model_filter_remove_root (child_path,
+ filter->virtual_root);
+
+ if (!real_path)
+ return NULL;
+
+ retval = gtk_tree_path_new ();
+ child_indices = gtk_tree_path_get_indices (real_path);
+
+ if (filter->root == NULL && build_levels)
+ egg_tree_model_filter_build_level (filter, NULL, NULL);
+ level = FILTER_LEVEL (filter->root);
+
+ for (i = 0; i < gtk_tree_path_get_depth (real_path); i++)
+ {
+ gint j;
+ gboolean found_child = FALSE;
+
+ if (!level)
+ {
+ gtk_tree_path_free (real_path);
+ gtk_tree_path_free (retval);
+ return NULL;
+ }
+
+ for (j = 0; j < level->array->len; j++)
+ {
+ if ((g_array_index (level->array, FilterElt, j)).offset == child_indices[i])
+ {
+ gtk_tree_path_append_index (retval, j);
+ if (g_array_index (level->array, FilterElt, j).children == NULL && build_levels)
+ egg_tree_model_filter_build_level (filter,
+ level,
+ &g_array_index (level->array, FilterElt, j));
+ level = g_array_index (level->array, FilterElt, j).children;
+ found_child = TRUE;
+ break;
+ }
+ }
+
+ if (!found_child && fetch_childs)
+ {
+ /* didn't find the child, let's try to bring it back */
+ if (!egg_tree_model_filter_fetch_child (filter, level, child_indices[i]))
+ {
+ /* not there */
+ gtk_tree_path_free (real_path);
+ gtk_tree_path_free (retval);
+ return NULL;
+ }
+
+ /* yay, let's search for the child again */
+ for (j = 0; j < level->array->len; j++)
+ {
+ if ((g_array_index (level->array, FilterElt, j)).offset == child_indices[i])
+ {
+ gtk_tree_path_append_index (retval, j);
+ if (g_array_index (level->array, FilterElt, j).children == NULL && build_levels)
+ egg_tree_model_filter_build_level (filter,
+ level,
+ &g_array_index (level->array, FilterElt, j));
+ level = g_array_index (level->array, FilterElt, j).children;
+ found_child = TRUE;
+ break;
+ }
+ }
+
+ if (!found_child)
+ {
+ /* our happy fun fetch attempt failed ?!?!?! no child! */
+ gtk_tree_path_free (real_path);
+ gtk_tree_path_free (retval);
+ return NULL;
+ }
+ }
+ else if (!found_child && !fetch_childs)
+ {
+ /* no path */
+ gtk_tree_path_free (real_path);
+ gtk_tree_path_free (retval);
+ return NULL;
+ }
+ }
+
+ gtk_tree_path_free (real_path);
+ return retval;
+}
+
+GtkTreePath *
+egg_tree_model_filter_convert_child_path_to_path (EggTreeModelFilter *filter,
+ GtkTreePath *child_path)
+{
+ /* this function does the sanity checks */
+ return egg_real_tree_model_filter_convert_child_path_to_path (filter,
+ child_path,
+ TRUE,
+ TRUE);
+}
+
+GtkTreePath *
+egg_tree_model_filter_convert_path_to_child_path (EggTreeModelFilter *filter,
+ GtkTreePath *filter_path)
+{
+ gint *filter_indices;
+ GtkTreePath *retval;
+ FilterLevel *level;
+ gint i;
+
+ g_return_val_if_fail (EGG_IS_TREE_MODEL_FILTER (filter), NULL);
+ g_return_val_if_fail (filter->child_model != NULL, NULL);
+ g_return_val_if_fail (filter_path != NULL, NULL);
+
+ /* convert path */
+ retval = gtk_tree_path_new ();
+ filter_indices = gtk_tree_path_get_indices (filter_path);
+ if (!filter->root)
+ egg_tree_model_filter_build_level (filter, NULL, NULL);
+ level = FILTER_LEVEL (filter->root);
+
+ for (i = 0; i < gtk_tree_path_get_depth (filter_path); i++)
+ {
+ gint count = filter_indices[i];
+
+ if (!level || level->array->len <= filter_indices[i])
+ {
+ gtk_tree_path_free (retval);
+ return NULL;
+ }
+
+ if (g_array_index (level->array, FilterElt, count).children == NULL)
+ egg_tree_model_filter_build_level (filter, level, &g_array_index (level->array, FilterElt, count));
+
+ if (!level || level->array->len <= 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;
+ }
+
+ /* apply vroot */
+
+ if (filter->virtual_root)
+ {
+ GtkTreePath *real_retval;
+
+ real_retval = egg_tree_model_filter_add_root (retval,
+ filter->virtual_root);
+ gtk_tree_path_free (retval);
+
+ return real_retval;
+ }
+
+ return retval;
+}
+
+void
+egg_tree_model_filter_clear_cache (EggTreeModelFilter *filter)
+{
+ g_return_if_fail (EGG_IS_TREE_MODEL_FILTER (filter));
+
+ if (filter->zero_ref_count)
+ egg_tree_model_filter_clear_cache_helper (filter,
+ FILTER_LEVEL (filter->root));
+}