summaryrefslogtreecommitdiff
path: root/gladeui
diff options
context:
space:
mode:
authorJuan Pablo Ugarte <juanpablougarte@gmail.com>2016-04-08 22:25:57 -0300
committerJuan Pablo Ugarte <juanpablougarte@gmail.com>2016-04-08 22:57:39 -0300
commit695ab6282a25ab554ae692d4d6e4f409d17411c5 (patch)
tree0e02138a6ccfa4d1ae81ea46ec61f6930342d260 /gladeui
parentb540ffcc55e3d722ae01ff57667e66e52fd625ef (diff)
downloadglade-695ab6282a25ab554ae692d4d6e4f409d17411c5.tar.gz
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"
Diffstat (limited to 'gladeui')
-rw-r--r--gladeui/Makefile.am4
-rw-r--r--gladeui/glade-preview-window.c794
-rw-r--r--gladeui/glade-preview-window.h92
-rw-r--r--gladeui/glade-previewer-main.c447
-rw-r--r--gladeui/glade-previewer.c1140
-rw-r--r--gladeui/glade-previewer.h97
6 files changed, 1267 insertions, 1307 deletions
diff --git a/gladeui/Makefile.am b/gladeui/Makefile.am
index ab256c21..7510084e 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-preview-window.c b/gladeui/glade-preview-window.c
deleted file mode 100644
index dc350ed3..00000000
--- a/gladeui/glade-preview-window.c
+++ /dev/null
@@ -1,794 +0,0 @@
-/*
- * glade-preview-window.c
- *
- * Copyright (C) 2013-2014 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.
- *
- */
-#include <config.h>
-
-#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>
-
-struct _GladePreviewWindowPrivate
-{
- GtkWidget *box;
- GtkWidget *info;
- GtkWidget *message_label;
- GtkWidget *widget;
-
- GtkCssProvider *css_provider;
- GFileMonitor *css_monitor;
- gchar *css_file;
- gchar *extension;
- gboolean print_handlers;
-};
-
-G_DEFINE_TYPE_WITH_PRIVATE (GladePreviewWindow, glade_preview_window, GTK_TYPE_WINDOW);
-
-static void
-glade_preview_window_init (GladePreviewWindow *window)
-{
- GladePreviewWindowPrivate *priv = glade_preview_window_get_instance_private (window);
- GtkWidget *content_area;
-
- window->priv = priv;
-
- gtk_window_set_title (GTK_WINDOW (window), _("Preview"));
- gtk_widget_add_events (GTK_WIDGET (window), GDK_KEY_PRESS_MASK);
-
- priv->box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
- priv->info = gtk_info_bar_new ();
- priv->message_label = gtk_label_new ("");
- gtk_label_set_line_wrap (GTK_LABEL (priv->message_label), TRUE);
- gtk_label_set_selectable (GTK_LABEL (priv->message_label), TRUE);
-
- gtk_widget_set_valign (priv->info, GTK_ALIGN_END);
- gtk_widget_set_vexpand (priv->info, FALSE);
- content_area = gtk_info_bar_get_content_area (GTK_INFO_BAR (priv->info));
- gtk_container_add (GTK_CONTAINER (content_area), priv->message_label);
-
- gtk_info_bar_set_show_close_button (GTK_INFO_BAR (priv->info), TRUE);
-
- g_signal_connect (priv->info, "response", G_CALLBACK (gtk_widget_hide), NULL);
-
- gtk_box_pack_start (GTK_BOX (priv->box), priv->info, FALSE, FALSE, 0);
- gtk_container_add (GTK_CONTAINER (window), priv->box);
-
- gtk_widget_show (priv->message_label);
- gtk_widget_show (priv->box);
-}
-
-static void
-glade_preview_window_dispose (GObject *object)
-{
- GladePreviewWindowPrivate *priv = GLADE_PREVIEW_WINDOW (object)->priv;
-
- 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);
-}
-
-static gboolean
-glade_preview_window_key_press_event (GtkWidget *widget, GdkEventKey *event)
-{
- GladePreviewWindow *window = GLADE_PREVIEW_WINDOW (widget);
- GladePreviewWindowPrivate *priv = window->priv;
- gchar *extension;
-
- switch (event->keyval)
- {
- 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 (extension)
- {
- gchar *tmp_file = g_strdup_printf ("glade-screenshot-XXXXXX.%s", extension);
-
- g_mkstemp (tmp_file);
- glade_preview_window_screenshot (window, FALSE, tmp_file);
- g_free (tmp_file);
-
- return TRUE;
- }
-
- return FALSE;
-}
-
-static void
-glade_preview_window_realize (GtkWidget *widget)
-{
- GladePreviewWindowPrivate *priv = GLADE_PREVIEW_WINDOW (widget)->priv;
-
- GTK_WIDGET_CLASS (glade_preview_window_parent_class)->realize (widget);
-
- if (priv->widget && gtk_widget_is_toplevel (priv->widget) &&
- gtk_widget_get_parent (priv->widget) == NULL)
- {
- gtk_widget_set_parent_window (priv->widget, gtk_widget_get_window (widget));
- gtk_box_pack_start (GTK_BOX (priv->box), priv->widget, TRUE, TRUE, 0);
- gtk_widget_show (priv->widget);
- }
-}
-
-static void
-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;
- widget_class->key_press_event = glade_preview_window_key_press_event;
-}
-
-GtkWidget *
-glade_preview_window_new (void)
-{
- return GTK_WIDGET (g_object_new (GLADE_TYPE_PREVIEW_WINDOW, NULL));
-}
-
-static void
-glade_preview_window_set_css_provider_forall (GtkWidget *widget, gpointer data)
-{
- gtk_style_context_add_provider (gtk_widget_get_style_context (widget),
- GTK_STYLE_PROVIDER (data),
- GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
-
- if (GTK_IS_CONTAINER (widget))
- gtk_container_forall (GTK_CONTAINER (widget), glade_preview_window_set_css_provider_forall, data);
-}
-
-void
-glade_preview_window_set_widget (GladePreviewWindow *window, GtkWidget *widget)
-{
- GladePreviewWindowPrivate *priv;
- GdkWindow *gdkwindow;
-
- g_return_if_fail (GLADE_IS_PREVIEW_WINDOW (window));
- g_return_if_fail (GTK_IS_WIDGET (widget));
-
- priv = window->priv;
-
- if (priv->widget)
- gtk_container_remove (GTK_CONTAINER (priv->box), priv->widget);
-
- priv->widget = widget;
-
- if (priv->css_provider)
- glade_preview_window_set_css_provider_forall (widget, priv->css_provider);
-
- if (gtk_widget_is_toplevel (widget))
- {
- /* Delay setting it until we have a window */
- if (!(gdkwindow = gtk_widget_get_window (priv->box)))
- return;
-
- gtk_widget_set_parent_window (widget, gdkwindow);
- }
-
- gtk_box_pack_start (GTK_BOX (priv->box), widget, TRUE, TRUE, 0);
-}
-
-void
-glade_preview_window_set_message (GladePreviewWindow *window,
- GtkMessageType type,
- const gchar *message)
-{
- GladePreviewWindowPrivate *priv;
-
- 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)
- {
- gtk_label_set_text (GTK_LABEL (priv->message_label), message);
- gtk_widget_show (priv->info);
- }
- else
- {
- gtk_label_set_text (GTK_LABEL (priv->message_label), "");
- gtk_widget_hide (priv->info);
- }
-}
-
-static void
-on_css_monitor_changed (GFileMonitor *monitor,
- GFile *file,
- GFile *other_file,
- GFileMonitorEvent event_type,
- GladePreviewWindow *window)
-{
- GladePreviewWindowPrivate *priv = window->priv;
- GError *error = NULL;
-
- gtk_css_provider_load_from_file (priv->css_provider, file, &error);
-
- if (error)
- {
- glade_preview_window_set_message (window, GTK_MESSAGE_WARNING, error->message);
- g_error_free (error);
- }
- else
- glade_preview_window_set_message (window, GTK_MESSAGE_OTHER, NULL);
-}
-
-void
-glade_preview_window_set_css_file (GladePreviewWindow *window,
- const gchar *css_file)
-{
- GladePreviewWindowPrivate *priv;
- GError *error = NULL;
- GFile *file;
-
- g_return_if_fail (GLADE_IS_PREVIEW_WINDOW (window));
- priv = window->priv;
-
- g_free (priv->css_file);
- g_clear_object (&priv->css_provider);
- g_clear_object (&priv->css_monitor);
-
- priv->css_file = g_strdup (css_file);
-
- file = g_file_new_for_path (css_file);
- priv->css_provider = gtk_css_provider_new ();
- g_object_ref_sink (priv->css_provider);
-
- priv->css_monitor = g_file_monitor_file (file, G_FILE_MONITOR_NONE, NULL, &error);
- if (error)
- {
- g_warning ("Cant monitor CSS file %s: %s", css_file, error->message);
- g_error_free (error);
- }
- else
- {
- g_object_ref_sink (priv->css_monitor);
- g_signal_connect (priv->css_monitor, "changed",
- G_CALLBACK (on_css_monitor_changed), window);
- }
-
- /* load CSS */
- gtk_css_provider_load_from_file (priv->css_provider, file, &error);
- if (error)
- {
- glade_preview_window_set_message (window, GTK_MESSAGE_INFO, error->message);
- g_message ("%s CSS parsing failed: %s", css_file, error->message);
- g_error_free (error);
- }
-
- if (priv->widget)
- glade_preview_window_set_css_provider_forall (priv->widget, priv->css_provider);
-
- g_object_unref (file);
-}
-
-void
-glade_preview_window_set_screenshot_extension (GladePreviewWindow *window,
- const gchar *extension)
-{
- GladePreviewWindowPrivate *priv;
-
- g_return_if_fail (GLADE_IS_PREVIEW_WINDOW (window));
- priv = window->priv;
-
- g_free (priv->extension);
- priv->extension = g_strdup (extension);
-}
-
-static gboolean
-quit_when_idle (gpointer loop)
-{
- g_main_loop_quit (loop);
-
- return G_SOURCE_REMOVE;
-}
-
-static void
-check_for_draw (GdkEvent *event, gpointer loop)
-{
- if (event->type == GDK_EXPOSE)
- {
- g_idle_add (quit_when_idle, loop);
- gdk_event_handler_set ((GdkEventFunc) gtk_main_do_event, NULL, NULL);
- }
-
- gtk_main_do_event (event);
-}
-
-/* Taken from Gtk sources gtk-reftest.c */
-static void
-glade_preview_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);
-}
-
-static const gchar *
-glade_preview_get_extension (const gchar *filename)
-{
- gchar *extension;
-
- g_return_val_if_fail (filename != NULL, NULL);
-
- extension = g_strrstr (filename,".");
-
- if (extension)
- extension++;
-
- if (!extension)
- {
- g_warning ("%s has no extension!", filename);
- return NULL;
- }
- return extension;
-}
-
-static void
-glade_preview_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 cairo_surface_t *
-glade_preview_surface_from_file (const gchar *filename, gdouble w, gdouble h)
-{
- cairo_surface_t *surface;
- const gchar *extension;
-
- extension = glade_preview_get_extension (filename);
-
- 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 surface;
-}
-
-/**
- * glade_preview_window_screenshot:
- * @window: A GladePreviewWindow
- * @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_preview_window_screenshot (GladePreviewWindow *window,
- gboolean wait,
- const gchar *filename)
-{
- GladePreviewWindowPrivate *priv;
- cairo_surface_t *surface;
- GdkWindow *gdkwindow;
- GdkScreen *screen;
- gdouble sx, sy;
- gint w, h;
-
- g_return_if_fail (GLADE_IS_PREVIEW_WINDOW (window));
- g_return_if_fail (filename != NULL);
- priv = window->priv;
-
- if (!priv->widget)
- return;
-
- gdkwindow = gtk_widget_get_window (priv->widget);
- screen = gdk_window_get_screen (gdkwindow);
-
- if (wait)
- glade_preview_wait_for_drawing (gdkwindow);
-
- w = gtk_widget_get_allocated_width (priv->widget);
- h = gtk_widget_get_allocated_height (priv->widget);
- glade_preview_get_scale (screen, &sx, &sy);
-
- surface = glade_preview_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_preview_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);
- }
-}
-
-/**
- * glade_preview_window_slideshow_save:
- * @window: A GladePreviewWindow
- * @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_preview_window_slideshow_save (GladePreviewWindow *window,
- const gchar *filename)
-{
- GladePreviewWindowPrivate *priv;
- cairo_surface_t *surface;
- GdkWindow *gdkwindow;
- GtkStack *stack;
- gdouble sx, sy;
-
- g_return_if_fail (GLADE_IS_PREVIEW_WINDOW (window));
- g_return_if_fail (filename != NULL);
- priv = window->priv;
-
- g_return_if_fail (priv->widget);
- g_return_if_fail (GTK_IS_STACK (priv->widget));
- stack = GTK_STACK (priv->widget);
-
- gdkwindow = gtk_widget_get_window (priv->widget);
- glade_preview_wait_for_drawing (gdkwindow);
-
- glade_preview_get_scale (gtk_widget_get_screen (GTK_WIDGET (window)), &sx, &sy);
- surface = glade_preview_surface_from_file (filename,
- gtk_widget_get_allocated_width (GTK_WIDGET (stack))*sx,
- gtk_widget_get_allocated_height (GTK_WIDGET (stack))*sy);
-
- if (surface)
- {
- GList *l, *children = gtk_container_get_children (GTK_CONTAINER (stack));
- cairo_t *cr= cairo_create (surface);
-
- cairo_scale (cr, sx, sy);
-
- for (l = children; l; l = g_list_next (l))
- {
- GtkWidget *child = l->data;
- gtk_stack_set_visible_child (stack, child);
- glade_preview_wait_for_drawing (gdkwindow);
- gtk_widget_draw (child, cr);
- cairo_show_page (cr);
- }
-
- 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);
-}
-
-/**
- * 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 ("");
-
- data->n_invocations++;
-
- 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);
-
- 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
deleted file mode 100644
index 56276e7b..00000000
--- a/gladeui/glade-preview-window.h
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * glade-preview-window.h
- *
- * Copyright (C) 2013 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_PREVIEW_WINDOW_H_
-#define _GLADE_PREVIEW_WINDOW_H_
-
-#include <gtk/gtk.h>
-
-G_BEGIN_DECLS
-
-#define GLADE_TYPE_PREVIEW_WINDOW (glade_preview_window_get_type ())
-#define GLADE_PREVIEW_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GLADE_TYPE_PREVIEW_WINDOW, GladePreviewWindow))
-#define GLADE_PREVIEW_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GLADE_TYPE_PREVIEW_WINDOW, GladePreviewWindowClass))
-#define GLADE_IS_PREVIEW_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GLADE_TYPE_PREVIEW_WINDOW))
-#define GLADE_IS_PREVIEW_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GLADE_TYPE_PREVIEW_WINDOW))
-#define GLADE_PREVIEW_WINDOW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GLADE_TYPE_PREVIEW_WINDOW, GladePreviewWindowClass))
-
-typedef struct _GladePreviewWindowClass GladePreviewWindowClass;
-typedef struct _GladePreviewWindow GladePreviewWindow;
-typedef struct _GladePreviewWindowPrivate GladePreviewWindowPrivate;
-
-
-struct _GladePreviewWindowClass
-{
- GtkWindowClass parent_class;
-};
-
-struct _GladePreviewWindow
-{
- GtkWindow parent_instance;
-
- GladePreviewWindowPrivate *priv;
-};
-
-GType glade_preview_window_get_type (void) G_GNUC_CONST;
-
-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);
-
-void glade_preview_window_set_css_file (GladePreviewWindow *window,
- const gchar *css_file);
-
-void glade_preview_window_set_screenshot_extension (GladePreviewWindow *window,
- const gchar *extension);
-
-void glade_preview_window_screenshot (GladePreviewWindow *window,
- gboolean wait,
- const gchar *filename);
-
-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-main.c b/gladeui/glade-previewer-main.c
new file mode 100644
index 00000000..5dab4acd
--- /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 44a23574..8472359d 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 00000000..3284c190
--- /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_ */