[gtk+] Bug 621076 - GtkTreeModelFilter does not emit all signals in some ...



commit 76cfd8bfacfa04ffa55a166905a99fcdf3d4f631
Author: Kristian Rietveld <kris gtk org>
Date:   Sat May 21 11:45:08 2011 +0200

    Bug 621076 - GtkTreeModelFilter does not emit all signals in some ...
    
    The bulk of the fix is to walk the chain of ancestors, starting at the
    root level, and check if the visibility of any of the ancestors has
    changed.  If yes, the necessary signals are emitted so that this change
    is propagated properly.  This walk is done after a node has been
    inserted, changed or deleted, see function
    gtk_tree_model_filter_check_ancestors().
    
    Bug reported, and initial debugging and analysis, by Xavier Claessens.

 gtk/gtktreemodelfilter.c |  249 ++++++++++++++++++++++++++++++++++++++++++----
 1 files changed, 228 insertions(+), 21 deletions(-)
---
diff --git a/gtk/gtktreemodelfilter.c b/gtk/gtktreemodelfilter.c
index 7771dc9..23967cb 100644
--- a/gtk/gtktreemodelfilter.c
+++ b/gtk/gtktreemodelfilter.c
@@ -333,6 +333,10 @@ static void         gtk_tree_model_filter_update_children                 (GtkTr
 static FilterElt   *bsearch_elt_with_offset                               (GArray                 *array,
                                                                            gint                   offset,
                                                                            gint                  *index);
+static void         gtk_tree_model_filter_emit_row_inserted_for_path      (GtkTreeModelFilter     *filter,
+                                                                           GtkTreeModel           *c_model,
+                                                                           GtkTreePath            *c_path,
+                                                                           GtkTreeIter            *c_iter);
 
 
 G_DEFINE_TYPE_WITH_CODE (GtkTreeModelFilter, gtk_tree_model_filter, G_TYPE_OBJECT,
@@ -919,6 +923,157 @@ gtk_tree_model_filter_elt_is_visible_in_target (FilterLevel *level,
   return TRUE;
 }
 
+/* If a change has occurred in path (inserted, changed or deleted),
+ * then this function is used to check all its ancestors.  An ancestor
+ * could have changed state as a result and this needs to be propagated
+ * to the objects monitoring the filter model.
+ */
+static void
+gtk_tree_model_filter_check_ancestors (GtkTreeModelFilter *filter,
+                                       GtkTreePath        *path)
+{
+  int i = 0;
+  int *indices = gtk_tree_path_get_indices (path);
+  FilterElt *elt;
+  FilterLevel *level;
+  GtkTreeIter c_iter, tmp_iter;
+
+  level = FILTER_LEVEL (filter->priv->root);
+
+  if (!level)
+    return;
+
+  if (filter->priv->virtual_root)
+    gtk_tree_model_get_iter (filter->priv->child_model, &c_iter,
+                             filter->priv->virtual_root);
+
+  tmp_iter = c_iter;
+  gtk_tree_model_iter_nth_child (filter->priv->child_model, &c_iter,
+                                 filter->priv->virtual_root ? &tmp_iter : NULL,
+                                 indices[i]);
+
+  while (i < gtk_tree_path_get_depth (path) - 1)
+    {
+      int j;
+      gboolean requested_state;
+
+      elt = bsearch_elt_with_offset (level->array,
+                                     gtk_tree_path_get_indices (path)[i],
+                                     &j);
+
+      requested_state = gtk_tree_model_filter_visible (filter, &c_iter);
+
+      if (!elt)
+        {
+          int index;
+          GtkTreePath *c_path;
+
+          if (requested_state == FALSE)
+            return;
+
+          /* The elt does not exist in this level (so it is not
+           * visible), but should now be visible.  We emit the
+           * row-inserted and row-has-child-toggled signals.
+           */
+          elt = gtk_tree_model_filter_insert_elt_in_level (filter,
+                                                           &c_iter,
+                                                           level,
+                                                           indices[i],
+                                                           &index);
+
+          /* insert_elt_in_level defaults to FALSE */
+          elt->visible = TRUE;
+          level->visible_nodes++;
+
+          c_path = gtk_tree_model_get_path (filter->priv->child_model,
+                                            &c_iter);
+
+          gtk_tree_model_filter_emit_row_inserted_for_path (filter,
+                                                            filter->priv->child_model,
+                                                            c_path,
+                                                            &c_iter);
+
+          gtk_tree_path_free (c_path);
+
+          /* We can immediately return, because this node was not visible
+           * before and its children will be checked for in response to
+           * the emitted row-has-child-toggled signal.
+           */
+          return;
+        }
+      else if (elt->visible)
+        {
+          if (!requested_state)
+            {
+              /* A node has turned invisible.  Remove it from the level
+               * and emit row-deleted.  Since this node is being
+               * deleted. it makes no sense to look further up the
+               * chain.
+               */
+              gtk_tree_model_filter_remove_elt_from_level (filter,
+                                                           level, elt);
+              return;
+            }
+
+          /* Otherwise continue up the chain */
+        }
+      else if (!elt->visible)
+        {
+          if (requested_state)
+            {
+              /* A node is already in the cache, but invisible.  This
+               * is usually a node on which a reference is kept by
+               * the filter model, or a node fetched on the filter's
+               * request, and thus not shown.  Therefore, we will
+               * not emit row-inserted for this node.  Instead,
+               * we signal to its parent that a change has occurred.
+               */
+              GtkTreeIter f_iter;
+              GtkTreePath *f_path;
+
+              elt->visible = TRUE;
+              level->visible_nodes++;
+
+              f_iter.stamp = filter->priv->stamp;
+              f_iter.user_data = level->parent_level;
+              f_iter.user_data2 = FILTER_LEVEL_PARENT_ELT(level);
+
+              f_path = gtk_tree_model_get_path (GTK_TREE_MODEL (filter),
+                                                &f_iter);
+              gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (filter),
+                                                    f_path, &f_iter);
+              gtk_tree_path_free (f_path);
+
+              /* We can immediately return, because this node was not visible
+               * before and the parent will check its children, including
+               * this node, in response to the emitted row-has-child-toggled
+               * signal.
+               */
+              return;
+            }
+
+          /* Not visible, so no need to continue. */
+          return;
+        }
+
+      if (!elt->children)
+        {
+          /* If an elt does not have children, these are not visible.
+           * Therefore, any signals emitted for these children will
+           * be ignored, so we do not have to emit them.
+           */
+          return;
+        }
+
+      level = elt->children;
+      i++;
+
+      tmp_iter = c_iter;
+      gtk_tree_model_iter_nth_child (filter->priv->child_model, &c_iter,
+                                     &tmp_iter, indices[i]);
+    }
+}
+
 static FilterElt *
 gtk_tree_model_filter_get_nth_visible (GtkTreeModelFilter *filter,
                                        FilterLevel        *level,
@@ -1356,11 +1511,14 @@ gtk_tree_model_filter_emit_row_inserted_for_path (GtkTreeModelFilter *filter,
 
   if (!filter->priv->root)
     {
+      /* The root level has not been exposed to the view yet, so we
+       * need to emit signals for any node that is being inserted.
+       */
       gtk_tree_model_filter_build_level (filter, NULL, -1, TRUE);
 
-      /* We will only proceed below if the item is found.  If the item
-       * is found, we can be sure row-inserted has just been emitted
-       * for it.
+      /* Check if the root level was built.  Then child levels
+       * that matter have also been built (due to update_children,
+       * which triggers iter_n_children).
        */
       if (filter->priv->root &&
           FILTER_LEVEL (filter->priv->root)->visible_nodes)
@@ -1440,6 +1598,7 @@ gtk_tree_model_filter_row_changed (GtkTreeModel *c_model,
   GtkTreeIter children;
   GtkTreeIter real_c_iter;
   GtkTreePath *path = NULL;
+  GtkTreePath *real_path = NULL;
 
   FilterElt *elt;
   FilterLevel *level;
@@ -1456,6 +1615,12 @@ gtk_tree_model_filter_row_changed (GtkTreeModel *c_model,
       free_c_path = TRUE;
     }
 
+  if (filter->priv->virtual_root)
+    real_path = gtk_tree_model_filter_remove_root (c_path,
+                                                   filter->priv->virtual_root);
+  else
+    real_path = gtk_tree_path_copy (c_path);
+
   if (c_iter)
     real_c_iter = *c_iter;
   else
@@ -1491,9 +1656,13 @@ gtk_tree_model_filter_row_changed (GtkTreeModel *c_model,
 
   if (current_state == TRUE && requested_state == FALSE)
     {
-      gtk_tree_model_filter_remove_elt_from_level (filter, level,
+      gtk_tree_model_filter_remove_elt_from_level (filter,
+                                                   FILTER_LEVEL (iter.user_data),
                                                    FILTER_ELT (iter.user_data2));
 
+      if (real_path)
+        gtk_tree_model_filter_check_ancestors (filter, real_path);
+
       goto done;
     }
 
@@ -1517,6 +1686,9 @@ gtk_tree_model_filter_row_changed (GtkTreeModel *c_model,
             gtk_tree_model_filter_update_children (filter, level, elt);
         }
 
+      if (real_path)
+        gtk_tree_model_filter_check_ancestors (filter, real_path);
+
       goto done;
     }
 
@@ -1525,6 +1697,9 @@ gtk_tree_model_filter_row_changed (GtkTreeModel *c_model,
    */
   g_return_if_fail (current_state == FALSE && requested_state == TRUE);
 
+  if (real_path)
+    gtk_tree_model_filter_check_ancestors (filter, real_path);
+
   gtk_tree_model_filter_emit_row_inserted_for_path (filter, c_model,
                                                     c_path, c_iter);
 
@@ -1532,6 +1707,9 @@ done:
   if (path)
     gtk_tree_path_free (path);
 
+  if (real_path)
+    gtk_tree_path_free (real_path);
+
   if (free_c_path)
     gtk_tree_path_free (c_path);
 }
@@ -1597,23 +1775,6 @@ gtk_tree_model_filter_row_inserted (GtkTreeModel *c_model,
         }
     }
 
-  if (!filter->priv->root)
-    {
-      /* No point in building the level if this node is not visible. */
-      if (!filter->priv->virtual_root
-          && !gtk_tree_model_filter_visible (filter, c_iter))
-        goto done;
-
-      /* build level will pull in the new child */
-      gtk_tree_model_filter_build_level (filter, NULL, -1, FALSE);
-
-      if (filter->priv->root
-          && FILTER_LEVEL (filter->priv->root)->visible_nodes)
-        emit_row_inserted = TRUE;
-
-      goto done;
-    }
-
   /* subtract virtual root if necessary */
   if (filter->priv->virtual_root)
     {
@@ -1626,6 +1787,35 @@ gtk_tree_model_filter_row_inserted (GtkTreeModel *c_model,
   else
     real_path = gtk_tree_path_copy (c_path);
 
+  if (!filter->priv->root)
+    {
+      /* If c_iter is in the root level and is not visible, then there
+       * is no point in building the root level.
+       * FIXME: For trees, this can likely be optimized by checking
+       * whether the node and none of its ancestors are visible.
+       */
+      if (!filter->priv->virtual_root &&
+          gtk_tree_path_get_depth (c_path) == 1 &&
+          !gtk_tree_model_filter_visible (filter, c_iter))
+        goto done;
+
+      /* The root level has not been exposed to the view yet, so we
+       * need to emit signals for any node that is being inserted.
+       */
+      gtk_tree_model_filter_build_level (filter, NULL, -1, TRUE);
+
+      /* Check if the root level was built.  Then child levels
+       * that matter have also been built (due to update_children,
+       * which triggers iter_n_children).
+       */
+      if (filter->priv->root &&
+          FILTER_LEVEL (filter->priv->root)->visible_nodes)
+        {
+          emit_row_inserted = FALSE;
+          goto done;
+        }
+    }
+
   if (gtk_tree_path_get_depth (real_path) - 1 >= 1)
     {
       gboolean found = FALSE;
@@ -1705,6 +1895,8 @@ gtk_tree_model_filter_row_inserted (GtkTreeModel *c_model,
     }
 
 done:
+  gtk_tree_model_filter_check_ancestors (filter, real_path);
+
   if (emit_row_inserted)
     gtk_tree_model_filter_emit_row_inserted_for_path (filter, c_model,
                                                       c_path, c_iter);
@@ -2080,6 +2272,21 @@ gtk_tree_model_filter_row_deleted (GtkTreeModel *c_model,
       filter->priv->in_row_deleted = FALSE;
     }
 
+  if (filter->priv->virtual_root)
+    {
+      GtkTreePath *real_path;
+
+      real_path = gtk_tree_model_filter_remove_root (c_path,
+                                                     filter->priv->root);
+      if (real_path)
+        {
+          gtk_tree_model_filter_check_ancestors (filter, real_path);
+          gtk_tree_path_free (real_path);
+        }
+    }
+  else
+    gtk_tree_model_filter_check_ancestors (filter, c_path);
+
   gtk_tree_path_free (path);
 }
 



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]