Blame gladeui/glade-preview-template.c

Packit 1e8aac
/*
Packit 1e8aac
 * glade-preview-template.c
Packit 1e8aac
 *
Packit 1e8aac
 * Copyright (C) 2013 Juan Pablo Ugarte
Packit 1e8aac
   *
Packit 1e8aac
 * Author: Juan Pablo Ugarte <juanpablougarte@gmail.com>
Packit 1e8aac
 *
Packit 1e8aac
 * This library is free software; you can redistribute it and/or modify it
Packit 1e8aac
 * under the terms of the GNU Lesser General Public License as
Packit 1e8aac
 * published by the Free Software Foundation; either version 2.1 of
Packit 1e8aac
 * the License, or (at your option) any later version.
Packit 1e8aac
   *
Packit 1e8aac
 * This library is distributed in the hope that it will be useful, but
Packit 1e8aac
 * WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 1e8aac
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 1e8aac
 * Lesser General Public License for more details.
Packit 1e8aac
 *
Packit 1e8aac
 * You should have received a copy of the GNU Lesser General Public 
Packit 1e8aac
 * License 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
 */
Packit 1e8aac
Packit 1e8aac
#include <config.h>
Packit 1e8aac
Packit 1e8aac
#include <string.h>
Packit 1e8aac
#include "glade-utils.h"
Packit 1e8aac
#include "glade-preview-template.h"
Packit 1e8aac
Packit 1e8aac
typedef struct
Packit 1e8aac
{
Packit 1e8aac
  GTypeInfo info;
Packit 1e8aac
  GString *template_string;
Packit 1e8aac
  GBytes *template_data;
Packit 1e8aac
Packit 1e8aac
  GtkBuilderConnectFunc connect_func;
Packit 1e8aac
  gpointer connect_data;
Packit 1e8aac
  gint count;
Packit 1e8aac
} TypeData;
Packit 1e8aac
Packit 1e8aac
/* We need to call gtk_widget_init_template() in the instance init for the
Packit 1e8aac
* template to work.
Packit 1e8aac
*/
Packit 1e8aac
static void
Packit 1e8aac
template_init (GTypeInstance *instance, gpointer g_class)
Packit 1e8aac
{  
Packit 1e8aac
  gtk_widget_init_template (GTK_WIDGET (instance));
Packit 1e8aac
}
Packit 1e8aac
Packit 1e8aac
static void
Packit 1e8aac
template_connect_function (GtkBuilder   *builder,
Packit 1e8aac
                           GObject      *object,
Packit 1e8aac
                           const gchar  *signal_name,
Packit 1e8aac
                           const gchar  *handler_name,
Packit 1e8aac
                           GObject      *connect_object,
Packit 1e8aac
                           GConnectFlags flags,
Packit 1e8aac
                           gpointer      data)
Packit 1e8aac
{
Packit 1e8aac
  /* Ignore signal connections */
Packit 1e8aac
}
Packit 1e8aac
Packit 1e8aac
/* Need to associate the class with a template */
Packit 1e8aac
static void
Packit 1e8aac
template_class_init (gpointer g_class, gpointer user_data)
Packit 1e8aac
{
Packit 1e8aac
  TypeData *data = user_data;
Packit 1e8aac
Packit 1e8aac
  gtk_widget_class_set_template (g_class, data->template_data);
Packit 1e8aac
Packit 1e8aac
  if (data->connect_func && data->connect_data)
Packit 1e8aac
    gtk_widget_class_set_connect_func (g_class, data->connect_func, data->connect_data, NULL);
Packit 1e8aac
  else
Packit 1e8aac
    gtk_widget_class_set_connect_func (g_class, template_connect_function, NULL, NULL);
Packit 1e8aac
}
Packit 1e8aac
Packit 1e8aac
static GQuark type_data_quark = 0;
Packit 1e8aac
Packit 1e8aac
static GType
Packit 1e8aac
template_generate_type (const gchar *name,
Packit 1e8aac
                        const gchar *parent_name,
Packit 1e8aac
                        GString *template_string,
Packit 1e8aac
                        GtkBuilderConnectFunc connect_func,
Packit 1e8aac
                        gpointer connect_data)
