summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am8
-rw-r--r--src/gtktreemodelfilter.c2458
-rw-r--r--src/gtktreemodelfilter.h105
-rw-r--r--src/yelp-base.c5
-rw-r--r--src/yelp-util.c1
-rw-r--r--src/yelp-view-content.c47
6 files changed, 2597 insertions, 27 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index f1cabec6..af12c4f3 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -28,7 +28,13 @@ yelp_SOURCES = \
yelp-window.c yelp-window.h
yelp_LDADD = \
- $(YELP_LIBS)
+ $(YELP_LIBS) \
+ libgtktreemodelfilter.la
+
+noinst_LTLIBRARIES = libgtktreemodelfilter.la
+
+libgtktreemodelfilter_la_SOURCES = \
+ gtktreemodelfilter.c gtktreemodelfilter.h
gnome_yelp_idl_sources = \
GNOME_Yelp-stubs.c \
diff --git a/src/gtktreemodelfilter.c b/src/gtktreemodelfilter.c
new file mode 100644
index 00000000..e9ea4611
--- /dev/null
+++ b/src/gtktreemodelfilter.c
@@ -0,0 +1,2458 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * 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.
+ *
+ * Author: Kristian Rietveld <kris@gtk.org>
+ */
+
+#include "gtktreemodelfilter.h"
+#include <gtk/gtksignal.h>
+#include <string.h>
+
+/* EVIL! */
+#define _
+
+//#define VERBOSE 1
+
+/* ITER FORMAT:
+ *
+ * iter->stamp = tree_model_filter->stamp
+ * iter->user_data = FilterLevel
+ * iter->user_data2 = FilterElt
+ */
+
+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,
+ /* construct args */
+ PROP_MODEL,
+ PROP_FILTER_COLUMN
+};
+
+#define GTK_TREE_MODEL_FILTER_CACHE_CHILD_ITERS(tree_model_filter) \
+ (((GtkTreeModelFilter *)tree_model_filter)->child_flags & GTK_TREE_MODEL_ITERS_PERSIST)
+#define FILTER_ELT(filter_elt) ((FilterElt *)filter_elt)
+#define FILTER_LEVEL(filter_level) ((FilterLevel *)filter_level)
+
+#define GET_CHILD_ITER(tree_model_filter,child_iter,filter_iter) gtk_tree_model_filter_convert_iter_to_child_iter(GTK_TREE_MODEL_FILTER (tree_model_filter), child_iter, filter_iter);
+
+
+static void gtk_tree_model_filter_init (GtkTreeModelFilter *tree_model_filter);
+static void gtk_tree_model_filter_class_init (GtkTreeModelFilterClass *tree_model_filter_class);
+static void gtk_tree_model_filter_tree_model_init (GtkTreeModelIface *iface);
+static void
+gtk_tree_model_filter_finalize (GObject *object);
+static void
+gtk_tree_model_filter_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void
+gtk_tree_model_filter_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+static void
+gtk_tree_model_filter_row_changed (GtkTreeModel *model,
+ GtkTreePath *start_path,
+ GtkTreeIter *start_iter,
+ gpointer data);
+static void
+gtk_tree_model_filter_row_inserted (GtkTreeModel *model,
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ gpointer data);
+static void
+gtk_tree_model_filter_row_has_child_toggled (GtkTreeModel *model,
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ gpointer data);
+static void
+gtk_tree_model_filter_row_deleted (GtkTreeModel *model,
+ GtkTreePath *path,
+ gpointer data);
+static void
+gtk_tree_model_filter_rows_reordered (GtkTreeModel *s_model,
+ GtkTreePath *s_path,
+ GtkTreeIter *s_iter,
+ gint *new_order,
+ gpointer data);
+
+/* TreeModel interface */
+static guint gtk_tree_model_filter_get_flags (GtkTreeModel *tree_model);
+static gint gtk_tree_model_filter_get_n_columns (GtkTreeModel *tree_model);
+static GType gtk_tree_model_filter_get_column_type (GtkTreeModel *tree_model,
+ gint index);
+static gboolean gtk_tree_model_filter_get_iter (GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ GtkTreePath *path);
+static GtkTreePath *gtk_tree_model_filter_get_path (GtkTreeModel *tree_model,
+ GtkTreeIter *iter);
+static void gtk_tree_model_filter_get_value (GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ gint column,
+ GValue *value);
+static gboolean gtk_tree_model_filter_iter_next (GtkTreeModel *tree_model,
+ GtkTreeIter *iter);
+static gboolean gtk_tree_model_filter_iter_children (GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ GtkTreeIter *parent);
+static gboolean gtk_tree_model_filter_iter_has_child (GtkTreeModel *tree_model,
+ GtkTreeIter *iter);
+static gint gtk_tree_model_filter_iter_n_children (GtkTreeModel *tree_model,
+ GtkTreeIter *iter);
+static gboolean gtk_tree_model_filter_iter_nth_child (GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ GtkTreeIter *parent,
+ gint n);
+static gboolean gtk_tree_model_filter_iter_parent (GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ GtkTreeIter *child);
+static void gtk_tree_model_filter_ref_node (GtkTreeModel *tree_model,
+ GtkTreeIter *iter);
+static void gtk_tree_model_filter_unref_node (GtkTreeModel *tree_model,
+ GtkTreeIter *iter);
+
+/* Private functions */
+static void gtk_tree_model_filter_build_level (GtkTreeModelFilter *tree_model_filter,
+ FilterLevel *parent_level,
+ FilterElt *parent_elt);
+static void gtk_tree_model_filter_free_level (GtkTreeModelFilter *tree_model_filter,
+ FilterLevel *filter_level);
+static void gtk_tree_model_filter_increment_stamp (GtkTreeModelFilter *tree_model_filter);
+static gboolean gtk_tree_model_filter_visible (GtkTreeModelFilter *tree_model_filter,
+ GtkTreeIter *child_iter);
+static GtkTreePath *gtk_tree_model_filter_elt_get_path (FilterLevel *level,
+ FilterElt *elt,
+ GtkTreePath *root);
+GtkTreeModel * gtk_tree_model_filter_get_model (GtkTreeModelFilter *tree_model);
+static void gtk_tree_model_filter_set_model (GtkTreeModelFilter *tree_model_filter,
+ GtkTreeModel *child_model);
+static void gtk_tree_model_filter_set_filter_column (GtkTreeModelFilter *tree_model_filter,
+ gint filter_column);
+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 *tree_model_filter,
+ GtkTreePath *child_path,
+ gboolean build_levels,
+ gboolean fetch_childs);
+
+
+static void
+gtk_tree_model_filter_remove (GtkTreeModelFilter *filter,
+ GtkTreeIter *iter,
+ gboolean emit_signal);
+static gboolean
+gtk_tree_model_filter_fetch_child (GtkTreeModelFilter *filter,
+ FilterLevel *level,
+ gint offset);
+static void
+gtk_tree_model_filter_row_back_child_updater (GtkTreeModelFilter *filter,
+ FilterLevel *level,
+ FilterElt *elt);
+static void gtk_tree_path_add (GtkTreePath *path1, GtkTreePath *path2);
+static GtkTreePath * gtk_tree_path_apply_root (GtkTreePath *path, GtkTreePath *root);
+
+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;
+}
+
+static void
+gtk_tree_model_filter_init (GtkTreeModelFilter *tree_model_filter)
+{
+ tree_model_filter->filter_column = -1;
+ tree_model_filter->filter_func = NULL;
+ tree_model_filter->user_data = NULL;
+ tree_model_filter->stamp = 0;
+ tree_model_filter->zero_ref_count = 0;
+ tree_model_filter->root = NULL;
+}
+
+static void
+gtk_tree_model_filter_class_init (GtkTreeModelFilterClass *tree_model_filter_class)
+{
+ GObjectClass *object_class;
+
+ object_class = (GObjectClass *) tree_model_filter_class;
+ parent_class = g_type_class_peek_parent (tree_model_filter_class);
+
+ object_class->set_property = gtk_tree_model_filter_set_property;
+ object_class->get_property = gtk_tree_model_filter_get_property;
+
+ object_class->finalize = gtk_tree_model_filter_finalize;
+
+ /* Properties */
+ g_object_class_install_property (object_class,
+ PROP_MODEL,
+ g_param_spec_object ("model",
+ _("TreeModelFilter model"),
+ _("The model for the TreeModelFilter to filter"),
+ GTK_TYPE_TREE_MODEL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+ /* FIXME: add some kind of property for the filter_func and filter_column*/
+}
+
+static void
+gtk_tree_model_filter_tree_model_init (GtkTreeModelIface *iface)
+{
+ iface->get_flags = gtk_tree_model_filter_get_flags;
+ iface->get_n_columns = gtk_tree_model_filter_get_n_columns;
+ iface->get_column_type = gtk_tree_model_filter_get_column_type;
+ iface->get_iter = gtk_tree_model_filter_get_iter;
+ iface->get_path = gtk_tree_model_filter_get_path;
+ iface->get_value = gtk_tree_model_filter_get_value;
+ iface->iter_next = gtk_tree_model_filter_iter_next;
+ iface->iter_children = gtk_tree_model_filter_iter_children;
+ iface->iter_has_child = gtk_tree_model_filter_iter_has_child;
+ iface->iter_n_children = gtk_tree_model_filter_iter_n_children;
+ iface->iter_nth_child = gtk_tree_model_filter_iter_nth_child;
+ iface->iter_parent = gtk_tree_model_filter_iter_parent;
+ iface->ref_node = gtk_tree_model_filter_ref_node;
+ iface->unref_node = gtk_tree_model_filter_unref_node;
+}
+
+/**
+ * gtk_tree_model_filter_new_with_model:
+ * @child_model: A #GtkTreeModel
+ * @filter_column: column in the child model which contains filter information
+ * @root: the virtual root, or %NULL for the regular root.
+ *
+ * Creates a new #GtkTreeModel, with @child_model as the child_model.
+ *
+ * Return value: A new #GtkTreeModel.
+ */
+GtkTreeModel *
+gtk_tree_model_filter_new_with_model (GtkTreeModel *child_model,
+ gint filter_column,
+ GtkTreePath *root)
+{
+ GtkTreeModel *retval;
+
+ 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));
+
+ gtk_tree_model_filter_set_model (GTK_TREE_MODEL_FILTER (retval), child_model);
+ gtk_tree_model_filter_set_filter_column (GTK_TREE_MODEL_FILTER (retval), filter_column);
+ gtk_tree_model_filter_set_root (GTK_TREE_MODEL_FILTER (retval), root);
+
+ return retval;
+}
+
+/* GObject callbacks */
+static void
+gtk_tree_model_filter_finalize (GObject *object)
+{
+ GtkTreeModelFilter *tree_model_filter = (GtkTreeModelFilter *) object;
+
+ gtk_tree_model_filter_set_model (tree_model_filter, NULL);
+
+ if (tree_model_filter->virtual_root)
+ gtk_tree_path_free (tree_model_filter->virtual_root);
+
+ if (tree_model_filter->root)
+ gtk_tree_model_filter_free_level (tree_model_filter,
+ tree_model_filter->root);
+
+ /* must chain up */
+ parent_class->finalize (object);
+}
+
+static void
+gtk_tree_model_filter_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GtkTreeModelFilter *tree_model_filter = GTK_TREE_MODEL_FILTER (object);
+
+ switch (prop_id)
+ {
+ case PROP_MODEL:
+ gtk_tree_model_filter_set_model (tree_model_filter, g_value_get_object (value));
+ break;
+ case PROP_FILTER_COLUMN:
+ gtk_tree_model_filter_set_filter_column (tree_model_filter, g_value_get_int (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gtk_tree_model_filter_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GtkTreeModelFilter *tree_model_filter = GTK_TREE_MODEL_FILTER (object);
+
+ switch (prop_id)
+ {
+ case PROP_MODEL:
+ g_value_set_object (value, gtk_tree_model_filter_get_model(tree_model_filter));
+ break;
+ case PROP_FILTER_COLUMN:
+ g_value_set_int (value, tree_model_filter->filter_column);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+/* helpers */
+static void
+gtk_tree_model_filter_row_back_child_updater (GtkTreeModelFilter *filter,
+ FilterLevel *level,
+ FilterElt *elt)
+{
+ int i, len;
+ GArray *a = NULL;
+ GtkTreeIter childs;
+
+#ifdef VERBOSE
+ g_print ("row back child updater v1.0\n");
+
+ if (!elt->visible)
+ g_print ("elt not visible, bailing out\n");
+#endif
+
+ if (!elt->visible)
+ return;
+
+ if (elt->children)
+ a = elt->children->array;
+
+ {
+ GtkTreeIter iter;
+ GtkTreeIter foo;
+
+ iter.stamp = filter->stamp;
+ iter.user_data = level;
+ iter.user_data2 = elt;
+
+ gtk_tree_model_filter_convert_iter_to_child_iter (filter, &foo, &iter);
+
+ len = gtk_tree_model_iter_n_children (filter->child_model, &foo);
+ gtk_tree_model_iter_children (filter->child_model, &childs, &foo);
+ }
+
+ /*
+ {
+ GtkTreeIter iter;
+ GtkTreeIter foo;
+ GtkTreePath *path = gtk_tree_model_filter_elt_get_path (level, elt,
+ NULL);
+
+ gtk_tree_model_get_iter (GTK_TREE_MODEL (filter), &iter, path);
+ gtk_tree_model_filter_convert_iter_to_child_iter (filter, &foo, &iter);
+
+ len = gtk_tree_model_iter_n_children (filter->child_model, &foo);
+ gtk_tree_path_free (path);
+
+ gtk_tree_model_iter_children (filter->child_model, &childs, &foo);
+ }
+ */
+
+ for (i = 0; i < len; i++)
+ {
+ gint j, index = -1;
+ GValue val = {0,};
+
+ if (a)
+ {
+ for (j = 0; j < a->len; j++)
+ if (g_array_index (a, FilterElt, j).offset == i)
+ {
+ index = i;
+ break;
+ }
+ }
+
+ gtk_tree_model_get_value (filter->child_model, &childs,
+ filter->filter_column, &val);
+
+ if (g_value_get_boolean (&val))
+ {
+ /* insert row */
+ if (index == -1)
+ {
+ FilterElt *e;
+ GtkTreePath *path;
+ GtkTreeIter it, tmp;
+
+ /* bring it back in */
+#ifdef VERBOSE
+ g_print ("need to bring row back in...\n");
+#endif
+ if (!a && !elt->children)
+ {
+ FilterLevel *new_level = NULL;
+ FilterLevel *parent_level = level;
+ FilterElt *parent_elt = elt;
+
+ new_level = g_new (FilterLevel, 1);
+ new_level->array = g_array_sized_new (FALSE, FALSE, sizeof (FilterElt), 0);
+ new_level->ref_count = 0;
+ new_level->parent_elt = elt;
+ new_level->parent_level = level;
+
+ elt->children = new_level;
+ a = elt->children->array;
+
+ /* increase the count of zero ref_counts */
+ while (parent_level)
+ {
+ // parent_elt->zero_ref_count++;
+ parent_elt->ref_count++;
+ parent_level->ref_count++;
+
+ parent_elt = parent_level->parent_elt;
+ parent_level = parent_level->parent_level;
+ }
+ //filter->zero_ref_count++;
+ }
+
+ gtk_tree_model_filter_fetch_child (filter,
+ elt->children, i);
+
+ for (j = 0; j < a->len; j++)
+ if (g_array_index (a, FilterElt, j).offset == i)
+ {
+ index = j;
+ break;
+ }
+
+ if (index == -1)
+ {
+ g_warning ("gtk_tree_model_filter_fetch_child failed!\n");
+ return;
+ }
+
+ e = &g_array_index (a, FilterElt, index);
+ e->visible = FALSE;
+
+ gtk_tree_model_filter_increment_stamp (filter);
+
+ it.stamp = filter->stamp;
+ it.user_data = elt->children;
+ it.user_data2 = e;
+
+ path = gtk_tree_model_get_path (GTK_TREE_MODEL (filter), &it);
+ /*
+ path = gtk_tree_model_filter_elt_get_path (elt->children, e,
+ NULL);
+ gtk_tree_model_filter_increment_stamp (filter);
+ gtk_tree_model_get_iter (GTK_TREE_MODEL (filter), &it, path);
+ */
+ gtk_tree_model_row_inserted (GTK_TREE_MODEL (filter), path, &it);
+ gtk_tree_path_free (path);
+
+ if (gtk_tree_model_iter_children (GTK_TREE_MODEL (filter), &tmp, &it))
+ gtk_tree_model_filter_row_back_child_updater (filter,
+ elt->children, e);
+ }
+ else
+ {
+ FilterElt *e = &g_array_index (a, FilterElt, index);
+ GtkTreePath *path;
+ GtkTreeIter i, tmp;
+
+ if (!e->visible)
+ {
+ e->visible = FALSE;
+
+#ifdef VERBOSE
+ g_print ("-> row back\n");
+#endif
+
+ gtk_tree_model_filter_increment_stamp (filter);
+
+ i.stamp = filter->stamp;
+ i.user_data = elt->children;
+ i.user_data2 = e;
+
+ path = gtk_tree_model_get_path (GTK_TREE_MODEL (filter), &i);
+ gtk_tree_model_row_inserted (GTK_TREE_MODEL (filter), path, &i);
+ gtk_tree_path_free (path);
+
+ /*
+ path = gtk_tree_model_filter_elt_get_path (elt->children, e,
+ NULL);
+ gtk_tree_model_filter_increment_stamp (filter);
+ gtk_tree_model_get_iter (GTK_TREE_MODEL (filter), &i, path);
+ gtk_tree_model_row_inserted (GTK_TREE_MODEL (filter), path, &i);
+ gtk_tree_path_free (path);
+ */
+
+ if (gtk_tree_model_iter_children (GTK_TREE_MODEL (filter), &tmp, &i))
+ gtk_tree_model_filter_row_back_child_updater (filter,
+ elt->children, e);
+ }
+ }
+ }
+ else
+ {
+ /* remove the iter */
+ if (index != -1)
+ {
+ FilterElt *e = &g_array_index (a, FilterElt, index);
+ GtkTreeIter i;
+
+#ifdef VERBOSE
+ g_print ("removing iter in silence\n");
+#endif
+
+ /*
+ path = gtk_tree_model_filter_elt_get_path (elt->children, e,
+ NULL);
+ gtk_tree_model_get_iter (GTK_TREE_MODEL (filter), &i, path);
+ gtk_tree_path_free (path);
+ */
+
+ i.stamp = filter->stamp;
+ i.user_data = elt->children;
+ i.user_data2 = e;
+
+ gtk_tree_model_filter_remove (filter, &i, FALSE);
+ }
+ }
+
+ gtk_tree_model_iter_next (filter->child_model, &childs);
+ }
+}
+
+static void
+gtk_tree_model_filter_remove (GtkTreeModelFilter *filter,
+ GtkTreeIter *iter,
+ gboolean emit_signal)
+{
+ FilterElt *elt, *parent;
+ FilterLevel *level, *parent_level;
+ gint offset, i, length;
+
+ 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 ("------- remove \n");
+#endif
+
+ /* ref couting */
+ while (elt->ref_count > 0)
+ gtk_tree_model_filter_unref_node (GTK_TREE_MODEL (filter), iter);
+
+ if (emit_signal)
+ {
+ GtkTreePath *path;
+
+ path = gtk_tree_model_get_path (GTK_TREE_MODEL (filter), iter);
+ gtk_tree_model_filter_increment_stamp (filter);
+ gtk_tree_model_row_deleted (GTK_TREE_MODEL (filter), path);
+ gtk_tree_path_free (path);
+ }
+
+ if (length == 1 && emit_signal && iter->user_data != filter->root)
+ {
+ /* the code above destroyed the level */
+ goto emit_has_child_toggled;
+ }
+
+ 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++)
+ {
+ 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 */
+ if ((parent && parent->children && parent->children->array->len <= 1)
+ || (length == 1 && emit_signal && iter->user_data != filter->root))
+ {
+ /* we're about to remove the latest child */
+ 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 ("emit has_child_toggled (in filter_remove)\n");
+
+ puts (gtk_tree_path_to_string (ppath));
+#endif
+ gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (filter),
+ ppath, &piter);
+ gtk_tree_path_free (ppath);
+ }
+}
+
+/* TreeModel signals */
+static void
+gtk_tree_model_filter_row_changed (GtkTreeModel *s_model,
+ GtkTreePath *start_path,
+ GtkTreeIter *start_iter,
+ gpointer data)
+{
+ GtkTreeModelFilter *filter = GTK_TREE_MODEL_FILTER (data);
+ GtkTreeIter iter;
+ GtkTreeIter siter;
+ GtkTreePath *path;
+
+ FilterElt *elt;
+ FilterLevel *level;
+ gint offset;
+
+ gboolean new;
+ gboolean free_start_path = FALSE;
+
+ g_return_if_fail (start_path != NULL || start_iter != NULL);
+
+ if (!start_path)
+ {
+ start_path = gtk_tree_model_get_path (s_model, start_iter);
+ free_start_path = TRUE;
+ }
+
+ if (!start_iter)
+ gtk_tree_model_get_iter (s_model, &siter, start_path);
+ else
+ siter = *start_iter;
+
+ path = gtk_real_tree_model_filter_convert_child_path_to_path (filter, start_path, TRUE, TRUE);
+ if (!path)
+ {
+ if (free_start_path)
+ gtk_tree_path_free (start_path);
+#ifdef VERBOSE
+ g_print ("couldn't convert path, bailing out\n");
+#endif
+ return;
+ }
+
+#ifdef VERBOSE
+ g_print ("_row_changed handling %s\n", gtk_tree_path_to_string (path));
+#endif
+
+ gtk_tree_model_get_iter (GTK_TREE_MODEL (filter), &iter, path);
+ gtk_tree_path_free (path);
+ if (free_start_path)
+ gtk_tree_path_free (start_path);
+
+ {
+ GValue val = {0, };
+
+ gtk_tree_model_get_value (s_model, &siter, filter->filter_column, &val);
+ new = g_value_get_boolean (&val);
+ g_value_unset (&val);
+ }
+
+ level = FILTER_LEVEL (iter.user_data);
+ elt = FILTER_ELT (iter.user_data2);
+ offset = elt->offset;
+
+ if (elt->visible == TRUE && new == FALSE)
+ {
+#ifdef VERBOSE
+ g_print ("visible to false -> delete row\n");
+#endif
+ gtk_tree_model_filter_remove (filter, &iter, TRUE);
+ }
+ else if (elt->visible == FALSE && new == TRUE)
+ {
+ GtkTreePath *path = gtk_tree_model_get_path (GTK_TREE_MODEL (filter), &iter);
+ GtkTreeIter tmp;
+
+#ifdef VERBOSE
+ g_print ("visible to true -> insert row\n");
+ puts (gtk_tree_path_to_string (path));
+#endif
+ elt->visible = TRUE;
+
+ gtk_tree_model_filter_increment_stamp (filter);
+ gtk_tree_model_row_inserted (GTK_TREE_MODEL (filter), path, &iter);
+
+ if (gtk_tree_model_iter_children (s_model, &tmp, start_iter))
+ gtk_tree_model_filter_row_back_child_updater (filter, level, elt);
+
+ gtk_tree_path_free (path);
+ }
+ else if (elt->visible == FALSE && new == FALSE)
+ {
+#ifdef VERBOSE
+ g_print ("remove iter in silence\n");
+#endif
+ gtk_tree_model_filter_remove (filter, &iter, FALSE);
+ }
+ else
+ {
+ GtkTreePath *path = gtk_tree_model_get_path (GTK_TREE_MODEL (filter), &iter);
+ GtkTreeIter tmp;
+#ifdef VERBOSE
+ g_print ("visible didn't change -- pass row_changed\n");
+#endif
+ gtk_tree_model_row_changed (GTK_TREE_MODEL (filter), path, &iter);
+
+ if (gtk_tree_model_iter_children (s_model, &tmp, start_iter)
+ && elt->visible)
+ gtk_tree_model_filter_row_back_child_updater (filter, level, elt);
+
+ gtk_tree_path_free (path);
+ }
+}
+
+static void
+gtk_tree_model_filter_row_inserted (GtkTreeModel *s_model,
+ GtkTreePath *s_path,
+ GtkTreeIter *s_iter,
+ gpointer data)
+{
+ GtkTreeModelFilter *filter = GTK_TREE_MODEL_FILTER (data);
+ GtkTreePath *path;
+ GtkTreePath *real_path;
+ GtkTreeIter iter;
+
+ GtkTreeIter real_s_iter;
+
+ FilterElt *elt;
+ FilterLevel *level;
+ FilterLevel *parent_level;
+
+ gint i = 0, offset, index;
+
+ gboolean free_s_path = FALSE;
+
+ g_return_if_fail (s_path != NULL || s_iter != NULL);
+
+ if (!s_path)
+ {
+ s_path = gtk_tree_model_get_path (s_model, s_iter);
+ free_s_path = TRUE;
+ }
+
+ if (s_iter)
+ real_s_iter = *s_iter;
+ else
+ gtk_tree_model_get_iter (s_model, &real_s_iter, s_path);
+
+ /* only insert the row if it's visible */
+ if (!gtk_tree_model_filter_visible (filter, s_iter))
+ goto done;
+
+ if (!filter->root)
+ {
+ gtk_tree_model_filter_build_level (filter, NULL, NULL);
+ /* that already put the inserted iter in the level */
+
+ goto done_and_submit;
+ }
+
+ parent_level = level = FILTER_LEVEL (filter->root);
+
+ /* subtract virtual root if needed */
+ if (filter->virtual_root)
+ {
+ real_path = gtk_tree_path_apply_root (s_path, filter->virtual_root);
+ /* not our root */
+ if (!real_path)
+ goto done;
+ }
+ else
+ real_path = gtk_tree_path_copy (s_path);
+
+ /*
+ if (free_s_path)
+ gtk_tree_path_free (s_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;
+
+ if (level->array->len < gtk_tree_path_get_indices (real_path)[i])
+ {
+ g_warning ("A node was inserted with a parent that's not in the tree.\nThis possibly means that a GtkTreeModel inserted a child node before the parent was insterted.");
+ 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 (s_path)[i])
+ {
+ elt = &g_array_index (level->array, FilterElt, j);
+ break;
+ }
+
+ if (!elt)
+ /* parent was probably 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)
+ {
+#ifdef VERBOSE
+ g_print ("has_child_toggled in row_inserted\n");
+#endif
+ 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 */
+ i = 0;
+ offset = gtk_tree_path_get_indices (real_path)[gtk_tree_path_get_depth (real_path) - 1];
+
+ if (gtk_tree_model_filter_visible (filter, &real_s_iter))
+ {
+ FilterElt felt;
+
+ if (GTK_TREE_MODEL_FILTER_CACHE_CHILD_ITERS (filter))
+ felt.iter = real_s_iter;
+ felt.offset = offset;
+ felt.zero_ref_count = 0;
+ felt.ref_count = 0;
+ felt.visible = FALSE;
+ felt.children = NULL;
+
+ for (i = 0; i < level->array->len; i++)
+ if (g_array_index (level->array, FilterElt, i).offset > offset)
+ break;
+
+/** if (i == 0)
+ g_array_prepend_val (level->array, felt);
+ else if (i == level->array->len)
+ g_array_append_val (level->array, felt);
+ else***/
+ g_array_insert_val (level->array, i, felt);
+ index = i;
+ }
+
+ 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;
+ }
+
+ if (!gtk_tree_model_filter_visible (filter, &real_s_iter))
+ goto done;
+
+ done_and_submit:
+ /* s_path here! AND NOT real_path, because this function does
+ * root subtraction */
+ path = gtk_real_tree_model_filter_convert_child_path_to_path (filter,
+ s_path,
+ FALSE, TRUE);
+ if (!path)
+ return;
+
+ gtk_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);
+
+ done:
+ if (free_s_path)
+ gtk_tree_path_free (s_path);
+
+ return;
+}
+
+static void
+gtk_tree_model_filter_row_has_child_toggled (GtkTreeModel *s_model,
+ GtkTreePath *s_path,
+ GtkTreeIter *s_iter,
+ gpointer data)
+{
+ GtkTreeModelFilter *filter = GTK_TREE_MODEL_FILTER (data);
+ GtkTreePath *path;
+ GtkTreeIter iter;
+
+ g_return_if_fail (s_path != NULL && s_iter != NULL);
+
+ /* FIXME: this code hasn't been tested and looks broken ... I need
+ * to investigate it */
+
+ if (!gtk_tree_model_filter_visible (filter, s_iter))
+ return;
+
+ path = gtk_real_tree_model_filter_convert_child_path_to_path (filter, s_path, FALSE, TRUE);
+ if (!path)
+ return;
+
+ gtk_tree_model_get_iter (GTK_TREE_MODEL (data), &iter, path);
+#ifdef VERBOSE
+ if (!FILTER_ELT (iter.user_data2)->visible)
+ g_warning ("Visible flag mismatch\n");
+ g_print ("has child toggled pass on\n");
+#endif
+ gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (data), path, &iter);
+
+ gtk_tree_path_free (path);
+}
+
+static void
+gtk_tree_model_filter_row_deleted (GtkTreeModel *s_model,
+ GtkTreePath *s_path,
+ gpointer data)
+{
+ GtkTreeModelFilter *filter = GTK_TREE_MODEL_FILTER (data);
+ GtkTreePath *path;
+ GtkTreeIter iter;
+ FilterElt *elt;
+ FilterLevel *level;
+ gint offset;
+ gboolean visible;
+ gint i;
+
+ g_return_if_fail (s_path != NULL);
+
+ path = gtk_real_tree_model_filter_convert_child_path_to_path (filter, s_path, FALSE, TRUE);
+ if (!path)
+ return;
+
+ 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;
+ visible = elt->visible;
+
+ if (visible)
+ {
+ if (level->ref_count == 0 && level != filter->root)
+ {
+ gtk_tree_model_filter_increment_stamp (filter);
+ gtk_tree_model_row_deleted (GTK_TREE_MODEL (data), path);
+
+ gtk_tree_path_free (path);
+ return;
+ }
+
+ gtk_tree_model_filter_increment_stamp (filter);
+ gtk_tree_model_row_deleted (GTK_TREE_MODEL (data), path);
+
+ while (elt->ref_count > 0)
+ gtk_tree_model_filter_unref_node (GTK_TREE_MODEL (data), &iter);
+ }
+
+ /* 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
+gtk_tree_model_filter_rows_reordered (GtkTreeModel *s_model,
+ GtkTreePath *s_path,
+ GtkTreeIter *s_iter,
+ gint *new_order,
+ gpointer data)
+{
+ FilterElt *elt;
+ FilterLevel *level;
+ GtkTreeModelFilter *filter = GTK_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);
+
+ if (s_path == NULL || gtk_tree_path_get_indices (s_path) == NULL)
+ {
+ if (filter->root == NULL)
+ return;
+
+ length = gtk_tree_model_iter_n_children (s_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;
+
+ /* FIXME: do path reordering if s_path->depth < vroot->depth */
+
+ path = gtk_real_tree_model_filter_convert_child_path_to_path (filter, s_path, FALSE, FALSE);
+ if (!path && filter->virtual_root &&
+ gtk_tree_path_compare (s_path, filter->virtual_root))
+ return;
+
+ if (!path)
+ {
+ /* root level mode */
+ if (!s_iter)
+ gtk_tree_model_get_iter (s_model, s_iter, s_path);
+ length = gtk_tree_model_iter_n_children (s_model, s_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;
+
+ GET_CHILD_ITER (filter, &child_iter, &iter);
+ length = gtk_tree_model_iter_n_children (s_model, &child_iter);
+ }
+ }
+
+ if (level->array->len < 1)
+ return;
+
+ /* note: we do not bail out here if level->array->len < 2, like
+ * the 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);
+
+#ifdef VERBOSE
+ g_print ("reorder: running with length = %d\n", length);
+#endif
+
+ 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++;
+ }
+
+#ifdef VERBOSE
+ g_print ("length: old %d, new %d\n", level->array->len,
+ new_array->len);
+#endif
+
+ g_array_free (level->array, TRUE);
+ level->array = new_array;
+
+#ifdef VERBOSE
+ for (j = 0; j < length; j++)
+ g_print ("%3d", new_order[j]);
+ g_print ("\n");
+
+ for (j = 0; j < level->array->len; j++)
+ g_print ("%3d", tmp_array[j]);
+ g_print ("\n");
+
+ for (j = 0; j < level->array->len; j++)
+ g_print ("%3d", g_array_index (level->array, FilterElt, j).offset);
+ g_print ("\n");
+#endif
+
+ /* 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 implemtation */
+static guint
+gtk_tree_model_filter_get_flags (GtkTreeModel *tree_model)
+{
+ g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (tree_model), 0);
+
+ return 0;
+}
+
+static gint
+gtk_tree_model_filter_get_n_columns (GtkTreeModel *tree_model)
+{
+ GtkTreeModelFilter *tree_model_filter = (GtkTreeModelFilter *) tree_model;
+
+ g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (tree_model), 0);
+
+ if (tree_model_filter->child_model == 0)
+ return 0;
+
+ return gtk_tree_model_get_n_columns (tree_model_filter->child_model);
+}
+
+static GType
+gtk_tree_model_filter_get_column_type (GtkTreeModel *tree_model,
+ gint index)
+{
+ g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (tree_model), G_TYPE_INVALID);
+ g_return_val_if_fail (GTK_TREE_MODEL_FILTER (tree_model)->child_model != NULL, G_TYPE_INVALID);
+
+ return gtk_tree_model_get_column_type (GTK_TREE_MODEL_FILTER (tree_model)->child_model, index);
+}
+
+static gboolean
+gtk_tree_model_filter_get_iter (GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ GtkTreePath *path)
+{
+ GtkTreeModelFilter *tree_model_filter;
+ gint *indices;
+ FilterLevel *level;
+ gint depth, i;
+
+ g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (tree_model), FALSE);
+ g_return_val_if_fail (GTK_TREE_MODEL_FILTER (tree_model)->child_model != NULL, FALSE);
+
+ tree_model_filter = (GtkTreeModelFilter *) tree_model;
+ indices = gtk_tree_path_get_indices (path);
+
+ if (tree_model_filter->root == NULL)
+ gtk_tree_model_filter_build_level (tree_model_filter, NULL, NULL);
+ level = FILTER_LEVEL (tree_model_filter->root);
+
+ depth = gtk_tree_path_get_depth (path);
+ if (depth == 0)
+ return FALSE;
+
+ for (i = 0; i < depth - 1; i++)
+ {
+ if ((level == NULL) ||
+ (level->array->len < indices[i]))
+ return FALSE;
+
+ if (g_array_index (level->array, FilterElt, indices[i]).children == NULL)
+ gtk_tree_model_filter_build_level (tree_model_filter, level, &g_array_index (level->array, FilterElt, indices[i]));
+ level = g_array_index (level->array, FilterElt, indices[i]).children;
+ }
+
+ if (level == NULL)
+ return FALSE;
+ iter->stamp = tree_model_filter->stamp;
+ iter->user_data = level;
+ iter->user_data2 = &g_array_index (level->array, FilterElt, indices[depth - 1]);
+ return TRUE;
+}
+
+static GtkTreePath *
+gtk_tree_model_filter_get_path (GtkTreeModel *tree_model,
+ GtkTreeIter *iter)
+{
+ GtkTreePath *retval;
+ FilterLevel *level;
+ FilterElt *elt;
+
+ g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (tree_model), NULL);
+ g_return_val_if_fail (GTK_TREE_MODEL_FILTER (tree_model)->child_model != NULL, NULL);
+ g_return_val_if_fail (GTK_TREE_MODEL_FILTER (tree_model)->stamp == iter->stamp, NULL);
+
+ retval = gtk_tree_path_new ();
+ level = iter->user_data;
+ elt = iter->user_data2;
+ while (level != NULL)
+ {
+ gtk_tree_path_prepend_index (retval, elt - (FilterElt *)level->array->data);
+ elt = level->parent_elt;
+ level = level->parent_level;
+ }
+
+ return retval;
+}
+
+static void
+gtk_tree_model_filter_get_value (GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ gint column,
+ GValue *value)
+{
+ GtkTreeIter child_iter;
+
+ g_return_if_fail (GTK_IS_TREE_MODEL_FILTER (tree_model));
+ g_return_if_fail (GTK_TREE_MODEL_FILTER (tree_model)->child_model != NULL);
+ g_return_if_fail (GTK_TREE_MODEL_FILTER (tree_model)->stamp == iter->stamp);
+
+ GET_CHILD_ITER (tree_model, &child_iter, iter);
+ gtk_tree_model_get_value (GTK_TREE_MODEL_FILTER (tree_model)->child_model,
+ &child_iter, column, value);
+}
+
+static gboolean
+gtk_tree_model_filter_iter_next (GtkTreeModel *tree_model,
+ GtkTreeIter *iter)
+{
+ FilterLevel *level;
+ FilterElt *elt;
+
+ g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (tree_model), FALSE);
+ g_return_val_if_fail (GTK_TREE_MODEL_FILTER (tree_model)->child_model != NULL, FALSE);
+ g_return_val_if_fail (GTK_TREE_MODEL_FILTER (tree_model)->stamp == iter->stamp, FALSE);
+
+ level = iter->user_data;
+ elt = iter->user_data2;
+
+ if (elt - (FilterElt *)level->array->data >= level->array->len - 1)
+ {
+ iter->stamp = 0;
+ return FALSE;
+ }
+ iter->user_data2 = elt + 1;
+
+ return TRUE;
+}
+
+static gboolean
+gtk_tree_model_filter_iter_children (GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ GtkTreeIter *parent)
+{
+ GtkTreeModelFilter *tree_model_filter = (GtkTreeModelFilter *) tree_model;
+ FilterLevel *level;
+
+ iter->stamp = 0;
+ g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (tree_model), FALSE);
+ g_return_val_if_fail (tree_model_filter->child_model != NULL, FALSE);
+ if (parent) g_return_val_if_fail (tree_model_filter->stamp == parent->stamp, FALSE);
+
+ if (parent == NULL)
+ {
+ if (tree_model_filter->root == NULL)
+ gtk_tree_model_filter_build_level (tree_model_filter, NULL, NULL);
+ if (tree_model_filter->root == NULL)
+ return FALSE;
+
+ level = tree_model_filter->root;
+ iter->stamp = tree_model_filter->stamp;
+ iter->user_data = level;
+ iter->user_data2 = level->array->data;
+ }
+ else
+ {
+ if (((FilterElt *)parent->user_data2)->children == NULL)
+ gtk_tree_model_filter_build_level (tree_model_filter,
+ (FilterLevel *)parent->user_data,
+ (FilterElt *)parent->user_data2);
+ if (((FilterElt *)parent->user_data2)->children == NULL)
+ return FALSE;
+ iter->stamp = tree_model_filter->stamp;
+ iter->user_data = ((FilterElt *)parent->user_data2)->children;
+ iter->user_data2 = ((FilterLevel *)iter->user_data)->array->data;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+gtk_tree_model_filter_iter_has_child (GtkTreeModel *tree_model,
+ GtkTreeIter *iter)
+{
+ GtkTreeIter child_iter;
+ GtkTreeModelFilter *filter;
+ FilterElt *elt;
+
+ g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (tree_model), FALSE);
+ g_return_val_if_fail (GTK_TREE_MODEL_FILTER (tree_model)->child_model != NULL, FALSE);
+ g_return_val_if_fail (GTK_TREE_MODEL_FILTER (tree_model)->stamp == iter->stamp, FALSE);
+
+ filter = GTK_TREE_MODEL_FILTER (tree_model);
+
+ GET_CHILD_ITER (tree_model, &child_iter, iter);
+ elt = FILTER_ELT (iter->user_data2);
+
+ if (!elt->children
+ && gtk_tree_model_iter_has_child (filter->child_model, &child_iter))
+ gtk_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
+gtk_tree_model_filter_iter_n_children (GtkTreeModel *tree_model,
+ GtkTreeIter *iter)
+{
+ GtkTreeIter child_iter;
+ GtkTreeModelFilter *filter;
+ FilterElt *elt;
+
+ g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (tree_model), 0);
+ g_return_val_if_fail (GTK_TREE_MODEL_FILTER (tree_model)->child_model != NULL, 0);
+ if (iter)
+ g_return_val_if_fail (GTK_TREE_MODEL_FILTER (tree_model)->stamp == iter->stamp, 0);
+
+ filter = GTK_TREE_MODEL_FILTER (tree_model);
+
+ if (!iter)
+ {
+ if (!GTK_TREE_MODEL_FILTER (tree_model)->root)
+ gtk_tree_model_filter_build_level (GTK_TREE_MODEL_FILTER (tree_model), NULL, NULL);
+
+#ifdef VERBOSE
+ g_print ("- length %d\n", FILTER_LEVEL (GTK_TREE_MODEL_FILTER (tree_model)->root)->array->len);
+#endif
+
+ return FILTER_LEVEL (GTK_TREE_MODEL_FILTER (tree_model)->root)->array->len;
+ }
+
+ elt = FILTER_ELT (iter->user_data2);
+ GET_CHILD_ITER (tree_model, &child_iter, iter);
+
+ if (!elt->children &&
+ gtk_tree_model_iter_has_child (filter->child_model, &child_iter))
+ gtk_tree_model_filter_build_level (filter, FILTER_LEVEL (iter->user_data),
+ elt);
+
+ if (elt->children && elt->children->array->len)
+ return elt->children->array->len;
+
+
+ return 0;
+}
+
+static gboolean
+gtk_tree_model_filter_iter_nth_child (GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ GtkTreeIter *parent,
+ gint n)
+{
+ FilterLevel *level;
+ /* We have this for the iter == parent case */
+ GtkTreeIter children;
+
+ g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (tree_model), FALSE);
+ if (parent) g_return_val_if_fail (GTK_TREE_MODEL_FILTER (tree_model)->stamp == parent->stamp, FALSE);
+
+ /* Use this instead of has_child to force us to build the level, if needed */
+ if (gtk_tree_model_filter_iter_children (tree_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 = GTK_TREE_MODEL_FILTER (tree_model)->stamp;
+ iter->user_data = level;
+ iter->user_data2 = &g_array_index (level->array, FilterElt, n);
+
+ return TRUE;
+}
+
+static gboolean
+gtk_tree_model_filter_iter_parent (GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ GtkTreeIter *child)
+{
+ FilterLevel *level;
+
+ iter->stamp = 0;
+ g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (tree_model), FALSE);
+ g_return_val_if_fail (GTK_TREE_MODEL_FILTER (tree_model)->child_model != NULL, FALSE);
+ g_return_val_if_fail (GTK_TREE_MODEL_FILTER (tree_model)->stamp == child->stamp, FALSE);
+
+ level = child->user_data;
+
+ if (level->parent_level)
+ {
+ iter->stamp = GTK_TREE_MODEL_FILTER (tree_model)->stamp;
+ iter->user_data = level->parent_level;
+ iter->user_data2 = level->parent_elt;
+
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static void
+gtk_tree_model_filter_ref_node (GtkTreeModel *tree_model,
+ GtkTreeIter *iter)
+{
+ GtkTreeModelFilter *tree_model_filter = (GtkTreeModelFilter *) tree_model;
+ FilterLevel *level;
+ FilterElt *elt;
+
+ g_return_if_fail (GTK_IS_TREE_MODEL_FILTER (tree_model));
+ g_return_if_fail (GTK_TREE_MODEL_FILTER (tree_model)->child_model != NULL);
+ g_return_if_fail (GTK_TREE_MODEL_FILTER (tree_model)->stamp == iter->stamp);
+
+ 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 decrement the zero_ref_count val */
+ do
+ {
+ if (parent_elt)
+ parent_elt->zero_ref_count--;
+ else
+ tree_model_filter->zero_ref_count--;
+
+ if (parent_level)
+ {
+ parent_elt = parent_level->parent_elt;
+ parent_level = parent_level->parent_level;
+ }
+ }
+ while (parent_level);
+ }
+}
+
+static void
+gtk_tree_model_filter_unref_node (GtkTreeModel *tree_model,
+ GtkTreeIter *iter)
+{
+ GtkTreeModelFilter *tree_model_filter = (GtkTreeModelFilter *) tree_model;
+ FilterLevel *level;
+ FilterElt *elt;
+
+ g_return_if_fail (GTK_IS_TREE_MODEL_FILTER (tree_model));
+ g_return_if_fail (GTK_TREE_MODEL_FILTER (tree_model)->child_model != NULL);
+ g_return_if_fail (GTK_TREE_MODEL_FILTER (tree_model)->stamp == iter->stamp);
+
+ 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 increment 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;
+ }
+ tree_model_filter->zero_ref_count++;
+ }
+}
+
+/* ModelFilter API */
+
+/**
+ * gtk_tree_model_filter_set_model:
+ * @tree_model_filter: The #GtkTreeModelFilter.
+ * @child_model: A #GtkTreeModel, or NULL.
+ *
+ * Sets the model of @tree_model_filter to be @model. If @model is NULL, then the * old model is unset. The filter function is unset as a result of this call.
+ * The model will be in an unfiltered state until a filter function is set.
+ **/
+static void
+gtk_tree_model_filter_set_model (GtkTreeModelFilter *tree_model_filter,
+ GtkTreeModel *child_model)
+{
+ g_return_if_fail (GTK_IS_TREE_MODEL_FILTER (tree_model_filter));
+
+ if (child_model)
+ g_object_ref (G_OBJECT (child_model));
+
+ if (tree_model_filter->child_model)
+ {
+ g_signal_handler_disconnect (G_OBJECT (tree_model_filter->child_model),
+ tree_model_filter->changed_id);
+ g_signal_handler_disconnect (G_OBJECT (tree_model_filter->child_model),
+ tree_model_filter->inserted_id);
+ g_signal_handler_disconnect (G_OBJECT (tree_model_filter->child_model),
+ tree_model_filter->has_child_toggled_id);
+ g_signal_handler_disconnect (G_OBJECT (tree_model_filter->child_model),
+ tree_model_filter->deleted_id);
+ g_signal_handler_disconnect (G_OBJECT (tree_model_filter->child_model),
+ tree_model_filter->reordered_id);
+
+ /* reset our state */
+ gtk_tree_model_filter_free_level (tree_model_filter, tree_model_filter->root);
+ tree_model_filter->root = NULL;
+ g_object_unref (G_OBJECT (tree_model_filter->child_model));
+ tree_model_filter->filter_column = -1;
+ }
+
+ tree_model_filter->child_model = child_model;
+
+ if (child_model)
+ {
+ gint n_columns;
+
+ g_object_ref (tree_model_filter->child_model);
+ tree_model_filter->changed_id =
+ g_signal_connect (child_model, "row_changed",
+ G_CALLBACK (gtk_tree_model_filter_row_changed),
+ tree_model_filter);
+ tree_model_filter->inserted_id =
+ g_signal_connect (child_model, "row_inserted",
+ G_CALLBACK (gtk_tree_model_filter_row_inserted),
+ tree_model_filter);
+ tree_model_filter->has_child_toggled_id =
+ g_signal_connect (child_model, "row_has_child_toggled",
+ G_CALLBACK (gtk_tree_model_filter_row_has_child_toggled),
+ tree_model_filter);
+ tree_model_filter->deleted_id =
+ g_signal_connect (child_model, "row_deleted",
+ G_CALLBACK (gtk_tree_model_filter_row_deleted),
+ tree_model_filter);
+ tree_model_filter->reordered_id =
+ g_signal_connect (child_model, "rows_reordered",
+ G_CALLBACK (gtk_tree_model_filter_rows_reordered),
+ tree_model_filter);
+
+ tree_model_filter->child_flags = gtk_tree_model_get_flags (child_model);
+ n_columns = gtk_tree_model_get_n_columns (child_model);
+
+ tree_model_filter->stamp = g_random_int ();
+ }
+}
+
+static void
+gtk_tree_model_filter_set_filter_column (GtkTreeModelFilter *tree_model_filter,
+ gint filter_column)
+{
+ g_return_if_fail (GTK_IS_TREE_MODEL_FILTER (tree_model_filter));
+ g_return_if_fail (filter_column >= 0);
+
+ tree_model_filter->filter_column = filter_column;
+}
+
+static void
+gtk_tree_model_filter_set_root (GtkTreeModelFilter *filter,
+ GtkTreePath *root)
+{
+ g_return_if_fail (GTK_IS_TREE_MODEL_FILTER (filter));
+
+ if (!root)
+ filter->virtual_root = NULL;
+ else
+ filter->virtual_root = gtk_tree_path_copy (root);
+}
+
+static GtkTreePath *
+gtk_tree_model_filter_elt_get_path (FilterLevel *level,
+ FilterElt *elt,
+ GtkTreePath *root)
+{
+ gchar *str = NULL;
+ GList *i;
+ GList *offsets = NULL;
+ 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);
+
+ while (walker && walker2)
+ {
+ offsets = g_list_prepend (offsets,
+ g_strdup_printf ("%d", walker2->offset));
+ walker2 = walker->parent_elt;
+ walker = walker->parent_level;
+ }
+ g_return_val_if_fail (g_list_length (offsets) > 0, NULL);
+
+ for (i = offsets; i; i = i->next)
+ {
+ gchar *copy = str;
+
+ if (str)
+ str = g_strconcat (copy, ":", i->data, NULL);
+ else
+ str = g_strdup (i->data);
+
+ if (copy)
+ g_free (copy);
+
+ g_free (i->data);
+ }
+
+ g_list_free (offsets);
+
+ if (root)
+ {
+ real_path = gtk_tree_path_copy (root);
+ path = gtk_tree_path_new_from_string (str);
+ g_free (str);
+
+ gtk_tree_path_add (real_path, path);
+ gtk_tree_path_free (path);
+ return real_path;
+ }
+
+ path = gtk_tree_path_new_from_string (str);
+ g_free (str);
+
+ return path;
+}
+
+/**
+ * gtk_tree_model_filter_get_model:
+ * @tree_model: a #GtkTreeModelFilter
+ *
+ * Returns the model the #GtkTreeModelFilter is filtering.
+ *
+ * Return value: the "child model" being filtered
+ **/
+GtkTreeModel *
+gtk_tree_model_filter_get_model (GtkTreeModelFilter *tree_model)
+{
+ g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (tree_model), NULL);
+
+ return tree_model->child_model;
+}
+
+static gboolean
+gtk_tree_model_filter_fetch_child (GtkTreeModelFilter *filter,
+ FilterLevel *level,
+ gint offset)
+{
+ int i = 0;
+ int len;
+ GtkTreePath *s_path = NULL;
+ GtkTreeIter s_iter;
+ FilterElt elt;
+
+#ifdef VERBOSE
+ g_print ("_fetch_child for offset %d\n", offset);
+#endif
+
+ if (level->parent_level)
+ {
+ /*
+
+ iter.stamp = filter->stamp;
+ iter.user_data = level->parent_level;
+ iter.user_data2 = level->parent_elt;
+ path = gtk_tree_model_get_path (GTK_TREE_MODEL (filter), &iter);
+ s_path = gtk_tree_model_filter_convert_path_to_child_path (filter,
+ path);
+ gtk_tree_path_free (path);
+
+ if (!s_path)
+ return FALSE;
+ */
+ s_path = gtk_tree_model_filter_elt_get_path (level->parent_level, level->parent_elt, filter->virtual_root);
+ if (!s_path)
+ return FALSE;
+ gtk_tree_model_get_iter (filter->child_model, &s_iter, s_path);
+
+ len = gtk_tree_model_iter_n_children (filter->child_model, &s_iter);
+ }
+ else
+ len = gtk_tree_model_iter_n_children (filter->child_model, NULL);
+
+ if (offset >= len)
+ {
+ if (s_path)
+ gtk_tree_path_free (s_path);
+ return FALSE;
+ }
+
+ /* add child */
+ elt.offset = offset;
+ elt.zero_ref_count = 0;
+ elt.ref_count = 0;
+ elt.children = NULL;
+ elt.visible = FALSE;
+ if (GTK_TREE_MODEL_FILTER_CACHE_CHILD_ITERS (filter))
+ {
+ if (!s_path)
+ {
+ if (filter->virtual_root)
+ s_path = gtk_tree_path_copy (filter->virtual_root);
+ else
+ s_path = gtk_tree_path_new ();
+ }
+ gtk_tree_path_append_index (s_path, offset);
+#ifdef VERBOSE
+ g_print ("fetching %s...\n", gtk_tree_path_to_string (s_path));
+#endif
+
+ gtk_tree_model_get_iter (filter->child_model, &elt.iter, s_path);
+#ifdef VERBOSE
+ puts (gtk_tree_path_to_string (s_path));
+ g_print ("Fetch child, got iter c%p\n", elt.iter.user_data);
+#endif
+ }
+
+ if (s_path)
+ gtk_tree_path_free (s_path);
+
+ /* find index */
+ for (i = 0; i < level->array->len; i++)
+ if (g_array_index (level->array, FilterElt, i).offset > offset)
+ break;
+
+/* if (i == 0)
+ g_array_prepend_val (level->array, elt);
+ else if (i == level->array->len)
+ g_array_append_val (level->array, elt);
+ else*/
+ 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 GtkTreePath *
+gtk_real_tree_model_filter_convert_child_path_to_path (GtkTreeModelFilter *tree_model_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 (GTK_IS_TREE_MODEL_FILTER (tree_model_filter), NULL);
+ g_return_val_if_fail (tree_model_filter->child_model != NULL, NULL);
+ g_return_val_if_fail (child_path != NULL, NULL);
+
+ if (!tree_model_filter->virtual_root)
+ real_path = gtk_tree_path_copy (child_path);
+ else
+ real_path = gtk_tree_path_apply_root (child_path, tree_model_filter->virtual_root);
+
+ if (!real_path)
+ return NULL;
+
+ retval = gtk_tree_path_new ();
+ child_indices = gtk_tree_path_get_indices (real_path);
+
+ if (tree_model_filter->root == NULL && build_levels)
+ gtk_tree_model_filter_build_level (tree_model_filter, NULL, NULL);
+ level = FILTER_LEVEL (tree_model_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)
+ gtk_tree_model_filter_build_level (tree_model_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)
+ {
+ /* hrm child not found, try to bring it back */
+ if (!gtk_tree_model_filter_fetch_child (tree_model_filter, level, child_indices[i]))
+ {
+ 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)
+ gtk_tree_model_filter_build_level (tree_model_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 ?!?!? */
+ 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;
+}
+
+/**
+ * gtk_tree_model_filter_convert_child_path_to_path:
+ * @tree_model_filter: A #GtkTreeModelFilter
+ * @child_path: A #GtkTreePath to convert
+ *
+ * Converts @child_path to a path relative to @tree_model_filter. That is,
+ * @child_path points to a path in the child model. The returned 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.
+ *
+ * Return value: A newly allocated #GtkTreePath, or %NULL
+ **/
+GtkTreePath *
+gtk_tree_model_filter_convert_child_path_to_path (GtkTreeModelFilter *tree_model_filter,
+ GtkTreePath *child_path)
+{
+ g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (tree_model_filter), NULL);
+ g_return_val_if_fail (tree_model_filter->child_model != NULL, NULL);
+ g_return_val_if_fail (child_path != NULL, NULL);
+
+ return gtk_real_tree_model_filter_convert_child_path_to_path (tree_model_filter, child_path, TRUE, TRUE);
+}
+
+/**
+ * gtk_tree_model_filter_convert_child_iter_to_iter:
+ * @tree_model_filter: A #GtkTreeModelFilter
+ * @filter_iter: An uninitialized #GtkTreeIter.
+ * @child_iter: A valid #GtkTreeIter pointing to a row on the child model
+ *
+ * Sets @filter_iter to point to the row in @tree_model_filter that corresponds to
+ * the row pointed at by @child_iter.
+ **/
+void
+gtk_tree_model_filter_convert_child_iter_to_iter (GtkTreeModelFilter *tree_model_filter,
+ GtkTreeIter *filter_iter,
+ GtkTreeIter *child_iter)
+{
+ GtkTreePath *child_path, *path;
+
+ g_return_if_fail (GTK_IS_TREE_MODEL_FILTER (tree_model_filter));
+ 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 (tree_model_filter->child_model, child_iter);
+ g_return_if_fail (child_path != NULL);
+
+ path = gtk_tree_model_filter_convert_child_path_to_path (tree_model_filter, child_path);
+ gtk_tree_path_free (child_path);
+ g_return_if_fail (path != NULL);
+
+ gtk_tree_model_get_iter (GTK_TREE_MODEL (tree_model_filter), filter_iter, path);
+ gtk_tree_path_free (path);
+}
+/**
+ * gtk_tree_model_filter_convert_path_to_child_path:
+ * @tree_model_filter: A #GtkTreeModelFilter
+ * @filtered_path: A #GtkTreePath to convert
+ *
+ * Converts @filter_path to a path on the child model of @tree_model_filter. That
+ * is, @filter_path points ot a location in @tree_model_filter. The returned path
+ * will point to the same location in the model not being filtered. If @path does not point to a
+ *
+ * Return value: A newly allocated #GtkTreePath, or %NULLL
+ **/
+GtkTreePath *
+gtk_tree_model_filter_convert_path_to_child_path (GtkTreeModelFilter *tree_model_filter,
+ GtkTreePath *filtered_path)
+{
+ gint *filtered_indices;
+ GtkTreePath *retval;
+ FilterLevel *level;
+ gint i;
+ /* FIXME: THIS IS BROKEN!!!!!!!!!!!!!!!!!!!! */
+
+ /* FIXME: this needs virtual root support */
+
+ g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (tree_model_filter), NULL);
+ g_return_val_if_fail (tree_model_filter->child_model != NULL, NULL);
+ g_return_val_if_fail (filtered_path != NULL, NULL);
+
+ g_warning ("_convert_path_to_child_path: THIS FUNCTION IS BROKEN!\n");
+
+ retval = gtk_tree_path_new ();
+ filtered_indices = gtk_tree_path_get_indices (filtered_path);
+ if (tree_model_filter->root == NULL)
+ gtk_tree_model_filter_build_level (tree_model_filter, NULL, NULL);
+ level = FILTER_LEVEL (tree_model_filter->root);
+
+ for (i = 0; i < gtk_tree_path_get_depth (filtered_path); i++)
+ {
+ if ((level == NULL) ||
+ (level->array->len < filtered_indices[i]))
+ {
+ gtk_tree_path_free (retval);
+ return NULL;
+ }
+ if (g_array_index (level->array, FilterElt, filtered_indices[i]).children == NULL)
+ gtk_tree_model_filter_build_level (tree_model_filter, level, &g_array_index (level->array, FilterElt, filtered_indices[i]));
+ if (level == NULL)
+
+ gtk_tree_path_append_index (retval, g_array_index (level->array, FilterElt, i).offset);
+ }
+
+ return retval;
+}
+
+/**
+ * gtk_tree_model_filter_convert_iter_to_child_iter:
+ * @tree_model_filter: A #GtkTreeModelFilter
+ * @child_iter: An uninitialized #GtkTreeIter
+ * @filtered_iter: A valid #GtkTreeIter pointing to a row on @tree_model_filter.
+ *
+ * Sets @child_iter to point to the row pointed to by *filtered_iter.
+ **/
+void
+gtk_tree_model_filter_convert_iter_to_child_iter (GtkTreeModelFilter *tree_model_filter,
+ GtkTreeIter *child_iter,
+ GtkTreeIter *filtered_iter)
+{
+ g_return_if_fail (GTK_IS_TREE_MODEL_FILTER (tree_model_filter));
+ g_return_if_fail (tree_model_filter->child_model != NULL);
+ g_return_if_fail (child_iter != NULL);
+ g_return_if_fail (filtered_iter != NULL);
+ g_return_if_fail (filtered_iter->stamp == tree_model_filter->stamp);
+
+ if (GTK_TREE_MODEL_FILTER_CACHE_CHILD_ITERS (tree_model_filter))
+ {
+ *child_iter = FILTER_ELT (filtered_iter->user_data2)->iter;
+ }
+ else
+ {
+ GtkTreePath *path;
+ FilterElt *elt;
+ FilterLevel *level;
+
+// path = gtk_tree_path_new ();
+ elt = FILTER_ELT (filtered_iter->user_data2);
+ level = FILTER_LEVEL (filtered_iter->user_data);
+
+ path = gtk_tree_model_filter_elt_get_path (level, elt,
+ tree_model_filter->virtual_root);
+ /*
+ path = gtk_tree_model_get_path (GTK_TREE_MODEL (tree_model_filter),
+ filtered_iter);
+ */
+
+ /*
+ while (level)
+ {
+ gtk_tree_path_prepend_index (path, elt->offset);
+
+ elt = level->parent_elt;
+ level = level->parent_level;
+ }
+ */
+
+ gtk_tree_model_get_iter (tree_model_filter->child_model, child_iter, path);
+ gtk_tree_path_free (path);
+ }
+}
+
+static void
+gtk_tree_model_filter_build_level (GtkTreeModelFilter *tree_model_filter,
+ FilterLevel *parent_level,
+ FilterElt *parent_elt)
+{
+ GtkTreeIter iter;
+ GtkTreeIter root;
+ FilterLevel *new_level;
+ gint length = 0;
+ gint i;
+
+ g_assert (tree_model_filter->child_model != NULL);
+
+ if (parent_level == NULL)
+ {
+ if (tree_model_filter->virtual_root)
+ {
+ if (gtk_tree_model_get_iter (tree_model_filter->child_model, &root, tree_model_filter->virtual_root) == FALSE)
+ return;
+ length = gtk_tree_model_iter_n_children (tree_model_filter->child_model, &root);
+#ifdef VERBOSE
+ g_print ("--- vroot %d children\n", length);
+#endif
+
+ if (gtk_tree_model_iter_children (tree_model_filter->child_model, &iter, &root) == FALSE)
+ return;
+ }
+ else
+ {
+ if (gtk_tree_model_get_iter_root (tree_model_filter->child_model, &iter) == FALSE)
+ return;
+ length = gtk_tree_model_iter_n_children (tree_model_filter->child_model, NULL);
+ }
+ }
+ else
+ {
+ GtkTreeIter parent_iter;
+ GtkTreeIter child_parent_iter;
+
+ parent_iter.stamp = tree_model_filter->stamp;
+ parent_iter.user_data = parent_level;
+ parent_iter.user_data2 = parent_elt;
+
+ gtk_tree_model_filter_convert_iter_to_child_iter (tree_model_filter,
+ &child_parent_iter,
+ &parent_iter);
+ if (gtk_tree_model_iter_children (tree_model_filter->child_model,
+ &iter,
+ &child_parent_iter) == FALSE)
+ return;
+ length = gtk_tree_model_iter_n_children (tree_model_filter->child_model, &child_parent_iter);
+ }
+
+ g_return_if_fail (length > 0);
+
+ 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
+ tree_model_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;
+ }
+ if (new_level != tree_model_filter->root)
+ tree_model_filter->zero_ref_count++;
+
+ i = 0;
+ do
+ {
+ if (gtk_tree_model_filter_visible (tree_model_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 (GTK_TREE_MODEL_FILTER_CACHE_CHILD_ITERS (tree_model_filter))
+ filter_elt.iter = iter;
+
+ g_array_append_val (new_level->array, filter_elt);
+ }
+ i++;
+ } while (gtk_tree_model_iter_next (tree_model_filter->child_model, &iter));
+}
+
+static void
+gtk_tree_model_filter_free_level (GtkTreeModelFilter *tree_model_filter,
+ FilterLevel *filter_level)
+{
+ gint i;
+
+#ifdef VERBOSE
+ g_print ("freeing level: %p, %p (ref = %d)\n", filter_level,
+ filter_level->array, filter_level->ref_count);
+ g_print ("-- parents - elt: %p, level %p\n",
+ filter_level->parent_elt, filter_level->parent_level);
+#endif
+
+ g_assert (filter_level);
+
+#ifdef VERBOSE
+ g_print ("-- freeing current level (ref = %d)\n",filter_level->ref_count);
+#endif
+
+ 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--;
+ else
+ tree_model_filter->zero_ref_count--;
+
+ if (parent_level)
+ {
+ parent_elt = parent_level->parent_elt;
+ parent_level = parent_level->parent_level;
+ }
+ }
+ while (parent_level);
+ }
+
+#ifdef VERBOSE
+ g_print ("-- freeing children\n");
+#endif
+
+ 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 (tree_model_filter,
+ FILTER_LEVEL(g_array_index (filter_level->array, FilterElt, i).children));
+ }
+
+ if (filter_level->parent_elt)
+ {
+ filter_level->parent_elt->children = NULL;
+ }
+ else
+ {
+ tree_model_filter->root = NULL;
+ }
+
+#ifdef VERBOSE
+ g_print ("free %p\n", filter_level->array);
+#endif
+
+ g_array_free (filter_level->array, TRUE);
+ filter_level->array = NULL;
+
+#ifdef VERBOSE
+ g_print ("free %p\n", filter_level);
+#endif
+
+ g_free (filter_level);
+ filter_level = NULL;
+
+#ifdef VERBOSE
+ g_print ("-------- done ---------\n");
+#endif
+}
+
+static void
+gtk_tree_model_filter_increment_stamp (GtkTreeModelFilter *tree_model_filter)
+{
+ while (tree_model_filter->stamp == 0) tree_model_filter->stamp++;
+
+ gtk_tree_model_filter_clear_cache (tree_model_filter);
+}
+
+static gboolean
+gtk_tree_model_filter_visible (GtkTreeModelFilter *tree_model_filter,
+ GtkTreeIter *child_iter)
+{
+ GValue val = {0, };
+
+ gtk_tree_model_get_value (tree_model_filter->child_model,
+ child_iter, tree_model_filter->filter_column,
+ &val);
+
+ if (g_value_get_boolean (&val))
+ {
+ g_value_unset (&val);
+ return TRUE;
+ }
+
+ g_value_unset (&val);
+ return FALSE;
+}
+
+static void
+gtk_tree_path_add (GtkTreePath *path1, GtkTreePath *path2)
+{
+ int i;
+ gint depth;
+ gint *indices;
+
+ g_return_if_fail (path1 != NULL);
+ g_return_if_fail (path2 != NULL);
+
+ depth = gtk_tree_path_get_depth (path2);
+ indices = gtk_tree_path_get_indices (path2);
+
+ for (i = 0; i < depth; i++)
+ gtk_tree_path_append_index (path1, indices[i]);
+}
+
+static GtkTreePath *
+gtk_tree_path_apply_root (GtkTreePath *path, GtkTreePath *root)
+{
+ GtkTreePath *retval;
+ int i;
+ gint depth;
+ gint *indices;
+
+ if (gtk_tree_path_get_depth (path) <= gtk_tree_path_get_depth (root))
+ return NULL;
+
+ depth = gtk_tree_path_get_depth (path);
+ indices = gtk_tree_path_get_indices (path);
+
+ 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
+gtk_tree_model_filter_clear_cache_helper (GtkTreeModelFilter *tree_model_filter,
+ FilterLevel *level)
+{
+ gint i;
+
+ g_assert (level != NULL);
+
+ for (i = 0; i < level->array->len; i++)
+ {
+ if (g_array_index (level->array, FilterElt, i).zero_ref_count > 0)
+ gtk_tree_model_filter_clear_cache_helper (tree_model_filter, g_array_index (level->array, FilterElt, i).children);
+ }
+
+ if (level->ref_count == 0 && level != tree_model_filter->root)
+ {
+ gtk_tree_model_filter_free_level (tree_model_filter, level);
+ return;
+ }
+}
+
+/**
+ * gtk_tree_model_filter_clear_cache:
+ * @tree_model_filter: A #GtkTreeModelFilter
+ *
+ * This function should almost never be called. It clears the @tree_model_filter
+ * of any cached iterators that haven't been reffed with
+ * 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
+ * iters will be invalid.
+ **/
+void
+gtk_tree_model_filter_clear_cache (GtkTreeModelFilter *tree_model_filter)
+{
+ g_return_if_fail (GTK_IS_TREE_MODEL_FILTER (tree_model_filter));
+
+ if (tree_model_filter->zero_ref_count)
+ gtk_tree_model_filter_clear_cache_helper (tree_model_filter, (FilterLevel *)tree_model_filter->root);
+}
diff --git a/src/gtktreemodelfilter.h b/src/gtktreemodelfilter.h
new file mode 100644
index 00000000..3479446c
--- /dev/null
+++ b/src/gtktreemodelfilter.h
@@ -0,0 +1,105 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * 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.
+ *
+ * Author: Kristian Rietveld <kris@gtk.org>
+ */
+
+#ifndef __GTK_TREE_MODEL_FILTER_H__
+#define __GTK_TREE_MODEL_FILTER_H__
+
+#include <gtk/gtktreemodel.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#define GTK_TYPE_TREE_MODEL_FILTER (gtk_tree_model_filter_get_type ())
+#define GTK_TREE_MODEL_FILTER(obj) (GTK_CHECK_CAST ((obj), GTK_TYPE_TREE_MODEL_FILTER, GtkTreeModelFilter))
+#define GTK_TREE_MODEL_FILTER_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), GTK_TYPE_TREE_MODEL_FILTER, GtkTreeModelFilterClass))
+#define GTK_IS_TREE_MODEL_FILTER(obj) (GTK_CHECK_TYPE ((obj), GTK_TYPE_TREE_MODEL_FILTER))
+#define GTK_IS_TREE_MODEL_FILTER_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((obj), GTK_TYPE_TREE_MODEL_FILTER))
+#define GTK_TREE_MODEL_FILTER_GET_CLASS(obj) (GTK_CHECK_GET_CLASS ((obj), GTK_TYPE_TREE_MODEL_FILTER, GtkTreeModelFilterClass))
+
+typedef struct _GtkTreeModelFilter GtkTreeModelFilter;
+typedef struct _GtkTreeModelFilterClass GtkTreeModelFilterClass;
+
+struct _GtkTreeModelFilter
+{
+ GObject parent;
+
+ /* < private > */
+ gpointer root;
+ gint stamp;
+ guint child_flags;
+ GtkTreeModel *child_model;
+ gint zero_ref_count;
+
+ gint filter_column;
+
+ gpointer filter_func;
+ gpointer user_data;
+
+ GtkTreePath *virtual_root;
+
+ /* signal ids */
+ guint changed_id;
+ guint inserted_id;
+ guint has_child_toggled_id;
+ guint deleted_id;
+ guint reordered_id;
+};
+
+struct _GtkTreeModelFilterClass
+{
+ GObjectClass parent_class;
+};
+
+typedef gboolean (* GtkTreeModelFilterFunc) (GtkTreeModel *model, GtkTreeIter *iter, gpointer data);
+
+GtkType gtk_tree_model_filter_get_type (void);
+GtkTreeModel *gtk_tree_model_filter_new_with_model (GtkTreeModel *child_model,
+ gint filter_column,
+ GtkTreePath *virtual_root);
+void gtk_tree_model_filter_clear_cache (GtkTreeModelFilter *tree_model_filter);
+
+void
+gtk_tree_model_filter_convert_child_iter_to_iter (GtkTreeModelFilter *tree_model_filter,
+ GtkTreeIter *filter_iter,
+ GtkTreeIter *child_iter);
+void
+gtk_tree_model_filter_convert_iter_to_child_iter (GtkTreeModelFilter *tree_model_filter,
+ GtkTreeIter *child_iter,
+ GtkTreeIter *filtered_iter);
+GtkTreePath *
+gtk_tree_model_filter_convert_child_path_to_path (GtkTreeModelFilter *tree_model_filter,
+ GtkTreePath *child_path);
+
+GtkTreePath *
+gtk_tree_model_filter_convert_path_to_child_path (GtkTreeModelFilter *tree_model_filter,
+ GtkTreePath *filtered_path);
+
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+
+#endif /* __GTK_TREE_MODEL_FILTER_H__ */
diff --git a/src/yelp-base.c b/src/yelp-base.c
index 3faf7e75..ab1db488 100644
--- a/src/yelp-base.c
+++ b/src/yelp-base.c
@@ -66,9 +66,10 @@ yelp_base_init (YelpBase *base)
priv = g_new0 (YelpBasePriv, 1);
- priv->content_store = gtk_tree_store_new (2,
+ priv->content_store = gtk_tree_store_new (3,
G_TYPE_STRING,
- G_TYPE_POINTER);
+ G_TYPE_POINTER,
+ G_TYPE_BOOLEAN);
base->priv = priv;
}
diff --git a/src/yelp-util.c b/src/yelp-util.c
index 89759c28..57321eaf 100644
--- a/src/yelp-util.c
+++ b/src/yelp-util.c
@@ -39,6 +39,7 @@ yelp_util_contents_add_section (GtkTreeStore *store,
gtk_tree_store_set (store, iter,
0, section->name,
1, section,
+ 2, TRUE,
-1);
return iter;
}
diff --git a/src/yelp-view-content.c b/src/yelp-view-content.c
index 11e950a1..ed90e130 100644
--- a/src/yelp-view-content.c
+++ b/src/yelp-view-content.c
@@ -26,6 +26,7 @@
#include <libgnome/gnome-i18n.h>
#include <gtk/gtktreeview.h>
+#include "gtktreemodelfilter.h"
#include "yelp-html.h"
#include "yelp-view-content.h"
@@ -99,6 +100,7 @@ yvc_tree_selection_changed_cb (GtkTreeSelection *selection,
YelpViewContentPriv *priv;
GtkTreeIter iter;
YelpSection *section;
+ GtkTreeModel *model;
g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
g_return_if_fail (YELP_IS_VIEW_CONTENT (content));
@@ -106,7 +108,9 @@ yvc_tree_selection_changed_cb (GtkTreeSelection *selection,
priv = content->priv;
if (gtk_tree_selection_get_selected (selection, NULL, &iter)) {
- gtk_tree_model_get (GTK_TREE_MODEL (priv->tree_model), &iter,
+ model = gtk_tree_view_get_model (priv->content_tree);
+
+ gtk_tree_model_get (model, &iter,
1, &section,
-1);
@@ -134,34 +138,27 @@ yelp_view_content_new (GtkTreeModel *tree_model)
{
YelpViewContent *view;
YelpViewContentPriv *priv;
- GtkCellRenderer *cell;
- GtkTreeViewColumn *column;
GtkTreeSelection *selection;
GtkWidget *html_sw;
- GtkWidget *tree_sw;
+ GtkWidget *tree_sw;
GtkWidget *frame;
-
+
view = g_object_new (YELP_TYPE_VIEW_CONTENT, NULL);
priv = view->priv;
priv->tree_model = tree_model;
-
+
/* Setup the content tree */
tree_sw = gtk_scrolled_window_new (NULL, NULL);
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (tree_sw),
GTK_POLICY_AUTOMATIC,
GTK_POLICY_AUTOMATIC);
- gtk_tree_view_set_model (GTK_TREE_VIEW (priv->content_tree),
- tree_model);
-
- cell = gtk_cell_renderer_text_new ();
- column = gtk_tree_view_column_new_with_attributes (_("Section"), cell,
- "text", 0,
- NULL);
- gtk_tree_view_column_set_sort_column_id (column, 0);
- gtk_tree_view_append_column (GTK_TREE_VIEW (priv->content_tree),
- column);
+ gtk_tree_view_insert_column_with_attributes (
+ GTK_TREE_VIEW (priv->content_tree), -1,
+ _("Section"), gtk_cell_renderer_text_new (),
+ "text", 0,
+ NULL);
selection = gtk_tree_view_get_selection (
GTK_TREE_VIEW (priv->content_tree));
@@ -169,7 +166,7 @@ yelp_view_content_new (GtkTreeModel *tree_model)
g_signal_connect (selection, "changed",
G_CALLBACK (yvc_tree_selection_changed_cb),
view);
-
+
gtk_container_add (GTK_CONTAINER (tree_sw), priv->content_tree);
/* Setup the Html view */
@@ -196,19 +193,21 @@ yelp_view_content_new (GtkTreeModel *tree_model)
}
void
-yelp_view_content_show_path (YelpViewContent *content,
+yelp_view_content_show_path (YelpViewContent *content_view,
GtkTreePath *path)
{
YelpViewContentPriv *priv;
+ GtkTreeModel *model;
- g_return_if_fail (YELP_IS_VIEW_CONTENT (content));
+ g_return_if_fail (YELP_IS_VIEW_CONTENT (content_view));
- priv = content->priv;
-
- gtk_tree_view_collapse_all (GTK_TREE_VIEW (priv->content_tree));
+ priv = content_view->priv;
+
+ model = gtk_tree_model_filter_new_with_model (priv->tree_model,
+ 2, path);
- gtk_tree_view_expand_row (GTK_TREE_VIEW (priv->content_tree),
- path, FALSE);
+ gtk_tree_view_set_model (GTK_TREE_VIEW (priv->content_tree), model);
+
}
void