|
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 |
}
|