Blame gladeui/glade-preview.c

Packit 1e8aac
/*
Packit 1e8aac
 * Copyright (C) 2010 Marco Diego Aurélio Mesquita
Packit 1e8aac
 *
Packit 1e8aac
 * This program is free software; you can redistribute it and/or modify
Packit 1e8aac
 * it under the terms of the GNU Lesser General Public License as
Packit 1e8aac
 * published by the Free Software Foundation; either version 2 of the
Packit 1e8aac
 * License, or (at your option) any later version.
Packit 1e8aac
 *
Packit 1e8aac
 * This program is distributed in the hope that it will be useful,
Packit 1e8aac
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 1e8aac
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 1e8aac
 * GNU General Public License for more details.
Packit 1e8aac
 *
Packit 1e8aac
 * You should have received a copy of the GNU Lesser General Public License
Packit 1e8aac
 * along with this program; if not, write to the Free Software
Packit 1e8aac
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
Packit 1e8aac
 *
Packit 1e8aac
 * Authors:
Packit 1e8aac
 *   Marco Diego Aurélio Mesquita <marcodiegomesquita@gmail.com>
Packit 1e8aac
 */
Packit 1e8aac
Packit 1e8aac
#include <config.h>
Packit 1e8aac
Packit 1e8aac
/**
Packit 1e8aac
 * SECTION:glade-preview
Packit 1e8aac
 * @Short_Description: The glade preview launch/kill interface.
Packit 1e8aac
 *
Packit 1e8aac
 * This object owns all data that is needed to keep a preview. It stores
Packit 1e8aac
 * the GIOChannel used for comunnication between glade and glade-previewer,
Packit 1e8aac
 * the event source id for a watch  (in the case a watch is used to monitor
Packit 1e8aac
 * the communication channel), the previewed widget and the pid of the
Packit 1e8aac
 * corresponding glade-previewer.
Packit 1e8aac
 * 
Packit 1e8aac
 */
Packit 1e8aac
Packit 1e8aac
Packit 1e8aac
#include <string.h>
Packit 1e8aac
#include <stdlib.h>
Packit 1e8aac
#include <glib.h>
Packit 1e8aac
#include <glib/gi18n-lib.h>
Packit 1e8aac
#include <glib/gstdio.h>
Packit 1e8aac
Packit 1e8aac
#include "glade.h"
Packit 1e8aac
#include "glade-preview.h"
Packit 1e8aac
#include "glade-project.h"
Packit 1e8aac
#include "glade-app.h"
Packit 1e8aac
Packit 1e8aac
#include "glade-preview-tokens.h"
Packit 1e8aac
Packit 1e8aac
#ifdef G_OS_WIN32
Packit 1e8aac
#define GLADE_PREVIEWER "glade-previewer.exe"
Packit 1e8aac
#else
Packit 1e8aac
#define GLADE_PREVIEWER "glade-previewer"
Packit 1e8aac
#endif
Packit 1e8aac
Packit 1e8aac
/* Private data for glade-preview */
Packit 1e8aac
struct _GladePreviewPrivate
Packit 1e8aac
{
Packit 1e8aac
  GIOChannel *channel;          /* Channel user for communication between glade and glade-previewer */
Packit 1e8aac
  guint watch;                  /* Event source id used to monitor the channel */
Packit 1e8aac
  GladeWidget *previewed_widget;
Packit 1e8aac
  GPid pid;                     /* Pid of the corresponding glade-previewer process */
Packit 1e8aac
};
Packit 1e8aac
Packit 1e8aac
G_DEFINE_TYPE_WITH_PRIVATE (GladePreview, glade_preview, G_TYPE_OBJECT);
Packit 1e8aac
Packit 1e8aac
enum
Packit 1e8aac
{
Packit 1e8aac
  PREVIEW_EXITS,
Packit 1e8aac
  LAST_SIGNAL
Packit 1e8aac
};
Packit 1e8aac
Packit 1e8aac
static guint glade_preview_signals[LAST_SIGNAL] = { 0 };
Packit 1e8aac
Packit 1e8aac
/**
Packit 1e8aac
 * glade_preview_kill
Packit 1e8aac
 * @preview: a #GladePreview that will be killed.
Packit 1e8aac
 *
Packit 1e8aac
 * Uses the communication channel and protocol to send the "<quit>" token to the
Packit 1e8aac
 * glade-previewer telling it to commit suicide.
Packit 1e8aac
 *
Packit 1e8aac
 */
