summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog14
-rw-r--r--docs/reference/gtk/gtk-sections.txt2
-rw-r--r--gtk/gtk.symbols2
-rw-r--r--gtk/gtkbuilder.c126
-rw-r--r--gtk/gtkbuilder.h9
-rw-r--r--gtk/gtkbuilderparser.c86
-rw-r--r--gtk/gtkbuilderprivate.h6
-rw-r--r--gtk/tests/builder.c129
8 files changed, 366 insertions, 8 deletions
diff --git a/ChangeLog b/ChangeLog
index 7257e58088..3e20563fa2 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,19 @@
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"
signal) case and plug a couple of leaks.
diff --git a/docs/reference/gtk/gtk-sections.txt b/docs/reference/gtk/gtk-sections.txt
index 6c5d9a1a4b..2d22a82b33 100644
--- a/docs/reference/gtk/gtk-sections.txt
+++ b/docs/reference/gtk/gtk-sections.txt
@@ -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
diff --git a/gtk/gtk.symbols b/gtk/gtk.symbols
index ad6f331b21..fcef780595 100644
--- a/gtk/gtk.symbols
+++ b/gtk/gtk.symbols
@@ -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
diff --git a/gtk/gtkbuilder.c b/gtk/gtkbuilder.c
index 8ba1da951b..1ea7db3ac7 100644
--- a/gtk/gtkbuilder.c
+++ b/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)
{
@@ -730,6 +795,61 @@ gtk_builder_add_from_string (GtkBuilder *builder,
}
/**
+ * 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
* @name: name of object to get
diff --git a/gtk/gtkbuilder.h b/gtk/gtkbuilder.h
index e6fe549c00..d33e32c306 100644
--- a/gtk/gtkbuilder.h
+++ b/gtk/gtkbuilder.h
@@ -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);
diff --git a/gtk/gtkbuilderparser.c b/gtk/gtkbuilderparser.c
index 9e073a426d..d1057811a3 100644
--- a/gtk/gtkbuilderparser.c
+++ b/gtk/gtkbuilderparser.c
@@ -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);
diff --git a/gtk/gtkbuilderprivate.h b/gtk/gtkbuilderprivate.h
index b860e6e12c..8c69630d6c 100644
--- a/gtk/gtkbuilderprivate.h
+++ b/gtk/gtkbuilderprivate.h
@@ -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,
diff --git a/gtk/tests/builder.c b/gtk/tests/builder.c
index 115abc7753..9d1e4a82b8 100644
--- a/gtk/tests/builder.c
+++ b/gtk/tests/builder.c
@@ -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();
}