Commit 9d5780dc authored by Juan Pablo Ugarte's avatar Juan Pablo Ugarte

GladePreviewer: show handler information in infobar when a signal is emited.

Added --print-handler option (print handlers info on stdout)
parent 52700db4
......@@ -32,6 +32,9 @@ typedef struct
GTypeInfo info;
GString *template_string;
GBytes *template_data;
GtkBuilderConnectFunc connect_func;
gpointer connect_data;
gint count;
} TypeData;
......@@ -61,8 +64,13 @@ static void
template_class_init (gpointer g_class, gpointer user_data)
{
TypeData *data = user_data;
gtk_widget_class_set_template (g_class, data->template_data);
gtk_widget_class_set_connect_func (g_class, template_connect_function, NULL, NULL);
if (data->connect_func && data->connect_data)
gtk_widget_class_set_connect_func (g_class, data->connect_func, data->connect_data, NULL);
else
gtk_widget_class_set_connect_func (g_class, template_connect_function, NULL, NULL);
}
static GQuark type_data_quark = 0;
......@@ -70,7 +78,9 @@ static GQuark type_data_quark = 0;
static GType
template_generate_type (const gchar *name,
const gchar *parent_name,
GString *template_string)
GString *template_string,
GtkBuilderConnectFunc connect_func,
gpointer connect_data)
{
GType parent_type, retval;
gchar *real_name = NULL;
......@@ -124,6 +134,8 @@ template_generate_type (const gchar *name,
data->info.instance_init = template_init;
data->info.class_data = data;
data->template_data = g_bytes_new_static (template_string->str, template_string->len);
data->connect_func = connect_func;
data->connect_data = connect_data;
retval = g_type_register_static (parent_type, real_name ? real_name : name, &data->info, 0);
......@@ -237,7 +249,10 @@ passthrough (GMarkupParseContext *context,
}
GObject *
glade_preview_template_object_new (const gchar *template_data, gsize len)
glade_preview_template_object_new (const gchar *template_data,
gsize len,
GtkBuilderConnectFunc connect_func,
gpointer connect_data)
{
GMarkupParser parser = { start_element, end_element, text, passthrough};
ParseData state = { FALSE, NULL};
......@@ -264,7 +279,9 @@ glade_preview_template_object_new (const gchar *template_data, gsize len)
{
GType template_type = template_generate_type (state.klass,
state.parent,
state.xml);
state.xml,
connect_func,
connect_data);
if (template_type)
object = g_object_new (template_type, NULL);
else
......
......@@ -29,7 +29,10 @@
G_BEGIN_DECLS
GObject *
glade_preview_template_object_new (const gchar *template_data, gsize len);
glade_preview_template_object_new (const gchar *template_data,
gsize len,
GtkBuilderConnectFunc connect_func,
gpointer connect_data);
G_END_DECLS
......
/*
* glade-preview-window.c
*
* Copyright (C) 2013 Juan Pablo Ugarte
* Copyright (C) 2013-2014 Juan Pablo Ugarte
*
* Author: Juan Pablo Ugarte <juanpablougarte@gmail.com>
*
......@@ -24,6 +24,7 @@
#include "glade-preview-window.h"
#include <glib/gi18n-lib.h>
#include <glib/gprintf.h>
#include <cairo-pdf.h>
#include <cairo-svg.h>
#include <cairo-ps.h>
......@@ -39,6 +40,7 @@ struct _GladePreviewWindowPrivate
GFileMonitor *css_monitor;
gchar *css_file;
gchar *extension;
gboolean print_handlers;
};
G_DEFINE_TYPE_WITH_PRIVATE (GladePreviewWindow, glade_preview_window, GTK_TYPE_WINDOW);
......@@ -77,14 +79,25 @@ glade_preview_window_init (GladePreviewWindow *window)
}
static void
glade_preview_window_finalize (GObject *object)
glade_preview_window_dispose (GObject *object)
{
GladePreviewWindowPrivate *priv = GLADE_PREVIEW_WINDOW (object)->priv;
g_free (priv->css_file);
priv->info = NULL;
g_clear_object (&priv->css_provider);
g_clear_object (&priv->css_monitor);
G_OBJECT_CLASS (glade_preview_window_parent_class)->dispose (object);
}
static void
glade_preview_window_finalize (GObject *object)
{
GladePreviewWindowPrivate *priv = GLADE_PREVIEW_WINDOW (object)->priv;
g_free (priv->css_file);
g_free (priv->extension);
G_OBJECT_CLASS (glade_preview_window_parent_class)->finalize (object);
}
......@@ -158,6 +171,7 @@ glade_preview_window_class_init (GladePreviewWindowClass *klass)
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
object_class->dispose = glade_preview_window_dispose;
object_class->finalize = glade_preview_window_finalize;
widget_class->realize = glade_preview_window_realize;
......@@ -222,6 +236,9 @@ glade_preview_window_set_message (GladePreviewWindow *window,
g_return_if_fail (GLADE_IS_PREVIEW_WINDOW (window));
priv = window->priv;
if (!priv->info)
return;
gtk_info_bar_set_message_type (GTK_INFO_BAR (priv->info), type);
if (message)
......@@ -551,3 +568,220 @@ glade_preview_window_slideshow_save (GladePreviewWindow *window,
else
g_warning ("Could not save slideshow to %s", filename);
}
/**
* glade_preview_window_set_print_handlers:
* @window: A GladePreviewWindow
* @print: whether to print handlers or not
*
* Set whether to print handlers when they are activated or not.
* It only works if you use glade_preview_window_connect_function() as the
* connect funtion.
*/
void
glade_preview_window_set_print_handlers (GladePreviewWindow *window,
gboolean print)
{
g_return_if_fail (GLADE_IS_PREVIEW_WINDOW (window));
window->priv->print_handlers = print;
}
typedef struct
{
gchar *handler_name;
GObject *connect_object;
GConnectFlags flags;
} HandlerData;
typedef struct
{
GladePreviewWindow *window;
gint n_invocations;
GSignalQuery query;
GObject *object;
GList *handlers;
} SignalData;
static void
handler_data_free (gpointer udata)
{
HandlerData *hd = udata;
g_clear_object (&hd->connect_object);
g_free (hd->handler_name);
g_free (hd);
}
static void
signal_data_free (gpointer udata, GClosure *closure)
{
SignalData *data = udata;
g_list_free_full (data->handlers, handler_data_free);
data->handlers = NULL;
g_clear_object (&data->window);
g_clear_object (&data->object);
g_free (data);
}
static inline const gchar *
object_get_name (GObject *object)
{
if (GTK_IS_BUILDABLE (object))
return gtk_buildable_get_name (GTK_BUILDABLE (object));
else
return g_object_get_data (object, "gtk-builder-name");
}
static void
glade_handler_append (GString *message,
GSignalQuery *query,
const gchar *object,
GList *handlers,
gboolean after)
{
GList *l;
for (l = handlers; l; l = g_list_next (l))
{
HandlerData *hd = l->data;
gboolean handler_after = (hd->flags & G_CONNECT_AFTER);
gboolean swapped = (hd->flags & G_CONNECT_SWAPPED);
GObject *obj = hd->connect_object;
gint i;
if ((after && !handler_after) || (!after && handler_after))
continue;
g_string_append_printf (message, "\n\t-> %s%s %s (%s%s%s",
g_type_name (query->return_type),
g_type_is_a (query->return_type, G_TYPE_OBJECT) ? " *" : "",
hd->handler_name,
(swapped) ? ((obj) ? G_OBJECT_TYPE_NAME (obj) : "") : g_type_name (query->itype),
(swapped) ? ((obj) ? " *" : "") : " *",
(swapped) ? ((obj) ? object_get_name (obj) : _("user_data")) : object);
for (i = 1; i < query->n_params; i++)
g_string_append_printf (message, ", %s%s",
g_type_name (query->param_types[i]),
g_type_is_a (query->param_types[i], G_TYPE_OBJECT) ? " *" : "");
g_string_append_printf (message, ", %s%s%s); ",
(swapped) ? g_type_name (query->itype) : ((obj) ? G_OBJECT_TYPE_NAME (obj) : ""),
(swapped) ? " *" : ((obj) ? " *" : ""),
(swapped) ? object : ((obj) ? object_get_name (obj) : _("user_data")));
if (swapped && after)
/* translators: GConnectFlags values */
g_string_append (message, _("Swapped | After"));
else if (swapped)
/* translators: GConnectFlags value */
g_string_append (message, _("Swapped"));
else if (after)
/* translators: GConnectFlags value */
g_string_append (message, _("After"));
}
}
static inline void
glade_handler_method_append (GString *msg, GSignalQuery *q, const gchar *flags)
{
g_string_append_printf (msg, "\n\t%sClass->%s(); %s", g_type_name (q->itype),
q->signal_name, flags);
}
static void
on_handler_called (SignalData *data)
{
GSignalQuery *query = &data->query;
GObject *object = data->object;
const gchar *object_name = object_get_name (object);
GString *message = g_string_new ("");
/* translators: this will be shown in glade previewer when a signal %s::%s is emited %d times */
g_string_append_printf (message, _("%s::%s emited %d time(s)"),
G_OBJECT_TYPE_NAME (object), query->signal_name,
++data->n_invocations);
if (query->signal_flags & G_SIGNAL_RUN_FIRST)
glade_handler_method_append (message, query, _("Run First"));
glade_handler_append (message, query, object_name, data->handlers, FALSE);
if (query->signal_flags & G_SIGNAL_RUN_LAST)
glade_handler_method_append (message, query, _("Run Last"));
glade_handler_append (message, query, object_name, data->handlers, TRUE);
if (query->signal_flags & G_SIGNAL_RUN_CLEANUP)
glade_handler_method_append (message, query, _("Run Cleanup"));
glade_preview_window_set_message (data->window, GTK_MESSAGE_INFO, message->str);
if (data->window->priv->print_handlers)
g_printf ("\n%s\n", message->str);
g_string_free (message, TRUE);
}
/**
* glade_preview_window_connect_function:
* @builder:
* @object:
* @signal_name:
* @handler_name:
* @connect_object:
* @flags:
* @window: a #GladePreviewWindow
*
* Function that collects every signal handler in @builder and shows them
* in @window info bar when the callback is activated
*/
void
glade_preview_window_connect_function (GtkBuilder *builder,
GObject *object,
const gchar *signal_name,
const gchar *handler_name,
GObject *connect_object,
GConnectFlags flags,
gpointer window)
{
SignalData *data;
HandlerData *hd;
guint signal_id;
gchar *key;
g_return_if_fail (GLADE_IS_PREVIEW_WINDOW (window));
if (!(signal_id = g_signal_lookup (signal_name, G_OBJECT_TYPE (object))))
return;
key = g_strconcat ("glade-signal-data-", signal_name, NULL);
data = g_object_get_data (object, key);
if (!data)
{
data = g_new0 (SignalData, 1);
data->window = g_object_ref (window);
g_signal_query (signal_id, &data->query);
data->object = g_object_ref (object);
g_signal_connect_data (object, signal_name,
G_CALLBACK (on_handler_called),
data, signal_data_free, G_CONNECT_SWAPPED);
g_object_set_data (object, key, data);
}
hd = g_new0 (HandlerData, 1);
hd->handler_name = g_strdup (handler_name);
hd->connect_object = connect_object ? g_object_ref (connect_object) : NULL;
hd->flags = flags;
data->handlers = g_list_append (data->handlers, hd);
g_free (key);
}
......@@ -59,6 +59,9 @@ GtkWidget *glade_preview_window_new (void);
void glade_preview_window_set_widget (GladePreviewWindow *window,
GtkWidget *widget);
void glade_preview_window_set_print_handlers (GladePreviewWindow *window,
gboolean print);
void glade_preview_window_set_message (GladePreviewWindow *window,
GtkMessageType type,
const gchar *message);
......@@ -76,6 +79,14 @@ void glade_preview_window_screenshot (GladePreviewWindow *window,
void glade_preview_window_slideshow_save (GladePreviewWindow *window,
const gchar *filename);
void glade_preview_window_connect_function (GtkBuilder *builder,
GObject *object,
const gchar *signal_name,
const gchar *handler_name,
GObject *connect_object,
GConnectFlags flags,
gpointer window);
G_END_DECLS
#endif /* _GLADE_PREVIEW_WINDOW_H_ */
......@@ -96,7 +96,7 @@ static GObject *
get_toplevel_from_string (GladePreviewer *app, gchar *name, gchar *string, gsize size)
{
gchar *wd = NULL;
GObject *retval;
GObject *retval = NULL;
/* We need to change the working directory so builder get a chance to load resources */
if (app->file_name)
......@@ -110,7 +110,10 @@ get_toplevel_from_string (GladePreviewer *app, gchar *name, gchar *string, gsize
/* We use template flag as a hint since the user can turn on and off template
* while the preview is live.
*/
retval = (app->is_template) ? glade_preview_template_object_new (string, size) : NULL;
if (app->is_template)
retval = glade_preview_template_object_new (string, size,
glade_preview_window_connect_function,
app->window);
if (!retval)
{
......@@ -121,11 +124,18 @@ get_toplevel_from_string (GladePreviewer *app, gchar *name, gchar *string, gsize
app->is_template = FALSE;
if (gtk_builder_add_from_string (builder, string, size, &error))
retval = get_toplevel (builder, name);
{
gtk_builder_connect_signals_full (builder,
glade_preview_window_connect_function,
app->window);
retval = get_toplevel (builder, name);
}
else
{
if (error->code == GTK_BUILDER_ERROR_UNHANDLED_TAG &&
(retval = glade_preview_template_object_new (string, size)))
(retval = glade_preview_template_object_new (string, size, NULL, NULL)
glade_preview_window_connect_function,
app->window)))
{
/* At this point we know it is a template, so keep a hint for next time */
app->is_template = TRUE;
......@@ -367,6 +377,7 @@ static gboolean listen = FALSE;
static gboolean version = FALSE;
static gboolean slideshow = FALSE;
static gboolean template = FALSE;
static gboolean print_handler = FALSE;
static gchar *file_name = NULL;
static gchar *toplevel_name = NULL;
static gchar *css_file_name = NULL;
......@@ -381,6 +392,7 @@ static GOptionEntry option_entries[] =
{"css", 0, 0, G_OPTION_ARG_FILENAME, &css_file_name, N_("CSS file to use"), NULL},
{"listen", 'l', 0, G_OPTION_ARG_NONE, &listen, N_("Listen standard input"), NULL},
{"slideshow", 0, 0, G_OPTION_ARG_NONE, &slideshow, N_("make a slideshow of every toplevel widget by adding them in a GtkStack"), NULL},
{"print-handler", 0, 0, G_OPTION_ARG_NONE, &print_handler, N_("Print handlers signature on invocation"), NULL},
{"version", 'v', 0, G_OPTION_ARG_NONE, &version, N_("Display previewer version"), NULL},
{NULL}
};
......@@ -432,7 +444,12 @@ main (int argc, char **argv)
app = glade_previewer_new (file_name, toplevel_name);
gtk_widget_show (GTK_WIDGET (app->window));
app->is_template = template;
if (print_handler)
glade_preview_window_set_print_handlers (GLADE_PREVIEW_WINDOW (app->window), TRUE);
if (css_file_name)
glade_preview_window_set_css_file (app->window, css_file_name);
......@@ -444,8 +461,6 @@ main (int argc, char **argv)
GIOChannel *input = g_io_channel_unix_new (fileno (stdin));
#endif
app->is_template = template;
g_io_add_watch (input, G_IO_IN | G_IO_HUP, on_data_incoming, app);
gtk_main ();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment