]> Pileus Git - ~andy/gtk/blobdiff - gtk/gtktreemodelfilter.c
remember the sensitivity of the steppers in GtkRangeLayout, update it in
[~andy/gtk] / gtk / gtktreemodelfilter.c
index 15aca9ae214bc40736c8995a0c9ced0c803a410b..4349004c50cc249a528c9d2507d4587143861fbc 100644 (file)
  * Boston, MA 02111-1307, USA.
  */
 
+#include <config.h>
 #include "gtktreemodelfilter.h"
 #include "gtkintl.h"
+#include "gtktreednd.h"
+#include "gtkalias.h"
+#include "gtkprivate.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,6 +71,7 @@ struct _FilterLevel
 {
   GArray *array;
   gint ref_count;
+  gint visible_nodes;
 
   FilterElt *parent_elt;
   FilterLevel *parent_level;
@@ -65,8 +87,6 @@ struct _GtkTreeModelFilterPrivate
   GtkTreeModel *child_model;
   gint zero_ref_count;
 
-  guint root_level_visible;
-
   GtkTreePath *virtual_root;
 
   GtkTreeModelFilterVisibleFunc visible_func;
@@ -77,13 +97,15 @@ 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;
+
   /* signal ids */
   guint changed_id;
   guint inserted_id;
@@ -107,9 +129,8 @@ enum
 #define FILTER_LEVEL(filter_level) ((FilterLevel *)filter_level)
 
 /* general code (object/interface init, properties, etc) */
-static void         gtk_tree_model_filter_init                            (GtkTreeModelFilter      *filter);
-static void         gtk_tree_model_filter_class_init                      (GtkTreeModelFilterClass *filter_class);
 static void         gtk_tree_model_filter_tree_model_init                 (GtkTreeModelIface       *iface);
+static void         gtk_tree_model_filter_drag_source_init                (GtkTreeDragSourceIface  *iface);
 static void         gtk_tree_model_filter_finalize                        (GObject                 *object);
 static void         gtk_tree_model_filter_set_property                    (GObject                 *object,
                                                                            guint                    prop_id,
@@ -143,10 +164,13 @@ static void         gtk_tree_model_filter_rows_reordered                  (GtkTr
                                                                            gpointer                data);
 
 /* GtkTreeModel interface */
-static guint        gtk_tree_model_filter_get_flags                       (GtkTreeModel           *model);
+static GtkTreeModelFlags gtk_tree_model_filter_get_flags                       (GtkTreeModel           *model);
 static gint         gtk_tree_model_filter_get_n_columns                   (GtkTreeModel           *model);
 static GType        gtk_tree_model_filter_get_column_type                 (GtkTreeModel           *model,
                                                                            gint                    index);
+static gboolean     gtk_tree_model_filter_get_iter_full                   (GtkTreeModel           *model,
+                                                                           GtkTreeIter            *iter,
+                                                                           GtkTreePath            *path);
 static gboolean     gtk_tree_model_filter_get_iter                        (GtkTreeModel           *model,
                                                                            GtkTreeIter            *iter,
                                                                            GtkTreePath            *path);
@@ -177,11 +201,21 @@ static void         gtk_tree_model_filter_ref_node                        (GtkTr
 static void         gtk_tree_model_filter_unref_node                      (GtkTreeModel           *model,
                                                                            GtkTreeIter            *iter);
 
+/* TreeDragSource interface */
+static gboolean    gtk_tree_model_filter_row_draggable                    (GtkTreeDragSource      *drag_source,
+                                                                           GtkTreePath            *path);
+static gboolean    gtk_tree_model_filter_drag_data_get                    (GtkTreeDragSource      *drag_source,
+                                                                           GtkTreePath            *path,
+                                                                           GtkSelectionData       *selection_data);
+static gboolean    gtk_tree_model_filter_drag_data_delete                 (GtkTreeDragSource      *drag_source,
+                                                                           GtkTreePath            *path);
 
 /* private functions */
 static void        gtk_tree_model_filter_build_level                      (GtkTreeModelFilter     *filter,
                                                                            FilterLevel            *parent_level,
-                                                                           FilterElt              *parent_elt);
+                                                                           FilterElt              *parent_elt,
+                                                                           gboolean                emit_inserted);
+
 static void        gtk_tree_model_filter_free_level                       (GtkTreeModelFilter     *filter,
                                                                            FilterLevel            *filter_level);
 
@@ -207,22 +241,32 @@ static void         gtk_tree_model_filter_real_unref_node                 (GtkTr
 
 static void         gtk_tree_model_filter_set_model                       (GtkTreeModelFilter     *filter,
                                                                            GtkTreeModel           *child_model);
+static void         gtk_tree_model_filter_ref_path                        (GtkTreeModelFilter     *filter,
+                                                                           GtkTreePath            *path);
+static void         gtk_tree_model_filter_unref_path                      (GtkTreeModelFilter     *filter,
+                                                                           GtkTreePath            *path);
 static void         gtk_tree_model_filter_set_root                        (GtkTreeModelFilter     *filter,
                                                                            GtkTreePath            *root);
 
 static GtkTreePath *gtk_real_tree_model_filter_convert_child_path_to_path (GtkTreeModelFilter     *filter,
                                                                            GtkTreePath            *child_path,
                                                                            gboolean                build_levels,
-                                                                           gboolean                fetch_childs);
+                                                                           gboolean                fetch_children);
+
+static FilterElt   *gtk_tree_model_filter_get_nth                         (GtkTreeModelFilter     *filter,
+                                                                           FilterLevel            *level,
+                                                                           int                     n);
+static FilterElt   *gtk_tree_model_filter_get_nth_visible                 (GtkTreeModelFilter     *filter,
+                                                                           FilterLevel            *level,
+                                                                           int                     n);
 
 static FilterElt   *gtk_tree_model_filter_fetch_child                     (GtkTreeModelFilter     *filter,
                                                                            FilterLevel            *level,
                                                                            gint                    offset,
                                                                            gint                   *index);
 static void         gtk_tree_model_filter_remove_node                     (GtkTreeModelFilter     *filter,
-                                                                           GtkTreeIter            *iter,
-                                                                           gboolean                emit_signal);
-static void         gtk_tree_model_filter_update_childs                   (GtkTreeModelFilter     *filter,
+                                                                           GtkTreeIter            *iter);
+static void         gtk_tree_model_filter_update_children                 (GtkTreeModelFilter     *filter,
                                                                            FilterLevel            *level,
                                                                            FilterElt              *elt);
 static FilterElt   *bsearch_elt_with_offset                               (GArray                 *array,
@@ -230,46 +274,11 @@ static FilterElt   *bsearch_elt_with_offset                               (GArra
                                                                            gint                  *index);
 
 
-static GObjectClass *parent_class = NULL;
-
-GType
-gtk_tree_model_filter_get_type (void)
-{
-  static GType tree_model_filter_type = 0;
-
-  if (!tree_model_filter_type)
-    {
-      static const GTypeInfo tree_model_filter_info =
-        {
-          sizeof (GtkTreeModelFilterClass),
-          NULL, /* base_init */
-          NULL, /* base_finalize */
-          (GClassInitFunc) gtk_tree_model_filter_class_init,
-          NULL, /* class_finalize */
-          NULL, /* class_data */
-          sizeof (GtkTreeModelFilter),
-          0, /* n_preallocs */
-          (GInstanceInitFunc) gtk_tree_model_filter_init
-        };
-
-      static const GInterfaceInfo tree_model_info =
-        {
-          (GInterfaceInitFunc) gtk_tree_model_filter_tree_model_init,
-          NULL,
-          NULL
-        };
-
-      tree_model_filter_type = g_type_register_static (G_TYPE_OBJECT,
-                                                       "GtkTreeModelFilter",
-                                                       &tree_model_filter_info, 0);
-
-      g_type_add_interface_static (tree_model_filter_type,
-                                   GTK_TYPE_TREE_MODEL,
-                                   &tree_model_info);
-    }
-
-  return tree_model_filter_type;
-}
+G_DEFINE_TYPE_WITH_CODE (GtkTreeModelFilter, gtk_tree_model_filter, G_TYPE_OBJECT,
+                        G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_MODEL,
+                                               gtk_tree_model_filter_tree_model_init)
+                        G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_SOURCE,
+                                               gtk_tree_model_filter_drag_source_init))
 
 static void
 gtk_tree_model_filter_init (GtkTreeModelFilter *filter)
@@ -280,6 +289,7 @@ 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;
 }
 
 static void
@@ -288,7 +298,6 @@ gtk_tree_model_filter_class_init (GtkTreeModelFilterClass *filter_class)
   GObjectClass *object_class;
 
   object_class = (GObjectClass *) filter_class;
-  parent_class = g_type_class_peek_parent (filter_class);
 
   object_class->set_property = gtk_tree_model_filter_set_property;
   object_class->get_property = gtk_tree_model_filter_get_property;