Packit 1e8aac
{
Packit 1e8aac
  GType parent_type, retval;
Packit 1e8aac
  gchar *real_name = NULL;
Packit 1e8aac
  GTypeQuery query;
Packit 1e8aac
  TypeData *data;
Packit 1e8aac
Packit 1e8aac
  g_return_val_if_fail (name != NULL, 0);
Packit 1e8aac
  g_return_val_if_fail (parent_name != NULL, 0);
Packit 1e8aac
Packit 1e8aac
  parent_type = glade_util_get_type_from_name (parent_name, FALSE);
Packit 1e8aac
  g_return_val_if_fail (parent_type != 0, 0);
Packit 1e8aac
Packit 1e8aac
  if ((retval = g_type_from_name (name)) &&
Packit 1e8aac
      (data = g_type_get_qdata (retval, type_data_quark)))
Packit 1e8aac
    {
Packit 1e8aac
      /* Type already registered! reuse TypeData
Packit 1e8aac
       * 
Packit 1e8aac
       * If the template and parent class are the same there is no need to
Packit 1e8aac
       * register a new type
Packit 1e8aac
       */
Packit 1e8aac
      if (g_type_parent (retval) == parent_type &&
Packit 1e8aac
          template_string->len == data->template_string->len && 
Packit 1e8aac
          g_strcmp0 (template_string->str, data->template_string->str) == 0)
Packit 1e8aac
        return retval;
Packit 1e8aac
Packit 1e8aac
      real_name = g_strdup_printf ("GladePreviewTemplate_%s_%d", name, data->count);
Packit 1e8aac
    }
Packit 1e8aac
  else
Packit 1e8aac
    {
Packit 1e8aac
      /* We only allocate a TypeData struct once for each type class */
Packit 1e8aac
      data = g_new0 (TypeData, 1);
Packit 1e8aac
    }
Packit 1e8aac
Packit 1e8aac
  g_type_query (parent_type, &query);
Packit 1e8aac
  g_return_val_if_fail (query.type != 0, 0);
Packit 1e8aac
Packit 1e8aac
  /* Free old template string */
Packit 1e8aac
  if (data->template_string)
Packit 1e8aac
    g_string_free (data->template_string, TRUE);
Packit 1e8aac
Packit 1e8aac
  /* And old bytes reference to template string */
Packit 1e8aac
  if (data->template_data)
Packit 1e8aac
    g_bytes_unref (data->template_data);
Packit 1e8aac
Packit 1e8aac
  /* Take ownership, will be freed next time we want to create this type */
Packit 1e8aac
  data->template_string = template_string;
Packit 1e8aac
Packit 1e8aac
  data->info.class_size = query.class_size;
Packit 1e8aac
  data->info.instance_size = query.instance_size;
Packit 1e8aac
  data->info.class_init = template_class_init;
Packit 1e8aac
  data->info.instance_init = template_init;
Packit 1e8aac
  data->info.class_data = data;
Packit 1e8aac
  data->template_data = g_bytes_new_static (template_string->str, template_string->len);
Packit 1e8aac
  data->connect_func = connect_func;
Packit 1e8aac
  data->connect_data = connect_data;
Packit 1e8aac
Packit 1e8aac
  retval = g_type_register_static (parent_type, real_name ? real_name : name, &data->info, 0);
Packit 1e8aac
Packit 1e8aac
  /* bind TypeData struct with GType */
Packit 1e8aac
  if (!data->count)
Packit 1e8aac
    g_type_set_qdata (retval, type_data_quark, data);
Packit 1e8aac
Packit 1e8aac
  data->count++;
Packit 1e8aac
Packit 1e8aac
  g_free (real_name);
Packit 1e8aac
Packit 1e8aac
  return retval;
Packit 1e8aac
}
Packit 1e8aac
Packit 1e8aac
typedef struct
Packit 1e8aac
{
Packit 1e8aac
  gboolean is_template;
Packit 1e8aac
  GString *xml;
Packit 1e8aac
  gchar *klass, *parent;
Packit 1e8aac
  gint indent;
Packit 1e8aac
} ParseData;
Packit 1e8aac
Packit 1e8aac
static void
Packit 1e8aac
start_element (GMarkupParseContext  *context,
Packit 1e8aac
               const gchar          *element_name,
Packit 1e8aac
               const gchar         **attribute_names,
Packit 1e8aac
               const gchar         **attribute_values,
Packit 1e8aac
               gpointer              user_data,
Packit 1e8aac
               GError              **error)
