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:
Paolo Borelli 2008-07-16 15:36:53 +00:00 committed by Paolo Borelli
parent 3e5cda3d3c
commit 6ee8be8899
8 changed files with 366 additions and 8 deletions

View File

@ -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"

View File

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

View File

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

View File

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

View File

@ -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);

View File

@ -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);

View File

@ -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,

View File

@ -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();
}