[gtk+] inspector: Deal with dying objects



commit 700657ad19f4444ed6a2f3f66ad54afc1dac3a29
Author: Matthias Clasen <mclasen redhat com>
Date:   Thu May 8 22:58:43 2014 -0400

    inspector: Deal with dying objects
    
    The widget-tree was not safe against object just going away.
    Fix this by using row references instead of iters where
    necessary, and by using weak refs to clean up when objects
    die.

 modules/inspector/classes-list.c  |   89 ++++++++++++++++++++++++-------------
 modules/inspector/classes-list.ui |    8 ++--
 modules/inspector/css-editor.c    |   17 ++++++-
 modules/inspector/css-editor.h    |    2 +-
 modules/inspector/prop-list.c     |   51 ++++++++++++++++-----
 modules/inspector/widget-tree.c   |   44 ++++++++++++++++---
 6 files changed, 155 insertions(+), 56 deletions(-)
---
diff --git a/modules/inspector/classes-list.c b/modules/inspector/classes-list.c
index 7c0d52a..cc5efbc 100644
--- a/modules/inspector/classes-list.c
+++ b/modules/inspector/classes-list.c
@@ -39,18 +39,28 @@ typedef struct
 
 struct _GtkInspectorClassesListPrivate
 {
-  GtkWidget *toolbar;
-  GtkWidget *view;
-  GtkTreeViewColumn *column;
-  GtkCellRenderer *name_renderer;
   GtkListStore *model;
-  GHashTable *contexts;
-  GtkStyleContext *current_context;
+  GtkStyleContext *context;
 };
 
 G_DEFINE_TYPE_WITH_PRIVATE (GtkInspectorClassesList, gtk_inspector_classes_list, GTK_TYPE_BOX)
 
 static void
+set_hash_context (GtkInspectorClassesList *cl, GHashTable *hash_context)
+{
+  g_object_set_data_full (G_OBJECT (cl->priv->context),
+                                    "gtk-inspector-hash-context",
+                                    hash_context, (GDestroyNotify)g_hash_table_unref);
+}
+
+static GHashTable *
+get_hash_context (GtkInspectorClassesList *cl)
+{
+  return (GHashTable *)g_object_get_data (G_OBJECT (cl->priv->context),
+                                          "gtk-inspector-hash-context");
+}
+
+static void
 enabled_toggled (GtkCellRendererToggle   *renderer,
                  const gchar             *path,
                  GtkInspectorClassesList *cl)
@@ -76,7 +86,7 @@ enabled_toggled (GtkCellRendererToggle   *renderer,
                       COLUMN_ENABLED, enabled,
                       -1);
 
