diff --git a/gtk/gtkbuilder.c b/gtk/gtkbuilder.c
index c6b6b2577a..695b53e79f 100644
--- a/gtk/gtkbuilder.c
+++ b/gtk/gtkbuilder.c
@@ -100,17 +100,12 @@
* specifying the id of the #GtkUIManager in the "constructor" attribute and the
* name of the object in the "id" attribute.
*
- * Objects must be given a name with the "id" attribute, which allows the
+ * Objects may be given a name with the "id" attribute, which allows the
* application to retrieve them from the builder with gtk_builder_get_object().
* An id is also necessary to use the object as property value in other parts of
- * the UI definition.
+ * the UI definition. GTK+ reserves ids starting and ending with ___ (3 underscores)
+ * for its own purposes.
*
- *
- * Prior to 2.20, GtkBuilder was setting the "name" property of constructed widgets to the
- * "id" attribute. In GTK+ 2.20 or newer, you have to use gtk_buildable_get_name() instead
- * of gtk_widget_get_name() to obtain the "id", or set the "name" property in your UI
- * definition.
- *
*
* Setting properties of objects is pretty straightforward with the
* <property> element: the "name" attribute specifies the name of the
diff --git a/gtk/gtkbuilder.rnc b/gtk/gtkbuilder.rnc
index 635fd4f289..6e3aea3469 100644
--- a/gtk/gtkbuilder.rnc
+++ b/gtk/gtkbuilder.rnc
@@ -9,7 +9,7 @@ requires = element requires {
}
object = element object {
- attribute id { xsd:ID },
+ attribute id { xsd:ID } ?,
attribute class { text },
attribute type-func { text } ?,
attribute constructor { text } ?,
diff --git a/gtk/gtkbuilder.rng b/gtk/gtkbuilder.rng
index eee698a607..341d19f3ea 100644
--- a/gtk/gtkbuilder.rng
+++ b/gtk/gtkbuilder.rng
@@ -29,9 +29,11 @@
-
-
-
+
+
+
+
+
diff --git a/gtk/gtkbuilderparser.c b/gtk/gtkbuilderparser.c
index 11e04cb506..e2111d65c9 100644
--- a/gtk/gtkbuilderparser.c
+++ b/gtk/gtkbuilderparser.c
@@ -301,7 +301,7 @@ is_requested_object (const gchar *object,
for (l = data->requested_objects; l; l = l->next)
{
- if (strcmp (l->data, object) == 0)
+ if (g_strcmp0 (l->data, object) == 0)
return TRUE;
}
@@ -369,10 +369,11 @@ parse_object (GMarkupParseContext *context,
return;
}
+ data->object_counter++;
+
if (!object_id)
{
- error_missing_attribute (data, element_name, "id", error);
- return;
+ object_id = g_strdup_printf ("___object_%d___", data->object_counter++);
}
++data->cur_object_level;
@@ -420,7 +421,6 @@ parse_object (GMarkupParseContext *context,
return;
}
-
g_hash_table_insert (data->object_ids, g_strdup (object_id), GINT_TO_POINTER (line));
}
diff --git a/gtk/gtkbuilderprivate.h b/gtk/gtkbuilderprivate.h
index 346459c678..b653373be7 100644
--- a/gtk/gtkbuilderprivate.h
+++ b/gtk/gtkbuilderprivate.h
@@ -107,6 +107,8 @@ typedef struct {
gint requested_object_level;
gint cur_object_level;
+ gint object_counter;
+
GHashTable *object_ids;
} ParserData;
diff --git a/testsuite/gtk/builder.c b/testsuite/gtk/builder.c
index 08582623c5..d20fba6a61 100644
--- a/testsuite/gtk/builder.c
+++ b/testsuite/gtk/builder.c
@@ -2728,6 +2728,53 @@ test_expose_object (void)
g_object_unref (image);
}
+static void
+test_no_ids (void)
+{
+ GtkBuilder *builder;
+ GError *error = NULL;
+ GObject *obj;
+ const gchar buffer[] =
+ ""
+ " "
+ "";
+
+ builder = gtk_builder_new ();
+ gtk_builder_add_from_string (builder, buffer, -1, &error);
+ g_assert (error == NULL);
+
+ obj = gtk_builder_get_object (builder, "button_ok");
+ g_assert (GTK_IS_BUTTON (obj));
+
+ g_object_unref (builder);
+}
+
int
main (int argc, char **argv)
{
@@ -2777,6 +2824,7 @@ main (int argc, char **argv)
g_test_add_func ("/Builder/GMenu", test_gmenu);
g_test_add_func ("/Builder/LevelBar", test_level_bar);
g_test_add_func ("/Builder/Expose Object", test_expose_object);
+ g_test_add_func ("/Builder/No IDs", test_no_ids);
return g_test_run();
}