Packit 1e8aac
{
Packit 1e8aac
  ParseData *state = user_data;
Packit 1e8aac
  gboolean is_template = FALSE;
Packit 1e8aac
  gint i;
Packit 1e8aac
Packit 1e8aac
  g_string_append_printf (state->xml, "<%s", element_name);
Packit 1e8aac
Packit 1e8aac
  if (g_strcmp0 (element_name, "template") == 0)
Packit 1e8aac
    state->is_template = is_template = TRUE;
Packit 1e8aac
Packit 1e8aac
  for (i = 0; attribute_names[i]; i++)
Packit 1e8aac
    {
Packit 1e8aac
      gchar *escaped_value = g_markup_escape_text (attribute_values[i], -1);
Packit 1e8aac
Packit 1e8aac
      if (is_template)
Packit 1e8aac
        {
Packit 1e8aac
          if (!g_strcmp0 (attribute_names[i], "class"))
Packit 1e8aac
            {
Packit 1e8aac
              TypeData *data;
Packit 1e8aac
              GType type;
Packit 1e8aac
Packit 1e8aac
              state->klass = g_strdup (attribute_values[i]);
Packit 1e8aac
Packit 1e8aac
              /* If we are in a template then we need to replace the class with
Packit 1e8aac
               * the fake name template_generate_type() will use to register
Packit 1e8aac
                 * a new class
Packit 1e8aac
               */
Packit 1e8aac
              if ((type = g_type_from_name (state->klass)) &&
Packit 1e8aac
                  (data = g_type_get_qdata (type, type_data_quark)))
Packit 1e8aac
                {
Packit 1e8aac
                  g_free (escaped_value);
Packit 1e8aac
                  escaped_value = g_strdup_printf ("GladePreviewTemplate_%s_%d", state->klass, data->count);
Packit 1e8aac
                }
Packit 1e8aac
            }
Packit 1e8aac
          else if (!g_strcmp0 (attribute_names[i], "parent"))
Packit 1e8aac
            state->parent = g_strdup (attribute_values[i]);
Packit 1e8aac
        }
Packit 1e8aac
Packit 1e8aac
      g_string_append_printf (state->xml, " %s=\"%s\"",
Packit 1e8aac
                              attribute_names[i], escaped_value);
Packit 1e8aac
      g_free (escaped_value);
Packit 1e8aac
    }
Packit 1e8aac
Packit 1e8aac
  g_string_append (state->xml, ">");
Packit 1e8aac
}
Packit 1e8aac
Packit 1e8aac
static void
Packit 1e8aac
end_element (GMarkupParseContext *context,
Packit 1e8aac
             const gchar         *element_name,
Packit 1e8aac
             gpointer             user_data,
Packit 1e8aac
             GError             **error)
Packit 1e8aac
{
Packit 1e8aac
  ParseData *state = user_data;
Packit 1e8aac
  g_string_append_printf (state->xml, "</%s>", element_name);
Packit 1e8aac
}
Packit 1e8aac
Packit 1e8aac
static void
Packit 1e8aac
text (GMarkupParseContext *context,
Packit 1e8aac
      const gchar         *text,
Packit 1e8aac
      gsize                text_len,
Packit 1e8aac
      gpointer             user_data,
Packit 1e8aac
      GError             **error)
