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.
This commit is contained in:
Matthias Clasen 2014-05-08 22:58:43 -04:00
parent 0dbfef5696
commit 700657ad19
6 changed files with 155 additions and 56 deletions

View File

@ -39,17 +39,27 @@ typedef struct
struct _GtkInspectorClassesListPrivate struct _GtkInspectorClassesListPrivate
{ {
GtkWidget *toolbar;
GtkWidget *view;
GtkTreeViewColumn *column;
GtkCellRenderer *name_renderer;
GtkListStore *model; GtkListStore *model;
GHashTable *contexts; GtkStyleContext *context;
GtkStyleContext *current_context;
}; };
G_DEFINE_TYPE_WITH_PRIVATE (GtkInspectorClassesList, gtk_inspector_classes_list, GTK_TYPE_BOX) 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 static void
enabled_toggled (GtkCellRendererToggle *renderer, enabled_toggled (GtkCellRendererToggle *renderer,
const gchar *path, const gchar *path,
@ -76,7 +86,7 @@ enabled_toggled (GtkCellRendererToggle *renderer,
COLUMN_ENABLED, enabled, COLUMN_ENABLED, enabled,
-1); -1);
context = g_hash_table_lookup (cl->priv->contexts, cl->priv->current_context); context = get_hash_context (cl);
if (context) if (context)
{ {
c = g_hash_table_lookup (context, name); c = g_hash_table_lookup (context, name);
@ -84,9 +94,9 @@ enabled_toggled (GtkCellRendererToggle *renderer,
{ {
c->enabled = enabled; c->enabled = enabled;
if (enabled) if (enabled)
gtk_style_context_add_class (cl->priv->current_context, name); gtk_style_context_add_class (cl->priv->context, name);
else else
gtk_style_context_remove_class (cl->priv->current_context, name); gtk_style_context_remove_class (cl->priv->context, name);
} }
else else
g_warning ("GtkInspector: Couldn't find the css class %s in the class hash table.", name); 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) if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK)
{ {
const gchar *name = gtk_entry_get_text (GTK_ENTRY (entry)); const gchar *name;
GHashTable *context = g_hash_table_lookup (cl->priv->contexts, cl->priv->current_context); GHashTable *context;
context = get_hash_context (cl);
name = gtk_entry_get_text (GTK_ENTRY (entry));
if (*name && !g_hash_table_contains (context, name)) if (*name && !g_hash_table_contains (context, name))
{ {
GtkTreeIter tree_iter; 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); GtkInspectorClassesListByContext *c = g_new0 (GtkInspectorClassesListByContext, 1);
c->enabled = TRUE; c->enabled = TRUE;
@ -155,7 +167,7 @@ read_classes_from_style_context (GtkInspectorClassesList *cl)
GHashTable *hash_context; GHashTable *hash_context;
hash_context = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); 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) for (l = classes; l; l = l->next)
{ {
@ -171,7 +183,7 @@ read_classes_from_style_context (GtkInspectorClassesList *cl)
-1); -1);
} }
g_list_free (classes); 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 static void
@ -181,15 +193,16 @@ restore_defaults_clicked (GtkButton *button,
GHashTableIter hash_iter; GHashTableIter hash_iter;
gchar *name; gchar *name;
GtkInspectorClassesListByContext *c; 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); g_hash_table_iter_init (&hash_iter, hash_context);
while (g_hash_table_iter_next (&hash_iter, (gpointer *)&name, (gpointer *)&c)) while (g_hash_table_iter_next (&hash_iter, (gpointer *)&name, (gpointer *)&c))
{ {
if (c->style == PANGO_STYLE_ITALIC) 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) 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); 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); cl->priv = gtk_inspector_classes_list_get_instance_private (cl);
gtk_widget_init_template (GTK_WIDGET (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 void
gtk_inspector_classes_list_set_widget (GtkInspectorClassesList *cl, gtk_inspector_classes_list_set_widget (GtkInspectorClassesList *cl,
GtkWidget *widget) GtkWidget *widget)
{ {
GtkStyleContext *widget_context;
GHashTable *hash_context; GHashTable *hash_context;
GtkTreeIter tree_iter; GtkTreeIter tree_iter;
GtkInspectorClassesListByContext *c; GtkInspectorClassesListByContext *c;
gtk_list_store_clear (cl->priv->model); cleanup_context (cl);
gtk_widget_set_sensitive (GTK_WIDGET (cl), TRUE); gtk_widget_set_sensitive (GTK_WIDGET (cl), TRUE);
widget_context = gtk_widget_get_style_context (widget);
cl->priv->current_context = widget_context; cl->priv->context = gtk_widget_get_style_context (widget);
gtk_widget_set_sensitive (cl->priv->toolbar, TRUE);
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) if (hash_context)
{ {
GHashTableIter hash_iter; GHashTableIter hash_iter;
@ -239,9 +272,7 @@ gtk_inspector_classes_list_set_widget (GtkInspectorClassesList *cl,
} }
} }
else else
{ read_classes_from_style_context (cl);
read_classes_from_style_context (cl);
}
} }
static void static void
@ -250,11 +281,7 @@ gtk_inspector_classes_list_class_init (GtkInspectorClassesListClass *klass)
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (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_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, 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, add_clicked);
gtk_widget_class_bind_template_callback (widget_class, restore_defaults_clicked); gtk_widget_class_bind_template_callback (widget_class, restore_defaults_clicked);
gtk_widget_class_bind_template_callback (widget_class, enabled_toggled); gtk_widget_class_bind_template_callback (widget_class, enabled_toggled);