@@ -300,19 +309,19 @@ gtk_tree_model_filter_class_init (GtkTreeModelFilterClass *filter_class)
    */
   g_object_class_install_property (object_class,
                                    PROP_CHILD_MODEL,
-                                   g_param_spec_object ("child_model",
+                                   g_param_spec_object ("child-model",
                                                         ("The child model"),
                                                         ("The model for the filtermodel to filter"),
                                                         GTK_TYPE_TREE_MODEL,
-                                                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+                                                        GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
 
   g_object_class_install_property (object_class,
                                    PROP_VIRTUAL_ROOT,
-                                   g_param_spec_boxed ("virtual_root",
+                                   g_param_spec_boxed ("virtual-root",
                                                        ("The virtual root"),
                                                        ("The virtual root (relative to the child model) for this filtermodel"),
                                                        GTK_TYPE_TREE_PATH,
-                                                       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+                                                       GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
 
   g_type_class_add_private (object_class, sizeof (GtkTreeModelFilterPrivate));
 }
@@ -336,12 +345,23 @@ 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)
+    gtk_tree_model_filter_unref_path (filter, filter->priv->virtual_root);
+
   gtk_tree_model_filter_set_model (filter, NULL);
 
   if (filter->priv->virtual_root)
@@ -352,9 +372,15 @@ gtk_tree_model_filter_finalize (GObject *object)
 
   if (filter->priv->modify_types)
     g_free (filter->priv->modify_types);
+  
+  if (filter->priv->modify_destroy)
+    filter->priv->modify_destroy (filter->priv->modify_data);
+
+  if (filter->priv->visible_destroy)
+    filter->priv->visible_destroy (filter->priv->visible_data);
 
   /* must chain up */
-  parent_class->finalize (object);
+  G_OBJECT_CLASS (gtk_tree_model_filter_parent_class)->finalize (object);
 }
 
 static void
@@ -406,9 +432,11 @@ gtk_tree_model_filter_get_property (GObject    *object,
 static void
 gtk_tree_model_filter_build_level (GtkTreeModelFilter *filter,
                                    FilterLevel        *parent_level,
-                                   FilterElt          *parent_elt)
+                                   FilterElt          *parent_elt,
+                                   gboolean            emit_inserted)
 {
   GtkTreeIter iter;
+  GtkTreeIter first_node;
   GtkTreeIter root;
   FilterLevel *new_level;
   gint length = 0;
@@ -416,6 +444,9 @@ gtk_tree_model_filter_build_level (GtkTreeModelFilter *filter,
 
   g_assert (filter->priv->child_model != NULL);
 
+  if (filter->priv->in_row_deleted)
+    return;
+
   if (!parent_level)
     {
       if (filter->priv->virtual_root)
@@ -463,6 +494,7 @@ gtk_tree_model_filter_build_level (GtkTreeModelFilter *filter,
                                         sizeof (FilterElt),
                                         length);
   new_level->ref_count = 0;
+  new_level->visible_nodes = 0;
   new_level->parent_elt = parent_elt;
   new_level->parent_level = parent_level;
 
@@ -479,12 +511,12 @@ gtk_tree_model_filter_build_level (GtkTreeModelFilter *filter,
       parent_elt = parent_level->parent_elt;
       parent_level = parent_level->parent_level;
     }
-  filter->priv->zero_ref_count++;
+  if (new_level != filter->priv->root)
+    filter->priv->zero_ref_count++;
 
   i = 0;
 
-  if (!new_level->parent_level)
-    filter->priv->root_level_visible = 0;
+  first_node = iter;
 
   do
     {
@@ -502,13 +534,63 @@ gtk_tree_model_filter_build_level (GtkTreeModelFilter *filter,
             filter_elt.iter = iter;
 
           g_array_append_val (new_level->array, filter_elt);
+          new_level->visible_nodes++;
+
+          if (new_level->parent_level || filter->priv->virtual_root)
+            {
+              GtkTreeIter f_iter;
 
-          if (!new_level->parent_level)
-            filter->priv->root_level_visible++;
+              f_iter.stamp = filter->priv->stamp;
+              f_iter.user_data = new_level;
+              f_iter.user_data2 = &(g_array_index (new_level->array, FilterElt, new_level->array->len - 1));
+
+              gtk_tree_model_filter_ref_node (GTK_TREE_MODEL (filter), &f_iter);
+
+              if (emit_inserted)
+                {
+                  GtkTreePath *f_path;
+
+                  f_path = gtk_tree_model_get_path (GTK_TREE_MODEL (filter),
+                                                    &f_iter);
+                  gtk_tree_model_row_inserted (GTK_TREE_MODEL (filter),
+                                               f_path, &f_iter);
+                  gtk_tree_path_free (f_path);
+                }
+            }
         }
       i++;
     }
   while (gtk_tree_model_iter_next (filter->priv->child_model, &iter));
+
+  if (new_level->array->len == 0
+      && (new_level != filter->priv->root || filter->priv->virtual_root))
+    {
+      /* If none of the nodes are visible, we will just pull in the
+       * first node of the level and keep a reference on it.  We need this
+       * to make sure that we get all signals for this level.
+       */
+      FilterElt filter_elt;
+      GtkTreeIter f_iter;
+
+      filter_elt.offset = 0;
+      filter_elt.zero_ref_count = 0;
+      filter_elt.ref_count = 0;
+      filter_elt.children = NULL;
+      filter_elt.visible = FALSE;
+
+      if (GTK_TREE_MODEL_FILTER_CACHE_CHILD_ITERS (filter))
+        filter_elt.iter = first_node;
+
+      g_array_append_val (new_level->array, filter_elt);
+
+      f_iter.stamp = filter->priv->stamp;
+      f_iter.user_data = new_level;
+      f_iter.user_data2 = &(g_array_index (new_level->array, FilterElt, new_level->array->len - 1));
+
+      gtk_tree_model_filter_ref_node (GTK_TREE_MODEL (filter), &f_iter);
+    }
+  else if (new_level->array->len == 0)
+    gtk_tree_model_filter_free_level (filter, new_level);
 }
 
 static void
@@ -519,6 +601,24 @@ 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;
@@ -536,19 +636,10 @@ gtk_tree_model_filter_free_level (GtkTreeModelFilter *filter,
             }
         }
       while (parent_level);
-      filter->priv->zero_ref_count--;
+      if (filter_level != filter->priv->root)
+        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->parent_level)
-    filter->priv->root_level_visible = 0;
-
   if (filter_level->parent_elt)
     filter_level->parent_elt->children = NULL;
   else
@@ -561,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,
@@ -654,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)
    {
@@ -675,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;
 }
 
@@ -700,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,
@@ -795,74 +927,82 @@ 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++)
+  if (i > 0)
+    i--;
+
+  for ( ; 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 offset, i, length;
 
-  /* FIXME: this function is very ugly. I need to rethink and
-   * rewrite it someday.
-   */
+  gboolean emit_child_toggled = FALSE;
 
   level = FILTER_LEVEL (iter->user_data);
   elt = FILTER_ELT (iter->user_data2);
 
   parent = level->parent_elt;
   parent_level = level->parent_level;
+
   length = level->array->len;
   offset = elt->offset;
 
-  /* ref counting */
-  while (elt->ref_count > 0)
-    gtk_tree_model_filter_real_unref_node (GTK_TREE_MODEL (filter),
-                                           iter, FALSE);
+  /* we distinguish a couple of cases:
+   *  - root level, length > 1: emit row-deleted and remove.
+   *  - root level, length == 1: emit row-deleted and keep in cache.
+   *  - level, length == 1: parent->ref_count > 1: emit row-deleted and keep.
+   *  - level, length > 1: emit row-deleted and remove.
+   *  - else, remove level.
+   *
+   *  if level != root level and visible nodes == 0, emit row-has-child-toggled.
+   */
 
-  level_refcount = level->ref_count;
+  if (level != filter->priv->root && level->visible_nodes == 0)
+    emit_child_toggled = TRUE;
 
-  /* do the ref counting first! this touches the stamp */
-  if (emit_signal)
+  if (length > 1)
     {
       GtkTreePath *path;
+      FilterElt *tmp;
+
+      /* we emit row-deleted, and remove the node from the cache.
+       */
 
       path = gtk_tree_model_get_path (GTK_TREE_MODEL (filter), iter);
+      elt->visible = FALSE;
       gtk_tree_model_filter_increment_stamp (filter);
+      iter->stamp = filter->priv->stamp;
       gtk_tree_model_row_deleted (GTK_TREE_MODEL (filter), path);
       gtk_tree_path_free (path);
-    }
 
-  if ((length == 1 || level_refcount == 0) &&
-      emit_signal && iter->user_data != filter->priv->root)
-    {
-      /* above code destroyed the level */
-      goto emit_has_child_toggled;
-    }
-
-  if (length == 1)
-    {
-      /* kill the level */
-      gtk_tree_model_filter_free_level (filter, level);
+      while (elt->ref_count > 1)
+        gtk_tree_model_filter_real_unref_node (GTK_TREE_MODEL (filter),
+                                               iter, FALSE);
 
-      if (!filter->priv->root)
-        /* we killed the root */
-        return;
-    }
-  else
-    {
-      FilterElt *tmp;
+      if (parent_level || filter->priv->virtual_root)
+        gtk_tree_model_filter_unref_node (GTK_TREE_MODEL (filter), iter);
+      else if (elt->ref_count > 0)
+        gtk_tree_model_filter_real_unref_node (GTK_TREE_MODEL (filter),
+                                               iter, FALSE);
 
       /* remove the node */
       tmp = bsearch_elt_with_offset (level->array, elt->offset, &i);
@@ -871,7 +1011,8 @@ gtk_tree_model_filter_remove_node (GtkTreeModelFilter *filter,
         {
           g_array_remove_index (level->array, i);
 
-          for (i = MAX (--i, 0); i < level->array->len; i++)
+         i--;
+          for (i = MAX (i, 0); i < level->array->len; i++)
             {
               /* NOTE: here we do *not* decrease offsets, because the node was
                * not removed from the child model
@@ -882,16 +1023,43 @@ gtk_tree_model_filter_remove_node (GtkTreeModelFilter *filter,
             }
         }
     }
+  else if ((length == 1 && parent && parent->ref_count > 1)
+           || (length == 1 && level == filter->priv->root))
+    {
+      GtkTreePath *path;
 
-emit_has_child_toggled:
-  /* children are being handled first, so we can check it this way
-   *
-   * yes this if-statement is ugly
-   */
-  if ((parent && parent->children && parent->children->array->len <= 1) ||
-      (length == 1 && emit_signal && iter->user_data != filter->priv->root))
+      /* we emit row-deleted, but keep the node in the cache and
+       * referenced.
+       */
+
+      path = gtk_tree_model_get_path (GTK_TREE_MODEL (filter), iter);
+      elt->visible = FALSE;
+      gtk_tree_model_filter_increment_stamp (filter);
+      gtk_tree_model_row_deleted (GTK_TREE_MODEL (filter), path);
+      gtk_tree_path_free (path);
+    }
+  else
+    {
+      GtkTreePath *path;
+
+      /* blow level away */
+
+      path = gtk_tree_model_get_path (GTK_TREE_MODEL (filter), iter);
+      elt->visible = FALSE;
+      gtk_tree_model_filter_increment_stamp (filter);
+      iter->stamp = filter->priv->stamp;
+      gtk_tree_model_row_deleted (GTK_TREE_MODEL (filter), path);
+      gtk_tree_path_free (path);
+
+      while (elt->ref_count > 1)
+        gtk_tree_model_filter_real_unref_node (GTK_TREE_MODEL (filter),
+                                               iter, FALSE);
+
+      gtk_tree_model_filter_free_level (filter, level);
+    }
+
+  if (emit_child_toggled)
     {
-      /* latest child has been removed, level has been destroyed */
       GtkTreeIter piter;
       GtkTreePath *ppath;
 
@@ -908,9 +1076,9 @@ emit_has_child_toggled:
 }
 
 static void
-gtk_tree_model_filter_update_childs (GtkTreeModelFilter *filter,
-                                     FilterLevel        *level,
-                                     FilterElt          *elt)
+gtk_tree_model_filter_update_children (GtkTreeModelFilter *filter,
+                                      FilterLevel        *level,
+                                      FilterElt          *elt)
 {
   GtkTreeIter c_iter;
   GtkTreeIter iter;
@@ -963,7 +1131,7 @@ bsearch_elt_with_offset (GArray *array,
         return NULL;
     }
 
-  while (start != end)
+  do
     {
       middle = (start + end) / 2;
 
@@ -976,6 +1144,7 @@ bsearch_elt_with_offset (GArray *array,
       else
         break;
     }
+  while (start != end);
 
   if (elt->offset == offset)
     {
@@ -995,7 +1164,7 @@ gtk_tree_model_filter_row_changed (GtkTreeModel *c_model,
 {
   GtkTreeModelFilter *filter = GTK_TREE_MODEL_FILTER (data);
   GtkTreeIter iter;
-  GtkTreeIter childs;
+  GtkTreeIter children;
   GtkTreeIter real_c_iter;
   GtkTreePath *path = NULL;
 
@@ -1036,7 +1205,8 @@ gtk_tree_model_filter_row_changed (GtkTreeModel *c_model,
 
   if (path)
     {
-      gtk_tree_model_get_iter (GTK_TREE_MODEL (filter), &iter, path);
+      gtk_tree_model_filter_get_iter_full (GTK_TREE_MODEL (filter),
+                                           &iter, path);
       current_state = FILTER_ELT (iter.user_data2)->visible;
     }
   else
@@ -1049,29 +1219,29 @@ gtk_tree_model_filter_row_changed (GtkTreeModel *c_model,
   if (current_state == TRUE && requested_state == FALSE)
     {
       /* get rid of this node */
-      gtk_tree_model_get_iter (GTK_TREE_MODEL (filter), &iter, path);
-      gtk_tree_model_filter_remove_node (filter, &iter, TRUE);
-
       level = FILTER_LEVEL (iter.user_data);
+      level->visible_nodes--;
 
-      if (!level->parent_level)
-        filter->priv->root_level_visible--;
+      gtk_tree_model_filter_remove_node (filter, &iter);
 
       goto done;
     }
 
   if (current_state == TRUE && requested_state == TRUE)
     {
-      /* progate the signal */
-      gtk_tree_model_get_iter (GTK_TREE_MODEL (filter), &iter, path);
+      /* propagate the signal; also get a path taking only visible
+       * nodes into account.
+       */
+      gtk_tree_path_free (path);
+      path = gtk_tree_model_get_path (GTK_TREE_MODEL (filter), &iter);
       gtk_tree_model_row_changed (GTK_TREE_MODEL (filter), path, &iter);
 
       level = FILTER_LEVEL (iter.user_data);
       elt = FILTER_ELT (iter.user_data2);
 
-      /* and update the childs */
-      if (gtk_tree_model_iter_children (c_model, &childs, &real_c_iter))
-        gtk_tree_model_filter_update_childs (filter, level, elt);
+      /* and update the children */
+      if (gtk_tree_model_iter_children (c_model, &children, &real_c_iter))
+        gtk_tree_model_filter_update_children (filter, level, elt);
 
       goto done;
     }
@@ -1087,7 +1257,7 @@ gtk_tree_model_filter_row_changed (GtkTreeModel *c_model,
       gint i;
       FilterLevel *root;
 
-      gtk_tree_model_filter_build_level (filter, NULL, NULL);
+      gtk_tree_model_filter_build_level (filter, NULL, NULL, FALSE);
 
       root = FILTER_LEVEL (filter->priv->root);
 
@@ -1095,34 +1265,45 @@ gtk_tree_model_filter_row_changed (GtkTreeModel *c_model,
         {
           for (i = 0; i < root->array->len; i++)
             g_array_index (root->array, FilterElt, i).visible = FALSE;
-          filter->priv->root_level_visible = 0;
+          root->visible_nodes = 0;
         }
     }
 
+  gtk_tree_model_filter_increment_stamp (filter);
+
   if (!path)
     path = gtk_real_tree_model_filter_convert_child_path_to_path (filter,
                                                                   c_path,
                                                                   TRUE,
                                                                   TRUE);
 
-  g_return_if_fail (path != NULL);
+  if (!path)
+    /* parent is probably being filtered out */
+    goto done;
 
-  gtk_tree_model_filter_increment_stamp (filter);
-  gtk_tree_model_get_iter (GTK_TREE_MODEL (filter), &iter, path);
+  gtk_tree_model_filter_get_iter_full (GTK_TREE_MODEL (filter), &iter, path);
 
   level = FILTER_LEVEL (iter.user_data);
   elt = FILTER_ELT (iter.user_data2);
 
-  elt->visible = TRUE;
+  /* elt->visible can be TRUE at this point if it was pulled in above */
+  if (!elt->visible)
+    {
+      elt->visible = TRUE;
+      level->visible_nodes++;
+    }
 
-  if (!level->parent_level)
-    filter->priv->root_level_visible++;
+  if ((level->parent_elt && level->parent_elt->visible) || !level->parent_elt)
+    {
+      /* visibility changed -- reget path */
+      gtk_tree_path_free (path);
+      path = gtk_tree_model_get_path (GTK_TREE_MODEL (filter), &iter);
 
-  /* update stamp */
-  gtk_tree_model_row_inserted (GTK_TREE_MODEL (filter), path, &iter);
+      gtk_tree_model_row_inserted (GTK_TREE_MODEL (filter), path, &iter);
 
-  if (gtk_tree_model_iter_children (c_model, &childs, c_iter))
-    gtk_tree_model_filter_update_childs (filter, level, elt);
+      if (gtk_tree_model_iter_children (c_model, &children, c_iter))
+        gtk_tree_model_filter_update_children (filter, level, elt);
+    }
 
 done:
   if (path)
@@ -1149,7 +1330,7 @@ gtk_tree_model_filter_row_inserted (GtkTreeModel *c_model,
   FilterLevel *level;
   FilterLevel *parent_level;
 
-  gint i = 0, offset, index = -1;
+  gint i = 0, offset;
 
   gboolean free_c_path = FALSE;
 
@@ -1176,22 +1357,34 @@ gtk_tree_model_filter_row_inserted (GtkTreeModel *c_model,
         {
           gint level;
           gint *v_indices, *c_indices;
+          gboolean common_prefix = TRUE;
 
           level = gtk_tree_path_get_depth (c_path) - 1;
           v_indices = gtk_tree_path_get_indices (filter->priv->virtual_root);
           c_indices = gtk_tree_path_get_indices (c_path);
 
-          if (v_indices[level] >= c_indices[level])
+          for (i = 0; i < level; i++)
+            if (v_indices[i] != c_indices[i])
+              {
+                common_prefix = FALSE;
+                break;
+              }
+
+          if (common_prefix && v_indices[level] >= c_indices[level])
             (v_indices[level])++;
         }
     }
 
   if (!filter->priv->root)
     {
-      gtk_tree_model_filter_build_level (filter, NULL, NULL);
-      /* that already put the inserted iter in the level */
+      /* build level will pull in the new child */
+      gtk_tree_model_filter_build_level (filter, NULL, NULL, FALSE);
 
-      goto done_and_emit;
+      if (filter->priv->root
+          && FILTER_LEVEL (filter->priv->root)->visible_nodes)
+        goto done_and_emit;
+      else
+        goto done;
     }
 
   parent_level = level = FILTER_LEVEL (filter->priv->root);
@@ -1201,7 +1394,7 @@ gtk_tree_model_filter_row_inserted (GtkTreeModel *c_model,
     {
       real_path = gtk_tree_model_filter_remove_root (c_path,
                                                      filter->priv->virtual_root);
-      /* not our kiddo */
+      /* not our child */
       if (!real_path)
         goto done;
     }
@@ -1291,14 +1484,23 @@ gtk_tree_model_filter_row_inserted (GtkTreeModel *c_model,
         if (g_array_index (level->array, FilterElt, i).offset > offset)
           break;
 
+      level->visible_nodes++;
+
       g_array_insert_val (level->array, i, felt);
-      index = i;
 
-      if (!level->parent_level)
-        filter->priv->root_level_visible++;
+      if (level->parent_level || filter->priv->virtual_root)
+        {
+          GtkTreeIter f_iter;
+
+          f_iter.stamp = filter->priv->stamp;
+          f_iter.user_data = level;
+          f_iter.user_data2 = &g_array_index (level->array, FilterElt, i);
+
+          gtk_tree_model_filter_ref_node (GTK_TREE_MODEL (filter), &f_iter);
+        }
     }
 
-  /* another iteration to update the references of childs to parents. */
+  /* another iteration to update the references of children to parents. */
   for (i = 0; i < level->array->len; i++)
     {
       FilterElt *e = &g_array_index (level->array, FilterElt, i);
@@ -1323,7 +1525,12 @@ done_and_emit:
 
   gtk_tree_model_filter_increment_stamp (filter);
 
-  gtk_tree_model_get_iter (GTK_TREE_MODEL (data), &iter, path);
+  gtk_tree_model_filter_get_iter_full (GTK_TREE_MODEL (data), &iter, path);
+
+  /* get a path taking only visible nodes into account */
+  gtk_tree_path_free (path);
+  path = gtk_tree_model_get_path (GTK_TREE_MODEL (data), &iter);
+
   gtk_tree_model_row_inserted (GTK_TREE_MODEL (data), path, &iter);
 
   gtk_tree_path_free (path);
@@ -1345,10 +1552,20 @@ gtk_tree_model_filter_row_has_child_toggled (GtkTreeModel *c_model,
   GtkTreeModelFilter *filter = GTK_TREE_MODEL_FILTER (data);
   GtkTreePath *path;
   GtkTreeIter iter;
+  FilterLevel *level;
+  FilterElt *elt;
 
   g_return_if_fail (c_path != NULL && c_iter != NULL);
 
-  /* FIXME: does this code work? */
+  /* If we get row-has-child-toggled on the virtual root, and there is
+   * no root level; try to build it now.
+   */
+  if (filter->priv->virtual_root && !filter->priv->root
+      && !gtk_tree_path_compare (c_path, filter->priv->virtual_root))
+    {
+      gtk_tree_model_filter_build_level (filter, NULL, NULL, TRUE);
+      return;
+    }
 
   if (!gtk_tree_model_filter_visible (filter, c_iter))
     return;
@@ -1360,10 +1577,25 @@ gtk_tree_model_filter_row_has_child_toggled (GtkTreeModel *c_model,
   if (!path)
     return;
 
-  gtk_tree_model_get_iter (GTK_TREE_MODEL (data), &iter, path);
-  gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (data), path, &iter);
+  gtk_tree_model_filter_get_iter_full (GTK_TREE_MODEL (data), &iter, path);
 
   gtk_tree_path_free (path);
+
+  level = FILTER_LEVEL (iter.user_data);
+  elt = FILTER_ELT (iter.user_data2);
+
+  g_assert (elt->visible);
+
+  /* If this node is referenced and has children, build the level so we
+   * can monitor it for changes.
+   */
+  if (elt->ref_count > 1 && gtk_tree_model_iter_has_child (c_model, c_iter))
+    gtk_tree_model_filter_build_level (filter, level, elt, TRUE);
+
+  /* get a path taking only visible nodes into account */
+  path = gtk_tree_model_get_path (GTK_TREE_MODEL (data), &iter);
+  gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (data), path, &iter);
+  gtk_tree_path_free (path);
 }
 
 static void
@@ -1374,10 +1606,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;
+  FilterLevel *level, *parent_level;
+  gboolean emit_child_toggled = FALSE;
   gint offset;
-  gboolean emit_signal = TRUE;
   gint i;
 
   g_return_if_fail (c_path != NULL);
@@ -1404,7 +1636,7 @@ gtk_tree_model_filter_row_deleted (GtkTreeModel *c_model,
       path = gtk_tree_path_new ();
       gtk_tree_path_append_index (path, 0);
 
-      for (i = 0; i < level->array->len; i++)
+      for (i = 0; i < level->visible_nodes; i++)
         gtk_tree_model_row_deleted (GTK_TREE_MODEL (data), path);
 
       gtk_tree_path_free (path);
@@ -1421,12 +1653,20 @@ gtk_tree_model_filter_row_deleted (GtkTreeModel *c_model,
         {
           gint level;
           gint *v_indices, *c_indices;
+          gboolean common_prefix = TRUE;
 
           level = gtk_tree_path_get_depth (c_path) - 1;
           v_indices = gtk_tree_path_get_indices (filter->priv->virtual_root);
           c_indices = gtk_tree_path_get_indices (c_path);
 
-          if (v_indices[level] > c_indices[level])
+          for (i = 0; i < level; i++)
+            if (v_indices[i] != c_indices[i])
+              {
+                common_prefix = FALSE;
+                break;
+              }
+
+          if (common_prefix && v_indices[level] > c_indices[level])
             (v_indices[level])--;
         }
     }
@@ -1438,7 +1678,10 @@ gtk_tree_model_filter_row_deleted (GtkTreeModel *c_model,
 
   if (!path)
     {
-      /* fixup the offsets */
+      /* The node deleted in the child model is not visible in the
+       * filter model.  We will not emit a signal, just fixup the offsets
+       * of the other nodes.
+       */
       GtkTreePath *real_path;
 
       if (!filter->priv->root)
@@ -1461,6 +1704,7 @@ gtk_tree_model_filter_row_deleted (GtkTreeModel *c_model,
       i = 0;
       if (gtk_tree_path_get_depth (real_path) - 1 >= 1)
         {
+          /* find the level where the deletion occurred */
           while (i < gtk_tree_path_get_depth (real_path) - 1)
             {
               gint j;
@@ -1494,10 +1738,7 @@ gtk_tree_model_filter_row_deleted (GtkTreeModel *c_model,
       if (!level)
         return;
 
-      /* we need:
-       * - the offset of the removed item
-       * - the level
-       */
+      /* decrease offset of all nodes following the deleted node */
       for (i = 0; i < level->array->len; i++)
         {
           elt = &g_array_index (level->array, FilterElt, i);
@@ -1510,35 +1751,41 @@ gtk_tree_model_filter_row_deleted (GtkTreeModel *c_model,
       return;
     }
 
-  gtk_tree_model_get_iter (GTK_TREE_MODEL (data), &iter, path);
+  /* a node was deleted, which was in our cache */
+  gtk_tree_model_filter_get_iter_full (GTK_TREE_MODEL (data), &iter, path);
 
   level = FILTER_LEVEL (iter.user_data);
   elt = FILTER_ELT (iter.user_data2);
   offset = elt->offset;
 
-  if (!level->parent_level && elt->visible)
-    filter->priv->root_level_visible--;
-
-  if (emit_signal)
+  if (elt->visible)
     {
-      if (level->ref_count == 0 && level != filter->priv->root)
-        {
-          gtk_tree_model_filter_increment_stamp (filter);
-          gtk_tree_model_row_deleted (GTK_TREE_MODEL (data), path);
+      /* get a path taking only visible nodes into account */
+      gtk_tree_path_free (path);
+      path = gtk_tree_model_get_path (GTK_TREE_MODEL (filter), &iter);
 
-          gtk_tree_path_free (path);
-          return;
+      level->visible_nodes--;
+
+      if (level->visible_nodes == 0)
+        {
+          emit_child_toggled = TRUE;
+          parent_level = level->parent_level;
+          parent = level->parent_elt;
         }
 
+      /* emit row_deleted */
       gtk_tree_model_filter_increment_stamp (filter);
       gtk_tree_model_row_deleted (GTK_TREE_MODEL (data), path);
       iter.stamp = filter->priv->stamp;
-
-      while (elt->ref_count > 0)
-        gtk_tree_model_filter_real_unref_node (GTK_TREE_MODEL (data), &iter,
-                                               FALSE);
     }
 
+  /* The filter model's reference on the child node is released
+   * below.
+   */
+  while (elt->ref_count > 1)
+    gtk_tree_model_filter_real_unref_node (GTK_TREE_MODEL (data), &iter,
+                                           FALSE);
+
   if (level->array->len == 1)
     {
       /* kill level */
@@ -1548,13 +1795,21 @@ gtk_tree_model_filter_row_deleted (GtkTreeModel *c_model,
     {
       FilterElt *tmp;
 
+      /* release the filter model's reference on the node */
+      if (level->parent_level || filter->priv->virtual_root)
+        gtk_tree_model_filter_unref_node (GTK_TREE_MODEL (filter), &iter);
+      else if (elt->ref_count > 0)
+        gtk_tree_model_filter_real_unref_node (GTK_TREE_MODEL (data), &iter,
+                                               FALSE);
+
       /* remove the row */
       tmp = bsearch_elt_with_offset (level->array, elt->offset, &i);
 
       offset = tmp->offset;
       g_array_remove_index (level->array, i);
 
-      for (i = MAX (--i, 0); i < level->array->len; i++)
+      i--;
+      for (i = MAX (i, 0); i < level->array->len; i++)
         {
           elt = &g_array_index (level->array, FilterElt, i);
           if (elt->offset > offset)
@@ -1564,6 +1819,27 @@ gtk_tree_model_filter_row_deleted (GtkTreeModel *c_model,
         }
     }
 
+  if (emit_child_toggled && parent_level)
+    {
+      GtkTreeIter iter;
+      GtkTreePath *path;
+
+      iter.stamp = filter->priv->stamp;
+      iter.user_data = parent_level;
+      iter.user_data2 = parent;
+
+      /* We set in_row_deleted to TRUE to avoid a level build triggered
+       * by row-has-child-toggled (parent model could call iter_has_child
+       * for example).
+       */
+      filter->priv->in_row_deleted = TRUE;
+      path = gtk_tree_model_get_path (GTK_TREE_MODEL (filter), &iter);
+      gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (filter),
+                                            path, &iter);
+      gtk_tree_path_free (path);
+      filter->priv->in_row_deleted = FALSE;
+    }
+
   gtk_tree_path_free (path);
 }
 
@@ -1591,9 +1867,6 @@ gtk_tree_model_filter_rows_reordered (GtkTreeModel *c_model,
 
   if (c_path == NULL || gtk_tree_path_get_indices (c_path) == NULL)
     {
-      if (!filter->priv->root)
-        return;
-
       length = gtk_tree_model_iter_n_children (c_model, NULL);
 
       if (filter->priv->virtual_root)
@@ -1621,8 +1894,7 @@ gtk_tree_model_filter_rows_reordered (GtkTreeModel *c_model,
 
       /* virtual root anchor reordering */
       if (filter->priv->virtual_root &&
-          gtk_tree_path_get_depth (c_path) <
-          gtk_tree_path_get_depth (filter->priv->virtual_root))
+         gtk_tree_path_is_ancestor (c_path, filter->priv->virtual_root))
         {
           gint new_pos = -1;
           gint length;
@@ -1653,6 +1925,7 @@ gtk_tree_model_filter_rows_reordered (GtkTreeModel *c_model,
                                                                     c_path,
                                                                     FALSE,
                                                                     FALSE);
+
       if (!path && filter->priv->virtual_root &&
           gtk_tree_path_compare (c_path, filter->priv->virtual_root))
         return;
@@ -1671,7 +1944,8 @@ gtk_tree_model_filter_rows_reordered (GtkTreeModel *c_model,
         }
       else
         {
-          gtk_tree_model_get_iter (GTK_TREE_MODEL (data), &iter, path);
+          gtk_tree_model_filter_get_iter_full (GTK_TREE_MODEL (data),
+                                               &iter, path);
 
           level = FILTER_LEVEL (iter.user_data);
           elt = FILTER_ELT (iter.user_data2);
@@ -1689,8 +1963,11 @@ gtk_tree_model_filter_rows_reordered (GtkTreeModel *c_model,
         }
     }
 
-  if (level->array->len < 1)
-    return;
+  if (!level || level->array->len < 1)
+    {
+      gtk_tree_path_free (path);
+      return;
+    }
 
   /* NOTE: we do not bail out here if level->array->len < 2 like
    * GtkTreeModelSort does. This because we do some special tricky
@@ -1740,8 +2017,14 @@ gtk_tree_model_filter_rows_reordered (GtkTreeModel *c_model,
     gtk_tree_model_rows_reordered (GTK_TREE_MODEL (data), path, NULL,
                                    tmp_array);
   else
-    gtk_tree_model_rows_reordered (GTK_TREE_MODEL (data), path, &iter,
-                                   tmp_array);
+    {
+      /* get a path taking only visible nodes into account */
+      gtk_tree_path_free (path);
+      path = gtk_tree_model_get_path (GTK_TREE_MODEL (data), &iter);
+
+      gtk_tree_model_rows_reordered (GTK_TREE_MODEL (data), path, &iter,
+                                     tmp_array);
+    }
 
   /* done */
   g_free (tmp_array);
@@ -1749,10 +2032,18 @@ gtk_tree_model_filter_rows_reordered (GtkTreeModel *c_model,
 }
 
 /* TreeModelIface implementation */
-static guint
+static GtkTreeModelFlags
 gtk_tree_model_filter_get_flags (GtkTreeModel *model)
 {
+  GtkTreeModelFlags flags;
+
   g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (model), 0);
+  g_return_val_if_fail (GTK_TREE_MODEL_FILTER (model)->priv->child_model != NULL, 0);
+
+  flags = gtk_tree_model_get_flags (GTK_TREE_MODEL_FILTER (model)->priv->child_model);
+
+  if ((flags & GTK_TREE_MODEL_LIST_ONLY) == GTK_TREE_MODEL_LIST_ONLY)
+    return GTK_TREE_MODEL_LIST_ONLY;
 
   return 0;
 }
@@ -1799,6 +2090,65 @@ gtk_tree_model_filter_get_column_type (GtkTreeModel *model,
   return gtk_tree_model_get_column_type (filter->priv->child_model, index);
 }
 
+/* A special case of _get_iter; this function can also get iters which
+ * are not visible.  These iters should ONLY be passed internally, never
+ * pass those along with a signal emission.
+ */
+static gboolean
+gtk_tree_model_filter_get_iter_full (GtkTreeModel *model,
+                                     GtkTreeIter  *iter,
+                                     GtkTreePath  *path)
+{
+  GtkTreeModelFilter *filter = (GtkTreeModelFilter *)model;
+  gint *indices;
+  FilterLevel *level;
+  FilterElt *elt;
+  gint depth, i;
+  g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (model), FALSE);
+  g_return_val_if_fail (filter->priv->child_model != NULL, FALSE);
+
+  indices = gtk_tree_path_get_indices (path);
+
+  if (filter->priv->root == NULL)
+    gtk_tree_model_filter_build_level (filter, NULL, NULL, FALSE);
+  level = FILTER_LEVEL (filter->priv->root);
+
+  depth = gtk_tree_path_get_depth (path);
+  if (!depth)
+    {
+      iter->stamp = 0;
+      return FALSE;
+    }
+
+  for (i = 0; i < depth - 1; i++)
+    {
+      if (!level || indices[i] >= level->array->len)
+        {
+          return FALSE;
+        }
+
+      elt = gtk_tree_model_filter_get_nth (filter, level, indices[i]);
+
+      if (!elt->children)
+        gtk_tree_model_filter_build_level (filter, level, elt, FALSE);
+      level = elt->children;
+    }
+
+  if (!level || indices[i] >= level->array->len)
+    {
+      iter->stamp = 0;
+      return FALSE;
+    }
+
+  iter->stamp = filter->priv->stamp;
+  iter->user_data = level;
+
+  elt = gtk_tree_model_filter_get_nth (filter, level, indices[depth - 1]);
+  iter->user_data2 = elt;
+
+  return TRUE;
+}
+
 static gboolean
 gtk_tree_model_filter_get_iter (GtkTreeModel *model,
                                 GtkTreeIter  *iter,
@@ -1807,15 +2157,15 @@ gtk_tree_model_filter_get_iter (GtkTreeModel *model,
   GtkTreeModelFilter *filter = (GtkTreeModelFilter *)model;
   gint *indices;
   FilterLevel *level;
+  FilterElt *elt;
   gint depth, i;
-
   g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (model), FALSE);
   g_return_val_if_fail (filter->priv->child_model != NULL, FALSE);
 
   indices = gtk_tree_path_get_indices (path);
 
   if (filter->priv->root == NULL)
-    gtk_tree_model_filter_build_level (filter, NULL, NULL);
+    gtk_tree_model_filter_build_level (filter, NULL, NULL, FALSE);
   level = FILTER_LEVEL (filter->priv->root);
 
   depth = gtk_tree_path_get_depth (path);
@@ -1827,20 +2177,19 @@ gtk_tree_model_filter_get_iter (GtkTreeModel *model,
 
   for (i = 0; i < depth - 1; i++)
     {
-      if (!level || indices[i] >= level->array->len)
+      if (!level || indices[i] >= level->visible_nodes)
         {
           return FALSE;
         }
 
-      if (!g_array_index (level->array, FilterElt, indices[i]).children)
-        gtk_tree_model_filter_build_level (filter, level,
-                                           &g_array_index (level->array,
-                                                           FilterElt,
-                                                           indices[i]));
-      level = g_array_index (level->array, FilterElt, indices[i]).children;
+      elt = gtk_tree_model_filter_get_nth_visible (filter, level, indices[i]);
+
+      if (!elt->children)
+        gtk_tree_model_filter_build_level (filter, level, elt, FALSE);
+      level = elt->children;
     }
 
-  if (!level || indices[i] >= level->array->len)
+  if (!level || indices[i] >= level->visible_nodes)
     {
       iter->stamp = 0;
       return FALSE;
@@ -1848,8 +2197,10 @@ gtk_tree_model_filter_get_iter (GtkTreeModel *model,
 
   iter->stamp = filter->priv->stamp;
   iter->user_data = level;
-  iter->user_data2 = &g_array_index (level->array, FilterElt,
-                                     indices[depth - 1]);
+
+  elt = gtk_tree_model_filter_get_nth_visible (filter, level,
+                                               indices[depth - 1]);
+  iter->user_data2 = elt;
 
   return TRUE;
 }
@@ -1870,10 +2221,23 @@ gtk_tree_model_filter_get_path (GtkTreeModel *model,
   level = iter->user_data;
   elt = iter->user_data2;
 
+  if (!elt->visible)
+    return NULL;
+
   while (level)
     {
-      gtk_tree_path_prepend_index (retval,
-                                   elt - FILTER_ELT (level->array->data));
+      int i = 0, index = 0;
+
+      while (&g_array_index (level->array, FilterElt, i) != elt)
+        {
+          if (g_array_index (level->array, FilterElt, i).visible)
+            index++;
+          i++;
+
+          g_assert (i < level->array->len);
+        }
+
+      gtk_tree_path_prepend_index (retval, index);
       elt = level->parent_elt;
       level = level->parent_level;
     }
@@ -1917,6 +2281,7 @@ static gboolean
 gtk_tree_model_filter_iter_next (GtkTreeModel *model,
                                  GtkTreeIter  *iter)
 {
+  int i;
   FilterLevel *level;
   FilterElt *elt;
 
@@ -1927,15 +2292,24 @@ gtk_tree_model_filter_iter_next (GtkTreeModel *model,
   level = iter->user_data;
   elt = iter->user_data2;
 
-  if (elt - FILTER_ELT (level->array->data) >= level->array->len - 1)
+  i = elt - FILTER_ELT (level->array->data);
+
+  while (i < level->array->len - 1)
     {
-      iter->stamp = 0;
-      return FALSE;
+      i++;
+      elt++;
+
+      if (elt->visible)
+        {
+          iter->user_data2 = elt;
+          return TRUE;
+        }
     }
 
-  iter->user_data2 = elt + 1;
+  /* no next visible iter */
+  iter->stamp = 0;
 
-  return TRUE;
+  return FALSE;
 }
 
 static gboolean
@@ -1954,35 +2328,74 @@ gtk_tree_model_filter_iter_children (GtkTreeModel *model,
 
   if (!parent)
     {
+      int i = 0;
+
       if (!filter->priv->root)
-        gtk_tree_model_filter_build_level (filter, NULL, NULL);
+        gtk_tree_model_filter_build_level (filter, NULL, NULL, FALSE);
       if (!filter->priv->root)
         return FALSE;
 
       level = filter->priv->root;
+
+      if (!level->visible_nodes)
+        return FALSE;
+
       iter->stamp = filter->priv->stamp;
       iter->user_data = level;
-      iter->user_data2 = level->array->data;
+
+      while (i < level->array->len)
+        {
+          if (!g_array_index (level->array, FilterElt, i).visible)
+            {
+              i++;
+              continue;
+            }
+
+          iter->user_data2 = &g_array_index (level->array, FilterElt, i);
+          return TRUE;
+        }
+
+      iter->stamp = 0;
+      return FALSE;
     }
   else
     {
+      int i = 0;
+
       if (FILTER_ELT (parent->user_data2)->children == NULL)
         gtk_tree_model_filter_build_level (filter,
                                            FILTER_LEVEL (parent->user_data),
-                                           FILTER_ELT (parent->user_data2));
+                                           FILTER_ELT (parent->user_data2),
+                                           FALSE);
       if (FILTER_ELT (parent->user_data2)->children == NULL)
         return FALSE;
 
-      /* empty array? */
-      if (FILTER_ELT (parent->user_data2)->children->array->len <= 0)
+      if (FILTER_ELT (parent->user_data2)->children->visible_nodes <= 0)
         return FALSE;
 
       iter->stamp = filter->priv->stamp;
       iter->user_data = FILTER_ELT (parent->user_data2)->children;
-      iter->user_data2 = FILTER_LEVEL (iter->user_data)->array->data;
+
+      level = FILTER_LEVEL (iter->user_data);
+
+      while (i < level->array->len)
+        {
+          if (!g_array_index (level->array, FilterElt, i).visible)
+            {
+              i++;
+              continue;
+            }
+
+          iter->user_data2 = &g_array_index (level->array, FilterElt, i);
+          return TRUE;
+        }
+
+      iter->stamp = 0;
+      return FALSE;
     }
 
-  return TRUE;
+  iter->stamp = 0;
+  return FALSE;
 }
 
 static gboolean
@@ -2002,18 +2415,18 @@ gtk_tree_model_filter_iter_has_child (GtkTreeModel *model,
   gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (model), &child_iter, iter);
   elt = FILTER_ELT (iter->user_data2);
 
+  if (!elt->visible)
+    return FALSE;
+
   /* we need to build the level to check if not all children are filtered
    * out
    */
   if (!elt->children
       && gtk_tree_model_iter_has_child (filter->priv->child_model, &child_iter))
     gtk_tree_model_filter_build_level (filter, FILTER_LEVEL (iter->user_data),
-                                       elt);
+                                       elt, FALSE);
 
-  /* FIXME: we should prolly count the visible nodes here, just like in
-   * _iter_n_children.
-   */
-  if (elt->children && elt->children->array->len > 0)
+  if (elt->children && elt->children->visible_nodes > 0)
     return TRUE;
 
   return FALSE;
@@ -2035,34 +2448,29 @@ gtk_tree_model_filter_iter_n_children (GtkTreeModel *model,
   if (!iter)
     {
       if (!filter->priv->root)
-        gtk_tree_model_filter_build_level (filter, NULL, NULL);
+        gtk_tree_model_filter_build_level (filter, NULL, NULL, FALSE);
+
+      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);
+                                       elt, FALSE);
 
-  if (elt->children && elt->children->array->len)
-    {
-      int i = 0;
-      int count = 0;
-      GArray *a = elt->children->array;
-
-      /* count visible nodes */
-      for (i = 0; i < a->len; i++)
-        if (g_array_index (a, FilterElt, i).visible)
-          count++;
-
-      return count;
-    }
+  if (elt->children)
+    return elt->children->visible_nodes;
 
   return 0;
 }
@@ -2073,6 +2481,7 @@ gtk_tree_model_filter_iter_nth_child (GtkTreeModel *model,
                                       GtkTreeIter  *parent,
                                       gint          n)
 {
+  FilterElt *elt;
   FilterLevel *level;
   GtkTreeIter children;
 
@@ -2080,7 +2489,7 @@ gtk_tree_model_filter_iter_nth_child (GtkTreeModel *model,
   if (parent)
     g_return_val_if_fail (GTK_TREE_MODEL_FILTER (model)->priv->stamp == parent->stamp, FALSE);
 
-  /* use this instead of has_Child to force us to build the level, if needed */
+  /* use this instead of has_child to force us to build the level, if needed */
   if (gtk_tree_model_filter_iter_children (model, &children, parent) == FALSE)
     {
       iter->stamp = 0;
@@ -2088,15 +2497,20 @@ gtk_tree_model_filter_iter_nth_child (GtkTreeModel *model,
     }
 
   level = children.user_data;
-  if (n >= level->array->len)
+  elt = FILTER_ELT (level->array->data);
+
+  if (n >= level->visible_nodes)
     {
       iter->stamp = 0;
       return FALSE;
     }
 
+  elt = gtk_tree_model_filter_get_nth_visible (GTK_TREE_MODEL_FILTER (model),
+                                               level, n);
+
   iter->stamp = GTK_TREE_MODEL_FILTER (model)->priv->stamp;
   iter->user_data = level;
-  iter->user_data2 = &g_array_index (level->array, FilterElt, n);
+  iter->user_data2 = elt;
 
   return TRUE;
 }
@@ -2222,6 +2636,62 @@ gtk_tree_model_filter_real_unref_node (GtkTreeModel *model,
     }
 }
 
+/* TreeDragSource interface implementation */
+static gboolean
+gtk_tree_model_filter_row_draggable (GtkTreeDragSource *drag_source,
+                                     GtkTreePath       *path)
+{
+  GtkTreeModelFilter *tree_model_filter = (GtkTreeModelFilter *)drag_source;
+  GtkTreePath *child_path;
+  gboolean draggable;
+
+  g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (drag_source), FALSE);
+  g_return_val_if_fail (path != NULL, FALSE);
+
+  child_path = gtk_tree_model_filter_convert_path_to_child_path (tree_model_filter, path);
+  draggable = gtk_tree_drag_source_row_draggable (GTK_TREE_DRAG_SOURCE (tree_model_filter->priv->child_model), child_path);
+  gtk_tree_path_free (child_path);
+
+  return draggable;
+}
+
+static gboolean
+gtk_tree_model_filter_drag_data_get (GtkTreeDragSource *drag_source,
+                                     GtkTreePath       *path,
+                                     GtkSelectionData  *selection_data)
+{
+  GtkTreeModelFilter *tree_model_filter = (GtkTreeModelFilter *)drag_source;
+  GtkTreePath *child_path;
+  gboolean gotten;
+
+  g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (drag_source), FALSE);
+  g_return_val_if_fail (path != NULL, FALSE);
+
+  child_path = gtk_tree_model_filter_convert_path_to_child_path (tree_model_filter, path);
+  gotten = gtk_tree_drag_source_drag_data_get (GTK_TREE_DRAG_SOURCE (tree_model_filter->priv->child_model), child_path, selection_data);
+  gtk_tree_path_free (child_path);
+
+  return gotten;
+}
+
+static gboolean
+gtk_tree_model_filter_drag_data_delete (GtkTreeDragSource *drag_source,
+                                        GtkTreePath       *path)
+{
+  GtkTreeModelFilter *tree_model_filter = (GtkTreeModelFilter *)drag_source;
+  GtkTreePath *child_path;
+  gboolean deleted;
+
+  g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (drag_source), FALSE);
+  g_return_val_if_fail (path != NULL, FALSE);
+
+  child_path = gtk_tree_model_filter_convert_path_to_child_path (tree_model_filter, path);
+  deleted = gtk_tree_drag_source_drag_data_delete (GTK_TREE_DRAG_SOURCE (tree_model_filter->priv->child_model), child_path);
+  gtk_tree_path_free (child_path);
+
+  return deleted;
+}
+
 /* bits and pieces */
 static void
 gtk_tree_model_filter_set_model (GtkTreeModelFilter *filter,
@@ -2231,15 +2701,15 @@ gtk_tree_model_filter_set_model (GtkTreeModelFilter *filter,
 
   if (filter->priv->child_model)
     {
-      g_signal_handler_disconnect (G_OBJECT (filter->priv->child_model),
+      g_signal_handler_disconnect (filter->priv->child_model,
                                    filter->priv->changed_id);
-      g_signal_handler_disconnect (G_OBJECT (filter->priv->child_model),
+      g_signal_handler_disconnect (filter->priv->child_model,
                                    filter->priv->inserted_id);
-      g_signal_handler_disconnect (G_OBJECT (filter->priv->child_model),
+      g_signal_handler_disconnect (filter->priv->child_model,
                                    filter->priv->has_child_toggled_id);
-      g_signal_handler_disconnect (G_OBJECT (filter->priv->child_model),
+      g_signal_handler_disconnect (filter->priv->child_model,
                                    filter->priv->deleted_id);
-      g_signal_handler_disconnect (G_OBJECT (filter->priv->child_model),
+      g_signal_handler_disconnect (filter->priv->child_model,
                                    filter->priv->reordered_id);
 
       /* reset our state */
@@ -2247,16 +2717,17 @@ gtk_tree_model_filter_set_model (GtkTreeModelFilter *filter,
         gtk_tree_model_filter_free_level (filter, filter->priv->root);
 
       filter->priv->root = NULL;
-      g_object_unref (G_OBJECT (filter->priv->child_model));
+      g_object_unref (filter->priv->child_model);
       filter->priv->visible_column = -1;
-      /* FIXME: destroy more crack here? the funcs? */
+
+      /* FIXME: do we need to destroy more here? */
     }
 
   filter->priv->child_model = child_model;
 
   if (child_model)
     {
-      g_object_ref (G_OBJECT (filter->priv->child_model));
+      g_object_ref (filter->priv->child_model);
       filter->priv->changed_id =
         g_signal_connect (child_model, "row_changed",
                           G_CALLBACK (gtk_tree_model_filter_row_changed),
@@ -2283,6 +2754,48 @@ gtk_tree_model_filter_set_model (GtkTreeModelFilter *filter,
     }
 }
 
+static void
+gtk_tree_model_filter_ref_path (GtkTreeModelFilter *filter,
+                                GtkTreePath        *path)
+{
+  int len;
+  GtkTreePath *p;
+
+  len = gtk_tree_path_get_depth (path);
+  p = gtk_tree_path_copy (path);
+  while (len--)
+    {
+      GtkTreeIter iter;
+
+      gtk_tree_model_get_iter (GTK_TREE_MODEL (filter->priv->child_model), &iter, p);
+      gtk_tree_model_ref_node (GTK_TREE_MODEL (filter->priv->child_model), &iter);
+      gtk_tree_path_up (p);
+    }
+
+  gtk_tree_path_free (p);
+}
+
+static void
+gtk_tree_model_filter_unref_path (GtkTreeModelFilter *filter,
+                                  GtkTreePath        *path)
+{
+  int len;
+  GtkTreePath *p;
+
+  len = gtk_tree_path_get_depth (path);
+  p = gtk_tree_path_copy (path);
+  while (len--)
+    {
+      GtkTreeIter iter;
+
+      gtk_tree_model_get_iter (GTK_TREE_MODEL (filter->priv->child_model), &iter, p);
+      gtk_tree_model_unref_node (GTK_TREE_MODEL (filter->priv->child_model), &iter);
+      gtk_tree_path_up (p);
+    }
+
+  gtk_tree_path_free (p);
+}
+
 static void
 gtk_tree_model_filter_set_root (GtkTreeModelFilter *filter,
                                 GtkTreePath        *root)
@@ -2314,14 +2827,18 @@ 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);
 
   return retval;
 }
@@ -2355,6 +2872,11 @@ gtk_tree_model_filter_get_model (GtkTreeModelFilter *filter)
  * function should return %TRUE if the given row should be visible and
  * %FALSE otherwise.
  *
+ * If the condition calculated by the function changes over time (e.g. because
+ * it depends on some global parameters), you must call 
+ * gtk_tree_model_filter_refilter() to keep the visibility information of 
+ * the model uptodate.
+ *
  * Since: 2.4
  */
 void
@@ -2387,12 +2909,17 @@ gtk_tree_model_filter_set_visible_func (GtkTreeModelFilter            *filter,
  * @filter: A #GtkTreeModelFilter.
  * @n_columns: The number of columns in the filter model.
  * @types: The #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
  */
@@ -2460,34 +2987,43 @@ gtk_tree_model_filter_set_visible_column (GtkTreeModelFilter *filter,
  * @child_iter: A valid #GtkTreeIter pointing to a row on the child model.
  *
  * Sets @filter_iter to point to the row in @filter that corresponds to the
- * row pointed at by @child_iter.
+ * row pointed at by @child_iter.  If @filter_iter was not set, %FALSE is
+ * returned.
+ *
+ * Return value: %TRUE, if @filter_iter was set, i.e. if @child_iter is a
+ * valid iterator pointing to a visible row in child model.
  *
  * Since: 2.4
  */
-void
+gboolean
 gtk_tree_model_filter_convert_child_iter_to_iter (GtkTreeModelFilter *filter,
                                                   GtkTreeIter        *filter_iter,
                                                   GtkTreeIter        *child_iter)
 {
+  gboolean ret;
   GtkTreePath *child_path, *path;
 
-  g_return_if_fail (GTK_IS_TREE_MODEL_FILTER (filter));
-  g_return_if_fail (filter->priv->child_model != NULL);
-  g_return_if_fail (filter_iter != NULL);
-  g_return_if_fail (child_iter != NULL);
+  g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (filter), FALSE);
+  g_return_val_if_fail (filter->priv->child_model != NULL, FALSE);
+  g_return_val_if_fail (filter_iter != NULL, FALSE);
+  g_return_val_if_fail (child_iter != NULL, FALSE);
 
   filter_iter->stamp = 0;
 
   child_path = gtk_tree_model_get_path (filter->priv->child_model, child_iter);
-  g_return_if_fail (child_path != NULL);
+  g_return_val_if_fail (child_path != NULL, FALSE);
 
   path = gtk_tree_model_filter_convert_child_path_to_path (filter,
                                                            child_path);
   gtk_tree_path_free (child_path);
-  g_return_if_fail (path != NULL);
 
-  gtk_tree_model_get_iter (GTK_TREE_MODEL (filter), filter_iter, path);
+  if (!path)
+    return FALSE;
+
+  ret = gtk_tree_model_get_iter (GTK_TREE_MODEL (filter), filter_iter, path);
   gtk_tree_path_free (path);
+
+  return ret;
 }
 
 /**
@@ -2527,11 +3063,12 @@ gtk_tree_model_filter_convert_iter_to_child_iter (GtkTreeModelFilter *filter,
     }
 }
 
+/* The path returned can only be used internally in the filter model. */
 static GtkTreePath *
 gtk_real_tree_model_filter_convert_child_path_to_path (GtkTreeModelFilter *filter,
                                                        GtkTreePath        *child_path,
                                                        gboolean            build_levels,
-                                                       gboolean            fetch_childs)
+                                                       gboolean            fetch_children)
 {
   gint *child_indices;
   GtkTreePath *retval;
@@ -2557,7 +3094,7 @@ gtk_real_tree_model_filter_convert_child_path_to_path (GtkTreeModelFilter *filte
   child_indices = gtk_tree_path_get_indices (real_path);
 
   if (filter->priv->root == NULL && build_levels)
-    gtk_tree_model_filter_build_level (filter, NULL, NULL);
+    gtk_tree_model_filter_build_level (filter, NULL, NULL, FALSE);
   level = FILTER_LEVEL (filter->priv->root);
 
   for (i = 0; i < gtk_tree_path_get_depth (real_path); i++)
@@ -2577,12 +3114,12 @@ gtk_real_tree_model_filter_convert_child_path_to_path (GtkTreeModelFilter *filte
         {
           gtk_tree_path_append_index (retval, j);
           if (!tmp->children && build_levels)
-            gtk_tree_model_filter_build_level (filter, level, tmp);
+            gtk_tree_model_filter_build_level (filter, level, tmp, FALSE);
           level = tmp->children;
           found_child = TRUE;
         }
 
-      if (!found_child && fetch_childs)
+      if (!found_child && fetch_children)
         {
           tmp = gtk_tree_model_filter_fetch_child (filter, level,
                                                    child_indices[i],
@@ -2599,11 +3136,11 @@ gtk_real_tree_model_filter_convert_child_path_to_path (GtkTreeModelFilter *filte
 
           gtk_tree_path_append_index (retval, j);
           if (!tmp->children && build_levels)
-            gtk_tree_model_filter_build_level (filter, level, tmp);
+            gtk_tree_model_filter_build_level (filter, level, tmp, FALSE);
           level = tmp->children;
           found_child = TRUE;
         }
-      else if (!found_child && !fetch_childs)
+      else if (!found_child && !fetch_children)
         {
           /* no path */
           gtk_tree_path_free (real_path);
@@ -2624,7 +3161,8 @@ gtk_real_tree_model_filter_convert_child_path_to_path (GtkTreeModelFilter *filte
  * Converts @child_path to a path relative to @filter. That is, @child_path
  * points to a path in the child model. The rerturned path will point to the
  * same row in the filtered model. If @child_path isn't a valid path on the
- * child model, then %NULL is returned.
+ * child model or points to a row which is not visible in @filter, then %NULL
+ * is returned.
  *
  * Return value: A newly allocated #GtkTreePath, or %NULL.
  *
@@ -2634,11 +3172,29 @@ GtkTreePath *
 gtk_tree_model_filter_convert_child_path_to_path (GtkTreeModelFilter *filter,
                                                   GtkTreePath        *child_path)
 {
+  GtkTreeIter iter;
+  GtkTreePath *path;
+
   /* this function does the sanity checks */
-  return gtk_real_tree_model_filter_convert_child_path_to_path (filter,
+  path = gtk_real_tree_model_filter_convert_child_path_to_path (filter,
                                                                 child_path,
                                                                 TRUE,
                                                                 TRUE);
+
+  if (!path)
+      return NULL;
+
+  /* get a new path which only takes visible nodes into account.
+   * -- if this gives any performance issues, we can write a special
+   *    version of convert_child_path_to_path immediately returning
+   *    a visible-nodes-only path.
+   */
+  gtk_tree_model_filter_get_iter_full (GTK_TREE_MODEL (filter), &iter, path);
+
+  gtk_tree_path_free (path);
+  path = gtk_tree_model_get_path (GTK_TREE_MODEL (filter), &iter);
+
+  return path;
 }
 
 /**
@@ -2672,30 +3228,33 @@ gtk_tree_model_filter_convert_path_to_child_path (GtkTreeModelFilter *filter,
   retval = gtk_tree_path_new ();
   filter_indices = gtk_tree_path_get_indices (filter_path);
   if (!filter->priv->root)
-    gtk_tree_model_filter_build_level (filter, NULL, NULL);
+    gtk_tree_model_filter_build_level (filter, NULL, NULL, FALSE);
   level = FILTER_LEVEL (filter->priv->root);
 
   for (i = 0; i < gtk_tree_path_get_depth (filter_path); i++)
     {
-      gint count = filter_indices[i];
+      FilterElt *elt;
 
-      if (!level || level->array->len <= filter_indices[i])
+      if (!level || level->visible_nodes <= filter_indices[i])
         {
           gtk_tree_path_free (retval);
           return NULL;
         }
 
-      if (g_array_index (level->array, FilterElt, count).children == NULL)
-        gtk_tree_model_filter_build_level (filter, level, &g_array_index (level->array, FilterElt, count));
+      elt = gtk_tree_model_filter_get_nth_visible (filter, level,
+                                                   filter_indices[i]);
+
+      if (elt->children == NULL)
+        gtk_tree_model_filter_build_level (filter, level, elt, FALSE);
 
-      if (!level || level->array->len <= filter_indices[i])
+      if (!level || level->visible_nodes <= filter_indices[i])
         {
           gtk_tree_path_free (retval);
           return NULL;
         }
 
-      gtk_tree_path_append_index (retval, g_array_index (level->array, FilterElt, count).offset);
-      level = g_array_index (level->array, FilterElt, count).children;
+      gtk_tree_path_append_index (retval, elt->offset);
+      level = elt->children;
     }
 
   /* apply vroot */
@@ -2755,7 +3314,7 @@ gtk_tree_model_filter_refilter (GtkTreeModelFilter *filter)
  * gtk_tree_model_ref_node(). This might be useful if the child model
  * being filtered is static (and doesn't change often) and there has been
  * a lot of unreffed access to nodes. As a side effect of this function,
- * all unreffed itters will be invalid.
+ * all unreffed iters will be invalid.
  *
  * Since: 2.4
  */
@@ -2764,7 +3323,10 @@ gtk_tree_model_filter_clear_cache (GtkTreeModelFilter *filter)
 {
   g_return_if_fail (GTK_IS_TREE_MODEL_FILTER (filter));
 
-  if (filter->priv->zero_ref_count)
+  if (filter->priv->zero_ref_count > 0)
     gtk_tree_model_filter_clear_cache_helper (filter,
                                               FILTER_LEVEL (filter->priv->root));
 }
+
+#define __GTK_TREE_MODEL_FILTER_C__
+#include "gtkaliasdef.c"