[glade/glade-3-20] glade-previewer: adapted GladePreviewWindow as GladePreviewer to not longer pack toplevels inside a



commit 565a89cb3384792b6b2814c98adbb979173cd1bd
Author: Juan Pablo Ugarte <juanpablougarte gmail com>
Date:   Fri Apr 8 22:25:57 2016 -0300

    glade-previewer: adapted GladePreviewWindow as GladePreviewer to not longer pack toplevels inside a window
    
    This avoids bug #761651 "[Wayland] glade previewer is resizing windows until it crashes gnome-shell"

 gladeui/Makefile.am            |    4 +-
 gladeui/glade-preview-window.c |  794 ----------------------------
 gladeui/glade-preview-window.h |   92 ----
 gladeui/glade-previewer-main.c |  447 ++++++++++++++++
 gladeui/glade-previewer.c      | 1140 +++++++++++++++++++++++++---------------
 gladeui/glade-previewer.h      |   97 ++++
 6 files changed, 1267 insertions(+), 1307 deletions(-)
---
diff --git a/gladeui/Makefile.am b/gladeui/Makefile.am
index ab256c2..7510084 100644
--- a/gladeui/Makefile.am
+++ b/gladeui/Makefile.am
@@ -22,8 +22,8 @@ glade_previewer_LDFLAGS = $(AM_LDFLAGS)
 glade_previewer_LDADD = libgladeui-2.la $(GTK_MAC_LIBS)
 
 glade_previewer_SOURCES = \
+       glade-previewer-main.c \
        glade-previewer.c \
-       glade-preview-window.c \
        glade-preview-template.c
 
 if NATIVE_WIN32
@@ -197,7 +197,7 @@ noinst_HEADERS = \
        glade-popup.h \
        glade-preview.h \
        glade-preview-tokens.h \
-       glade-preview-window.h \
+       glade-previewer.h \
        glade-preview-template.h \
        glade-private.h \
        glade-project-properties.h \