View File

@ -10,7 +10,7 @@
<template class="GtkInspectorClassesList" parent="GtkBox"> <template class="GtkInspectorClassesList" parent="GtkBox">
<property name="orientation">vertical</property> <property name="orientation">vertical</property>
<child> <child>
<object class="GtkToolbar" id="toolbar"> <object class="GtkToolbar">
<property name="visible">True</property> <property name="visible">True</property>
<property name="icon-size">small-toolbar</property> <property name="icon-size">small-toolbar</property>
<property name="sensitive">False</property> <property name="sensitive">False</property>
@ -37,11 +37,11 @@
<property name="visible">True</property> <property name="visible">True</property>
<property name="expand">True</property> <property name="expand">True</property>
<child> <child>
<object class="GtkTreeView" id="view"> <object class="GtkTreeView">
<property name="visible">True</property> <property name="visible">True</property>
<property name="model">model</property> <property name="model">model</property>
<child> <child>
<object class="GtkTreeViewColumn" id="column"> <object class="GtkTreeViewColumn">
<property name="title" translatable="yes">Name</property> <property name="title" translatable="yes">Name</property>
<child> <child>
<object class="GtkCellRendererToggle"> <object class="GtkCellRendererToggle">
@ -53,7 +53,7 @@
</attributes> </attributes>
</child> </child>
<child> <child>
<object class="GtkCellRendererText" id="name_renderer"> <object class="GtkCellRendererText">
<property name="scale">0.8</property> <property name="scale">0.8</property>
</object> </object>
<attributes> <attributes>

View File

