forked from AuroraMiddleware/gtk
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:
parent
0dbfef5696
commit
700657ad19
@ -39,17 +39,27 @@ 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,
|
||||
@ -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);
|
||||
|
@ -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>
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user