forked from AuroraMiddleware/gtk
Bug 447998 - GtkBuilder does not support building parts of the xml tree
2008-07-15 Paolo Borelli <pborelli@katamail.com> Bug 447998 - GtkBuilder does not support building parts of the xml tree * gtk/gtkbuilder.c: * gtk/gtkbuilder.h: * gtk/gtkbuilderprivate.h: * gtk/gtkbuilderparser.c: * gtk/gtk.symbols: Add two new functions that allow cherry picking and construct objects from a ui description file or string. * gtk/tests/builder.c: tests for the above. svn path=/trunk/; revision=20845
This commit is contained in:
parent
3e5cda3d3c
commit
6ee8be8899
14
ChangeLog
14
ChangeLog
@ -1,3 +1,17 @@
|
||||
2008-07-15 Paolo Borelli <pborelli@katamail.com>
|
||||
|
||||
Bug 447998 - GtkBuilder does not support building parts of the xml tree
|
||||
|
||||
* gtk/gtkbuilder.c:
|
||||
* gtk/gtkbuilder.h:
|
||||
* gtk/gtkbuilderprivate.h:
|
||||
* gtk/gtkbuilderparser.c:
|
||||
* gtk/gtk.symbols:
|
||||
Add two new functions that allow cherry picking and construct
|
||||
objects from a ui description file or string.
|
||||
|
||||
* gtk/tests/builder.c: tests for the above.
|
||||
|
||||
2008-07-15 Paolo Borelli <pborelli@katamail.com>
|
||||
|
||||
* gtk/tests/builder.c: fix up broken test (cellview has no "clicked"
|
||||
|
@ -478,6 +478,8 @@ GtkBuilderError
|
||||
gtk_builder_new
|
||||
gtk_builder_add_from_file
|
||||
gtk_builder_add_from_string
|
||||
gtk_builder_add_objects_from_file
|
||||
gtk_builder_add_objects_from_string
|
||||
gtk_builder_get_object
|
||||
gtk_builder_get_objects
|
||||
gtk_builder_connect_signals
|
||||
|
@ -445,6 +445,8 @@ gtk_buildable_set_buildable_property
|
||||
#if IN_FILE(__GTK_BUILDER_C__)
|
||||
gtk_builder_add_from_file
|
||||
gtk_builder_add_from_string
|
||||
gtk_builder_add_objects_from_file
|
||||
gtk_builder_add_objects_from_string
|
||||
gtk_builder_error_quark
|
||||
gtk_builder_get_object
|
||||
gtk_builder_get_objects
|
||||
|
126
gtk/gtkbuilder.c
126
gtk/gtkbuilder.c
@ -658,7 +658,7 @@ gtk_builder_add_from_file (GtkBuilder *builder,
|
||||
|
||||
g_return_val_if_fail (GTK_IS_BUILDER (builder), 0);
|
||||
g_return_val_if_fail (filename != NULL, 0);
|
||||
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
|
||||
g_return_val_if_fail (error == NULL || *error == NULL, 0);
|
||||
|
||||
tmp_error = NULL;
|
||||
|
||||
@ -673,6 +673,70 @@ gtk_builder_add_from_file (GtkBuilder *builder,
|
||||
|
||||
_gtk_builder_parser_parse_buffer (builder, filename,
|
||||
buffer, length,
|
||||
NULL,
|
||||
&tmp_error);
|
||||
|
||||
g_free (buffer);
|
||||
|
||||
if (tmp_error != NULL)
|
||||
{
|
||||
g_propagate_error (error, tmp_error);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_builder_add_objects_from_file:
|
||||
* @builder: a #GtkBuilder
|
||||
* @filename: the name of the file to parse
|
||||
* @object_ids: nul-terminated array of objects to build
|
||||
* @error: return location for an error, or %NULL
|
||||
*
|
||||
* Parses a file containing a <link linkend="BUILDER-UI">GtkBuilder
|
||||
* UI definition</link> building only the requested objects and merges
|
||||
* them with the current contents of @builder.
|
||||
*
|
||||
* <note><para>
|
||||
* If you are adding an object that depends on an object that is not
|
||||
* its child (for instance a #GtkTreeView that depends on its
|
||||
* #GtkTreeModel), you have to explicitely list all of them in @object_ids.
|
||||
* </para></note>
|
||||
*
|
||||
* Returns: A positive value on success, 0 if an error occurred
|
||||
*
|
||||
* Since: 2.14
|
||||
**/
|
||||
guint
|
||||
gtk_builder_add_objects_from_file (GtkBuilder *builder,
|
||||
const gchar *filename,
|
||||
gchar **object_ids,
|
||||
GError **error)
|
||||
{
|
||||
gchar *buffer;
|
||||
gsize length;
|
||||
GError *tmp_error;
|
||||
|
||||
g_return_val_if_fail (GTK_IS_BUILDER (builder), 0);
|
||||
g_return_val_if_fail (filename != NULL, 0);
|
||||
g_return_val_if_fail (object_ids != NULL && object_ids[0] != NULL, 0);
|
||||
g_return_val_if_fail (error == NULL || *error == NULL, 0);
|
||||
|
||||
tmp_error = NULL;
|
||||
|
||||
if (!g_file_get_contents (filename, &buffer, &length, &tmp_error))
|
||||
{
|
||||
g_propagate_error (error, tmp_error);
|
||||
return 0;
|
||||
}
|
||||
|
||||
g_free (builder->priv->filename);
|
||||
builder->priv->filename = g_strdup (filename);
|
||||
|
||||
_gtk_builder_parser_parse_buffer (builder, filename,
|
||||
buffer, length,
|
||||
object_ids,
|
||||
&tmp_error);
|
||||
|
||||
g_free (buffer);
|
||||
@ -695,7 +759,7 @@ gtk_builder_add_from_file (GtkBuilder *builder,
|
||||
*
|
||||
* Parses a string containing a <link linkend="BUILDER-UI">GtkBuilder
|
||||
* UI definition</link> and merges it with the current contents of @builder.
|
||||
*
|
||||
*
|
||||
* Returns: A positive value on success, 0 if an error occurred
|
||||
*
|
||||
* Since: 2.12
|
||||
@ -710,7 +774,7 @@ gtk_builder_add_from_string (GtkBuilder *builder,
|
||||
|
||||
g_return_val_if_fail (GTK_IS_BUILDER (builder), 0);
|
||||
g_return_val_if_fail (buffer != NULL, 0);
|
||||
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
|
||||
g_return_val_if_fail (error == NULL || *error == NULL, 0);
|
||||
|
||||
tmp_error = NULL;
|
||||
|
||||
@ -719,6 +783,7 @@ gtk_builder_add_from_string (GtkBuilder *builder,
|
||||
|
||||
_gtk_builder_parser_parse_buffer (builder, "<input>",
|
||||
buffer, length,
|
||||
NULL,
|
||||
&tmp_error);
|
||||
if (tmp_error != NULL)
|
||||
{
|
||||
@ -729,6 +794,61 @@ gtk_builder_add_from_string (GtkBuilder *builder,
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_builder_add_objects_from_string:
|
||||
* @builder: a #GtkBuilder
|
||||
* @buffer: the string to parse
|
||||
* @length: the length of @buffer (may be -1 if @buffer is nul-terminated)
|
||||
* @object_ids: nul-terminated array of objects to build
|
||||
* @error: return location for an error, or %NULL
|
||||
*
|
||||
* Parses a string containing a <link linkend="BUILDER-UI">GtkBuilder
|
||||
* UI definition</link> building only the requested objects and merges
|
||||
* them with the current contents of @builder.
|
||||
*
|
||||
* <note><para>
|
||||
* If you are adding an object that depends on an object that is not
|
||||
* its child (for instance a #GtkTreeView that depends on its
|
||||
* #GtkTreeModel), you have to explicitely list all of them in @object_ids.
|
||||
* </para></note>
|
||||
*
|
||||
* Returns: A positive value on success, 0 if an error occurred
|
||||
*
|
||||
* Since: 2.14
|
||||
**/
|
||||
guint
|
||||
gtk_builder_add_objects_from_string (GtkBuilder *builder,
|
||||
const gchar *buffer,
|
||||
gsize length,
|
||||
gchar **object_ids,
|
||||
GError **error)
|
||||
{
|
||||
GError *tmp_error;
|
||||
|
||||
g_return_val_if_fail (GTK_IS_BUILDER (builder), 0);
|
||||
g_return_val_if_fail (buffer != NULL, 0);
|
||||
g_return_val_if_fail (object_ids != NULL && object_ids[0] != NULL, 0);
|
||||
g_return_val_if_fail (error == NULL || *error == NULL, 0);
|
||||
|
||||
tmp_error = NULL;
|
||||
|
||||
g_free (builder->priv->filename);
|
||||
builder->priv->filename = g_strdup (".");
|
||||
|
||||
_gtk_builder_parser_parse_buffer (builder, "<input>",
|
||||
buffer, length,
|
||||
object_ids,
|
||||
&tmp_error);
|
||||
|
||||
if (tmp_error != NULL)
|
||||
{
|
||||
g_propagate_error (error, tmp_error);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_builder_get_object:
|
||||
* @builder: a #GtkBuilder
|
||||
|
@ -100,6 +100,15 @@ guint gtk_builder_add_from_string (GtkBuilder *builder,
|
||||
const gchar *buffer,
|
||||
gsize length,
|
||||
GError **error);
|
||||
guint gtk_builder_add_objects_from_file (GtkBuilder *builder,
|
||||
const gchar *filename,
|
||||
gchar **object_ids,
|
||||
GError **error);
|
||||
guint gtk_builder_add_objects_from_string (GtkBuilder *builder,
|
||||
const gchar *buffer,
|
||||
gsize length,
|
||||
gchar **object_ids,
|
||||
GError **error);
|
||||
GObject* gtk_builder_get_object (GtkBuilder *builder,
|
||||
const gchar *name);
|
||||
GSList* gtk_builder_get_objects (GtkBuilder *builder);
|
||||
|
@ -282,6 +282,21 @@ parse_requires (ParserData *data,
|
||||
req_info->tag.name = element_name;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
is_requested_object (const gchar *object,
|
||||
ParserData *data)
|
||||
{
|
||||
GSList *l;
|
||||
|
||||
for (l = data->requested_objects; l; l = l->next)
|
||||
{
|
||||
if (strcmp (l->data, object) == 0)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
parse_object (ParserData *data,
|
||||
const gchar *element_name,
|
||||
@ -346,6 +361,25 @@ parse_object (ParserData *data,
|
||||
return;
|
||||
}
|
||||
|
||||
++data->cur_object_level;
|
||||
|
||||
/* check if we reached a requested object (if it is specified) */
|
||||
if (data->requested_objects && !data->inside_requested_object)
|
||||
{
|
||||
if (is_requested_object (object_id, data))
|
||||
{
|
||||
data->requested_object_level = data->cur_object_level;
|
||||
|
||||
GTK_NOTE (BUILDER, g_print ("requested object \"%s\" found at level %d\n",
|
||||
object_id,
|
||||
data->requested_object_level));
|
||||
|
||||
data->inside_requested_object = TRUE;
|
||||
}
|
||||
else
|
||||
return;
|
||||
}
|
||||
|
||||
object_info = g_slice_new0 (ObjectInfo);
|
||||
object_info->class_name = object_class;
|
||||
object_info->id = object_id;
|
||||
@ -801,6 +835,11 @@ start_element (GMarkupParseContext *context,
|
||||
parse_requires (data, element_name, names, values, error);
|
||||
else if (strcmp (element_name, "object") == 0)
|
||||
parse_object (data, element_name, names, values, error);
|
||||
else if (data->requested_objects && !data->inside_requested_object)
|
||||
{
|
||||
/* If outside a requested object, simply ignore this tag */
|
||||
return;
|
||||
}
|
||||
else if (strcmp (element_name, "child") == 0)
|
||||
parse_child (data, element_name, names, values, error);
|
||||
else if (strcmp (element_name, "property") == 0)
|
||||
@ -910,11 +949,32 @@ end_element (GMarkupParseContext *context,
|
||||
GTK_MAJOR_VERSION, GTK_MINOR_VERSION);
|
||||
}
|
||||
}
|
||||
else if (strcmp (element_name, "interface") == 0)
|
||||
{
|
||||
}
|
||||
else if (data->requested_objects && !data->inside_requested_object)
|
||||
{
|
||||
/* If outside a requested object, simply ignore this tag */
|
||||
return;
|
||||
}
|
||||
else if (strcmp (element_name, "object") == 0)
|
||||
{
|
||||
ObjectInfo *object_info = state_pop_info (data, ObjectInfo);
|
||||
ChildInfo* child_info = state_peek_info (data, ChildInfo);
|
||||
|
||||
if (data->requested_objects && data->inside_requested_object &&
|
||||
(data->cur_object_level == data->requested_object_level))
|
||||
{
|
||||
GTK_NOTE (BUILDER, g_print ("requested object end found at level %d\n",
|
||||
data->requested_object_level));
|
||||
|
||||
data->inside_requested_object = FALSE;
|
||||
}
|
||||
|
||||
--data->cur_object_level;
|
||||
|
||||
g_assert (data->cur_object_level >= 0);
|
||||
|
||||
object_info->object = builder_construct (data, object_info, error);
|
||||
if (!object_info->object)
|
||||
{
|
||||
@ -976,9 +1036,6 @@ end_element (GMarkupParseContext *context,
|
||||
object_info->signals =
|
||||
g_slist_prepend (object_info->signals, signal_info);
|
||||
}
|
||||
else if (strcmp (element_name, "interface") == 0)
|
||||
{
|
||||
}
|
||||
else if (strcmp (element_name, "placeholder") == 0)
|
||||
{
|
||||
}
|
||||
@ -1056,6 +1113,7 @@ _gtk_builder_parser_parse_buffer (GtkBuilder *builder,
|
||||
const gchar *filename,
|
||||
const gchar *buffer,
|
||||
gsize length,
|
||||
gchar **requested_objs,
|
||||
GError **error)
|
||||
{
|
||||
ParserData *data;
|
||||
@ -1066,13 +1124,31 @@ _gtk_builder_parser_parse_buffer (GtkBuilder *builder,
|
||||
data->filename = filename;
|
||||
data->domain = g_strdup (gtk_builder_get_translation_domain (builder));
|
||||
|
||||
data->requested_objects = NULL;
|
||||
if (requested_objs)
|
||||
{
|
||||
gint i;
|
||||
|
||||
data->inside_requested_object = FALSE;
|
||||
for (i = 0; requested_objs[i]; ++i)
|
||||
{
|
||||
data->requested_objects = g_slist_prepend (data->requested_objects,
|
||||
g_strdup (requested_objs[i]));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* get all the objects */
|
||||
data->inside_requested_object = TRUE;
|
||||
}
|
||||
|
||||
data->ctx = g_markup_parse_context_new (&parser,
|
||||
G_MARKUP_TREAT_CDATA_AS_TEXT,
|
||||
data, NULL);
|
||||
|
||||
if (!g_markup_parse_context_parse (data->ctx, buffer, length, error))
|
||||
goto out;
|
||||
|
||||
|
||||
_gtk_builder_finish (builder);
|
||||
|
||||
/* Custom parser_finished */
|
||||
@ -1103,6 +1179,8 @@ _gtk_builder_parser_parse_buffer (GtkBuilder *builder,
|
||||
g_slist_foreach (data->custom_finalizers, (GFunc)free_subparser, NULL);
|
||||
g_slist_free (data->custom_finalizers);
|
||||
g_slist_free (data->finalizers);
|
||||
g_slist_foreach (data->requested_objects, (GFunc) g_free, NULL);
|
||||
g_slist_free (data->requested_objects);
|
||||
g_free (data->domain);
|
||||
g_markup_parse_context_free (data->ctx);
|
||||
g_free (data);
|
||||
|
@ -96,6 +96,11 @@ typedef struct {
|
||||
const gchar *filename;
|
||||
GSList *finalizers;
|
||||
GSList *custom_finalizers;
|
||||
|
||||
GSList *requested_objects; /* NULL if all the objects are requested */
|
||||
gboolean inside_requested_object;
|
||||
gint requested_object_level;
|
||||
gint cur_object_level;
|
||||
} ParserData;
|
||||
|
||||
typedef GType (*GTypeGetFunc) (void);
|
||||
@ -105,6 +110,7 @@ void _gtk_builder_parser_parse_buffer (GtkBuilder *builder,
|
||||
const gchar *filename,
|
||||
const gchar *buffer,
|
||||
gsize length,
|
||||
gchar **requested_objs,
|
||||
GError **error);
|
||||
GObject * _gtk_builder_construct (GtkBuilder *builder,
|
||||
ObjectInfo *info,
|
||||
|
@ -2105,7 +2105,6 @@ test_pango_attributes (void)
|
||||
g_error_free (error);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
test_requires (void)
|
||||
{
|
||||
@ -2127,6 +2126,133 @@ test_requires (void)
|
||||
g_error_free (error);
|
||||
}
|
||||
|
||||
static void
|
||||
test_add_objects (void)
|
||||
{
|
||||
GtkBuilder *builder;
|
||||
GError *error;
|
||||
gint ret;
|
||||
GObject *obj;
|
||||
GtkUIManager *manager;
|
||||
GtkWidget *menubar;
|
||||
GObject *menu, *label;
|
||||
GList *children;
|
||||
gchar *objects[2] = {"mainbox", NULL};
|
||||
gchar *objects2[3] = {"mainbox", "window2", NULL};
|
||||
gchar *objects3[2] = {"uimgr1", NULL};
|
||||
const gchar buffer[] =
|
||||
"<interface>"
|
||||
" <object class=\"GtkWindow\" id=\"window\">"
|
||||
" <child>"
|
||||
" <object class=\"GtkVBox\" id=\"mainbox\">"
|
||||
" <property name=\"visible\">True</property>"
|
||||
" <child>"
|
||||
" <object class=\"GtkLabel\" id=\"label1\">"
|
||||
" <property name=\"visible\">True</property>"
|
||||
" <property name=\"label\" translatable=\"no\">first label</property>"
|
||||
" </object>"
|
||||
" </child>"
|
||||
" <child>"
|
||||
" <object class=\"GtkLabel\" id=\"label2\">"
|
||||
" <property name=\"visible\">True</property>"
|
||||
" <property name=\"label\" translatable=\"no\">second label</property>"
|
||||
" </object>"
|
||||
" <packing>"
|
||||
" <property name=\"position\">1</property>"
|
||||
" </packing>"
|
||||
" </child>"
|
||||
" </object>"
|
||||
" </child>"
|
||||
" </object>"
|
||||
" <object class=\"GtkWindow\" id=\"window2\">"
|
||||
" <child>"
|
||||
" <object class=\"GtkLabel\" id=\"label1\">"
|
||||
" <property name=\"label\" translatable=\"no\">second label</property>"
|
||||
" </object>"
|
||||
" </child>"
|
||||
" </object>"
|
||||
"<interface/>";
|
||||
const gchar buffer2[] =
|
||||
"<interface>"
|
||||
" <object class=\"GtkUIManager\" id=\"uimgr1\">"
|
||||
" <child>"
|
||||
" <object class=\"GtkActionGroup\" id=\"ag1\">"
|
||||
" <child>"
|
||||
" <object class=\"GtkAction\" id=\"file\">"
|
||||
" <property name=\"label\">_File</property>"
|
||||
" </object>"
|
||||
" <accelerator key=\"n\" modifiers=\"GDK_CONTROL_MASK\"/>"
|
||||
" </child>"
|
||||
" </object>"
|
||||
" </child>"
|
||||
" <ui>"
|
||||
" <menubar name=\"menubar1\">"
|
||||
" <menu action=\"file\">"
|
||||
" </menu>"
|
||||
" </menubar>"
|
||||
" </ui>"
|
||||
" </object>"
|
||||
" <object class=\"GtkWindow\" id=\"window1\">"
|
||||
" <child>"
|
||||
" <object class=\"GtkMenuBar\" id=\"menubar1\" constructor=\"uimgr1\"/>"
|
||||
" </child>"
|
||||
" </object>"
|
||||
"</interface>";
|
||||
|
||||
error = NULL;
|
||||
builder = gtk_builder_new ();
|
||||
ret = gtk_builder_add_objects_from_string (builder, buffer, -1, objects, &error);
|
||||
g_assert (ret);
|
||||
g_assert (error == NULL);
|
||||
obj = gtk_builder_get_object (builder, "window");
|
||||
g_assert (obj == NULL);
|
||||
obj = gtk_builder_get_object (builder, "window2");
|
||||
g_assert (obj == NULL);
|
||||
obj = gtk_builder_get_object (builder, "mainbox");
|
||||
g_assert (GTK_IS_WIDGET (obj));
|
||||
g_object_unref (builder);
|
||||
|
||||
error = NULL;
|
||||
builder = gtk_builder_new ();
|
||||
ret = gtk_builder_add_objects_from_string (builder, buffer, -1, objects2, &error);
|
||||
g_assert (ret);
|
||||
g_assert (error == NULL);
|
||||
obj = gtk_builder_get_object (builder, "window");
|
||||
g_assert (obj == NULL);
|
||||
obj = gtk_builder_get_object (builder, "window2");
|
||||
g_assert (GTK_IS_WINDOW (obj));
|
||||
gtk_widget_destroy (GTK_WIDGET (obj));
|
||||
obj = gtk_builder_get_object (builder, "mainbox");
|
||||
g_assert (GTK_IS_WIDGET (obj));
|
||||
g_object_unref (builder);
|
||||
|
||||
/* test cherry picking a ui manager */
|
||||
error = NULL;
|
||||
builder = gtk_builder_new ();
|
||||
ret = gtk_builder_add_objects_from_string (builder, buffer2, -1, objects3, &error);
|
||||
g_assert (ret);
|
||||
obj = gtk_builder_get_object (builder, "uimgr1");
|
||||
g_assert (GTK_IS_UI_MANAGER (obj));
|
||||
manager = GTK_UI_MANAGER (obj);
|
||||
obj = gtk_builder_get_object (builder, "file");
|
||||
g_assert (GTK_IS_ACTION (obj));
|
||||
menubar = gtk_ui_manager_get_widget (manager, "/menubar1");
|
||||
g_assert (GTK_IS_MENU_BAR (menubar));
|
||||
|
||||
children = gtk_container_get_children (GTK_CONTAINER (menubar));
|
||||
menu = children->data;
|
||||
g_assert (menu != NULL);
|
||||
g_assert (GTK_IS_MENU_ITEM (menu));
|
||||
g_assert (strcmp (GTK_WIDGET (menu)->name, "file") == 0);
|
||||
g_list_free (children);
|
||||
|
||||
label = G_OBJECT (GTK_BIN (menu)->child);
|
||||
g_assert (label != NULL);
|
||||
g_assert (GTK_IS_LABEL (label));
|
||||
g_assert (strcmp (gtk_label_get_text (GTK_LABEL (label)), "File") == 0);
|
||||
|
||||
g_object_unref (builder);
|
||||
}
|
||||
|
||||
static void
|
||||
test_file (const gchar *filename)
|
||||
@ -2212,6 +2338,7 @@ main (int argc, char **argv)
|
||||
g_test_add_func ("/Builder/IconFactory", test_icon_factory);
|
||||
g_test_add_func ("/Builder/PangoAttributes", test_pango_attributes);
|
||||
g_test_add_func ("/Builder/Requires", test_requires);
|
||||
g_test_add_func ("/Builder/AddObjects", test_add_objects);
|
||||
|
||||
return g_test_run();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user