mirror of
https://gitlab.gnome.org/GNOME/glade.git
synced 2025-10-15 00:02:24 -04:00
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
This commit is contained in:
parent
451dd4024f
commit
856a93ad0e
@ -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 \
|
||||
|
@ -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
|
||||
|
260
gladeui/glade-composite-template.c
Normal file
260
gladeui/glade-composite-template.c
Normal file
@ -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);
|
||||
}
|
42
gladeui/glade-composite-template.h
Normal file
42
gladeui/glade-composite-template.h
Normal file
@ -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__ */
|
@ -1993,6 +1993,25 @@ glade_project_save (GladeProject *project, const gchar *path, GError **error)
|
||||
return ret > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
g_list_foreach (priv->child_packings, (GFunc) gwa_child_packing_free, NULL);
|
||||
g_list_free (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);
|
||||
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 (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)
|
||||
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)
|
||||
{
|
||||
@ -2600,6 +2725,54 @@ generate_deprecated_icon (const gchar *icon_name)
|
||||
return deprecated;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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__ */
|
||||
|
@ -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 *
|
||||
|
@ -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>
|
||||
|
Loading…
x
Reference in New Issue
Block a user