[gtk/wip/otte/listview: 95/199] listitemmanager: Move list of listitems here



commit c8c5f5f3ee0ebdc1406034bf065b102f597ad1e8
Author: Benjamin Otte <otte redhat com>
Date:   Wed Feb 6 20:48:08 2019 +0100

    listitemmanager: Move list of listitems here
    
    All the listview infrastructure moved with it, so the next step is
    moving that back...

 gtk/gtklistitemmanager.c        | 676 ++++++++++++++++++++++++++++++++++++++--
 gtk/gtklistitemmanagerprivate.h |  45 ++-
 gtk/gtklistview.c               | 658 +++-----------------------------------
 3 files changed, 748 insertions(+), 631 deletions(-)
---
diff --git a/gtk/gtklistitemmanager.c b/gtk/gtklistitemmanager.c
index 00bce1a10b..6cadec777b 100644
--- a/gtk/gtklistitemmanager.c
+++ b/gtk/gtklistitemmanager.c
@@ -24,6 +24,8 @@
 #include "gtklistitemprivate.h"
 #include "gtkwidgetprivate.h"
 
+#define GTK_LIST_VIEW_MAX_LIST_ITEMS 200
+
 struct _GtkListItemManager
 {
   GObject parent_instance;
@@ -31,6 +33,14 @@ struct _GtkListItemManager
   GtkWidget *widget;
   GtkSelectionModel *model;
   GtkListItemFactory *factory;
+
+  GtkRbTree *items;
+
+  /* managing the visible region */
+  GtkWidget *anchor; /* may be NULL if list is empty */
+  int anchor_align; /* what to align the anchor to */
+  guint anchor_start; /* start of region we allocate row widgets for */
+  guint anchor_end; /* end of same region - first position to not have a widget */
 };
 
 struct _GtkListItemManagerClass
@@ -45,57 +55,669 @@ struct _GtkListItemManagerChange
 
 G_DEFINE_TYPE (GtkListItemManager, gtk_list_item_manager, G_TYPE_OBJECT)
 
-static void
-gtk_list_item_manager_dispose (GObject *object)
+void
+gtk_list_item_manager_augment_node (GtkRbTree *tree,
+                                    gpointer   node_augment,
+                                    gpointer   node,
+                                    gpointer   left,
+                                    gpointer   right)
 {
-  GtkListItemManager *self = GTK_LIST_ITEM_MANAGER (object);
+  GtkListItemManagerItem *item = node;
+  GtkListItemManagerItemAugment *aug = node_augment;
 
-  g_clear_object (&self->model);
-  g_clear_object (&self->factory);
+  aug->n_items = item->n_items;
 
-  G_OBJECT_CLASS (gtk_list_item_manager_parent_class)->dispose (object);
-}
+  if (left)
+    {
+      GtkListItemManagerItemAugment *left_aug = gtk_rb_tree_get_augment (tree, left);
 
-static void
-gtk_list_item_manager_class_init (GtkListItemManagerClass *klass)
-{
-  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+      aug->n_items += left_aug->n_items;
+    }
 
-  object_class->dispose = gtk_list_item_manager_dispose;
+  if (right)
+    {
+      GtkListItemManagerItemAugment *right_aug = gtk_rb_tree_get_augment (tree, right);
+
+      aug->n_items += right_aug->n_items;
+    }
 }
 
 static void
-gtk_list_item_manager_init (GtkListItemManager *self)
+gtk_list_item_manager_clear_node (gpointer _item)
 {
+  GtkListItemManagerItem *item = _item;
+
+  g_assert (item->widget == NULL);
 }
 
 GtkListItemManager *
-gtk_list_item_manager_new (GtkWidget *widget)
+gtk_list_item_manager_new_for_size (GtkWidget            *widget,
+                                    gsize                 element_size,
+                                    gsize                 augment_size,
+                                    GtkRbTreeAugmentFunc  augment_func)
 {
   GtkListItemManager *self;
 
   g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
+  g_return_val_if_fail (element_size >= sizeof (GtkListItemManagerItem), NULL);
+  g_return_val_if_fail (augment_size >= sizeof (GtkListItemManagerItemAugment), NULL);
 
   self = g_object_new (GTK_TYPE_LIST_ITEM_MANAGER, NULL);
 
+  /* not taking a ref because the widget refs us */
   self->widget = widget;
 
+  self->items = gtk_rb_tree_new_for_size (element_size,
+                                          augment_size,
+                                          augment_func,
+                                          gtk_list_item_manager_clear_node,
+                                          NULL);
+
   return self;
 }
 
