? gail-table-patch.diff
? gail-treeview-patch.diff
? stamp-h1
? docs/reference/libgail-util/gail-libgail-util.interfaces
? docs/reference/libgail-util/gail-libgail-util.prerequisites
? gail/gailtreeview.c.debug
Index: ChangeLog
===================================================================
RCS file: /cvs/gnome/gail/ChangeLog,v
retrieving revision 1.797
diff -u -r1.797 ChangeLog
--- ChangeLog	25 Apr 2005 11:17:19 -0000	1.797
+++ ChangeLog	28 Apr 2005 11:15:46 -0000
@@ -1,3 +1,17 @@
+2005-04-25  Bill Haneman <bill.haneman@sun.com>
+
+	* configure.in: Revved to 1.8.4.
+
+	* gail/gailtreeview.c: 
+	(GailTreeViewCellInfo): Added gboolean in_use.
+	(clean_cell_info): Mark in_use=FALSE on GailTreeViewCell when 
+	cleaning; don't free the cell_info or remove the link at this
+	point, wait for garbage_collect_cell_data pass.  Queue
+	garbage collecton idle hander if not already pending.
+	(garbage_collect_cell_data): New, traverse the cellinfo list and
+	remove links that aren't in use, freeing them.
+	(traverse_cells): Only operate on cells that are in_use.
+
 2005-04-25  Oana Serb <os@baum.ro>. Patch from Remus Draica <rd@baum.ro>
 
 	* gail/gailtreeview.c:
Index: configure.in
===================================================================
RCS file: /cvs/gnome/gail/configure.in,v
retrieving revision 1.140
diff -u -r1.140 configure.in
--- configure.in	23 Apr 2005 05:47:27 -0000	1.140
+++ configure.in	28 Apr 2005 11:15:46 -0000
@@ -5,11 +5,11 @@
 AM_CONFIG_HEADER(config.h)
 
 
-AM_INIT_AUTOMAKE(gail, 1.8.3)
+AM_INIT_AUTOMAKE(gail, 1.8.4)
 
 # libtool versioning
 LT_CURRENT=17
-LT_REVISION=2
+LT_REVISION=3
 LT_AGE=0
 LT_CURRENT_MINUS_AGE=`expr $LT_CURRENT - $LT_AGE`
 AC_SUBST(LT_CURRENT)
Index: gail/gailtreeview.c
===================================================================
RCS file: /cvs/gnome/gail/gail/gailtreeview.c,v
retrieving revision 1.176
diff -u -r1.176 gailtreeview.c
--- gail/gailtreeview.c	25 Apr 2005 11:17:19 -0000	1.176
+++ gail/gailtreeview.c	28 Apr 2005 11:15:58 -0000
@@ -38,8 +38,7 @@
 static void             gail_tree_view_finalize         (GObject                *object);
 
 static void             gail_tree_view_connect_widget_destroyed 
-                                                        (GtkAccessible        
-*accessible);
+                                                        (GtkAccessible          *accessible);
 static void             gail_tree_view_destroyed        (GtkWidget              *widget,
                                                          GtkAccessible          *accessible); 
 /* atkobject.h */
@@ -333,6 +332,7 @@
                                                          GailCell               *cell,
                                                           GList**                list);
 static AtkObject *       get_header_from_column         (GtkTreeViewColumn      *tv_col);
+static gboolean          garbage_collect_cell_data      (gpointer data);
 
 static GailWidgetClass *parent_class = NULL;
 static GQuark quark_column_desc_object = 0;
@@ -340,6 +340,7 @@
 static gboolean editing = FALSE;
 static const gchar* hadjustment = "hadjustment";
 static const gchar* vadjustment = "vadjustment";
+static gboolean garbage_collection_pending = FALSE;
 
 struct _GailTreeViewRowInfo
 {
@@ -353,6 +354,7 @@
   GailCell *cell;
   GtkTreeRowReference *cell_row_ref;
   GtkTreeViewColumn *cell_col_ref;
+  gboolean in_use;
 };
 
 typedef struct _GailTreeViewIdleData   GailTreeViewIdleData;
@@ -1038,6 +1040,7 @@
       g_object_unref (relation_set);
     }
   gtk_tree_path_free (path);
