aboutsummaryrefslogtreecommitdiffstats
path: root/lib/egg
diff options
context:
space:
mode:
authorMarco Pesenti Gritti <marco@it.gnome.org>2003-04-08 03:41:46 +0800
committerMarco Pesenti Gritti <mpeseng@src.gnome.org>2003-04-08 03:41:46 +0800
commit20dd70b526a557dccd623f9a1c0b05720317e9f7 (patch)
treeb138618f8e9d3daf7d9d915495b367dbec198b1e /lib/egg
parent30177ef207092d95ebbc2c835d52262618436c90 (diff)
downloadgsoc2013-epiphany-20dd70b526a557dccd623f9a1c0b05720317e9f7.tar
gsoc2013-epiphany-20dd70b526a557dccd623f9a1c0b05720317e9f7.tar.gz
gsoc2013-epiphany-20dd70b526a557dccd623f9a1c0b05720317e9f7.tar.bz2
gsoc2013-epiphany-20dd70b526a557dccd623f9a1c0b05720317e9f7.tar.lz
gsoc2013-epiphany-20dd70b526a557dccd623f9a1c0b05720317e9f7.tar.xz
gsoc2013-epiphany-20dd70b526a557dccd623f9a1c0b05720317e9f7.tar.zst
gsoc2013-epiphany-20dd70b526a557dccd623f9a1c0b05720317e9f7.zip
Move and autoupdate these from egg.
2003-04-07 Marco Pesenti Gritti <marco@it.gnome.org> * lib/widgets/Makefile.am: * lib/widgets/eggtreemodelfilter.c: * lib/widgets/eggtreemodelfilter.h: * lib/widgets/eggtreemultidnd.c: * lib/widgets/eggtreemultidnd.h: * lib/egg/Makefile.am: * lib/egg/eggtreemodelfilter.c: * lib/egg/eggtreemodelfilter.h: * lib/egg/eggtreemultidnd.c: * lib/egg/eggtreemultidnd.h: * lib/egg/update-from-egg.sh: Move and autoupdate these from egg. * lib/ephy-dnd.c: * lib/ephy-dnd.h: * src/ephy-favicon-action.c: Remove an unnecessary param in ephydnd API. * lib/widgets/ephy-tree-model-sort.c: Use the real multidnd api. 2
Diffstat (limited to 'lib/egg')
-rw-r--r--lib/egg/Makefile.am10
-rw-r--r--lib/egg/eggtreemodelfilter.c2636
-rw-r--r--lib/egg/eggtreemodelfilter.h125
-rw-r--r--lib/egg/eggtreemultidnd.c412
-rw-r--r--lib/egg/eggtreemultidnd.h76
-rwxr-xr-xlib/egg/update-from-egg.sh3
6 files changed, 3259 insertions, 3 deletions
diff --git a/lib/egg/Makefile.am b/lib/egg/Makefile.am
index da634d881..06070380f 100644
--- a/lib/egg/Makefile.am
+++ b/lib/egg/Makefile.am
@@ -10,6 +10,8 @@ EGGSOURCES = \
egg-action.c \
egg-action-group.c \
egg-toggle-action.c \
+ eggtreemodelfilter.c \
+ eggtreemultidnd.c \
egg-radio-action.c \
egg-menu-merge.c \
egg-accel-dialog.c \
@@ -19,14 +21,16 @@ EGGSOURCES = \
eggseparatortoolitem.c \
eggtoolbar.c \
eggtoolbutton.c \
- eggmarshalers.c \
egg-editable-toolbar.c \
egg-toolbars-group.c
libegg_la_SOURCES = \
- $(EGGSOURCES)
+ $(EGGSOURCES) \
+ eggmarshalers.c
EGGHEADERS = \
+ eggtreemodelfilter.h \
+ eggtreemultidnd.h \
egg-menu.h \
egg-action.h \
egg-action-group.h \
@@ -40,12 +44,12 @@ EGGHEADERS = \
eggseparatortoolitem.h \
eggtoolbar.h \
eggtoolbutton.h \
- eggmarshalers.h \
egg-editable-toolbar.h \
egg-toolbars-group.h
noinst_HEADERS = \
$(EGGHEADERS) \
+ eggmarshalers.h \
eggintl.h
eggmarshalers.h:
diff --git a/lib/egg/eggtreemodelfilter.c b/lib/egg/eggtreemodelfilter.c
new file mode 100644
index 000000000..9bff9cd1a
--- /dev/null
+++ b/lib/egg/eggtreemodelfilter.c
@@ -0,0 +1,2636 @@
+/* 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>
+
+/* FIXME: remove this when we move it to GTK+ */
+#include "eggintl.h"
+
+/* 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 filtermodel 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);
+
+ 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);
+
+ 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++;
+
+ i = 0;
+
+ if (!new_level->parent_level)
+ filter->root_level_visible = 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);
+
+ if (!new_level->parent_level)
+ filter->root_level_visible++;
+ }
+ 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--;
+ }
+
+ 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_level)
+ filter->root_level_visible = 0;
+
+ 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;
+
+ /* 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;
+
+ /* 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 */
+ egg_tree_model_filter_free_level (filter, level);
+
+ if (!filter->root)
+ /* we killed the root */
+ return;
+ }
+ else
+ {
+ /* 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);
+
+ 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;
+
+ if (!elt->visible)
+ 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;
+ filter->root_level_visible = 0;
+ }
+ }
+
+ 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)
+ {
+ egg_tree_model_filter_remove_node (filter, &iter, TRUE);
+
+ if (!level->parent_level)
+ filter->root_level_visible--;
+ }
+ else if (elt->visible == FALSE && new == TRUE)
+ {
+ GtkTreeIter childs;
+
+ elt->visible = TRUE;
+
+ egg_tree_model_filter_increment_stamp (filter);
+
+ if (!level->parent_level)
+ filter->root_level_visible++;
+
+ /* 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)
+ {
+ egg_tree_model_filter_remove_node (filter, &iter, FALSE);
+ }
+ else
+ {
+ GtkTreeIter childs;
+
+ 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];
+
+ /* 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))
+ e->offset++;
+ }
+
+ /* 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;
+
+ if (!level->parent_level)
+ filter->root_level_visible++;
+ }
+
+ /* another iteration to update the references of childs to parents. */
+ 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;
+ }
+
+ /* 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);
+
+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 (!level->parent_level && elt->visible)
+ filter->root_level_visible--;
+
+ 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);
+
+ 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;
+
+ /* empty array? */
+ if (FILTER_ELT (parent->user_data2)->children->array->len <= 0)
+ 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);
+
+ /* FIXME: we should prolly count the visible nodes here, just like in
+ * _iter_n_children.
+ */
+ 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)
+ {
+ if (!filter->root)
+ egg_tree_model_filter_build_level (filter, NULL, NULL);
+
+ /* count visible nodes */
+ return filter->root_level_visible;
+ }
+
+ 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--;
+ }
+}
+
+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++;
+ }
+}
+
+/* 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 */
+
+/**
+ * egg_tree_model_filter_new:
+ * @child_model: A #GtkTreeModel.
+ * @root: A #GtkTreePath or %NULL.
+ *
+ * Creates a new #GtkTreeModel, with @child_model as the child_model
+ * and @root as the virtual root.
+ *
+ * Return value: A new #GtkTreeModel.
+ */
+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;
+}
+
+/**
+ * egg_tree_model_filter_get_model:
+ * @filter: A #EggTreeModelFilter.
+ *
+ * Returns a pointer to the child model of @filter.
+ *
+ * Return value: A pointer to a #GtkTreeModel.
+ */
+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;
+}
+
+/**
+ * egg_tree_model_filter_set_visible_func:
+ * @filter: A #EggTreeModelFilter.
+ * @func: A #EggTreeModelFilterVisibleFunc, the visible function.
+ * @data: User data to pass to the visible function, or %NULL.
+ * @destroy: Destroy notifier of @data, or %NULL.
+ *
+ * Sets the visible function used when filtering the @filter to be @func. The
+ * function should return %TRUE if the given row should be visible and
+ * %FALSE otherwise.
+ */
+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;
+}
+
+/**
+ * egg_tree_model_filter_set_modify_func:
+ * @filter: A #EggTreeModelFilter.
+ * @n_columns: The number of columns in the filter model.
+ * @types: The #GType<!-- -->s of the columns.
+ * @func: A #EggTreeModelFilterModifyFunc, or %NULL.
+ * @data: User data to pass to the modify function, or %NULL.
+ * @destroy: Destroy notifier of @data, or %NULL.
+ *
+ * Sets the @filter to have @n_columns columns with @types. If @func
+ * is not %NULL, it will set @func to be the modify function of @filter.
+ */
+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;
+}
+
+/**
+ * egg_tree_model_filter_set_visible_column:
+ * @filter: A #EggTreeModelFilter.
+ * @column: A #gint which is the column containing the visible information.
+ *
+ * Sets @column of the child_model to be the column where @filter should
+ * look for visibility information. @columns should be a column of type
+ * %G_TYPE_BOOLEAN, where %TRUE means that a row is visible, and %FALSE
+ * if not.
+ */
+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 */
+
+/**
+ * egg_tree_model_filter_convert_child_iter_to_iter:
+ * @filter: A #EggTreeModelFilter.
+ * @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 @filter that corresponds to the
+ * row pointed at by @child_iter.
+ */
+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);
+}
+
+/**
+ * egg_tree_model_filter_convert_iter_to_child_iter:
+ * @filter: A #EggTreeModelFilter.
+ * @child_iter: An uninitialized #GtkTreeIter.
+ * @filter_iter: A valid #GtkTreeIter pointing to a row on @filter.
+ *
+ * Sets @child_iter to point to the row pointed to by @filter_iter.
+ */
+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;
+}
+
+/**
+ * egg_tree_model_filter_convert_child_path_to_path:
+ * @filter: A #EggTreeModelFilter.
+ * @child_path: A #GtkTreePath to convert.
+ *
+ * Converts @child_path to a path relative to @filter. That is, @child_path
+ * points to a path in the child model. The rerturned path will point to the
+ * same row in the filtered model. If @child_path isn't a valid path on the
+ * child model, then %NULL is returned.
+ *
+ * Return value: A newly allocated #GtkTreePath, or %NULL.
+ */
+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);
+}
+
+/**
+ * egg_tree_model_filter_convert_path_to_child_path:
+ * @filter: A #EggTreeModelFilter.
+ * @filter_path: A #GtkTreePath to convert.
+ *
+ * Converts @filter_path to a path on the child model of @filter. That is,
+ * @filter_path points to a location in @filter. The returned path will
+ * point to the same location in the model not being filtered. If @filter_path
+ * does not point to a location in the child model, %NULL is returned.
+ *
+ * Return value: A newly allocated #GtkTreePath, or %NULL.
+ */
+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;
+}
+
+static gboolean
+egg_tree_model_filter_refilter_helper (GtkTreeModel *model,
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ gpointer data)
+{
+ gtk_tree_model_row_changed (model, path, iter);
+
+ return FALSE;
+}
+
+/**
+ * egg_tree_model_filter_refilter:
+ * @filter: A #EggTreeModelFilter.
+ *
+ * Emits ::row_changed for each row in the child model, which causes
+ * the filter to re-evaluate whether a row is visible or not.
+ */
+void
+egg_tree_model_filter_refilter (EggTreeModelFilter *filter)
+{
+ g_return_if_fail (EGG_IS_TREE_MODEL_FILTER (filter));
+
+ if (!filter->root)
+ return;
+
+ /* S L O W */
+ gtk_tree_model_foreach (filter->child_model,
+ egg_tree_model_filter_refilter_helper,
+ filter);
+}
+
+/**
+ * egg_tree_model_filter_clear_cache:
+ * @filter: A #EggTreeModelFilter.
+ *
+ * This function should almost never be called. It clears the @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 itters will be invalid.
+ */
+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));
+}
diff --git a/lib/egg/eggtreemodelfilter.h b/lib/egg/eggtreemodelfilter.h
new file mode 100644
index 000000000..b30fc2e1a
--- /dev/null
+++ b/lib/egg/eggtreemodelfilter.h
@@ -0,0 +1,125 @@
+/* eggtreemodelfilter.h
+ * 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.
+ */
+
+#ifndef __EGG_TREE_MODEL_FILTER_H__
+#define __EGG_TREE_MODEL_FILTER_H__
+
+#include <gtk/gtktreemodel.h>
+
+G_BEGIN_DECLS
+
+#define EGG_TYPE_TREE_MODEL_FILTER (egg_tree_model_filter_get_type ())
+#define EGG_TREE_MODEL_FILTER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EGG_TYPE_TREE_MODEL_FILTER, EggTreeModelFilter))
+#define EGG_TREE_MODEL_FILTER_CLASS(vtable) (G_TYPE_CHECK_CLASS_CAST ((vtable), EGG_TYPE_TREE_MODEL_FILTER, EggTreeModelFilterClass))
+#define EGG_IS_TREE_MODEL_FILTER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EGG_TYPE_TREE_MODEL_FILTER))
+#define EGG_IS_TREE_MODEL_FILTER_CLASS(vtable) (G_TYPE_CHECK_CLASS_TYPE ((vtable), EGG_TYPE_TREE_MODEL_FILTER))
+#define EGG_TREE_MODEL_FILTER_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((obj), EGG_TYPE_TREE_MODEL_FILTER, EggTreeModelFilterClass))
+
+typedef gboolean (* EggTreeModelFilterVisibleFunc) (GtkTreeModel *model,
+ GtkTreeIter *iter,
+ gpointer data);
+typedef void (* EggTreeModelFilterModifyFunc) (GtkTreeModel *model,
+ GtkTreeIter *iter,
+ GValue *value,
+ gint column,
+ gpointer data);
+
+typedef struct _EggTreeModelFilter EggTreeModelFilter;
+typedef struct _EggTreeModelFilterClass EggTreeModelFilterClass;
+
+struct _EggTreeModelFilter
+{
+ GObject parent;
+
+ /*< private >*/
+ gpointer root;
+ gint stamp;
+ guint child_flags;
+ GtkTreeModel *child_model;
+ gint zero_ref_count;
+
+ guint root_level_visible;
+
+ GtkTreePath *virtual_root;
+
+ EggTreeModelFilterVisibleFunc visible_func;
+ gpointer visible_data;
+ GtkDestroyNotify visible_destroy;
+
+ gint modify_n_columns;
+ GType *modify_types;
+ EggTreeModelFilterModifyFunc modify_func;
+ gpointer modify_data;
+ gpointer modify_destroy;
+
+ gint visible_column;
+
+ gboolean visible_method_set;
+ gboolean modify_func_set;
+
+ /* signal ids */
+ guint changed_id;
+ guint inserted_id;
+ guint has_child_toggled_id;
+ guint deleted_id;
+ guint reordered_id;
+};
+
+struct _EggTreeModelFilterClass
+{
+ GObjectClass parent_class;
+};
+
+GType egg_tree_model_filter_get_type (void);
+GtkTreeModel *egg_tree_model_filter_new (GtkTreeModel *child_model,
+ GtkTreePath *root);
+void egg_tree_model_filter_set_visible_func (EggTreeModelFilter *filter,
+ EggTreeModelFilterVisibleFunc func,
+ gpointer data,
+ GtkDestroyNotify destroy);
+void egg_tree_model_filter_set_modify_func (EggTreeModelFilter *filter,
+ gint n_columns,
+ GType *types,
+ EggTreeModelFilterModifyFunc func,
+ gpointer data,
+ GtkDestroyNotify destroy);
+void egg_tree_model_filter_set_visible_column (EggTreeModelFilter *filter,
+ gint column);
+
+GtkTreeModel *egg_tree_model_filter_get_model (EggTreeModelFilter *filter);
+
+/* conversion */
+void egg_tree_model_filter_convert_child_iter_to_iter (EggTreeModelFilter *filter,
+ GtkTreeIter *filter_iter,
+ GtkTreeIter *child_iter);
+void egg_tree_model_filter_convert_iter_to_child_iter (EggTreeModelFilter *filter,
+ GtkTreeIter *child_iter,
+ GtkTreeIter *filter_iter);
+GtkTreePath *egg_tree_model_filter_convert_child_path_to_path (EggTreeModelFilter *filter,
+ GtkTreePath *child_path);
+GtkTreePath *egg_tree_model_filter_convert_path_to_child_path (EggTreeModelFilter *path,
+ GtkTreePath *filter_path);
+
+void egg_tree_model_filter_refilter (EggTreeModelFilter *filter);
+void egg_tree_model_filter_clear_cache (EggTreeModelFilter *filter);
+
+G_END_DECLS
+
+#endif /* __EGG_TREE_MODEL_FILTER_H__ */
diff --git a/lib/egg/eggtreemultidnd.c b/lib/egg/eggtreemultidnd.c
new file mode 100644
index 000000000..ae7a4d5aa
--- /dev/null
+++ b/lib/egg/eggtreemultidnd.c
@@ -0,0 +1,412 @@
+/* eggtreemultidnd.c
+ * Copyright (C) 2001 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <string.h>
+#include <gtk/gtktreeselection.h>
+#include <gtk/gtksignal.h>
+#include <gtk/gtkwidget.h>
+#include <gtk/gtkmain.h>
+#include "eggtreemultidnd.h"
+
+#define EGG_TREE_MULTI_DND_STRING "EggTreeMultiDndString"
+
+typedef struct
+{
+ guint pressed_button;
+ gint x;
+ gint y;
+ guint motion_notify_handler;
+ guint button_release_handler;
+ guint drag_data_get_handler;
+ GSList *event_list;
+} EggTreeMultiDndData;
+
+/* CUT-N-PASTE from gtktreeview.c */
+typedef struct _TreeViewDragInfo TreeViewDragInfo;
+struct _TreeViewDragInfo
+{
+ GdkModifierType start_button_mask;
+ GtkTargetList *source_target_list;
+ GdkDragAction source_actions;
+
+ GtkTargetList *dest_target_list;
+
+ guint source_set : 1;
+ guint dest_set : 1;
+};
+
+
+GType
+egg_tree_multi_drag_source_get_type (void)
+{
+ static GType our_type = 0;
+
+ if (!our_type)
+ {
+ static const GTypeInfo our_info =
+ {
+ sizeof (EggTreeMultiDragSourceIface), /* class_size */
+ NULL, /* base_init */
+ NULL, /* base_finalize */
+ NULL,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ 0,
+ 0, /* n_preallocs */
+ NULL
+ };
+
+ our_type = g_type_register_static (G_TYPE_INTERFACE, "EggTreeMultiDragSource", &our_info, 0);
+ }
+
+ return our_type;
+}
+
+
+/**
+ * egg_tree_multi_drag_source_row_draggable:
+ * @drag_source: a #EggTreeMultiDragSource
+ * @path: row on which user is initiating a drag
+ *
+ * Asks the #EggTreeMultiDragSource whether a particular row can be used as
+ * the source of a DND operation. If the source doesn't implement
+ * this interface, the row is assumed draggable.
+ *
+ * Return value: %TRUE if the row can be dragged
+ **/
+gboolean
+egg_tree_multi_drag_source_row_draggable (EggTreeMultiDragSource *drag_source,
+ GList *path_list)
+{
+ EggTreeMultiDragSourceIface *iface = EGG_TREE_MULTI_DRAG_SOURCE_GET_IFACE (drag_source);
+
+ g_return_val_if_fail (EGG_IS_TREE_MULTI_DRAG_SOURCE (drag_source), FALSE);
+ g_return_val_if_fail (iface->row_draggable != NULL, FALSE);
+ g_return_val_if_fail (path_list != NULL, FALSE);
+
+ if (iface->row_draggable)
+ return (* iface->row_draggable) (drag_source, path_list);
+ else
+ return TRUE;
+}
+
+
+/**
+ * egg_tree_multi_drag_source_drag_data_delete:
+ * @drag_source: a #EggTreeMultiDragSource
+ * @path: row that was being dragged
+ *
+ * Asks the #EggTreeMultiDragSource to delete the row at @path, because
+ * it was moved somewhere else via drag-and-drop. Returns %FALSE
+ * if the deletion fails because @path no longer exists, or for
+ * some model-specific reason. Should robustly handle a @path no
+ * longer found in the model!
+ *
+ * Return value: %TRUE if the row was successfully deleted
+ **/
+gboolean
+egg_tree_multi_drag_source_drag_data_delete (EggTreeMultiDragSource *drag_source,
+ GList *path_list)
+{
+ EggTreeMultiDragSourceIface *iface = EGG_TREE_MULTI_DRAG_SOURCE_GET_IFACE (drag_source);
+
+ g_return_val_if_fail (EGG_IS_TREE_MULTI_DRAG_SOURCE (drag_source), FALSE);
+ g_return_val_if_fail (iface->drag_data_delete != NULL, FALSE);
+ g_return_val_if_fail (path_list != NULL, FALSE);
+
+ return (* iface->drag_data_delete) (drag_source, path_list);
+}
+
+/**
+ * egg_tree_multi_drag_source_drag_data_get:
+ * @drag_source: a #EggTreeMultiDragSource
+ * @path: row that was dragged
+ * @selection_data: a #EggSelectionData to fill with data from the dragged row
+ *
+ * Asks the #EggTreeMultiDragSource to fill in @selection_data with a
+ * representation of the row at @path. @selection_data->target gives
+ * the required type of the data. Should robustly handle a @path no
+ * longer found in the model!
+ *
+ * Return value: %TRUE if data of the required type was provided
+ **/
+gboolean
+egg_tree_multi_drag_source_drag_data_get (EggTreeMultiDragSource *drag_source,
+ GList *path_list,
+ GtkSelectionData *selection_data)
+{
+ EggTreeMultiDragSourceIface *iface = EGG_TREE_MULTI_DRAG_SOURCE_GET_IFACE (drag_source);
+
+ g_return_val_if_fail (EGG_IS_TREE_MULTI_DRAG_SOURCE (drag_source), FALSE);
+ g_return_val_if_fail (iface->drag_data_get != NULL, FALSE);
+ g_return_val_if_fail (path_list != NULL, FALSE);
+ g_return_val_if_fail (selection_data != NULL, FALSE);
+
+ return (* iface->drag_data_get) (drag_source, path_list, selection_data);
+}
+
+static void
+stop_drag_check (GtkWidget *widget)
+{
+ EggTreeMultiDndData *priv_data;
+ GSList *l;
+
+ priv_data = g_object_get_data (G_OBJECT (widget), EGG_TREE_MULTI_DND_STRING);
+
+ for (l = priv_data->event_list; l != NULL; l = l->next)
+ gdk_event_free (l->data);
+
+ g_slist_free (priv_data->event_list);
+ priv_data->event_list = NULL;
+ g_signal_handler_disconnect (widget, priv_data->motion_notify_handler);
+ g_signal_handler_disconnect (widget, priv_data->button_release_handler);
+}
+
+static gboolean
+egg_tree_multi_drag_button_release_event (GtkWidget *widget,
+ GdkEventButton *event,
+ gpointer data)
+{
+ EggTreeMultiDndData *priv_data;
+ GSList *l;
+
+ priv_data = g_object_get_data (G_OBJECT (widget), EGG_TREE_MULTI_DND_STRING);
+
+ for (l = priv_data->event_list; l != NULL; l = l->next)
+ gtk_propagate_event (widget, l->data);
+
+ stop_drag_check (widget);
+
+ return FALSE;
+}
+
+static void
+selection_foreach (GtkTreeModel *model,
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ gpointer data)
+{
+ GList **list_ptr;
+
+ list_ptr = (GList **) data;
+
+ *list_ptr = g_list_prepend (*list_ptr, gtk_tree_row_reference_new (model, path));
+}
+
+static void
+path_list_free (GList *path_list)
+{
+ g_list_foreach (path_list, (GFunc) gtk_tree_row_reference_free, NULL);
+ g_list_free (path_list);
+}
+
+static void
+set_context_data (GdkDragContext *context,
+ GList *path_list)
+{
+ g_object_set_data_full (G_OBJECT (context),
+ "egg-tree-view-multi-source-row",
+ path_list,
+ (GDestroyNotify) path_list_free);
+}
+
+static GList *
+get_context_data (GdkDragContext *context)
+{
+ return g_object_get_data (G_OBJECT (context),
+ "egg-tree-view-multi-source-row");
+}
+
+/* CUT-N-PASTE from gtktreeview.c */
+static TreeViewDragInfo*
+get_info (GtkTreeView *tree_view)
+{
+ return g_object_get_data (G_OBJECT (tree_view), "gtk-tree-view-drag-info");
+}
+
+
+static void
+egg_tree_multi_drag_drag_data_get (GtkWidget *widget,
+ GdkDragContext *context,
+ GtkSelectionData *selection_data,
+ guint info,
+ guint time)
+{
+ GtkTreeView *tree_view;
+ GtkTreeModel *model;
+ TreeViewDragInfo *di;
+ GList *path_list;
+
+ tree_view = GTK_TREE_VIEW (widget);
+
+ model = gtk_tree_view_get_model (tree_view);
+
+ if (model == NULL)
+ return;
+
+ di = get_info (GTK_TREE_VIEW (widget));
+
+ if (di == NULL)
+ return;
+
+ path_list = get_context_data (context);
+
+ if (path_list == NULL)
+ return;
+
+ /* We can implement the GTK_TREE_MODEL_ROW target generically for
+ * any model; for DragSource models there are some other targets
+ * we also support.
+ */
+
+ if (EGG_IS_TREE_MULTI_DRAG_SOURCE (model))
+ {
+ egg_tree_multi_drag_source_drag_data_get (EGG_TREE_MULTI_DRAG_SOURCE (model),
+ path_list,
+ selection_data);
+ }
+}
+
+static gboolean
+egg_tree_multi_drag_motion_event (GtkWidget *widget,
+ GdkEventMotion *event,
+ gpointer data)
+{
+ EggTreeMultiDndData *priv_data;
+
+ priv_data = g_object_get_data (G_OBJECT (widget), EGG_TREE_MULTI_DND_STRING);
+
+ if (gtk_drag_check_threshold (widget,
+ priv_data->x,
+ priv_data->y,
+ event->x,
+ event->y))
+ {
+ GList *path_list = NULL;
+ GtkTreeSelection *selection;
+ GtkTreeModel *model;
+ GdkDragContext *context;
+ TreeViewDragInfo *di;
+
+ di = get_info (GTK_TREE_VIEW (widget));
+
+ if (di == NULL)
+ return FALSE;
+
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
+ stop_drag_check (widget);
+ gtk_tree_selection_selected_foreach (selection, selection_foreach, &path_list);
+ path_list = g_list_reverse (path_list);
+ model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
+ if (egg_tree_multi_drag_source_row_draggable (EGG_TREE_MULTI_DRAG_SOURCE (model), path_list))
+ {
+
+ context = gtk_drag_begin (widget,
+ di->source_target_list,
+ di->source_actions,
+ priv_data->pressed_button,
+ (GdkEvent*)event);
+ set_context_data (context, path_list);
+ gtk_drag_set_icon_default (context);
+
+ }
+ else
+ {
+ path_list_free (path_list);
+ }
+ }
+
+ return TRUE;
+}
+
+static gboolean
+egg_tree_multi_drag_button_press_event (GtkWidget *widget,
+ GdkEventButton *event,
+ gpointer data)
+{
+ GtkTreeView *tree_view;
+ GtkTreePath *path = NULL;
+ GtkTreeViewColumn *column = NULL;
+ gint cell_x, cell_y;
+ GtkTreeSelection *selection;
+ EggTreeMultiDndData *priv_data;
+
+ tree_view = GTK_TREE_VIEW (widget);
+ priv_data = g_object_get_data (G_OBJECT (tree_view), EGG_TREE_MULTI_DND_STRING);
+ if (priv_data == NULL)
+ {
+ priv_data = g_new0 (EggTreeMultiDndData, 1);
+ g_object_set_data (G_OBJECT (tree_view), EGG_TREE_MULTI_DND_STRING, priv_data);
+ }
+
+ if (g_slist_find (priv_data->event_list, event))
+ return FALSE;
+
+ if (priv_data->event_list)
+ {
+ /* save the event to be propagated in order */
+ priv_data->event_list = g_slist_append (priv_data->event_list, gdk_event_copy ((GdkEvent*)event));
+ return TRUE;
+ }
+
+ if (event->type == GDK_2BUTTON_PRESS)
+ return FALSE;
+
+ gtk_tree_view_get_path_at_pos (tree_view,
+ event->x, event->y,
+ &path, &column,
+ &cell_x, &cell_y);
+
+ selection = gtk_tree_view_get_selection (tree_view);
+
+ if (path && gtk_tree_selection_path_is_selected (selection, path))
+ {
+ priv_data->pressed_button = event->button;
+ priv_data->x = event->x;
+ priv_data->y = event->y;
+ priv_data->event_list = g_slist_append (priv_data->event_list, gdk_event_copy ((GdkEvent*)event));
+ priv_data->motion_notify_handler =
+ g_signal_connect (G_OBJECT (tree_view), "motion_notify_event", G_CALLBACK (egg_tree_multi_drag_motion_event), NULL);
+ priv_data->button_release_handler =
+ g_signal_connect (G_OBJECT (tree_view), "button_release_event", G_CALLBACK (egg_tree_multi_drag_button_release_event), NULL);
+
+ if (priv_data->drag_data_get_handler == 0)
+ {
+ priv_data->drag_data_get_handler =
+ g_signal_connect (G_OBJECT (tree_view), "drag_data_get", G_CALLBACK (egg_tree_multi_drag_drag_data_get), NULL);
+ }
+
+ return TRUE;
+ }
+
+ if (path)
+ {
+ gtk_tree_path_free (path);
+ }
+
+ return FALSE;
+}
+
+void
+egg_tree_multi_drag_add_drag_support (GtkTreeView *tree_view)
+{
+ g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
+ g_signal_connect (G_OBJECT (tree_view), "button_press_event", G_CALLBACK (egg_tree_multi_drag_button_press_event), NULL);
+}
+
diff --git a/lib/egg/eggtreemultidnd.h b/lib/egg/eggtreemultidnd.h
new file mode 100644
index 000000000..810d48ea7
--- /dev/null
+++ b/lib/egg/eggtreemultidnd.h
@@ -0,0 +1,76 @@
+/* eggtreednd.h
+ * Copyright (C) 2001 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __EGG_TREE_MULTI_DND_H__
+#define __EGG_TREE_MULTI_DND_H__
+
+#include <gtk/gtktreemodel.h>
+#include <gtk/gtktreeview.h>
+#include <gtk/gtkdnd.h>
+
+G_BEGIN_DECLS
+
+#define EGG_TYPE_TREE_MULTI_DRAG_SOURCE (egg_tree_multi_drag_source_get_type ())
+#define EGG_TREE_MULTI_DRAG_SOURCE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EGG_TYPE_TREE_MULTI_DRAG_SOURCE, EggTreeMultiDragSource))
+#define EGG_IS_TREE_MULTI_DRAG_SOURCE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EGG_TYPE_TREE_MULTI_DRAG_SOURCE))
+#define EGG_TREE_MULTI_DRAG_SOURCE_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), EGG_TYPE_TREE_MULTI_DRAG_SOURCE, EggTreeMultiDragSourceIface))
+
+typedef struct _EggTreeMultiDragSource EggTreeMultiDragSource; /* Dummy typedef */
+typedef struct _EggTreeMultiDragSourceIface EggTreeMultiDragSourceIface;
+
+struct _EggTreeMultiDragSourceIface
+{
+ GTypeInterface g_iface;
+
+ /* VTable - not signals */
+ gboolean (* row_draggable) (EggTreeMultiDragSource *drag_source,
+ GList *path_list);
+
+ gboolean (* drag_data_get) (EggTreeMultiDragSource *drag_source,
+ GList *path_list,
+ GtkSelectionData *selection_data);
+
+ gboolean (* drag_data_delete) (EggTreeMultiDragSource *drag_source,
+ GList *path_list);
+};
+
+GType egg_tree_multi_drag_source_get_type (void) G_GNUC_CONST;
+
+/* Returns whether the given row can be dragged */
+gboolean egg_tree_multi_drag_source_row_draggable (EggTreeMultiDragSource *drag_source,
+ GList *path_list);
+
+/* Deletes the given row, or returns FALSE if it can't */
+gboolean egg_tree_multi_drag_source_drag_data_delete (EggTreeMultiDragSource *drag_source,
+ GList *path_list);
+
+
+/* Fills in selection_data with type selection_data->target based on the row
+ * denoted by path, returns TRUE if it does anything
+ */
+gboolean egg_tree_multi_drag_source_drag_data_get (EggTreeMultiDragSource *drag_source,
+ GList *path_list,
+ GtkSelectionData *selection_data);
+void egg_tree_multi_drag_add_drag_support (GtkTreeView *tree_view);
+
+
+
+G_END_DECLS
+
+#endif /* __EGG_TREE_MULTI_DND_H__ */
diff --git a/lib/egg/update-from-egg.sh b/lib/egg/update-from-egg.sh
index b2e74bb7a..40de7352d 100755
--- a/lib/egg/update-from-egg.sh
+++ b/lib/egg/update-from-egg.sh
@@ -30,6 +30,9 @@ for FILE in $EGGFILES; do
if test -e $EGGDIR/util/$FILE ; then
SRCFILE=$EGGDIR/util/$FILE
fi
+ if test -e $EGGDIR/treeviewutils/$FILE ; then
+ SRCFILE=$EGGDIR/treeviewutils/$FILE
+ fi
fi
if cmp -s $SRCFILE $FILE; then
echo "File $FILE is unchanged"