-  context = g_hash_table_lookup (cl->priv->contexts, cl->priv->current_context);
+  context = get_hash_context (cl);
   if (context)
     {
       c = g_hash_table_lookup (context, name);
@@ -84,9 +94,9 @@ enabled_toggled (GtkCellRendererToggle   *renderer,
         {
           c->enabled = enabled;
           if (enabled)
-            gtk_style_context_add_class (cl->priv->current_context, name);
+            gtk_style_context_add_class (cl->priv->context, name);
           else
-            gtk_style_context_remove_class (cl->priv->current_context, name);
+            gtk_style_context_remove_class (cl->priv->context, name);
         }
       else
         g_warning ("GtkInspector: Couldn't find the css class %s in the class hash table.", name);
@@ -120,14 +130,16 @@ add_clicked (GtkButton               *button,
 
   if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK)
     {
-      const gchar *name = gtk_entry_get_text (GTK_ENTRY (entry));
-      GHashTable *context = g_hash_table_lookup (cl->priv->contexts, cl->priv->current_context);
+      const gchar *name;
+      GHashTable *context;
 
+      context = get_hash_context (cl);
+      name = gtk_entry_get_text (GTK_ENTRY (entry));
       if (*name && !g_hash_table_contains (context, name))
         {
           GtkTreeIter tree_iter;
 
-          gtk_style_context_add_class (cl->priv->current_context, name);
+          gtk_style_context_add_class (cl->priv->context, name);
 
           GtkInspectorClassesListByContext *c = g_new0 (GtkInspectorClassesListByContext, 1);
           c->enabled = TRUE;
@@ -155,7 +167,7 @@ read_classes_from_style_context (GtkInspectorClassesList *cl)
   GHashTable *hash_context;
 
   hash_context = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
-  classes = gtk_style_context_list_classes (cl->priv->current_context);
+  classes = gtk_style_context_list_classes (cl->priv->context);
 
   for (l = classes; l; l = l->next)
     {
@@ -171,7 +183,7 @@ read_classes_from_style_context (GtkInspectorClassesList *cl)
                           -1);
     }
     g_list_free (classes);
-    g_hash_table_replace (cl->priv->contexts, cl->priv->current_context, hash_context);
+    set_hash_context (cl, hash_context);
 }
 
 static void
@@ -181,15 +193,16 @@ restore_defaults_clicked (GtkButton           *button,
   GHashTableIter hash_iter;
   gchar *name;
   GtkInspectorClassesListByContext *c;
-  GHashTable *hash_context = g_hash_table_lookup (cl->priv->contexts, cl->priv->current_context);
+  GHashTable *hash_context;
 
+  hash_context = get_hash_context (cl);
   g_hash_table_iter_init (&hash_iter, hash_context);
   while (g_hash_table_iter_next (&hash_iter, (gpointer *)&name, (gpointer *)&c))
     {
       if (c->style == PANGO_STYLE_ITALIC)
-        gtk_style_context_remove_class (cl->priv->current_context, name);
+        gtk_style_context_remove_class (cl->priv->context, name);
       else if (!c->enabled)
-        gtk_style_context_add_class (cl->priv->current_context, name);
+        gtk_style_context_add_class (cl->priv->context, name);
     }
 
   gtk_list_store_clear (cl->priv->model);
@@ -201,27 +214,47 @@ gtk_inspector_classes_list_init (GtkInspectorClassesList *cl)
 {
   cl->priv = gtk_inspector_classes_list_get_instance_private (cl);
   gtk_widget_init_template (GTK_WIDGET (cl));
-  cl->priv->contexts = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, 
(GDestroyNotify)g_hash_table_destroy);
+}
+
+static void remove_dead_object (gpointer data, GObject *dead_object);
+
+static void
+cleanup_context (GtkInspectorClassesList *cl)
+{
+  if (cl->priv->context)
+    g_object_weak_unref (G_OBJECT (cl->priv->context), remove_dead_object, cl);
+
+  gtk_list_store_clear (cl->priv->model);
+  cl->priv->context = NULL;
+  gtk_widget_set_sensitive (GTK_WIDGET (cl), FALSE);
+}
+
+static void
+remove_dead_object (gpointer data, GObject *dead_object)
+{
+  GtkInspectorClassesList *cl = data;
+
+  cl->priv->context = NULL;
+  cleanup_context (cl);
 }
 
 void
 gtk_inspector_classes_list_set_widget (GtkInspectorClassesList *cl,
                                        GtkWidget               *widget)
 {
-  GtkStyleContext *widget_context;
   GHashTable *hash_context;
   GtkTreeIter tree_iter;
   GtkInspectorClassesListByContext *c;
 
-  gtk_list_store_clear (cl->priv->model);
+  cleanup_context (cl);
 
   gtk_widget_set_sensitive (GTK_WIDGET (cl), TRUE);
-  widget_context = gtk_widget_get_style_context (widget);
 
-  cl->priv->current_context = widget_context;
-  gtk_widget_set_sensitive (cl->priv->toolbar, TRUE);
+  cl->priv->context = gtk_widget_get_style_context (widget);
 
-  hash_context = g_hash_table_lookup (cl->priv->contexts, widget_context);
+  g_object_weak_ref (G_OBJECT (cl->priv->context), remove_dead_object, cl);
+
+  hash_context = get_hash_context (cl);
   if (hash_context)
     {
       GHashTableIter hash_iter;
@@ -239,9 +272,7 @@ gtk_inspector_classes_list_set_widget (GtkInspectorClassesList *cl,
         }
     }
   else
-    {
-      read_classes_from_style_context (cl);
-    }
+    read_classes_from_style_context (cl);
 }
 
 static void
@@ -250,11 +281,7 @@ gtk_inspector_classes_list_class_init (GtkInspectorClassesListClass *klass)
   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
 
   gtk_widget_class_set_template_from_resource (widget_class, "/org/gtk/inspector/classes-list.ui");
-  gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorClassesList, toolbar);
-  gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorClassesList, view);
   gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorClassesList, model);