+
   /*
    * We do not increase the reference count here; when g_object_unref() is 
    * called for the cell then cell_destroyed() is called and
@@ -1879,7 +1882,6 @@
         {
           row_info = g_array_index (array, GailTreeViewRowInfo*, i);
           row_path = gtk_tree_row_reference_get_path (row_info->row_ref);
-
           if (row_path != NULL)
             {
               if (gtk_tree_path_compare (row_path, path) == 0)
@@ -2197,7 +2199,6 @@
       renderer = g_list_nth_data (renderers, index);
       g_list_free (renderers);
     }
-
   path = gtk_tree_row_reference_get_path (cell_info->cell_row_ref);
   if (path)
     {
@@ -2413,12 +2414,15 @@
     {
       info = (GailTreeViewCellInfo *) (l->data);
 
-      gail_cell_remove_state (info->cell, ATK_STATE_SELECTED, TRUE); 
-
-      path = gtk_tree_row_reference_get_path (info->cell_row_ref);
-      if (path && gtk_tree_selection_path_is_selected (tree_selection, path))
-        gail_cell_add_state (info->cell, ATK_STATE_SELECTED, TRUE); 
-      gtk_tree_path_free (path);
+      if (info->in_use)
+      {
+	  gail_cell_remove_state (info->cell, ATK_STATE_SELECTED, TRUE); 
+	  
+	  path = gtk_tree_row_reference_get_path (info->cell_row_ref);
+	  if (path && gtk_tree_selection_path_is_selected (tree_selection, path))
+	      gail_cell_add_state (info->cell, ATK_STATE_SELECTED, TRUE); 
+	  gtk_tree_path_free (path);
+      }
     }
   if (GTK_WIDGET_REALIZED (widget))
     g_signal_emit_by_name (gailview, "selection_changed");
@@ -2651,20 +2655,23 @@
   for (l = gailview->cell_data; l; l = l->next)
     {
       cell_info = (GailTreeViewCellInfo *) l->data;
-      cell_path = gtk_tree_row_reference_get_path (cell_info->cell_row_ref);
+      if (cell_info->in_use) 
+      {
+	  cell_path = gtk_tree_row_reference_get_path (cell_info->cell_row_ref);
 
-      if (cell_path != NULL)
-        {
-          if (gtk_tree_path_compare (cell_path, path) == 0)
-            {
-              if (GAIL_IS_RENDERER_CELL (cell_info->cell))
-                {
-                  update_cell_value (GAIL_RENDERER_CELL (cell_info->cell),
-                                     gailview, TRUE);
-                }
-            }
-          gtk_tree_path_free (cell_path);
-        }
+	  if (cell_path != NULL)
+	  {
+	      if (gtk_tree_path_compare (cell_path, path) == 0)
+	      {
+		  if (GAIL_IS_RENDERER_CELL (cell_info->cell))
+		  {
+		      update_cell_value (GAIL_RENDERER_CELL (cell_info->cell),
+					 gailview, TRUE);
+		  }
+	      }
+	      gtk_tree_path_free (cell_path);
+	  }
+      }
     }
   g_signal_emit_by_name (gailview, "visible-data-changed");
 }
@@ -2695,29 +2702,30 @@
       for (l = gailview->cell_data; l; l = l->next)
         {
           cell_info = (GailTreeViewCellInfo *) l->data;
-          tv_col = cell_info->cell_col_ref;
-          if (tv_col == this_col)
-            {
-              GtkTreePath *row_path;
-      
-              row_path = gtk_tree_row_reference_get_path (cell_info->cell_row_ref);
-              if (GAIL_IS_RENDERER_CELL (cell_info->cell))
-                {
-                  if (gtk_tree_view_column_get_visible (tv_col))
-                    set_cell_visibility (tree_view, 
-                                         cell_info->cell, 
-                                         tv_col, row_path, FALSE);
-                  else
-                    {
-                      gail_cell_remove_state (cell_info->cell, 
-                                              ATK_STATE_VISIBLE, TRUE);
-                      gail_cell_remove_state (cell_info->cell, 
-                                              ATK_STATE_SHOWING, TRUE);
-                    }
-                }
-              gtk_tree_path_free (row_path);
-            }
-      
+	  if (cell_info->in_use) 
+	  {
+	      tv_col = cell_info->cell_col_ref;
+	      if (tv_col == this_col)
+	      {
+		  GtkTreePath *row_path;
+		  row_path = gtk_tree_row_reference_get_path (cell_info->cell_row_ref);
+		  if (GAIL_IS_RENDERER_CELL (cell_info->cell))
+		  {
+		      if (gtk_tree_view_column_get_visible (tv_col))
+			  set_cell_visibility (tree_view, 
+					       cell_info->cell, 
+					       tv_col, row_path, FALSE);
+		      else
+		      {
+			  gail_cell_remove_state (cell_info->cell, 
+						  ATK_STATE_VISIBLE, TRUE);
+			  gail_cell_remove_state (cell_info->cell, 
+						  ATK_STATE_SHOWING, TRUE);
+		      }
+		  }
+		  gtk_tree_path_free (row_path);
+	      }
+	  }
         }
     }
 }
@@ -3421,11 +3429,12 @@
   obj = G_OBJECT (cell_info->cell);
 
   gail_cell_add_state (cell_info->cell, ATK_STATE_DEFUNCT, TRUE);
-  if (cell_info->cell_row_ref != NULL)
-    gtk_tree_row_reference_free (cell_info->cell_row_ref);
   g_object_weak_unref (obj, (GWeakNotify) cell_destroyed, obj);
-  gailview->cell_data = g_list_remove_link (gailview->cell_data, list);
-  g_free (cell_info); 
+  cell_info->in_use = FALSE; 
+  if (!garbage_collection_pending) {
+      garbage_collection_pending = TRUE;
+      g_idle_add (garbage_collect_cell_data, gailview);
+  }
 }
 
 static void 
@@ -3528,6 +3537,34 @@
     }
 }
 
+static gboolean
+garbage_collect_cell_data (gpointer data)
+{
+      GailTreeView *tree_view = data;
+      GList *temp_list = tree_view->cell_data;
+      GailTreeViewCellInfo *cell_info;
+
+      garbage_collection_pending = FALSE;
+
+      /* Must loop through them all */
+      while (temp_list != NULL)
+      {
+          cell_info = temp_list->data;
+	  if (!cell_info->in_use)
+	  {
+	      /* g_object_unref (cell_info->cell); */
+	      tree_view->cell_data = g_list_remove_link (tree_view->cell_data, 
+							 temp_list);
+	      if (cell_info->cell_row_ref)
+		  gtk_tree_row_reference_free (cell_info->cell_row_ref);
+	      g_free (cell_info);
+	  }
+          temp_list = temp_list->next;
+      }
+      
+      return garbage_collection_pending;
+}
+
 /**
  * If tree_path is passed in as NULL, then all cells are acted on.
  * Otherwise, just act on those cells that are on a row greater than 
@@ -3570,32 +3607,35 @@
           cell_info = temp_list->data;
           temp_list = temp_list->next;
 
-          row_path = gtk_tree_row_reference_get_path (cell_info->cell_row_ref);
-          g_assert (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 (act_on_cell)
-            {
-              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);
-        }
+	  if (cell_info->in_use)
+	  {
+	      row_path = gtk_tree_row_reference_get_path (cell_info->cell_row_ref);
+	      g_assert (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 (act_on_cell)
+	      {
+		  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");
 }
@@ -3654,77 +3694,80 @@
         {
           cell_info = temp_list->data;
           temp_list = temp_list->next;
-          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 (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) == 0)
-                  /* 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);
-        }
+	  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 (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) == 0)
+			  /* 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);
+	  }
+	}
     }
 }
 
@@ -3945,11 +3988,7 @@
   info = find_cell_info (tree_view, cell, &temp_list);
   if (info)
     {
-      tree_view->cell_data = g_list_remove_link (tree_view->cell_data, 
-                                                 temp_list);
-      if (info->cell_row_ref)
-        gtk_tree_row_reference_free (info->cell_row_ref);
-      g_free (info);
+      info->in_use = FALSE;
       return;
     }
   g_warning ("No cell removed in cell_info_remove\n");
@@ -3985,6 +4024,7 @@
 
   cell_info->cell_col_ref = tv_col;
   cell_info->cell = cell;
+  cell_info->in_use = TRUE; /* if we've created it, assume it's in use */
   gailview->cell_data = g_list_append (gailview->cell_data, cell_info);
       
   /* Setup weak reference notification */
@@ -4003,6 +4043,8 @@
   GList *cell_list;
   GList *l;
   gint real_index;
+  gboolean needs_cleaning = FALSE;
+  GailCell *retval = NULL;
 
   tree_view = GTK_TREE_VIEW (GTK_ACCESSIBLE (gailview)->widget);
   cell_list = gailview->cell_data;
@@ -4010,12 +4052,24 @@
   for (l = cell_list; l; l = l->next)
     {
       info = (GailTreeViewCellInfo *) (l->data);
-
-      cell_info_get_index (tree_view, info, &real_index);
-      if (index == real_index)
-        return info->cell;
+      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;
+      }
     }
-  return NULL;
+  if (needs_cleaning)
+     garbage_collect_cell_data (gailview);
+
+  return retval;
 }
 
 static void
@@ -4108,6 +4162,7 @@
       view->row_data = NULL;
     }
 
+  garbage_collect_cell_data (view);
   if (view->cell_data)
     {
       /* Must loop through them all */
