[glade] Added intitial support for runtime creation and loading of composite templates.
- From: Juan Pablo Ugarte <jpu src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [glade] Added intitial support for runtime creation and loading of composite templates.
- Date: Tue, 2 Oct 2012 19:44:32 +0000 (UTC)
commit 856a93ad0efe6608b9db0b967e0b7332111e68d6
Author: Juan Pablo Ugarte <juanpablougarte gmail com>
Date: Tue Oct 2 16:12:16 2012 -0300
Added intitial support for runtime creation and loading of composite templates.
gladeui/glade-app.c: Load composite templates from G_USER_DIRECTORY_TEMPLATES
gladeui/glade-project.[ch]
o Added glade_project_dump_string()
o Addes safe guards for NULL catalogs (composite template adaptors does not have a catalog)
gladeui/glade-widget-adaptor.[ch]
o Added template and template-path properties
o Added glade_widget_adaptor_get_template() and
glade_widget_adaptor_from_composite_template()
gladeui/glade-composite-template.[ch]: Added support to load composite templates and export a widget as such.
plugins/gtk+/glade-gtk.c, plugins/gtk+/gtk+.xml.in: added "Export as template" action
gladeui/Makefile.am | 2 +
gladeui/glade-app.c | 3 +
gladeui/glade-composite-template.c | 260 ++++++++++++++++++++++++++++++++++
gladeui/glade-composite-template.h | 42 ++++++
gladeui/glade-project.c | 35 +++++-
gladeui/glade-project.h | 1 +
gladeui/glade-widget-adaptor.c | 269 +++++++++++++++++++++++++++++-------
gladeui/glade-widget-adaptor.h | 22 ++-
gladeui/glade.h | 1 +
plugins/gtk+/glade-gtk.c | 69 +++++++++
plugins/gtk+/gtk+.xml.in | 5 +-
11 files changed, 651 insertions(+), 58 deletions(-)
---
diff --git a/gladeui/Makefile.am b/gladeui/Makefile.am
index 141aaed..288848d 100644
--- a/gladeui/Makefile.am
+++ b/gladeui/Makefile.am
@@ -53,6 +53,7 @@ libgladeui_2_la_SOURCES = \
glade-object-stub.c \
glade-xml-utils.c \
glade-catalog.c \
+ glade-composite-template.c \
glade-widget-adaptor.c \
glade-widget.c \
glade-property-class.c \
@@ -122,6 +123,7 @@ libgladeuiinclude_HEADERS = \
glade-design-view.h \
glade-widget.h \
glade-widget-adaptor.h \
+ glade-composite-template.h \
glade-property.h \
glade-property-class.h \
glade-utils.h \
diff --git a/gladeui/glade-app.c b/gladeui/glade-app.c
index fd1bedb..f5d0a3e 100644
--- a/gladeui/glade-app.c
+++ b/gladeui/glade-app.c
@@ -39,6 +39,7 @@
#include "glade-design-layout.h"
#include "glade-marshallers.h"
#include "glade-accumulators.h"
+#include "glade-composite-template.h"
#include <string.h>
#include <glib.h>
@@ -348,6 +349,8 @@ glade_app_init (GladeApp *app)
/* Load the configuration file */
priv->config = g_key_file_ref (glade_app_get_config ());
+
+ glade_composite_template_load_directory (g_get_user_special_dir (G_USER_DIRECTORY_TEMPLATES));
}
static void
diff --git a/gladeui/glade-composite-template.c b/gladeui/glade-composite-template.c
new file mode 100644
index 0000000..80a331c
--- /dev/null
+++ b/gladeui/glade-composite-template.c
@@ -0,0 +1,260 @@
+/*
+ * glade-composite-template.c
+ *
+ * Copyright (C) 2012 Juan Pablo Ugarte
+ *
+ * Author: Juan Pablo Ugarte <juanpablougarte gmail com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU 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 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 "glade-composite-template.h"
+#include "glade-app.h"
+#include "glade-utils.h"
+
+typedef struct
+{
+ gboolean right_id;
+ GType parent;
+ const gchar *type_name;
+} ParseData;
+
+static void
+start_element (GMarkupParseContext *context,
+ const gchar *element_name,
+ const gchar **attribute_names,
+ const gchar **attribute_values,
+ gpointer user_data,
+ GError **error)
+{
+ ParseData *state = user_data;
+
+ if (g_strcmp0 (element_name, "template") == 0)
+ {
+ gint i;
+
+ for (i = 0; attribute_names[i]; i++)
+ {
+ if (!g_strcmp0 (attribute_names[i], "parent"))
+ state->parent = glade_util_get_type_from_name (attribute_values[i], FALSE);
+ else if (!g_strcmp0 (attribute_names[i], "class"))
+ state->type_name = g_intern_string (attribute_values[i]);
+ else if (!g_strcmp0 (attribute_names[i], "id"))
+ state->right_id = (g_strcmp0 (attribute_values[i], "this") == 0);
+ }
+ }
+}
+
+static gboolean
+parse_template (const gchar *template_str, GType *parent, const gchar **type_name)
+{
+ GMarkupParser parser = { start_element };
+ ParseData state = { FALSE, G_TYPE_INVALID, NULL };
+ GMarkupParseContext *context;
+
+ context = g_markup_parse_context_new (&parser,
+ G_MARKUP_TREAT_CDATA_AS_TEXT |
+ G_MARKUP_PREFIX_ERROR_POSITION,
+ &state, NULL);
+
+ g_markup_parse_context_parse (context, template_str, -1, NULL);
+ g_markup_parse_context_end_parse (context, NULL);
+ g_markup_parse_context_free (context);
+
+ if (!g_type_is_a (state.parent, GTK_TYPE_CONTAINER))
+ {
+ g_warning ("Composite templates should derive from GtkContainer");
+ return FALSE;
+ }
+
+ if (parent) *parent = state.parent;
+ if (type_name) *type_name = state.type_name;
+
+ return state.right_id;
+}
+
+static void
+composite_template_derived_init (GTypeInstance *instance, gpointer g_class)
+{
+}
+
+static void
+composite_template_derived_class_init (gpointer g_class, gpointer class_data)
+{
+}
+
+static inline GType
+generate_type (GType parent, const gchar *type_name)
+{
+ GTypeQuery query;
+
+ g_type_query (parent, &query);
+
+ return g_type_register_static_simple (parent, type_name,
+ query.class_size,
+ composite_template_derived_class_init,
+ query.instance_size,
+ composite_template_derived_init,
+ 0);
+}
+
+/* Public API */
+
+/**
+ * glade_composite_template_load_from_string:
+ * @template_xml: a #GtkBuilder UI description string
+ *
+ * This function will create a new GType from the template UI description defined
+ * by @template_xml and its corresponding #GladeWidgetAdator
+ *
+ * Returns: A newlly created and registered #GladeWidgetAdptor or NULL if @template_xml is malformed.
+ */
+GladeWidgetAdaptor *
+glade_composite_template_load_from_string (const gchar *template_xml)
+{
+ const gchar *type_name = NULL;
+ GType parent;
+
+ g_return_val_if_fail (template_xml != NULL, NULL);
+
+ if (parse_template (template_xml, &parent, &type_name))
+ {
+ GladeWidgetAdaptor *adaptor;
+ GType template_type;
+
+ /* Generate dummy template type */
+ template_type = generate_type (parent, type_name);
+
+ /* Create adaptor for template */
+ adaptor = glade_widget_adaptor_from_composite_template (template_type,
+ template_xml,
+ type_name,
+ NULL); /* TODO: generate icon name from parent icon plus some emblem */
+ /* Register adaptor */
+ glade_widget_adaptor_register (adaptor);
+
+ return adaptor;
+ }
+ else
+ g_warning ("Could not parse template");
+
+ return NULL;
+}
+
+/**
+ * glade_composite_template_load_from_file:
+ * @path: a filename to load
+ *
+ * Loads a composite template from a file.
+ * See glade_composite_template_load_from_string() for details.
+ *
+ * Returns: A newlly created and registered #GladeWidgetAdaptor or NULL.
+ */
+GladeWidgetAdaptor *
+glade_composite_template_load_from_file (const gchar *path)
+{
+ GladeWidgetAdaptor *adaptor;
+ GError *error = NULL;
+ gchar *contents;
+
+ g_return_val_if_fail (path != NULL, NULL);
+
+ if (!g_file_get_contents (path, &contents, NULL, &error))
+ {
+ g_warning ("Could not load template `%s` %s", path, error->message);
+ g_error_free (error);
+ return NULL;
+ }
+
+ if ((adaptor = glade_composite_template_load_from_string (contents)))
+ g_object_set (adaptor, "template-path", path, NULL);
+
+ g_free (contents);
+
+ return adaptor;
+}
+
+/**
+ * glade_composite_template_load_directory:
+ * @directory: a directory path.
+ *
+ * Loads every .ui composite template found in @directory
+ */
+void
+glade_composite_template_load_directory (const gchar *directory)
+{
+ GError *error = NULL;
+ const gchar *name;
+ GDir *dir;
+
+ g_return_if_fail (path != NULL);
+
+ if (!(dir = g_dir_open (directory, 0, &error)))
+ {
+ g_warning ("Could not open directory `%s` %s", directory, error->message);
+ g_error_free (error);
+ return;
+ }
+
+ while ((name = g_dir_read_name (dir)))
+ {
+ if (g_str_has_suffix (name, ".ui"))
+ {
+ gchar *fullname = g_build_filename (directory, name, NULL);
+ glade_composite_template_load_from_file (fullname);
+ g_free (fullname);
+ }
+ }
+
+ g_dir_close (dir);
+}
+
+/**
+ * glade_composite_template_save_from_widget:
+ * @gwidget: a #GladeWidget
+ * @template_class: the name of the new composite template class
+ * @filename: a file name to save the template
+ *
+ * Saves a copy of @gwidget as a composite template in @filename with @template_class
+ * as the class name
+ */
+void
+glade_composite_template_save_from_widget (GladeWidget *gwidget,
+ const gchar *template_class,
+ const gchar *filename)
+{
+ GladeProject *project;
+ gchar *template_xml;
+ GladeWidget *dup;
+
+ g_return_if_fail (GLADE_IS_WIDGET (gwidget));
+ g_return_if_fail (template_class && filename);
+ g_return_if_fail (GTK_IS_CONTAINER (glade_widget_get_object (gwidget)));
+
+ project = glade_project_new ();
+ dup = glade_widget_dup (gwidget, TRUE);
+
+ /* make dupped widget a template */
+ glade_widget_set_name (dup, "this");
+ glade_widget_set_template_class (dup, template_class);
+
+ glade_project_add_object (project, glade_widget_get_object (dup));
+ template_xml = glade_project_dump_string (project);
+
+ g_file_set_contents (filename, template_xml, -1, NULL);
+
+ g_free (template_xml);
+ g_object_unref (project);
+}
diff --git a/gladeui/glade-composite-template.h b/gladeui/glade-composite-template.h
new file mode 100644
index 0000000..557cea9
--- /dev/null
+++ b/gladeui/glade-composite-template.h
@@ -0,0 +1,42 @@
+/*
+ * glade-composite-template.h
+ *
+ * Copyright (C) 2012 Juan Pablo Ugarte
+ *
+ * Author: Juan Pablo Ugarte <juanpablougarte gmail com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU 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 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_COMPOSITE_TEMPLATE_H__
+#define __GLADE_COMPOSITE_TEMPLATE_H__
+
+#include <gladeui/glade-widget.h>
+
+G_BEGIN_DECLS
+
+GladeWidgetAdaptor *glade_composite_template_load_from_string (const gchar *template_xml);
+
+GladeWidgetAdaptor *glade_composite_template_load_from_file (const gchar *path);
+
+void glade_composite_template_load_directory (const gchar *directory);
+
+void glade_composite_template_save_from_widget (GladeWidget *gwidget,
+ const gchar *template_class,
+ const gchar *filename);
+
+G_END_DECLS
+
+#endif /* __GLADE_COMPOSITE_TEMPLATE_H__ */
diff --git a/gladeui/glade-project.c b/gladeui/glade-project.c
index 7b1cf02..e9aa181 100644
--- a/gladeui/glade-project.c
+++ b/gladeui/glade-project.c
@@ -1994,6 +1994,25 @@ glade_project_save (GladeProject *project, const gchar *path, GError **error)
}
/**
+ * glade_project_dump_string:
+ * @project: a #GladeProject
+ *
+ * Returns: @project as a newlly allocated string
+ */
+gchar *
+glade_project_dump_string (GladeProject *project)
+{
+ GladeXmlContext *context;
+ gchar *retval;
+
+ context = glade_project_write (project);
+ retval = glade_xml_dump_from_context (context);
+ glade_xml_context_destroy (context);
+
+ return retval;
+}
+
+/**
* glade_project_preview:
* @project: a #GladeProject
* @gwidget: a #GladeWidget
@@ -2122,6 +2141,13 @@ glade_project_verify_property_internal (GladeProject *project,
adaptor = glade_widget_adaptor_from_pspec (prop_adaptor, pspec);
g_object_get (adaptor, "catalog", &catalog, NULL);
+
+ /* no need to check if there is no catalog because its a composite widget
+ * automagically loaded by libgladeui
+ */
+ if (catalog == NULL && glade_widget_adaptor_get_template (adaptor))
+ return;
+
glade_project_target_version_for_adaptor (project, adaptor,
&target_major, &target_minor);
@@ -2207,6 +2233,10 @@ glade_project_verify_signal_internal (GladeWidget *widget,
adaptor = glade_signal_class_get_adaptor (signal_class);
g_object_get (adaptor, "catalog", &catalog, NULL);
+
+ if (catalog == NULL && glade_widget_adaptor_get_template (adaptor))
+ return;
+
glade_project_target_version_for_adaptor (glade_widget_get_project (widget),
adaptor,
&target_major, &target_minor);
@@ -2427,8 +2457,11 @@ glade_project_verify_adaptor (GladeProject *project,
for (adaptor_iter = adaptor; adaptor_iter && support_mask == GLADE_SUPPORT_OK;
adaptor_iter = glade_widget_adaptor_get_parent_adaptor (adaptor_iter))
{
-
g_object_get (adaptor_iter, "catalog", &catalog, NULL);
+
+ if (catalog == NULL && glade_widget_adaptor_get_template (adaptor))
+ continue;
+
glade_project_target_version_for_adaptor (project, adaptor_iter,
&target_major, &target_minor);
diff --git a/gladeui/glade-project.h b/gladeui/glade-project.h
index cdde53d..0910943 100644
--- a/gladeui/glade-project.h
+++ b/gladeui/glade-project.h
@@ -121,6 +121,7 @@ gboolean glade_project_load_from_file (GladeProject *proj
gboolean glade_project_save (GladeProject *project,
const gchar *path,
GError **error);
+gchar *glade_project_dump_string (GladeProject *project);
void glade_project_push_progress (GladeProject *project);
gboolean glade_project_load_cancelled (GladeProject *project);
void glade_project_cancel_load (GladeProject *project);
diff --git a/gladeui/glade-widget-adaptor.c b/gladeui/glade-widget-adaptor.c
index 4078dfb..309c67a 100644
--- a/gladeui/glade-widget-adaptor.c
+++ b/gladeui/glade-widget-adaptor.c
@@ -102,6 +102,8 @@ struct _GladeWidgetAdaptorPrivate
* are special children (like notebook tab
* widgets for example).
*/
+ gchar *template_xml; /* The GtkBuilder template if this is a composite template class */
+ GFileMonitor *template_monitor;
};
struct _GladeChildPacking
@@ -135,7 +137,9 @@ enum
PROP_CATALOG,
PROP_BOOK,
PROP_SPECIAL_TYPE,
- PROP_CURSOR
+ PROP_CURSOR,
+ PROP_TEMPLATE,
+ PROP_TEMPLATE_PATH
};
typedef struct _GladeChildPacking GladeChildPacking;
@@ -380,7 +384,7 @@ gwa_clone_parent_properties (GladeWidgetAdaptor *adaptor, gboolean is_packing)
parent_adaptor->priv->packing_props : parent_adaptor->priv->properties;
/* Reset versioning in derived catalogs just once */
- reset_version = strcmp (adaptor->priv->catalog, parent_adaptor->priv->catalog) != 0;
+ reset_version = g_strcmp0 (adaptor->priv->catalog, parent_adaptor->priv->catalog) != 0;
for (list = proplist; list; list = list->next)
{
@@ -533,8 +537,8 @@ gwa_inherit_signals (GladeWidgetAdaptor *adaptor)
parent_signal = node->data;
/* Reset versioning in derived catalogs just once */
- if (strcmp (adaptor->priv->catalog,
- parent_adaptor->priv->catalog))
+ if (g_strcmp0 (adaptor->priv->catalog,
+ parent_adaptor->priv->catalog))
glade_signal_class_set_since (signal, 0, 0);
else
glade_signal_class_set_since (signal,
@@ -624,7 +628,7 @@ glade_widget_adaptor_constructor (GType type,
/* Reset version numbering if we're in a new catalog just once */
if (parent_adaptor &&
- strcmp (adaptor->priv->catalog, parent_adaptor->priv->catalog))
+ g_strcmp0 (adaptor->priv->catalog, parent_adaptor->priv->catalog))
{
GLADE_WIDGET_ADAPTOR_GET_CLASS (adaptor)->version_since_major =
GLADE_WIDGET_ADAPTOR_GET_CLASS (adaptor)->version_since_minor = 0;
@@ -711,59 +715,48 @@ static void
glade_widget_adaptor_finalize (GObject *object)
{
GladeWidgetAdaptor *adaptor = GLADE_WIDGET_ADAPTOR (object);
-
+ GladeWidgetAdaptorPrivate *priv = adaptor->priv;
+
/* Free properties and signals */
- g_list_foreach (adaptor->priv->properties, (GFunc) glade_property_class_free, NULL);
- g_list_free (adaptor->priv->properties);
+ g_list_foreach (priv->properties, (GFunc) glade_property_class_free, NULL);
+ g_list_free (priv->properties);
- g_list_foreach (adaptor->priv->packing_props, (GFunc) glade_property_class_free,
+ g_list_foreach (priv->packing_props, (GFunc) glade_property_class_free,
NULL);
- g_list_free (adaptor->priv->packing_props);
+ g_list_free (priv->packing_props);
/* Be careful, this list holds GladeSignalClass* not GladeSignal,
* thus g_free is enough as all members are const */
- g_list_foreach (adaptor->priv->signals, (GFunc) g_free, NULL);
- g_list_free (adaptor->priv->signals);
-
+ g_list_foreach (priv->signals, (GFunc) g_free, NULL);
+ g_list_free (priv->signals);
/* Free child packings */
- g_list_foreach (adaptor->priv->child_packings,
- (GFunc) gwa_child_packing_free, NULL);
- g_list_free (adaptor->priv->child_packings);
-
- if (adaptor->priv->book)
- g_free (adaptor->priv->book);
- if (adaptor->priv->catalog)
- g_free (adaptor->priv->catalog);
- if (adaptor->priv->special_child_type)
- g_free (adaptor->priv->special_child_type);
-
- if (adaptor->priv->cursor != NULL)
- g_object_unref (adaptor->priv->cursor);
-
- if (adaptor->priv->name)
- g_free (adaptor->priv->name);
- if (adaptor->priv->generic_name)
- g_free (adaptor->priv->generic_name);
- if (adaptor->priv->title)
- g_free (adaptor->priv->title);
- if (adaptor->priv->icon_name)
- g_free (adaptor->priv->icon_name);
- if (adaptor->priv->missing_icon)
- g_free (adaptor->priv->missing_icon);
-
- if (adaptor->priv->actions)
+ g_list_foreach (priv->child_packings, (GFunc) gwa_child_packing_free, NULL);
+ g_list_free (priv->child_packings);
+
+ g_free (priv->book);
+ g_free (priv->catalog);
+ g_free (priv->special_child_type);
+ g_clear_object (&priv->cursor);
+ g_free (priv->name);
+ g_free (priv->generic_name);
+ g_free (priv->title);
+ g_free (priv->icon_name);
+ g_free (priv->missing_icon);
+ g_free (priv->template_xml);
+
+ if (priv->actions)
{
- g_list_foreach (adaptor->priv->actions,
+ g_list_foreach (priv->actions,
(GFunc) glade_widget_action_class_free, NULL);
- g_list_free (adaptor->priv->actions);
+ g_list_free (priv->actions);
}
- if (adaptor->priv->packing_actions)
+ if (priv->packing_actions)
{
- g_list_foreach (adaptor->priv->packing_actions,
+ g_list_foreach (priv->packing_actions,
(GFunc) glade_widget_action_class_free, NULL);
- g_list_free (adaptor->priv->packing_actions);
+ g_list_free (priv->packing_actions);
}
gwa_internal_children_free (adaptor);
@@ -771,6 +764,103 @@ glade_widget_adaptor_finalize (GObject *object)
G_OBJECT_CLASS (glade_widget_adaptor_parent_class)->finalize (object);
}
+static inline void
+gwa_template_rebuild_objects (GladeWidgetAdaptor *adaptor, GType object_type)
+{
+ GList *l, *rebuild = NULL;
+
+ /* Iterate all projects */
+ for (l = glade_app_get_projects (); l; l = g_list_next (l))
+ {
+ GladeProject *project = l->data;
+ const GList *o;
+
+ /* Iterate all objects in the project */
+ for (o = glade_project_get_objects (project); o; o = g_list_next (o))
+ {
+ GObject *obj = o->data;
+
+ /* And rebuild widget if its template just changed */
+ if (g_type_is_a (G_OBJECT_TYPE (obj), object_type))
+ rebuild = g_list_prepend (rebuild, glade_widget_get_from_gobject (obj));
+ }
+ }
+
+ for (l = rebuild; l; l = g_list_next (l))
+ glade_widget_rebuild (l->data);
+
+ g_list_free (rebuild);
+}
+
+static inline void
+glade_widget_adaptor_set_template (GladeWidgetAdaptor *adaptor,
+ const gchar *template_xml)
+{
+ GladeWidgetAdaptorPrivate *priv = adaptor->priv;
+ GtkContainerClass *klass;
+ GType object_type;
+
+ if (g_strcmp0 (priv->template_xml, template_xml) == 0)
+ return;
+
+ g_free (priv->template_xml);
+ priv->template_xml = g_strdup (template_xml);
+
+ /* Update container class template */
+ object_type = glade_widget_adaptor_get_object_type (adaptor);
+ klass = g_type_class_peek (object_type);
+ gtk_container_class_set_template_from_string (klass, priv->template_xml);
+ gwa_template_rebuild_objects (adaptor, object_type);
+}
+
+static void
+on_template_file_changed (GFileMonitor *monitor,
+ GFile *file,
+ GFile *other_file,
+ GFileMonitorEvent event_type,
+ GladeWidgetAdaptor *adaptor)
+{
+ gchar *contents = NULL;
+ gsize len;
+
+ if (event_type != G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT)
+ return;
+
+ if (g_file_load_contents (file, NULL, &contents, &len, NULL, NULL))
+ {
+ g_object_set (adaptor, "template", contents, NULL);
+ g_free (contents);
+ }
+}
+
+static inline void
+glade_widget_adaptor_set_template_path (GladeWidgetAdaptor *adaptor,
+ const gchar *path)
+{
+ GladeWidgetAdaptorPrivate *priv = adaptor->priv;
+ GFile *file = g_file_new_for_path (path);
+ GError *error = NULL;
+
+ g_clear_object (&priv->template_monitor);
+
+ /* Add watch for file */
+ priv->template_monitor = g_file_monitor_file (file, G_FILE_MONITOR_NONE, NULL, &error);
+
+ if (priv->template_monitor)
+ {
+ g_signal_connect (priv->template_monitor, "changed",
+ G_CALLBACK (on_template_file_changed),
+ adaptor);
+ }
+ else
+ {
+ g_warning ("Unable to monitor path `%s` %s", path, error->message);
+ g_error_free (error);
+ }
+
+ g_object_unref (file);
+}
+
static void
glade_widget_adaptor_real_set_property (GObject *object,
guint prop_id,
@@ -820,6 +910,12 @@ glade_widget_adaptor_real_set_property (GObject *object,
g_free (adaptor->priv->special_child_type);
adaptor->priv->special_child_type = g_value_dup_string (value);
break;
+ case PROP_TEMPLATE:
+ glade_widget_adaptor_set_template (adaptor, g_value_get_string (value));
+ break;
+ case PROP_TEMPLATE_PATH:
+ glade_widget_adaptor_set_template_path (adaptor, g_value_get_string (value));
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -866,6 +962,9 @@ glade_widget_adaptor_real_get_property (GObject *object,
case PROP_CURSOR:
g_value_set_pointer (value, adaptor->priv->cursor);
break;
+ case PROP_TEMPLATE:
+ g_value_set_string (value, adaptor->priv->template_xml);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -1455,6 +1554,20 @@ glade_widget_adaptor_class_init (GladeWidgetAdaptorClass *adaptor_class)
("cursor", _("Cursor"),
_("A cursor for inserting widgets in the UI"), G_PARAM_READABLE));
+ g_object_class_install_property
+ (object_class, PROP_TEMPLATE,
+ g_param_spec_string
+ ("template", _("Template"),
+ _("Builder template of the class"),
+ NULL, G_PARAM_READWRITE));
+
+ g_object_class_install_property
+ (object_class, PROP_TEMPLATE_PATH,
+ g_param_spec_string
+ ("template-path", _("Template path"),
+ _("Builder template file path of the class, if set it will be used to monitor and update template property automatically"),
+ NULL, G_PARAM_WRITABLE));
+
g_type_class_add_private (adaptor_class, sizeof (GladeWidgetAdaptorPrivate));
}
@@ -1632,9 +1745,14 @@ static void
gwa_derived_class_init (GladeWidgetAdaptorClass *adaptor_class,
GWADerivedClassData *data)
{
- GladeXmlNode *node = data->node;
- GModule *module = data->module;
+ GladeXmlNode *node;
+ GModule *module;
+
+ if (data == NULL) return;
+ node = data->node;
+ module = data->module;
+
/* Load catalog symbols from module */
if (module)
gwa_extend_with_node_load_sym (adaptor_class, node, module);
@@ -1705,7 +1823,6 @@ gwa_derive_adaptor_for_type (GType object_type, GWADerivedClassData *data)
return derived_type;
}
-
/*******************************************************************************
API
*******************************************************************************/
@@ -1781,6 +1898,14 @@ glade_widget_adaptor_get_signals (GladeWidgetAdaptor *adaptor)
return adaptor->priv->signals;
}
+G_CONST_RETURN gchar *
+glade_widget_adaptor_get_template (GladeWidgetAdaptor *adaptor)
+{
+ g_return_val_if_fail (GLADE_IS_WIDGET_ADAPTOR (adaptor), NULL);
+
+ return adaptor->priv->template_xml;
+}
+
static void
accum_adaptor (GType *type, GladeWidgetAdaptor *adaptor, GList **list)
{
@@ -2601,6 +2726,54 @@ generate_deprecated_icon (const gchar *icon_name)
}
/**
+ * glade_widget_adaptor_from_composite_template:
+ * @template_type: a #GType
+ * @template_xml: composite template ui description
+ * @generic_name: the genereic name of the adaptor
+ * @icon_name: the icon name for the adaptor or NULL to fallback to the parent
+ *
+ * Dynamicaly creates the corresponding adaptor for the template type.
+ */
+GladeWidgetAdaptor *
+glade_widget_adaptor_from_composite_template (GType template_type,
+ const gchar *template_xml,
+ const gchar *generic_name,
+ const gchar *icon_name)
+{
+ gchar *adaptor_icon_name = NULL;
+ GladeWidgetAdaptor *adaptor;
+ GType adaptor_type;
+ const gchar *name;
+
+ g_return_val_if_fail (g_type_is_a (template_type, GTK_TYPE_CONTAINER), NULL);
+
+ adaptor_type = gwa_derive_adaptor_for_type (template_type, NULL);
+ name = g_type_name (template_type);
+
+ /* Fallback to parent icon */
+ if (!icon_name)
+ {
+ GladeWidgetAdaptor *parent = glade_widget_adaptor_get_parent_adaptor_by_type (template_type);
+ adaptor_icon_name = g_strdup ((parent && parent->priv->icon_name) ?
+ parent->priv->icon_name : DEFAULT_ICON_NAME);
+ }
+
+ adaptor = g_object_new (adaptor_type,
+ "type", template_type,
+ "template", template_xml,
+ "name", name,
+ "catalog", NULL, /* yup NULL, it does not have a catalog */
+ "generic-name", generic_name,
+ "icon-name", icon_name ? icon_name : adaptor_icon_name,
+ "title", name,
+ NULL);
+
+ g_free (adaptor_icon_name);
+
+ return adaptor;
+}
+
+/**
* glade_widget_adaptor_from_catalog:
* @catalog: A #GladeCatalog
* @class_node: the #GladeXmlNode to load
diff --git a/gladeui/glade-widget-adaptor.h b/gladeui/glade-widget-adaptor.h
index ab33a53..6f07a16 100644
--- a/gladeui/glade-widget-adaptor.h
+++ b/gladeui/glade-widget-adaptor.h
@@ -185,8 +185,8 @@ typedef enum
* Returns: A newly created #GladeWidget for the said adaptor.
*/
typedef GladeWidget * (* GladeCreateWidgetFunc) (GladeWidgetAdaptor *adaptor,
- const gchar *first_property_name,
- va_list var_args);
+ const gchar *first_property_name,
+ va_list var_args);
/**
* GladeSetPropertyFunc:
@@ -201,9 +201,9 @@ typedef GladeWidget * (* GladeCreateWidgetFunc) (GladeWidgetAdaptor *adaptor,
* Sets @value on @object for a given #GladePropertyClass
*/
typedef void (* GladeSetPropertyFunc) (GladeWidgetAdaptor *adaptor,
- GObject *object,
- const gchar *property_name,
- const GValue *value);
+ GObject *object,
+ const gchar *property_name,
+ const GValue *value);
/**
* GladeGetPropertyFunc:
@@ -215,7 +215,7 @@ typedef void (* GladeSetPropertyFunc) (GladeWidgetAdaptor *adaptor,
* Gets @value on @object for a given #GladePropertyClass
*/
typedef void (* GladeGetPropertyFunc) (GladeWidgetAdaptor *adaptor,
- GObject *object,
+ GObject *object,
const gchar *property_name,
GValue *value);
@@ -686,12 +686,18 @@ G_CONST_RETURN gchar *glade_widget_adaptor_get_missing_icon (GladeWidgetAdaptor
G_CONST_RETURN GList *glade_widget_adaptor_get_properties (GladeWidgetAdaptor *adaptor);
G_CONST_RETURN GList *glade_widget_adaptor_get_packing_props(GladeWidgetAdaptor *adaptor);
G_CONST_RETURN GList *glade_widget_adaptor_get_signals (GladeWidgetAdaptor *adaptor);
+G_CONST_RETURN gchar *glade_widget_adaptor_get_template (GladeWidgetAdaptor *adaptor);
GList *glade_widget_adaptor_list_adaptors (void);
GladeWidgetAdaptor *glade_widget_adaptor_from_catalog (GladeCatalog *catalog,
- GladeXmlNode *class_node,
- GModule *module);
+ GladeXmlNode *class_node,
+ GModule *module);
+
+GladeWidgetAdaptor *glade_widget_adaptor_from_composite_template (GType template_type,
+ const gchar *template_xml,
+ const gchar *generic_name,
+ const gchar *icon_name);
void glade_widget_adaptor_register (GladeWidgetAdaptor *adaptor);
diff --git a/gladeui/glade.h b/gladeui/glade.h
index ddfb178..04dc406 100644
--- a/gladeui/glade.h
+++ b/gladeui/glade.h
@@ -46,5 +46,6 @@
#include <gladeui/glade-displayable-values.h>
#include <gladeui/glade-cell-renderer-icon.h>
#include <gladeui/glade-cursor.h>
+#include <gladeui/glade-composite-template.h>
#endif /* __GLADE_H__ */
diff --git a/plugins/gtk+/glade-gtk.c b/plugins/gtk+/glade-gtk.c
index cb1ca4a..121bf2c 100644
--- a/plugins/gtk+/glade-gtk.c
+++ b/plugins/gtk+/glade-gtk.c
@@ -1367,6 +1367,75 @@ glade_gtk_container_get_property (GladeWidgetAdaptor *adaptor,
else GWA_GET_CLASS (G_TYPE_OBJECT)->get_property (adaptor, object, id, value);
}
+void
+glade_gtk_container_action_activate (GladeWidgetAdaptor *adaptor,
+ GObject *object,
+ const gchar *action_path)
+{
+ GladeWidget *gwidget = glade_widget_get_from_gobject (object);
+
+ if (strcmp (action_path, "template") == 0)
+ {
+ GtkFileFilter *filter = gtk_file_filter_new ();
+ gchar *template_class, *template_file;
+ GtkWidget *dialog, *help_label;
+ GtkFileChooser *chooser;
+
+ gtk_file_filter_add_pattern (filter, "*.ui");
+
+ dialog = gtk_file_chooser_dialog_new (_("Save Template"),
+ GTK_WINDOW (glade_app_get_window ()),
+ GTK_FILE_CHOOSER_ACTION_SAVE,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
+ NULL);
+ chooser = GTK_FILE_CHOOSER (dialog);
+ gtk_file_chooser_set_do_overwrite_confirmation (chooser, TRUE);
+ gtk_file_chooser_set_filter (chooser, filter);
+ help_label = gtk_label_new (_("NOTE: the base name of the file will be used"
+ " as the class name so it should be in CamelCase"));
+ gtk_file_chooser_set_extra_widget (chooser, help_label);
+ gtk_widget_show (help_label);
+
+ template_class = g_strconcat ("My", G_OBJECT_TYPE_NAME (object), NULL);
+ template_file = g_strconcat (template_class, ".ui", NULL);
+
+ gtk_file_chooser_set_current_folder (chooser, g_get_user_special_dir (G_USER_DIRECTORY_TEMPLATES));
+ gtk_file_chooser_set_current_name (chooser, template_file);
+
+ if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
+ {
+ gchar *filename = gtk_file_chooser_get_filename (chooser);
+ gchar *basename = g_path_get_basename (filename);
+ gchar *dot = g_strrstr (basename, ".ui");
+
+ /* Strip file extension to get the class name */
+ if (dot)
+ {
+ *dot = '\0';
+ }
+ else
+ {
+ /* Or add it if it is not present */
+ gchar *tmp = g_strconcat (filename, ".ui", NULL);
+ g_free (filename);
+ filename = tmp;
+ }
+
+ glade_composite_template_save_from_widget (gwidget, basename, filename);
+
+ g_free (basename);
+ g_free (filename);
+ }
+
+ g_free (template_class);
+ g_free (template_file);
+ gtk_widget_destroy (dialog);
+ }
+ else
+ GWA_GET_CLASS (GTK_TYPE_WIDGET)->action_activate (adaptor, object, action_path);
+}
+
/* ----------------------------- GtkBox ------------------------------ */
GladeWidget *
diff --git a/plugins/gtk+/gtk+.xml.in b/plugins/gtk+/gtk+.xml.in
index f4846a0..7028319 100644
--- a/plugins/gtk+/gtk+.xml.in
+++ b/plugins/gtk+/gtk+.xml.in
@@ -297,7 +297,10 @@ embedded in another object</_tooltip>
<get-children-function>glade_gtk_container_get_children</get-children-function>
<set-property-function>glade_gtk_container_set_property</set-property-function>
<get-property-function>glade_gtk_container_get_property</get-property-function>
-
+ <action-activate-function>glade_gtk_container_action_activate</action-activate-function>
+ <actions>
+ <action id="template" _name="Export as template" stock="gtk-convert" important="True"/>
+ </actions>
<properties>
<property id="glade-template-class" _name="Template Class" save="False" custom-layout="True">
<parameter-spec>
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]