-  gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorClassesList, column);
-  gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorClassesList, name_renderer);
   gtk_widget_class_bind_template_callback (widget_class, add_clicked);
   gtk_widget_class_bind_template_callback (widget_class, restore_defaults_clicked);
   gtk_widget_class_bind_template_callback (widget_class, enabled_toggled);
diff --git a/modules/inspector/classes-list.ui b/modules/inspector/classes-list.ui
index e26700f..0835eef 100644
--- a/modules/inspector/classes-list.ui
+++ b/modules/inspector/classes-list.ui
@@ -10,7 +10,7 @@
   <template class="GtkInspectorClassesList" parent="GtkBox">
     <property name="orientation">vertical</property>
     <child>
-      <object class="GtkToolbar" id="toolbar">
+      <object class="GtkToolbar">
         <property name="visible">True</property>
         <property name="icon-size">small-toolbar</property>
         <property name="sensitive">False</property>
@@ -37,11 +37,11 @@
         <property name="visible">True</property>
         <property name="expand">True</property>
         <child>
-          <object class="GtkTreeView" id="view">
+          <object class="GtkTreeView">
             <property name="visible">True</property>
             <property name="model">model</property>
             <child>
-              <object class="GtkTreeViewColumn" id="column">
+              <object class="GtkTreeViewColumn">
                 <property name="title" translatable="yes">Name</property>
                 <child>
                   <object class="GtkCellRendererToggle">
@@ -53,7 +53,7 @@
                   </attributes>
                 </child>
                 <child>
-                  <object class="GtkCellRendererText" id="name_renderer">
+                  <object class="GtkCellRendererText">
                     <property name="scale">0.8</property>
                   </object>
                   <attributes>
diff --git a/modules/inspector/css-editor.c b/modules/inspector/css-editor.c
index b993e24..d5a59b9 100644
--- a/modules/inspector/css-editor.c
+++ b/modules/inspector/css-editor.c
@@ -205,8 +205,9 @@ create_provider (GtkInspectorCssEditor *ce)
       gtk_style_context_add_provider (ce->priv->context,
                                       GTK_STYLE_PROVIDER (provider),
                                       G_MAXUINT);
-      g_object_set_data (G_OBJECT (ce->priv->context),
-                         GTK_INSPECTOR_CSS_EDITOR_PROVIDER, provider);
+      g_object_set_data_full (G_OBJECT (ce->priv->context),
+                              GTK_INSPECTOR_CSS_EDITOR_PROVIDER, provider,
+                              g_object_unref);
     }
 
   g_signal_connect (provider, "parsing-error",
@@ -302,6 +303,15 @@ gtk_inspector_css_editor_new (gboolean global)
                                    NULL));
 }
 
+static void
+remove_dead_object (gpointer data, GObject *dead_object)
+{
+  GtkInspectorCssEditor *ce = data;
+
+  ce->priv->context = NULL;
+  gtk_widget_set_sensitive (GTK_WIDGET (ce), ce->priv->global);
+}
+
 void
 gtk_inspector_css_editor_set_widget (GtkInspectorCssEditor *ce,
                                      GtkWidget             *widget)