Packit 1e8aac
static void
Packit 1e8aac
glade_preview_kill (GladePreview *preview)
Packit 1e8aac
{
Packit 1e8aac
  const gchar *quit = QUIT_TOKEN;
Packit 1e8aac
  GIOChannel *channel;
Packit 1e8aac
  GError *error = NULL;
Packit 1e8aac
  gsize size;
Packit 1e8aac
Packit 1e8aac
  channel = preview->priv->channel;
Packit 1e8aac
  g_io_channel_write_chars (channel, quit, strlen (quit), &size, &error);
Packit 1e8aac
Packit 1e8aac
  if (size != strlen (quit) && error != NULL)
Packit 1e8aac
    {
Packit 1e8aac
      g_warning ("Error passing quit signal trough pipe: %s", error->message);
Packit 1e8aac
      g_error_free (error);
Packit 1e8aac
    }
Packit 1e8aac
Packit 1e8aac
  g_io_channel_flush (channel, &error);
Packit 1e8aac
  if (error != NULL)
Packit 1e8aac
    {
Packit 1e8aac
      g_warning ("Error flushing channel: %s", error->message);
Packit 1e8aac
      g_error_free (error);
Packit 1e8aac
    }
Packit 1e8aac
Packit 1e8aac
  g_io_channel_shutdown (channel, TRUE, &error);
Packit 1e8aac
  if (error != NULL)
Packit 1e8aac
    {
Packit 1e8aac
      g_warning ("Error shutting down channel: %s", error->message);
Packit 1e8aac
      g_error_free (error);
Packit 1e8aac
    }
Packit 1e8aac
}
Packit 1e8aac
Packit 1e8aac
static void
Packit 1e8aac
glade_preview_dispose (GObject *gobject)
Packit 1e8aac
{
Packit 1e8aac
  GladePreview *self = GLADE_PREVIEW (gobject);
Packit 1e8aac
Packit 1e8aac
  if (self->priv->watch)
Packit 1e8aac
    {
Packit 1e8aac
      g_source_remove (self->priv->watch);
Packit 1e8aac
      glade_preview_kill (self);
Packit 1e8aac
    }
Packit 1e8aac
Packit 1e8aac
  if (self->priv->channel)
Packit 1e8aac
    {
Packit 1e8aac
      g_io_channel_unref (self->priv->channel);
Packit 1e8aac
      self->priv->channel = NULL;
Packit 1e8aac
    }
Packit 1e8aac
Packit 1e8aac
  G_OBJECT_CLASS (glade_preview_parent_class)->dispose (gobject);
Packit 1e8aac
}
Packit 1e8aac
Packit 1e8aac
/* We have to use finalize because of the signal that is sent in dispose */
Packit 1e8aac
static void
Packit 1e8aac
glade_preview_finalize (GObject *gobject)
Packit 1e8aac
{
Packit 1e8aac
  G_OBJECT_CLASS (glade_preview_parent_class)->finalize (gobject);
Packit 1e8aac
}
Packit 1e8aac
Packit 1e8aac
static void
Packit 1e8aac
glade_preview_class_init (GladePreviewClass *klass)
Packit 1e8aac
{
Packit 1e8aac
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
Packit 1e8aac
Packit 1e8aac
  gobject_class->dispose  = glade_preview_dispose;
Packit 1e8aac
  gobject_class->finalize = glade_preview_finalize;
Packit 1e8aac
Packit 1e8aac
  /**
Packit 1e8aac
   * GladePreview::exits:
Packit 1e8aac
   * @gladepreview: the #GladePreview which received the signal.
Packit 1e8aac
   * @gladeproject: the #GladeProject associated with the preview.
Packit 1e8aac
   *
Packit 1e8aac
   * Emitted when @preview exits.
Packit 1e8aac
   */
Packit 1e8aac
  glade_preview_signals[PREVIEW_EXITS] =
Packit 1e8aac
      g_signal_new ("exits",
Packit 1e8aac
                    G_TYPE_FROM_CLASS (gobject_class),
Packit 1e8aac
                    G_SIGNAL_RUN_FIRST,
Packit 1e8aac
                    0, NULL, NULL,
Packit 1e8aac
                    g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
Packit 1e8aac
}
Packit 1e8aac
Packit 1e8aac
static void
Packit 1e8aac
glade_preview_init (GladePreview *self)
Packit 1e8aac
{
Packit 1e8aac
  GladePreviewPrivate *priv;
Packit 1e8aac
Packit 1e8aac
  self->priv = priv = glade_preview_get_instance_private (self);
Packit 1e8aac
  priv->channel = NULL;
Packit 1e8aac
}
Packit 1e8aac
Packit 1e8aac
static void
Packit 1e8aac
glade_preview_internal_watch (GPid pid, gint status, gpointer data)
Packit 1e8aac
{
Packit 1e8aac
  GladePreview *preview = GLADE_PREVIEW (data);
Packit 1e8aac
Packit 1e8aac
  preview->priv->watch = 0;
Packit 1e8aac
Packit 1e8aac
  /* This means a preview exited. We'll now signal it */
Packit 1e8aac
  g_signal_emit (preview, glade_preview_signals[PREVIEW_EXITS], 0);
Packit 1e8aac
}
Packit 1e8aac
Packit 1e8aac
/**
Packit 1e8aac
 * glade_preview_launch:
Packit 1e8aac
 * @widget: Pointer to a local instance of the widget that will be previewed.
Packit 1e8aac
 * @buffer: Contents of an xml definition of the interface which will be previewed
Packit 1e8aac
 *
Packit 1e8aac
 * Creates a new #GladePreview and launches glade-previewer to preview it.
Packit 1e8aac
 *
Packit 1e8aac
 * Returns: a new #GladePreview or NULL if launch fails.
Packit 1e8aac
 * 
Packit 1e8aac
 */
Packit 1e8aac
GladePreview *
Packit 1e8aac
glade_preview_launch (GladeWidget *widget, const gchar *buffer)
Packit 1e8aac
{
Packit 1e8aac
  GPid pid;
Packit 1e8aac
  GError *error = NULL;
Packit 1e8aac
  gchar *argv[10], *executable;
Packit 1e8aac
  gint child_stdin;
Packit 1e8aac
  gsize bytes_written;
Packit 1e8aac
  GIOChannel *output;
Packit 1e8aac
  GladePreview *preview = NULL;
Packit 1e8aac
  const gchar *css_provider, *filename;
Packit 1e8aac
  GladeProject *project;
Packit 1e8aac
  gchar *name;
Packit 1e8aac
  gint i;
Packit 1e8aac
Packit 1e8aac
  g_return_val_if_fail (GLADE_IS_WIDGET (widget), NULL);
Packit 1e8aac
Packit 1e8aac
  executable = g_find_program_in_path (GLADE_PREVIEWER);
Packit 1e8aac
Packit 1e8aac
  project = glade_widget_get_project (widget);
Packit 1e8aac
  filename = glade_project_get_path (project);
Packit 1e8aac
  name = (filename) ? NULL : glade_project_get_name (project);
Packit 1e8aac
  
Packit 1e8aac
  argv[0] = executable;
Packit 1e8aac
  argv[1] = "--listen";
Packit 1e8aac
  argv[2] = "--toplevel";
Packit 1e8aac
  argv[3] = (gchar *) glade_widget_get_name (widget);
Packit 1e8aac
  argv[4] = "--filename";
Packit 1e8aac
  argv[5] = (filename) ? (gchar *) filename : name;
Packit 1e8aac
Packit 1e8aac
  i = 5;
Packit 1e8aac
  if (glade_project_get_template (project))
Packit 1e8aac
    argv[++i] = "--template";
Packit 1e8aac
    
Packit 1e8aac
  argv[++i] = NULL;
Packit 1e8aac
Packit 1e8aac
  css_provider = glade_project_get_css_provider_path (glade_widget_get_project (widget));
Packit 1e8aac
  if (css_provider)
Packit 1e8aac
    {
Packit 1e8aac
      argv[i] = "--css";
Packit 1e8aac
      argv[++i] = (gchar *) css_provider;
Packit 1e8aac
      argv[++i] = NULL;
Packit 1e8aac
    }
Packit 1e8aac
  
Packit 1e8aac
  if (g_spawn_async_with_pipes (NULL,
Packit 1e8aac
                                argv,
Packit 1e8aac
                                NULL, G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL,
Packit 1e8aac
                                &pid, &child_stdin, NULL, NULL,
Packit 1e8aac
                                &error) == FALSE)
Packit 1e8aac
    {
Packit 1e8aac
      g_warning (_("Error launching previewer: %s\n"), error->message);
Packit 1e8aac
      glade_util_ui_message (glade_app_get_window (),
Packit 1e8aac
                             GLADE_UI_ERROR, NULL,
Packit 1e8aac
                             _("Failed to launch preview: %s.\n"),
Packit 1e8aac
                             error->message);
Packit 1e8aac
      g_error_free (error);
Packit 1e8aac
      g_free (executable);
Packit 1e8aac
      g_free (name);
Packit 1e8aac
      return NULL;
Packit 1e8aac
    }
Packit 1e8aac
Packit 1e8aac
#ifdef G_OS_WIN32
Packit 1e8aac
  output = g_io_channel_win32_new_fd (child_stdin);
Packit 1e8aac
#else
Packit 1e8aac
  output = g_io_channel_unix_new (child_stdin);
Packit 1e8aac
#endif
Packit 1e8aac
Packit 1e8aac
  g_io_channel_write_chars (output, buffer, strlen (buffer), &bytes_written,
Packit 1e8aac
                            &error);
Packit 1e8aac
Packit 1e8aac
  if (bytes_written != strlen (buffer) && error != NULL)
Packit 1e8aac
    {
Packit 1e8aac
      g_warning ("Error passing UI trough pipe: %s", error->message);
Packit 1e8aac
      g_error_free (error);
Packit 1e8aac
    }
Packit 1e8aac
Packit 1e8aac
  g_io_channel_flush (output, &error);
Packit 1e8aac
  if (error != NULL)
Packit 1e8aac
    {
Packit 1e8aac
      g_warning ("Error flushing UI trough pipe: %s", error->message);
Packit 1e8aac
      g_error_free (error);
Packit 1e8aac
    }
Packit 1e8aac
Packit 1e8aac
  /* Setting up preview data */
Packit 1e8aac
  preview                         = g_object_new (GLADE_TYPE_PREVIEW, NULL);
Packit 1e8aac
  preview->priv->channel          = output;
Packit 1e8aac
  preview->priv->previewed_widget = widget;
Packit 1e8aac
  preview->priv->pid              = pid;
Packit 1e8aac
Packit 1e8aac
  preview->priv->watch = 
Packit 1e8aac
    g_child_watch_add (preview->priv->pid,
Packit 1e8aac
		       glade_preview_internal_watch,
Packit 1e8aac
		       preview);
Packit 1e8aac
Packit 1e8aac
  g_free (executable);
Packit 1e8aac
  g_free (name);
Packit 1e8aac
Packit 1e8aac
  return preview;
Packit 1e8aac
}
Packit 1e8aac
Packit 1e8aac
void
Packit 1e8aac
glade_preview_update (GladePreview *preview, const gchar  *buffer)
Packit 1e8aac
{
Packit 1e8aac
  const gchar *update_token = UPDATE_TOKEN;
Packit 1e8aac
  gchar *update;
Packit 1e8aac
  GIOChannel *channel;
Packit 1e8aac
  GError *error = NULL;
Packit 1e8aac
  gsize size;
Packit 1e8aac
  gsize bytes_written;
Packit 1e8aac
  GladeWidget *gwidget;
Packit 1e8aac
Packit 1e8aac
  g_return_if_fail (GLADE_IS_PREVIEW (preview));
Packit 1e8aac
  g_return_if_fail (buffer && buffer[0]);
Packit 1e8aac
Packit 1e8aac
  gwidget = glade_preview_get_widget (preview);
Packit 1e8aac
Packit 1e8aac
  update = g_strdup_printf ("%s%s\n", update_token,
Packit 1e8aac
                            glade_widget_get_name (gwidget));
Packit 1e8aac
Packit 1e8aac
  channel = preview->priv->channel;
Packit 1e8aac
  g_io_channel_write_chars (channel, update, strlen (update), &size, &error);
Packit 1e8aac
Packit 1e8aac
  if (size != strlen (update) && error != NULL)
Packit 1e8aac
    {
Packit 1e8aac
      g_warning ("Error passing quit signal trough pipe: %s", error->message);
Packit 1e8aac
      g_error_free (error);
Packit 1e8aac
    }
Packit 1e8aac
Packit 1e8aac
  g_io_channel_flush (channel, &error);
Packit 1e8aac
  if (error != NULL)
Packit 1e8aac
    {
Packit 1e8aac
      g_warning ("Error flushing channel: %s", error->message);
Packit 1e8aac
      g_error_free (error);
Packit 1e8aac
    }
Packit 1e8aac
Packit 1e8aac
  /* We'll now send the interface: */
Packit 1e8aac
  g_io_channel_write_chars (channel, buffer, strlen (buffer), &bytes_written,
Packit 1e8aac
                            &error);
Packit 1e8aac
Packit 1e8aac
  if (bytes_written != strlen (buffer) && error != NULL)
Packit 1e8aac
    {
Packit 1e8aac
      g_warning ("Error passing UI trough pipe: %s", error->message);
Packit 1e8aac
      g_error_free (error);
Packit 1e8aac
    }
Packit 1e8aac
Packit 1e8aac
  g_io_channel_flush (channel, &error);
Packit 1e8aac
  if (error != NULL)
Packit 1e8aac
    {
Packit 1e8aac
      g_warning ("Error flushing UI trough pipe: %s", error->message);
Packit 1e8aac
      g_error_free (error);
Packit 1e8aac
    }
Packit 1e8aac
Packit 1e8aac
  g_free (update);
Packit 1e8aac
}
Packit 1e8aac
Packit 1e8aac
GladeWidget *
Packit 1e8aac
glade_preview_get_widget (GladePreview *preview)
Packit 1e8aac
{
Packit 1e8aac
  g_return_val_if_fail (GLADE_IS_PREVIEW (preview), NULL);
Packit 1e8aac
  return preview->priv->previewed_widget;
Packit 1e8aac
}
Packit 1e8aac
Packit 1e8aac
GPid
Packit 1e8aac
glade_preview_get_pid (GladePreview *preview)
Packit 1e8aac
{
Packit 1e8aac
  g_return_val_if_fail (GLADE_IS_PREVIEW (preview), 0);
Packit 1e8aac
  return preview->priv->pid;
Packit 1e8aac
}