diff options
-rw-r--r-- | ChangeLog | 14 | ||||
-rw-r--r-- | docs/reference/gtk/gtk-sections.txt | 2 | ||||
-rw-r--r-- | gtk/gtk.symbols | 2 | ||||
-rw-r--r-- | gtk/gtkbuilder.c | 126 | ||||
-rw-r--r-- | gtk/gtkbuilder.h | 9 | ||||
-rw-r--r-- | gtk/gtkbuilderparser.c | 86 | ||||
-rw-r--r-- | gtk/gtkbuilderprivate.h | 6 | ||||
-rw-r--r-- | gtk/tests/builder.c | 129 |
8 files changed, 366 insertions, 8 deletions
@@ -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(); } |