diff --git a/gladeui/glade-previewer-main.c b/gladeui/glade-previewer-main.c
new file mode 100644
index 0000000..5dab4ac
--- /dev/null
+++ b/gladeui/glade-previewer-main.c
@@ -0,0 +1,447 @@
+/*
+ * Copyright (C) 2010 Marco Diego Aurélio Mesquita
+ *
+ * This program 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 program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Authors:
+ *   Marco Diego Aurélio Mesquita <marcodiegomesquita gmail com>
+ */
+
+#include <config.h>
+
+#include <gladeui/glade.h>
+
+#include <stdlib.h>
+#include <locale.h>
+#include <glib/gi18n-lib.h>
+#include <glib/gstdio.h>
+
+#include "glade-previewer.h"
+#include "glade-preview-template.h"
+#include "glade-preview-tokens.h"
+
+typedef struct
+{
+  GladePreviewer *preview;
+  gchar *file_name, *toplevel;
+  gboolean is_template;
+} GladePreviewerApp;
+
+static GObject *
+get_toplevel (GtkBuilder *builder, gchar *name)
+{
+  GObject *toplevel = NULL;
+  GObject *object;
+
+  if (name == NULL)
+    {
+      GSList *l, *objects = gtk_builder_get_objects (builder);
+
+      /* Iterate trough objects and search for a window or widget */
+      for (l = objects; l; l = g_slist_next (l))
+        {
+          GObject *obj = l->data;
+
+          if (!GTK_IS_WIDGET (obj) || gtk_widget_get_parent (GTK_WIDGET (obj)))
+            continue;
+
+          if (toplevel == NULL)
+            toplevel = obj;
+          else if (GTK_IS_WINDOW (obj))
+            toplevel = obj;
+        }
+
+      g_slist_free (objects);
+      if (toplevel == NULL)
+        {
+          g_printerr (_("UI definition has no previewable widgets.\n"));
+          exit (1);
+        }
+    }
+  else
+    {
+      object = gtk_builder_get_object (builder, name);
+
+      if (object == NULL)
+        {
+          g_printerr (_("Object %s not found in UI definition.\n"), name);
+          exit (1);
+        }
+
+      if (!GTK_IS_WIDGET (object))
+        {
+          g_printerr (_("Object is not previewable.\n"));
+          exit (1);
+        }
+
+      toplevel = object;
+    }
+
+  return g_object_ref_sink (toplevel);
+}
+
+static GObject *
+get_toplevel_from_string (GladePreviewerApp *app, gchar *name, gchar *string, gsize size)
+{
+  gchar *wd = NULL;
+  GObject *retval = NULL;
+
+  /* We need to change the working directory so builder get a chance to load resources */
+  if (app->file_name)
+    {
+      gchar *dirname = g_path_get_dirname (app->file_name);
+      wd = g_get_current_dir ();
+      g_chdir (dirname);
+      g_free (dirname);
+    }
+
+  /* We use template flag as a hint since the user can turn on and off template
+   * while the preview is live.
+   */
+  if (app->is_template)
+    retval = glade_preview_template_object_new (string, size,
+                                                  glade_previewer_connect_function,
+                                                  app->preview);
+
+  if (!retval)
+    {
+      GtkBuilder *builder = gtk_builder_new ();
+      GError *error = NULL;
+
+      /* We do not know if its a template yet */
+      app->is_template = FALSE;
+
+      if (gtk_builder_add_from_string (builder, string, size, &error))
+        {
+          gtk_builder_connect_signals_full (builder,
+                                            glade_previewer_connect_function,
+                                            app->preview);
+          retval = get_toplevel (builder, name);
+        }
+      else
+        {
+          if (error->code == GTK_BUILDER_ERROR_UNHANDLED_TAG &&
+              (retval = glade_preview_template_object_new (string, size,
+                                                             glade_previewer_connect_function,
+                                                             app->preview)))
+            {
+              /* At this point we know it is a template, so keep a hint for next time */
+              app->is_template = TRUE;
+            }
+          else
+            {
+              gchar *message = g_strdup_printf (_("Couldn't load builder definition: %s"), error->message);
+              glade_previewer_set_message (app->preview, GTK_MESSAGE_ERROR, message);
+              g_free (message);
+            }
+
+          g_error_free (error);
+        }
+
+      g_object_unref (builder);
+    }
+
+  /* restore directory */
+  if (wd)
+    {
+      g_chdir (wd);
+      g_free (wd);
+    }
+  
+  return retval;
+}
+
+static gchar *
+read_buffer (GIOChannel * source)
+{
+  gchar *buffer;
+  gchar *token;
+  gchar *tmp;
+  GError *error = NULL;
+
+  if (g_io_channel_read_line (source, &token, NULL, NULL, &error) !=
+      G_IO_STATUS_NORMAL)
+    {
+      g_printerr (_("Error: %s.\n"), error->message);
+      g_error_free (error);
+      exit (1);
+    }
+
+  /* Check for quit token */
+  if (g_strcmp0 (QUIT_TOKEN, token) == 0)
+    {
+      g_free (token);
+      return NULL;
+    }
+
+  /* Loop to load the UI */
+  buffer = g_strdup (token);
+  do
+    {
+      g_free (token);
+      if (g_io_channel_read_line (source, &token, NULL, NULL, &error) !=
+          G_IO_STATUS_NORMAL)
+        {
+          g_printerr (_("Error: %s.\n"), error->message);
+          g_error_free (error);
+          exit (1);
+        }
+      tmp = buffer;
+      buffer = g_strconcat (buffer, token, NULL);
+      g_free (tmp);
+    }
+  while (g_strcmp0 ("</interface>\n", token) != 0);
+  g_free (token);
+
+  return buffer;
+}
+
+static gboolean
+on_data_incoming (GIOChannel *source, GIOCondition condition, gpointer data)
+{
+  GladePreviewerApp *app = data;
+  GObject *new_widget;
+  gchar *buffer;
+
+  buffer = read_buffer (source);
+  if (buffer == NULL)
+    {
+      gtk_main_quit ();
+      return FALSE;
+    }
+
+  if (condition & G_IO_HUP)
+    {
+      g_printerr (_("Broken pipe!\n"));
+      exit (1);
+    }
+
+  /* We have an update */
+  if (g_str_has_prefix (buffer, UPDATE_TOKEN))
+    {
+     gchar **split_buffer = g_strsplit_set (buffer + UPDATE_TOKEN_SIZE, "\n", 2);
+
+      if (!split_buffer)
+        {
+          g_free (buffer);
+          return FALSE;
+        }
+
+      new_widget = get_toplevel_from_string (app, split_buffer[0], split_buffer[1], -1);
+
+      g_strfreev (split_buffer);
+    }
+  else
+    {
+      new_widget = get_toplevel_from_string (app, app->toplevel, buffer, -1);
+    }
+
+  if (new_widget)
+    {
+      glade_previewer_set_widget (app->preview, GTK_WIDGET (new_widget));
+      gtk_widget_show (GTK_WIDGET (new_widget));
+    }
+
+  glade_previewer_present (app->preview);
+  
+  g_free (buffer);
+  
+  return TRUE;
+}
+
+static GladePreviewerApp *
+glade_previewer_app_new (gchar *filename, gchar *toplevel)
+{
+  GladePreviewerApp *app = g_new0 (GladePreviewerApp, 1);
+
+  app->preview = GLADE_PREVIEWER (glade_previewer_new ());
+  g_object_ref_sink (app->preview);
+
+  app->file_name = g_strdup (filename);
+  app->toplevel = g_strdup (toplevel);
+
+  return app;
+}
+
+static void
+glade_previewer_free (GladePreviewerApp *app)
+{
+  g_object_unref (app->preview);
+  g_free (app->file_name);
+  g_free (app->toplevel);
+  g_free (app);
+}
+
+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;
+static gchar *screenshot_file_name = NULL;
+
+static GOptionEntry option_entries[] =
+{
+    {"filename", 'f', 0, G_OPTION_ARG_FILENAME, &file_name, N_("Name of the file to preview"), "FILENAME"},
+    {"template", 0, 0, G_OPTION_ARG_NONE, &template, N_("Creates dummy widget class to load a template"), 
NULL},
+    {"toplevel", 't', 0, G_OPTION_ARG_STRING, &toplevel_name, N_("Name of the toplevel to preview"), 
"TOPLEVELNAME"},
+    {"screenshot", 0, 0, G_OPTION_ARG_FILENAME, &screenshot_file_name, N_("File name to save a screenshot"), 
NULL},
+    {"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}
+};
+
+int
+main (int argc, char **argv)
+{
+  GladePreviewerApp *app;
+  GOptionContext *context;
+  GError *error = NULL;
+  GObject *toplevel = NULL;
+
+#ifdef ENABLE_NLS
+  setlocale (LC_ALL, "");
+  bindtextdomain (GETTEXT_PACKAGE, glade_app_get_locale_dir ());
+  bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+  textdomain (GETTEXT_PACKAGE);
+#endif
+
+  context = g_option_context_new (_("- previews a glade UI definition"));
+  g_option_context_add_main_entries (context, option_entries, GETTEXT_PACKAGE);
+  g_option_context_add_group (context, gtk_get_option_group (TRUE));
+  
+  if (!g_option_context_parse (context, &argc, &argv, &error))
+    {
+      g_printerr (_("%s\nRun '%s --help' to see a full list of available command line "
+                   "options.\n"), error->message, argv[0]);
+      g_error_free (error);
+      g_option_context_free (context);
+      return 1;
+    }
+
+  g_option_context_free (context);
+
+  if (version)
+    {
+      g_print ("glade-previewer " VERSION "\n");
+      return 0;
+    }
+
+  if (!listen && !file_name)
+    {
+      g_printerr (_("Either --listen or --filename must be specified.\n"));
+      return 0;
+    }
+
+  gtk_init (&argc, &argv);
+  glade_app_get ();
+
+  app = glade_previewer_app_new (file_name, toplevel_name);
+
+  app->is_template = template;
+
+  if (print_handler)
+    glade_previewer_set_print_handlers (GLADE_PREVIEWER (app->preview), TRUE);
+
+  if (css_file_name)
+    glade_previewer_set_css_file (app->preview, css_file_name);
+
+  if (listen)
+    {
+#ifdef WINDOWS
+      GIOChannel *input = g_io_channel_win32_new_fd (fileno (stdin));
+#else
+      GIOChannel *input = g_io_channel_unix_new (fileno (stdin));
+#endif
+
+      g_io_add_watch (input, G_IO_IN | G_IO_HUP, on_data_incoming, app);
+
+      gtk_main ();
+    }
+  else if (template)
+    {
+      gchar *contents = NULL;
+      gsize size;
+
+      if (g_file_get_contents (file_name, &contents, &size, NULL))
+        toplevel = get_toplevel_from_string (app, NULL, contents, size);
+
+      g_free (contents);
+    }
+  else if (file_name)
+    {
+      GtkBuilder *builder = gtk_builder_new ();
+      GError *error = NULL;
+
+      /* Use from_file() function gives builder a chance to know where to load resources from */
+      if (!gtk_builder_add_from_file (builder, app->file_name, &error))
+        {
+          g_printerr (_("Couldn't load builder definition: %s"), error->message);
+          g_error_free (error);
+          return 1;
+        }
+
+      if (slideshow)
+        {
+          GSList *objects = gtk_builder_get_objects (builder);
+
+          glade_previewer_set_slideshow_widgets (app->preview, objects);
+          glade_previewer_present (app->preview);
+
+          if (screenshot_file_name)
+            glade_previewer_slideshow_save (app->preview, screenshot_file_name);
+          else
+            gtk_main ();
+
+          g_slist_free (objects);
+        }
+      else
+        {
+          toplevel = get_toplevel (builder, toplevel_name);
+
+          gtk_builder_connect_signals_full (builder,
+                                            glade_previewer_connect_function,
+                                            app->preview);
+        }
+
+      g_object_unref (builder);
+    }
+
+  if (toplevel)
+    {
+      glade_previewer_set_widget (app->preview, GTK_WIDGET (toplevel));
+      g_object_unref (toplevel);
+      glade_previewer_present (app->preview);
+
+      if (screenshot_file_name)
+        glade_previewer_screenshot (app->preview, TRUE, screenshot_file_name);
+      else
+        gtk_main ();
+    }
+
+  /* free unused resources */
+  g_free (file_name);
+  g_free (toplevel_name);
+  g_free (css_file_name);
+  g_free (screenshot_file_name);
+  glade_previewer_free (app);
+
+  return 0;
+}
diff --git a/gladeui/glade-previewer.c b/gladeui/glade-previewer.c
index 44a2357..8472359 100644
--- a/gladeui/glade-previewer.c
+++ b/gladeui/glade-previewer.c
@@ -1,332 +1,524 @@
 /*
- * Copyright (C) 2010 Marco Diego Aurélio Mesquita
+ * glade-previewer.c
  *
- * This program 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.
+ * Copyright (C) 2013-2016 Juan Pablo Ugarte
+   *
+ * Author: Juan Pablo Ugarte <juanpablougarte gmail com>
  *
- * This program 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 General Public License for more details.
+ * 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.1 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 program; if not, write to the Free Software
+ * You should have received a copy of the GNU Lesser General Public 
+ * License along with this program; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  *
- * Authors:
- *   Marco Diego Aurélio Mesquita <marcodiegomesquita gmail com>
  */
-
 #include <config.h>
 
-#include <gladeui/glade.h>
-
-#include <stdlib.h>
-#include <locale.h>
+#include "glade-previewer.h"
 #include <glib/gi18n-lib.h>
-#include <glib/gstdio.h>
+#include <glib/gprintf.h>
+#include <cairo-pdf.h>
+#include <cairo-svg.h>
+#include <cairo-ps.h>
 
-#include "glade-preview-window.h"
-#include "glade-preview-template.h"
-#include "glade-preview-tokens.h"
+struct _GladePreviewerPrivate
+{
+  GtkWidget *widget;    /* Preview widget */
+  GList     *objects;   /* SlideShow objects */
+  GtkWidget *dialog;    /* Dialog to show messages */
+  GtkWidget *textview;
 
-typedef struct
+  GtkCssProvider *css_provider;
+  GFileMonitor *css_monitor;
+  gchar *css_file;
+  gchar *extension;
+
+  gboolean print_handlers;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (GladePreviewer, glade_previewer, G_TYPE_OBJECT);
+
+static void
+glade_previewer_init (GladePreviewer *preview)
 {
-  GladePreviewWindow *window;
-  gchar *file_name, *toplevel;
-  gboolean is_template;
-} GladePreviewer;
+  GladePreviewerPrivate *priv = glade_previewer_get_instance_private (preview);
+
+  preview->priv = priv;
+}
 
-static GObject *
-get_toplevel (GtkBuilder *builder, gchar *name)
+static void
+glade_previewer_dispose (GObject *object)
 {
-  GObject *toplevel = NULL;
-  GObject *object;
+  GladePreviewerPrivate *priv = GLADE_PREVIEWER (object)->priv;
 
-  if (name == NULL)
-    {
-      GSList *l, *objects = gtk_builder_get_objects (builder);
+  g_list_free (priv->objects);
+  
+  priv->objects = NULL;
+  priv->dialog = NULL;
+  g_clear_object (&priv->css_provider);
+  g_clear_object (&priv->css_monitor);
 
-      /* Iterate trough objects and search for a window or widget */
-      for (l = objects; l; l = g_slist_next (l))
-        {
-          GObject *obj = l->data;
+  G_OBJECT_CLASS (glade_previewer_parent_class)->dispose (object);
+}
 
-          if (!GTK_IS_WIDGET (obj) || gtk_widget_get_parent (GTK_WIDGET (obj)))
-            continue;
+static void
+glade_previewer_finalize (GObject *object)
+{
+  GladePreviewerPrivate *priv = GLADE_PREVIEWER (object)->priv;
 
-          if (toplevel == NULL)
-            toplevel = obj;
-          else if (GTK_IS_WINDOW (obj))
-            toplevel = obj;
-        }
+  g_free (priv->css_file);
+  g_free (priv->extension);
 
-      g_slist_free (objects);
-      if (toplevel == NULL)
-        {
-          g_printerr (_("UI definition has no previewable widgets.\n"));
-          exit (1);
-        }
+  G_OBJECT_CLASS (glade_previewer_parent_class)->finalize (object);
+}
+
+static gboolean 
+on_widget_key_press_event (GtkWidget      *widget,
+                           GdkEventKey    *event,
+                           GladePreviewer *preview)
+{
+  GladePreviewerPrivate *priv = preview->priv;
+  GList *node = NULL;
+  GtkStack *stack;
+  gchar *extension;
+
+  if (priv->objects)
+    {
+      stack = GTK_STACK (gtk_bin_get_child (GTK_BIN (priv->widget)));
+      node = g_list_find (priv->objects, gtk_stack_get_visible_child (stack));
     }
-  else
+
+  switch (event->keyval)
     {
-      object = gtk_builder_get_object (builder, name);
+      case GDK_KEY_Page_Up:
+        if (node && node->prev)
+          gtk_stack_set_visible_child  (stack, node->prev->data);
+        return TRUE;
+      break;
+      case GDK_KEY_Page_Down:
+        if (node && node->next)
+          gtk_stack_set_visible_child  (stack, node->next->data);
+        return TRUE;
+      break;
+      case GDK_KEY_F5:
+        extension = "svg";
+      break;
+      case GDK_KEY_F6:
+        extension = "ps";
+      break;
+      case GDK_KEY_F7:
+        extension = "pdf";
+      break;
+      case GDK_KEY_F8:
+        extension = priv->extension ? priv->extension : "png";
+      break;
+      case GDK_KEY_F11:
+        if (gdk_window_get_state (gtk_widget_get_window (widget)) & GDK_WINDOW_STATE_FULLSCREEN)
+          gtk_window_unfullscreen (GTK_WINDOW (widget));
+        else
+          gtk_window_fullscreen (GTK_WINDOW (widget));
+
+        return TRUE;
+      break;
+      default:
+        return FALSE;
+      break;
+    }
 
-      if (object == NULL)
-        {
-          g_printerr (_("Object %s not found in UI definition.\n"), name);
-          exit (1);
-        }
+  if (extension)
+    {
+      gchar *tmp_file = g_strdup_printf ("glade-screenshot-XXXXXX.%s", extension); 
 
-      if (!GTK_IS_WIDGET (object))
-        {
-          g_printerr (_("Object is not previewable.\n"));
-          exit (1);
-        }
+      g_mkstemp (tmp_file);
+      glade_previewer_screenshot (preview, FALSE, tmp_file);
+      g_free (tmp_file);
 
-      toplevel = object;
+      return TRUE;
     }
 
-  return g_object_ref_sink (toplevel);
+  return FALSE;
 }
 
-static GObject *
-get_toplevel_from_string (GladePreviewer *app, gchar *name, gchar *string, gsize size)
+static void
+glade_previewer_class_init (GladePreviewerClass *klass)
 {
-  gchar *wd = NULL;
-  GObject *retval = NULL;
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
 
-  /* We need to change the working directory so builder get a chance to load resources */
-  if (app->file_name)
-    {
-      gchar *dirname = g_path_get_dirname (app->file_name);
-      wd = g_get_current_dir ();
-      g_chdir (dirname);
-      g_free (dirname);
-    }
+  object_class->dispose = glade_previewer_dispose;
+  object_class->finalize = glade_previewer_finalize;
+}
 
-  /* We use template flag as a hint since the user can turn on and off template
-   * while the preview is live.
-   */
-  if (app->is_template)
-    retval = glade_preview_template_object_new (string, size,
-                                                glade_preview_window_connect_function,
-                                                app->window);
+GObject *
+glade_previewer_new (void)
+{
+  return g_object_new (GLADE_TYPE_PREVIEWER, NULL);
+}
 
-  if (!retval)
-    {
-      GtkBuilder *builder = gtk_builder_new ();
-      GError *error = NULL;
+void
+glade_previewer_present (GladePreviewer *preview)
+{
+  g_return_if_fail (GLADE_IS_PREVIEWER (preview));
+  gtk_window_present (GTK_WINDOW (preview->priv->widget));
+}
 
-      /* We do not know if its a template yet */
-      app->is_template = FALSE;
+void
+glade_previewer_set_widget (GladePreviewer *preview, GtkWidget *widget)
+{
+  GladePreviewerPrivate *priv;
+  GtkWidget *sw;
 
-      if (gtk_builder_add_from_string (builder, string, size, &error))
-        {
-          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,
-                                                           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;
-            }
-          else
-            {
-              gchar *message = g_strdup_printf (_("Couldn't load builder definition: %s"), error->message);
-              glade_preview_window_set_message (app->window, GTK_MESSAGE_ERROR, message);
-              g_free (message);
-            }
+  g_return_if_fail (GLADE_IS_PREVIEWER (preview));
+  g_return_if_fail (GTK_IS_WIDGET (widget));
 
-          g_error_free (error);
-        }
+  priv = preview->priv;
 
-      g_object_unref (builder);
-    }
+  if (priv->widget)
+    gtk_widget_destroy (priv->widget);
 
-  /* restore directory */
-  if (wd)
+  if (!gtk_widget_is_toplevel (widget))
     {
-      g_chdir (wd);
-      g_free (wd);
+      GtkWidget *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+
+      gtk_container_add (GTK_CONTAINER (window), widget);
+
+      priv->widget = window;
     }
-  
-  return retval;
+  else
+    {
+      priv->widget = widget;
+    }
+
+  /* Create dialog to display messages */
+  priv->dialog = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+  gtk_window_set_default_size (GTK_WINDOW (priv->dialog), 640, 320);
+  gtk_window_set_title (GTK_WINDOW (priv->dialog), _("Glade Previewer log"));
+  gtk_window_set_transient_for (GTK_WINDOW (priv->dialog), GTK_WINDOW (priv->widget));
+
+  priv->textview = gtk_text_view_new ();
+  gtk_widget_show (priv->textview);
+
+  sw = gtk_scrolled_window_new (NULL, NULL);
+  gtk_widget_show (sw);
+
+  gtk_container_add (GTK_CONTAINER (sw), priv->textview);
+  gtk_container_add (GTK_CONTAINER (priv->dialog), sw);
+
+  /* Hide dialog on delete event */
+  g_signal_connect (priv->dialog, "delete-event",
+                    G_CALLBACK (gtk_widget_hide),
+                    NULL);
+
+  /* Quit on delete event */
+  g_signal_connect (priv->widget, "delete-event",
+                    G_CALLBACK (gtk_main_quit),
+                    NULL);
+
+  /* Make sure we get press events */
+  gtk_widget_add_events (priv->widget, GDK_KEY_PRESS_MASK);
+
+  /* Handle key presses for screenshot feature */
+  g_signal_connect_object (priv->widget, "key-press-event",
+                           G_CALLBACK (on_widget_key_press_event),
+                           preview, 0);
 }
 
-static gchar *
-read_buffer (GIOChannel * source)
+void
+glade_previewer_set_message (GladePreviewer *preview, 
+                             GtkMessageType  type,
+                             const gchar    *message)
 {
-  gchar *buffer;
-  gchar *token;
-  gchar *tmp;
-  GError *error = NULL;
+  GladePreviewerPrivate *priv;
+  GtkTextBuffer *buffer;
 
-  if (g_io_channel_read_line (source, &token, NULL, NULL, &error) !=
-      G_IO_STATUS_NORMAL)
-    {
-      g_printerr (_("Error: %s.\n"), error->message);
-      g_error_free (error);
-      exit (1);
-    }
+  g_return_if_fail (GLADE_IS_PREVIEWER (preview));
+  priv = preview->priv;
 
-  /* Check for quit token */
-  if (g_strcmp0 (QUIT_TOKEN, token) == 0)
+  if (!priv->textview)
+    return;
+
+  buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->textview));
+  
+  if (message)
     {
-      g_free (token);
-      return NULL;
+      GtkTextIter iter;
+
+      /* TODO: use message type to color text */
+      gtk_text_buffer_get_start_iter (buffer, &iter);
+      gtk_text_buffer_insert (buffer, &iter, "\n", -1);
+
+      gtk_text_buffer_get_start_iter (buffer, &iter);
+      gtk_text_buffer_insert (buffer, &iter, message, -1);
+
+      gtk_window_present (GTK_WINDOW (priv->dialog));
     }
