gtk+ r20845 - in trunk: . docs/reference/gtk gtk gtk/tests



Author: pborelli
Date: Wed Jul 16 15:36:53 2008
New Revision: 20845
URL: http://svn.gnome.org/viewvc/gtk+?rev=20845&view=rev

Log:
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.



Modified:
   trunk/ChangeLog
   trunk/docs/reference/gtk/gtk-sections.txt
   trunk/gtk/gtk.symbols
   trunk/gtk/gtkbuilder.c
   trunk/gtk/gtkbuilder.h
   trunk/gtk/gtkbuilderparser.c
   trunk/gtk/gtkbuilderprivate.h
   trunk/gtk/tests/builder.c

Modified: trunk/docs/reference/gtk/gtk-sections.txt
==============================================================================
--- trunk/docs/reference/gtk/gtk-sections.txt	(original)
+++ trunk/docs/reference/gtk/gtk-sections.txt	Wed Jul 16 15:36:53 2008
@@ -478,6 +478,8 @@
 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

Modified: trunk/gtk/gtk.symbols
==============================================================================
--- trunk/gtk/gtk.symbols	(original)
+++ trunk/gtk/gtk.symbols	Wed Jul 16 15:36:53 2008
@@ -445,6 +445,8 @@
 #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

Modified: trunk/gtk/gtkbuilder.c
==============================================================================
--- trunk/gtk/gtkbuilder.c	(original)
+++ trunk/gtk/gtkbuilder.c	Wed Jul 16 15:36:53 2008
@@ -658,7 +658,70 @@
 
   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;
+
+  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,
+                                    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;
 
@@ -673,6 +736,7 @@
 
   _gtk_builder_parser_parse_buffer (builder, filename,
                                     buffer, length,
+                                    object_ids,
                                     &tmp_error);
 
   g_free (buffer);
@@ -695,7 +759,7 @@
  *
  * 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 @@
 
   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_parser_parse_buffer (builder, "<input>",
                                     buffer, length,
+                                    NULL,
                                     &tmp_error);
   if (tmp_error != NULL)
     {
@@ -730,6 +795,61 @@
 }
 
 /**
+ * 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

Modified: trunk/gtk/gtkbuilder.h
==============================================================================
--- trunk/gtk/gtkbuilder.h	(original)
+++ trunk/gtk/gtkbuilder.h	Wed Jul 16 15:36:53 2008
@@ -100,6 +100,15 @@
                                                   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);

Modified: trunk/gtk/gtkbuilderparser.c
==============================================================================
--- trunk/gtk/gtkbuilderparser.c	(original)
+++ trunk/gtk/gtkbuilderparser.c	Wed Jul 16 15:36:53 2008
@@ -282,6 +282,21 @@
   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 @@
       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 @@
     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 @@
 			 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 @@
       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 @@
                                   const gchar  *filename,
                                   const gchar  *buffer,
                                   gsize         length,
+                                  gchar       **requested_objs,
                                   GError      **error)
 {
   ParserData *data;
@@ -1066,13 +1124,31 @@
   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 @@
   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);

Modified: trunk/gtk/gtkbuilderprivate.h
==============================================================================
--- trunk/gtk/gtkbuilderprivate.h	(original)
+++ trunk/gtk/gtkbuilderprivate.h	Wed Jul 16 15:36:53 2008
@@ -96,6 +96,11 @@
   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 @@
                                        const gchar *filename,
                                        const gchar *buffer,
                                        gsize length,
+                                       gchar **requested_objs,
                                        GError **error);
 GObject * _gtk_builder_construct (GtkBuilder *builder,
                                   ObjectInfo *info,

Modified: trunk/gtk/tests/builder.c
==============================================================================
--- trunk/gtk/tests/builder.c	(original)
+++ trunk/gtk/tests/builder.c	Wed Jul 16 15:36:53 2008
@@ -2105,7 +2105,6 @@
   g_error_free (error);
 }
 
-
 static void
 test_requires (void)
 {
@@ -2127,6 +2126,133 @@
   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 @@
   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();
 }



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]