+gpointer
+gtk_list_item_manager_get_first (GtkListItemManager *self)
+{
+  return gtk_rb_tree_get_first (self->items);
+}
+
+gpointer
+gtk_list_item_manager_get_root (GtkListItemManager *self)
+{
+  return gtk_rb_tree_get_root (self->items);
+}
+
+/*
+ * gtk_list_item_manager_get_nth:
+ * @self: a #GtkListItemManager
+ * @position: position of the item
+ * @offset: (out): offset into the returned item
+ *
+ * Looks up the GtkListItemManagerItem that represents @position.
+ *
+ * If a the returned item represents multiple rows, the @offset into
+ * the returned item for @position will be set. If the returned item
+ * represents a row with an existing widget, @offset will always be 0.
+ *
+ * Returns: (type GtkListItemManagerItem): the item for @position or
+ *     %NULL if position is out of range
+ **/
+gpointer
+gtk_list_item_manager_get_nth (GtkListItemManager *self,
+                               guint               position,
+                               guint              *offset)
+{
+  GtkListItemManagerItem *item, *tmp;
+
+  item = gtk_rb_tree_get_root (self->items);
+
+  while (item)
+    {
+      tmp = gtk_rb_tree_node_get_left (item);
+      if (tmp)
+        {
+          GtkListItemManagerItemAugment *aug = gtk_rb_tree_get_augment (self->items, tmp);
+          if (position < aug->n_items)
+            {
+              item = tmp;
+              continue;
+            }
+          position -= aug->n_items;
+        }
+
+      if (position < item->n_items)
+        break;
+      position -= item->n_items;
+
+      item = gtk_rb_tree_node_get_right (item);
+    }
+
+  if (offset)
+    *offset = item ? position : 0;
+
+  return item;
+}
+
+guint
+gtk_list_item_manager_get_item_position (GtkListItemManager *self,
+                                         gpointer            item)
+{
+  GtkListItemManagerItem *parent, *left;
+  int pos;
+
+  left = gtk_rb_tree_node_get_left (item);
+  if (left)
+    {
+      GtkListItemManagerItemAugment *aug = gtk_rb_tree_get_augment (self->items, left);
+      pos = aug->n_items;
+    }
+  else
+    {
+      pos = 0; 
+    }
+
+  for (parent = gtk_rb_tree_node_get_parent (item);
+       parent != NULL;
+       parent = gtk_rb_tree_node_get_parent (item))
+    {
+      left = gtk_rb_tree_node_get_left (parent);
+
+      if (left != item)
+        {
+          if (left)
+            {
+              GtkListItemManagerItemAugment *aug = gtk_rb_tree_get_augment (self->items, left);
+              pos += aug->n_items;
+            }
+          pos += parent->n_items;
+        }
+
+      item = parent;
+    }
+
+  return pos;
+}
+
+gpointer
+gtk_list_item_manager_get_item_augment (GtkListItemManager *self,
+                                        gpointer            item)
+{
+  return gtk_rb_tree_get_augment (self->items, item);
+}
+
+static void
+gtk_list_item_manager_remove_items (GtkListItemManager       *self,
+                                    GtkListItemManagerChange *change,
+                                    guint                     position,
+                                    guint                     n_items)
+{
+  GtkListItemManagerItem *item;
+
+  if (n_items == 0)
+    return;
+
+  item = gtk_list_item_manager_get_nth (self, position, NULL);
+
+  while (n_items > 0)
+    {
+      if (item->n_items > n_items)
+        {
+          item->n_items -= n_items;
+          gtk_rb_tree_node_mark_dirty (item);
+          n_items = 0;
+        }
+      else
+        {
+          GtkListItemManagerItem *next = gtk_rb_tree_node_get_next (item);
+          if (item->widget)
+            gtk_list_item_manager_release_list_item (self, change, item->widget);
+          item->widget = NULL;
+          n_items -= item->n_items;
+          gtk_rb_tree_remove (self->items, item);
+          item = next;
+        }
+    }
+
+  gtk_widget_queue_resize (GTK_WIDGET (self->widget));
+}
+
+static void
+gtk_list_item_manager_add_items (GtkListItemManager *self,
+                                 guint               position,
+                                 guint               n_items)
+{  
+  GtkListItemManagerItem *item;
+  guint offset;
+
+  if (n_items == 0)
+    return;
+
+  item = gtk_list_item_manager_get_nth (self, position, &offset);
+
+  if (item == NULL || item->widget)
+    item = gtk_rb_tree_insert_before (self->items, item);
+  item->n_items += n_items;
+  gtk_rb_tree_node_mark_dirty (item);
+
+  gtk_widget_queue_resize (GTK_WIDGET (self->widget));
+}
+
+static void
+gtk_list_item_manager_unset_anchor (GtkListItemManager *self)
+{
+  self->anchor = NULL;
+  self->anchor_align = 0;
+  self->anchor_start = 0;
+  self->anchor_end = 0;
+}
+
+static gboolean
+gtk_list_item_manager_merge_list_items (GtkListItemManager     *self,
+                                        GtkListItemManagerItem *first,
+                                        GtkListItemManagerItem *second)
+{
+  if (first->widget || second->widget)
+    return FALSE;
+
+  first->n_items += second->n_items;
+  gtk_rb_tree_node_mark_dirty (first);
+  gtk_rb_tree_remove (self->items, second);
+
+  return TRUE;
+}
+
+static void
+gtk_list_item_manager_release_items (GtkListItemManager *self,
+                                     GQueue             *released)
+{
+  GtkListItemManagerItem *item, *prev, *next;
+  guint i;
+
+  item = gtk_rb_tree_get_first (self->items);
+  i = 0;
+  while (i < self->anchor_start)
+    {
+      if (item->widget)
+        {
+          g_queue_push_tail (released, item->widget);
+          item->widget = NULL;
+          i++;
+          prev = gtk_rb_tree_node_get_previous (item);
+          if (prev && gtk_list_item_manager_merge_list_items (self, prev, item))
+            item = prev;
+          next = gtk_rb_tree_node_get_next (item);
+          if (next && next->widget == NULL)
+            {
+              i += next->n_items;
+              if (!gtk_list_item_manager_merge_list_items (self, next, item))
+                g_assert_not_reached ();
+              item = gtk_rb_tree_node_get_next (next);
+            }
+          else 
+            {
+              item = next;
+            }
+        }
+      else
+        {
+          i += item->n_items;
+          item = gtk_rb_tree_node_get_next (item);
+        }
+    }
+
+  item = gtk_list_item_manager_get_nth (self, self->anchor_end, NULL);
+  if (item == NULL)
+    return;
+  
+  if (item->widget)
+    {
+      g_queue_push_tail (released, item->widget);
+      item->widget = NULL;
+      prev = gtk_rb_tree_node_get_previous (item);
+      if (prev && gtk_list_item_manager_merge_list_items (self, prev, item))
+        item = prev;
+    }
+
+  while ((next = gtk_rb_tree_node_get_next (item)))
+    {
+      if (next->widget)
+        {
+          g_queue_push_tail (released, next->widget);
+          next->widget = NULL;
+        }
+      gtk_list_item_manager_merge_list_items (self, item, next);
+    }
+}
+
+static void
+gtk_list_item_manager_ensure_items (GtkListItemManager       *self,
+                                    GtkListItemManagerChange *change,
+                                    guint                     update_start)
+{
+  GtkListItemManagerItem *item, *new_item;
+  guint i, offset;
+  GtkWidget *widget, *insert_after;
+  GQueue released = G_QUEUE_INIT;
+
+  gtk_list_item_manager_release_items (self, &released);
+
+  item = gtk_list_item_manager_get_nth (self, self->anchor_start, &offset);
+  if (offset > 0)
+    {
+      new_item = gtk_rb_tree_insert_before (self->items, item);
+      new_item->n_items = offset;
+      item->n_items -= offset;
+      gtk_rb_tree_node_mark_dirty (item);
+    }
+
+  insert_after = NULL;
+
+  for (i = self->anchor_start; i < self->anchor_end; i++)
+    {
+      if (item->n_items > 1)
+        {
+          new_item = gtk_rb_tree_insert_before (self->items, item);
+          new_item->n_items = 1;
+          item->n_items--;
+          gtk_rb_tree_node_mark_dirty (item);
+        }
+      else
+        {
+          new_item = item;
+          item = gtk_rb_tree_node_get_next (item);
+        }
+      if (new_item->widget == NULL)
+        {
+          if (change)
+            {
+              new_item->widget = gtk_list_item_manager_try_reacquire_list_item (self,
+                                                                                change,
+                                                                                i,
+                                                                                insert_after);
+            }
+          if (new_item->widget == NULL)
+            {
+              new_item->widget = g_queue_pop_head (&released);
+              if (new_item->widget)
+                {
+                  gtk_list_item_manager_move_list_item (self,
+                                                        new_item->widget,
+                                                        i,
+                                                        insert_after);
+                }
+              else
+                {
+                  new_item->widget = gtk_list_item_manager_acquire_list_item (self,
+                                                                              i,
+                                                                              insert_after);
+                }
+            }
+        }
+      else
+        {
+          if (update_start <= i)
+            gtk_list_item_manager_update_list_item (self, new_item->widget, i);
+        }
+      insert_after = new_item->widget;
+    }
+
+  while ((widget = g_queue_pop_head (&released)))
+    gtk_list_item_manager_release_list_item (self, NULL, widget);
+}
+
+void
+gtk_list_item_manager_set_anchor (GtkListItemManager       *self,
+                                  guint                     position,
+                                  double                    align,
+                                  GtkListItemManagerChange *change,
+                                  guint                     update_start)
+{
+  GtkListItemManagerItem *item;
+  guint items_before, items_after, total_items, n_items;
+
+  g_assert (align >= 0.0 && align <= 1.0);
+
+  if (self->model)
+    n_items = g_list_model_get_n_items (G_LIST_MODEL (self->model));
+  else
+    n_items = 0;
+  if (n_items == 0)
+    {
+      gtk_list_item_manager_unset_anchor (self);
+      return;
+    }
+  total_items = MIN (GTK_LIST_VIEW_MAX_LIST_ITEMS, n_items);
+  if (align < 0.5)
+    items_before = ceil (total_items * align);
+  else
+    items_before = floor (total_items * align);
+  items_after = total_items - items_before;
+  self->anchor_start = CLAMP (position, items_before, n_items - items_after) - items_before;
+  self->anchor_end = self->anchor_start + total_items;
+  g_assert (self->anchor_end <= n_items);
+
+  gtk_list_item_manager_ensure_items (self, change, update_start);
+
+  item = gtk_list_item_manager_get_nth (self, position, NULL);
+  self->anchor = item->widget;
+  g_assert (self->anchor);
+  self->anchor_align = align;
+
+  gtk_widget_queue_allocate (GTK_WIDGET (self->widget));
+}
+
+static void
+gtk_list_item_manager_model_items_changed_cb (GListModel         *model,
+                                              guint               position,
+                                              guint               removed,
+                                              guint               added,
+                                              GtkListItemManager *self)
+{
+  GtkListItemManagerChange *change;
+
+  change = gtk_list_item_manager_begin_change (self);
+
+  gtk_list_item_manager_remove_items (self, change, position, removed);
+  gtk_list_item_manager_add_items (self, position, added);
+
+  /* The anchor was removed, but it may just have moved to a different position */
+  if (self->anchor && gtk_list_item_manager_change_contains (change, self->anchor))
+    {
+      /* The anchor was removed, do a more expensive rebuild trying to find if
+       * the anchor maybe got readded somewhere else */
+      GtkListItemManagerItem *item, *new_item;
+      GtkWidget *insert_after;
+      guint i, offset, anchor_pos;
+      
+      item = gtk_list_item_manager_get_nth (self, position, &offset);
+      for (new_item = item ? gtk_rb_tree_node_get_previous (item) : gtk_rb_tree_get_last (self->items);
+           new_item && new_item->widget == NULL;
+           new_item = gtk_rb_tree_node_get_previous (new_item))
+        { }
+      if (new_item)
+        insert_after = new_item->widget;
+      else
+        insert_after = NULL; /* we're at the start */
+
+      for (i = 0; i < added; i++)
+        {
+          GtkWidget *widget;
+
+          widget = gtk_list_item_manager_try_reacquire_list_item (self,
+                                                                  change,
+                                                                  position + i,
+                                                                  insert_after);
+          if (widget == NULL)
+            {
+              offset++;
+              continue;
+            }
+
+          if (offset > 0)
+            {
+              new_item = gtk_rb_tree_insert_before (self->items, item);
+              new_item->n_items = offset;
+              item->n_items -= offset;
+              offset = 0;
+              gtk_rb_tree_node_mark_dirty (item);
+            }
+
+          if (item->n_items == 1)
+            {
+              new_item = item;
+              item = gtk_rb_tree_node_get_next (item);
+            }
+          else
+            {
+              new_item = gtk_rb_tree_insert_before (self->items, item);
+              new_item->n_items = 1;
+              item->n_items--;
+              gtk_rb_tree_node_mark_dirty (item);
+            }
+
+          new_item->widget = widget;
+          insert_after = widget;
+
+          if (widget == self->anchor)
+            {
+              anchor_pos = position + i;
+              break;
+            }
+        }
+
+      if (i == added)
+        {
+          /* The anchor wasn't readded. Guess a good anchor position */
+          anchor_pos = gtk_list_item_get_position (GTK_LIST_ITEM (self->anchor));
+
+          anchor_pos = position + (anchor_pos - position) * added / removed;
+          if (anchor_pos >= g_list_model_get_n_items (G_LIST_MODEL (self->model)) &&
+              anchor_pos > 0)
+            anchor_pos--;
+        }
+      gtk_list_item_manager_set_anchor (self, anchor_pos, self->anchor_align, change, position);
+    }
+  else
+    {
+      /* The anchor is still where it was.
+       * We just may need to update its position and check that its surrounding widgets
+       * exist (they might be new ones). */
+      guint anchor_pos;
+      
+      if (self->anchor)
+        {
+          anchor_pos = gtk_list_item_get_position (GTK_LIST_ITEM (self->anchor));
+
+          if (anchor_pos >= position)
+            anchor_pos += added - removed;
+        }
+      else
+        {
+          anchor_pos = 0;
+        }
+
+      gtk_list_item_manager_set_anchor (self, anchor_pos, self->anchor_align, change, position);
+    }
+
+  gtk_list_item_manager_end_change (self, change);
+}
+
+static void
+gtk_list_item_manager_model_selection_changed_cb (GListModel         *model,
+                                                  guint               position,
+                                                  guint               n_items,
+                                                  GtkListItemManager *self)
+{
+  GtkListItemManagerItem *item;
+  guint offset;
+
+  item = gtk_list_item_manager_get_nth (self, position, &offset);
+
+  if (offset)
+    {
+      position += item->n_items - offset;
+      if (item->n_items - offset > n_items)
+        n_items = 0;
+      else
+        n_items -= item->n_items - offset;
+      item = gtk_rb_tree_node_get_next (item);
+    }
+
+  while (n_items > 0)
+    {
+      if (item->widget)
+        gtk_list_item_manager_update_list_item (self, item->widget, position);
+      position += item->n_items;
+      n_items -= MIN (n_items, item->n_items);
+      item = gtk_rb_tree_node_get_next (item);
+    }
+}
+
+guint
+gtk_list_item_manager_get_anchor (GtkListItemManager *self,
+                                  double             *align)
+{
+  guint anchor_pos;
+
+  if (self->anchor)
+    anchor_pos = gtk_list_item_get_position (GTK_LIST_ITEM (self->anchor));
+  else
+    anchor_pos = 0;
+
+  if (align)
+    *align = self->anchor_align;
+
+  return anchor_pos;
+}
+
+static void
+gtk_list_item_manager_clear_model (GtkListItemManager *self)
+{
+  if (self->model == NULL)
+    return;
+
+  gtk_list_item_manager_remove_items (self, NULL, 0, g_list_model_get_n_items (G_LIST_MODEL (self->model)));
+
+  g_signal_handlers_disconnect_by_func (self->model,
+                                        gtk_list_item_manager_model_selection_changed_cb,
+                                        self);
+  g_signal_handlers_disconnect_by_func (self->model,
+                                        gtk_list_item_manager_model_items_changed_cb,
+                                        self);
+  g_clear_object (&self->model);
+
+  gtk_list_item_manager_unset_anchor (self);
+}
+
+static void
+gtk_list_item_manager_dispose (GObject *object)
+{
+  GtkListItemManager *self = GTK_LIST_ITEM_MANAGER (object);
+
+  gtk_list_item_manager_clear_model (self);
+
+  g_clear_object (&self->factory);
+
+  g_clear_pointer (&self->items, gtk_rb_tree_unref);
+
+  G_OBJECT_CLASS (gtk_list_item_manager_parent_class)->dispose (object);
+}
+
+static void
+gtk_list_item_manager_class_init (GtkListItemManagerClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->dispose = gtk_list_item_manager_dispose;
+}
+
+static void
+gtk_list_item_manager_init (GtkListItemManager *self)
+{
+}
+
 void
 gtk_list_item_manager_set_factory (GtkListItemManager *self,
                                    GtkListItemFactory *factory)
 {
+  guint n_items, anchor;
+  double anchor_align;
+
   g_return_if_fail (GTK_IS_LIST_ITEM_MANAGER (self));
   g_return_if_fail (GTK_IS_LIST_ITEM_FACTORY (factory));
 
   if (self->factory == factory)
     return;
 
-  g_clear_object (&self->factory);
+  n_items = self->model ? g_list_model_get_n_items (G_LIST_MODEL (self->model)) : 0;
+  anchor = gtk_list_item_manager_get_anchor (self, &anchor_align);
+  gtk_list_item_manager_remove_items (self, NULL, 0, n_items);
+
+  g_set_object (&self->factory, factory);
 
-  self->factory = g_object_ref (factory);
+  gtk_list_item_manager_add_items (self, 0, n_items);
+  gtk_list_item_manager_set_anchor (self, anchor, anchor_align, NULL, (guint) -1);
 }
 
 GtkListItemFactory *
