[gnome-builder/wip/lazy-tree] tree: make GbTree build nodes lazily



commit 3f01397b69c84b1cf55addc51b8b80132cd662f6
Author: Christian Hergert <christian hergert me>
Date:   Sun Jun 14 14:38:06 2015 -0700

    tree: make GbTree build nodes lazily
    
    This changes GbTree to only build tree nodes lazily. This means a couple
    things for consumers. For example, performing a foreach on the tree
    will no longer yield results unless the item has already been added.
    
    Such features such as "Reveal in Tree" will need to be updated to deal
    with this.
    
    However, what this lets us do is change things like the Project Tree to
    no longer use IdeProjectFiles, but instead lookup files from the file-
    system directly, and lazily. Doing so should improve support for larger
    project trees greatly.
    
    Still need to deal with the fallout from this, but this covers the
    plumbing required.

 src/tree/gb-tree-builder.c |   52 ++++-------
 src/tree/gb-tree-node.c    |   69 +++++++++-----
 src/tree/gb-tree-private.h |    7 ++
 src/tree/gb-tree.c         |  220 +++++++++++++++++++++++++++++---------------
 4 files changed, 212 insertions(+), 136 deletions(-)
---
diff --git a/src/tree/gb-tree-builder.c b/src/tree/gb-tree-builder.c
index 050d731..03b3cb1 100644
--- a/src/tree/gb-tree-builder.c
+++ b/src/tree/gb-tree-builder.c
@@ -126,16 +126,9 @@ _gb_tree_builder_removed (GbTreeBuilder *builder,
   g_signal_emit (builder, gSignals [REMOVED], 0, tree);
 }
 
