2014-05-08 05:04:16 +00:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2014 Red Hat, Inc.
|
|
|
|
*
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
* version 2 of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
#include <glib/gi18n-lib.h>
|
2014-07-12 03:14:04 +00:00
|
|
|
|
2014-05-08 05:04:16 +00:00
|
|
|
#include "signals-list.h"
|
|
|
|
|
2014-07-12 03:14:04 +00:00
|
|
|
#include "gtkcellrenderer.h"
|
|
|
|
#include "gtkliststore.h"
|
|
|
|
#include "gtktextbuffer.h"
|
|
|
|
#include "gtktoggletoolbutton.h"
|
|
|
|
#include "gtktreeviewcolumn.h"
|
|
|
|
|
2014-05-08 05:04:16 +00:00
|
|
|
enum
|
|
|
|
{
|
|
|
|
COLUMN_NAME,
|
|
|
|
COLUMN_CLASS,
|
2014-05-10 14:40:38 +00:00
|
|
|
COLUMN_CONNECTED,
|
|
|
|
COLUMN_COUNT,
|
|
|
|
COLUMN_NO_HOOKS,
|
|
|
|
COLUMN_SIGNAL_ID,
|
|
|
|
COLUMN_HOOK_ID
|
2014-05-08 05:04:16 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
struct _GtkInspectorSignalsListPrivate
|
|
|
|
{
|
|
|
|
GtkWidget *view;
|
|
|
|
GtkListStore *model;
|
2014-05-10 14:40:38 +00:00
|
|
|
GtkTextBuffer *text;
|
|
|
|
GtkWidget *log_win;
|
|
|
|
GtkWidget *trace_button;
|
2014-05-20 16:33:19 +00:00
|
|
|
GtkWidget *clear_button;
|
2014-05-10 14:40:38 +00:00
|
|
|
GtkTreeViewColumn *count_column;
|
|
|
|
GtkCellRenderer *count_renderer;
|
|
|
|
GObject *object;
|
|
|
|
GHashTable *iters;
|
|
|
|
gboolean tracing;
|
2014-05-08 05:04:16 +00:00
|
|
|
};
|
|
|
|
|
2014-05-10 14:40:38 +00:00
|
|
|
G_DEFINE_TYPE_WITH_PRIVATE (GtkInspectorSignalsList, gtk_inspector_signals_list, GTK_TYPE_PANED)
|
2014-05-08 05:04:16 +00:00
|
|
|
|
|
|
|
static GType *
|
|
|
|
get_types (GObject *object, guint *length)
|
|
|
|
{
|
|
|
|
GHashTable *seen;
|
|
|
|
GType *ret;
|
|
|
|
GType type;
|
|
|
|
GType *iface;
|
|
|
|
gint i;
|
|
|
|
|
|
|
|
seen = g_hash_table_new (g_direct_hash, g_direct_equal);
|
|
|
|
|
|
|
|
type = ((GTypeInstance*)object)->g_class->g_type;
|
|
|
|
while (type)
|
|
|
|
{
|
|
|
|
g_hash_table_add (seen, GSIZE_TO_POINTER (type));
|
|
|
|
iface = g_type_interfaces (type, NULL);
|
|
|
|
for (i = 0; iface[i]; i++)
|
|
|
|
g_hash_table_add (seen, GSIZE_TO_POINTER (iface[i]));
|
|
|
|
g_free (iface);
|
|
|
|
type = g_type_parent (type);
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = (GType *)g_hash_table_get_keys_as_array (seen, length);
|
|
|
|
g_hash_table_unref (seen);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
add_signals (GtkInspectorSignalsList *sl,
|
|
|
|
GType type,
|
|
|
|
GObject *object)
|
|
|
|
{
|
|
|
|
guint *ids;
|
|
|
|
guint n_ids;
|
|
|
|
gint i;
|
|
|
|
GSignalQuery query;
|
|
|
|
GtkTreeIter iter;
|
|
|
|
gboolean has_handler;
|
|
|
|
|
|
|
|
if (!G_TYPE_IS_INSTANTIATABLE (type) && !G_TYPE_IS_INTERFACE (type))
|
|
|
|
return;
|
|
|
|
|
|
|
|
ids = g_signal_list_ids (type, &n_ids);
|
|
|
|
for (i = 0; i < n_ids; i++)
|
|
|
|
{
|
|
|
|
g_signal_query (ids[i], &query);
|
|
|
|
has_handler = g_signal_has_handler_pending (object, ids[i], 0, TRUE);
|
|
|
|
gtk_list_store_append (sl->priv->model, &iter);
|
|
|
|
gtk_list_store_set (sl->priv->model, &iter,
|
|
|
|
COLUMN_NAME, query.signal_name,
|
|
|
|
COLUMN_CLASS, g_type_name (type),
|
|
|
|
COLUMN_CONNECTED, has_handler ? _("Yes") : "",
|
2014-05-10 14:40:38 +00:00
|
|
|
COLUMN_COUNT, 0,
|
|
|
|
COLUMN_NO_HOOKS, (query.signal_flags & G_SIGNAL_NO_HOOKS) != 0,
|
|
|
|
COLUMN_SIGNAL_ID, ids[i],
|
|
|
|
COLUMN_HOOK_ID, 0,
|
2014-05-08 05:04:16 +00:00
|
|
|
-1);
|
2014-05-10 14:40:38 +00:00
|
|
|
g_hash_table_insert (sl->priv->iters,
|
|
|
|
GINT_TO_POINTER (ids[i]), gtk_tree_iter_copy (&iter));
|
2014-05-08 05:04:16 +00:00
|
|
|
}
|
|
|
|
g_free (ids);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
read_signals_from_object (GtkInspectorSignalsList *sl,
|
|
|
|
GObject *object)
|
|
|
|
{
|
|
|
|
GType *types;
|
|
|
|
guint length;
|
|
|
|
gint i;
|
|
|
|
|
|
|
|
types = get_types (object, &length);
|
|
|
|
for (i = 0; i < length; i++)
|
|
|
|
add_signals (sl, types[i], object);
|
|
|
|
g_free (types);
|
|
|
|
}
|
|
|
|
|
2014-05-10 14:40:38 +00:00
|
|
|
static void stop_tracing (GtkInspectorSignalsList *sl);
|
|
|
|
|
2014-05-08 05:04:16 +00:00
|
|
|
void
|
|
|
|
gtk_inspector_signals_list_set_object (GtkInspectorSignalsList *sl,
|
|
|
|
GObject *object)
|
|
|
|
{
|
2014-05-10 14:40:38 +00:00
|
|
|
if (sl->priv->object == object)
|
|
|
|
return;
|
|
|
|
|
|
|
|
stop_tracing (sl);
|
2014-05-08 05:04:16 +00:00
|
|
|
gtk_list_store_clear (sl->priv->model);
|
2014-05-10 14:40:38 +00:00
|
|
|
g_hash_table_remove_all (sl->priv->iters);
|
|
|
|
|
|
|
|
sl->priv->object = object;
|
|
|
|
|
|
|
|
if (object)
|
|
|
|
read_signals_from_object (sl, object);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
render_count (GtkTreeViewColumn *column,
|
|
|
|
GtkCellRenderer *renderer,
|
|
|
|
GtkTreeModel *model,
|
|
|
|
GtkTreeIter *iter,
|
|
|
|
gpointer data)
|
|
|
|
{
|
|
|
|
gint count;
|
|
|
|
gboolean no_hooks;
|
|
|
|
gchar text[100];
|
|
|
|
|
|
|
|
gtk_tree_model_get (model, iter,
|
|
|
|
COLUMN_COUNT, &count,
|
|
|
|
COLUMN_NO_HOOKS, &no_hooks,
|
|
|
|
-1);
|
|
|
|
if (no_hooks)
|
|
|
|
{
|
|
|
|
g_object_set (renderer, "markup", "<i>(untraceable)</i>", NULL);
|
|
|
|
}
|
|
|
|
else if (count != 0)
|
|
|
|
{
|
|
|
|
g_snprintf (text, 100, "%d", count);
|
|
|
|
g_object_set (renderer, "text", text, NULL);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
g_object_set (renderer, "text", "", NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gtk_inspector_signals_list_init (GtkInspectorSignalsList *sl)
|
|
|
|
{
|
|
|
|
sl->priv = gtk_inspector_signals_list_get_instance_private (sl);
|
|
|
|
gtk_widget_init_template (GTK_WIDGET (sl));
|
|
|
|
|
2014-05-21 14:50:37 +00:00
|
|
|
gtk_tool_button_set_icon_name (GTK_TOOL_BUTTON (sl->priv->clear_button), "edit-clear-symbolic");
|
2014-05-20 16:33:19 +00:00
|
|
|
|
2014-05-10 14:40:38 +00:00
|
|
|
gtk_tree_view_column_set_cell_data_func (sl->priv->count_column,
|
|
|
|
sl->priv->count_renderer,
|
|
|
|
render_count,
|
|
|
|
NULL, NULL);
|
|
|
|
|
|
|
|
sl->priv->iters = g_hash_table_new_full (g_direct_hash,
|
|
|
|
g_direct_equal,
|
|
|
|
NULL,
|
|
|
|
(GDestroyNotify) gtk_tree_iter_free);
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
trace_hook (GSignalInvocationHint *ihint,
|
|
|
|
guint n_param_values,
|
|
|
|
const GValue *param_values,
|
|
|
|
gpointer data)
|
|
|
|
{
|
|
|
|
GtkInspectorSignalsList *sl = data;
|
|
|
|
GObject *object;
|
|
|
|
|
|
|
|
object = g_value_get_object (param_values);
|
|
|
|
|
|
|
|
if (object == sl->priv->object)
|
|
|
|
{
|
|
|
|
gint count;
|
|
|
|
GtkTreeIter *iter;
|
|
|
|
|
|
|
|
iter = (GtkTreeIter *)g_hash_table_lookup (sl->priv->iters, GINT_TO_POINTER (ihint->signal_id));
|
|
|
|
|
|
|
|
gtk_tree_model_get (GTK_TREE_MODEL (sl->priv->model), iter, COLUMN_COUNT, &count, -1);
|
|
|
|
gtk_list_store_set (sl->priv->model, iter, COLUMN_COUNT, count + 1, -1);
|
|
|
|
}
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
start_tracing_cb (GtkTreeModel *model,
|
|
|
|
GtkTreePath *path,
|
|
|
|
GtkTreeIter *iter,
|
|
|
|
gpointer data)
|
|
|
|
{
|
|
|
|
GtkInspectorSignalsList *sl = data;
|
|
|
|
guint signal_id;
|
|
|
|
gulong hook_id;
|
|
|
|
gboolean no_hooks;
|
|
|
|
|
|
|
|
gtk_tree_model_get (model, iter,
|
|
|
|
COLUMN_SIGNAL_ID, &signal_id,
|
|
|
|
COLUMN_HOOK_ID, &hook_id,
|
|
|
|
COLUMN_NO_HOOKS, &no_hooks,
|
|
|
|
-1);
|
|
|
|
|
|
|
|
g_assert (signal_id != 0);
|
|
|
|
g_assert (hook_id == 0);
|
|
|
|
|
|
|
|
if (!no_hooks)
|
|
|
|
{
|
|
|
|
hook_id = g_signal_add_emission_hook (signal_id, 0, trace_hook, sl, NULL);
|
|
|
|
|
|
|
|
gtk_list_store_set (GTK_LIST_STORE (model), iter,
|
|
|
|
COLUMN_COUNT, 0,
|
|
|
|
COLUMN_HOOK_ID, hook_id,
|
|
|
|
-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
stop_tracing_cb (GtkTreeModel *model,
|
|
|
|
GtkTreePath *path,
|
|
|
|
GtkTreeIter *iter,
|
|
|
|
gpointer data)
|
|
|
|
{
|
|
|
|
guint signal_id;
|
|
|
|
gulong hook_id;
|
|
|
|
|
|
|
|
gtk_tree_model_get (model, iter,
|
|
|
|
COLUMN_SIGNAL_ID, &signal_id,
|
|
|
|
COLUMN_HOOK_ID, &hook_id,
|
|
|
|
-1);
|
|
|
|
|
|
|
|
g_assert (signal_id != 0);
|
|
|
|
|
|
|
|
if (hook_id != 0)
|
|
|
|
{
|
|
|
|
g_signal_remove_emission_hook (signal_id, hook_id);
|
|
|
|
gtk_list_store_set (GTK_LIST_STORE (model), iter,
|
|
|
|
COLUMN_HOOK_ID, 0,
|
|
|
|
-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
start_tracing (GtkInspectorSignalsList *sl)
|
|
|
|
{
|
|
|
|
sl->priv->tracing = TRUE;
|
|
|
|
gtk_tree_model_foreach (GTK_TREE_MODEL (sl->priv->model), start_tracing_cb, sl);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
stop_tracing (GtkInspectorSignalsList *sl)
|
|
|
|
{
|
|
|
|
sl->priv->tracing = FALSE;
|
|
|
|
gtk_tree_model_foreach (GTK_TREE_MODEL (sl->priv->model), stop_tracing_cb, sl);
|
|
|
|
gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (sl->priv->trace_button), FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
toggle_tracing (GtkToggleToolButton *button, GtkInspectorSignalsList *sl)
|
|
|
|
{
|
|
|
|
if (gtk_toggle_tool_button_get_active (button) == sl->priv->tracing)
|
|
|
|
return;
|
|
|
|
|
|
|
|
//gtk_widget_show (sl->priv->log_win);
|
|
|
|
|
|
|
|
if (gtk_toggle_tool_button_get_active (button))
|
|
|
|
start_tracing (sl);
|
|
|
|
else
|
|
|
|
stop_tracing (sl);
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
clear_log_cb (GtkTreeModel *model,
|
|
|
|
GtkTreePath *path,
|
|
|
|
GtkTreeIter *iter,
|
|
|
|
gpointer data)
|
|
|
|
{
|
|
|
|
gtk_list_store_set (GTK_LIST_STORE (model), iter, COLUMN_COUNT, 0, -1);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
clear_log (GtkToolButton *button, GtkInspectorSignalsList *sl)
|
|
|
|
{
|
|
|
|
gtk_text_buffer_set_text (sl->priv->text, "", -1);
|
2014-05-08 05:04:16 +00:00
|
|
|
|
2014-05-10 14:40:38 +00:00
|
|
|
gtk_tree_model_foreach (GTK_TREE_MODEL (sl->priv->model), clear_log_cb, sl);
|
2014-05-08 05:04:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gtk_inspector_signals_list_class_init (GtkInspectorSignalsListClass *klass)
|
|
|
|
{
|
|
|
|
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
|
|
|
|
|
|
|
|
gtk_widget_class_set_template_from_resource (widget_class, "/org/gtk/inspector/signals-list.ui");
|
|
|
|
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorSignalsList, view);
|
|
|
|
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorSignalsList, model);
|
2014-05-10 14:40:38 +00:00
|
|
|
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorSignalsList, text);
|
|
|
|
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorSignalsList, log_win);
|
|
|
|
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorSignalsList, count_column);
|
|
|
|
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorSignalsList, count_renderer);
|
|
|
|
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorSignalsList, trace_button);
|
2014-05-20 16:33:19 +00:00
|
|
|
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorSignalsList, clear_button);
|
2014-05-10 14:40:38 +00:00
|
|
|
gtk_widget_class_bind_template_callback (widget_class, toggle_tracing);
|
|
|
|
gtk_widget_class_bind_template_callback (widget_class, clear_log);
|
2014-05-08 05:04:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// vim: set et sw=2 ts=2:
|