@ -205,8 +205,9 @@ create_provider (GtkInspectorCssEditor *ce)
gtk_style_context_add_provider (ce->priv->context, gtk_style_context_add_provider (ce->priv->context,
GTK_STYLE_PROVIDER (provider), GTK_STYLE_PROVIDER (provider),
G_MAXUINT); G_MAXUINT);
g_object_set_data (G_OBJECT (ce->priv->context), g_object_set_data_full (G_OBJECT (ce->priv->context),
GTK_INSPECTOR_CSS_EDITOR_PROVIDER, provider); GTK_INSPECTOR_CSS_EDITOR_PROVIDER, provider,
g_object_unref);
} }
g_signal_connect (provider, "parsing-error", g_signal_connect (provider, "parsing-error",
@ -302,6 +303,15 @@ gtk_inspector_css_editor_new (gboolean global)
NULL)); 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 void
gtk_inspector_css_editor_set_widget (GtkInspectorCssEditor *ce, gtk_inspector_css_editor_set_widget (GtkInspectorCssEditor *ce,
GtkWidget *widget) GtkWidget *widget)
@ -316,6 +326,7 @@ gtk_inspector_css_editor_set_widget (GtkInspectorCssEditor *ce,
if (ce->priv->context) 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)); text = get_current_text (GTK_TEXT_BUFFER (ce->priv->text));
g_object_set_data_full (G_OBJECT (ce->priv->context), g_object_set_data_full (G_OBJECT (ce->priv->context),
GTK_INSPECTOR_CSS_EDITOR_TEXT, GTK_INSPECTOR_CSS_EDITOR_TEXT,
@ -331,6 +342,8 @@ gtk_inspector_css_editor_set_widget (GtkInspectorCssEditor *ce,
set_initial_text (ce); set_initial_text (ce);
disable_toggled (ce->priv->disable_button, 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: // vim: set et sw=2 ts=2:

View File

@ -50,7 +50,7 @@ G_BEGIN_DECLS
GType gtk_inspector_css_editor_get_type (void); GType gtk_inspector_css_editor_get_type (void);
GtkWidget *gtk_inspector_css_editor_new (gboolean global); 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); GtkWidget *widget);
G_END_DECLS G_END_DECLS

View File

@ -232,6 +232,38 @@ gtk_inspector_prop_list_new (GtkWidget *widget_tree,
NULL); 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 gboolean
gtk_inspector_prop_list_set_object (GtkInspectorPropList *pl, gtk_inspector_prop_list_set_object (GtkInspectorPropList *pl,
GObject *object) GObject *object)
@ -244,18 +276,16 @@ gtk_inspector_prop_list_set_object (GtkInspectorPropList *pl,
if (pl->priv->object == object) if (pl->priv->object == object)
return FALSE; return FALSE;
if (pl->priv->notify_handler_id != 0) cleanup_object (pl);
{
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);
pl->priv->object = object; 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) if (pl->priv->child_properties)
{ {
GtkWidget *parent; GtkWidget *parent;
@ -294,9 +324,6 @@ gtk_inspector_prop_list_set_object (GtkInspectorPropList *pl,
G_CALLBACK (gtk_inspector_prop_list_prop_changed_cb), G_CALLBACK (gtk_inspector_prop_list_prop_changed_cb),
pl); pl);
g_object_set (pl->priv->attribute_column,
"visible", !pl->priv->child_properties && GTK_IS_CELL_RENDERER (object),
NULL);
return TRUE; return TRUE;
} }

View File

@ -63,20 +63,42 @@ on_widget_selected (GtkTreeSelection *selection,
typedef struct typedef struct
{ {
GtkInspectorWidgetTree *wt;
GObject *object; GObject *object;
GtkTreeIter *iter; GtkTreeRowReference *row;
gulong map_handler; gulong map_handler;
gulong unmap_handler; gulong unmap_handler;
} ObjectData; } 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 static void
object_data_free (gpointer data) object_data_free (gpointer data)
{ {
ObjectData *od = 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->map_handler);
g_signal_handler_disconnect (od->object, od->unmap_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) const gchar *name)
{ {
GtkTreeIter iter; GtkTreeIter iter;
GtkTreePath *path;
const gchar *class_name = G_OBJECT_CLASS_NAME (G_OBJECT_GET_CLASS (object)); const gchar *class_name = G_OBJECT_CLASS_NAME (G_OBJECT_GET_CLASS (object));
gchar *address; gchar *address;
gboolean mapped; gboolean mapped;
@ -231,8 +254,11 @@ gtk_inspector_widget_tree_append_object (GtkInspectorWidgetTree *wt,
-1); -1);
od = g_new0 (ObjectData, 1); od = g_new0 (ObjectData, 1);
od->wt = wt;
od->object = object; 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)) if (GTK_IS_WIDGET (object))
{ {
od->map_handler = g_signal_connect (object, "map", G_CALLBACK (map_or_unmap), wt); 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_hash_table_insert (wt->priv->iters, object, od);
g_object_weak_ref (object, remove_dead_object, od);
g_free (address); g_free (address);
@ -306,9 +333,14 @@ gtk_inspector_widget_tree_find_object (GtkInspectorWidgetTree *wt,
ObjectData *od; ObjectData *od;
od = g_hash_table_lookup (wt->priv->iters, object); 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; return TRUE;
} }