-/**
- * gb_tree_builder_set_tree:
- * @builder: (in): A #GbTreeBuilder.
- * @tree: (in): A #GbTree.
- *
- * Sets the tree the builder is associated with.
- */
-static void
-gb_tree_builder_set_tree (GbTreeBuilder *builder,
-                          GbTree        *tree)
+void
+_gb_tree_builder_set_tree (GbTreeBuilder *builder,
+                           GbTree        *tree)
 {
        GbTreeBuilderPrivate *priv = gb_tree_builder_get_instance_private (builder);
 
@@ -143,10 +136,21 @@ gb_tree_builder_set_tree (GbTreeBuilder *builder,
        g_return_if_fail (priv->tree == NULL);
        g_return_if_fail (GB_IS_TREE (tree));
 
-       if (tree)
+  if (priv->tree != tree)
     {
-      priv->tree = tree;
-      g_object_add_weak_pointer (G_OBJECT (priv->tree), (gpointer *)&priv->tree);
+      if (priv->tree != NULL)
+        {
+          g_object_remove_weak_pointer (G_OBJECT (priv->tree), (gpointer *)&priv->tree);
+          priv->tree = NULL;
+        }
+
+      if (tree != NULL)
+        {
+          priv->tree = tree;
+          g_object_add_weak_pointer (G_OBJECT (priv->tree), (gpointer *)&priv->tree);
+        }
+
+      g_object_notify_by_pspec (G_OBJECT (builder), gParamSpecs [PROP_TREE]);
     }
 }
 
@@ -204,39 +208,19 @@ gb_tree_builder_get_property (GObject    *object,
 }
 
 static void
-gb_tree_builder_set_property (GObject      *object,
-                              guint         prop_id,
-                              const GValue *value,
-                              GParamSpec   *pspec)
-{
-       GbTreeBuilder *builder = GB_TREE_BUILDER (object);
-
-       switch (prop_id)
-    {
-    case PROP_TREE:
-      gb_tree_builder_set_tree (builder, g_value_get_object (value));
-      break;
-
-    default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-    }
-}
-
-static void
 gb_tree_builder_class_init (GbTreeBuilderClass *klass)
 {
        GObjectClass *object_class = G_OBJECT_CLASS (klass);
 
        object_class->finalize = gb_tree_builder_finalize;
        object_class->get_property = gb_tree_builder_get_property;
-       object_class->set_property = gb_tree_builder_set_property;
 
        gParamSpecs[PROP_TREE] =
                g_param_spec_object("tree",
                                    _("Tree"),
                                    _("The GbTree the builder belongs to."),
                                    GB_TYPE_TREE,
-                                   G_PARAM_READWRITE);
+                                   G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
 
   g_object_class_install_properties (object_class, LAST_PROP, gParamSpecs);
 
diff --git a/src/tree/gb-tree-node.c b/src/tree/gb-tree-node.c
index afc5982..b7a9f44 100644
--- a/src/tree/gb-tree-node.c
+++ b/src/tree/gb-tree-node.c
@@ -34,6 +34,7 @@ struct _GbTreeNode
   GbTree            *tree;
   GQuark             icon_name;
   guint              use_markup : 1;
+  guint              needs_build : 1;
 };
 
 typedef struct
@@ -280,27 +281,27 @@ gb_tree_node_set_item (GbTreeNode *node,
     g_object_notify_by_pspec (G_OBJECT (node), gParamSpecs [PROP_ITEM]);
 }
 
-/**
- * gb_tree_node_set_parent:
- * @node: (in): A #GbTreeNode.
- * @parent: (in): A #GbTreeNode.
- *
- * Sets the parent for this node. This is a weak pointer to prevent
- * cyclic references.
- */
-static void
-gb_tree_node_set_parent (GbTreeNode *node,
-                         GbTreeNode *parent)
+void
+_gb_tree_node_set_parent (GbTreeNode *node,
+                          GbTreeNode *parent)
 {
   g_return_if_fail (GB_IS_TREE_NODE (node));
   g_return_if_fail (node->parent == NULL);
   g_return_if_fail (!parent || GB_IS_TREE_NODE (parent));
 
-  if (parent)
+  if (parent != node->parent)
     {
-      node->parent = parent;
-      g_object_add_weak_pointer (G_OBJECT (node->parent),
-                                 (gpointer *)&node->parent);
+      if (node->parent != NULL)
+        {
+          g_object_remove_weak_pointer (G_OBJECT (node->parent), (gpointer *)&node->parent);
+          node->parent = NULL;
+        }
+
+      if (parent != NULL)
+        {
+          node->parent = parent;
+          g_object_add_weak_pointer (G_OBJECT (node->parent), (gpointer *)&node->parent);
+        }
     }
 }
 
@@ -446,16 +447,18 @@ gb_tree_node_rebuild (GbTreeNode *self)
 gboolean
 gb_tree_node_get_expanded (GbTreeNode *self)
 {
-  GbTree *tree;
   GtkTreePath *path;
-  gboolean ret;
+  gboolean ret = TRUE;
 
   g_return_val_if_fail (GB_IS_TREE_NODE (self), FALSE);
 
-  tree = gb_tree_node_get_tree (self);
-  path = gb_tree_node_get_path (self);
-  ret = gtk_tree_view_row_expanded (GTK_TREE_VIEW (tree), path);
-  gtk_tree_path_free (path);
+  if ((self->tree != NULL) && (self->parent != NULL) && (self->parent->parent != NULL))
+    {
+      path = gb_tree_node_get_path (self);
+      g_assert (path != NULL);
+      ret = gtk_tree_view_row_expanded (GTK_TREE_VIEW (self->tree), path);
+      gtk_tree_path_free (path);
+    }
 
   return ret;
 }
@@ -560,10 +563,6 @@ gb_tree_node_set_property (GObject      *object,
       gb_tree_node_set_item (node, g_value_get_object (value));
       break;
 
-    case PROP_PARENT:
-      gb_tree_node_set_parent (node, g_value_get_object (value));
-      break;
-
     case PROP_TEXT:
       gb_tree_node_set_text (node, g_value_get_string (value));
       break;
@@ -626,7 +625,7 @@ gb_tree_node_class_init (GbTreeNodeClass *klass)
                          _("Parent"),
                          _("The parent node."),
                          GB_TYPE_TREE_NODE,
-                         G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+                         G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
 
   /**
    * GbTreeNode:tree:
@@ -676,6 +675,7 @@ gb_tree_node_class_init (GbTreeNodeClass *klass)
 static void
 gb_tree_node_init (GbTreeNode *node)
 {
+  node->needs_build = TRUE;
 }
 
 static gboolean
@@ -789,3 +789,20 @@ gb_tree_node_show_popover (GbTreeNode *self,
       gb_tree_node_show_popover_timeout_cb (popreq);
     }
 }
+
+gboolean
+_gb_tree_node_get_needs_build (GbTreeNode *self)
+{
+  g_assert (GB_IS_TREE_NODE (self));
+
+  return self->needs_build;
+}
+
+void
+_gb_tree_node_set_needs_build (GbTreeNode *self,
+                               gboolean    needs_build)
+{
+  g_assert (GB_IS_TREE_NODE (self));
+
+  self->needs_build = !!needs_build;
+}
diff --git a/src/tree/gb-tree-private.h b/src/tree/gb-tree-private.h
index df57db0..ff9114c 100644
--- a/src/tree/gb-tree-private.h
+++ b/src/tree/gb-tree-private.h
@@ -30,7 +30,14 @@ GtkTreePath *_gb_tree_get_path                (GbTree        *tree,
 
 void         _gb_tree_node_set_tree           (GbTreeNode    *node,
                                                GbTree        *tree);
+void         _gb_tree_node_set_parent         (GbTreeNode    *node,
+                                               GbTreeNode    *parent);
+gboolean     _gb_tree_node_get_needs_build    (GbTreeNode    *node);
+void         _gb_tree_node_set_needs_build    (GbTreeNode    *node,
+                                               gboolean       needs_build);
 
+void         _gb_tree_builder_set_tree        (GbTreeBuilder *builder,
+                                               GbTree        *tree);
 void         _gb_tree_builder_added           (GbTreeBuilder *builder,
                                                GbTree        *tree);
 void         _gb_tree_builder_removed         (GbTreeBuilder *builder,
diff --git a/src/tree/gb-tree.c b/src/tree/gb-tree.c
index a011526..d000378 100644
--- a/src/tree/gb-tree.c
+++ b/src/tree/gb-tree.c
@@ -36,7 +36,6 @@ typedef struct
   GtkCellRenderer   *cell_text;
   GtkTreeStore      *store;
   guint              show_icons : 1;
-  guint              building;
 } GbTreePrivate;
 
 typedef struct
@@ -71,6 +70,27 @@ static GParamSpec *gParamSpecs [LAST_PROP];
 static guint gSignals [LAST_SIGNAL];
 
 static void
+gb_tree_build_node (GbTree     *self,
+                    GbTreeNode *node)
+{
+  GbTreePrivate *priv = gb_tree_get_instance_private (self);
+  gsize i;
+
+  g_assert (GB_IS_TREE (self));
+  g_assert (GB_IS_TREE_NODE (node));
+
+  _gb_tree_node_set_needs_build (node, FALSE);
+
+  for (i = 0; i < priv->builders->len; i++)
+    {
+      GbTreeBuilder *builder;
+
+      builder = g_ptr_array_index (priv->builders, i);
+      _gb_tree_builder_build_node (builder, node);
+    }
+}
+
+static void
 gb_tree_unselect (GbTree *self)
 {
   GtkTreeSelection *selection;
@@ -397,7 +417,8 @@ gb_tree_add_builder_foreach_cb (GtkTreeModel *model,
   g_return_val_if_fail (iter != NULL, FALSE);
 
   gtk_tree_model_get (model, iter, 0, &node, -1);
-  _gb_tree_builder_build_node (builder, node);
+  if (!_gb_tree_node_get_needs_build (node))
+    _gb_tree_builder_build_node (builder, node);
   g_clear_object (&node);
 
   IDE_RETURN (FALSE);
@@ -500,45 +521,39 @@ gb_tree_add (GbTree     *self,
              gboolean    prepend)
 {
   GbTreePrivate *priv = gb_tree_get_instance_private (self);
-  GbTreeBuilder *builder;
   GtkTreePath *path;
+  GtkTreeIter *parentptr;
   GtkTreeIter iter;
-  GtkTreeIter that;
-  gint i;
+  GtkTreeIter parent;
 
   g_return_if_fail (GB_IS_TREE (self));
   g_return_if_fail (GB_IS_TREE_NODE (node));
   g_return_if_fail (GB_IS_TREE_NODE (child));
 
-  g_object_set (child, "parent", node, NULL);
+  _gb_tree_node_set_tree (child, self);
+  _gb_tree_node_set_parent (child, node);
 
-  if ((path = gb_tree_node_get_path (node)))
+  if (node == priv->root)
     {
-      gtk_tree_model_get_iter (GTK_TREE_MODEL (priv->store), &iter, path);
-      if (prepend)
-        gtk_tree_store_prepend (priv->store, &that, &iter);
-      else
-        gtk_tree_store_append (priv->store, &that, &iter);
-      gtk_tree_store_set (priv->store, &that, 0, child, -1);
-      gtk_tree_path_free (path);
+      parentptr = NULL;
     }
   else
     {
-      if (prepend)
-        gtk_tree_store_prepend (priv->store, &iter, NULL);
-      else
-        gtk_tree_store_append (priv->store, &iter, NULL);
-      gtk_tree_store_set (priv->store, &iter, 0, child, -1);
+      path = gb_tree_node_get_path (node);
+      gtk_tree_model_get_iter (GTK_TREE_MODEL (priv->store), &parent, path);
+      parentptr = &parent;
+      g_clear_pointer (&path, gtk_tree_path_free);
     }
 
-  if (!priv->building)
-    {
-      for (i = 0; i < priv->builders->len; i++)
-        {
-          builder = g_ptr_array_index (priv->builders, i);
-          _gb_tree_builder_build_node (builder, child);
-        }
-    }
+  if (prepend)
+    gtk_tree_store_prepend (priv->store, &iter, parentptr);
+  else
+    gtk_tree_store_append (priv->store, &iter, parentptr);
+
+  gtk_tree_store_set (priv->store, &iter, 0, child, -1);
+
+  if (gb_tree_node_get_expanded (node))
+    gb_tree_build_node (self, child);
 }
 
 static void
@@ -581,6 +596,66 @@ gb_tree_row_activated (GtkTreeView       *tree_view,
     }
 }
 
+static void
+gb_tree_row_expanded (GtkTreeView *tree_view,
+                      GtkTreeIter *iter,
+                      GtkTreePath *path)
+{
+  GbTree *self = (GbTree *)tree_view;
+  GbTreePrivate *priv = gb_tree_get_instance_private (self);
+  GtkTreeModel *model;
+  GbTreeNode *node;
+
+  g_assert (GB_IS_TREE (self));
+  g_assert (iter != NULL);
+  g_assert (path != NULL);
+
+  model = GTK_TREE_MODEL (priv->store);
+
+  gtk_tree_model_get (model, iter, 0, &node, -1);
+
+  if (_gb_tree_node_get_needs_build (node))
+    gb_tree_build_node (self, node);
+
+  /*
+   * The following code looks like inefficient use of GtkTreeModel as we
+   * are not using iter_children() and iter_next(). However, this is required
+   * since the tree will likely have changes since our last iteration cycle.
+   * This is due to the builders adding children for the individual nodes.
+   * Therefore, we simply require that path is still valid (since builders
+   * cannot change anything but current/child nodes).
+   */
+
+  if (gtk_tree_model_iter_has_child (model, iter))
+    {
+      GtkTreeIter child_iter;
+      guint n_children;
+      guint i;
+
+      n_children = gtk_tree_model_iter_n_children (model, iter);
+
+      for (i = 0; i < n_children; i++)
+        {
+          gtk_tree_model_get_iter (model, iter, path);
+
+          if (gtk_tree_model_iter_nth_child (model, &child_iter, iter, i))
+            {
+              GbTreeNode *child;
+
+              gtk_tree_model_get (model, &child_iter, 0, &child, -1);
+
+              if (_gb_tree_node_get_needs_build (child))
+                gb_tree_build_node (self, child);
+
+              g_clear_object (&child);
+            }
+        }
+    }
+
+  g_clear_object (&node);
+}
+
+
 static gboolean
 gb_tree_button_press_event (GtkWidget      *widget,
                             GdkEventButton *button)
@@ -636,10 +711,16 @@ static gboolean
 gb_tree_find_item_foreach_cb (GtkTreeModel *model,
                               GtkTreePath  *path,
                               GtkTreeIter  *iter,
-                              gpointer      data)
+                              gpointer      user_data)
 {
   GbTreeNode *node = NULL;
-  NodeLookup *lookup = data;
+  NodeLookup *lookup = user_data;
+  gboolean ret = FALSE;
+
+  g_assert (GTK_IS_TREE_MODEL (model));
+  g_assert (path != NULL);
+  g_assert (iter != NULL);
+  g_assert (lookup != NULL);
 
   gtk_tree_model_get (model, iter, 0, &node, -1);
 
@@ -652,11 +733,13 @@ gb_tree_find_item_foreach_cb (GtkTreeModel *model,
       if (lookup->equal_func (lookup->key, item))
         {
           lookup->result = node;
-          return TRUE;
+          ret = TRUE;
         }
     }
 
-  return FALSE;
+  g_clear_object (&node);
+
+  return ret;
 }
 
 static void
