[gtk+] GailTreeView: optimize a bit



commit 2deeab4c4f929ebd32d5f6141ea1cec572a76728
Author: Matthias Clasen <mclasen redhat com>
Date:   Wed Jun 22 22:27:10 2011 -0400

    GailTreeView: optimize a bit
    
    Avoid many unnecessary list iterations by using a hash table
    to store cell infos, and caching row and column counts. Based
    on patches by William Jon McCann, bug 554171.
    
    tree-performance results:
    
    before:  (MINPERF:large tree test with a11y: 9.18531sec)
    after:   (MINPERF:large tree test with a11y: 0.923463sec)
    
    for comparison, without accessibility:
    (MINPERF:large tree test: 0.016179sec)

 gtk/a11y/gailtreeview.c |  660 +++++++++++++++++++----------------------------
 1 files changed, 270 insertions(+), 390 deletions(-)
---
diff --git a/gtk/a11y/gailtreeview.c b/gtk/a11y/gailtreeview.c
index 338e646..4ecf590 100644
--- a/gtk/a11y/gailtreeview.c
+++ b/gtk/a11y/gailtreeview.c
@@ -82,7 +82,6 @@ static AtkObject*       gail_tree_view_table_ref_at     (AtkTable
                                                          gint                   column);
 static gint             gail_tree_view_get_n_rows       (AtkTable               *table);
 static gint             gail_tree_view_get_n_columns    (AtkTable               *table);