+}
+
+static void 
+on_css_monitor_changed (GFileMonitor       *monitor,
+                        GFile              *file,
+                        GFile              *other_file,
+                        GFileMonitorEvent   event_type,
+                        GladePreviewer     *preview)
+{
+  GladePreviewerPrivate *priv = preview->priv;
+  GError *error = NULL;
+
+  gtk_css_provider_load_from_file (priv->css_provider, file, &error);
 
-  /* Loop to load the UI */
-  buffer = g_strdup (token);
-  do
+  if (error)
     {
-      g_free (token);
-      if (g_io_channel_read_line (source, &token, NULL, NULL, &error) !=
-          G_IO_STATUS_NORMAL)
-        {
-          g_printerr (_("Error: %s.\n"), error->message);
-          g_error_free (error);
-          exit (1);
-        }
-      tmp = buffer;
-      buffer = g_strconcat (buffer, token, NULL);
-      g_free (tmp);
+      glade_previewer_set_message (preview, GTK_MESSAGE_WARNING, error->message);
+      g_error_free (error);
     }
-  while (g_strcmp0 ("</interface>\n", token) != 0);
-  g_free (token);
-
-  return buffer;
+  else
+    glade_previewer_set_message (preview, GTK_MESSAGE_OTHER, NULL);
 }
 