Packit 1e8aac
{
Packit 1e8aac
  gchar *escaped_text = g_markup_escape_text (text, text_len);
Packit 1e8aac
  ParseData *state = user_data;
Packit 1e8aac
Packit 1e8aac
  g_string_append_len (state->xml, escaped_text, -1);
Packit 1e8aac
Packit 1e8aac
  g_free (escaped_text);
Packit 1e8aac
}
Packit 1e8aac
Packit 1e8aac
static void
Packit 1e8aac
passthrough (GMarkupParseContext *context,
Packit 1e8aac
             const gchar         *text,
Packit 1e8aac
             gsize                text_len,
Packit 1e8aac
             gpointer             user_data,
Packit 1e8aac
             GError             **error)
Packit 1e8aac
{
Packit 1e8aac
  ParseData *state = user_data;
Packit 1e8aac
  g_string_append_len (state->xml, text, text_len);
Packit 1e8aac
  g_string_append_c (state->xml, '\n');
Packit 1e8aac
}
Packit 1e8aac
Packit 1e8aac
GObject *
Packit 1e8aac
glade_preview_template_object_new (const gchar          *template_data,
Packit 1e8aac
                                   gsize                 len,
Packit 1e8aac
                                   GtkBuilderConnectFunc connect_func,
Packit 1e8aac
                                   gpointer              connect_data)
Packit 1e8aac
{
Packit 1e8aac
  GMarkupParser parser = { start_element, end_element, text, passthrough};
Packit 1e8aac
  ParseData state = { FALSE, NULL};
Packit 1e8aac
  GMarkupParseContext *context;
Packit 1e8aac
  GObject *object = NULL;
Packit 1e8aac
Packit 1e8aac
  if (type_data_quark == 0)
Packit 1e8aac
    type_data_quark = g_quark_from_string ("glade-preview-type-data");
Packit 1e8aac
Packit 1e8aac
  if (len == -1)
Packit 1e8aac
    len = strlen (template_data);
Packit 1e8aac
Packit 1e8aac
  /* Preallocate enough for the string plus the new fake template name */
Packit 1e8aac
  state.xml = g_string_sized_new (len + 32);
Packit 1e8aac
Packit 1e8aac
  context = g_markup_parse_context_new (&parser,
Packit 1e8aac
                                        G_MARKUP_TREAT_CDATA_AS_TEXT |
Packit 1e8aac
                                        G_MARKUP_PREFIX_ERROR_POSITION,
Packit 1e8aac
                                        &state, NULL);
Packit 1e8aac
Packit 1e8aac
  if (g_markup_parse_context_parse (context, template_data, len, NULL) &&
Packit 1e8aac
      g_markup_parse_context_end_parse (context, NULL) && 
Packit 1e8aac
      state.is_template)
Packit 1e8aac
    {
Packit 1e8aac
      GType template_type = template_generate_type (state.klass,
Packit 1e8aac
                                                    state.parent,
Packit 1e8aac
                                                    state.xml,
Packit 1e8aac
                                                    connect_func,
Packit 1e8aac
                                                    connect_data);
Packit 1e8aac
      if (template_type)
Packit 1e8aac
        object = g_object_new (template_type, NULL);
Packit 1e8aac
      else
Packit 1e8aac
        {
Packit 1e8aac
          /* on success template_generate_type() takes ownership of xml */
Packit 1e8aac
          g_string_free (state.xml, TRUE);
Packit 1e8aac
        }
Packit 1e8aac
    }
Packit 1e8aac
  else
Packit 1e8aac
    g_string_free (state.xml, TRUE);
Packit 1e8aac
Packit 1e8aac
  g_free (state.klass);
Packit 1e8aac
  g_free (state.parent);
Packit 1e8aac
  g_markup_parse_context_free (context);
Packit 1e8aac
Packit 1e8aac
  return object ? g_object_ref_sink (object) : NULL;
Packit 1e8aac
}