-static gint             get_n_actual_columns            (GtkTreeView            *tree_view);
 static gboolean         gail_tree_view_is_row_selected  (AtkTable               *table,
                                                          gint                   row);
 static gboolean         gail_tree_view_is_selected      (AtkTable               *table,
@@ -245,8 +244,6 @@ static GtkTreeIter*     return_iter_nth_row             (GtkTreeView
 static void             free_row_info                   (GArray                 *array,
                                                          gint                   array_idx,
                                                          gboolean               shift);
-static void             clean_cell_info                 (GailTreeView           *tree_view,
-                                                         GList                  *list); 
 static void             clean_rows                      (GailTreeView           *tree_view);
 static void             clean_cols                      (GailTreeView           *tree_view,
                                                          GtkTreeViewColumn      *tv_col);
@@ -326,7 +323,6 @@ static gboolean         get_next_node_with_child        (GtkTreeModel
 static gboolean         get_tree_path_from_row_index    (GtkTreeModel           *model,
                                                          gint                   row_index,
                                                          GtkTreePath            **tree_path);
-static gint             get_row_count                   (GtkTreeModel           *model);
 static gboolean         get_path_column_from_index      (GtkTreeView            *tree_view,
                                                          gint                   index,
                                                          GtkTreePath            **path,
@@ -335,7 +331,6 @@ static void             set_cell_expandable             (GailCell
 
 static GailTreeViewCellInfo* find_cell_info             (GailTreeView           *view,
                                                          GailCell               *cell,
-                                                          GList**                list,
 							 gboolean                live_only);
 static AtkObject *       get_header_from_column         (GtkTreeViewColumn      *tv_col);
 static gboolean          idle_garbage_collect_cell_data (gpointer data);
@@ -459,7 +454,6 @@ gail_tree_view_real_initialize (AtkObject *obj,
   view->summary = NULL;
   view->row_data = NULL;
   view->col_data = NULL;
-  view->cell_data = NULL;
   view->focus_cell = NULL;
   view->old_hadj = NULL;
   view->old_vadj = NULL;
@@ -468,6 +462,8 @@ gail_tree_view_real_initialize (AtkObject *obj,
 
   view->n_children_deleted = 0;
 
+  view->cell_info_by_index = g_hash_table_new (g_int_hash, g_int_equal);
+
   widget = GTK_WIDGET (data);
   g_signal_connect_after (widget,
                           "row-collapsed",
@@ -502,9 +498,12 @@ gail_tree_view_real_initialize (AtkObject *obj,
     (GCallback) focus_out, NULL, NULL, 0);
 
   view->tree_model = tree_model;
+  view->n_rows = 0;
+  view->n_cols = 0;
   if (tree_model)
     {
       g_object_add_weak_pointer (G_OBJECT (view->tree_model), (gpointer *)&view->tree_model);
+      count_rows (tree_model, NULL, NULL, &view->n_rows, 0, G_MAXINT);
       connect_model_signals (tree_view, view);
 
       if (gtk_tree_model_get_flags (tree_model) & GTK_TREE_MODEL_LIST_ONLY)
@@ -537,6 +536,7 @@ gail_tree_view_real_initialize (AtkObject *obj,
 
   for (tmp_list = tv_cols; tmp_list; tmp_list = tmp_list->next)
     {
+      view->n_cols++;
       g_signal_connect_data (tmp_list->data, "notify::visible",
        (GCallback)column_visibility_changed, 
         tree_view, NULL, FALSE);
@@ -574,12 +574,14 @@ gail_tree_view_real_notify_gtk (GObject             *obj,
         disconnect_model_signals (gailview);
       clear_cached_data (gailview);
       gailview->tree_model = tree_model;
+      gailview->n_rows = 0;
       /*
        * if there is no model the GtkTreeView is probably being destroyed
        */
       if (tree_model)
         {
           g_object_add_weak_pointer (G_OBJECT (gailview->tree_model), (gpointer *)&gailview->tree_model);
+          count_rows (tree_model, NULL, NULL, &gailview->n_rows, 0, G_MAXINT);
           connect_model_signals (tree_view, gailview);
 
           if (gtk_tree_model_get_flags (tree_model) & GTK_TREE_MODEL_LIST_ONLY)
@@ -650,6 +652,9 @@ gail_tree_view_finalize (GObject	    *object)
   if (view->tree_model)
     disconnect_model_signals (view);
 
+  if (view->cell_info_by_index)
+    g_hash_table_destroy (view->cell_info_by_index);
+
   if (view->col_data)
     {
       GArray *array = view->col_data;
@@ -768,31 +773,14 @@ static gint
 gail_tree_view_get_n_children (AtkObject *obj)
 {
   GtkWidget *widget;
-  GtkTreeView *tree_view;
-  GtkTreeModel *tree_model;
-  gint n_rows, n_cols;
 
   gail_return_val_if_fail (GAIL_IS_TREE_VIEW (obj), 0);
 
   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (obj));
   if (widget == NULL)
-    /*
-     * State is defunct
-     */
     return 0;
 
-  tree_view = GTK_TREE_VIEW (widget);
-  tree_model = gtk_tree_view_get_model (tree_view);
-
-  /*
-   * We get the total number of rows including those which are collapsed
-   */
-  n_rows = get_row_count (tree_model);
-  /*
-   * We get the total number of columns including those which are not visible
-   */
-  n_cols = get_n_actual_columns (tree_view);
-  return (n_rows * n_cols);
+  return (GAIL_TREE_VIEW (obj)->n_rows + 1) * GAIL_TREE_VIEW (obj)->n_cols;
 }
 
 static AtkObject*
@@ -835,8 +823,9 @@ gail_tree_view_ref_child (AtkObject *obj,
   if (i >= gail_tree_view_get_n_children (obj))
     return NULL;
 
+  gailview = GAIL_TREE_VIEW (obj);
   tree_view = GTK_TREE_VIEW (widget);
-  if (i < get_n_actual_columns (tree_view))
+  if (i < gailview->n_cols)
     {
       tv_col = gtk_tree_view_get_column (tree_view, i);
       child = get_header_from_column (tv_col);
@@ -845,7 +834,6 @@ gail_tree_view_ref_child (AtkObject *obj,
       return child;
     }
 
-  gailview = GAIL_TREE_VIEW (obj);
   /*
    * Check whether the child is cached
    */
@@ -1034,10 +1022,8 @@ gail_tree_view_ref_child (AtkObject *obj,
       else
         {
           gint parent_index;
-          gint n_columns;
 
-          n_columns = get_n_actual_columns (tree_view);
-          parent_index = get_index (tree_view, path, i % n_columns);
+          parent_index = get_index (tree_view, path, i % gailview->n_cols);
           parent_node = atk_object_ref_accessible_child (obj, parent_index);
         }
       accessible_array[0] = parent_node;
@@ -1208,7 +1194,7 @@ gail_tree_view_get_column_at_index (AtkTable *table,
     return -1;
 
   tree_view = GTK_TREE_VIEW (widget);
-  n_columns = get_n_actual_columns (tree_view);
+  n_columns = GAIL_TREE_VIEW (table)->n_cols;
 
   if (n_columns == 0)
     return 0;
@@ -1274,11 +1260,10 @@ gail_tree_view_get_n_rows (AtkTable *table)
   if (!tree_model)
     n_rows = 0;
   else if (gtk_tree_model_get_flags (tree_model) & GTK_TREE_MODEL_LIST_ONLY)
-   /* 
-    * If working with a LIST store, then this is a faster way
-    * to get the number of rows.
+   /*
+    * If working with a LIST store, then no rows are collapsed
     */
-    n_rows = gtk_tree_model_iter_n_children (tree_model, NULL);
+    n_rows = GAIL_TREE_VIEW (table)->n_rows;
   else
     {
       GtkTreePath *root_tree;
@@ -1293,23 +1278,7 @@ gail_tree_view_get_n_rows (AtkTable *table)
   return n_rows;
 }
 
-/*
- * The function get_n_actual_columns returns the number of columns in the 
- * GtkTreeView. i.e. it include both visible and non-visible columns.
- */
-static gint 
-get_n_actual_columns (GtkTreeView *tree_view)
-{
-  GList *columns;
-  gint n_cols;
-
-  columns = gtk_tree_view_get_columns (tree_view);
-  n_cols = g_list_length (columns);
-  g_list_free (columns);
-  return n_cols;
-}
-
-static gint 
+static gint
 gail_tree_view_get_n_columns (AtkTable *table)
 {
   GtkWidget *widget;
@@ -2094,7 +2063,7 @@ gail_tree_view_get_cell_area (GailCellParent *parent,
     {
       top_cell = cell;
     }
-  cell_info = find_cell_info (GAIL_TREE_VIEW (parent), top_cell, NULL, TRUE);
+  cell_info = find_cell_info (GAIL_TREE_VIEW (parent), top_cell, TRUE);
   gail_return_if_fail (cell_info);
   gail_return_if_fail (cell_info->cell_col_ref);
   gail_return_if_fail (cell_info->cell_row_ref);
@@ -2178,7 +2147,7 @@ gail_tree_view_grab_cell_focus  (GailCellParent *parent,
 
   tree_view = GTK_TREE_VIEW (widget);
 
-  cell_info = find_cell_info (GAIL_TREE_VIEW (parent), cell, NULL, TRUE);
+  cell_info = find_cell_info (GAIL_TREE_VIEW (parent), cell, TRUE);
   gail_return_val_if_fail (cell_info, FALSE);
   gail_return_val_if_fail (cell_info->cell_col_ref, FALSE);
   gail_return_val_if_fail (cell_info->cell_row_ref, FALSE);
@@ -2384,14 +2353,12 @@ gail_tree_view_changed_gtk (GtkTreeSelection *selection,
   GailTreeView *gailview;
   GtkTreeView *tree_view;
   GtkWidget *widget;
-  GList *cell_list;
-  GList *l;
   GailTreeViewCellInfo *info;
   GtkTreeSelection *tree_selection;
   GtkTreePath *path;
+  GHashTableIter iter;
 
   gailview = GAIL_TREE_VIEW (data);
-  cell_list = gailview->cell_data;
   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (gailview));
   if (widget == NULL)
     /*
@@ -2404,10 +2371,10 @@ gail_tree_view_changed_gtk (GtkTreeSelection *selection,
 
   clean_rows (gailview);
 
-  for (l = cell_list; l; l = l->next)
+  /* FIXME: clean rows iterates through all cells too */
+  g_hash_table_iter_init (&iter, gailview->cell_info_by_index);
+  while (g_hash_table_iter_next (&iter, NULL, (gpointer *)&info))
     {
-      info = (GailTreeViewCellInfo *) (l->data);
-
       if (info->in_use)
       {
 	  gail_cell_remove_state (info->cell, ATK_STATE_SELECTED, TRUE); 
@@ -2440,6 +2407,7 @@ columns_changed (GtkTreeView *tree_view)
   * gailview->col_data
   */
   tv_cols = gtk_tree_view_get_columns (tree_view);
+  gailview->n_cols = g_list_length (tv_cols);
 
   /* check for adds or moves */
   for (tmp_list = tv_cols; tmp_list; tmp_list = tmp_list->next)
@@ -2480,7 +2448,7 @@ columns_changed (GtkTreeView *tree_view)
       */
       if (!column_found)
         {
-          gint n_cols, n_rows, row;
+          gint row;
 
           if (!stale_set)
             {
@@ -2493,15 +2461,13 @@ columns_changed (GtkTreeView *tree_view)
           g_signal_emit_by_name (atk_obj, "column_inserted", column_count, 1);
 
           /* Generate children-changed signals */
-          n_rows = get_row_count (gtk_tree_view_get_model (tree_view));
-          n_cols = get_n_actual_columns (tree_view);
-          for (row = 0; row < n_rows; row++)
+          for (row = 0; row < gailview->n_rows; row++)
             {
              /*
               * Pass NULL as the child object, i.e. 4th argument.
               */
               g_signal_emit_by_name (atk_obj, "children_changed::add",
-                                    ((row * n_cols) + column_count), NULL, NULL);
+                                    ((row * gailview->n_cols) + column_count), NULL, NULL);
             }
         }
 
@@ -2530,7 +2496,7 @@ columns_changed (GtkTreeView *tree_view)
         */
       if (!column_found)
         {
-          gint n_rows, n_cols, row;
+          gint row;
 
           clean_cols (gailview,
                       (GtkTreeViewColumn *)g_array_index (gailview->col_data,
@@ -2547,15 +2513,13 @@ columns_changed (GtkTreeView *tree_view)
           g_signal_emit_by_name (atk_obj, "column_deleted", i, 1);
 
           /* Generate children-changed signals */
-          n_rows = get_row_count (gtk_tree_view_get_model (tree_view));
-          n_cols = get_n_actual_columns (tree_view);
-          for (row = 0; row < n_rows; row++)
+          for (row = 0; row < gailview->n_rows; row++)
             {
              /*
               * Pass NULL as the child object, 4th argument.
               */
               g_signal_emit_by_name (atk_obj, "children_changed::remove",
-                                    ((row * n_cols) + column_count), NULL, NULL);
+                                    ((row * gailview->n_cols) + column_count), NULL, NULL);
             }
         }
     }
@@ -2696,17 +2660,17 @@ model_row_changed (GtkTreeModel *tree_model,
   GtkTreeView *tree_view = GTK_TREE_VIEW(user_data);
   GailTreeView *gailview;
   GtkTreePath *cell_path;
-  GList *l;
   GailTreeViewCellInfo *cell_info;
- 
+  GHashTableIter hash_iter;
+
   gailview = GAIL_TREE_VIEW (gtk_widget_get_accessible (GTK_WIDGET (tree_view)));
 
   /* Loop through our cached cells */
   /* Must loop through them all */
-  for (l = gailview->cell_data; l; l = l->next)
+  g_hash_table_iter_init (&hash_iter, gailview->cell_info_by_index);
+  while (g_hash_table_iter_next (&hash_iter, NULL, (gpointer *)&cell_info))
     {
-      cell_info = (GailTreeViewCellInfo *) l->data;
-      if (cell_info->in_use) 
+      if (cell_info->in_use)
       {
 	  cell_path = gtk_tree_row_reference_get_path (cell_info->cell_row_ref);
 
@@ -2741,19 +2705,19 @@ column_visibility_changed (GObject    *object,
        */ 
       GtkTreeView *tree_view = (GtkTreeView *)user_data;
       GailTreeView *gailview;
-      GList *l;
       GailTreeViewCellInfo *cell_info;
       GtkTreeViewColumn *this_col = GTK_TREE_VIEW_COLUMN (object);
       GtkTreeViewColumn *tv_col;
+      GHashTableIter iter;
 
       gailview = GAIL_TREE_VIEW (gtk_widget_get_accessible (GTK_WIDGET (tree_view))
 );
       g_signal_emit_by_name (gailview, "model_changed");
 
-      for (l = gailview->cell_data; l; l = l->next)
+      g_hash_table_iter_init (&iter, gailview->cell_info_by_index);
+      while (g_hash_table_iter_next (&iter, NULL, (gpointer *)&cell_info))
         {
-          cell_info = (GailTreeViewCellInfo *) l->data;
-	  if (cell_info->in_use) 
+	  if (cell_info->in_use)
 	  {
 	      tv_col = cell_info->cell_col_ref;
 	      if (tv_col == this_col)
@@ -2793,6 +2757,8 @@ model_row_inserted (GtkTreeModel *tree_model,
   GailTreeView *gailview = GAIL_TREE_VIEW (atk_obj);
   gint row, n_inserted, child_row;
 
+  gailview->n_rows++;
+
   if (gailview->idle_expand_id)
     {
       g_source_remove (gailview->idle_expand_id);
@@ -2882,12 +2848,15 @@ model_row_deleted (GtkTreeModel *tree_model,
   GtkTreePath *path_copy;
   AtkObject *atk_obj;
   GailTreeView *gailview;
-  gint row, col, n_cols;
+  gint row, col;
 
   tree_view = (GtkTreeView *)user_data;
   atk_obj = gtk_widget_get_accessible (GTK_WIDGET (tree_view));
   gailview = GAIL_TREE_VIEW (atk_obj);
 
+  gailview->n_rows--;
+  g_assert (gailview->n_rows >= 0);
+
   if (gailview->idle_expand_id)
     {
       g_source_remove (gailview->idle_expand_id);
@@ -2923,14 +2892,13 @@ model_row_deleted (GtkTreeModel *tree_model,
   gailview->n_children_deleted = 0;
 
   /* Generate children-changed signals */
-  n_cols = get_n_actual_columns (tree_view);
-  for (col = 0; col < n_cols; col++)
+  for (col = 0; col < gailview->n_cols; col++)
   {
     /*
      * Pass NULL as the child object, 4th argument.
      */
     g_signal_emit_by_name (atk_obj, "children_changed::remove",
-                           ((row * n_cols) + col), NULL, NULL);
+                           ((row * gailview->n_cols) + col), NULL, NULL);
   }
 }
 
@@ -3104,7 +3072,7 @@ update_cell_value (GailRendererCell *renderer_cell,
   prop_list = gail_renderer_cell_class->property_list;
 
   cell = GAIL_CELL (renderer_cell);
-  cell_info = find_cell_info (gailview, cell, NULL, TRUE);
+  cell_info = find_cell_info (gailview, cell, TRUE);
   gail_return_val_if_fail (cell_info, FALSE);
   gail_return_val_if_fail (cell_info->cell_col_ref, FALSE);
   gail_return_val_if_fail (cell_info->cell_row_ref, FALSE);
@@ -3480,15 +3448,12 @@ iterate_thru_children(GtkTreeView  *tree_view,
 
 static void
 clean_cell_info (GailTreeView *gailview,
-                 GList        *list) 
+                 GailTreeViewCellInfo *cell_info)
 {
-  GailTreeViewCellInfo *cell_info;
   GObject *obj;
 
   g_assert (GAIL_IS_TREE_VIEW (gailview));
 
-  cell_info = list->data;
-
   if (cell_info->in_use) {
       obj = G_OBJECT (cell_info->cell);
       
@@ -3508,6 +3473,8 @@ static void
 clean_rows (GailTreeView *gailview)
 {
   GArray *array;
+  GailTreeViewCellInfo *cell_info;
+  GHashTableIter iter;
 
   /* Clean GailTreeViewRowInfo data */
 
@@ -3536,70 +3503,48 @@ clean_rows (GailTreeView *gailview)
     }
 
   /* Clean GailTreeViewCellInfo data */
-
-  if (gailview->cell_data != NULL)
+  g_hash_table_iter_init (&iter, gailview->cell_info_by_index);
+  while (g_hash_table_iter_next (&iter, NULL, (gpointer *)&cell_info))
     {
-      GailTreeViewCellInfo *cell_info;
       GtkTreePath *row_path;
-      GList *cur_list;
-      GList *temp_list;
 
-      temp_list = gailview->cell_data;
-
-      /* Must loop through them all */
-      while (temp_list != NULL)
+      row_path = gtk_tree_row_reference_get_path (cell_info->cell_row_ref);
+      /*
+       * If the cell has become invalid because the row has been removed,
+       * then set the cell's state to ATK_STATE_DEFUNCT and schedule
+       * its removal.  If row_path is NULL then the row has
+       * been removed.
+       */
+      if (row_path == NULL)
         {
-          cur_list = temp_list;
-          cell_info = temp_list->data;
-          temp_list = temp_list->next;
-          row_path = gtk_tree_row_reference_get_path (cell_info->cell_row_ref);
-
-         /*
-          * If the cell has become invalid because the row has been removed, 
-          * then set the cell's state to ATK_STATE_DEFUNCT and remove the cell
-          * from gailview->cell_data.  If row_path is NULL then the row has
-          * been removed.
-          */
-          if (row_path == NULL)
-            {
-              clean_cell_info (gailview, cur_list);
-            }
-          else
-            {
-              gtk_tree_path_free (row_path);
-            }
+          clean_cell_info (gailview, cell_info);
+        }
+      else
+        {
+          gtk_tree_path_free (row_path);
         }
     }
 }
 
-static void 
+static void
 clean_cols (GailTreeView      *gailview,
             GtkTreeViewColumn *tv_col)
 {
-  /* Clean GailTreeViewCellInfo data */
+  GailTreeViewCellInfo *cell_info;
+  GHashTableIter iter;
 
-  if (gailview->cell_data != NULL)
+  /* Clean GailTreeViewCellInfo data */
+  g_hash_table_iter_init (&iter, gailview->cell_info_by_index);
+  while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &cell_info))
     {
-      GailTreeViewCellInfo *cell_info;
-      GList *cur_list, *temp_list;
-
-      temp_list = gailview->cell_data;
-
-      while (temp_list != NULL)
+      /*
+       * If the cell has become invalid because the column tv_col
+       * has been removed, then set the cell's state to ATK_STATE_DEFUNCT
+       * and remove the cell from gailview->cell_data.
+       */
+      if (cell_info->cell_col_ref == tv_col)
         {
-          cur_list = temp_list;
-          cell_info = temp_list->data;
-          temp_list = temp_list->next;
-
-         /*
-          * If the cell has become invalid because the column tv_col
-          * has been removed, then set the cell's state to ATK_STATE_DEFUNCT
-          * and remove the cell from gailview->cell_data. 
-          */
-          if (cell_info->cell_col_ref == tv_col)
-            {
-              clean_cell_info (gailview, cur_list);
-            }
+          clean_cell_info (gailview, cell_info);
         }
     }
 }
@@ -3631,12 +3576,11 @@ static gboolean
 garbage_collect_cell_data (gpointer data)
 {
       GailTreeView *tree_view;
-      GList *temp_list, *list;
       GailTreeViewCellInfo *cell_info;
+      GHashTableIter iter;
 
       g_assert (GAIL_IS_TREE_VIEW (data));
       tree_view = (GailTreeView *)data;
-      list = g_list_copy (tree_view->cell_data);
 
       tree_view->garbage_collection_pending = FALSE;
       if (tree_view->idle_garbage_collect_id != 0) 
@@ -3646,22 +3590,18 @@ garbage_collect_cell_data (gpointer data)
       }
 
       /* Must loop through them all */
-      temp_list = list;
-      while (temp_list != NULL)
+      g_hash_table_iter_init (&iter, tree_view->cell_info_by_index);
+      while (g_hash_table_iter_next (&iter, NULL, (gpointer *)&cell_info))
       {
-          cell_info = temp_list->data;
 	  if (!cell_info->in_use)
 	  {
 	      /* g_object_unref (cell_info->cell); */
-	      tree_view->cell_data = g_list_remove (tree_view->cell_data, 
-						    cell_info);
 	      if (cell_info->cell_row_ref)
 		  gtk_tree_row_reference_free (cell_info->cell_row_ref);
 	      g_free (cell_info);
+              g_hash_table_iter_remove (&iter);
 	  }
-          temp_list = temp_list->next;
       }
-      g_list_free (list);
 
       return tree_view->garbage_collection_pending;
 }
@@ -3684,63 +3624,58 @@ traverse_cells (GailTreeView *tree_view,
                 gboolean     set_stale,
                 gboolean     inc_row)
 {
-  if (tree_view->cell_data != NULL)
-    {
-      GailTreeViewCellInfo *cell_info;
-      GtkTreeView *gtk_tree_view;
-      GList *temp_list;
-      GtkWidget *widget;
+  GailTreeViewCellInfo *cell_info;
+  GtkTreeView *gtk_tree_view;
+  GtkWidget *widget;
+  GHashTableIter iter;
 
-      g_assert (GTK_IS_ACCESSIBLE (tree_view));
+  g_assert (GTK_IS_ACCESSIBLE (tree_view));
 
-      widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (tree_view));
-      if (!widget)
-        /* Widget is being deleted */
-        return;
+  widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (tree_view));
+  if (!widget)
+    /* Widget is being deleted */
+    return;
 
-      gtk_tree_view = GTK_TREE_VIEW (widget);
-      temp_list = tree_view->cell_data;
+  gtk_tree_view = GTK_TREE_VIEW (widget);
 
-      /* Must loop through them all */
-      while (temp_list != NULL)
-        {
-          GtkTreePath *row_path;
-          gboolean act_on_cell;
+  /* Must loop through them all */
+  g_hash_table_iter_init (&iter, tree_view->cell_info_by_index);
+  while (g_hash_table_iter_next (&iter, NULL, (gpointer *)&cell_info))
+    {
+      GtkTreePath *row_path;
+      gboolean act_on_cell;
 
-          cell_info = temp_list->data;
-          temp_list = temp_list->next;
+      if (cell_info->in_use)
+        {
+          row_path = gtk_tree_row_reference_get_path (cell_info->cell_row_ref);
+          g_return_if_fail (row_path != NULL);
+          if (tree_path == NULL)
+            act_on_cell = TRUE;
+          else
+            {
+              gint comparison;
 
-	  if (cell_info->in_use)
-	  {
-	      row_path = gtk_tree_row_reference_get_path (cell_info->cell_row_ref);
-	      g_return_if_fail (row_path != NULL);
-	      if (tree_path == NULL)
-		  act_on_cell = TRUE;
-	      else 
-	      {
-		  gint comparison;
-		  
-		  comparison =  gtk_tree_path_compare (row_path, tree_path);
-		  if ((comparison > 0) ||
-		      (comparison == 0 && inc_row))
-		      act_on_cell = TRUE;
-		  else
-		      act_on_cell = FALSE;
-	      }
-	      if (!cell_info->in_use) g_warning ("warning: cell info destroyed during traversal");
-	      if (act_on_cell && cell_info->in_use)
-	      {
-		  if (set_stale)
-		      gail_cell_add_state (cell_info->cell, ATK_STATE_STALE, TRUE);
-		  set_cell_visibility (gtk_tree_view,
-				       cell_info->cell,
-				       cell_info->cell_col_ref,
-				       row_path, TRUE);
-	      }
-	      gtk_tree_path_free (row_path);
-	  }
-	}
+              comparison =  gtk_tree_path_compare (row_path, tree_path);
+              if ((comparison > 0) ||
+                  (comparison == 0 && inc_row))
+                act_on_cell = TRUE;
+              else
+                act_on_cell = FALSE;
+            }
+          if (!cell_info->in_use) g_warning ("warning: cell info destroyed during traversal");
+          if (act_on_cell && cell_info->in_use)
+            {
+              if (set_stale)
+                gail_cell_add_state (cell_info->cell, ATK_STATE_STALE, TRUE);
+              set_cell_visibility (gtk_tree_view,
+                                   cell_info->cell,
+                                   cell_info->cell_col_ref,
+                                   row_path, TRUE);
+            }
+          gtk_tree_path_free (row_path);
+        }
     }
+
   g_signal_emit_by_name (tree_view, "visible-data-changed");
 }
 
@@ -3783,95 +3718,85 @@ set_expand_state (GtkTreeView  *tree_view,
                   GtkTreePath  *tree_path,
                   gboolean     set_on_ancestor)
 {
-  if (gailview->cell_data != NULL)
+  GtkTreeViewColumn *expander_tv;
+  GailTreeViewCellInfo *cell_info;
+  GtkTreePath *cell_path;
+  GtkTreeIter iter;
+  gboolean found;
+  GHashTableIter hash_iter;
+
+  g_hash_table_iter_init (&hash_iter, gailview->cell_info_by_index);
+  while (g_hash_table_iter_next (&hash_iter, NULL, (gpointer *) &cell_info))
     {
-      GtkTreeViewColumn *expander_tv;
-      GailTreeViewCellInfo *cell_info;
-      GList *temp_list;
-      GtkTreePath *cell_path;
-      GtkTreeIter iter;
-      gboolean found;
+      if (cell_info->in_use)
+        {
+          cell_path = gtk_tree_row_reference_get_path (cell_info->cell_row_ref);
+          found = FALSE;
 
-      temp_list = gailview->cell_data;
+          if (cell_path != NULL)
+            {
+              GailCell *cell  = GAIL_CELL (cell_info->cell);
 
-      while (temp_list != NULL)
-        {
-          cell_info = temp_list->data;
-          temp_list = temp_list->next;
-	  if (cell_info->in_use)
-	  {
-	      cell_path = gtk_tree_row_reference_get_path (cell_info->cell_row_ref);
-	      found = FALSE;
-	      
-	      if (cell_path != NULL)
-	      {
-		  GailCell *cell  = GAIL_CELL (cell_info->cell);
-		  
-		  expander_tv = gtk_tree_view_get_expander_column (tree_view);
-		  
-		  /*
-		   * Only set state for the cell that is in the column with the
-		   * expander toggle
-		   */
-		  if (expander_tv == cell_info->cell_col_ref)
-		  {
-		      if (tree_path && gtk_tree_path_compare (cell_path, tree_path) == 0)
-			  found = TRUE;
-		      else if (set_on_ancestor &&
-			       gtk_tree_path_get_depth (cell_path) <
-			       gtk_tree_path_get_depth (tree_path) && 
-			       gtk_tree_path_is_ancestor (cell_path, tree_path) == 1)
-			  /* Only set if set_on_ancestor was passed in as TRUE */
-			  found = TRUE;
-		  }
-		  
-		  /*
-		   * Set ATK_STATE_EXPANDABLE and ATK_STATE_EXPANDED
-		   * for ancestors and found cells.
-		   */
-		  if (found)
-		  {
-		      /*
-		       * Must check against cell_path since cell_path
-		       * can be equal to or an ancestor of tree_path.
-		       */
-		      gtk_tree_model_get_iter (tree_model, &iter, cell_path);
-		      
-		      /* Set or unset ATK_STATE_EXPANDABLE as appropriate */
-		      if (gtk_tree_model_iter_has_child (tree_model, &iter)) 
-		      {
-			  set_cell_expandable (cell);
-			  
-			  if (gtk_tree_view_row_expanded (tree_view, cell_path))
-			      gail_cell_add_state (cell, ATK_STATE_EXPANDED, TRUE);
-			  else
-			      gail_cell_remove_state (cell, 
-						      ATK_STATE_EXPANDED, TRUE);
-		      }
-		      else
-		      {
-			  gail_cell_remove_state (cell, 
-						  ATK_STATE_EXPANDED, TRUE);
-			  if (gail_cell_remove_state (cell,
-						      ATK_STATE_EXPANDABLE, TRUE))
-			      /* The state may have been propagated to the container cell */
-			      if (!GAIL_IS_CONTAINER_CELL (cell))
-				  gail_cell_remove_action_by_name (cell,
-								   "expand or contract");
-		      }
-		      
-		      /*
-		       * We assume that each cell in the cache once and
-		       * a container cell is before its child cells so we are 
-		       * finished if set_on_ancestor is not set to TRUE.
-		       */
-		      if (!set_on_ancestor)
-			  break;
-		  }
-	      }
-	      gtk_tree_path_free (cell_path);
-	  }
-	}
+              expander_tv = gtk_tree_view_get_expander_column (tree_view);
+
+              /* Only set state for the cell that is in the column with the
+               * expander toggle
+               */
+              if (expander_tv == cell_info->cell_col_ref)
+                {
+                  if (tree_path && gtk_tree_path_compare (cell_path, tree_path) == 0)
+                    found = TRUE;
+                  else if (set_on_ancestor &&
+                          gtk_tree_path_get_depth (cell_path) <
+                          gtk_tree_path_get_depth (tree_path) &&
+                          gtk_tree_path_is_ancestor (cell_path, tree_path) == 1)
+                    /* Only set if set_on_ancestor was passed in as TRUE */
+                    found = TRUE;
+                }
+
+              /* Set ATK_STATE_EXPANDABLE and ATK_STATE_EXPANDED
+               * for ancestors and found cells.
+               */
+              if (found)
+                {
+                  /* Must check against cell_path since cell_path
+                   * can be equal to or an ancestor of tree_path.
+                   */
+                  gtk_tree_model_get_iter (tree_model, &iter, cell_path);
+
+                  /* Set or unset ATK_STATE_EXPANDABLE as appropriate */
+                  if (gtk_tree_model_iter_has_child (tree_model, &iter))
+                    {
+                      set_cell_expandable (cell);
+
+                      if (gtk_tree_view_row_expanded (tree_view, cell_path))
+                        gail_cell_add_state (cell, ATK_STATE_EXPANDED, TRUE);
+                      else
+                        gail_cell_remove_state (cell,
+                                                ATK_STATE_EXPANDED, TRUE);
+                    }
+                  else
+                    {
+                      gail_cell_remove_state (cell,
+                                              ATK_STATE_EXPANDED, TRUE);
+                      if (gail_cell_remove_state (cell,
+                                                  ATK_STATE_EXPANDABLE, TRUE))
+                      /* The state may have been propagated to the container cell */
+                      if (!GAIL_IS_CONTAINER_CELL (cell))
+                        gail_cell_remove_action_by_name (cell,
+                                                         "expand or contract");
+                    }
+
+                  /* We assume that each cell in the cache once and
+                   * a container cell is before its child cells so we are
+                   * finished if set_on_ancestor is not set to TRUE.
+                   */
+                  if (!set_on_ancestor)
+                    break;
+                }
+            }
+          gtk_tree_path_free (cell_path);
+        }
     }
 }
 
@@ -3912,7 +3837,7 @@ toggle_cell_expanded (GailCell *cell)
   if (GAIL_IS_CONTAINER_CELL (parent))
     parent = atk_object_get_parent (parent);
 
-  cell_info = find_cell_info (GAIL_TREE_VIEW (parent), cell, NULL, TRUE);
+  cell_info = find_cell_info (GAIL_TREE_VIEW (parent), cell, TRUE);
   gail_return_if_fail (cell_info);
   gail_return_if_fail (cell_info->cell_col_ref);
   gail_return_if_fail (cell_info->cell_row_ref);
@@ -3948,7 +3873,7 @@ toggle_cell_toggled (GailCell *cell)
       parent = atk_object_get_parent (parent);
     }
 
-  cell_info = find_cell_info (GAIL_TREE_VIEW (parent), cell, NULL, TRUE);
+  cell_info = find_cell_info (GAIL_TREE_VIEW (parent), cell, TRUE);
   gail_return_if_fail (cell_info);
   gail_return_if_fail (cell_info->cell_col_ref);
   gail_return_if_fail (cell_info->cell_row_ref);
@@ -3996,7 +3921,7 @@ edit_cell (GailCell *cell)
   if (GAIL_IS_CONTAINER_CELL (parent))
     parent = atk_object_get_parent (parent);
 
-  cell_info = find_cell_info (GAIL_TREE_VIEW (parent), cell, NULL, TRUE);
+  cell_info = find_cell_info (GAIL_TREE_VIEW (parent), cell, TRUE);
   gail_return_if_fail (cell_info);
   gail_return_if_fail (cell_info->cell_col_ref);
   gail_return_if_fail (cell_info->cell_row_ref);
@@ -4022,7 +3947,7 @@ activate_cell (GailCell *cell)
   if (GAIL_IS_CONTAINER_CELL (parent))
     parent = atk_object_get_parent (parent);
 
-  cell_info = find_cell_info (GAIL_TREE_VIEW (parent), cell, NULL, TRUE);
+  cell_info = find_cell_info (GAIL_TREE_VIEW (parent), cell, TRUE);
   gail_return_if_fail (cell_info);
   gail_return_if_fail (cell_info->cell_col_ref);
   gail_return_if_fail (cell_info->cell_row_ref);
@@ -4053,24 +3978,6 @@ cell_destroyed (gpointer data)
   }
 }
 
-#if 0
-static void
-cell_info_remove (GailTreeView *tree_view, 
-                  GailCell     *cell)
-{
-  GailTreeViewCellInfo *info;
-  GList *temp_list;
-
-  info = find_cell_info (tree_view, cell, &temp_list, FALSE);
-  if (info)
-    {
-      info->in_use = FALSE;
-      return;
-    }
-  g_warning ("No cell removed in cell_info_remove\n");
-}
-#endif
-
 static void
 cell_info_get_index (GtkTreeView            *tree_view, 
                      GailTreeViewCellInfo   *info,
@@ -4088,8 +3995,8 @@ cell_info_get_index (GtkTreeView            *tree_view,
 }
 
 static void
-cell_info_new (GailTreeView      *gailview, 
-               GtkTreeModel      *tree_model, 
+cell_info_new (GailTreeView      *gailview,
+               GtkTreeModel      *tree_model,
                GtkTreePath       *path,
                GtkTreeViewColumn *tv_col,
                GailCell          *cell )
@@ -4105,8 +4012,8 @@ cell_info_new (GailTreeView      *gailview,
   cell_info->cell = cell;
   cell_info->in_use = TRUE; /* if we've created it, assume it's in use */
   cell_info->view = gailview;
-  gailview->cell_data = g_list_append (gailview->cell_data, cell_info);
-      
+  g_hash_table_insert (gailview->cell_info_by_index, &cell->index, cell_info);
+
   /* Setup weak reference notification */
 
   g_object_weak_ref (G_OBJECT (cell),
@@ -4119,35 +4026,11 @@ find_cell (GailTreeView *gailview,
            gint         index)
 {
   GailTreeViewCellInfo *info;
-  GtkTreeView *tree_view;
-  GList *cell_list;
-  GList *l;
-  gint real_index;
-  gboolean needs_cleaning = FALSE;
   GailCell *retval = NULL;
 
-  tree_view = GTK_TREE_VIEW (gtk_accessible_get_widget (GTK_ACCESSIBLE (gailview)));
-  cell_list = gailview->cell_data;
-
-  for (l = cell_list; l; l = l->next)
-    {
-      info = (GailTreeViewCellInfo *) (l->data);
-      if (info->in_use)
-      {
-	  cell_info_get_index (tree_view, info, &real_index);
-	  if (index == real_index)
-	  {
-	      retval =  info->cell;
-	      break;
-	  }
-      }
-      else
-      {
-	  needs_cleaning = TRUE;
-      }
-    }
-  if (needs_cleaning)
-     garbage_collect_cell_data (gailview);
+  info = g_hash_table_lookup (gailview->cell_info_by_index, &index);
+  if (info != NULL)
+    retval = info->cell;
 
   return retval;
 }
@@ -4158,20 +4041,23 @@ refresh_cell_index (GailCell *cell)
   GailTreeViewCellInfo *info;
   AtkObject *parent;
   GtkTreeView *tree_view;
+  GailTreeView *gailview;
   gint index;
 
   parent = atk_object_get_parent (ATK_OBJECT (cell));
   gail_return_if_fail (GAIL_IS_TREE_VIEW (parent));
+  gailview = GAIL_TREE_VIEW (parent);
 
   tree_view = GTK_TREE_VIEW (gtk_accessible_get_widget (GTK_ACCESSIBLE (parent)));
 
   /* Find this cell in the GailTreeView's cache */
 
-  info = find_cell_info (GAIL_TREE_VIEW (parent), cell, NULL, TRUE);
+  info = find_cell_info (gailview, cell, TRUE);
   gail_return_if_fail (info);
   
   cell_info_get_index (tree_view, info, &index); 
   cell->index = index;
+  g_hash_table_insert (gailview->cell_info_by_index, &index, info);
 }
 
 static void
@@ -4206,7 +4092,7 @@ connect_model_signals (GtkTreeView  *view,
 }
 
 static void
-disconnect_model_signals (GailTreeView *view) 
+disconnect_model_signals (GailTreeView *view)
 {
   GObject *obj;
   GtkWidget *widget;
@@ -4222,7 +4108,8 @@ disconnect_model_signals (GailTreeView *view)
 static void
 clear_cached_data (GailTreeView  *view)
 {
-  GList *temp_list;
+  GailTreeViewCellInfo *cell_info;
+  GHashTableIter iter;
 
   if (view->row_data)
     {
@@ -4230,7 +4117,7 @@ clear_cached_data (GailTreeView  *view)
       gint i;
 
      /*
-      * Since the third argument to free_row_info is FALSE, we don't remove 
+      * Since the third argument to free_row_info is FALSE, we don't remove
       * the element.  Therefore it is safe to loop forward.
       */
       for (i = 0; i < array->len; i++)
@@ -4241,19 +4128,15 @@ clear_cached_data (GailTreeView  *view)
       view->row_data = NULL;
     }
 
-  if (view->cell_data)
+  /* Must loop through them all */
+  g_hash_table_iter_init (&iter, view->cell_info_by_index);
+  while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &cell_info))
     {
-      /* Must loop through them all */
-      for (temp_list = view->cell_data; temp_list; temp_list = temp_list->next)
-        {
-	    clean_cell_info (view, temp_list);
-        }
+      clean_cell_info (view, cell_info);
     }
+
+  /* FIXME: seems pretty inefficient to loop again here */
   garbage_collect_cell_data (view);
-  if (view->cell_data)
-      g_list_free (view->cell_data);
-  
-  view->cell_data = NULL;
 }
 
 /*
@@ -4269,37 +4152,45 @@ get_column_number (GtkTreeView       *tree_view,
                    GtkTreeViewColumn *column,
                    gboolean          visible)
 {
-  GList *temp_list, *column_list;
   GtkTreeViewColumn *tv_column;
   gint ret_val;
+  gint i;
+  AtkObject *atk_obj;
+  GailTreeView *gailview;
+
+  atk_obj = gtk_widget_get_accessible (GTK_WIDGET (tree_view));
+  gailview = GAIL_TREE_VIEW (atk_obj);
 
-  column_list = gtk_tree_view_get_columns (tree_view);
   ret_val = 0;
-  for (temp_list = column_list; temp_list; temp_list = temp_list->next)
+  for (i = 0; i < gailview->col_data->len; i++)
     {
-      tv_column = GTK_TREE_VIEW_COLUMN (temp_list->data);
+      tv_column = g_array_index (gailview->col_data, GtkTreeViewColumn *, i);
       if (tv_column == column)
         break;
       if (!visible || gtk_tree_view_column_get_visible (tv_column))
         ret_val++;
     }
-  if (temp_list == NULL)
+  if (i == gailview->col_data->len)
     {
       ret_val = -1;
     }
-  g_list_free (column_list);
+
   return ret_val;
-} 
+}
 
 static gint
-get_index (GtkTreeView       *tree_view,
-           GtkTreePath       *path,
-           gint              actual_column)
+get_index (GtkTreeView *tree_view,
+           GtkTreePath *path,
+           gint         actual_column)
 {
+  AtkObject *atk_obj;
+  GailTreeView *gailview;
   gint depth = 0;
   gint index = 1;
   gint *indices = NULL;
 
+  atk_obj = gtk_widget_get_accessible (GTK_WIDGET (tree_view));
+  gailview = GAIL_TREE_VIEW (atk_obj);
 
   if (path)
     {
@@ -4321,7 +4212,7 @@ get_index (GtkTreeView       *tree_view,
 
   if (path)
     index += indices[depth-1];
-  index *= get_n_actual_columns (tree_view);
+  index *= gailview->n_cols;
   index +=  actual_column;
   return index;
 }
@@ -4356,6 +4247,9 @@ count_rows (GtkTreeModel *model,
             gtk_tree_path_to_string (gtk_tree_model_get_path (model, iter)));
 #endif
 
+  if (gtk_tree_model_get_flags (model) & GTK_TREE_MODEL_LIST_ONLY)
+    return;
+
   if (level >= depth)
     return;
 
@@ -4527,20 +4421,7 @@ get_tree_path_from_row_index (GtkTreeModel *model,
            *tree_path = NULL;
           return FALSE;
         }
-    }  
-}
-
-/*
- * This function returns the number of rows, including those which are collapsed
- */
-static gint
-get_row_count (GtkTreeModel *model)
-{
-  gint n_rows = 1;
-
-  count_rows (model, NULL, NULL, &n_rows, 0, G_MAXINT);
-
-  return n_rows;
+    }
 }
 
 static gboolean
@@ -4550,14 +4431,17 @@ get_path_column_from_index (GtkTreeView       *tree_view,
                             GtkTreeViewColumn **column)
 {
   GtkTreeModel *tree_model;
-  gint n_columns;
+  AtkObject *atk_obj;
+  GailTreeView *gailview;
+
+  atk_obj = gtk_widget_get_accessible (GTK_WIDGET (tree_view));
+  gailview = GAIL_TREE_VIEW (atk_obj);
 
   tree_model = gtk_tree_view_get_model (tree_view);
-  n_columns = get_n_actual_columns (tree_view);
-  if (n_columns == 0)
+  if (gailview->n_cols == 0)
     return FALSE;
   /* First row is the column headers */
-  index -= n_columns;
+  index -= gailview->n_cols;
   if (index < 0)
     return FALSE;
 
@@ -4566,7 +4450,7 @@ get_path_column_from_index (GtkTreeView       *tree_view,
       gint row_index;
       gboolean retval;
 
-      row_index = index / n_columns;
+      row_index = index / gailview->n_cols;
       retval = get_tree_path_from_row_index (tree_model, row_index, path);
       gail_return_val_if_fail (retval, FALSE);
       if (*path == NULL)
@@ -4575,7 +4459,7 @@ get_path_column_from_index (GtkTreeView       *tree_view,
 
   if (column)
     {
-      *column = gtk_tree_view_get_column (tree_view, index % n_columns);
+      *column = gtk_tree_view_get_column (tree_view, index % gailview->n_cols);
       if (*column == NULL)
         {
 	  if (path)
@@ -4603,21 +4487,17 @@ set_cell_expandable (GailCell *cell)
 static GailTreeViewCellInfo*
 find_cell_info (GailTreeView *view,
                 GailCell     *cell,
-                GList**      list,
-		gboolean     live_only)
+                gboolean      live_only)
 {
-  GList *temp_list;
   GailTreeViewCellInfo *cell_info;
+  GHashTableIter iter;
 
-  for (temp_list = view->cell_data; temp_list; temp_list = temp_list->next)
+  /* Clean GailTreeViewCellInfo data */
+  g_hash_table_iter_init (&iter, view->cell_info_by_index);
+  while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &cell_info))
     {
-      cell_info = (GailTreeViewCellInfo *) temp_list->data;
       if (cell_info->cell == cell && (!live_only || cell_info->in_use))
-        {
-          if (list)
-            *list = temp_list;
-          return cell_info;
-        }
+        return cell_info;
     }
   return NULL;
 }



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