]> Pileus Git - ~andy/gtk/blobdiff - gtk/gtktreemodelfilter.c
always set hide_tooltip to TRUE if we are handling a leave notify event.
[~andy/gtk] / gtk / gtktreemodelfilter.c
index e5be15d23dedee0851ba0e46653b6d70c9111295..9d2b89587c52d5b9dc1018d0f8508559359c2c81 100644 (file)
@@ -2,24 +2,28 @@
  * Copyright (C) 2000,2001  Red Hat, Inc., Jonathan Blandford <jrb@redhat.com>
  * Copyright (C) 2001-2003  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 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 program is distributed in the hope that it will be useful,
+ * 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
- * General Public License for more details.
+ * Library 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
+ * 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 <config.h>
 #include "gtktreemodelfilter.h"
-#include <gtk/gtkintl.h>
+#include "gtkintl.h"
+#include "gtktreednd.h"
+#include "gtkprivate.h"
+#include "gtkalias.h"
 #include <string.h>
 
 /* ITER FORMAT:
  * child model.
  */
 
+/* A few notes:
+ *  There are three model/views involved, so there are two mappings:
+ *    * this model -> child model: mapped via offset in FilterElt.
+ *    * this model -> parent model (or view): mapped via the array index
+ *                                            of FilterElt.
+ *
+ *  Note that there are two kinds of paths relative to the filter model
+ *  (those generated from the array indices): paths taking non-visible
+ *  nodes into account, and paths which don't.  Paths which take
+ *  non-visible nodes into account should only be used internally and
+ *  NEVER be passed along with a signal emission.
+ *
+ *  The filter model has a reference on every node that is not in the root
+ *  level and has a parent with ref_count > 1.  Exception is a virtual root
+ *  level; all nodes in the virtual root level are referenced too.
+ */
+
 typedef struct _FilterElt FilterElt;
 typedef struct _FilterLevel FilterLevel;
 
@@ -50,11 +71,14 @@ struct _FilterLevel
 {
   GArray *array;
   gint ref_count;
+  gint visible_nodes;
 
   FilterElt *parent_elt;
   FilterLevel *parent_level;
 };
 
+#define GTK_TREE_MODEL_FILTER_GET_PRIVATE(obj)  (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_TREE_MODEL_FILTER, GtkTreeModelFilterPrivate))
+
 struct _GtkTreeModelFilterPrivate
 {
   gpointer root;
@@ -63,8 +87,6 @@ struct _GtkTreeModelFilterPrivate
   GtkTreeModel *child_model;
   gint zero_ref_count;
 
-  guint root_level_visible;
-
   GtkTreePath *virtual_root;
 
   GtkTreeModelFilterVisibleFunc visible_func;
@@ -75,13 +97,16 @@ struct _GtkTreeModelFilterPrivate
   GType *modify_types;
   GtkTreeModelFilterModifyFunc modify_func;
   gpointer modify_data;
-  gpointer modify_destroy;
+  GtkDestroyNotify modify_destroy;
 
   gint visible_column;
 
   gboolean visible_method_set;
   gboolean modify_func_set;
 
+  gboolean in_row_deleted;
+  gboolean virtual_root_deleted;
+
   /* signal ids */
   guint changed_id;
   guint inserted_id;
@@ -105,9 +130,8 @@ enum
 #define FILTER_LEVEL(filter_level) ((FilterLevel *)filter_level)
 
 /* general code (object/interface init, properties, etc) */
-static void         gtk_tree_model_filter_init                            (GtkTreeModelFilter      *filter);
-static void         gtk_tree_model_filter_class_init                      (GtkTreeModelFilterClass *filter_class);
 static void         gtk_tree_model_filter_tree_model_init                 (GtkTreeModelIface       *iface);
+static void         gtk_tree_model_filter_drag_source_init                (GtkTreeDragSourceIface  *iface);
 static void         gtk_tree_model_filter_finalize                        (GObject                 *object);
 static void         gtk_tree_model_filter_set_property                    (GObject                 *object,
                                                                            guint                    prop_id,
@@ -141,10 +165,13 @@ static void         gtk_tree_model_filter_rows_reordered                  (GtkTr
                                                                            gpointer                data);
 
 /* GtkTreeModel interface */
-static guint        gtk_tree_model_filter_get_flags                       (GtkTreeModel           *model);
+static GtkTreeModelFlags gtk_tree_model_filter_get_flags                       (GtkTreeModel           *model);
 static gint         gtk_tree_model_filter_get_n_columns                   (GtkTreeModel           *model);
 static GType        gtk_tree_model_filter_get_column_type                 (GtkTreeModel           *model,
                                                                            gint                    index);
+static gboolean     gtk_tree_model_filter_get_iter_full                   (GtkTreeModel           *model,
+                                                                           GtkTreeIter            *iter,
+                                                                           GtkTreePath            *path);
 static gboolean     gtk_tree_model_filter_get_iter                        (GtkTreeModel           *model,
                                                                            GtkTreeIter            *iter,
                                                                            GtkTreePath            *path);
@@ -175,11 +202,21 @@ static void         gtk_tree_model_filter_ref_node                        (GtkTr
 static void         gtk_tree_model_filter_unref_node                      (GtkTreeModel           *model,
                                                                            GtkTreeIter            *iter);
 
+/* TreeDragSource interface */
+static gboolean    gtk_tree_model_filter_row_draggable                    (GtkTreeDragSource      *drag_source,
+                                                                           GtkTreePath            *path);
+static gboolean    gtk_tree_model_filter_drag_data_get                    (GtkTreeDragSource      *drag_source,
+                                                                           GtkTreePath            *path,
+                                                                           GtkSelectionData       *selection_data);
+static gboolean    gtk_tree_model_filter_drag_data_delete                 (GtkTreeDragSource      *drag_source,
+                                                                           GtkTreePath            *path);
 
 /* private functions */
 static void        gtk_tree_model_filter_build_level                      (GtkTreeModelFilter     *filter,
                                                                            FilterLevel            *parent_level,
-                                                                           FilterElt              *parent_elt);
+                                                                           FilterElt              *parent_elt,
+                                                                           gboolean                emit_inserted);
+
 static void        gtk_tree_model_filter_free_level                       (GtkTreeModelFilter     *filter,
                                                                            FilterLevel            *filter_level);
 
@@ -205,22 +242,32 @@ static void         gtk_tree_model_filter_real_unref_node                 (GtkTr
 
 static void         gtk_tree_model_filter_set_model                       (GtkTreeModelFilter     *filter,
                                                                            GtkTreeModel           *child_model);
+static void         gtk_tree_model_filter_ref_path                        (GtkTreeModelFilter     *filter,
+                                                                           GtkTreePath            *path);
+static void         gtk_tree_model_filter_unref_path                      (GtkTreeModelFilter     *filter,
+                                                                           GtkTreePath            *path);
 static void         gtk_tree_model_filter_set_root                        (GtkTreeModelFilter     *filter,
                                                                            GtkTreePath            *root);
 
 static GtkTreePath *gtk_real_tree_model_filter_convert_child_path_to_path (GtkTreeModelFilter     *filter,
                                                                            GtkTreePath            *child_path,
                                                                            gboolean                build_levels,
-                                                                           gboolean                fetch_childs);
+                                                                           gboolean                fetch_children);
+
+static FilterElt   *gtk_tree_model_filter_get_nth                         (GtkTreeModelFilter     *filter,
+                                                                           FilterLevel            *level,
+                                                                           int                     n);
+static FilterElt   *gtk_tree_model_filter_get_nth_visible                 (GtkTreeModelFilter     *filter,
+                                                                           FilterLevel            *level,
+                                                                           int                     n);
 
 static FilterElt   *gtk_tree_model_filter_fetch_child                     (GtkTreeModelFilter     *filter,
                                                                            FilterLevel            *level,
                                                                            gint                    offset,
                                                                            gint                   *index);
 static void         gtk_tree_model_filter_remove_node                     (GtkTreeModelFilter     *filter,
-                                                                           GtkTreeIter            *iter,
-                                                                           gboolean                emit_signal);
-static void         gtk_tree_model_filter_update_childs                   (GtkTreeModelFilter     *filter,
+                                                                           GtkTreeIter            *iter);
+static void         gtk_tree_model_filter_update_children                 (GtkTreeModelFilter     *filter,
                                                                            FilterLevel            *level,
                                                                            FilterElt              *elt);
 static FilterElt   *bsearch_elt_with_offset                               (GArray                 *array,
@@ -228,46 +275,11 @@ static FilterElt   *bsearch_elt_with_offset                               (GArra
                                                                            gint                  *index);
 
 
-static GObjectClass *parent_class = NULL;
-
-GType
-gtk_tree_model_filter_get_type (void)
-{
-  static GType tree_model_filter_type = 0;
-
-  if (!tree_model_filter_type)
-    {
-      static const GTypeInfo tree_model_filter_info =
-        {
-          sizeof (GtkTreeModelFilterClass),
-          NULL, /* base_init */
-          NULL, /* base_finalize */
-          (GClassInitFunc) gtk_tree_model_filter_class_init,
-          NULL, /* class_finalize */
-          NULL, /* class_data */
-          sizeof (GtkTreeModelFilter),
-          0, /* n_preallocs */
-          (GInstanceInitFunc) gtk_tree_model_filter_init
-        };
-
-      static const GInterfaceInfo tree_model_info =
-        {
-          (GInterfaceInitFunc) gtk_tree_model_filter_tree_model_init,
-          NULL,
-          NULL
-        };
-
-      tree_model_filter_type = g_type_register_static (G_TYPE_OBJECT,
-                                                       "GtkTreeModelFilter",
-                                                       &tree_model_filter_info, 0);
-
-      g_type_add_interface_static (tree_model_filter_type,
-                                   GTK_TYPE_TREE_MODEL,
-                                   &tree_model_info);
-    }
-
-  return tree_model_filter_type;
-}
+G_DEFINE_TYPE_WITH_CODE (GtkTreeModelFilter, gtk_tree_model_filter, G_TYPE_OBJECT,
+                        G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_MODEL,
+                                               gtk_tree_model_filter_tree_model_init)
+                        G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_SOURCE,
+                                               gtk_tree_model_filter_drag_source_init))
 
 static void
 gtk_tree_model_filter_init (GtkTreeModelFilter *filter)
@@ -278,6 +290,8 @@ gtk_tree_model_filter_init (GtkTreeModelFilter *filter)
   filter->priv->zero_ref_count = 0;
   filter->priv->visible_method_set = FALSE;
   filter->priv->modify_func_set = FALSE;
+  filter->priv->in_row_deleted = FALSE;
+  filter->priv->virtual_root_deleted = FALSE;
 }
 
 static void
@@ -286,7 +300,6 @@ gtk_tree_model_filter_class_init (GtkTreeModelFilterClass *filter_class)
   GObjectClass *object_class;
 
   object_class = (GObjectClass *) filter_class;
-  parent_class = g_type_class_peek_parent (filter_class);
 
   object_class->set_property = gtk_tree_model_filter_set_property;
   object_class->get_property = gtk_tree_model_filter_get_property;