-static void
-glade_previewer_window_set_title (GtkWindow *window, 
-                                  gchar *filename,
-                                  gchar *toplevel)
+void
+glade_previewer_set_css_file (GladePreviewer *preview,
+                            const gchar  *css_file)
 {
-  gchar *title, *pretty_path = NULL;
-  const gchar *widget_name = toplevel;
-  if (widget_name && g_str_has_prefix (widget_name, GLADE_UNNAMED_PREFIX))
-    widget_name = _("(unnamed)");
+  GladePreviewerPrivate *priv;
+  GError *error = NULL;
+  GFile *file;
+
+  g_return_if_fail (GLADE_IS_PREVIEWER (preview));
+  priv = preview->priv;
 
-  if (filename && g_path_is_absolute (filename))
+  g_free (priv->css_file);
+  g_clear_object (&priv->css_monitor);
+
+  priv->css_file = g_strdup (css_file);
+  
+  file = g_file_new_for_path (css_file);
+  
+  if (!priv->css_provider)
     {
-      gchar *canonical_path = glade_util_canonical_path (filename); 
-      filename = pretty_path = glade_utils_replace_home_dir_with_tilde (canonical_path);
-      g_free (canonical_path);      
+      priv->css_provider = gtk_css_provider_new ();
+      g_object_ref_sink (priv->css_provider);
+
+      /* Set provider for default screen once */
+      gtk_style_context_add_provider_for_screen (gdk_screen_get_default (),
+                                                 GTK_STYLE_PROVIDER (priv->css_provider),
+                                                 GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
     }
 
-  if (filename)
+  priv->css_monitor = g_file_monitor_file (file, G_FILE_MONITOR_NONE, NULL, &error);
+  if (error)
     {
-      if (toplevel)
-        title = g_strdup_printf (_("Previewing %s - %s"), filename, widget_name);
-      else
-        title = g_strdup_printf (_("Previewing %s"), filename);
+      g_warning ("Cant monitor CSS file %s: %s", css_file, error->message);
+      g_error_free (error);
     }
-  else if (toplevel)
+  else
     {
-      title = g_strdup_printf (_("Previewing %s"), widget_name);
+      g_object_ref_sink (priv->css_monitor);
+      g_signal_connect (priv->css_monitor, "changed",
+                        G_CALLBACK (on_css_monitor_changed), preview);
     }
-  else
+
+  /* load CSS */
+  gtk_css_provider_load_from_file (priv->css_provider, file, &error);
+  if (error)
     {
-      gtk_window_set_title (window, _("Glade Preview"));
-      return;
+      glade_previewer_set_message (preview, GTK_MESSAGE_INFO, error->message);
+      g_message ("%s CSS parsing failed: %s", css_file, error->message);
+      g_error_free (error);
     }
+  
+  g_object_unref (file);
+}
 
-  gtk_window_set_title (window, title);
-  g_free (pretty_path);
-  g_free (title);
+void
+glade_previewer_set_screenshot_extension (GladePreviewer *preview,
+                                          const gchar    *extension)
+{
+  GladePreviewerPrivate *priv;
+
+  g_return_if_fail (GLADE_IS_PREVIEWER (preview));
+  priv = preview->priv;
+
+  g_free (priv->extension);
+  priv->extension = g_strdup (extension);
 }
 
 static gboolean
-on_data_incoming (GIOChannel *source, GIOCondition condition, gpointer data)
+quit_when_idle (gpointer loop)
 {
-  GladePreviewer *app = data;
-  GObject *new_widget;
-  gchar *buffer;
+  g_main_loop_quit (loop);
 
-  buffer = read_buffer (source);
-  if (buffer == NULL)
-    {
-      gtk_main_quit ();
-      return FALSE;
-    }
+  return G_SOURCE_REMOVE;
+}
 
-  if (condition & G_IO_HUP)
+static void
+check_for_draw (GdkEvent *event, gpointer loop)
+{
+  if (event->type == GDK_EXPOSE)
     {
-      g_printerr (_("Broken pipe!\n"));
-      exit (1);
+      g_idle_add (quit_when_idle, loop);
+      gdk_event_handler_set ((GdkEventFunc) gtk_main_do_event, NULL, NULL);
     }
 
-  /* We have an update */
-  if (g_str_has_prefix (buffer, UPDATE_TOKEN))
-    {
-     gchar **split_buffer = g_strsplit_set (buffer + UPDATE_TOKEN_SIZE, "\n", 2);
+  gtk_main_do_event (event);
+}
 
-      if (!split_buffer)
-        {
-          g_free (buffer);
-          return FALSE;
-        }
+/* Taken from Gtk sources gtk-reftest.c  */
+static void
+glade_previewer_wait_for_drawing (GdkWindow *window)
+{
+  GMainLoop *loop;
+
+  loop = g_main_loop_new (NULL, FALSE);
+  /* We wait until the widget is drawn for the first time.
+   * We can not wait for a GtkWidget::draw event, because that might not
+   * happen if the window is fully obscured by windowed child widgets.
+   * Alternatively, we could wait for an expose event on widget's window.
+   * Both of these are rather hairy, not sure what's best. */
+  gdk_event_handler_set (check_for_draw, loop, NULL);
+  g_main_loop_run (loop);
+
+  /* give the WM/server some time to sync. They need it.
+   * Also, do use popups instead of toplevls in your tests
+   * whenever you can. */
+  gdk_display_sync (gdk_window_get_display (window));
+  g_timeout_add (500, quit_when_idle, loop);
+  g_main_loop_run (loop);
+}
 
-      new_widget = get_toplevel_from_string (app, split_buffer[0], split_buffer[1], -1);
-      glade_previewer_window_set_title (GTK_WINDOW (app->window), app->file_name,
-                                        split_buffer[0]);
+static const gchar *
+glade_previewer_get_extension (const gchar *filename)
+{
+  gchar *extension;
+  
+  g_return_val_if_fail (filename != NULL, NULL);
 
-      g_strfreev (split_buffer);
-    }
-  else
-    {
-      new_widget = get_toplevel_from_string (app, app->toplevel, buffer, -1);
-      glade_previewer_window_set_title (GTK_WINDOW (app->window), app->file_name, app->toplevel);
-    }
+  extension = g_strrstr (filename,".");
 
-  if (new_widget)
+  if (extension)
+    extension++;
+
+  if (!extension)
     {
-      glade_preview_window_set_widget (app->window, GTK_WIDGET (new_widget));
-      gtk_widget_show (GTK_WIDGET (new_widget));
+      g_warning ("%s has no extension!", filename);
+      return NULL;
     }
+  return extension;
+}
 
-  gtk_window_present (GTK_WINDOW (app->window));
-  
-  g_free (buffer);
-  
-  return TRUE;
+static void
+glade_previewer_get_scale (GdkScreen *screen, gdouble *sx, gdouble *sy)
+{
+  if (sx)
+    *sx = 72.0 / (gdk_screen_get_width (screen) / (gdk_screen_get_width_mm (screen) * 0.03937008));
+
+  if (sy)
+    *sy = 72.0 / (gdk_screen_get_height (screen) / (gdk_screen_get_height_mm (screen) * 0.03937008));
 }
 
-static GladePreviewer *
-glade_previewer_new (gchar *filename, gchar *toplevel)
+static cairo_surface_t *
+glade_previewer_surface_from_file (const gchar *filename, gdouble w, gdouble h)
 {
-  GladePreviewer *app = g_new0 (GladePreviewer, 1);
+  cairo_surface_t *surface;
+  const gchar *extension;
 
-  app->window = GLADE_PREVIEW_WINDOW (glade_preview_window_new ());
-  g_object_ref_sink (app->window);
+  extension = glade_previewer_get_extension (filename);
   
-  g_signal_connect (app->window, "delete-event", G_CALLBACK (gtk_main_quit), NULL);
-  glade_previewer_window_set_title (GTK_WINDOW (app->window), filename, toplevel);
-   
-  app->file_name = g_strdup (filename);
-  app->toplevel = g_strdup (toplevel);
+  if (extension == NULL)
+    return NULL;
+
+  if (g_strcmp0 (extension, "svg") == 0)
+#if CAIRO_HAS_SVG_SURFACE
+    surface = cairo_svg_surface_create (filename, w, h);
+#else
+    g_warning ("PDF not supported by the cairo version used");
+#endif
+  else if (g_strcmp0 (extension, "ps") == 0)
+#if CAIRO_HAS_PS_SURFACE
+    surface = cairo_ps_surface_create (filename, w, h);
+#else
+    g_warning ("PS not supported by the cairo version used");
+#endif
+  else if (g_strcmp0 (extension, "pdf") == 0)
+#if CAIRO_HAS_PDF_SURFACE
+    surface = cairo_pdf_surface_create (filename, w, h);
+#else
+    g_warning ("PDF not supported by the cairo version used");
+#endif
+  else
+    return NULL;
 
-  return app;
+  return surface;
 }
 
-static void
-glade_previewer_free (GladePreviewer *app)
+/**
+ * glade_previewer_screenshot:
+ * @preview: A GladePreviewer
+ * @wait: True if it should wait for widget to draw.
+ * @filename:  a filename to save the image.
+ * 
+ * Takes a screenshot of the current widget @window is showing and save it to @filename
+ * Supported extension are svg, ps, pdf and wahtever gdk-pixbuf supports 
+ */
+void
+glade_previewer_screenshot (GladePreviewer *preview,
+                            gboolean        wait,
+                            const gchar    *filename)
 {
-  g_object_unref (app->window);
-  g_free (app->file_name);
-  g_free (app->toplevel);
-  g_free (app);
+  GladePreviewerPrivate *priv;
+  cairo_surface_t *surface;
+  GdkWindow *gdkwindow;
+  GdkScreen *screen;
+  gdouble sx, sy;
+  gint w, h;
+
+  g_return_if_fail (GLADE_IS_PREVIEWER (preview));
+  g_return_if_fail (filename != NULL);
+  priv = preview->priv;
+
+  if (!priv->widget)
+    return;
+
+  gdkwindow = gtk_widget_get_window (priv->widget);
+  screen = gdk_window_get_screen (gdkwindow);
+
+  if (wait)
+    glade_previewer_wait_for_drawing (gdkwindow);
+
+  w = gtk_widget_get_allocated_width (priv->widget);
+  h = gtk_widget_get_allocated_height (priv->widget);
+  glade_previewer_get_scale (screen, &sx, &sy);
+    
+  surface = glade_previewer_surface_from_file (filename, w*sx, h*sy);
+
+  if (surface)
+    {
+      cairo_t *cr = cairo_create (surface);
+      cairo_scale (cr, sx, sy);
+      gtk_widget_draw (priv->widget, cr);
+      cairo_destroy (cr);
+      cairo_surface_destroy(surface);
+    }
+  else
+    {
+      GdkPixbuf *pix = gdk_pixbuf_get_from_window (gdkwindow, 0, 0, w, h);
+      const gchar *ext = glade_previewer_get_extension (filename);
+      GError *error = NULL;
+      
+      if (!gdk_pixbuf_save (pix, filename, ext ? ext : "png", &error, NULL))
+        {
+          g_warning ("Could not save screenshot to %s because %s", filename, error->message);
+          g_error_free (error);
+        }
+
+      g_object_unref (pix);
+    }
 }
 
 static gint
@@ -337,231 +529,341 @@ objects_cmp_func (gconstpointer a, gconstpointer b)
   name_b = gtk_buildable_get_name (GTK_BUILDABLE (b));
   return g_strcmp0 (name_a, name_b);
 }