@@ -111,15 +733,30 @@ gtk_list_item_manager_set_model (GtkListItemManager *self,
                                  GtkSelectionModel  *model)
 {
   g_return_if_fail (GTK_IS_LIST_ITEM_MANAGER (self));
-  g_return_if_fail (model == NULL || G_IS_LIST_MODEL (model));
+  g_return_if_fail (model == NULL || GTK_IS_SELECTION_MODEL (model));
 
   if (self->model == model)
     return;
 
-  g_clear_object (&self->model);
+  gtk_list_item_manager_clear_model (self);
 
   if (model)
-    self->model = g_object_ref (model);
+    {
+      self->model = g_object_ref (model);
+
+      g_signal_connect (model,
+                        "items-changed",
+                        G_CALLBACK (gtk_list_item_manager_model_items_changed_cb),
+                        self);
+      g_signal_connect (model,
+                        "selection-changed",
+                        G_CALLBACK (gtk_list_item_manager_model_selection_changed_cb),
+                        self);
+
+      gtk_list_item_manager_add_items (self, 0, g_list_model_get_n_items (G_LIST_MODEL (model)));
+
+      gtk_list_item_manager_set_anchor (self, 0, 0, NULL, (guint) -1);
+    }
 }
 
 GtkSelectionModel *
@@ -400,3 +1037,4 @@ gtk_list_item_manager_release_list_item (GtkListItemManager       *self,
   gtk_list_item_factory_unbind (self->factory, GTK_LIST_ITEM (item));
   gtk_widget_unparent (item);
 }