@@ -835,6 +918,7 @@ gb_tree_class_init (GbTreeClass *klass)
   widget_class->button_press_event = gb_tree_button_press_event;
 
   tree_view_class->row_activated = gb_tree_row_activated;
+  tree_view_class->row_expanded = gb_tree_row_expanded;
 
   klass->action = gb_tree_real_action;
 
@@ -1101,18 +1185,14 @@ gb_tree_add_builder (GbTree        *self,
   g_return_if_fail (GB_IS_TREE (self));
   g_return_if_fail (GB_IS_TREE_BUILDER (builder));
 
-  g_object_set (builder, "tree", self, NULL);
   g_ptr_array_add (priv->builders, g_object_ref_sink (builder));
 
-  if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (priv->store), &iter))
-    {
-      priv->building++;
-      gb_tree_foreach (self, &iter, gb_tree_add_builder_foreach_cb, builder);
-      priv->building--;
-    }
-
+  _gb_tree_builder_set_tree (builder, self);
   _gb_tree_builder_added (builder, self);
 
+  if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (priv->store), &iter))
+    gb_tree_foreach (self, &iter, gb_tree_add_builder_foreach_cb, builder);
+
   IDE_EXIT;
 }
 
@@ -1180,26 +1260,30 @@ void
 gb_tree_set_root (GbTree     *self,
                   GbTreeNode *root)
 {
-  GbTreeBuilder *builder;
-  gint i;
   GbTreePrivate *priv = gb_tree_get_instance_private (self);
 
   IDE_ENTRY;
 
   g_return_if_fail (GB_IS_TREE (self));
 
-  gtk_tree_store_clear (priv->store);
-  g_clear_object (&priv->root);
-
-  if (root)
+  if (priv->root != root)
     {
-      priv->root = g_object_ref_sink (root);
-      _gb_tree_node_set_tree (root, self);
-      for (i = 0; i < priv->builders->len; i++)
+      if (priv->root != NULL)
         {
-          builder = g_ptr_array_index (priv->builders, i);
-          _gb_tree_builder_build_node (builder, root);
+          _gb_tree_node_set_parent (priv->root, NULL);
+          _gb_tree_node_set_tree (priv->root, NULL);
+          g_clear_object (&priv->root);
         }
+
+      if (root != NULL)
+        {
+          priv->root = g_object_ref (root);
+          _gb_tree_node_set_parent (priv->root, NULL);
+          _gb_tree_node_set_tree (priv->root, self);
+          gb_tree_build_node (self, priv->root);
+        }
+
+      g_object_notify_by_pspec (G_OBJECT (self), gParamSpecs [PROP_ROOT]);
     }
 
   IDE_EXIT;
@@ -1208,20 +1292,21 @@ gb_tree_set_root (GbTree     *self,
 void
 gb_tree_rebuild (GbTree *self)
 {
-  GbTreeNode *root;
-  GtkTreeSelection *selection;
   GbTreePrivate *priv = gb_tree_get_instance_private (self);
+  GtkTreeSelection *selection;
 
   g_return_if_fail (GB_IS_TREE (self));
 
-  /* avoid dealign with selection changes while rebuilding */
+  /*
+   * We don't want notification of selection changes while rebuilding.
+   */
   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (self));
   gtk_tree_selection_unselect_all (selection);
 
-  if ((root = priv->root ? g_object_ref (priv->root) : NULL))
+  if (priv->root != NULL)
     {
-      gb_tree_set_root (self, root);
-      g_object_unref (root);
+      gtk_tree_store_clear (priv->store);
+      gb_tree_build_node (self, priv->root);
     }
 }
 
@@ -1331,7 +1416,6 @@ _gb_tree_rebuild_node (GbTree     *self,
   GtkTreePath *path;
   GtkTreeIter iter;
   GtkTreeIter child;
-  guint i;
 
   g_return_if_fail (GB_IS_TREE (self));
   g_return_if_fail (GB_IS_TREE_NODE (node));
@@ -1343,27 +1427,11 @@ _gb_tree_rebuild_node (GbTree     *self,
   if (gtk_tree_model_iter_children (model, &child, &iter))
     {
       while (gtk_tree_store_remove (priv->store, &child))
-        { /* Do Nothing */ }
+        {
+        }
     }
 
-  priv->building++;
-  for (i = 0; i < priv->builders->len; i++)
-    {
-      GbTreeBuilder *builder;
-
-      /*
-       * FIXME:
-       *
-       * Refactor this to do all builders when walking each node.
-       */
-
-      builder = g_ptr_array_index (priv->builders, i);
-      gb_tree_foreach (self,
-                       &iter,
-                       gb_tree_add_builder_foreach_cb,
-                       builder);
-      priv->building--;
-    }
+  gb_tree_build_node (self, node);
 
   gtk_tree_path_free (path);
 }


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