-static gboolean 
-glade_previewer_stack_key_press_event (GtkWidget *window, GdkEventKey *event, GtkWidget *widget)
+
+/**
+ * glade_previewer_set_slideshow_widgets:
+ * @preview: A GladePreviewer
+ * @objects: GSlist of GObject
+ * 
+ * Add a list of objects to slideshow
+ */
+void
+glade_previewer_set_slideshow_widgets (GladePreviewer *preview,
+                                      GSList          *objects)
 {
-  GtkWidget *child =  gtk_stack_get_visible_child  (GTK_STACK (widget));
-  GList *children, *node;
-  gboolean retval = FALSE;
-  
-  if (!child)
-    return FALSE;
+  GladePreviewerPrivate *priv;
+  GtkStack *stack;
+  GSList *l;
 
-  children = gtk_container_get_children (GTK_CONTAINER (widget));
+  g_return_if_fail (GLADE_IS_PREVIEWER (preview));
+  priv = preview->priv;
 
-  node = g_list_find (children, child);
+  stack = GTK_STACK (gtk_stack_new ());
+  gtk_stack_set_transition_type (stack, GTK_STACK_TRANSITION_TYPE_CROSSFADE);
 
-  if (node)
+  objects = g_slist_sort (g_slist_copy (objects), objects_cmp_func);
+
+  for (l = objects; l; l = g_slist_next (l))
     {
-      switch (event->keyval)
-        {
-          case GDK_KEY_Page_Up:
-            if (node->prev)
-              gtk_stack_set_visible_child  (GTK_STACK (widget), node->prev->data);
-            retval = TRUE;
-            break;
-          case GDK_KEY_Page_Down:
-            if (node->next)
-              gtk_stack_set_visible_child  (GTK_STACK (widget), node->next->data);
-            retval = TRUE;
-            break;
-          default:
-            retval = FALSE;
-            break;
-        }
+       GObject *obj = l->data;
+
+       if (!GTK_IS_WIDGET (obj) || gtk_widget_get_parent (GTK_WIDGET (obj)))
+         continue;
+
+       /* TODO: make sure we can add a toplevel inside a stack */
+       if (GTK_IS_WINDOW (obj))
+         continue;
+
+       priv->objects = g_list_prepend (priv->objects, obj);
+
+       gtk_stack_add_named (stack, GTK_WIDGET (obj),
+                            gtk_buildable_get_name (GTK_BUILDABLE (obj)));
     }
 
-  g_list_free (children);
-  return retval;
+  priv->objects = g_list_reverse (priv->objects); 
+
+  glade_previewer_set_widget (preview, GTK_WIDGET (stack));
+  gtk_widget_show (GTK_WIDGET (stack));
+  
+  g_slist_free (objects);
 }
 