@@ -316,6 +326,7 @@ gtk_inspector_css_editor_set_widget (GtkInspectorCssEditor *ce,
 
   if (ce->priv->context)
     {
+      g_object_weak_unref (G_OBJECT (ce->priv->context), remove_dead_object, ce);
       text = get_current_text (GTK_TEXT_BUFFER (ce->priv->text));
       g_object_set_data_full (G_OBJECT (ce->priv->context),
                               GTK_INSPECTOR_CSS_EDITOR_TEXT,
@@ -331,6 +342,8 @@ gtk_inspector_css_editor_set_widget (GtkInspectorCssEditor *ce,
 
   set_initial_text (ce);
   disable_toggled (ce->priv->disable_button, ce);
+
+  g_object_weak_ref (G_OBJECT (ce->priv->context), remove_dead_object, ce);
 }
 
 // vim: set et sw=2 ts=2:
diff --git a/modules/inspector/css-editor.h b/modules/inspector/css-editor.h
index 80be309..08ee4e0 100644
--- a/modules/inspector/css-editor.h
+++ b/modules/inspector/css-editor.h
@@ -50,7 +50,7 @@ G_BEGIN_DECLS
 
 GType      gtk_inspector_css_editor_get_type   (void);
 GtkWidget *gtk_inspector_css_editor_new        (gboolean               global);
-void       gtk_inspector_css_editor_set_widget (GtkInspectorCssEditor *editor,
+void       gtk_inspector_css_editor_set_widget (GtkInspectorCssEditor *ce,
                                                 GtkWidget             *widget);
 
 G_END_DECLS
diff --git a/modules/inspector/prop-list.c b/modules/inspector/prop-list.c
index d58cbc3..6c78777 100644
--- a/modules/inspector/prop-list.c
+++ b/modules/inspector/prop-list.c
@@ -232,6 +232,38 @@ gtk_inspector_prop_list_new (GtkWidget *widget_tree,
                        NULL);
 }
 
+static void remove_dead_object (gpointer data, GObject *dead_object);
+
+static void
+cleanup_object (GtkInspectorPropList *pl)
+{
+  gtk_widget_set_sensitive (GTK_WIDGET (pl), FALSE);
+
+  if (pl->priv->object)
+    g_object_weak_unref (pl->priv->object, remove_dead_object, pl);
+
+  if (pl->priv->object && pl->priv->notify_handler_id != 0)
+    {
+      g_signal_handler_disconnect (pl->priv->object, pl->priv->notify_handler_id);
+      pl->priv->notify_handler_id = 0;
+    }
+
+  pl->priv->object = NULL;
+
+  g_hash_table_remove_all (pl->priv->prop_iters);
+  gtk_list_store_clear (pl->priv->model);
+}
+
+static void
+remove_dead_object (gpointer data, GObject *dead_object)
+{
+  GtkInspectorPropList *pl = data;
+
+  pl->priv->notify_handler_id = 0;
+  pl->priv->object = NULL;
+  cleanup_object (pl);
+}
+
 gboolean
 gtk_inspector_prop_list_set_object (GtkInspectorPropList *pl,
                                     GObject              *object)
@@ -244,18 +276,16 @@ gtk_inspector_prop_list_set_object (GtkInspectorPropList *pl,
   if (pl->priv->object == object)
     return FALSE;
 
-  if (pl->priv->notify_handler_id != 0)
-    {
-      g_signal_handler_disconnect (pl->priv->object, pl->priv->notify_handler_id);
-      pl->priv->notify_handler_id = 0;
-    }
-
-  g_hash_table_remove_all (pl->priv->prop_iters);
-  gtk_list_store_clear (pl->priv->model);
-  gtk_widget_set_sensitive (GTK_WIDGET (pl), FALSE);
+  cleanup_object (pl);
 
   pl->priv->object = object;
 
+  g_object_weak_ref (object, remove_dead_object, pl);
+
+  g_object_set (pl->priv->attribute_column,
+                "visible", !pl->priv->child_properties && GTK_IS_CELL_RENDERER (object),
+                NULL);
+
   if (pl->priv->child_properties)
     {
       GtkWidget *parent;
@@ -294,9 +324,6 @@ gtk_inspector_prop_list_set_object (GtkInspectorPropList *pl,
                         G_CALLBACK (gtk_inspector_prop_list_prop_changed_cb),
                         pl);
 
-  g_object_set (pl->priv->attribute_column,
-                "visible", !pl->priv->child_properties && GTK_IS_CELL_RENDERER (object),
-                NULL);
   return TRUE;
 }
 
diff --git a/modules/inspector/widget-tree.c b/modules/inspector/widget-tree.c
index 165339a..4b63cb8 100644
--- a/modules/inspector/widget-tree.c
+++ b/modules/inspector/widget-tree.c
@@ -63,20 +63,42 @@ on_widget_selected (GtkTreeSelection       *selection,
 
 typedef struct
 {
+  GtkInspectorWidgetTree *wt;
   GObject *object;
-  GtkTreeIter *iter;
+  GtkTreeRowReference *row;
   gulong map_handler;
   gulong unmap_handler;
 } ObjectData;
 
 static void
+remove_dead_object (gpointer data, GObject *dead_object)
+{
+  ObjectData *od = data;
+
+  if (gtk_tree_row_reference_valid (od->row))
+    {
+      GtkTreePath *path;
+      GtkTreeIter iter;
+      path = gtk_tree_row_reference_get_path (od->row);
+      gtk_tree_model_get_iter (GTK_TREE_MODEL (od->wt->priv->model), &iter, path);
+      gtk_tree_store_remove (od->wt->priv->model, &iter);
+      gtk_tree_path_free (path);
+    }
+  od->object = NULL;
+  g_hash_table_remove (od->wt->priv->iters, dead_object);
+}
+
+static void
 object_data_free (gpointer data)
 {
   ObjectData *od = data;
 
-  gtk_tree_iter_free (od->iter);
+  gtk_tree_row_reference_free (od->row);
 
-  if (g_signal_handler_is_connected (od->object, od->map_handler))
+  if (od->object)
+    g_object_weak_unref (od->object, remove_dead_object, od);
+
+  if (od->object && od->map_handler)
     {
       g_signal_handler_disconnect (od->object, od->map_handler);
       g_signal_handler_disconnect (od->object, od->unmap_handler);
@@ -181,6 +203,7 @@ gtk_inspector_widget_tree_append_object (GtkInspectorWidgetTree *wt,
                                          const gchar            *name)
 {
   GtkTreeIter iter;
+  GtkTreePath *path;
   const gchar *class_name = G_OBJECT_CLASS_NAME (G_OBJECT_GET_CLASS (object));
   gchar *address;
   gboolean mapped;
@@ -231,8 +254,11 @@ gtk_inspector_widget_tree_append_object (GtkInspectorWidgetTree *wt,
                       -1);
 
   od = g_new0 (ObjectData, 1);
+  od->wt = wt;
   od->object = object;
-  od->iter = gtk_tree_iter_copy (&iter);
+  path = gtk_tree_model_get_path (GTK_TREE_MODEL (wt->priv->model), &iter);
+  od->row = gtk_tree_row_reference_new (GTK_TREE_MODEL (wt->priv->model), path);
+  gtk_tree_path_free (path);
   if (GTK_IS_WIDGET (object))
     {
       od->map_handler = g_signal_connect (object, "map", G_CALLBACK (map_or_unmap), wt);
@@ -240,6 +266,7 @@ gtk_inspector_widget_tree_append_object (GtkInspectorWidgetTree *wt,
     }
 
   g_hash_table_insert (wt->priv->iters, object, od);
+  g_object_weak_ref (object, remove_dead_object, od);
 
   g_free (address);
 
@@ -306,9 +333,14 @@ gtk_inspector_widget_tree_find_object (GtkInspectorWidgetTree *wt,
   ObjectData *od;
 
   od = g_hash_table_lookup (wt->priv->iters, object);
-  if (od)
+  if (od && gtk_tree_row_reference_valid (od->row))
     {
-      *iter = *od->iter;
+      GtkTreePath *path;
+
+      path = gtk_tree_row_reference_get_path (od->row);
+      gtk_tree_model_get_iter (GTK_TREE_MODEL (wt->priv->model), iter, path);
+      gtk_tree_path_free (path);
+
       return TRUE;
     }
 


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