@@ -298,19 +311,19 @@ gtk_tree_model_filter_class_init (GtkTreeModelFilterClass *filter_class)
    */
   g_object_class_install_property (object_class,
                                    PROP_CHILD_MODEL,
-                                   g_param_spec_object ("child_model",
+                                   g_param_spec_object ("child-model",
                                                         ("The child model"),
                                                         ("The model for the filtermodel to filter"),
                                                         GTK_TYPE_TREE_MODEL,
-                                                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+                                                        GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
 
   g_object_class_install_property (object_class,
                                    PROP_VIRTUAL_ROOT,
-                                   g_param_spec_boxed ("virtual_root",
+                                   g_param_spec_boxed ("virtual-root",
                                                        ("The virtual root"),
                                                        ("The virtual root (relative to the child model) for this filtermodel"),
                                                        GTK_TYPE_TREE_PATH,
-                                                       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+                                                       GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
 
   g_type_class_add_private (object_class, sizeof (GtkTreeModelFilterPrivate));
 }
@@ -334,12 +347,26 @@ gtk_tree_model_filter_tree_model_init (GtkTreeModelIface *iface)
   iface->unref_node = gtk_tree_model_filter_unref_node;
 }
 
+static void
+gtk_tree_model_filter_drag_source_init (GtkTreeDragSourceIface *iface)
+{
+  iface->row_draggable = gtk_tree_model_filter_row_draggable;
+  iface->drag_data_delete = gtk_tree_model_filter_drag_data_delete;
+  iface->drag_data_get = gtk_tree_model_filter_drag_data_get;
+}
+
 
 static void
 gtk_tree_model_filter_finalize (GObject *object)
 {
   GtkTreeModelFilter *filter = (GtkTreeModelFilter *) object;
 
+  if (filter->priv->virtual_root && !filter->priv->virtual_root_deleted)
+    {
+      gtk_tree_model_filter_unref_path (filter, filter->priv->virtual_root);
+      filter->priv->virtual_root_deleted = TRUE;
+    }
+
   gtk_tree_model_filter_set_model (filter, NULL);
 
   if (filter->priv->virtual_root)
@@ -348,11 +375,16 @@ gtk_tree_model_filter_finalize (GObject *object)
   if (filter->priv->root)
     gtk_tree_model_filter_free_level (filter, filter->priv->root);
 
-  if (filter->priv->modify_types)
-    g_free (filter->priv->modify_types);
+  g_free (filter->priv->modify_types);
+  
+  if (filter->priv->modify_destroy)
+    filter->priv->modify_destroy (filter->priv->modify_data);
+
+  if (filter->priv->visible_destroy)
+    filter->priv->visible_destroy (filter->priv->visible_data);
 
   /* must chain up */
-  parent_class->finalize (object);
+  G_OBJECT_CLASS (gtk_tree_model_filter_parent_class)->finalize (object);
 }
 
 static void
@@ -404,9 +436,11 @@ gtk_tree_model_filter_get_property (GObject    *object,
 static void
 gtk_tree_model_filter_build_level (GtkTreeModelFilter *filter,
                                    FilterLevel        *parent_level,
-                                   FilterElt          *parent_elt)
+                                   FilterElt          *parent_elt,
+                                   gboolean            emit_inserted)
 {
   GtkTreeIter iter;
+  GtkTreeIter first_node;
   GtkTreeIter root;
   FilterLevel *new_level;
   gint length = 0;
@@ -414,6 +448,9 @@ gtk_tree_model_filter_build_level (GtkTreeModelFilter *filter,
 
   g_assert (filter->priv->child_model != NULL);
 
+  if (filter->priv->in_row_deleted)
+    return;
+
   if (!parent_level)
     {
       if (filter->priv->virtual_root)
@@ -461,6 +498,7 @@ gtk_tree_model_filter_build_level (GtkTreeModelFilter *filter,
                                         sizeof (FilterElt),
                                         length);
   new_level->ref_count = 0;
+  new_level->visible_nodes = 0;
   new_level->parent_elt = parent_elt;
   new_level->parent_level = parent_level;
 
@@ -477,12 +515,12 @@ gtk_tree_model_filter_build_level (GtkTreeModelFilter *filter,
       parent_elt = parent_level->parent_elt;
       parent_level = parent_level->parent_level;
     }
-  filter->priv->zero_ref_count++;
+  if (new_level != filter->priv->root)
+    filter->priv->zero_ref_count++;
 
   i = 0;
 
-  if (!new_level->parent_level)
-    filter->priv->root_level_visible = 0;
+  first_node = iter;
 
   do
     {
@@ -500,13 +538,63 @@ gtk_tree_model_filter_build_level (GtkTreeModelFilter *filter,
             filter_elt.iter = iter;
 
           g_array_append_val (new_level->array, filter_elt);
+          new_level->visible_nodes++;
+
+          if (new_level->parent_level || filter->priv->virtual_root)
+            {
+              GtkTreeIter f_iter;
+
+              f_iter.stamp = filter->priv->stamp;
+              f_iter.user_data = new_level;
+              f_iter.user_data2 = &(g_array_index (new_level->array, FilterElt, new_level->array->len - 1));
+
+              gtk_tree_model_filter_ref_node (GTK_TREE_MODEL (filter), &f_iter);
+
+              if (emit_inserted)
+                {
+                  GtkTreePath *f_path;
 
-          if (!new_level->parent_level)
-            filter->priv->root_level_visible++;
+                  f_path = gtk_tree_model_get_path (GTK_TREE_MODEL (filter),
+                                                    &f_iter);
+                  gtk_tree_model_row_inserted (GTK_TREE_MODEL (filter),
+                                               f_path, &f_iter);
+                  gtk_tree_path_free (f_path);
+                }
+            }
         }
       i++;
     }
   while (gtk_tree_model_iter_next (filter->priv->child_model, &iter));
+
+  if (new_level->array->len == 0
+      && (new_level != filter->priv->root || filter->priv->virtual_root))
+    {
+      /* If none of the nodes are visible, we will just pull in the
+       * first node of the level and keep a reference on it.  We need this
+       * to make sure that we get all signals for this level.
+       */
+      FilterElt filter_elt;
+      GtkTreeIter f_iter;
+
+      filter_elt.offset = 0;
+      filter_elt.zero_ref_count = 0;
+      filter_elt.ref_count = 0;
+      filter_elt.children = NULL;
+      filter_elt.visible = FALSE;
+
+      if (GTK_TREE_MODEL_FILTER_CACHE_CHILD_ITERS (filter))
+        filter_elt.iter = first_node;
+
+      g_array_append_val (new_level->array, filter_elt);
+
+      f_iter.stamp = filter->priv->stamp;
+      f_iter.user_data = new_level;
+      f_iter.user_data2 = &(g_array_index (new_level->array, FilterElt, new_level->array->len - 1));
+
+      gtk_tree_model_filter_ref_node (GTK_TREE_MODEL (filter), &f_iter);
+    }
+  else if (new_level->array->len == 0)
+    gtk_tree_model_filter_free_level (filter, new_level);
 }
 
 static void
@@ -517,36 +605,41 @@ gtk_tree_model_filter_free_level (GtkTreeModelFilter *filter,
 
   g_assert (filter_level);
 
+  for (i = 0; i < filter_level->array->len; i++)
+    {
+      if (g_array_index (filter_level->array, FilterElt, i).children)
+        gtk_tree_model_filter_free_level (filter,
+                                          FILTER_LEVEL (g_array_index (filter_level->array, FilterElt, i).children));
+
+      if (filter_level->parent_level || filter->priv->virtual_root)
+        {
+          GtkTreeIter f_iter;
+
+          f_iter.stamp = filter->priv->stamp;
+          f_iter.user_data = filter_level;
+          f_iter.user_data2 = &(g_array_index (filter_level->array, FilterElt, i));
+
+          gtk_tree_model_filter_unref_node (GTK_TREE_MODEL (filter), &f_iter);
+        }
+    }
+
   if (filter_level->ref_count == 0)
     {
       FilterLevel *parent_level = filter_level->parent_level;
       FilterElt *parent_elt = filter_level->parent_elt;
 
-      do
+      while (parent_level)
         {
-          if (parent_elt)
-            parent_elt->zero_ref_count--;
+         parent_elt->zero_ref_count--;
 
-          if (parent_level)
-            {
-              parent_elt = parent_level->parent_elt;
-              parent_level = parent_level->parent_level;
-            }
+         parent_elt = parent_level->parent_elt;
+         parent_level = parent_level->parent_level;
         }
-      while (parent_level);
-      filter->priv->zero_ref_count--;
-    }
 
-  for (i = 0; i < filter_level->array->len; i++)
-    {
-      if (g_array_index (filter_level->array, FilterElt, i).children)
-        gtk_tree_model_filter_free_level (filter,
-                                          FILTER_LEVEL (g_array_index (filter_level->array, FilterElt, i).children));
+      if (filter_level != filter->priv->root)
+        filter->priv->zero_ref_count--;
     }
 
-  if (!filter_level->parent_level)
-    filter->priv->root_level_visible = 0;
-
   if (filter_level->parent_elt)
     filter_level->parent_elt->children = NULL;
   else
@@ -559,6 +652,7 @@ gtk_tree_model_filter_free_level (GtkTreeModelFilter *filter,
   filter_level = NULL;
 }
 
+/* Creates paths suitable for accessing the child model. */
 static GtkTreePath *
 gtk_tree_model_filter_elt_get_path (FilterLevel *level,
                                     FilterElt   *elt,
@@ -652,9 +746,10 @@ gtk_tree_model_filter_visible (GtkTreeModelFilter *filter,
 {
   if (filter->priv->visible_func)
     {
-      return (filter->priv->visible_func (filter->priv->child_model,
-                                    child_iter,
-                                    filter->priv->visible_data));
+      return filter->priv->visible_func (filter->priv->child_model,
+                                        child_iter,
+                                        filter->priv->visible_data)
+       ? TRUE : FALSE;
     }
   else if (filter->priv->visible_column >= 0)
    {
@@ -673,7 +768,7 @@ gtk_tree_model_filter_visible (GtkTreeModelFilter *filter,
      return FALSE;
    }
 
-  /* no filter thing set, so always visible */
+  /* no visible function set, so always visible */
   return TRUE;
 }
 
@@ -698,6 +793,45 @@ gtk_tree_model_filter_clear_cache_helper (GtkTreeModelFilter *filter,
     }
 }
 
+static FilterElt *
+gtk_tree_model_filter_get_nth (GtkTreeModelFilter *filter,
+                               FilterLevel        *level,
+                               int                 n)
+{
+  if (level->array->len <= n)
+    return NULL;
+
+  return &g_array_index (level->array, FilterElt, n);
+}
+
+static FilterElt *
+gtk_tree_model_filter_get_nth_visible (GtkTreeModelFilter *filter,
+                                       FilterLevel        *level,
+                                       int                 n)
+{
+  int i = 0;
+  FilterElt *elt;
+
+  if (level->visible_nodes <= n)
+    return NULL;
+
+  elt = FILTER_ELT (level->array->data);
+  while (!elt->visible)
+    elt++;
+
+  while (i < n)
+    {
+      if (elt->visible)
+        i++;
+      elt++;
+    }
+
+  while (!elt->visible)
+    elt++;
+
+  return elt;
+}
+
 static FilterElt *
 gtk_tree_model_filter_fetch_child (GtkTreeModelFilter *filter,
                                    FilterLevel        *level,
@@ -793,74 +927,78 @@ gtk_tree_model_filter_fetch_child (GtkTreeModelFilter *filter,
   g_array_insert_val (level->array, i, elt);
   *index = i;
 
-  for (i = MAX (--i, 0); i < level->array->len; i++)
+  for (i = 0; i < level->array->len; i++)
     {
       FilterElt *e = &(g_array_index (level->array, FilterElt, i));
       if (e->children)
         e->children->parent_elt = e;
     }
 
+  c_iter.stamp = filter->priv->stamp;
+  c_iter.user_data = level;
+  c_iter.user_data2 = &g_array_index (level->array, FilterElt, *index);
+
+  if (level->parent_level || filter->priv->virtual_root)
+    gtk_tree_model_filter_ref_node (GTK_TREE_MODEL (filter), &c_iter);
+
   return &g_array_index (level->array, FilterElt, *index);
 }
 
 static void
 gtk_tree_model_filter_remove_node (GtkTreeModelFilter *filter,
-                                   GtkTreeIter        *iter,
-                                   gboolean            emit_signal)
+                                   GtkTreeIter        *iter)
 {
   FilterElt *elt, *parent;
   FilterLevel *level, *parent_level;
-  gint offset, i, length, level_refcount;
+  gint i, length;
 
-  /* FIXME: this function is very ugly. I need to rethink and
-   * rewrite it someday.
-   */
+  gboolean emit_child_toggled = FALSE;
 
   level = FILTER_LEVEL (iter->user_data);
   elt = FILTER_ELT (iter->user_data2);
 
   parent = level->parent_elt;
   parent_level = level->parent_level;
+
   length = level->array->len;
-  offset = elt->offset;
 
-  /* ref counting */
-  while (elt->ref_count > 0)
-    gtk_tree_model_filter_real_unref_node (GTK_TREE_MODEL (filter),
-                                           iter, FALSE);
+  /* we distinguish a couple of cases:
+   *  - root level, length > 1: emit row-deleted and remove.
+   *  - root level, length == 1: emit row-deleted and keep in cache.
+   *  - level, length == 1: parent->ref_count > 1: emit row-deleted and keep.
+   *  - level, length > 1: emit row-deleted and remove.
+   *  - else, remove level.
+   *
+   *  if level != root level and visible nodes == 0, emit row-has-child-toggled.
+   */
 
-  level_refcount = level->ref_count;
+  if (level != filter->priv->root && level->visible_nodes == 0)
+    emit_child_toggled = TRUE;
 
-  /* do the ref counting first! this touches the stamp */
-  if (emit_signal)
+  if (length > 1)
     {
       GtkTreePath *path;
+      FilterElt *tmp;
+
+      /* we emit row-deleted, and remove the node from the cache.
+       */
 
       path = gtk_tree_model_get_path (GTK_TREE_MODEL (filter), iter);
+      elt->visible = FALSE;
       gtk_tree_model_filter_increment_stamp (filter);
+      iter->stamp = filter->priv->stamp;
       gtk_tree_model_row_deleted (GTK_TREE_MODEL (filter), path);
       gtk_tree_path_free (path);
-    }
 
-  if ((length == 1 || level_refcount == 0) &&
-      emit_signal && iter->user_data != filter->priv->root)
-    {
-      /* above code destroyed the level */
-      goto emit_has_child_toggled;
-    }
+      while (elt->ref_count > 1)
+        gtk_tree_model_filter_real_unref_node (GTK_TREE_MODEL (filter),
+                                               iter, FALSE);
 
-  if (length == 1)
-    {
-      /* kill the level */
-      gtk_tree_model_filter_free_level (filter, level);
-
-      if (!filter->priv->root)
-        /* we killed the root */
-        return;
-    }
-  else
-    {
-      FilterElt *tmp;
+      if (parent_level || filter->priv->virtual_root)
+        gtk_tree_model_filter_unref_node (GTK_TREE_MODEL (filter), iter);
+      else if (elt->ref_count > 0)
+        gtk_tree_model_filter_real_unref_node (GTK_TREE_MODEL (filter),
+                                               iter, FALSE);
 
       /* remove the node */
       tmp = bsearch_elt_with_offset (level->array, elt->offset, &i);
@@ -869,7 +1007,8 @@ gtk_tree_model_filter_remove_node (GtkTreeModelFilter *filter,
         {
           g_array_remove_index (level->array, i);
 
-          for (i = MAX (--i, 0); i < level->array->len; i++)
+         i--;
+          for (i = MAX (i, 0); i < level->array->len; i++)
             {
               /* NOTE: here we do *not* decrease offsets, because the node was
                * not removed from the child model
@@ -880,16 +1019,43 @@ gtk_tree_model_filter_remove_node (GtkTreeModelFilter *filter,
             }
         }
     }
+  else if ((length == 1 && parent && parent->ref_count > 1)
+           || (length == 1 && level == filter->priv->root))
+    {
+      GtkTreePath *path;
 
-emit_has_child_toggled:
-  /* children are being handled first, so we can check it this way
-   *
-   * yes this if-statement is ugly
-   */
-  if ((parent && parent->children && parent->children->array->len <= 1) ||
-      (length == 1 && emit_signal && iter->user_data != filter->priv->root))
+      /* we emit row-deleted, but keep the node in the cache and
+       * referenced.
+       */
+
+      path = gtk_tree_model_get_path (GTK_TREE_MODEL (filter), iter);
+      elt->visible = FALSE;
+      gtk_tree_model_filter_increment_stamp (filter);
+      gtk_tree_model_row_deleted (GTK_TREE_MODEL (filter), path);
+      gtk_tree_path_free (path);
+    }
+  else
+    {
+      GtkTreePath *path;
+
+      /* blow level away */
+
+      path = gtk_tree_model_get_path (GTK_TREE_MODEL (filter), iter);
+      elt->visible = FALSE;
+      gtk_tree_model_filter_increment_stamp (filter);
+      iter->stamp = filter->priv->stamp;
+      gtk_tree_model_row_deleted (GTK_TREE_MODEL (filter), path);
+      gtk_tree_path_free (path);
+
+      while (elt->ref_count > 1)
+        gtk_tree_model_filter_real_unref_node (GTK_TREE_MODEL (filter),
+                                               iter, FALSE);
+
+      gtk_tree_model_filter_free_level (filter, level);
+    }
+
+  if (emit_child_toggled)
     {
-      /* latest child has been removed, level has been destroyed */
       GtkTreeIter piter;
       GtkTreePath *ppath;
 
@@ -906,9 +1072,9 @@ emit_has_child_toggled:
 }
 
 static void
-gtk_tree_model_filter_update_childs (GtkTreeModelFilter *filter,
-                                     FilterLevel        *level,
-                                     FilterElt          *elt)
+gtk_tree_model_filter_update_children (GtkTreeModelFilter *filter,
+                                      FilterLevel        *level,
+                                      FilterElt          *elt)
 {
   GtkTreeIter c_iter;
   GtkTreeIter iter;
@@ -961,7 +1127,7 @@ bsearch_elt_with_offset (GArray *array,
         return NULL;
     }
 
-  while (start != end)
+  do
     {
       middle = (start + end) / 2;
 
@@ -974,6 +1140,7 @@ bsearch_elt_with_offset (GArray *array,
       else
         break;
     }
+  while (start != end);
 
   if (elt->offset == offset)
     {
@@ -993,7 +1160,7 @@ gtk_tree_model_filter_row_changed (GtkTreeModel *c_model,
 {
   GtkTreeModelFilter *filter = GTK_TREE_MODEL_FILTER (data);
   GtkTreeIter iter;
-  GtkTreeIter childs;
+  GtkTreeIter children;
   GtkTreeIter real_c_iter;
   GtkTreePath *path = NULL;
 
@@ -1034,7 +1201,8 @@ gtk_tree_model_filter_row_changed (GtkTreeModel *c_model,
 
   if (path)
     {
-      gtk_tree_model_get_iter (GTK_TREE_MODEL (filter), &iter, path);
+      gtk_tree_model_filter_get_iter_full (GTK_TREE_MODEL (filter),
+                                           &iter, path);
       current_state = FILTER_ELT (iter.user_data2)->visible;
     }
   else
@@ -1047,29 +1215,29 @@ gtk_tree_model_filter_row_changed (GtkTreeModel *c_model,
   if (current_state == TRUE && requested_state == FALSE)
     {
       /* get rid of this node */
-      gtk_tree_model_get_iter (GTK_TREE_MODEL (filter), &iter, path);
-      gtk_tree_model_filter_remove_node (filter, &iter, TRUE);
-
       level = FILTER_LEVEL (iter.user_data);
+      level->visible_nodes--;
 
-      if (!level->parent_level)
-        filter->priv->root_level_visible--;
+      gtk_tree_model_filter_remove_node (filter, &iter);
 
       goto done;
     }
 
   if (current_state == TRUE && requested_state == TRUE)
     {
-      /* progate the signal */
-      gtk_tree_model_get_iter (GTK_TREE_MODEL (filter), &iter, path);
+      /* propagate the signal; also get a path taking only visible
+       * nodes into account.
+       */
+      gtk_tree_path_free (path);
+      path = gtk_tree_model_get_path (GTK_TREE_MODEL (filter), &iter);
       gtk_tree_model_row_changed (GTK_TREE_MODEL (filter), path, &iter);
 
       level = FILTER_LEVEL (iter.user_data);
       elt = FILTER_ELT (iter.user_data2);
 
-      /* and update the childs */
-      if (gtk_tree_model_iter_children (c_model, &childs, &real_c_iter))
-        gtk_tree_model_filter_update_childs (filter, level, elt);
+      /* and update the children */
+      if (gtk_tree_model_iter_children (c_model, &children, &real_c_iter))
+        gtk_tree_model_filter_update_children (filter, level, elt);
 
       goto done;
     }
@@ -1085,7 +1253,7 @@ gtk_tree_model_filter_row_changed (GtkTreeModel *c_model,
       gint i;
       FilterLevel *root;
 
-      gtk_tree_model_filter_build_level (filter, NULL, NULL);
+      gtk_tree_model_filter_build_level (filter, NULL, NULL, FALSE);
 
       root = FILTER_LEVEL (filter->priv->root);
 
@@ -1093,34 +1261,45 @@ gtk_tree_model_filter_row_changed (GtkTreeModel *c_model,
         {
           for (i = 0; i < root->array->len; i++)
             g_array_index (root->array, FilterElt, i).visible = FALSE;
-          filter->priv->root_level_visible = 0;
+          root->visible_nodes = 0;
         }
     }
 
+  gtk_tree_model_filter_increment_stamp (filter);
+
   if (!path)
     path = gtk_real_tree_model_filter_convert_child_path_to_path (filter,
                                                                   c_path,
                                                                   TRUE,
                                                                   TRUE);
 
-  g_return_if_fail (path != NULL);
+  if (!path)
+    /* parent is probably being filtered out */
+    goto done;
 
-  gtk_tree_model_filter_increment_stamp (filter);
-  gtk_tree_model_get_iter (GTK_TREE_MODEL (filter), &iter, path);
+  gtk_tree_model_filter_get_iter_full (GTK_TREE_MODEL (filter), &iter, path);
 
   level = FILTER_LEVEL (iter.user_data);
   elt = FILTER_ELT (iter.user_data2);
 
-  elt->visible = TRUE;
+  /* elt->visible can be TRUE at this point if it was pulled in above */
+  if (!elt->visible)
+    {
+      elt->visible = TRUE;
+      level->visible_nodes++;
+    }
 
-  if (!level->parent_level)
-    filter->priv->root_level_visible++;
+  if ((level->parent_elt && level->parent_elt->visible) || !level->parent_elt)
+    {
+      /* visibility changed -- reget path */
+      gtk_tree_path_free (path);
+      path = gtk_tree_model_get_path (GTK_TREE_MODEL (filter), &iter);
 
-  /* update stamp */
-  gtk_tree_model_row_inserted (GTK_TREE_MODEL (filter), path, &iter);
+      gtk_tree_model_row_inserted (GTK_TREE_MODEL (filter), path, &iter);
 
-  if (gtk_tree_model_iter_children (c_model, &childs, c_iter))
-    gtk_tree_model_filter_update_childs (filter, level, elt);
+      if (gtk_tree_model_iter_children (c_model, &children, c_iter))
+        gtk_tree_model_filter_update_children (filter, level, elt);
+    }
 
 done:
   if (path)
@@ -1137,8 +1316,8 @@ gtk_tree_model_filter_row_inserted (GtkTreeModel *c_model,
                                     gpointer      data)
 {
   GtkTreeModelFilter *filter = GTK_TREE_MODEL_FILTER (data);
-  GtkTreePath *path;
-  GtkTreePath *real_path;
+  GtkTreePath *path = NULL;
+  GtkTreePath *real_path = NULL;
   GtkTreeIter iter;
 
   GtkTreeIter real_c_iter;
@@ -1147,7 +1326,7 @@ gtk_tree_model_filter_row_inserted (GtkTreeModel *c_model,
   FilterLevel *level;
   FilterLevel *parent_level;
 
-  gint i = 0, offset, index = -1;
+  gint i = 0, offset;
 
   gboolean free_c_path = FALSE;
 
@@ -1174,22 +1353,34 @@ gtk_tree_model_filter_row_inserted (GtkTreeModel *c_model,
         {
           gint level;
           gint *v_indices, *c_indices;
+          gboolean common_prefix = TRUE;
 
           level = gtk_tree_path_get_depth (c_path) - 1;
           v_indices = gtk_tree_path_get_indices (filter->priv->virtual_root);
           c_indices = gtk_tree_path_get_indices (c_path);
 
-          if (v_indices[level] >= c_indices[level])
+          for (i = 0; i < level; i++)
+            if (v_indices[i] != c_indices[i])
+              {
+                common_prefix = FALSE;
+                break;
+              }
+
+          if (common_prefix && v_indices[level] >= c_indices[level])
             (v_indices[level])++;
         }
     }
 
   if (!filter->priv->root)
     {
-      gtk_tree_model_filter_build_level (filter, NULL, NULL);
-      /* that already put the inserted iter in the level */
+      /* build level will pull in the new child */
+      gtk_tree_model_filter_build_level (filter, NULL, NULL, FALSE);
 
-      goto done_and_emit;
+      if (filter->priv->root
+          && FILTER_LEVEL (filter->priv->root)->visible_nodes)
+        goto done_and_emit;
+      else
+        goto done;
     }
 
   parent_level = level = FILTER_LEVEL (filter->priv->root);
@@ -1199,7 +1390,7 @@ gtk_tree_model_filter_row_inserted (GtkTreeModel *c_model,
     {
       real_path = gtk_tree_model_filter_remove_root (c_path,
                                                      filter->priv->virtual_root);
-      /* not our kiddo */
+      /* not our child */
       if (!real_path)
         goto done;
     }
@@ -1289,14 +1480,23 @@ gtk_tree_model_filter_row_inserted (GtkTreeModel *c_model,
         if (g_array_index (level->array, FilterElt, i).offset > offset)
           break;
 
+      level->visible_nodes++;
+
       g_array_insert_val (level->array, i, felt);
-      index = i;
 
-      if (!level->parent_level)
-        filter->priv->root_level_visible++;
+      if (level->parent_level || filter->priv->virtual_root)
+        {
+          GtkTreeIter f_iter;
+
+          f_iter.stamp = filter->priv->stamp;
+          f_iter.user_data = level;
+          f_iter.user_data2 = &g_array_index (level->array, FilterElt, i);
+
+          gtk_tree_model_filter_ref_node (GTK_TREE_MODEL (filter), &f_iter);
+        }
     }
 
-  /* another iteration to update the references of childs to parents. */
+  /* another iteration to update the references of children to parents. */
   for (i = 0; i < level->array->len; i++)
     {
       FilterElt *e = &g_array_index (level->array, FilterElt, i);
@@ -1317,14 +1517,24 @@ done_and_emit:
                                                                 FALSE, TRUE);
 
   if (!path)
-    return;
+    goto done;
 
   gtk_tree_model_filter_increment_stamp (filter);
 
-  gtk_tree_model_get_iter (GTK_TREE_MODEL (data), &iter, path);
+  gtk_tree_model_filter_get_iter_full (GTK_TREE_MODEL (data), &iter, path);
+
+  /* get a path taking only visible nodes into account */
+  gtk_tree_path_free (path);
+  path = gtk_tree_model_get_path (GTK_TREE_MODEL (data), &iter);
+
   gtk_tree_model_row_inserted (GTK_TREE_MODEL (data), path, &iter);
 
+  gtk_tree_path_free (path);
+
 done:
+  if (real_path)
+    gtk_tree_path_free (real_path);
+
   if (free_c_path)
     gtk_tree_path_free (c_path);
 }
@@ -1338,10 +1548,20 @@ gtk_tree_model_filter_row_has_child_toggled (GtkTreeModel *c_model,
   GtkTreeModelFilter *filter = GTK_TREE_MODEL_FILTER (data);
   GtkTreePath *path;
   GtkTreeIter iter;
+  FilterLevel *level;
+  FilterElt *elt;
 
   g_return_if_fail (c_path != NULL && c_iter != NULL);
 
-  /* FIXME: does this code work? */
+  /* If we get row-has-child-toggled on the virtual root, and there is
+   * no root level; try to build it now.
+   */
+  if (filter->priv->virtual_root && !filter->priv->root
+      && !gtk_tree_path_compare (c_path, filter->priv->virtual_root))
+    {
+      gtk_tree_model_filter_build_level (filter, NULL, NULL, TRUE);
+      return;
+    }
 
   if (!gtk_tree_model_filter_visible (filter, c_iter))
     return;
@@ -1353,9 +1573,24 @@ gtk_tree_model_filter_row_has_child_toggled (GtkTreeModel *c_model,
   if (!path)
     return;
 
-  gtk_tree_model_get_iter (GTK_TREE_MODEL (data), &iter, path);
-  gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (data), path, &iter);
+  gtk_tree_model_filter_get_iter_full (GTK_TREE_MODEL (data), &iter, path);
+
+  gtk_tree_path_free (path);
+
+  level = FILTER_LEVEL (iter.user_data);
+  elt = FILTER_ELT (iter.user_data2);
+
+  g_assert (elt->visible);
+
+  /* If this node is referenced and has children, build the level so we
+   * can monitor it for changes.
+   */
+  if (elt->ref_count > 1 && gtk_tree_model_iter_has_child (c_model, c_iter))
+    gtk_tree_model_filter_build_level (filter, level, elt, TRUE);
 
+  /* get a path taking only visible nodes into account */
+  path = gtk_tree_model_get_path (GTK_TREE_MODEL (data), &iter);
+  gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (data), path, &iter);
   gtk_tree_path_free (path);
 }
 
@@ -1367,10 +1602,10 @@ gtk_tree_model_filter_row_deleted (GtkTreeModel *c_model,
   GtkTreeModelFilter *filter = GTK_TREE_MODEL_FILTER (data);
   GtkTreePath *path;
   GtkTreeIter iter;
-  FilterElt *elt;
-  FilterLevel *level;
+  FilterElt *elt, *parent = NULL;
+  FilterLevel *level, *parent_level = NULL;
+  gboolean emit_child_toggled = FALSE;
   gint offset;
-  gboolean emit_signal = TRUE;
   gint i;
 
   g_return_if_fail (c_path != NULL);
@@ -1384,6 +1619,9 @@ gtk_tree_model_filter_row_deleted (GtkTreeModel *c_model,
       GtkTreePath *path;
       FilterLevel *level = FILTER_LEVEL (filter->priv->root);
 
+      gtk_tree_model_filter_unref_path (filter, filter->priv->virtual_root);
+      filter->priv->virtual_root_deleted = TRUE;
+
       if (!level)
         return;
 
@@ -1397,7 +1635,7 @@ gtk_tree_model_filter_row_deleted (GtkTreeModel *c_model,
       path = gtk_tree_path_new ();
       gtk_tree_path_append_index (path, 0);
 
-      for (i = 0; i < level->array->len; i++)
+      for (i = 0; i < level->visible_nodes; i++)
         gtk_tree_model_row_deleted (GTK_TREE_MODEL (data), path);
 
       gtk_tree_path_free (path);
@@ -1414,12 +1652,20 @@ gtk_tree_model_filter_row_deleted (GtkTreeModel *c_model,
         {
           gint level;
           gint *v_indices, *c_indices;
+          gboolean common_prefix = TRUE;
 
           level = gtk_tree_path_get_depth (c_path) - 1;
           v_indices = gtk_tree_path_get_indices (filter->priv->virtual_root);
           c_indices = gtk_tree_path_get_indices (c_path);
 
-          if (v_indices[level] > c_indices[level])
+          for (i = 0; i < level; i++)
+            if (v_indices[i] != c_indices[i])
+              {
+                common_prefix = FALSE;
+                break;
+              }
+
+          if (common_prefix && v_indices[level] > c_indices[level])
             (v_indices[level])--;
         }
     }
@@ -1428,119 +1674,117 @@ gtk_tree_model_filter_row_deleted (GtkTreeModel *c_model,
                                                                 c_path,
                                                                 FALSE,
                                                                 FALSE);
+
   if (!path)
     {
-      path = gtk_real_tree_model_filter_convert_child_path_to_path (filter,
-                                                                    c_path,
-                                                                    FALSE,
-                                                                    TRUE);
+      /* The node deleted in the child model is not visible in the
+       * filter model.  We will not emit a signal, just fixup the offsets
+       * of the other nodes.
+       */
+      GtkTreePath *real_path;
 
-      if (!path)
-        {
-          /* fixup the offsets */
-          GtkTreePath *real_path;
+      if (!filter->priv->root)
+        return;
 
-          if (!filter->priv->root)
-            return;
+      level = FILTER_LEVEL (filter->priv->root);
 
-          level = FILTER_LEVEL (filter->priv->root);
+      /* subtract vroot if necessary */
+      if (filter->priv->virtual_root)
+        {
+          real_path = gtk_tree_model_filter_remove_root (c_path,
+                                                         filter->priv->virtual_root);
+          /* we don't handle this */
+          if (!real_path)
+            return;
+        }
+      else
+        real_path = gtk_tree_path_copy (c_path);
 
-          /* subtract vroot if necessary */
-          if (filter->priv->virtual_root)
+      i = 0;
+      if (gtk_tree_path_get_depth (real_path) - 1 >= 1)
+        {
+          /* find the level where the deletion occurred */
+          while (i < gtk_tree_path_get_depth (real_path) - 1)
             {
-              real_path = gtk_tree_model_filter_remove_root (c_path,
-                                                             filter->priv->virtual_root);
-              /* we don't handle this */
-              if (!real_path)
-                return;
-            }
-          else
-            real_path = gtk_tree_path_copy (c_path);
+              gint j;
 
-          i = 0;
-          if (gtk_tree_path_get_depth (real_path) - 1 >= 1)
-            {
-              while (i < gtk_tree_path_get_depth (real_path) - 1)
+              if (!level)
                 {
-                  gint j;
-
-                  if (!level)
-                    {
-                      /* we don't cover this */
-                      gtk_tree_path_free (real_path);
-                      return;
-                    }
-
-                  elt = bsearch_elt_with_offset (level->array,
-                                                 gtk_tree_path_get_indices (real_path)[i],
-                                                 &j);
-
-                  if (!elt || !elt->children)
-                    {
-                      /* parent is filtered out, so no level */
-                      gtk_tree_path_free (real_path);
-                      return;
-                    }
-
-                  level = elt->children;
-                  i++;
+                  /* we don't cover this */
+                  gtk_tree_path_free (real_path);
+                  return;
                 }
-            }
 
-          offset = gtk_tree_path_get_indices (real_path)[gtk_tree_path_get_depth (real_path) - 1];
-          gtk_tree_path_free (real_path);
+              elt = bsearch_elt_with_offset (level->array,
+                                             gtk_tree_path_get_indices (real_path)[i],
+                                             &j);
 
-          if (!level)
-            return;
+              if (!elt || !elt->children)
+                {
+                  /* parent is filtered out, so no level */
+                  gtk_tree_path_free (real_path);
+                  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;
+              level = elt->children;
+              i++;
             }
+        }
 
-          return;
+      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;
+
+      /* decrease offset of all nodes following the deleted node */
+      for (i = 0; i < level->array->len; i++)
+        {
+          elt = &g_array_index (level->array, FilterElt, i);
+          if (elt->offset > offset)
+            elt->offset--;
+          if (elt->children)
+            elt->children->parent_elt = elt;
         }
 
-      emit_signal = FALSE;
+      return;
     }
 
-  gtk_tree_model_get_iter (GTK_TREE_MODEL (data), &iter, path);
+  /* a node was deleted, which was in our cache */
+  gtk_tree_model_filter_get_iter_full (GTK_TREE_MODEL (data), &iter, path);
 
   level = FILTER_LEVEL (iter.user_data);
   elt = FILTER_ELT (iter.user_data2);
   offset = elt->offset;
 
-  if (!level->parent_level && elt->visible)
-    filter->priv->root_level_visible--;
-
-  if (emit_signal)
+  if (elt->visible)
     {
-      if (level->ref_count == 0 && level != filter->priv->root)
-        {
-          gtk_tree_model_filter_increment_stamp (filter);
-          gtk_tree_model_row_deleted (GTK_TREE_MODEL (data), path);
+      /* get a path taking only visible nodes into account */
+      gtk_tree_path_free (path);
+      path = gtk_tree_model_get_path (GTK_TREE_MODEL (filter), &iter);
 
-          gtk_tree_path_free (path);
-          return;
+      level->visible_nodes--;
+
+      if (level->visible_nodes == 0)
+        {
+          emit_child_toggled = TRUE;
+          parent_level = level->parent_level;
+          parent = level->parent_elt;
         }
 
+      /* emit row_deleted */
       gtk_tree_model_filter_increment_stamp (filter);
       gtk_tree_model_row_deleted (GTK_TREE_MODEL (data), path);
       iter.stamp = filter->priv->stamp;
-
-      while (elt->ref_count > 0)
-        gtk_tree_model_filter_real_unref_node (GTK_TREE_MODEL (data), &iter,
-                                               FALSE);
     }
 
+  /* The filter model's reference on the child node is released
+   * below.
+   */
+  while (elt->ref_count > 1)
+    gtk_tree_model_filter_real_unref_node (GTK_TREE_MODEL (data), &iter,
+                                           FALSE);
+
   if (level->array->len == 1)
     {
       /* kill level */
@@ -1550,13 +1794,21 @@ gtk_tree_model_filter_row_deleted (GtkTreeModel *c_model,
     {
       FilterElt *tmp;
 
+      /* release the filter model's reference on the node */
+      if (level->parent_level || filter->priv->virtual_root)
+        gtk_tree_model_filter_unref_node (GTK_TREE_MODEL (filter), &iter);
+      else if (elt->ref_count > 0)
+        gtk_tree_model_filter_real_unref_node (GTK_TREE_MODEL (data), &iter,
+                                               FALSE);
+
       /* remove the row */
       tmp = bsearch_elt_with_offset (level->array, elt->offset, &i);
 
       offset = tmp->offset;
       g_array_remove_index (level->array, i);
 
-      for (i = MAX (--i, 0); i < level->array->len; i++)
+      i--;
+      for (i = MAX (i, 0); i < level->array->len; i++)
         {
           elt = &g_array_index (level->array, FilterElt, i);
           if (elt->offset > offset)
@@ -1566,6 +1818,27 @@ gtk_tree_model_filter_row_deleted (GtkTreeModel *c_model,
         }
     }
 
+  if (emit_child_toggled && parent_level)
+    {
+      GtkTreeIter iter;
+      GtkTreePath *path;
+
+      iter.stamp = filter->priv->stamp;
+      iter.user_data = parent_level;
+      iter.user_data2 = parent;
+
+      /* We set in_row_deleted to TRUE to avoid a level build triggered
+       * by row-has-child-toggled (parent model could call iter_has_child
+       * for example).
+       */
+      filter->priv->in_row_deleted = TRUE;
+      path = gtk_tree_model_get_path (GTK_TREE_MODEL (filter), &iter);
+      gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (filter),
+                                            path, &iter);
+      gtk_tree_path_free (path);
+      filter->priv->in_row_deleted = FALSE;
+    }
+
   gtk_tree_path_free (path);
 }
 
@@ -1593,9 +1866,6 @@ gtk_tree_model_filter_rows_reordered (GtkTreeModel *c_model,
 
   if (c_path == NULL || gtk_tree_path_get_indices (c_path) == NULL)
     {
-      if (!filter->priv->root)
-        return;
-
       length = gtk_tree_model_iter_n_children (c_model, NULL);
 
       if (filter->priv->virtual_root)
@@ -1623,8 +1893,7 @@ gtk_tree_model_filter_rows_reordered (GtkTreeModel *c_model,
 
       /* virtual root anchor reordering */
       if (filter->priv->virtual_root &&
-          gtk_tree_path_get_depth (c_path) <
-          gtk_tree_path_get_depth (filter->priv->virtual_root))
+         gtk_tree_path_is_ancestor (c_path, filter->priv->virtual_root))
         {
           gint new_pos = -1;
           gint length;
@@ -1655,6 +1924,7 @@ gtk_tree_model_filter_rows_reordered (GtkTreeModel *c_model,
                                                                     c_path,
                                                                     FALSE,
                                                                     FALSE);
+
       if (!path && filter->priv->virtual_root &&
           gtk_tree_path_compare (c_path, filter->priv->virtual_root))
         return;
@@ -1673,7 +1943,8 @@ gtk_tree_model_filter_rows_reordered (GtkTreeModel *c_model,
         }
       else
         {
-          gtk_tree_model_get_iter (GTK_TREE_MODEL (data), &iter, path);
+          gtk_tree_model_filter_get_iter_full (GTK_TREE_MODEL (data),
+                                               &iter, path);
 
           level = FILTER_LEVEL (iter.user_data);
           elt = FILTER_ELT (iter.user_data2);
@@ -1691,8 +1962,11 @@ gtk_tree_model_filter_rows_reordered (GtkTreeModel *c_model,
         }
     }
 
-  if (level->array->len < 1)
-    return;
+  if (!level || level->array->len < 1)
+    {
+      gtk_tree_path_free (path);
+      return;
+    }
 
   /* NOTE: we do not bail out here if level->array->len < 2 like
    * GtkTreeModelSort does. This because we do some special tricky
@@ -1742,8 +2016,14 @@ gtk_tree_model_filter_rows_reordered (GtkTreeModel *c_model,
     gtk_tree_model_rows_reordered (GTK_TREE_MODEL (data), path, NULL,
                                    tmp_array);
   else
-    gtk_tree_model_rows_reordered (GTK_TREE_MODEL (data), path, &iter,
-                                   tmp_array);
+    {
+      /* get a path taking only visible nodes into account */
+      gtk_tree_path_free (path);
+      path = gtk_tree_model_get_path (GTK_TREE_MODEL (data), &iter);
+
+      gtk_tree_model_rows_reordered (GTK_TREE_MODEL (data), path, &iter,
+                                     tmp_array);
+    }
 
   /* done */
   g_free (tmp_array);
@@ -1751,10 +2031,18 @@ gtk_tree_model_filter_rows_reordered (GtkTreeModel *c_model,
 }
 
 /* TreeModelIface implementation */
-static guint
+static GtkTreeModelFlags
 gtk_tree_model_filter_get_flags (GtkTreeModel *model)
 {
+  GtkTreeModelFlags flags;
+
   g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (model), 0);
+  g_return_val_if_fail (GTK_TREE_MODEL_FILTER (model)->priv->child_model != NULL, 0);
+
+  flags = gtk_tree_model_get_flags (GTK_TREE_MODEL_FILTER (model)->priv->child_model);
+
+  if ((flags & GTK_TREE_MODEL_LIST_ONLY) == GTK_TREE_MODEL_LIST_ONLY)
+    return GTK_TREE_MODEL_LIST_ONLY;
 
   return 0;
 }
@@ -1801,6 +2089,65 @@ gtk_tree_model_filter_get_column_type (GtkTreeModel *model,
   return gtk_tree_model_get_column_type (filter->priv->child_model, index);
 }
 
+/* A special case of _get_iter; this function can also get iters which
+ * are not visible.  These iters should ONLY be passed internally, never
+ * pass those along with a signal emission.
+ */
+static gboolean
+gtk_tree_model_filter_get_iter_full (GtkTreeModel *model,
+                                     GtkTreeIter  *iter,
+                                     GtkTreePath  *path)
+{
+  GtkTreeModelFilter *filter = (GtkTreeModelFilter *)model;
+  gint *indices;
+  FilterLevel *level;
+  FilterElt *elt;
+  gint depth, i;
+  g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (model), FALSE);
+  g_return_val_if_fail (filter->priv->child_model != NULL, FALSE);
+
+  indices = gtk_tree_path_get_indices (path);
+
+  if (filter->priv->root == NULL)
+    gtk_tree_model_filter_build_level (filter, NULL, NULL, FALSE);
+  level = FILTER_LEVEL (filter->priv->root);
+
+  depth = gtk_tree_path_get_depth (path);
+  if (!depth)
+    {
+      iter->stamp = 0;
+      return FALSE;
+    }
+
+  for (i = 0; i < depth - 1; i++)
+    {
+      if (!level || indices[i] >= level->array->len)
+        {
+          return FALSE;
+        }
+
+      elt = gtk_tree_model_filter_get_nth (filter, level, indices[i]);
+
+      if (!elt->children)
+        gtk_tree_model_filter_build_level (filter, level, elt, FALSE);
+      level = elt->children;
+    }
+
+  if (!level || indices[i] >= level->array->len)
+    {
+      iter->stamp = 0;
+      return FALSE;
+    }
+
+  iter->stamp = filter->priv->stamp;
+  iter->user_data = level;
+
+  elt = gtk_tree_model_filter_get_nth (filter, level, indices[depth - 1]);
+  iter->user_data2 = elt;
+
+  return TRUE;
+}
+
 static gboolean
 gtk_tree_model_filter_get_iter (GtkTreeModel *model,
                                 GtkTreeIter  *iter,
@@ -1809,15 +2156,15 @@ gtk_tree_model_filter_get_iter (GtkTreeModel *model,
   GtkTreeModelFilter *filter = (GtkTreeModelFilter *)model;
   gint *indices;
   FilterLevel *level;
+  FilterElt *elt;
   gint depth, i;
-
   g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (model), FALSE);
   g_return_val_if_fail (filter->priv->child_model != NULL, FALSE);
 
   indices = gtk_tree_path_get_indices (path);
 
   if (filter->priv->root == NULL)
-    gtk_tree_model_filter_build_level (filter, NULL, NULL);
+    gtk_tree_model_filter_build_level (filter, NULL, NULL, FALSE);
   level = FILTER_LEVEL (filter->priv->root);
 
   depth = gtk_tree_path_get_depth (path);
@@ -1829,20 +2176,19 @@ gtk_tree_model_filter_get_iter (GtkTreeModel *model,
 
   for (i = 0; i < depth - 1; i++)
     {
-      if (!level || indices[i] >= level->array->len)
+      if (!level || indices[i] >= level->visible_nodes)
         {
           return FALSE;
         }
 
-      if (!g_array_index (level->array, FilterElt, indices[i]).children)
-        gtk_tree_model_filter_build_level (filter, level,
-                                           &g_array_index (level->array,
-                                                           FilterElt,
-                                                           indices[i]));
-      level = g_array_index (level->array, FilterElt, indices[i]).children;
+      elt = gtk_tree_model_filter_get_nth_visible (filter, level, indices[i]);
+
+      if (!elt->children)
+        gtk_tree_model_filter_build_level (filter, level, elt, FALSE);
+      level = elt->children;
     }
 
-  if (!level || indices[i] >= level->array->len)
+  if (!level || indices[i] >= level->visible_nodes)
     {
       iter->stamp = 0;
       return FALSE;
@@ -1850,8 +2196,10 @@ gtk_tree_model_filter_get_iter (GtkTreeModel *model,
 
   iter->stamp = filter->priv->stamp;
   iter->user_data = level;
-  iter->user_data2 = &g_array_index (level->array, FilterElt,
-                                     indices[depth - 1]);
+
+  elt = gtk_tree_model_filter_get_nth_visible (filter, level,
+                                               indices[depth - 1]);
+  iter->user_data2 = elt;
 
   return TRUE;
 }
@@ -1872,10 +2220,23 @@ gtk_tree_model_filter_get_path (GtkTreeModel *model,
   level = iter->user_data;
   elt = iter->user_data2;
 
+  if (!elt->visible)
+    return NULL;
+
   while (level)
     {
-      gtk_tree_path_prepend_index (retval,
-                                   elt - FILTER_ELT (level->array->data));
+      int i = 0, index = 0;
+
+      while (&g_array_index (level->array, FilterElt, i) != elt)
+        {
+          if (g_array_index (level->array, FilterElt, i).visible)
+            index++;
+          i++;
+
+          g_assert (i < level->array->len);
+        }
+
+      gtk_tree_path_prepend_index (retval, index);
       elt = level->parent_elt;
       level = level->parent_level;
     }
@@ -1919,6 +2280,7 @@ static gboolean
 gtk_tree_model_filter_iter_next (GtkTreeModel *model,
                                  GtkTreeIter  *iter)
 {
+  int i;
   FilterLevel *level;
   FilterElt *elt;
 
@@ -1929,15 +2291,24 @@ gtk_tree_model_filter_iter_next (GtkTreeModel *model,
   level = iter->user_data;
   elt = iter->user_data2;
 
-  if (elt - FILTER_ELT (level->array->data) >= level->array->len - 1)
+  i = elt - FILTER_ELT (level->array->data);
+
+  while (i < level->array->len - 1)
     {
-      iter->stamp = 0;
-      return FALSE;
+      i++;
+      elt++;
+
+      if (elt->visible)
+        {
+          iter->user_data2 = elt;
+          return TRUE;
+        }
     }
 
-  iter->user_data2 = elt + 1;
+  /* no next visible iter */
+  iter->stamp = 0;
 
-  return TRUE;
+  return FALSE;
 }
 
 static gboolean
@@ -1956,35 +2327,74 @@ gtk_tree_model_filter_iter_children (GtkTreeModel *model,
 
   if (!parent)
     {
+      int i = 0;
+
       if (!filter->priv->root)
-        gtk_tree_model_filter_build_level (filter, NULL, NULL);
+        gtk_tree_model_filter_build_level (filter, NULL, NULL, FALSE);
       if (!filter->priv->root)
         return FALSE;
 
       level = filter->priv->root;
+
+      if (!level->visible_nodes)
+        return FALSE;
+
       iter->stamp = filter->priv->stamp;
       iter->user_data = level;
-      iter->user_data2 = level->array->data;
+
+      while (i < level->array->len)
+        {
+          if (!g_array_index (level->array, FilterElt, i).visible)
+            {
+              i++;
+              continue;
+            }
+
+          iter->user_data2 = &g_array_index (level->array, FilterElt, i);
+          return TRUE;
+        }
+
+      iter->stamp = 0;
+      return FALSE;
     }
   else
     {
+      int i = 0;
+
       if (FILTER_ELT (parent->user_data2)->children == NULL)
         gtk_tree_model_filter_build_level (filter,
                                            FILTER_LEVEL (parent->user_data),
-                                           FILTER_ELT (parent->user_data2));
+                                           FILTER_ELT (parent->user_data2),
+                                           FALSE);
       if (FILTER_ELT (parent->user_data2)->children == NULL)
         return FALSE;
 
-      /* empty array? */
-      if (FILTER_ELT (parent->user_data2)->children->array->len <= 0)
+      if (FILTER_ELT (parent->user_data2)->children->visible_nodes <= 0)
         return FALSE;
 
       iter->stamp = filter->priv->stamp;
       iter->user_data = FILTER_ELT (parent->user_data2)->children;
-      iter->user_data2 = FILTER_LEVEL (iter->user_data)->array->data;
+
+      level = FILTER_LEVEL (iter->user_data);
+
+      while (i < level->array->len)
+        {
+          if (!g_array_index (level->array, FilterElt, i).visible)
+            {
+              i++;
+              continue;
+            }
+
+          iter->user_data2 = &g_array_index (level->array, FilterElt, i);
+          return TRUE;
+        }
+
+      iter->stamp = 0;
+      return FALSE;
     }
 
-  return TRUE;
+  iter->stamp = 0;
+  return FALSE;
 }
 
 static gboolean
@@ -2004,18 +2414,18 @@ gtk_tree_model_filter_iter_has_child (GtkTreeModel *model,
   gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (model), &child_iter, iter);
   elt = FILTER_ELT (iter->user_data2);
 
+  if (!elt->visible)
+    return FALSE;
+
   /* we need to build the level to check if not all children are filtered
    * out
    */
   if (!elt->children
       && gtk_tree_model_iter_has_child (filter->priv->child_model, &child_iter))
     gtk_tree_model_filter_build_level (filter, FILTER_LEVEL (iter->user_data),
-                                       elt);
+                                       elt, FALSE);
 
-  /* FIXME: we should prolly count the visible nodes here, just like in
-   * _iter_n_children.
-   */
-  if (elt->children && elt->children->array->len > 0)
+  if (elt->children && elt->children->visible_nodes > 0)
     return TRUE;
 
   return FALSE;
@@ -2037,34 +2447,29 @@ gtk_tree_model_filter_iter_n_children (GtkTreeModel *model,
   if (!iter)
     {
       if (!filter->priv->root)
-        gtk_tree_model_filter_build_level (filter, NULL, NULL);
+        gtk_tree_model_filter_build_level (filter, NULL, NULL, FALSE);
+
+      if (filter->priv->root)
+        return FILTER_LEVEL (filter->priv->root)->visible_nodes;
 
-      /* count visible nodes */
-      return filter->priv->root_level_visible;
+      return 0;
     }
 
   elt = FILTER_ELT (iter->user_data2);
+
+  if (!elt->visible)
+    return 0;
+
   gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (model), &child_iter, iter);
 
   if (!elt->children &&
       gtk_tree_model_iter_has_child (filter->priv->child_model, &child_iter))
     gtk_tree_model_filter_build_level (filter,
                                        FILTER_LEVEL (iter->user_data),
-                                       elt);
-
-  if (elt->children && elt->children->array->len)
-    {
-      int i = 0;
-      int count = 0;
-      GArray *a = elt->children->array;
+                                       elt, FALSE);
 
-      /* count visible nodes */
-      for (i = 0; i < a->len; i++)
-        if (g_array_index (a, FilterElt, i).visible)
-          count++;
-
-      return count;
-    }
+  if (elt->children)
+    return elt->children->visible_nodes;
 
   return 0;
 }
@@ -2075,6 +2480,7 @@ gtk_tree_model_filter_iter_nth_child (GtkTreeModel *model,
                                       GtkTreeIter  *parent,
                                       gint          n)
 {
+  FilterElt *elt;
   FilterLevel *level;
   GtkTreeIter children;
 
@@ -2082,7 +2488,7 @@ gtk_tree_model_filter_iter_nth_child (GtkTreeModel *model,
   if (parent)
     g_return_val_if_fail (GTK_TREE_MODEL_FILTER (model)->priv->stamp == parent->stamp, FALSE);
 
-  /* use this instead of has_Child to force us to build the level, if needed */
+  /* use this instead of has_child to force us to build the level, if needed */
   if (gtk_tree_model_filter_iter_children (model, &children, parent) == FALSE)
     {
       iter->stamp = 0;
@@ -2090,15 +2496,20 @@ gtk_tree_model_filter_iter_nth_child (GtkTreeModel *model,
     }
 
   level = children.user_data;
-  if (n >= level->array->len)
+  elt = FILTER_ELT (level->array->data);
+
+  if (n >= level->visible_nodes)
     {
       iter->stamp = 0;
       return FALSE;
     }
 
+  elt = gtk_tree_model_filter_get_nth_visible (GTK_TREE_MODEL_FILTER (model),
+                                               level, n);
+
   iter->stamp = GTK_TREE_MODEL_FILTER (model)->priv->stamp;
   iter->user_data = level;
-  iter->user_data2 = &g_array_index (level->array, FilterElt, n);
+  iter->user_data2 = elt;
 
   return TRUE;
 }
@@ -2157,19 +2568,16 @@ gtk_tree_model_filter_ref_node (GtkTreeModel *model,
       FilterElt *parent_elt = level->parent_elt;
 
       /* we were at zero -- time to decrease the zero_ref_count val */
-      do
+      while (parent_level)
         {
-          if (parent_elt)
-            parent_elt->zero_ref_count--;
+         parent_elt->zero_ref_count--;
 
-          if (parent_level)
-            {
-              parent_elt = parent_level->parent_elt;
-              parent_level = parent_level->parent_level;
-            }
+         parent_elt = parent_level->parent_elt;
+         parent_level = parent_level->parent_level;
         }
-      while (parent_level);
-      filter->priv->zero_ref_count--;
+
+      if (filter->priv->root != level)
+       filter->priv->zero_ref_count--;
     }
 }
 
@@ -2220,10 +2628,68 @@ gtk_tree_model_filter_real_unref_node (GtkTreeModel *model,
           parent_elt = parent_level->parent_elt;
           parent_level = parent_level->parent_level;
         }
-      filter->priv->zero_ref_count++;
+
+      if (filter->priv->root != level)
+       filter->priv->zero_ref_count++;
     }
 }
 
+/* TreeDragSource interface implementation */
+static gboolean
+gtk_tree_model_filter_row_draggable (GtkTreeDragSource *drag_source,
+                                     GtkTreePath       *path)
+{
+  GtkTreeModelFilter *tree_model_filter = (GtkTreeModelFilter *)drag_source;
+  GtkTreePath *child_path;
+  gboolean draggable;
+
+  g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (drag_source), FALSE);
+  g_return_val_if_fail (path != NULL, FALSE);
+
+  child_path = gtk_tree_model_filter_convert_path_to_child_path (tree_model_filter, path);
+  draggable = gtk_tree_drag_source_row_draggable (GTK_TREE_DRAG_SOURCE (tree_model_filter->priv->child_model), child_path);
+  gtk_tree_path_free (child_path);
+
+  return draggable;
+}
+
+static gboolean
+gtk_tree_model_filter_drag_data_get (GtkTreeDragSource *drag_source,
+                                     GtkTreePath       *path,
+                                     GtkSelectionData  *selection_data)
+{
+  GtkTreeModelFilter *tree_model_filter = (GtkTreeModelFilter *)drag_source;
+  GtkTreePath *child_path;
+  gboolean gotten;
+
+  g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (drag_source), FALSE);
+  g_return_val_if_fail (path != NULL, FALSE);
+
+  child_path = gtk_tree_model_filter_convert_path_to_child_path (tree_model_filter, path);
+  gotten = gtk_tree_drag_source_drag_data_get (GTK_TREE_DRAG_SOURCE (tree_model_filter->priv->child_model), child_path, selection_data);
+  gtk_tree_path_free (child_path);
+
+  return gotten;
+}
+
+static gboolean
+gtk_tree_model_filter_drag_data_delete (GtkTreeDragSource *drag_source,
+                                        GtkTreePath       *path)
+{
+  GtkTreeModelFilter *tree_model_filter = (GtkTreeModelFilter *)drag_source;
+  GtkTreePath *child_path;
+  gboolean deleted;
+
+  g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (drag_source), FALSE);
+  g_return_val_if_fail (path != NULL, FALSE);
+
+  child_path = gtk_tree_model_filter_convert_path_to_child_path (tree_model_filter, path);
+  deleted = gtk_tree_drag_source_drag_data_delete (GTK_TREE_DRAG_SOURCE (tree_model_filter->priv->child_model), child_path);
+  gtk_tree_path_free (child_path);
+
+  return deleted;
+}
+
 /* bits and pieces */
 static void
 gtk_tree_model_filter_set_model (GtkTreeModelFilter *filter,
@@ -2233,15 +2699,15 @@ gtk_tree_model_filter_set_model (GtkTreeModelFilter *filter,
 
   if (filter->priv->child_model)
     {
-      g_signal_handler_disconnect (G_OBJECT (filter->priv->child_model),
+      g_signal_handler_disconnect (filter->priv->child_model,
                                    filter->priv->changed_id);
-      g_signal_handler_disconnect (G_OBJECT (filter->priv->child_model),
+      g_signal_handler_disconnect (filter->priv->child_model,
                                    filter->priv->inserted_id);
-      g_signal_handler_disconnect (G_OBJECT (filter->priv->child_model),
+      g_signal_handler_disconnect (filter->priv->child_model,
                                    filter->priv->has_child_toggled_id);
-      g_signal_handler_disconnect (G_OBJECT (filter->priv->child_model),
+      g_signal_handler_disconnect (filter->priv->child_model,
                                    filter->priv->deleted_id);
-      g_signal_handler_disconnect (G_OBJECT (filter->priv->child_model),
+      g_signal_handler_disconnect (filter->priv->child_model,
                                    filter->priv->reordered_id);
 
       /* reset our state */
@@ -2249,16 +2715,17 @@ gtk_tree_model_filter_set_model (GtkTreeModelFilter *filter,
         gtk_tree_model_filter_free_level (filter, filter->priv->root);
 
       filter->priv->root = NULL;
-      g_object_unref (G_OBJECT (filter->priv->child_model));
+      g_object_unref (filter->priv->child_model);
       filter->priv->visible_column = -1;
-      /* FIXME: destroy more crack here? the funcs? */
+
+      /* FIXME: do we need to destroy more here? */
     }
 
   filter->priv->child_model = child_model;
 
   if (child_model)
     {
-      g_object_ref (G_OBJECT (filter->priv->child_model));
+      g_object_ref (filter->priv->child_model);
       filter->priv->changed_id =
         g_signal_connect (child_model, "row_changed",
                           G_CALLBACK (gtk_tree_model_filter_row_changed),
@@ -2285,6 +2752,48 @@ gtk_tree_model_filter_set_model (GtkTreeModelFilter *filter,
     }
 }
 
+static void
+gtk_tree_model_filter_ref_path (GtkTreeModelFilter *filter,
+                                GtkTreePath        *path)
+{
+  int len;
+  GtkTreePath *p;
+
+  len = gtk_tree_path_get_depth (path);
+  p = gtk_tree_path_copy (path);
+  while (len--)
+    {
+      GtkTreeIter iter;
+
+      gtk_tree_model_get_iter (GTK_TREE_MODEL (filter->priv->child_model), &iter, p);
+      gtk_tree_model_ref_node (GTK_TREE_MODEL (filter->priv->child_model), &iter);
+      gtk_tree_path_up (p);
+    }
+
+  gtk_tree_path_free (p);
+}
+
+static void
+gtk_tree_model_filter_unref_path (GtkTreeModelFilter *filter,
+                                  GtkTreePath        *path)
+{
+  int len;
+  GtkTreePath *p;
+
+  len = gtk_tree_path_get_depth (path);
+  p = gtk_tree_path_copy (path);
+  while (len--)
+    {
+      GtkTreeIter iter;
+
+      gtk_tree_model_get_iter (GTK_TREE_MODEL (filter->priv->child_model), &iter, p);
+      gtk_tree_model_unref_node (GTK_TREE_MODEL (filter->priv->child_model), &iter);
+      gtk_tree_path_up (p);
+    }
+
+  gtk_tree_path_free (p);
+}
+
 static void
 gtk_tree_model_filter_set_root (GtkTreeModelFilter *filter,
                                 GtkTreePath        *root)
@@ -2308,20 +2817,29 @@ gtk_tree_model_filter_set_root (GtkTreeModelFilter *filter,
  * and @root as the virtual root.
  *
  * Return value: A new #GtkTreeModel.
+ *
+ * Since: 2.4
  */
 GtkTreeModel *
 gtk_tree_model_filter_new (GtkTreeModel *child_model,
                            GtkTreePath  *root)
 {
   GtkTreeModel *retval;
+  GtkTreeModelFilter *filter;
 
   g_return_val_if_fail (GTK_IS_TREE_MODEL (child_model), NULL);
 
-  retval = GTK_TREE_MODEL (g_object_new (gtk_tree_model_filter_get_type (), NULL));
+  retval = g_object_new (GTK_TYPE_TREE_MODEL_FILTER, 
+                        "child-model", child_model,
+                        "virtual-root", root,
+                        NULL);
 
-  gtk_tree_model_filter_set_model (GTK_TREE_MODEL_FILTER (retval),
-                                   child_model);
-  gtk_tree_model_filter_set_root (GTK_TREE_MODEL_FILTER (retval), root);
+  filter = GTK_TREE_MODEL_FILTER (retval);
+  if (filter->priv->virtual_root)
+    {
+      gtk_tree_model_filter_ref_path (filter, filter->priv->virtual_root);
+      filter->priv->virtual_root_deleted = FALSE;
+    }
 
   return retval;
 }
@@ -2333,6 +2851,8 @@ gtk_tree_model_filter_new (GtkTreeModel *child_model,
  * Returns a pointer to the child model of @filter.
  *
  * Return value: A pointer to a #GtkTreeModel.
+ *
+ * Since: 2.4
  */
 GtkTreeModel *
 gtk_tree_model_filter_get_model (GtkTreeModelFilter *filter)
@@ -2352,6 +2872,13 @@ gtk_tree_model_filter_get_model (GtkTreeModelFilter *filter)
  * 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.
+ *
+ * If the condition calculated by the function changes over time (e.g. because
+ * it depends on some global parameters), you must call 
+ * gtk_tree_model_filter_refilter() to keep the visibility information of 
+ * the model uptodate.
+ *
+ * Since: 2.4
  */
 void
 gtk_tree_model_filter_set_visible_func (GtkTreeModelFilter            *filter,
@@ -2383,12 +2910,19 @@ gtk_tree_model_filter_set_visible_func (GtkTreeModelFilter            *filter,
  * @filter: A #GtkTreeModelFilter.
  * @n_columns: The number of columns in the filter model.
  * @types: The #GType<!-- -->s of the columns.
- * @func: A #GtkTreeModelFilterModifyFunc, or %NULL.
+ * @func: A #GtkTreeModelFilterModifyFunc
  * @data: User data to pass to the modify function, or %NULL.
  * @destroy: Destroy notifier of @data, or %NULL.
  *
- * Sets the @filter to have @n_columns columns with @types. If @func
- * is not %NULL, it will set @func to be the modify function of @filter.
+ * With the @n_columns and @types parameters, you give an array of column
+ * types for this model (which will be exposed to the parent model/view).
+ * The @func, @data and @destroy parameters are for specifying the modify
+ * function. The modify function will get called for <emphasis>each</emphasis>
+ * data access, the goal of the modify function is to return the data which 
+ * should be displayed at the location specified using the parameters of the 
+ * modify function.
+ *
+ * Since: 2.4
  */
 void
 gtk_tree_model_filter_set_modify_func (GtkTreeModelFilter           *filter,
@@ -2429,6 +2963,8 @@ gtk_tree_model_filter_set_modify_func (GtkTreeModelFilter           *filter,
  * 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.
+ *
+ * Since: 2.4
  */
 void
 gtk_tree_model_filter_set_visible_column (GtkTreeModelFilter *filter,
@@ -2452,32 +2988,43 @@ gtk_tree_model_filter_set_visible_column (GtkTreeModelFilter *filter,
  * @child_iter: A valid #GtkTreeIter pointing to a row on the child model.
  *
  * Sets @filter_iter to point to the row in @filter that corresponds to the
- * row pointed at by @child_iter.
+ * row pointed at by @child_iter.  If @filter_iter was not set, %FALSE is
+ * returned.
+ *
+ * Return value: %TRUE, if @filter_iter was set, i.e. if @child_iter is a
+ * valid iterator pointing to a visible row in child model.
+ *
+ * Since: 2.4
  */
-void
+gboolean
 gtk_tree_model_filter_convert_child_iter_to_iter (GtkTreeModelFilter *filter,
                                                   GtkTreeIter        *filter_iter,
                                                   GtkTreeIter        *child_iter)
 {
+  gboolean ret;
   GtkTreePath *child_path, *path;
 
-  g_return_if_fail (GTK_IS_TREE_MODEL_FILTER (filter));
-  g_return_if_fail (filter->priv->child_model != NULL);
-  g_return_if_fail (filter_iter != NULL);
-  g_return_if_fail (child_iter != NULL);
+  g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (filter), FALSE);
+  g_return_val_if_fail (filter->priv->child_model != NULL, FALSE);
+  g_return_val_if_fail (filter_iter != NULL, FALSE);
+  g_return_val_if_fail (child_iter != NULL, FALSE);
 
   filter_iter->stamp = 0;
 
   child_path = gtk_tree_model_get_path (filter->priv->child_model, child_iter);
-  g_return_if_fail (child_path != NULL);
+  g_return_val_if_fail (child_path != NULL, FALSE);
 
   path = gtk_tree_model_filter_convert_child_path_to_path (filter,
                                                            child_path);
   gtk_tree_path_free (child_path);
-  g_return_if_fail (path != NULL);
 
-  gtk_tree_model_get_iter (GTK_TREE_MODEL (filter), filter_iter, path);
+  if (!path)
+    return FALSE;
+
+  ret = gtk_tree_model_get_iter (GTK_TREE_MODEL (filter), filter_iter, path);
   gtk_tree_path_free (path);
+
+  return ret;
 }
 
 /**
@@ -2487,6 +3034,8 @@ gtk_tree_model_filter_convert_child_iter_to_iter (GtkTreeModelFilter *filter,
  * @filter_iter: A valid #GtkTreeIter pointing to a row on @filter.
  *
  * Sets @child_iter to point to the row pointed to by @filter_iter.
+ *
+ * Since: 2.4
  */
 void
 gtk_tree_model_filter_convert_iter_to_child_iter (GtkTreeModelFilter *filter,
@@ -2515,11 +3064,12 @@ gtk_tree_model_filter_convert_iter_to_child_iter (GtkTreeModelFilter *filter,
     }
 }
 
+/* The path returned can only be used internally in the filter model. */
 static GtkTreePath *
 gtk_real_tree_model_filter_convert_child_path_to_path (GtkTreeModelFilter *filter,
                                                        GtkTreePath        *child_path,
                                                        gboolean            build_levels,
-                                                       gboolean            fetch_childs)
+                                                       gboolean            fetch_children)
 {
   gint *child_indices;
   GtkTreePath *retval;
@@ -2545,7 +3095,7 @@ gtk_real_tree_model_filter_convert_child_path_to_path (GtkTreeModelFilter *filte
   child_indices = gtk_tree_path_get_indices (real_path);
 
   if (filter->priv->root == NULL && build_levels)
-    gtk_tree_model_filter_build_level (filter, NULL, NULL);
+    gtk_tree_model_filter_build_level (filter, NULL, NULL, FALSE);
   level = FILTER_LEVEL (filter->priv->root);
 
   for (i = 0; i < gtk_tree_path_get_depth (real_path); i++)
@@ -2565,12 +3115,12 @@ gtk_real_tree_model_filter_convert_child_path_to_path (GtkTreeModelFilter *filte
         {
           gtk_tree_path_append_index (retval, j);
           if (!tmp->children && build_levels)
-            gtk_tree_model_filter_build_level (filter, level, tmp);
+            gtk_tree_model_filter_build_level (filter, level, tmp, FALSE);
           level = tmp->children;
           found_child = TRUE;
         }
 
-      if (!found_child && fetch_childs)
+      if (!found_child && fetch_children)
         {
           tmp = gtk_tree_model_filter_fetch_child (filter, level,
                                                    child_indices[i],
@@ -2587,11 +3137,11 @@ gtk_real_tree_model_filter_convert_child_path_to_path (GtkTreeModelFilter *filte
 
           gtk_tree_path_append_index (retval, j);
           if (!tmp->children && build_levels)
-            gtk_tree_model_filter_build_level (filter, level, tmp);
+            gtk_tree_model_filter_build_level (filter, level, tmp, FALSE);
           level = tmp->children;
           found_child = TRUE;
         }
-      else if (!found_child && !fetch_childs)
+      else if (!found_child && !fetch_children)
         {
           /* no path */
           gtk_tree_path_free (real_path);
@@ -2612,19 +3162,40 @@ gtk_real_tree_model_filter_convert_child_path_to_path (GtkTreeModelFilter *filte
  * Converts @child_path to a path relative to @filter. That is, @child_path
  * points to a path in the child model. The rerturned path will point to the
  * same row in the filtered model. If @child_path isn't a valid path on the
- * child model, then %NULL is returned.
+ * child model or points to a row which is not visible in @filter, then %NULL
+ * is returned.
  *
  * Return value: A newly allocated #GtkTreePath, or %NULL.
+ *
+ * Since: 2.4
  */
 GtkTreePath *
 gtk_tree_model_filter_convert_child_path_to_path (GtkTreeModelFilter *filter,
                                                   GtkTreePath        *child_path)
 {
+  GtkTreeIter iter;
+  GtkTreePath *path;
+
   /* this function does the sanity checks */
-  return gtk_real_tree_model_filter_convert_child_path_to_path (filter,
+  path = gtk_real_tree_model_filter_convert_child_path_to_path (filter,
                                                                 child_path,
                                                                 TRUE,
                                                                 TRUE);
+
+  if (!path)
+      return NULL;
+
+  /* get a new path which only takes visible nodes into account.
+   * -- if this gives any performance issues, we can write a special
+   *    version of convert_child_path_to_path immediately returning
+   *    a visible-nodes-only path.
+   */
+  gtk_tree_model_filter_get_iter_full (GTK_TREE_MODEL (filter), &iter, path);
+
+  gtk_tree_path_free (path);
+  path = gtk_tree_model_get_path (GTK_TREE_MODEL (filter), &iter);
+
+  return path;
 }
 
 /**
@@ -2638,6 +3209,8 @@ gtk_tree_model_filter_convert_child_path_to_path (GtkTreeModelFilter *filter,
  * does not point to a location in the child model, %NULL is returned.
  *
  * Return value: A newly allocated #GtkTreePath, or %NULL.
+ *
+ * Since: 2.4
  */
 GtkTreePath *
 gtk_tree_model_filter_convert_path_to_child_path (GtkTreeModelFilter *filter,
@@ -2656,30 +3229,33 @@ gtk_tree_model_filter_convert_path_to_child_path (GtkTreeModelFilter *filter,
   retval = gtk_tree_path_new ();
   filter_indices = gtk_tree_path_get_indices (filter_path);
   if (!filter->priv->root)
-    gtk_tree_model_filter_build_level (filter, NULL, NULL);
+    gtk_tree_model_filter_build_level (filter, NULL, NULL, FALSE);
   level = FILTER_LEVEL (filter->priv->root);
 
   for (i = 0; i < gtk_tree_path_get_depth (filter_path); i++)
     {
-      gint count = filter_indices[i];
+      FilterElt *elt;
 
-      if (!level || level->array->len <= filter_indices[i])
+      if (!level || level->visible_nodes <= filter_indices[i])
         {
           gtk_tree_path_free (retval);
           return NULL;
         }
 
-      if (g_array_index (level->array, FilterElt, count).children == NULL)
-        gtk_tree_model_filter_build_level (filter, level, &g_array_index (level->array, FilterElt, count));
+      elt = gtk_tree_model_filter_get_nth_visible (filter, level,
+                                                   filter_indices[i]);
+
+      if (elt->children == NULL)
+        gtk_tree_model_filter_build_level (filter, level, elt, FALSE);
 
-      if (!level || level->array->len <= filter_indices[i])
+      if (!level || level->visible_nodes <= filter_indices[i])
         {
           gtk_tree_path_free (retval);
           return NULL;
         }
 
-      gtk_tree_path_append_index (retval, g_array_index (level->array, FilterElt, count).offset);
-      level = g_array_index (level->array, FilterElt, count).children;
+      gtk_tree_path_append_index (retval, elt->offset);
+      level = elt->children;
     }
 
   /* apply vroot */
@@ -2716,6 +3292,8 @@ gtk_tree_model_filter_refilter_helper (GtkTreeModel *model,
  *
  * Emits ::row_changed for each row in the child model, which causes
  * the filter to re-evaluate whether a row is visible or not.
+ *
+ * Since: 2.4
  */
 void
 gtk_tree_model_filter_refilter (GtkTreeModelFilter *filter)
@@ -2737,14 +3315,19 @@ gtk_tree_model_filter_refilter (GtkTreeModelFilter *filter)
  * gtk_tree_model_ref_node(). This might be useful if the child model
  * being filtered is static (and doesn't change often) and there has been
  * a lot of unreffed access to nodes. As a side effect of this function,
- * all unreffed itters will be invalid.
+ * all unreffed iters will be invalid.
+ *
+ * Since: 2.4
  */
 void
 gtk_tree_model_filter_clear_cache (GtkTreeModelFilter *filter)
 {
   g_return_if_fail (GTK_IS_TREE_MODEL_FILTER (filter));
 
-  if (filter->priv->zero_ref_count)
+  if (filter->priv->zero_ref_count > 0)
     gtk_tree_model_filter_clear_cache_helper (filter,
                                               FILTER_LEVEL (filter->priv->root));
 }
+
+#define __GTK_TREE_MODEL_FILTER_C__
+#include "gtkaliasdef.c"