+/**
+ * glade_previewer_slideshow_save:
+ * @preview: A GladePreviewer
+ * @filename:  a filename to save the slideshow.
+ * 
+ * Takes a screenshot of every widget GtkStack children and save it to @filename
+ * each in a different page
+ */
+void
+glade_previewer_slideshow_save (GladePreviewer *preview,
+                                const gchar    *filename)
+{
+  GladePreviewerPrivate *priv;
+  cairo_surface_t *surface;
+  GdkWindow *gdkwindow;
+  GtkWidget *child;
+  GtkStack *stack;
+  gdouble sx, sy;
 
-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;
-static gchar *screenshot_file_name = NULL;
-
-static GOptionEntry option_entries[] =
-{
-    {"filename", 'f', 0, G_OPTION_ARG_FILENAME, &file_name, N_("Name of the file to preview"), "FILENAME"},
-    {"template", 0, 0, G_OPTION_ARG_NONE, &template, N_("Creates dummy widget class to load a template"), 
NULL},
-    {"toplevel", 't', 0, G_OPTION_ARG_STRING, &toplevel_name, N_("Name of the toplevel to preview"), 
"TOPLEVELNAME"},
-    {"screenshot", 0, 0, G_OPTION_ARG_FILENAME, &screenshot_file_name, N_("File name to save a screenshot"), 
NULL},
-    {"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}
-};
+  g_return_if_fail (GLADE_IS_PREVIEWER (preview));
+  g_return_if_fail (filename != NULL);
+  priv = preview->priv;
 
-int
-main (int argc, char **argv)
-{
-  GladePreviewer *app;
-  GOptionContext *context;
-  GError *error = NULL;
-  GObject *toplevel = NULL;
+  g_return_if_fail (GTK_IS_BIN (priv->widget));
 
-#ifdef ENABLE_NLS
-  setlocale (LC_ALL, "");
-  bindtextdomain (GETTEXT_PACKAGE, glade_app_get_locale_dir ());
-  bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
-  textdomain (GETTEXT_PACKAGE);
-#endif
+  child = gtk_bin_get_child (GTK_BIN (priv->widget));
+  g_return_if_fail (GTK_IS_STACK (child));
+  stack = GTK_STACK (child);
 
-  context = g_option_context_new (_("- previews a glade UI definition"));
-  g_option_context_add_main_entries (context, option_entries, GETTEXT_PACKAGE);
-  g_option_context_add_group (context, gtk_get_option_group (TRUE));
+  gtk_stack_set_transition_type (stack, GTK_STACK_TRANSITION_TYPE_NONE);
+
+  gdkwindow = gtk_widget_get_window (priv->widget);
+  glade_previewer_wait_for_drawing (gdkwindow);
   
-  if (!g_option_context_parse (context, &argc, &argv, &error))
+  glade_previewer_get_scale (gtk_widget_get_screen (GTK_WIDGET (priv->widget)), &sx, &sy); 
+  surface = glade_previewer_surface_from_file (filename, 
+                                             gtk_widget_get_allocated_width (GTK_WIDGET (stack))*sx,
+                                             gtk_widget_get_allocated_height (GTK_WIDGET (stack))*sy);
+
+  if (surface)
     {
-      g_printerr (_("%s\nRun '%s --help' to see a full list of available command line "
-                   "options.\n"), error->message, argv[0]);
-      g_error_free (error);
-      g_option_context_free (context);
-      return 1;
-    }
+      GList *l, *children = gtk_container_get_children (GTK_CONTAINER (stack));
+      cairo_t *cr= cairo_create (surface);
 
-  g_option_context_free (context);
+      cairo_scale (cr, sx, sy);
 
-  if (version)
-    {
-      g_print ("glade-previewer " VERSION "\n");
-      return 0;
-    }
+      for (l = children; l; l = g_list_next (l))
+        {
+          GtkWidget *child = l->data;
+          gtk_stack_set_visible_child (stack, child);
+          glade_previewer_wait_for_drawing (gdkwindow);
+          gtk_widget_draw (child, cr);
+          cairo_show_page (cr);
+        }
 
-  if (!listen && !file_name)
-    {
-      g_printerr (_("Either --listen or --filename must be specified.\n"));
-      return 0;
+      if (children)
+        gtk_stack_set_visible_child (stack, children->data);
+
+      g_list_free (children);
+      cairo_destroy (cr);
+      cairo_surface_destroy(surface);
     }
+  else
+    g_warning ("Could not save slideshow to %s", filename);
+}
 
-  gtk_init (&argc, &argv);
-  glade_app_get ();
+/**
+ * glade_previewer_set_print_handlers:
+ * @preview: A GladePreviewer
+ * @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_previewer_connect_function() as the 
+ * connect funtion.
+ */
+void
+glade_previewer_set_print_handlers (GladePreviewer *preview,
+                                    gboolean        print)
+{
+  g_return_if_fail (GLADE_IS_PREVIEWER (preview));
+  preview->priv->print_handlers = print;
+}
 
-  app = glade_previewer_new (file_name, toplevel_name);
-  gtk_widget_show (GTK_WIDGET (app->window));
+typedef struct
+{
+  gchar        *handler_name;
+  GObject      *connect_object;
+  GConnectFlags flags;
+} HandlerData;
+
+typedef struct
+{
+  GladePreviewer *window;
+  gint          n_invocations;
 
-  app->is_template = template;
+  GSignalQuery  query;
+  GObject      *object;
+  GList        *handlers;
+} SignalData;
 
-  if (print_handler)
-    glade_preview_window_set_print_handlers (GLADE_PREVIEW_WINDOW (app->window), TRUE);
+static void
+handler_data_free (gpointer udata)
+{
+  HandlerData *hd = udata;
+  g_clear_object (&hd->connect_object);
+  g_free (hd->handler_name);
+  g_free (hd);
+}
 
-  if (css_file_name)
-    glade_preview_window_set_css_file (app->window, css_file_name);
+static void
+signal_data_free (gpointer udata, GClosure *closure)
+{
+  SignalData *data = udata;
 
-  if (listen)
-    {
-#ifdef WINDOWS
-      GIOChannel *input = g_io_channel_win32_new_fd (fileno (stdin));
-#else
-      GIOChannel *input = g_io_channel_unix_new (fileno (stdin));
-#endif
+  g_list_free_full (data->handlers, handler_data_free);
+  data->handlers = NULL;
 
-      g_io_add_watch (input, G_IO_IN | G_IO_HUP, on_data_incoming, app);
+  g_clear_object (&data->window);
+  g_clear_object (&data->object);
 
-      gtk_main ();
-    }
-  else if (template)
-    {
-      gchar *contents = NULL;
-      gsize size;
+  g_free (data);
+}
 
-      if (g_file_get_contents (file_name, &contents, &size, NULL))
-        toplevel = get_toplevel_from_string (app, NULL, contents, size);
+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");
+}
 
-      g_free (contents);
-    }
-  else if (file_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))
     {
-      GtkBuilder *builder = gtk_builder_new ();
-      GError *error = NULL;
+      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"));
+    }
+}
 
