[glade] GladePreviewer: show handler information in infobar when a signal is emited. Added --print-handler o



commit 9d5780dc7797195e3c004cc5ec4c4b3b33d2118e
Author: Juan Pablo Ugarte <juanpablougarte gmail com>
Date:   Fri May 9 14:28:32 2014 -0300

    GladePreviewer: show handler information in infobar when a signal is emited.
    Added --print-handler option (print handlers info on stdout)

 gladeui/glade-preview-template.c |   25 ++++-
 gladeui/glade-preview-template.h |    5 +-
 gladeui/glade-preview-window.c   |  240 +++++++++++++++++++++++++++++++++++++-
 gladeui/glade-preview-window.h   |   11 ++
 gladeui/glade-previewer.c        |   29 ++++-
 5 files changed, 295 insertions(+), 15 deletions(-)
---
diff --git a/gladeui/glade-preview-template.c b/gladeui/glade-preview-template.c
index 1f9cdb8..b7d18a9 100644
--- a/gladeui/glade-preview-template.c
+++ b/gladeui/glade-preview-template.c
@@ -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
diff --git a/gladeui/glade-preview-template.h b/gladeui/glade-preview-template.h
index 9c5fda9..14cd759 100644
--- a/gladeui/glade-preview-template.h
+++ b/gladeui/glade-preview-template.h
@@ -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
 
diff --git a/gladeui/glade-preview-window.c b/gladeui/glade-preview-window.c
index cb2bfd6..651bc95 100644
--- a/gladeui/glade-preview-window.c
+++ b/gladeui/glade-preview-window.c
@@ -1,7 +1,7 @@
 /*
  * 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);
+}
diff --git a/gladeui/glade-preview-window.h b/gladeui/glade-preview-window.h
index 0ff2bdc..56276e7 100644
--- a/gladeui/glade-preview-window.h
+++ b/gladeui/glade-preview-window.h
@@ -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_ */
diff --git a/gladeui/glade-previewer.c b/gladeui/glade-previewer.c
index dc26cf8..9f51057 100644
--- a/gladeui/glade-previewer.c
+++ b/gladeui/glade-previewer.c
@@ -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 ();


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