+
diff --git a/gtk/gtklistitemmanagerprivate.h b/gtk/gtklistitemmanagerprivate.h
index 47a4434d7d..9e1ebab073 100644
--- a/gtk/gtklistitemmanagerprivate.h
+++ b/gtk/gtklistitemmanagerprivate.h
@@ -24,6 +24,7 @@
 #include "gtk/gtktypes.h"
 
 #include "gtk/gtklistitemfactoryprivate.h"
+#include "gtk/gtkrbtreeprivate.h"
 #include "gtk/gtkselectionmodel.h"
 
 G_BEGIN_DECLS
@@ -38,10 +39,44 @@ G_BEGIN_DECLS
 typedef struct _GtkListItemManager GtkListItemManager;
 typedef struct _GtkListItemManagerClass GtkListItemManagerClass;
 typedef struct _GtkListItemManagerChange GtkListItemManagerChange;
+typedef struct _GtkListItemManagerItem GtkListItemManagerItem; /* sorry */
+typedef struct _GtkListItemManagerItemAugment GtkListItemManagerItemAugment;
+
+struct _GtkListItemManagerItem
+{
+  GtkWidget *widget;
+  guint n_items;
+};
+
+struct _GtkListItemManagerItemAugment
+{
+  guint n_items;
+};
+
 
 GType                   gtk_list_item_manager_get_type          (void) G_GNUC_CONST;
 
-GtkListItemManager *    gtk_list_item_manager_new               (GtkWidget              *widget);
+GtkListItemManager *    gtk_list_item_manager_new_for_size      (GtkWidget              *widget,
+                                                                 gsize                   element_size,
+                                                                 gsize                   augment_size,
+                                                                 GtkRbTreeAugmentFunc    augment_func);
+#define gtk_list_item_manager_new(widget, type, augment_type, augment_func) \
+  gtk_list_item_manager_new_for_size (widget, sizeof (type), sizeof (augment_type), (augment_func))
+
+void                    gtk_list_item_manager_augment_node      (GtkRbTree              *tree,
+                                                                 gpointer                node_augment,
+                                                                 gpointer                node,
+                                                                 gpointer                left,
+                                                                 gpointer                right);
+gpointer                gtk_list_item_manager_get_root          (GtkListItemManager     *self);
+gpointer                gtk_list_item_manager_get_first         (GtkListItemManager     *self);
+gpointer                gtk_list_item_manager_get_nth           (GtkListItemManager     *self,
+                                                                 guint                   position,
+                                                                 guint                  *offset);
+guint                   gtk_list_item_manager_get_item_position (GtkListItemManager     *self,
+                                                                 gpointer                item);
+gpointer                gtk_list_item_manager_get_item_augment  (GtkListItemManager     *self,
+                                                                 gpointer                item);
 
 void                    gtk_list_item_manager_set_factory       (GtkListItemManager     *self,
                                                                  GtkListItemFactory     *factory);
@@ -52,6 +87,14 @@ GtkSelectionModel *     gtk_list_item_manager_get_model         (GtkListItemMana
 
 guint                   gtk_list_item_manager_get_size          (GtkListItemManager     *self);
 
+void                    gtk_list_item_manager_set_anchor        (GtkListItemManager     *self,
+                                                                 guint                   position,
+                                                                 double                  align,
+                                                                 GtkListItemManagerChange *change,
+                                                                 guint                   update_start);
+guint                   gtk_list_item_manager_get_anchor        (GtkListItemManager     *self,
+                                                                 double                 *align);
+
 GtkListItemManagerChange *
                         gtk_list_item_manager_begin_change      (GtkListItemManager     *self);
 void                    gtk_list_item_manager_end_change        (GtkListItemManager     *self,
diff --git a/gtk/gtklistview.c b/gtk/gtklistview.c
index 36c18d698b..e91b1b7b62 100644
--- a/gtk/gtklistview.c
+++ b/gtk/gtklistview.c
@@ -58,26 +58,18 @@ struct _GtkListView
   GtkAdjustment *adjustment[2];
   GtkScrollablePolicy scroll_policy[2];
 
-  GtkRbTree *rows;
   int list_width;
-
-  /* managing the visible region */
-  GtkWidget *anchor; /* may be NULL if list is empty */
-  int anchor_align; /* what to align the anchor to */
-  guint anchor_start; /* start of region we allocate row widgets for */
-  guint anchor_end; /* end of same region - first position to not have a widget */
 };
 
 struct _ListRow
 {
-  guint n_rows;
+  GtkListItemManagerItem parent;
   guint height; /* per row */
-  GtkWidget *widget;
 };
 
 struct _ListRowAugment
 {
-  guint n_rows;
+  GtkListItemManagerItemAugment parent;
   guint height; /* total */
 };
 
@@ -106,15 +98,15 @@ dump (GtkListView *self)
 
   n_widgets = 0;
   n_list_rows = 0;
-  g_print ("ANCHOR: %u - %u\n", self->anchor_start, self->anchor_end);
-  for (row = gtk_rb_tree_get_first (self->rows);
+  //g_print ("ANCHOR: %u - %u\n", self->anchor_start, self->anchor_end);
+  for (row = gtk_list_item_manager_get_first (self->item_manager);
        row;
        row = gtk_rb_tree_node_get_next (row))
     {
-      if (row->widget)
+      if (row->parent.widget)
         n_widgets++;
       n_list_rows++;
-      g_print ("  %4u%s (%upx)\n", row->n_rows, row->widget ? " (widget)" : "", row->height);
+      g_print ("  %4u%s (%upx)\n", row->parent.n_items, row->parent.widget ? " (widget)" : "", row->height);
     }
 
   g_print ("  => %u widgets in %u list rows\n", n_widgets, n_list_rows);
@@ -130,15 +122,15 @@ list_row_augment (GtkRbTree *tree,
   ListRow *row = node;
   ListRowAugment *aug = node_augment;
 
-  aug->height = row->height * row->n_rows;
-  aug->n_rows = row->n_rows;
+  gtk_list_item_manager_augment_node (tree, node_augment, node, left, right);
+
+  aug->height = row->height * row->parent.n_items;
 
   if (left)
     {
       ListRowAugment *left_aug = gtk_rb_tree_get_augment (tree, left);
 
       aug->height += left_aug->height;
-      aug->n_rows += left_aug->n_rows;
     }
 
   if (right)
@@ -146,94 +138,9 @@ list_row_augment (GtkRbTree *tree,
       ListRowAugment *right_aug = gtk_rb_tree_get_augment (tree, right);
 
       aug->height += right_aug->height;
-      aug->n_rows += right_aug->n_rows;
     }
 }
 
-static void
-list_row_clear (gpointer _row)
-{
-  ListRow *row = _row;
-
-  g_assert (row->widget == NULL);
-}
-
-static ListRow *
-gtk_list_view_get_row (GtkListView *self,
-                       guint        position,
-                       guint       *offset)
-{
-  ListRow *row, *tmp;
-
-  row = gtk_rb_tree_get_root (self->rows);
-
-  while (row)
-    {
-      tmp = gtk_rb_tree_node_get_left (row);
-      if (tmp)
-        {
-          ListRowAugment *aug = gtk_rb_tree_get_augment (self->rows, tmp);
-          if (position < aug->n_rows)
-            {
-              row = tmp;
-              continue;
-            }
-          position -= aug->n_rows;
-        }
-
-      if (position < row->n_rows)
-        break;
-      position -= row->n_rows;
-
-      row = gtk_rb_tree_node_get_right (row);
-    }
-
-  if (offset)
-    *offset = row ? position : 0;
-
-  return row;
-}
-
-static guint
-list_row_get_position (GtkListView *self,
-                       ListRow     *row)
-{
-  ListRow *parent, *left;
-  int pos;
-
-  left = gtk_rb_tree_node_get_left (row);
-  if (left)
-    {
-      ListRowAugment *aug = gtk_rb_tree_get_augment (self->rows, left);
-      pos = aug->n_rows;
-    }
-  else
-    {
-      pos = 0; 
-    }
-
-  for (parent = gtk_rb_tree_node_get_parent (row);
-       parent != NULL;
-       parent = gtk_rb_tree_node_get_parent (row))
-    {
-      left = gtk_rb_tree_node_get_left (parent);
-
-      if (left != row)
-        {
-          if (left)
-            {
-              ListRowAugment *aug = gtk_rb_tree_get_augment (self->rows, left);
-              pos += aug->n_rows;
-            }
-          pos += parent->n_rows;
-        }
-
-      row = parent;
-    }
-
-  return pos;
-}
-
 static ListRow *
 gtk_list_view_get_row_at_y (GtkListView *self,
                             int          y,
@@ -241,14 +148,14 @@ gtk_list_view_get_row_at_y (GtkListView *self,
 {
   ListRow *row, *tmp;
 
-  row = gtk_rb_tree_get_root (self->rows);
+  row = gtk_list_item_manager_get_root (self->item_manager);
 
   while (row)
     {
       tmp = gtk_rb_tree_node_get_left (row);
       if (tmp)
         {
-          ListRowAugment *aug = gtk_rb_tree_get_augment (self->rows, tmp);
+          ListRowAugment *aug = gtk_list_item_manager_get_item_augment (self->item_manager, tmp);
           if (y < aug->height)
             {
               row = tmp;
@@ -257,9 +164,9 @@ gtk_list_view_get_row_at_y (GtkListView *self,
           y -= aug->height;
         }
 
-      if (y < row->height * row->n_rows)
+      if (y < row->height * row->parent.n_items)
         break;
-      y -= row->height * row->n_rows;
+      y -= row->height * row->parent.n_items;
 
       row = gtk_rb_tree_node_get_right (row);
     }
@@ -280,7 +187,7 @@ list_row_get_y (GtkListView *self,
   left = gtk_rb_tree_node_get_left (row);
   if (left)
     {
-      ListRowAugment *aug = gtk_rb_tree_get_augment (self->rows, left);
+      ListRowAugment *aug = gtk_list_item_manager_get_item_augment (self->item_manager, left);
       y = aug->height;
     }
   else
@@ -296,10 +203,10 @@ list_row_get_y (GtkListView *self,
         {
           if (left)
             {
-              ListRowAugment *aug = gtk_rb_tree_get_augment (self->rows, left);
+              ListRowAugment *aug = gtk_list_item_manager_get_item_augment (self->item_manager, left);
               y += aug->height;
             }
-          y += parent->height * parent->n_rows;
+          y += parent->height * parent->parent.n_items;
         }
 
       row = parent;
@@ -314,218 +221,14 @@ gtk_list_view_get_list_height (GtkListView *self)
   ListRow *row;
   ListRowAugment *aug;
   
-  row = gtk_rb_tree_get_root (self->rows);
+  row = gtk_list_item_manager_get_root (self->item_manager);
   if (row == NULL)
     return 0;
 
-  aug = gtk_rb_tree_get_augment (self->rows, row);
+  aug = gtk_list_item_manager_get_item_augment (self->item_manager, row);
   return aug->height;
 }
 
-static gboolean
-gtk_list_view_merge_list_rows (GtkListView *self,
-                               ListRow     *first,
-                               ListRow     *second)
-{
-  if (first->widget || second->widget)
-    return FALSE;
-
-  first->n_rows += second->n_rows;
-  gtk_rb_tree_node_mark_dirty (first);
-  gtk_rb_tree_remove (self->rows, second);
-
-  return TRUE;
-}
-
-static void
-gtk_list_view_release_rows (GtkListView *self,
-                            GQueue      *released)
-{
-  ListRow *row, *prev, *next;
-  guint i;
-
-  row = gtk_rb_tree_get_first (self->rows);
-  i = 0;
-  while (i < self->anchor_start)
-    {
-      if (row->widget)
-        {
-          g_queue_push_tail (released, row->widget);
-          row->widget = NULL;
-          i++;
-          prev = gtk_rb_tree_node_get_previous (row);
-          if (prev && gtk_list_view_merge_list_rows (self, prev, row))
-            row = prev;
-          next = gtk_rb_tree_node_get_next (row);
-          if (next && next->widget == NULL)
-            {
-              i += next->n_rows;
-              if (!gtk_list_view_merge_list_rows (self, next, row))
-                g_assert_not_reached ();
-              row = gtk_rb_tree_node_get_next (next);
-            }
-          else 
-            {
-              row = next;
-            }
-        }
-      else
-        {
-          i += row->n_rows;
-          row = gtk_rb_tree_node_get_next (row);
-        }
-    }
-
-  row = gtk_list_view_get_row (self, self->anchor_end, NULL);
-  if (row == NULL)
-    return;
-  
-  if (row->widget)
-    {
-      g_queue_push_tail (released, row->widget);
-      row->widget = NULL;
-      prev = gtk_rb_tree_node_get_previous (row);
-      if (prev && gtk_list_view_merge_list_rows (self, prev, row))
-        row = prev;
-    }
-
-  while ((next = gtk_rb_tree_node_get_next (row)))
-    {
-      if (next->widget)
-        {
-          g_queue_push_tail (released, next->widget);
-          next->widget = NULL;
-        }
-      gtk_list_view_merge_list_rows (self, row, next);
-    }
-}
-
-static void
-gtk_list_view_ensure_rows (GtkListView              *self,
-                           GtkListItemManagerChange *change,
-                           guint                     update_start)
-{
-  ListRow *row, *new_row;
-  guint i, offset;
-  GtkWidget *widget, *insert_after;
-  GQueue released = G_QUEUE_INIT;
-
-  gtk_list_view_release_rows (self, &released);
-
-  row = gtk_list_view_get_row (self, self->anchor_start, &offset);
-  if (offset > 0)
-    {
-      new_row = gtk_rb_tree_insert_before (self->rows, row);
-      new_row->n_rows = offset;
-      row->n_rows -= offset;
-      gtk_rb_tree_node_mark_dirty (row);
-    }
-
-  insert_after = NULL;
-
-  for (i = self->anchor_start; i < self->anchor_end; i++)
-    {
-      if (row->n_rows > 1)
-        {
-          new_row = gtk_rb_tree_insert_before (self->rows, row);
-          new_row->n_rows = 1;
-          row->n_rows--;
-          gtk_rb_tree_node_mark_dirty (row);
-        }
-      else
-        {
-          new_row = row;
-          row = gtk_rb_tree_node_get_next (row);
-        }
-      if (new_row->widget == NULL)
-        {
-          if (change)
-            {
-              new_row->widget = gtk_list_item_manager_try_reacquire_list_item (self->item_manager,
-                                                                               change,
-                                                                               i,
-                                                                               insert_after);
-            }
-          if (new_row->widget == NULL)
-            {
-              new_row->widget = g_queue_pop_head (&released);
-              if (new_row->widget)
-                {
-                  gtk_list_item_manager_move_list_item (self->item_manager,
-                                                        new_row->widget,
-                                                        i,
-                                                        insert_after);
-                }
-              else
-                {
-                  new_row->widget = gtk_list_item_manager_acquire_list_item (self->item_manager,
-                                                                             i,
-                                                                             insert_after);
-                }
-            }
-        }
-      else
-        {
-          if (update_start <= i)
-            gtk_list_item_manager_update_list_item (self->item_manager, new_row->widget, i);
-        }
-      insert_after = new_row->widget;
-    }
-
-  while ((widget = g_queue_pop_head (&released)))
-    gtk_list_item_manager_release_list_item (self->item_manager, NULL, widget);
-}
-
-static void
-gtk_list_view_unset_anchor (GtkListView *self)
-{
-  self->anchor = NULL;
-  self->anchor_align = 0;
-  self->anchor_start = 0;
-  self->anchor_end = 0;
-}
-
-static void
-gtk_list_view_set_anchor (GtkListView              *self,
-                          guint                     position,
-                          double                    align,
-                          GtkListItemManagerChange *change,
-                          guint                     update_start)
-{
-  ListRow *row;
-  guint items_before, items_after, total_items, n_rows;
-
-  g_assert (align >= 0.0 && align <= 1.0);
-
-  if (self->model)
-    n_rows = g_list_model_get_n_items (self->model);
-  else
-    n_rows = 0;
-  if (n_rows == 0)
-    {
-      gtk_list_view_unset_anchor (self);
-      return;
-    }
-  total_items = MIN (GTK_LIST_VIEW_MAX_LIST_ITEMS, n_rows);
-  if (align < 0.5)
-    items_before = ceil (total_items * align);
-  else
-    items_before = floor (total_items * align);
-  items_after = total_items - items_before;
-  self->anchor_start = CLAMP (position, items_before, n_rows - items_after) - items_before;
-  self->anchor_end = self->anchor_start + total_items;
-  g_assert (self->anchor_end <= n_rows);
-
-  gtk_list_view_ensure_rows (self, change, update_start);
-
-  row = gtk_list_view_get_row (self, position, NULL);
-  self->anchor = row->widget;
-  g_assert (self->anchor);
-  self->anchor_align = align;
-
-  gtk_widget_queue_allocate (GTK_WIDGET (self));
-}
-
 static void
 gtk_list_view_adjustment_value_changed_cb (GtkAdjustment *adjustment,
                                            GtkListView   *self)
@@ -539,17 +242,15 @@ gtk_list_view_adjustment_value_changed_cb (GtkAdjustment *adjustment,
       row = gtk_list_view_get_row_at_y (self, gtk_adjustment_get_value (adjustment), &dy);
       if (row)
         {
-          pos = list_row_get_position (self, row) + dy / row->height;
+          pos = gtk_list_item_manager_get_item_position (self->item_manager, row) + dy / row->height;
         }
       else
         pos = 0;
 
-      gtk_list_view_set_anchor (self, pos, 0, NULL, (guint) -1);
-    }
-  else
-    { 
-      gtk_widget_queue_allocate (GTK_WIDGET (self));
+      gtk_list_item_manager_set_anchor (self->item_manager, pos, 0, NULL, (guint) -1);
     }
+  
+  gtk_widget_queue_allocate (GTK_WIDGET (self));
 }
 
 static void
@@ -570,19 +271,19 @@ gtk_list_view_update_adjustments (GtkListView    *self,
   else
     {
       ListRow *row;
+      guint anchor;
+      double anchor_align;
 
       page_size = gtk_widget_get_height (GTK_WIDGET (self));
       upper = gtk_list_view_get_list_height (self);
 
-      if (self->anchor)
-        row = gtk_list_view_get_row (self, gtk_list_item_get_position (GTK_LIST_ITEM (self->anchor)), NULL);
-      else
-        row = NULL;
+      anchor = gtk_list_item_manager_get_anchor (self->item_manager, &anchor_align);
+      row = gtk_list_item_manager_get_nth (self->item_manager, anchor, NULL);
       if (row)
         value = list_row_get_y (self, row);
       else
         value = 0;
-      value -= self->anchor_align * (page_size - (row ? row->height : 0));
+      value -= anchor_align * (page_size - (row ? row->height : 0));
     }
   upper = MAX (upper, page_size);
 
@@ -637,15 +338,15 @@ gtk_list_view_measure_across (GtkWidget      *widget,
   min = 0;
   nat = 0;
 
-  for (row = gtk_rb_tree_get_first (self->rows);
+  for (row = gtk_list_item_manager_get_first (self->item_manager);
        row != NULL;
        row = gtk_rb_tree_node_get_next (row))
     {
       /* ignore unavailable rows */
-      if (row->widget == NULL)
+      if (row->parent.widget == NULL)
         continue;
 
-      gtk_widget_measure (row->widget,
+      gtk_widget_measure (row->parent.widget,
                           orientation, for_size,
                           &child_min, &child_nat, NULL, NULL);
       min = MAX (min, child_min);
@@ -675,13 +376,13 @@ gtk_list_view_measure_list (GtkWidget      *widget,
   min = 0;
   nat = 0;
 
-  for (row = gtk_rb_tree_get_first (self->rows);
+  for (row = gtk_list_item_manager_get_first (self->item_manager);
        row != NULL;
        row = gtk_rb_tree_node_get_next (row))
     {
-      if (row->widget)
+      if (row->parent.widget)
         {
-          gtk_widget_measure (row->widget,
+          gtk_widget_measure (row->parent.widget,
                               orientation, for_size,
                               &child_min, &child_nat, NULL, NULL);
           g_array_append_val (min_heights, child_min);
@@ -691,7 +392,7 @@ gtk_list_view_measure_list (GtkWidget      *widget,
         }
       else
         {
-          n_unknown += row->n_rows;
+          n_unknown += row->parent.n_items;
         }
     }
 
@@ -735,7 +436,7 @@ gtk_list_view_size_allocate (GtkWidget *widget,
   int min, nat, row_height;
 
   /* step 0: exit early if list is empty */
-  if (gtk_rb_tree_get_root (self->rows) == NULL)
+  if (gtk_list_item_manager_get_root (self->item_manager) == NULL)
     return;
 
   /* step 1: determine width of the list */
@@ -750,14 +451,14 @@ gtk_list_view_size_allocate (GtkWidget *widget,
   /* step 2: determine height of known list items */
   heights = g_array_new (FALSE, FALSE, sizeof (int));
 
-  for (row = gtk_rb_tree_get_first (self->rows);
+  for (row = gtk_list_item_manager_get_first (self->item_manager);
        row != NULL;
        row = gtk_rb_tree_node_get_next (row))
     {
-      if (row->widget == NULL)
+      if (row->parent.widget == NULL)
         continue;
 
-      gtk_widget_measure (row->widget, GTK_ORIENTATION_VERTICAL,
+      gtk_widget_measure (row->parent.widget, GTK_ORIENTATION_VERTICAL,
                           self->list_width,
                           &min, &nat, NULL, NULL);
       if (self->scroll_policy[GTK_ORIENTATION_VERTICAL] == GTK_SCROLL_MINIMUM)
@@ -776,11 +477,11 @@ gtk_list_view_size_allocate (GtkWidget *widget,
   row_height = gtk_list_view_get_unknown_row_height (self, heights);
   g_array_free (heights, TRUE);
 
-  for (row = gtk_rb_tree_get_first (self->rows);
+  for (row = gtk_list_item_manager_get_first (self->item_manager);
        row != NULL;
        row = gtk_rb_tree_node_get_next (row))
     {
-      if (row->widget)
+      if (row->parent.widget)
         continue;
 
       if (row->height != row_height)
@@ -798,257 +499,20 @@ gtk_list_view_size_allocate (GtkWidget *widget,
   child_allocation.x = - gtk_adjustment_get_value (self->adjustment[GTK_ORIENTATION_HORIZONTAL]);
   child_allocation.y = - round (gtk_adjustment_get_value (self->adjustment[GTK_ORIENTATION_VERTICAL]));
   child_allocation.width = self->list_width;
-  for (row = gtk_rb_tree_get_first (self->rows);
+  for (row = gtk_list_item_manager_get_first (self->item_manager);
        row != NULL;
        row = gtk_rb_tree_node_get_next (row))
     {
-      if (row->widget)
+      if (row->parent.widget)
         {
           child_allocation.height = row->height;
-          gtk_widget_size_allocate (row->widget, &child_allocation, -1);
+          gtk_widget_size_allocate (row->parent.widget, &child_allocation, -1);
         }
 
-      child_allocation.y += row->height * row->n_rows;
+      child_allocation.y += row->height * row->parent.n_items;
     }
 }
 
-static guint
-gtk_list_view_get_anchor (GtkListView *self,
-                          double      *align)
-{
-  guint anchor_pos;
-
-  if (self->anchor)
-    anchor_pos = gtk_list_item_get_position (GTK_LIST_ITEM (self->anchor));
-  else
-    anchor_pos = 0;
-
-  if (align)
-    *align = self->anchor_align;
-
-  return anchor_pos;
-}
-
-static void
-gtk_list_view_remove_rows (GtkListView              *self,
-                           GtkListItemManagerChange *change,
-                           guint                     position,
-                           guint                     n_rows)
-{
-  ListRow *row;
-
-  if (n_rows == 0)
-    return;
-
-  row = gtk_list_view_get_row (self, position, NULL);
-
-  while (n_rows > 0)
-    {
-      if (row->n_rows > n_rows)
-        {
-          row->n_rows -= n_rows;
-          gtk_rb_tree_node_mark_dirty (row);
-          n_rows = 0;
-        }
-      else
-        {
-          ListRow *next = gtk_rb_tree_node_get_next (row);
-          if (row->widget)
-            gtk_list_item_manager_release_list_item (self->item_manager, change, row->widget);
-          row->widget = NULL;
-          n_rows -= row->n_rows;
-          gtk_rb_tree_remove (self->rows, row);
-          row = next;
-        }
-    }
-
-  gtk_widget_queue_resize (GTK_WIDGET (self));
-}
-
-static void
-gtk_list_view_add_rows (GtkListView *self,
-                        guint        position,
-                        guint        n_rows)
-{  
-  ListRow *row;
-  guint offset;
-
-  if (n_rows == 0)
-    return;
-
-  row = gtk_list_view_get_row (self, position, &offset);
-
-  if (row == NULL || row->widget)
-    row = gtk_rb_tree_insert_before (self->rows, row);
-  row->n_rows += n_rows;
-  gtk_rb_tree_node_mark_dirty (row);
-
-  gtk_widget_queue_resize (GTK_WIDGET (self));
-}
-
-static void
-gtk_list_view_model_items_changed_cb (GListModel  *model,
-                                      guint        position,
-                                      guint        removed,
-                                      guint        added,
-                                      GtkListView *self)
-{
-  GtkListItemManagerChange *change;
-
-  change = gtk_list_item_manager_begin_change (self->item_manager);
-
-  gtk_list_view_remove_rows (self, change, position, removed);
-  gtk_list_view_add_rows (self, position, added);
-
-  /* The anchor was removed, but it may just have moved to a different position */
-  if (self->anchor && gtk_list_item_manager_change_contains (change, self->anchor))
-    {
-      /* The anchor was removed, do a more expensive rebuild trying to find if
-       * the anchor maybe got readded somewhere else */
-      ListRow *row, *new_row;
-      GtkWidget *insert_after;
-      guint i, offset, anchor_pos;
-      
-      row = gtk_list_view_get_row (self, position, &offset);
-      for (new_row = row ? gtk_rb_tree_node_get_previous (row) : gtk_rb_tree_get_last (self->rows);
-           new_row && new_row->widget == NULL;
-           new_row = gtk_rb_tree_node_get_previous (new_row))
-        { }
-      if (new_row)
-        insert_after = new_row->widget;
-      else
-        insert_after = NULL; /* we're at the start */
-
-      for (i = 0; i < added; i++)
-        {
-          GtkWidget *widget;
-
-          widget = gtk_list_item_manager_try_reacquire_list_item (self->item_manager,
-                                                                  change,
-                                                                  position + i,
-                                                                  insert_after);
-          if (widget == NULL)
-            {
-              offset++;
-              continue;
-            }
-
-          if (offset > 0)
-            {
-              new_row = gtk_rb_tree_insert_before (self->rows, row);
-              new_row->n_rows = offset;
-              row->n_rows -= offset;
-              offset = 0;
-              gtk_rb_tree_node_mark_dirty (row);
-            }
-
-          if (row->n_rows == 1)
-            {
-              new_row = row;
-              row = gtk_rb_tree_node_get_next (row);
-            }
-          else
-            {
-              new_row = gtk_rb_tree_insert_before (self->rows, row);
-              new_row->n_rows = 1;
-              row->n_rows--;
-              gtk_rb_tree_node_mark_dirty (row);
-            }
-
-          new_row->widget = widget;
-          insert_after = widget;
-
-          if (widget == self->anchor)
-            {
-              anchor_pos = position + i;
-              break;
-            }
-        }
-
-      if (i == added)
-        {
-          /* The anchor wasn't readded. Guess a good anchor position */
-          anchor_pos = gtk_list_item_get_position (GTK_LIST_ITEM (self->anchor));
-
-          anchor_pos = position + (anchor_pos - position) * added / removed;
-          if (anchor_pos >= g_list_model_get_n_items (self->model) &&
-              anchor_pos > 0)
-            anchor_pos--;
-        }
-      gtk_list_view_set_anchor (self, anchor_pos, self->anchor_align, change, position);
-    }
-  else
-    {
-      /* The anchor is still where it was.
-       * We just may need to update its position and check that its surrounding widgets
-       * exist (they might be new ones). */
-      guint anchor_pos;
-      
-      if (self->anchor)
-        {
-          anchor_pos = gtk_list_item_get_position (GTK_LIST_ITEM (self->anchor));
-
-          if (anchor_pos >= position)
-            anchor_pos += added - removed;
-        }
-      else
-        {
-          anchor_pos = 0;
-        }
-
-      gtk_list_view_set_anchor (self, anchor_pos, self->anchor_align, change, position);
-    }
-
-  gtk_list_item_manager_end_change (self->item_manager, change);
-}
-
-static void
-gtk_list_view_model_selection_changed_cb (GListModel  *model,
-                                          guint        position,
-                                          guint        n_items,
-                                          GtkListView *self)
-{
-  ListRow *row;
-  guint offset;
-
-  row = gtk_list_view_get_row (self, position, &offset);
-
-  if (offset)
-    {
-      position += row->n_rows - offset;
-      n_items -= row->n_rows - offset;
-      row = gtk_rb_tree_node_get_next (row);
-    }
-
-  while (n_items > 0)
-    {
-      if (row->widget)
-        gtk_list_item_manager_update_list_item (self->item_manager, row->widget, position);
-      position += row->n_rows;
-      n_items -= MIN (n_items, row->n_rows);
-      row = gtk_rb_tree_node_get_next (row);
-    }
-}
-
-static void
-gtk_list_view_clear_model (GtkListView *self)
-{
-  if (self->model == NULL)
-    return;
-
-  gtk_list_view_remove_rows (self, NULL, 0, g_list_model_get_n_items (self->model));
-
-  g_signal_handlers_disconnect_by_func (self->model,
-                                        gtk_list_view_model_selection_changed_cb,
-                                        self);
-  g_signal_handlers_disconnect_by_func (self->model,
-                                        gtk_list_view_model_items_changed_cb,
-                                        self);
-  g_clear_object (&self->model);
-
-  gtk_list_view_unset_anchor (self);
-}
-
 static void
 gtk_list_view_clear_adjustment (GtkListView    *self,
                                 GtkOrientation  orientation)
@@ -1067,7 +531,7 @@ gtk_list_view_dispose (GObject *object)
 {
   GtkListView *self = GTK_LIST_VIEW (object);
 
-  gtk_list_view_clear_model (self);
+  g_clear_object (&self->model);
 
   gtk_list_view_clear_adjustment (self, GTK_ORIENTATION_HORIZONTAL);
   gtk_list_view_clear_adjustment (self, GTK_ORIENTATION_VERTICAL);
@@ -1082,7 +546,6 @@ gtk_list_view_finalize (GObject *object)
 {
   GtkListView *self = GTK_LIST_VIEW (object);
 
-  gtk_rb_tree_unref (self->rows);
   g_clear_object (&self->item_manager);
 
   G_OBJECT_CLASS (gtk_list_view_parent_class)->finalize (object);
@@ -1302,13 +765,7 @@ gtk_list_view_class_init (GtkListViewClass *klass)
 static void
 gtk_list_view_init (GtkListView *self)
 {
-  self->item_manager = gtk_list_item_manager_new (GTK_WIDGET (self));
-
-  self->rows = gtk_rb_tree_new (ListRow,
-                                ListRowAugment,
-                                list_row_augment,
-                                list_row_clear,
-                                NULL);
+  self->item_manager = gtk_list_item_manager_new (GTK_WIDGET (self), ListRow, ListRowAugment, 
list_row_augment);
 
   self->adjustment[GTK_ORIENTATION_HORIZONTAL] = gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
   self->adjustment[GTK_ORIENTATION_VERTICAL] = gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
@@ -1368,7 +825,7 @@ gtk_list_view_set_model (GtkListView *self,
   if (self->model == model)
     return;
 
-  gtk_list_view_clear_model (self);
+  g_clear_object (&self->model);
 
   if (model)
     {
@@ -1383,19 +840,7 @@ gtk_list_view_set_model (GtkListView *self,
 
       gtk_list_item_manager_set_model (self->item_manager, selection_model);
 
-      g_signal_connect (model,
-                        "items-changed",
-                        G_CALLBACK (gtk_list_view_model_items_changed_cb),
-                        self);
-      g_signal_connect (selection_model,
-                        "selection-changed",
-                        G_CALLBACK (gtk_list_view_model_selection_changed_cb),
-                        self);
-
       g_object_unref (selection_model);
-
-      gtk_list_view_add_rows (self, 0, g_list_model_get_n_items (model));
-      gtk_list_view_set_anchor (self, 0, 0, NULL, (guint) -1);
     }
   else
     {
@@ -1414,22 +859,13 @@ gtk_list_view_set_functions (GtkListView          *self,
                              GDestroyNotify        user_destroy)
 {
   GtkListItemFactory *factory;
-  guint n_items, anchor;
-  double anchor_align;
 
   g_return_if_fail (GTK_IS_LIST_VIEW (self));
   g_return_if_fail (setup_func || bind_func);
   g_return_if_fail (user_data != NULL || user_destroy == NULL);
 
-  n_items = self->model ? g_list_model_get_n_items (self->model) : 0;
-  anchor = gtk_list_view_get_anchor (self, &anchor_align);
-  gtk_list_view_remove_rows (self, NULL, 0, n_items);
-
   factory = gtk_list_item_factory_new (setup_func, bind_func, user_data, user_destroy);
   gtk_list_item_manager_set_factory (self->item_manager, factory);
   g_object_unref (factory);
-
-  gtk_list_view_add_rows (self, 0, n_items);
-  gtk_list_view_set_anchor (self, anchor, anchor_align, NULL, (guint) -1);
 }
 


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