-      /* Use from_file() function gives builder a chance to know where to load resources from */
-      if (!gtk_builder_add_from_file (builder, app->file_name, &error))
-        {
-          g_printerr (_("Couldn't load builder definition: %s"), error->message);
-          g_error_free (error);
-          return 1;
-        }
+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);
+}
 
-      if (slideshow)
-        {
-          GSList *l, *objects = gtk_builder_get_objects (builder);
-          GtkStack *stack = GTK_STACK (gtk_stack_new ());
+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 ("");
 
-          /* Add Page up and Page down key binding */
-          g_signal_connect (app->window, "key-press-event",
-                            G_CALLBACK (glade_previewer_stack_key_press_event),
-                            stack);
+  data->n_invocations++;
 
-          objects = g_slist_sort (objects, objects_cmp_func);
+  if (data->n_invocations == 1)
+    /* translators: this will be shown in glade previewer when a signal %s::%s is emited one time */
+    g_string_append_printf (message, _("%s::%s emitted one time"),
+                            G_OBJECT_TYPE_NAME (object), query->signal_name);
+  else
+    /* translators: this will be shown in glade previewer when a signal %s::%s is emited %d times */
+    g_string_append_printf (message, _("%s::%s emitted %d times"),
+                            G_OBJECT_TYPE_NAME (object), query->signal_name,
+                            data->n_invocations);
 
-          for (l = objects; l; l = g_slist_next (l))
-            {
-              GObject *obj = l->data;
+  if (query->signal_flags & G_SIGNAL_RUN_FIRST)
+    glade_handler_method_append (message, query, _("Run First"));
 
-              if (!GTK_IS_WIDGET (obj) || gtk_widget_get_parent (GTK_WIDGET (obj)))
-                continue;
+  glade_handler_append (message, query, object_name, data->handlers, FALSE);
 
-              /* TODO: make sure we can add a toplevel inside a stack */
-              if (GTK_IS_WINDOW (obj))
-                continue;
+  if (query->signal_flags & G_SIGNAL_RUN_LAST)
+    glade_handler_method_append (message, query, _("Run Last"));
 
-              gtk_stack_add_named (stack, GTK_WIDGET (obj),
-                                   gtk_buildable_get_name (GTK_BUILDABLE (obj)));
-            }
+  glade_handler_append (message, query, object_name, data->handlers, TRUE);
 
-          glade_preview_window_set_widget (app->window, GTK_WIDGET (stack));
-          gtk_widget_show (GTK_WIDGET (stack));
-          
-          if (screenshot_file_name)
-            glade_preview_window_slideshow_save (app->window, screenshot_file_name);
-          else
-            {
-              gtk_stack_set_transition_type (stack, GTK_STACK_TRANSITION_TYPE_CROSSFADE);
+  if (query->signal_flags & G_SIGNAL_RUN_CLEANUP)
+    glade_handler_method_append (message, query, _("Run Cleanup"));
 
-              gtk_main ();
-            }
+  glade_previewer_set_message (data->window, GTK_MESSAGE_INFO, message->str);
 
-          g_slist_free (objects);
-        }
-      else
-        {
-          toplevel = get_toplevel (builder, toplevel_name);
+  if (data->window->priv->print_handlers)
+    g_printf ("\n%s\n", message->str);
 
-          gtk_builder_connect_signals_full (builder,
-                                            glade_preview_window_connect_function,
-                                            app->window);
-        }
+  g_string_free (message, TRUE);
+}
 
-      g_object_unref (builder);
-    }
+/**
+ * glade_previewer_connect_function:
+ * @builder:
+ * @object:
+ * @signal_name:
+ * @handler_name:
+ * @connect_object:
+ * @flags:
+ * @window: a #GladePreviewer
+ * 
+ * Function that collects every signal handler in @builder and shows them
+ * in @window info bar when the callback is activated
+ */
+void
+glade_previewer_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;
 
-  if (toplevel)
+  g_return_if_fail (GLADE_IS_PREVIEWER (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)
     {
-      glade_preview_window_set_widget (app->window, GTK_WIDGET (toplevel));
-      g_object_unref (toplevel);
-      gtk_widget_show (GTK_WIDGET (toplevel));
-
-      if (screenshot_file_name)
-        glade_preview_window_screenshot (app->window, TRUE, screenshot_file_name);
-      else
-        gtk_main ();
+      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);
     }
 
-  /* free unused resources */
-  g_free (file_name);
-  g_free (toplevel_name);
-  g_free (css_file_name);
-  g_free (screenshot_file_name);
-  glade_previewer_free (app);
+  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);
 
-  return 0;
+  g_free (key);
 }
diff --git a/gladeui/glade-previewer.h b/gladeui/glade-previewer.h
new file mode 100644
index 0000000..3284c19
--- /dev/null
+++ b/gladeui/glade-previewer.h
@@ -0,0 +1,97 @@
+/*
+ * glade-previewer.h
+ *
+ * Copyright (C) 2013-2016 Juan Pablo Ugarte
+   *
+ * Author: Juan Pablo Ugarte <juanpablougarte gmail com>
+ *
+ * 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.1 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 program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef _GLADE_PREVIEWER_H_
+#define _GLADE_PREVIEWER_H_
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define GLADE_TYPE_PREVIEWER             (glade_previewer_get_type ())
+#define GLADE_PREVIEWER(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), GLADE_TYPE_PREVIEWER, 
GladePreviewer))
+#define GLADE_PREVIEWER_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), GLADE_TYPE_PREVIEWER, 
GladePreviewerClass))
+#define GLADE_IS_PREVIEWER(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GLADE_TYPE_PREVIEWER))
+#define GLADE_IS_PREVIEWER_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), GLADE_TYPE_PREVIEWER))
+#define GLADE_PREVIEWER_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), GLADE_TYPE_PREVIEWER, 
GladePreviewerClass))
+
+typedef struct _GladePreviewerClass GladePreviewerClass;
+typedef struct _GladePreviewer GladePreviewer;
+typedef struct _GladePreviewerPrivate GladePreviewerPrivate;
+
+
+struct _GladePreviewerClass
+{
+  GObjectClass parent_class;
+};
+
+struct _GladePreviewer
+{
+  GObject parent_instance;
+
+  GladePreviewerPrivate *priv; 
+};
+
+GType      glade_previewer_get_type    (void) G_GNUC_CONST;
+
+GObject   *glade_previewer_new         (void);
+
+void       glade_previewer_set_widget  (GladePreviewer *preview,
+                                        GtkWidget      *widget);
+
+void       glade_previewer_present     (GladePreviewer *preview);
+
+void       glade_previewer_set_print_handlers (GladePreviewer *preview,
+                                               gboolean        print);
+
+void       glade_previewer_set_message (GladePreviewer   *preview,
+                                        GtkMessageType    type,
+                                       const gchar       *message);
+
+void       glade_previewer_set_css_file (GladePreviewer *preview,
+                                         const gchar    *css_file);
+
+void       glade_previewer_set_screenshot_extension (GladePreviewer *preview,
+                                                     const gchar    *extension);
+
+void       glade_previewer_screenshot  (GladePreviewer *preview,
+                                        gboolean        wait,
+                                        const gchar    *filename);
+
+void       glade_previewer_set_slideshow_widgets (GladePreviewer *preview,
+                                                  GSList         *objects);
+
+void       glade_previewer_slideshow_save (GladePreviewer *preview,
+                                           const gchar    *filename);
+
+void       glade_previewer_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_PREVIEWER_H_ */


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