After an evaluation, GNOME has moved from Bugzilla to GitLab. Learn more about GitLab.
No new issues can be reported in GNOME Bugzilla anymore.
To report an issue in a GNOME project, go to GNOME GitLab.
Do not go to GNOME Gitlab for: Bluefish, Doxygen, GnuCash, GStreamer, java-gnome, LDTP, NetworkManager, Tomboy.
Bug 172535 - (gtkbuilder) Add support for UI builders in gtk+
(gtkbuilder)
Add support for UI builders in gtk+
Status: RESOLVED FIXED
Product: gtk+
Classification: Platform
Component: Widget: Other
unspecified
Other Linux
: High enhancement
: Medium API
Assigned To: Johan (not receiving bugmail) Dahlin
gtk-bugs
Depends on: 172536 172631
Blocks: 383035
 
 
Reported: 2005-04-03 19:31 UTC by Johan (not receiving bugmail) Dahlin
Modified: 2007-06-15 20:08 UTC
See Also:
GNOME target: ---
GNOME version: ---


Attachments
v1: first draft of GtkBuilder (202.07 KB, patch)
2005-04-06 19:31 UTC, Johan (not receiving bugmail) Dahlin
needs-work Details | Review
v2: rewrite from scratch. (106.03 KB, patch)
2006-05-11 02:39 UTC, Johan (not receiving bugmail) Dahlin
none Details | Review
v3: include tests, move out widget & window property handling (125.95 KB, patch)
2006-05-12 16:16 UTC, Johan (not receiving bugmail) Dahlin
none Details | Review
v4: treeview/celllayout, liststore, tooltips, dialog, signals autoconnect, (206.22 KB, patch)
2006-05-18 19:28 UTC, Johan (not receiving bugmail) Dahlin
none Details | Review
v5: typeutils (9.44 KB, patch)
2006-05-23 19:56 UTC, Johan (not receiving bugmail) Dahlin
none Details | Review
v5: gtkbuildable, gtkbuilder & gtkbuilderparser.c (60.29 KB, patch)
2006-05-23 19:57 UTC, Johan (not receiving bugmail) Dahlin
none Details | Review
v5: implement buildable (83.95 KB, patch)
2006-05-23 20:00 UTC, Johan (not receiving bugmail) Dahlin
none Details | Review
v5: Add GtkCellView:model property (1.96 KB, patch)
2006-05-23 20:00 UTC, Johan (not receiving bugmail) Dahlin
none Details | Review
v5: add tests (54.08 KB, patch)
2006-05-23 20:01 UTC, Johan (not receiving bugmail) Dahlin
none Details | Review
v6: typeutils (11.91 KB, patch)
2006-10-25 18:56 UTC, Johan (not receiving bugmail) Dahlin
needs-work Details | Review
v6: gtkbuildable, gtkbuilder & gtkbuilderparser.c (70.75 KB, patch)
2006-10-25 18:58 UTC, Johan (not receiving bugmail) Dahlin
none Details | Review
v6: implement buildable (81.70 KB, patch)
2006-10-25 18:58 UTC, Johan (not receiving bugmail) Dahlin
none Details | Review
v6: tests (54.27 KB, patch)
2006-10-25 18:59 UTC, Johan (not receiving bugmail) Dahlin
none Details | Review
v6.1: gtkbuildable, gtkbuilder & gtkbuilderparser.c (70.61 KB, patch)
2006-10-25 20:56 UTC, Johan (not receiving bugmail) Dahlin
needs-work Details | Review
gtkbuilder/gtkbuildable/gtkbuilderparser portion (48.92 KB, patch)
2006-12-04 16:31 UTC, Tristan Van Berkom
none Details | Review
typeutils portion (tristan's patches) (5.56 KB, patch)
2006-12-04 16:33 UTC, Tristan Van Berkom
none Details | Review
implement buildable portion (tristan's patches) (30.33 KB, patch)
2006-12-04 16:34 UTC, Tristan Van Berkom
none Details | Review
tests (tristan's patches) (50.05 KB, patch)
2006-12-04 16:36 UTC, Tristan Van Berkom
none Details | Review
The png file for gtk+/tests/Fry.png (21.21 KB, image/png)
2006-12-04 16:37 UTC, Tristan Van Berkom
  Details
A Convenience "all-in-one" patch against cvs (tristan's patches) (148.38 KB, patch)
2006-12-04 16:38 UTC, Tristan Van Berkom
none Details | Review
v7: typeutils (12.44 KB, patch)
2007-06-02 15:18 UTC, Johan (not receiving bugmail) Dahlin
none Details | Review
v8: typeutils (7.11 KB, patch)
2007-06-04 18:20 UTC, Johan (not receiving bugmail) Dahlin
none Details | Review
v8: buildable (72.85 KB, patch)
2007-06-04 18:20 UTC, Johan (not receiving bugmail) Dahlin
none Details | Review
v8: buildable implementation (86.34 KB, patch)
2007-06-04 18:20 UTC, Johan (not receiving bugmail) Dahlin
none Details | Review
v8: tests (59.07 KB, patch)
2007-06-04 18:20 UTC, Johan (not receiving bugmail) Dahlin
none Details | Review
v9: tests (61.45 KB, patch)
2007-06-07 19:06 UTC, Johan (not receiving bugmail) Dahlin
none Details | Review
v9: buildable (79.68 KB, patch)
2007-06-07 19:19 UTC, Johan (not receiving bugmail) Dahlin
none Details | Review
v9: buildable implementation (84.94 KB, patch)
2007-06-07 19:19 UTC, Johan (not receiving bugmail) Dahlin
none Details | Review
v9: typeutils (2.40 KB, patch)
2007-06-07 19:19 UTC, Johan (not receiving bugmail) Dahlin
none Details | Review
Patch to make gtk_builder_signal_autoconnect() reject double handlers (1.17 KB, patch)
2007-06-08 12:30 UTC, Kalle Vahlman
none Details | Review
v10: all in one (226.76 KB, patch)
2007-06-11 14:47 UTC, Johan (not receiving bugmail) Dahlin
none Details | Review
v11: all in one (224.32 KB, patch)
2007-06-11 20:06 UTC, Johan (not receiving bugmail) Dahlin
none Details | Review
v12: all in one (228.80 KB, patch)
2007-06-12 00:51 UTC, Johan (not receiving bugmail) Dahlin
none Details | Review
v13: all in one (231.12 KB, patch)
2007-06-12 15:20 UTC, Johan (not receiving bugmail) Dahlin
none Details | Review
v14: all in one (235.29 KB, patch)
2007-06-12 20:02 UTC, Johan (not receiving bugmail) Dahlin
none Details | Review
v15: all in one (244.03 KB, patch)
2007-06-14 14:31 UTC, Johan (not receiving bugmail) Dahlin
none Details | Review

Description Johan (not receiving bugmail) Dahlin 2005-04-03 19:31:59 UTC
Tracking bug for a UI builder inclusing in gtk+
Comment 1 Owen Taylor 2005-04-03 22:08:41 UTC
Shouldn't this be titled "UI Builder support in GTK+" or are you
suggesting that we actually incorporate glade (not libglade) into
the GTK+ tree?
Comment 2 Johan (not receiving bugmail) Dahlin 2005-04-03 22:18:18 UTC
Owen: Right, updating summary.
Comment 3 Johan (not receiving bugmail) Dahlin 2005-04-06 19:31:48 UTC
Created attachment 39767 [details] [review]
v1: first draft of GtkBuilder

Adds GtkBuilder, an example, a glade file and a DTD.
GObject support over libglade. GtkUIManager support in xml format.
GtkSizeGroup, GtkTreeViewColumn and GtkCellRenderers supported.
Basic support for deprecated widgets, not complete backwards compatible with
libglade. Broken widgets removed (and GtkPreview)
No interface implemented for containers
Comment 4 Johan (not receiving bugmail) Dahlin 2005-04-06 19:33:44 UTC
Documentation needs to be updated and added for new methods. Is the name okay?
Do we need an interface or can it wait? 
Do we need better backwards compatibility with libglade?
Comment 5 Dan Winship 2005-05-17 13:01:13 UTC
I feel like if we're going to have the chance to break file-format
compatibility, then we should break it a little more drastically than this.
There are a bunch of odd special cases in the format that could be fixed if
we allowed <widget> to be a child of <property>. Eg, instead of saving
notebook tab labels as children with special magic packing types, we could
just store them directly as the "tab_widget" child property of their
corresponding page. Likewise for expander and frame labels, and the menu
"children" of GtkOptionMenu and GtkMenuItem.

Less breakingly, I'd suggest changing the signature of the custom widget
creation function, to have a user_data at the end (which was passed into
gtk_builder_new). The current glade way requires you to use global variables
if you need to pass data to the creation function.

Also, if anyone ever succeeds in writing an app that uses two different gtk
language bindings simultaneously, gtk_builder_set_custom_handler() would
have to be per-builder, not global. (In fact, it could just be a virtual
method, and bindings authors could subclass GtkBuilder to override it.)
Comment 6 Matthias Clasen 2005-06-13 20:29:49 UTC
Punting some big, unfinished features from 2.8
Comment 7 Johan (not receiving bugmail) Dahlin 2006-05-11 02:39:15 UTC
Created attachment 65210 [details] [review]
v2: rewrite from scratch.

Rewrite of libglade providing most needed functionallity.
Parsing is now doing in one step instead of two for increased speed and decreased footprint.
For more information see http://article.gmane.org/gmane.comp.gnome.gtk%2B.devel.general/10160

The patch is not yet complete, but from an API point of view it should be pretty stable. More functionallity, especially in the parser can be added without breaking the API.
Comment 8 Johan (not receiving bugmail) Dahlin 2006-05-12 16:16:08 UTC
Created attachment 65329 [details] [review]
v3: include tests, move out widget & window property handling

This moves the handling of GtkWidget::has_focus/has_default and visible for GtkWidnow subclasses out of GtkBuilder. It also removes the special treatment of GtkAdjustment and strncmp abuse.
Comment 9 Johan (not receiving bugmail) Dahlin 2006-05-18 19:28:29 UTC
Created attachment 65787 [details] [review]
v4: treeview/celllayout, liststore, tooltips, dialog, signals autoconnect, 

Adds support for treeview to displaying columns and cells, tooltips & dialogs are supported, signal autoconnect works aswell.
Comment 10 Matthias Clasen 2006-05-20 16:08:27 UTC
First impressions, after reading the first half of the patch:

Meta comment
------------

Hard to review a 8000 line patch. I'd like to see this broken up
in a few smaller patches (or as the kernel guys say, "a proper
patch sequence"):
1) necessary preparations (like adding the GtkCellView::model property)
2) add the GtkBuildable interface and GtkBuilder itself
3) add tests
4) implement GtkBuildable throughout the hierarchy


Bigger things
-------------

I think the GtkBuildable interface needs some documentation explaining
when I have to implement which functions, and what they are supposed
to do. The applies to some of the peculiarities of GtkBuilder, e.g.
delayed properties. 

Do you have any plan for documenting the allowed values for the
type parameter of GtkBuildable::add ?

Can a GtkBuilder be used only once, or can I give it multiple files ?
I think it would be slightly nicer to have a _new() function which
just constructs a new GtkBuilder, and a separate _parse() function
which parses a file.

Why are GtkActions and action groups buildable ? That goes against my 
intuition that actions are defined by the code, and the ui definition 
hooks them up with a control interface.

You might want to hold off on making tooltips buildable, since 
Kris wants to land his new tooltip api soon.

The API documentation is obviously not quite done yet. We also need
docs for the xml format - not necessarily a DTD, but some description
of the general structure, and also any special tags which are
supported for individual objects. Including examples. Ideally,
we'd also add a chapter to our migration guides explaining how
to convert from glade to the new format (what is the name of the
new format, btw ?)


Stylistic stuff
---------------

Most comments are only exemplaric, and apply to other places as well.


+static void
+gtk_widget_buildable_set_property (GtkBuildable        *buildable,
+				   GtkBuilder          *builder,
+				   const gchar         *name,
+				   const GValue        *value)
+{
+  if (!strcmp (name, "has_default") && g_value_get_boolean (value) == TRUE)
+    g_signal_connect_swapped (builder, "finish",
+			      G_CALLBACK (gtk_widget_grab_default),


What if the name is given as "has-default" ?
Don't do == TRUE for booleans


+static GMarkupParser size_group_parser =
+  {
+    size_group_start_element
+  };
+


Should be const


+G_DEFINE_TYPE_WITH_CODE (GtkSizeGroup, gtk_size_group, G_TYPE_OBJECT,
+			 G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
+						gtk_size_group_buildable_init));


No ; after G_DEFINE_TYPE


+      parser_data = g_new0 (GSListSubParserData, 1);


Could use g_slice here


+static void
+gtk_size_group_buildable_custom_tag_end (GtkBuildable  *buildable,
+					 GtkBuilder    *builder,
+					 GObject       *child,
+					 const gchar   *tagname,
+					 gpointer       data)
+{
+  g_signal_connect (builder, "finish",
+		    (GCallback)builder_finish_cb, data);
+}


Is it necessary to do this in custom_tag_end, or can we move
it to custom_tag_start, and do away with the custom_tag_end ?


+static void
+gtk_expander_buildable_init (GtkBuildableIface *iface)
+{
+  parent_buildable_iface = g_type_interface_peek_parent (iface);


parent_buildable_iface seems unused in gtkexpander.c


+gboolean
+gtk_cell_layout_buildable_custom_tag_start (GtkBuildable  *buildable,


Should be static, or if needed in other source files, _-prefixed.
This is not public api, right ?


+/* gtkbuilderparser.h


Comment and guard define don't match the header filename.


+  g_object_set_data_full (obj,
+			  "gtk-builder-name",
+			  g_strdup (info->id),
+			  g_free);


I wonder why you add a GtkSizeGroup private just for the name, when
you set it as object data on all objects, anyway.



+      if (!GTK_BUILDABLE_GET_IFACE (info->object) ||
+	  !GTK_BUILDABLE_GET_IFACE (info->object)->get_internal_child)
+	continue;
+      
+      obj = gtk_buildable_get_internal_child (GTK_BUILDABLE (info->object),
+					      builder,
+					      childname);


This should just be 
   if (GTK_IS_BUILDABLE (info->object)
     obj = gtk_buildable_get_internal_child (GTK_BUILDABLE (info->object),
                                             builder,
                                             childname);
and gtk_buildable_get_internal_child should just return NULL if 
get_internal_child is NULL
  

+void
+_gtk_builder_finish (GtkBuilder  *builder,
+		     const gchar *domain)


Why is the domain passed in here ? Looking at parse_buffer, it
appears the domain is already known before the parsing starts,
so you could just set it on the builder there.


+const gchar*   gtk_builder_get_name           (GtkBuilder *builder,
+					       GObject *object);


Should this be get_object_name ? It is not returning the name
of the builder, right ?


+  item = g_new (gpointer, 2);
+  item[0] = g_strndup (text, text_len);
+  item[1] = parser_data->response;


How about using a struct here, for clarity ?


+  /* Do not free the signal list, the GtkBuilder takes
+     ownership of it */
+  g_slist_free (info->signals);  


What gives ? 


+    g_error ("<property> needs a name");


It would be nice if these errors could contain some location
information, like filename:linenumber. Also, there are a lot
of them with similar strings, so it might be nice to parametrize
that a bit:
   g_error ("%s:%d: Tag <%s> requires attribute "%s"", 
            filename, lineno, tag, attribute);


+  if (G_UNLIKELY (gtk_debug_flags & GTK_DEBUG_BUILDER))
+    {
+      GString *tags = g_string_new ("");
+      int i;
+      for (i = 0; names[i]; i++)
+	g_string_append_printf (tags, "%s=\"%s\" ", names[i], values[i]);
+      
+      if (i)
+	{
+	  g_string_insert_c (tags, 0, ' ');
+	  g_string_truncate (tags, tags->len - 1);
+	}
+      g_print ("<%s%s>\n", element_name, tags->str);
+      g_string_free (tags, TRUE);
+    }
+


Some of the excessive debug code should either be removed, or surrounded
by
#ifdef GTK_ENABLE_DEBUG
#endif


+  else if (!strcmp (element_name, "glade-interface"))


What is glade-interface ?
Comment 11 Johan (not receiving bugmail) Dahlin 2006-05-22 18:48:31 UTC
> Hard to review a 8000 line patch. I'd like to see this broken up
> in a few smaller patches (or as the kernel guys say, "a proper
> patch sequence"):
> 1) necessary preparations (like adding the GtkCellView::model property)
> 2) add the GtkBuildable interface and GtkBuilder itself
> 3) add tests
> 4) implement GtkBuildable throughout the hierarchy

I'll swap 3 & 4 and insert a new one in the beginning for the lazy type utilities.

> I think the GtkBuildable interface needs some documentation explaining
> when I have to implement which functions, and what they are supposed
> to do. The applies to some of the peculiarities of GtkBuilder, e.g.
> delayed properties. 

Agreed. I started doing this, I will complete the rest of the documentation.

> Do you have any plan for documenting the allowed values for the
> type parameter of GtkBuildable::add ?

It's defined per widget, similar to custom tags.
Currently only used by GtkExpander, GtkFrame and GtkNotebook.

> Can a GtkBuilder be used only once, or can I give it multiple files ?
> I think it would be slightly nicer to have a _new() function which
> just constructs a new GtkBuilder, and a separate _parse() function
> which parses a file.

Sure, sounds good. I'm planning to add xul like overlays at a future point, which is one way of allowing you to dynamically merge in new interfaces.
The behavior of calling parse multiple times is undefined.

> Why are GtkActions and action groups buildable ? That goes against my 
> intuition that actions are defined by the code, and the ui definition 
> hooks them up with a control interface.

I thought it's a nice feature to have, to be able to implement actions
inside a ui manager. Of course, you'll not be forced to use them. 
In my opinion, as much as possible should be doable in a user interface
designer.

> The API documentation is obviously not quite done yet. We also need
> docs for the xml format - not necessarily a DTD, but some description
> of the general structure, and also any special tags which are
> supported for individual objects. Including examples. Ideally,
> we'd also add a chapter to our migration guides explaining how
> to convert from glade to the new format (what is the name of the
> new format, btw ?)

I'll improve the documentation, I hesitated to write it properly, to be able to
focus on the implementation details. Agreed a description of the general
structure is necessary.

> +static void
> +gtk_size_group_buildable_custom_tag_end (GtkBuildable  *buildable,
> +                                        GtkBuilder    *builder,
> +                                        GObject       *child,
> +                                        const gchar   *tagname,
> +                                        gpointer       data)
> +{
> +  g_signal_connect (builder, "finish",
> +                   (GCallback)builder_finish_cb, data);
> +}
> 
> Is it necessary to do this in custom_tag_end, or can we move
> it to custom_tag_start, and do away with the custom_tag_end ?

Good point. I think custom_tag_end can go away entierly.
It'll make it a little tricker when having multiple parsers (eg disconnecting
the signal when done)

> +gboolean
> +gtk_cell_layout_buildable_custom_tag_start (GtkBuildable  *buildable,
> 
> Should be static, or if needed in other source files, _-prefixed.
> This is not public api, right ?

I made it public to be able to use attributes for objects implementing GtkCellLayout which is not inside gtk.

> +      if (!GTK_BUILDABLE_GET_IFACE (info->object) ||
> +         !GTK_BUILDABLE_GET_IFACE (info->object)->get_internal_child)
> +       continue;
> +      
> +      obj = gtk_buildable_get_internal_child (GTK_BUILDABLE (info->object),
> +                                             builder,
> +                                             childname);
> 
> 
> This should just be 
>    if (GTK_IS_BUILDABLE (info->object)
>      obj = gtk_buildable_get_internal_child (GTK_BUILDABLE (info->object),
>                                              builder,
>                                              childname);
>
> and gtk_buildable_get_internal_child should just return NULL if 
> get_internal_child is NULL

That won't work for some dialogs, created in the glade way. You need to be able
to reference an internal child which is not in a direct parent:
<fontselectiondialog>
  <object ... internal-child="action_area">
    <object ... internal-child="ok_button">

ok_button is an internal child (eg attribute) of fontselectiondialog, not of action_area.

> +_gtk_builder_finish (GtkBuilder  *builder,
> +                    const gchar *domain)
> 
> 
> Why is the domain passed in here ? Looking at parse_buffer, it
> appears the domain is already known before the parsing starts,
> so you could just set it on the builder there.

It's not always known, there are two ways of specify the domain;
1) in the constructor, just like libglade
2) as tag at the root element

1) is the only way you can do in libglade.
2) was added to gazpacho, which is very useful for interfaces which 
are part of libraries, where the domain is static but different 
from the default one (application defined).

> +const gchar*   gtk_builder_get_name           (GtkBuilder *builder,
> +                                              GObject *object);
>
>
> Should this be get_object_name ? It is not returning the name
> of the builder, right ?

That was actually fixed in v4 of the patch.

> +  /* Do not free the signal list, the GtkBuilder takes
> +     ownership of it */
> +  g_slist_free (info->signals);  
> 
> What gives ? 

Not sure I understand what you mean here.
I'm reusing the content of the list in signal autoconnect, because of that
it should not be freed by the parser itself.

> Some of the excessive debug code should either be removed, or surrounded
> by
> #ifdef GTK_ENABLE_DEBUG
> #endif

I added ifdef guards around the start/end element output.
I still kept the object/property output enabled, because it's more useful.

> +  else if (!strcmp (element_name, "glade-interface"))
> 
> 
> What is glade-interface ?

The root element, from libglade. 
I'll rename it to interface and actually make sure it's the root element.
Comment 12 Matthias Clasen 2006-05-23 02:08:02 UTC
>> This should just be 
>>    if (GTK_IS_BUILDABLE (info->object)
>>      obj = gtk_buildable_get_internal_child (GTK_BUILDABLE (info->object),
>>                                              builder,
>>                                              childname);
>>
.> and gtk_buildable_get_internal_child should just return NULL if 
>> get_internal_child is NULL
>
>That won't work for some dialogs, created in the glade way. You need to be able
>to reference an internal child which is not in a direct parent:
><fontselectiondialog>
>  <object ... internal-child="action_area">
>    <object ... internal-child="ok_button">
>
>ok_button is an internal child (eg attribute) of fontselectiondialog, not of
>action_area.

Hmm, not sure I see why this is a problem. I think the outcome will be just the
same if the code is changed as I proposed, but I may be missing something.

>> Why is the domain passed in here ? Looking at parse_buffer, it
>> appears the domain is already known before the parsing starts,
>> so you could just set it on the builder there.
>
>It's not always known, there are two ways of specify the domain;
>1) in the constructor, just like libglade
>2) as tag at the root element
>
>1) is the only way you can do in libglade.
>2) was added to gazpacho, which is very useful for interfaces which 
>are part of libraries, where the domain is static but different 
>from the default one (application defined).

Makes a lot of sense.

>> +  /* Do not free the signal list, the GtkBuilder takes
>> +     ownership of it */
>> +  g_slist_free (info->signals);  
>> 
>> What gives ? 
>
>Not sure I understand what you mean here.

The comment seems to contradict the code immediately following it...


Comment 13 Johan (not receiving bugmail) Dahlin 2006-05-23 19:56:42 UTC
Created attachment 66079 [details] [review]
v5: typeutils

Also includes debugging support in gtkmain.
Comment 14 Johan (not receiving bugmail) Dahlin 2006-05-23 19:57:53 UTC
Created attachment 66080 [details] [review]
v5: gtkbuildable, gtkbuilder & gtkbuilderparser.c
Comment 15 Johan (not receiving bugmail) Dahlin 2006-05-23 20:00:00 UTC
Created attachment 66081 [details] [review]
v5: implement buildable
Comment 16 Johan (not receiving bugmail) Dahlin 2006-05-23 20:00:55 UTC
Created attachment 66082 [details] [review]
v5: Add GtkCellView:model property
Comment 17 Johan (not receiving bugmail) Dahlin 2006-05-23 20:01:28 UTC
Created attachment 66083 [details] [review]
v5: add tests
Comment 18 Matthias Clasen 2006-05-24 16:25:26 UTC
Comment on attachment 66082 [details] [review]
v5: Add GtkCellView:model property

The GtkCellView::model
patch is good, please commit.
Comment 19 Matthias Clasen 2006-05-24 16:38:41 UTC
Comment on attachment 66079 [details] [review]
v5: typeutils

Do you envision gtk_enum_from_string and
gtk_flags_from_string to move to gobject at some point ? In that case, we may want to keep them private for now. 

gtk_lazy_type_from_name
is not a very clear name, 
IMO. You could argue that g_type_from_name is actually lazier than your
variant...

Is the type registration thing supposed to be extensible at runtime, ie 
is gtk_type_register meant to be called outside gtk ?

I would love to avoid the duplication between the relocation-causing array and the hash table.
Comment 20 Matthias Clasen 2006-05-24 17:07:56 UTC
Comment on attachment 66080 [details] [review]
v5: gtkbuildable, gtkbuilder & gtkbuilderparser.c



+gboolean gtk_builder_value_from_string (GParamSpec  *pspec,
+					const gchar *string,
+					GValue      *value);
+gboolean gtk_builder_value_from_string_type (GType        type,
+					     const gchar *string,
+					     GValue      *value);

Same question as for the type utils: is this needed as public api ?

What is the relation between gtk_builder_get_object_name
and gtk_buildable_set_name ?
Is the name stored via the interface or as qdata ?
Comment 21 Matthias Clasen 2006-05-24 17:12:06 UTC
Comment on attachment 66080 [details] [review]
v5: gtkbuildable, gtkbuilder & gtkbuilderparser.c

Why is GtkBuilderPrivate.delayed_properties 
freed in finish, but the rest in finalize ? If you want to make repeated parse  calls work, finish
should probably return to the same state as init
Comment 22 Johan (not receiving bugmail) Dahlin 2006-05-24 17:13:26 UTC
(In reply to comment #19)
> (From update of attachment 66079 [details] [review] [edit])
> Do you envision gtk_enum_from_string and
> gtk_flags_from_string to move to gobject at some point ? In that case, we may
> want to keep them private for now.

Yes, I hope they'll make it to glib at some point. They seem to be generally useful. So I'll make them private.

> gtk_lazy_type_from_name
> is not a very clear name, 
> IMO. You could argue that g_type_from_name is actually lazier than your
> variant...

I don't know of a better name, do you have any suggestions?
gtk_type_from_name is already taken.

> Is the type registration thing supposed to be extensible at runtime, ie 
> is gtk_type_register meant to be called outside gtk ?

Yes, that's why it was added.
Otherwise "FooWidget" won't work unless you force a call to foo_widget_get_type
before instantiating your builder object.

> I would love to avoid the duplication between the relocation-causing array and
> the hash table.

Would changing it to be a long list of g_hash_table_insert calls solve that?

Comment 23 Johan (not receiving bugmail) Dahlin 2006-05-24 17:19:44 UTC
> +gboolean gtk_builder_value_from_string (GParamSpec  *pspec,
> +                                       const gchar *string,
> +                                       GValue      *value);
> +gboolean gtk_builder_value_from_string_type (GType        type,
> +                                            const gchar *string,
> +                                            GValue      *value);
> 
> Same question as for the type utils: is this needed as public api ?

These ones are needed to be able to implement custom parsers outside of gtk.
Tim does not want them in glib, but I would prefer making them public, to avoid having to reimplement them.

> What is the relation between gtk_builder_get_object_name
> and gtk_buildable_set_name ?
> Is the name stored via the interface or as qdata ?

gtk_buildable_set_name sets the name as qdata, it also allows the object to synchronize the name of the builder with it's internal name, if it has one.
GtkWidget implements this and updates it's own name attribute.
This is useful so widgets created by the builder will have the same name as they do in the xml representation.
Comment 24 Johan (not receiving bugmail) Dahlin 2006-05-24 17:21:55 UTC
(In reply to comment #21)
> (From update of attachment 66080 [details] [review] [edit])
> Why is GtkBuilderPrivate.delayed_properties 
> freed in finish, but the rest in finalize ? If you want to make repeated parse 
> calls work, finish
> should probably return to the same state as init

Good point.

Repeated call to parse() *might* require the whole tree saved.

Comment 25 Matthias Clasen 2006-05-24 17:24:49 UTC
Comment on attachment 66080 [details] [review]
v5: gtkbuildable, gtkbuilder & gtkbuilderparser.c

I think the way the GtkBuildable interface is documented is ideal.

"This method can be implemented to...". The documentation for gtk_buildable_... should just explain what this function does, we need some other way to document the interface for people who want to implement it. I know that we have no prior art for that...
Comment 26 Matthias Clasen 2006-05-24 17:30:46 UTC
>Yes, that's why it was added.
>Otherwise "FooWidget" won't work unless you force a call to foo_widget_get_type
>before instantiating your builder object.



What is the practical difference between calling

gtk_type_register (foo, bar)

before instantiating the builder and calling

foo_get_type () 

?
Comment 27 Johan (not receiving bugmail) Dahlin 2006-05-24 17:38:51 UTC
(In reply to comment #26)
> >Yes, that's why it was added.
> >Otherwise "FooWidget" won't work unless you force a call to foo_widget_get_type
> >before instantiating your builder object.
> 
> 
> 
> What is the practical difference between calling
> 
> gtk_type_register (foo, bar)
> 
> before instantiating the builder and calling
> 
> foo_get_type () 
> 
> ?

gtk_type_register can be done in foo_init (), to make sure that the type is not actually registered until it's needed. It might be a big deal for widget libraries which registers 10-15 widgets or more.
Comment 28 Matthias Clasen 2006-05-24 17:41:05 UTC
I doubt that registering 10-15 types can be a big deal, but ok
Comment 29 Matthias Clasen 2006-06-02 00:50:28 UTC
lets get this in early in the 2.12 cycle.
Comment 30 Mike Auty 2006-10-16 23:34:37 UTC
Can I ask about the status of this bug?  Did this manage to make it into the 2.12 cycle, or will we have to wait until 2.14 to start developing for it?  The CVS repository for gtk+ doesn't seem to contain it, but I'm not sure if it's too late to get it included in 2.12...
Comment 31 Johan (not receiving bugmail) Dahlin 2006-10-25 18:56:12 UTC
Created attachment 75396 [details] [review]
v6: typeutils

Avoid relocations by using direct calls to g_hash_table_insert.
gtk_lazy_type_from_name -> gtk_registered_type_from_name
Made the flag/enum -> string functions private.
Removed debugging parts which belongs to the builder patch.
Comment 32 Johan (not receiving bugmail) Dahlin 2006-10-25 18:58:04 UTC
Created attachment 75397 [details] [review]
v6: gtkbuildable, gtkbuilder & gtkbuilderparser.c

Frees delayed properties in finalize.
Documentation updates
Avoids exporting symbols as PLT.
Comment 33 Johan (not receiving bugmail) Dahlin 2006-10-25 18:58:58 UTC
Created attachment 75398 [details] [review]
v6: implement buildable

Updated for trunk and API changes. Avoids PLT entries.
Comment 34 Johan (not receiving bugmail) Dahlin 2006-10-25 18:59:44 UTC
Created attachment 75399 [details] [review]
v6: tests

Updated for trunk
Comment 35 Johan (not receiving bugmail) Dahlin 2006-10-25 20:56:28 UTC
Created attachment 75411 [details] [review]
v6.1: gtkbuildable, gtkbuilder & gtkbuilderparser.c

Contains a couple of documentation fixes and some unused code removed.
Comment 36 Tristan Van Berkom 2006-12-04 16:29:40 UTC
I am about to attach a storm of patches, this is basicly my
own interpretation of Johan's work... I picked up his tree
and expanded on it... I will include a detailed changelog of
everything I did at the end...
Comment 37 Tristan Van Berkom 2006-12-04 16:31:42 UTC
Created attachment 77648 [details] [review]
gtkbuilder/gtkbuildable/gtkbuilderparser portion

This is an incremental patch that assumes you have already
applied Johan's work to gtk+ HEAD.
Comment 38 Tristan Van Berkom 2006-12-04 16:33:06 UTC
Created attachment 77649 [details] [review]
typeutils portion (tristan's patches)

This is an icremental patch that assumes you have Johan's work
applied.
Comment 39 Tristan Van Berkom 2006-12-04 16:34:21 UTC
Created attachment 77650 [details] [review]
implement buildable portion (tristan's patches)

Again, an incremental patch on Johan's work
Comment 40 Tristan Van Berkom 2006-12-04 16:36:12 UTC
Created attachment 77651 [details] [review]
tests (tristan's patches)

Another incremental patch on Johan's patches...

Note that the tests arent very clean the way I left them,
but they do demonstrate ALOT of capacities of the builder...
the glade file now references Fry.png... I guess I'll attach that too.
Comment 41 Tristan Van Berkom 2006-12-04 16:37:04 UTC
Created attachment 77652 [details]
The png file for gtk+/tests/Fry.png
Comment 42 Tristan Van Berkom 2006-12-04 16:38:15 UTC
Created attachment 77653 [details] [review]
A Convenience "all-in-one" patch against cvs (tristan's patches)
Comment 43 Tristan Van Berkom 2006-12-04 16:44:19 UTC
With the above changes, you should be able to load any glade file
with the GtkBuilder with the sole exception of glade files containing
GtkCList widgets (look at libglade/glade/glade-gtk.c:clist_build_children())
to understand why ;-p

Here is a changelog of what I did in these patches:

   - Implemented accelerators (work should be done on making sure
     accelerators dont conflict with those created by uimanager).

   - Implemented versioning: <glade-interface version="1">

   - Parse adjustments and pixbufs the old fashioned way for version 1 
     glade files.

   - Handle unsupported atk property tags so that they are not mistaken for
     custom tags (preemptively building the object and the constructor ignoring 
     any trailing properties, such as accelerators).

   - Ported BOOL macros to the private header from libglade (this fixes non 0
     integer values, case insensitive yes/true as valid truth strings from
     the xml, formerly it was only "yes" that was supported, but libglade 
     supports more).

   - Handle packing properties in the builder parser proper, added 
     buildable->child_set_property() method and implemented it in GtkContainer,
     this allows non-gtkcontainers to have packing properties without
     doing that complex parsing jargon and also allows GtkContainer subclasses
     to handle buildable->child_set_property() in thier own way and chain up.

   - Allow virtual properties (packing or otherwise), properties listed in 
     the glade file that dont have coresponding pspecs installed will be 
     delivered to the buildable's set_property()/child_set_property()
     method as G_TYPE_STRING.

     The rationale behind this change is that it allows us to port in all the
     special-case code from libglade without opening up the can-of-worms 
     attached to making these special-case properties "real" object properties,
     this also made it rediculously easy to port all the support code from
     libglade in a safe manner.

   - Icky special case code added for GtkDialog --> GtkButton relationships
     (wrt response-id), added in the buildable code strictly for version 1
     glade files.

   - Dealt with digging out "type" packing properties in the case of
     version 1 glade files. (the new style is <child type="tab">, older
     glade files store "tab" in a packing property).

That pretty much describes what I did to the builder in short, I'll let
you guys draw your own conclusions... hoping to hear what people have
to say :)

Comment 44 Behdad Esfahbod 2006-12-04 20:18:19 UTC
w00t.  So cool...
Lets get this rolling again.
Comment 45 Johan (not receiving bugmail) Dahlin 2006-12-05 14:32:10 UTC
Tristan: Thanks for taking the time to do this work. However as there are already enough patches on this bug, I think it's better if you open up a new bug and make it depend on this one.

I'll review your patch(es) as soon as Matthias has given a go on the rest.

Matthias, ping?
Comment 46 giulio 2006-12-17 22:21:39 UTC
Hi,

more than a bug it's a RFE:

using Glade-GUI I would like add for a widget's signal (example a button) in "Object" field, a function-pointer for a generic function.

Example:

void on_button_clicked(GtkButton * button, gpointer user_data)
{
	...
	void (* func) (...) = user_data;
        ...
}

but actually, in run-time you'll get "user_data = 0x0".

Could be it a good "future-feature"?

Thanks,
Giulio
Comment 47 Tim Janik 2006-12-18 11:03:34 UTC
the enum lookup function can't be moved to glib and can't go into gtk either, because it is inherently broken:

>gint
>+_gtk_enum_from_string (GType type, const char *string)
>+{
>+  GEnumClass *eclass;
>+  GEnumValue *ev;
>+  gchar *endptr;
>+  gint ret = 0;
>+
>+  ret = strtoul (string, &endptr, 0);
>+  if (endptr != string) /* parsed a number */
>+    return ret;
>+
>+  eclass = g_type_class_ref (type);

here, you load the type's .so file so it's strings are in memory.

>+  ev = g_enum_get_value_by_name (eclass, string);
>+  if (!ev)
>+    ev = g_enum_get_value_by_nick (eclass, string);
>+
>+  if (ev)
>+    ret = ev->value;

here, you store a constant string pointer to a string.

>+
>+  g_type_class_unref (eclass);

here, you *unload* the type's .so file, so all it's functions, data and strings are removed from memory.

>+
>+  return ret;

at this point, the string pointer is an INVALID MEMORY REFERENCE.
>+}

so either you strdup the returned string before doing class_unref, or you use the existing GLib API for enum value lookups which force the caller to keep the enum class alive around calling the lookup function.
Comment 48 Tim Janik 2006-12-18 22:53:24 UTC
i'm sorry, Maxim Udushlivy correctly pointed out to me in private email that i've been misanalyzing the following code snippet:

(In reply to comment #47)
> the enum lookup function can't be moved to glib and can't go into gtk either,
> because it is inherently broken:
[...]
>+  gint ret = 0;
[...]
> >+  if (ev)
> >+    ret = ev->value;
> 
> here, you store a constant string pointer to a string.
[...]
> >+  return ret;

the code is not broken with regards to string references, because the extracted and returned value is an integer and not a string. it's all my fault, i've made an attempt at explaining why stale string references are bad while being completely blind to the fact that the affected type was an integer ;-)
Comment 49 Matthias Clasen 2007-06-01 16:51:50 UTC
Coming back after a long time, here are some comments on v6: typeutils

I don't particularly like the gtk_type_register stuff, but since we don't 
have introspection, it is clear that we need it. 

You might run into bug 403823 with gtk_registered_type_from_name().

gtk_type_register needs to be documented, and there probably also needs to be some higher-level documentation explaining that it is necessary to call gtk_type_register to make your library "GtkBuilder-ready". How do we expect third-party libraries to do that, btw ? export an init() function ?
Comment 50 Johan (not receiving bugmail) Dahlin 2007-06-01 17:39:11 UTC
(In reply to comment #49)
> Coming back after a long time, here are some comments on v6: typeutils
> 
> I don't particularly like the gtk_type_register stuff, but since we don't 
> have introspection, it is clear that we need it. 

Right. It's likely to be deprecated as soon as we get introspection.

> You might run into bug 403823 with gtk_registered_type_from_name().

Ouch, should probably fixed in glib, not sure it's worth working around that in this bug.

> gtk_type_register needs to be documented, and there probably also needs to be
> some higher-level documentation explaining that it is necessary to call
> gtk_type_register to make your library "GtkBuilder-ready". How do we expect
> third-party libraries to do that, btw ? export an init() function ?

While writing the API I envisioned the third-party libraries to call gtk_type_register some time during initialization (just after gtk_init for instance):

  gtk_init(..)
  gtk_type_register("FooButton", foo_button_get_type)
  gtk_type_register("BarEntry", bar_entry_button_get_type)

I'll add documentation and follow up with a new patch shortly.

Comment 51 Matthias Clasen 2007-06-01 17:58:31 UTC
Well, since they are libraries, they don't really have any business calling gtk_init()... so, you'd have to add an init() function to each library and
have the application call that:

gtk_init (...);
libgnome_init ();
libgnomecanvas_init ();

which is not nice. There is not a lot of good alternatives, though

- if you play games with __attribute__(constructor), 
  that gets executed before main()

- if you try to heuristically guess the mapping from type name to get_type()
  function name, it is going to fail sometimes

- you could try to dump that mapping into some files somewhere in the filesystem
  and parse those at gtk_init() time - that might be the least evil option
Comment 52 Johan (not receiving bugmail) Dahlin 2007-06-01 18:12:05 UTC
(In reply to comment #51)
> Well, since they are libraries, they don't really have any business calling
> gtk_init()... so, you'd have to add an init() function to each library and
> have the application call that:
> 
> gtk_init (...);
> libgnome_init ();
> libgnomecanvas_init ();

Oh that's true, I forgot about that.

> - you could try to dump that mapping into some files somewhere in the
> filesystem
>   and parse those at gtk_init() time - that might be the least evil option

GObject introspection would already do this if I'm not mistaken.

A fourth option could also be, to solve this problem in a generic way:

- add something like GnomeProgram which in combination with a library 
  specific startup routine.

> - if you try to heuristically guess the mapping from type name to get_type()
>   function name, it is going to fail sometimes

This is not too bad actually, since most well behaved libraries will use the
same naming mechanism as GTK+. For not well behaved, just require a _init function or wait for introspection.

How does that sound?

Comment 53 Matthias Clasen 2007-06-01 18:20:01 UTC
> > - you could try to dump that mapping into some files somewhere in the
> > filesystem
> >   and parse those at gtk_init() time - that might be the least evil option
> 
> GObject introspection would already do this if I'm not mistaken.

Yes, this would essentially be the very-very-minimal introspection just for getting this going.

> > - if you try to heuristically guess the mapping from type name to get_type()
> >   function name, it is going to fail sometimes
> 
> This is not too bad actually, since most well behaved libraries will use the
> same naming mechanism as GTK+. For not well behaved, just require a _init
> function or wait for introspection.

I guess we can try that and see how it goes. One other option might be to just
let GUI builders dump the map into the xml somewhere, since they most certainly know. If calling g_type_from_name() fails and there is no map in the xml, you
can still fall back to heuristics to go from type name to get_type function.

Might be worth taking this to gtk-devel-list.
Comment 54 Matthias Clasen 2007-06-01 19:06:10 UTC
comments on buildable v6.1:

- could probably use g_type_register_static_simple()

- why is gtk_buildable_set_name() always storing a copy of the
  name as object data ? I think it would be cleaner to also
  virtualize get_name, and then do away with the extra copy of the
  name if the object already has a name property.

- it also seems slightly odd that there is a gtk_builder_get_object_name.
  why is that needed, in addition to gtk_buildable_get_name ?

- The docs for the type parameter of gtk_buildable_add() could be
  improved. "how the child should be added" doesn't really say a lot.
  How about "Some objects have different 'slots' in which children 
  can be added. The @type parameter can be used to determine this 
  slot." Or something to that effect. And then give a very
  contrete example:

   #GtkNotebook uses the @type to distinguish between page labels
   (@type = "page-label") and normal children.

   or whatever type GtkNotebook uses for that...

- In general, the documentation needs some polish, but I guess
  we are not at the point to worry about that yet.

- Are some vfuncs mandatory to implement ? I don't see NULL
  checks for construct_child, custom_start_tag and custom_end_tag.
  Or rather, they have return_if_fail checks. 

- In gtk_builder_finalize:

+  g_hash_table_foreach (priv->objects, (GHFunc)g_free, NULL);
+  g_hash_table_destroy (priv->objects);

You could save that foreach if you used new_full to construct the hash table.

- The docs for gtk_builder_value_from_string_type refer to the 
  wrong function.

- In gtk_builder_value_from_string_type, I'm pretty sure you actually want
  to handle errors from strtol and friends. 

- Can we just move _gtk_{enum,flags}_from_string into gtkbuilder.c and
  make them static ? They seem to fit really well here. Or are they used
  somewhere else ?

- Do we want to make gtk_builder_parse_{filename,buffer} return a boolean
  indicating success, like many other GError-taking functions do ?

- gtk_builder_set_translation_domain() should be a regular setter
  for the domain property. Ie either change its name, or change the name
  of the property, and add change notification, and make set_property
  use the setter, perhaps.
  And where there is a setter, there shall also be a getter...


- Is there something missing here ? Other than a comment...
+  else if (!strcmp (element_name, "placeholder"))
+    {
+    }

 
- Do you want to pass G_MARKUP_TREAD_CDATA_AS_TEXT here ?
+  data->ctx = g_markup_parse_context_new (&parser, 0, data, NULL);


Comment 55 Matthias Clasen 2007-06-01 19:32:37 UTC
some initial comments on buildable-implementation v6:

- still doesn't make much sense to me to have actions and action groups
  defined by the xml, since they are supposed to correspond to the features
  exposed by your code...

- the names used for internal children and for slots to add children need to be 
  documented 
Comment 56 Matthias Clasen 2007-06-02 04:37:36 UTC
Some good ideas picked up from gtk-devel discussion:

- replace the GtkBuilder::finish signal with a
  GtkBuildable::parsing_finished vfunc

- add a GtkBuildable::set_child_property vfunc 
Comment 57 Johan (not receiving bugmail) Dahlin 2007-06-02 15:18:39 UTC
Created attachment 89239 [details] [review]
v7: typeutils
Comment 58 Matthias Clasen 2007-06-03 02:47:15 UTC
Do you want to add a way to add to the map in the xml ? 
Like 

<types>
  <type name="GtkUIManager" get_type_func="gtk_ui_manger_get_type"/>
  <type name="FooBar" get_type_func="FooBarTypeNum"/>
  ...

The parser would simply call gtk_register_type on the pairs.
Comment 59 Johan (not receiving bugmail) Dahlin 2007-06-03 03:20:21 UTC
(In reply to comment #58)
> Do you want to add a way to add to the map in the xml ? 
> Like 
> 
> <types>
>   <type name="GtkUIManager" get_type_func="gtk_ui_manger_get_type"/>
>   <type name="FooBar" get_type_func="FooBarTypeNum"/>
>   ...
> 
> The parser would simply call gtk_register_type on the pairs.
> 

That's an idea, it's not ideal to require that information to specified in all xml files though, but I guess acceptable until we have introspection.

Another idea, is to allow a symbol name to be specified instead of a type name when constructing an object:

either

  <object type="GtkUIManager:gtk_ui_manger_get_type">

which would mean, use GtkUIManager if registered otherwise call gtk_ui_manager_get_type to get the type.

or just

  <object type="gtk_ui_manger_get_type">
Comment 60 Matthias Clasen 2007-06-03 03:30:50 UTC
I'd rather add a separate attribute then

 <object type="GtkUIManager" get_type_func="gtk_ui_manager_get_type">

Since we are already dealing with xml, we can just as well let the xml parser
work for us rather then rolling an attribute-value-parser ourselves...
Comment 61 Tristan Van Berkom 2007-06-04 14:14:16 UTC
Yay, great to see this ball rolling... just adding a few fringe
comments...

(In reply to comment #54)
> comments on buildable v6.1:
[...]
> 
> - it also seems slightly odd that there is a gtk_builder_get_object_name.
>   why is that needed, in addition to gtk_buildable_get_name ?

Not every built object is GtkBuildable, only objects that require
special case code are GtkBuildable.

[...]
> - Can we just move _gtk_{enum,flags}_from_string into gtkbuilder.c and
>   make them static ? They seem to fit really well here. Or are they used
>   somewhere else ?

Those functions can be useful when custom parsing properties
in GtkBuildable code, for that reason I'd go so far as suggesting
we export those symbols for the sake of third party widgets.

> - Is there something missing here ? Other than a comment...
> +  else if (!strcmp (element_name, "placeholder"))
> +    {
> +    }

It prevents the parser code to fall into the
following "else choke_on_bad_xml()" clause (I did the same in
my patches to avoid choking on atk properties).

I wonder if it would be better policy to just ignore unrecognized
portions of the xml.
Comment 62 Johan (not receiving bugmail) Dahlin 2007-06-04 18:13:22 UTC
(In reply to comment #54)
> comments on buildable v6.1:
> 
> - could probably use g_type_register_static_simple()

Done, but it doesn't save too much..

> - why is gtk_buildable_set_name() always storing a copy of the
>   name as object data ? I think it would be cleaner to also
>   virtualize get_name, and then do away with the extra copy of the
>   name if the object already has a name property.

I can't remember the original reason, perhaps I just wanted to avoid
implementing get_name() in a couple of places.
Anyway I changed this and it looks a lot better.

> - it also seems slightly odd that there is a gtk_builder_get_object_name.
>   why is that needed, in addition to gtk_buildable_get_name ?

Can't remember, I removed it in favor of gtk_buildable_get_name.
Perhaps it was for libglade compatibility.

> - The docs for the type parameter of gtk_buildable_add() could be
>   improved. "how the child should be added" doesn't really say a lot.
>   How about "Some objects have different 'slots' in which children 
>   can be added. The @type parameter can be used to determine this 
>   slot." Or something to that effect. And then give a very
>   contrete example:
> 
>    #GtkNotebook uses the @type to distinguish between page labels
>    (@type = "page-label") and normal children.
> 
>    or whatever type GtkNotebook uses for that...
> 
> - In general, the documentation needs some polish, but I guess
>   we are not at the point to worry about that yet.

I skipped this for now, I'll go back to these items when the rest is
acceptable.

> - Are some vfuncs mandatory to implement ? I don't see NULL
>   checks for construct_child, custom_start_tag and custom_end_tag.
>   Or rather, they have return_if_fail checks. 

Depends.
They are not mandatory to implement unless you add custom tags
in your buildable XML definition.

> - In gtk_builder_finalize:
> 
> +  g_hash_table_foreach (priv->objects, (GHFunc)g_free, NULL);
> +  g_hash_table_destroy (priv->objects);
> 
> You could save that foreach if you used new_full to construct the hash table.

Look reasonable, done.

> - The docs for gtk_builder_value_from_string_type refer to the 
>   wrong function.

And the arguments were wrong too, updated.

> - In gtk_builder_value_from_string_type, I'm pretty sure you actually want
>   to handle errors from strtol and friends. 

Assume you also meant g_strtod (which is changed to g_ascii_strtod). Changed
to check for errno and print a warning for overflows and other errors.

> - Can we just move _gtk_{enum,flags}_from_string into gtkbuilder.c and
>   make them static ? They seem to fit really well here. Or are they used
>   somewhere else ?

Not quite, they're used by a few of the buildable implementations.
I added a gtk_builder prefer to them, made them public and moved them
into gtkbuildable.c

> - Do we want to make gtk_builder_parse_{filename,buffer} return a boolean
>   indicating success, like many other GError-taking functions do ?

Sure, not a bad idea at all.

> - gtk_builder_set_translation_domain() should be a regular setter
>   for the domain property. Ie either change its name, or change the name
>   of the property, and add change notification, and make set_property
>   use the setter, perhaps.
>   And where there is a setter, there shall also be a getter...

Agreed, I changed the property name to translation-domain, added a getter
and made gtk_builder_set_property call the setter.

> - Is there something missing here ? Other than a comment...
> +  else if (!strcmp (element_name, "placeholder"))
> +    {
> +    }

Tristian already pointed out why, I added a comment explaining.

> - Do you want to pass G_MARKUP_TREAD_CDATA_AS_TEXT here ?
> +  data->ctx = g_markup_parse_context_new (&parser, 0, data, NULL);

Right, that's probably a good idea too. Added.

(In reply to comment #56)
> Some good ideas picked up from gtk-devel discussion:
> 
> - replace the GtkBuilder::finish signal with a
>   GtkBuildable::parsing_finished vfunc

Good idea, it was a bit of work but it solves the bug Tristan pointed out.
Also resolved.

> - add a GtkBuildable::set_child_property vfunc 

I'm not so sure about this, since a set_child_property() would be specific to GtkContainer, should it really be exposed inside the API?
Also, it shouldn't really be necessary, there's no real need to override the way to set child properties is there?
Comment 63 Johan (not receiving bugmail) Dahlin 2007-06-04 18:20:05 UTC
Created attachment 89349 [details] [review]
v8: typeutils
Comment 64 Johan (not receiving bugmail) Dahlin 2007-06-04 18:20:26 UTC
Created attachment 89350 [details] [review]
v8: buildable
Comment 65 Johan (not receiving bugmail) Dahlin 2007-06-04 18:20:43 UTC
Created attachment 89351 [details] [review]
v8: buildable implementation
Comment 66 Johan (not receiving bugmail) Dahlin 2007-06-04 18:20:53 UTC
Created attachment 89352 [details] [review]
v8: tests
Comment 67 Tristan Van Berkom 2007-06-04 19:12:59 UTC
(In reply to comment #62)
[...]
> > - add a GtkBuildable::set_child_property vfunc 
> 
> I'm not so sure about this, since a set_child_property() would be specific to
> GtkContainer, should it really be exposed inside the API?
> Also, it shouldn't really be necessary, there's no real need to override the
> way to set child properties is there?

I disagree that child properties are limited to GtkContainer, as
soon as we enter the realm of building non widget objects in the
builder there becomes instances when a delagate object can
be parented by another object - these occurrences can as easily
have packing properties as widgets do in gtkcontainers.

As far as needing to override them from the buildable iface goes,
I think ideally we shouldnt need to override set_property()
or child_set_property(); maybe:
  - the object maybe should simply check if its being loaded at 
    set_[child_]property() time if theres any special code to write ?
  - objects could have a new property flag, along the lines of
    G_PARAM_CONSTRUCT, could be G_PARAM_BUILABLE[_ONLY] ?
  - the object's author should simply write code that works regardless
    of the context in which properties are being set ?

That idealistic notion aside, I think its important to not leave
packing properties out of the picture, so if there is a way to 
override how a property is set on an object, please let there
be a way to override how a packing property is set for a child
object.
Comment 68 Johan (not receiving bugmail) Dahlin 2007-06-04 20:41:19 UTC
One thing that is still unimplemented is unreffing of objects after the builder is finalized. I discussed this with James the last time he was in Brazil and left the following notes which I'm adding here so I won't lose them (which I almost did):

Builder should have two hash tables:
1: name -> object
2: object ->name.

There should be a weak ref handler on each object which does:

1. lookup object name in second hash
2. lookup object in first hash
3. If objects are the same, remove entry from 1st hash
4. remove entry from 2nd hash which frees the name

In the builders finalize, do the following:

1. g_hash_table_foreach_remove on 2nd hash table
   * remove reak ref from object
   * return True to remove entry from hash table
2. unref 1st and 2nd hash table

For GObjects where we own the reference return by g_object_new, add to a list

when building is finished unref each object in list.

should work for size groups, tree models, text buffers, but don't do this for GtkWindow, which is floating.



Comment 69 Nick Schermer 2007-06-05 09:22:31 UTC
Small note on this patch: maybe it's better to move GtkBuilderPrivate *priv to the GtkBuilder structure and call GTK_BUILDER_GET_PRIVATE only once in the gtk_builder_init function, just like most of the widgets in gtk do.
Comment 70 Matthias Clasen 2007-06-06 16:01:04 UTC
> I disagree that child properties are limited to GtkContainer, as
> soon as we enter the realm of building non widget objects in the
> builder there becomes instances when a delagate object can
> be parented by another object - these occurrences can as easily
> have packing properties as widgets do in gtkcontainers.

A likely candidate for non-container child properties would be 
cell layout and cell renderers, though I don't think they really 
have any packing properties right now.

Comment 71 Matthias Clasen 2007-06-06 16:10:48 UTC
One good comment from recent mailing list discussion was to add a virtual 
function for type resolution, so that, e.g. gtkmm can override it. 

I'd also like to bring up the idea of an xml attribute to specify the get_type
function again. 

Ideally, I'd like the type registration stuff to be not exposed as public api 
at all. Do you think that is feasible when we have the heuristic name guessing 
and the get_type attribute ?

Comment 72 Johan (not receiving bugmail) Dahlin 2007-06-06 16:16:58 UTC
(In reply to comment #71)
> One good comment from recent mailing list discussion was to add a virtual 
> function for type resolution, so that, e.g. gtkmm can override it. 

This is rather ugly IMO, I'll talk to Murray first and see if it's possible to avoid it, if not I'll just port over the vtable func from libglade.

> I'd also like to bring up the idea of an xml attribute to specify the get_type
> function again. 

I've done this locally already. I need to test it first although. Consider it done.

> Ideally, I'd like the type registration stuff to be not exposed as public api 
> at all. Do you think that is feasible when we have the heuristic name guessing 
> and the get_type attribute ?

As mentioned on IRC:

<johan> mclasen: possibly
<mclasen> would be much nicer imo, if we could keep gtk out of the type registration business, and limp along with heuristics and xml attributes for now
<johan> agreed
<johan> it's fine assuming gobject-introspection will be ready in the not too distant future
<johan> there are workaround today aswell, such as forcing registration of these types before loading the xml
<mclasen> exactly. Also, I need to ask rambokid about fixing the g_type_from_name() segfault
<johan> another workaround is renaming the get_type functions to use compatible naming or adding an additional/alias
Comment 73 Matthias Clasen 2007-06-06 16:18:42 UTC
With GtkBuildable::parser_finished in place, do we still need GtkBuilder:finish ?
Comment 74 Johan (not receiving bugmail) Dahlin 2007-06-06 16:22:24 UTC
(In reply to comment #73)
> With GtkBuildable::parser_finished in place, do we still need GtkBuilder:finish
> ?
> 

It's still used for;
GtkWidget::has-default -> gtk_widget_grab_default()
GtkWidget::has-focus -> gtk_widget_grab_focus()
GtkWindow::visible -> gtk_widget_show()

All of these needs to be done after the whole interface is constructed.
For visible to avoid showing the construction of the interface until it is completed, for focus/default to be able to set them for the right widget.


Comment 75 Matthias Clasen 2007-06-06 16:31:34 UTC
Considering that ::finish is emitted before the parser_finished cbs are executed, you could just as well do this from parser_finished, no ?
Comment 76 Matthias Clasen 2007-06-06 16:32:27 UTC
Also, somebodys earlier comment about disconnecting the signal handlers still stands.
Comment 77 Tristan Van Berkom 2007-06-06 16:35:50 UTC
(In reply to comment #74)
> (In reply to comment #73)
> > With GtkBuildable::parser_finished in place, do we still need GtkBuilder:finish
> > ?
> > 
> 
> It's still used for;
> GtkWidget::has-default -> gtk_widget_grab_default()
> GtkWidget::has-focus -> gtk_widget_grab_focus()
> GtkWindow::visible -> gtk_widget_show()
> 
> All of these needs to be done after the whole interface is constructed.
> For visible to avoid showing the construction of the interface until it is
> completed, for focus/default to be able to set them for the right widget.
> 

If GtkWidget does those things for those properties and
handles GtkBuilder:finish, it would also have to unregister
to the GtkBuilder:finish callback afterwords.

Rather, I would have expected the GtkBuilder to emit the
buildable::parser_finished signal on all buildable objects 
after parsing the entire interface.

note also this removes the need for explicit signal connections
on a per property basis in gtkwidget.c afaics, just do the
appropriate activity inside the parser_finished() implementation
(i.e. grab_focus/grab_default/show if appropriate all in one
place).

Comment 78 Johan (not receiving bugmail) Dahlin 2007-06-06 16:38:43 UTC
(In reply to comment #75)
> Considering that ::finish is emitted before the parser_finished cbs are
> executed, you could just as well do this from parser_finished, no ?

(In reply to comment #76)
> Also, somebodys earlier comment about disconnecting the signal handlers still
> stands.

Doable yes, but I need to figure out how to persist data from custom_set_property() to parser_finished(). Perhaps qdata or priv is the right solution here.
Comment 79 Tristan Van Berkom 2007-06-06 16:45:14 UTC
(In reply to comment #71)
> One good comment from recent mailing list discussion was to add a virtual 
> function for type resolution, so that, e.g. gtkmm can override it. 
> 
> I'd also like to bring up the idea of an xml attribute to specify the get_type
> function again. 
> 
> Ideally, I'd like the type registration stuff to be not exposed as public api 
> at all. Do you think that is feasible when we have the heuristic name guessing 
> and the get_type attribute ?
> 

Personally I think that specifying the get_type() function is what
should be primarily relied upon, rather than opening up the can of worms
of making rules about proper naming conventions for registered types.

Maybe this would be an interesting approach:
   - Make a type resolver vfunc to GType get_type (char *type_name);
   - Default implementation assumes that "type_name" is the name
     of the function that returns the type ("foo_bar_get_type").
   - language bindings can override the vfunc and do whatever method
     they see fit, if a special type isnt found; chain up to the default
     implementation.
Comment 80 Johan (not receiving bugmail) Dahlin 2007-06-06 17:02:37 UTC
(In reply to comment #70)
> > I disagree that child properties are limited to GtkContainer, as
> > soon as we enter the realm of building non widget objects in the
> > builder there becomes instances when a delagate object can
> > be parented by another object - these occurrences can as easily
> > have packing properties as widgets do in gtkcontainers.
> 
> A likely candidate for non-container child properties would be 
> cell layout and cell renderers, though I don't think they really 
> have any packing properties right now.
> 

They aren't actually child properties in the sense that they have a pspec.
They're attributes which are mapped to real properties.

If we want to make something generic we'd have to avoid exposing pspecs.
Comment 81 Matthias Clasen 2007-06-06 17:08:41 UTC
Looking at the delayed properties implementation, I notice that it currently does not handle construct-only object properties. One such example is
GtkFileChooserButton::dialog. 

What should probably happen here is that the builder should pass construct-only object parameters into the constructor if the object has already been constructed,
and fail with a clear error otherwise. It will then be the responsibility of the ui builder to put objects in the xml in the right order.
Comment 82 Tristan Van Berkom 2007-06-06 17:15:48 UTC
(In reply to comment #80)
> > 
> > A likely candidate for non-container child properties would be 
> > cell layout and cell renderers, though I don't think they really 
> > have any packing properties right now.
> 
> They aren't actually child properties in the sense that they have a pspec.
> They're attributes which are mapped to real properties.
> 
> If we want to make something generic we'd have to avoid exposing pspecs.
> 

It might be a little late in the game but its really not a long
shot away to do GContainerIface, in which case packing properties
can be introspected with their pspecs and all (would just have
to be implemented by GtkContainer and accessed as GContainer
from the builder, any object at that point would be free to
implement GContainer and publish packing properties).
Comment 83 Kalle Vahlman 2007-06-06 17:29:47 UTC
Warning: API nitpicks ahead (from the "user == developer" POV):

gtk_builder_parse_filename() should be renamed (it doesn't parse the file name)

Now, I'm thinking there are few options for this, the obvious one being *_parse_file().

GtkUIManager and loads of other API seem to use *_from_file() as the term for "load stuff from file" (which makes sense).

Also the word "parse" seems to be more description of what happens during the function, not of what the developer actually wants to happen. So perhaps "build" would be more accurate (though "builder_build" is a bit funny), or even go the UIManager path and make it gtk_builder_add_ui_from_file(). This would align nicely the two conceptually similar APIs.

"Add" would also support the fact that you actually can add UI multiple times to the same builder (is this "officially" supported btw?), barring the fact that if you call signal_autoconnect() multiple times (it registers multiple handlers). I'm sure that wouldn't be too hard to fix though. You likely never want to connect the same handler to the same object twice, right?

Another thing that should be considered is the parse_buffer(). I think for much the same reasons as above, this should be *_from_string(). Also for internal consistency since the flag, value and enum parsers are *_from_string() and operate on the same parameter type.

Comment 84 Matthias Clasen 2007-06-06 18:27:39 UTC
> It might be a little late in the game but its really not a long
> shot away to do GContainerIface, in which case packing properties
> can be introspected with their pspecs and all (would just have
> to be implemented by GtkContainer and accessed as GContainer
> from the builder, any object at that point would be free to
> implement GContainer and publish packing properties).

Maybe we are all on the same page here, but child properties can already be introspected with pspecs and everything from GtkContainer today.
Comment 85 Tristan Van Berkom 2007-06-06 18:57:11 UTC
Yes they can, its just that once the moment comes that child
properties are used for object children that are not GtkContainers,
then this cant be done (the patches I wrote on bug 383035 do
a special case exception "if (GTK_IS_CONTAINER())" to introspect
packing properties, if any other packing properties are found then
they are wrapped into a GParamSpecString and passed along without
any type conversions)

this would ofcourse work better if non GtkContainer parents were
also allowed to declare packing properties by implementing GContainer.
Comment 86 Johan (not receiving bugmail) Dahlin 2007-06-07 18:57:25 UTC
Okay, time to attach another round of patches, changes since the last revision:

* Rename parser_finished to custom_finished, this is called one time for each
  custom parser specified, this simplifies writing custom parsers which need to 
  have things done when parsing is finished (eg, sizegroup, dialog, treestore).
* Add parser_finished vfunc and removed GtkBuilder::finished signal
* Add tests for GtkWidget::has-default/has-focus
* Change custom_tag_start/end user_data argument to bea gpointer *, instead of gpointer.
* Add vfunc get_type_from_name, similar to GladeXML->lookup_func (for gtkmm)
* Add support for construct only object properties
* Add beginning of GError support, much more work will be done here for improved
  error reporting which will not add significant new API
* As suggested by Kalle, rename parse_filename to add_from_file and parse_buffer to add_from_string. While calling add_from_file is currently untested/undefined it is probably already working in some case, future work is required to test and define what is going to happen.
* get_type symbol name guessing heuristic is improved to handle all known types in Gtk+.
* gtk_type_register & type table is removed
Comment 87 Johan (not receiving bugmail) Dahlin 2007-06-07 19:06:42 UTC
Created attachment 89557 [details] [review]
v9: tests
Comment 88 Johan (not receiving bugmail) Dahlin 2007-06-07 19:19:09 UTC
Created attachment 89558 [details] [review]
v9: buildable
Comment 89 Johan (not receiving bugmail) Dahlin 2007-06-07 19:19:12 UTC
Created attachment 89559 [details] [review]
v9: buildable implementation
Comment 90 Johan (not receiving bugmail) Dahlin 2007-06-07 19:19:47 UTC
Created attachment 89560 [details] [review]
v9: typeutils
Comment 91 Matthias Clasen 2007-06-08 00:32:46 UTC
> * Change custom_tag_start/end user_data argument to bea gpointer *, instead of
> gpointer.

Hmm, how is this supposed to be used, and how does the memory management work ?
E.g. looking at 

+gtk_list_store_buildable_custom_tag_start
+gtk_list_store_buildable_custom_tag_end

I find it slightly confusing that the start function allocates the user_data,
but end does not free it.

The documentation for user_data is not really clarifying the situation:
+ * @data: user data that will be passed in to parser functions


> * gtk_type_register & type table is removed

With this gone, there is not much point in having gtk_registered_type_by_name()
public either. I'd really really like gtk to stay out of the type registration business 


> +      object_type = gtk_builder_get_type_from_name (builder, G_OBJECT_TYPE_NAME (object));

This looks fishy. If you already have an object, there is no need to go to such length to get its type...

Comment 92 Johan (not receiving bugmail) Dahlin 2007-06-08 03:58:23 UTC
(In reply to comment #91)
> > * Change custom_tag_start/end user_data argument to bea gpointer *, instead of
> > gpointer.
> 
> Hmm, how is this supposed to be used, and how does the memory management work ?
> E.g. looking at 
> 
> +gtk_list_store_buildable_custom_tag_start
> +gtk_list_store_buildable_custom_tag_end
> 
> I find it slightly confusing that the start function allocates the user_data,
> but end does not free it.
> 
> The documentation for user_data is not really clarifying the situation:
> + * @data: user data that will be passed in to parser functions

I can't remember of my head, but I think that's a leak in each case, the structures allocated in start should definitely be freed end.

The documentation has not really seen the love it needed in the last couple of rounds, it's all subject to change (or be written at all!).

> > * gtk_type_register & type table is removed
> 
> With this gone, there is not much point in having gtk_registered_type_by_name()
> public either. I'd really really like gtk to stay out of the type registration
> business 

It is just (very) badly named, the only thing it does now is guessing the name of the types corresponding *_get_type function if g_type_name returns G_TYPE_INVALID. Perhaps this should be moved to glib itself, I definitely think that there's a use case for this kind of function outside of the builder itself.

> > +      object_type = gtk_builder_get_type_from_name (builder, G_OBJECT_TYPE_NAME (object));
> 
> This looks fishy. If you already have an object, there is no need to go to such
> length to get its type...

gtk_builder_get_type_from_name is used to be able to support overloading of types which apparently is required by gtkmm, I replaced all g_type_name with that call for consistency.

Comment 93 Matthias Clasen 2007-06-08 04:12:41 UTC
> It is just (very) badly named, the only thing it does now is guessing the name
> of the types corresponding *_get_type function if g_type_name returns
> G_TYPE_INVALID. Perhaps this should be moved to glib itself, I definitely think
> that there's a use case for this kind of function outside of the builder
> itself.

I know that it does that, I just wonder if we can avoid exporting this function from gtk. If there is a use case for this function, then let it be reported separately and lets have the function added to gobject. If we don't export it from gtk at this point, we can then later simply switch to using the glib function.
If we export it from gtk, we're stuck with it forever, and the best we can do is deprecate it. 


> gtk_builder_get_type_from_name is used to be able to support overloading of
> types which apparently is required by gtkmm, I replaced all g_type_name with
> that call for consistency.

Well, look at the code. You have an object, and you want a type. 
What is wrong with simply using G_OBJECT_TYPE (object) there ?
 
Comment 94 Kalle Vahlman 2007-06-08 12:30:58 UTC
Created attachment 89600 [details] [review]
Patch to make gtk_builder_signal_autoconnect() reject double handlers

(In reply to comment #83)
> "Add" would also support the fact that you actually can add UI multiple times
> to the same builder (is this "officially" supported btw?), barring the fact
> that if you call signal_autoconnect() multiple times (it registers multiple
> handlers). I'm sure that wouldn't be too hard to fix though. You likely never
> want to connect the same handler to the same object twice, right?
[...]
(In reply to comment #86)
> * As suggested by Kalle, rename parse_filename to add_from_file and
> parse_buffer to add_from_string. While calling add_from_file is currently
> untested/undefined it is probably already working in some case, future work is
> required to test and define what is going to happen.

Well, at least for the simple case of having two toplevel windows in different xml:s and reusing the same handler in both works right with the attached patch (ie. doesn't connect the callback twice to the objects in the last loaded UI).

It does this by looking up the signal id for the type and searches for a registered handler for that id with the same callback. If it finds it, it'll complain (though this is mostly for testing) and refuse to connect it.

My test case included a duplication of the demo.glade in tests, an extra callback for the about dialog (which is connected only to the about action in the duplicated .glade) in builderdemo.c as well as duplicating the loading portion there (except the creation of the builder object of course).

So the duplicated about action should fire two handlers, and the original just one. Before the patch all three defined handlers are fired on activation of the duplicated about item, with the patch only the ones that are uniquely defined to that object (this also rejects double handlers if you specifically ask for it in the .glade).

Does this look/sound right?
Comment 95 Johan (not receiving bugmail) Dahlin 2007-06-08 13:58:05 UTC
(In reply to comment #93)
> > It is just (very) badly named, the only thing it does now is guessing the name
> > of the types corresponding *_get_type function if g_type_name returns
> > G_TYPE_INVALID. Perhaps this should be moved to glib itself, I definitely think
> > that there's a use case for this kind of function outside of the builder
> > itself.
> 
> I know that it does that, I just wonder if we can avoid exporting this function
> from gtk. If there is a use case for this function, then let it be reported
> separately and lets have the function added to gobject. If we don't export it
> from gtk at this point, we can then later simply switch to using the glib
> function.
> If we export it from gtk, we're stuck with it forever, and the best we can do
> is deprecate it. 

Right, I see your point. I'll open up a separate bug against glib, make it private and move it to gtk_builder_get_type_from_name, the only call site left.

> > gtk_builder_get_type_from_name is used to be able to support overloading of
> > types which apparently is required by gtkmm, I replaced all g_type_name with
> > that call for consistency.
> 
> Well, look at the code. You have an object, and you want a type. 
> What is wrong with simply using G_OBJECT_TYPE (object) there ?

Oh sorry, I misunderstood, I thought you were pointing at similar code at another place. You're right of course, it should just use G_OBJECT_TYPE.

Comment 96 Johan (not receiving bugmail) Dahlin 2007-06-08 14:33:19 UTC
(In reply to comment #94)
[..]
> My test case included a duplication of the demo.glade in tests, an extra
> callback for the about dialog (which is connected only to the about action in
> the duplicated .glade) in builderdemo.c as well as duplicating the loading
> portion there (except the creation of the builder object of course).

Is that really a valid use case, why would you want to parse an identical file twice?

Comment 97 Johan (not receiving bugmail) Dahlin 2007-06-08 15:26:12 UTC
(In reply to comment #92)
> (In reply to comment #91)
> > > * Change custom_tag_start/end user_data argument to bea gpointer *, instead of
> > > gpointer.
> > 
> > Hmm, how is this supposed to be used, and how does the memory management work ?
> > E.g. looking at 
> > 
> > +gtk_list_store_buildable_custom_tag_start
> > +gtk_list_store_buildable_custom_tag_end
> > 
> > I find it slightly confusing that the start function allocates the user_data,
> > but end does not free it.
> > 
> > The documentation for user_data is not really clarifying the situation:
> > + * @data: user data that will be passed in to parser functions
> 
> I can't remember of my head, but I think that's a leak in each case, the
> structures allocated in start should definitely be freed end.

Actually, after looking at the code I found out that the following is the case:
In most cases the data is not freed in custom_tag_end because it maybe reference other widgets not yet constructed, so it needs to wait until custom_finished until it can actually free all the data structures used.

In some cases it can already free them in custom_tag_end, like the liststore and it should probably be done there (instead of the GMarkupParsers element_end) for clarity.
Comment 98 Kalle Vahlman 2007-06-08 15:51:32 UTC
(In reply to comment #96)
> (In reply to comment #94)
> [..]
> > My test case included a duplication of the demo.glade in tests, an extra
> > callback for the about dialog (which is connected only to the about action in
> > the duplicated .glade) in builderdemo.c as well as duplicating the loading
> > portion there (except the creation of the builder object of course).
> 
> Is that really a valid use case, why would you want to parse an identical file
> twice?
 
If you would do such outrageous thing like dynamically generating the UI description, or build multiple descriptions from an "outside source", ensuring that every widget is named uniquely would be tedious. As long as you don't want to look it up by name, you should be fine using callbacks even if it isn't uniquely named right?.

I could imagine wanting to load the same glade multiple times in multiwindowed applications too.

That said, all of this can of course be achieved by instantiating a builder object per file/string (without the drawback of not being able to look the objects up). I guess the builder object doesn't have such overhead that it would matter, does it?

The patch should also prevent multiple callback connecting within one file however, which I think would be worth it even if loading multiple non-unique descriptions isn't going to be supported.
Comment 99 Matthias Clasen 2007-06-10 04:43:48 UTC
I don't think the case of multiple files with conflicting ids is worth supporting.
Nonconflicting ids are only on sed call away...

I also don't think doing extra work to reject duplicate signal handlers is worth it. If this was a problem, GUI builders can easily enforce the "only one signal handler" rule.
Comment 100 Kalle Vahlman 2007-06-10 08:49:54 UTC
(In reply to comment #99)
> I don't think the case of multiple files with conflicting ids is worth
> supporting.
> Nonconflicting ids are only on sed call away...

Fair enough.
 
> I also don't think doing extra work to reject duplicate signal handlers is
> worth it. If this was a problem, GUI builders can easily enforce the "only one
> signal handler" rule.

I'm more concerned about the "only one invocation of signal_autoconnect() on one instance of gtkbuilder during it's lifetime" restriction this imposes (unless fixed elsewhere).

If you require that, there's no point in providing it as separate API call, it could be done implicitly after the build process. And if every UI description needs a GtkBuilder instance, maybe the API should relfect that and offer new_form_*() constructors instead of the parse API and autoconnect implicitly (though I guess this would be bad for bindings?).

It's not just about duplicate ID's you see, if you load your main UI and reuse the builder for a dialog .glade it will reconnect the signals in the main UI. Now, either this could be fixed by flagging the signal info with "already did that" or by the bit more generic way that my patch does, check for the handler in the object instance.

In my opinion, limiting the builder instances to one description sounds like more work for the developer since he has to manage builders for each .glade he/she wants to use. Keeping all dialogs etc in the same .glade sounds like a big startup hit for the embedded case.
Comment 101 Matthias Clasen 2007-06-10 13:02:59 UTC
Hmm, good point. 

Is there any argument for having signal_autoconnect() as explicit api, Johan ?
It sounds nice to me to implicitly do the autoconnect when the parser finishes, 
and then only do the signals that were set up during the current parse. 

Would that work ?
Comment 102 Johan (not receiving bugmail) Dahlin 2007-06-11 13:21:10 UTC
(In reply to comment #101)
> Hmm, good point. 
> 
> Is there any argument for having signal_autoconnect() as explicit api, Johan ?
> It sounds nice to me to implicitly do the autoconnect when the parser finishes, 
> and then only do the signals that were set up during the current parse. 
> 
> Would that work ?
> 

It's easier to change signal_autoconnect to behave in the following way:
Connect all knowns signals free them afterwards, that would make signal_autoconnect() "destructive", you won't get the same behavior the second time you call it, that's probably not a big deal, since you normally want to connect a signal only once, as Kalle pointed out. But it would solve the problem he pointed out and make the following work:

  add_from_string(xml)
  add_from_string(additional_xml)
  signal_autoconnect()
  add_from_string(some_more_xml)
  signal_autoconnect()

In this example the first call to signal_autoconncet() would connect all the signals in the xml and additional_xml definitions and the second call only the ones found in some_more_xml.

That seems like the expected behavior, right?

Comment 103 Johan (not receiving bugmail) Dahlin 2007-06-11 14:47:05 UTC
Created attachment 89758 [details] [review]
v10: all in one

This fixes the signal_autoconnect issue in the way mentioned in comment 102, it also adds a test for this.
Comment 104 Matthias Clasen 2007-06-11 15:55:38 UTC
In what sense is it all-in-one ? it seems to miss at least gtkbuildable.[hc] ?
Comment 105 Johan (not receiving bugmail) Dahlin 2007-06-11 16:01:21 UTC
(In reply to comment #69)
> Small note on this patch: maybe it's better to move GtkBuilderPrivate *priv to
> the GtkBuilder structure and call GTK_BUILDER_GET_PRIVATE only once in the
> gtk_builder_init function, just like most of the widgets in gtk do.
> 

There seems to be different, I basically copied what gtkbutton.c did.
Perhaps it's just because the GtkButton structure is already filled that they didn't put the priv inside the structure.
Comment 106 Johan (not receiving bugmail) Dahlin 2007-06-11 16:17:05 UTC
(In reply to comment #104)
> In what sense is it all-in-one ? it seems to miss at least gtkbuildable.[hc] ?
> 

They are present, however towards the end of the diff, svn diff does not sort the output by filename.
Comment 107 Matthias Clasen 2007-06-11 17:20:58 UTC
Wrt. to priv pointers, for gtkbutton.c there was indeed no room for a priv pointer, but for a new structure, we should add one.
Comment 108 Matthias Clasen 2007-06-11 19:10:12 UTC
Small stylistic thing:

+  g_return_val_if_fail (GTK_IS_BUILDABLE (buildable), NULL);
+  g_return_val_if_fail (GTK_IS_BUILDER (builder), NULL);
+  g_return_val_if_fail (childname != NULL, NULL);
+
+  iface = GTK_BUILDABLE_GET_IFACE (buildable);
+
+  g_return_val_if_fail (iface != NULL, NULL);

since you already check GTK_IS_BUILDABLE, the iface != NULL check
seems redundant. We don't do that in other places in GTK+ that involve
interfaces.
Comment 109 Matthias Clasen 2007-06-11 19:13:38 UTC
gtk_buildable_custom_tag_start and
gtk_buildable_custom_tag_end handle this inconsistently. the start method has

+  g_return_val_if_fail (iface->custom_tag_start != NULL, FALSE);

the end method just silently does nothing.
Comment 110 Johan (not receiving bugmail) Dahlin 2007-06-11 19:18:18 UTC
(In reply to comment #109)
> gtk_buildable_custom_tag_start and
> gtk_buildable_custom_tag_end handle this inconsistently. the start method has
> 
> +  g_return_val_if_fail (iface->custom_tag_start != NULL, FALSE);
> 
> the end method just silently does nothing.
> 

They are actually different, a start should always be implemented, end doesn't need to since, freeing the data structure in custom_finished is also a valid use case. SizeGroup does this for example.

(In reply to comment #108)
> Small stylistic thing:
> 
> +  g_return_val_if_fail (GTK_IS_BUILDABLE (buildable), NULL);
> +  g_return_val_if_fail (GTK_IS_BUILDER (builder), NULL);
> +  g_return_val_if_fail (childname != NULL, NULL);
> +
> +  iface = GTK_BUILDABLE_GET_IFACE (buildable);
> +
> +  g_return_val_if_fail (iface != NULL, NULL);
> 
> since you already check GTK_IS_BUILDABLE, the iface != NULL check
> seems redundant. We don't do that in other places in GTK+ that involve
> interfaces.

Good point, this has been fixed. I also found out that I missed g_return_if_fail in a couple of public APIs.

(In reply to comment #107)
> Wrt. to priv pointers, for gtkbutton.c there was indeed no room for a priv
> pointer, but for a new structure, we should add one.
> 

Right, I updated this, it's actually much nicer.

Comment 111 Matthias Clasen 2007-06-11 19:22:49 UTC
Wrt. to signal autoconnection:

> That seems like the expected behavior, right?

Yes, sounds fine. I don't think the destructive nature of autoconnect
is a big deal, it just needs to be documented clearly.
Comment 112 Johan (not receiving bugmail) Dahlin 2007-06-11 20:06:33 UTC
Created attachment 89769 [details] [review]
v11: all in one

Sorted patch this time, gtk/gtkbuild* are in the beginning.
Changes:
* Make sure that the buildertest runs without leaks, free the list of construct only properties, free the domain, unref all builders & free all lists in buildertest.
* Rename root element from glade-interface to interface
* Remove g_return_if_fail(iface) calls inside GtkBuildable interface
* Implement signal_autoconnect using signal_autoconnect_full to avoid code duplication
* Add private structure to GtkBuilder
* Remove reference to Gazpacho DTD in example
Comment 113 Johan (not receiving bugmail) Dahlin 2007-06-12 00:51:07 UTC
Created attachment 89782 [details] [review]
v12: all in one

Changes:
* Rewrote most docstrings, cross references, examples. Still missing a complete DTD/RelaxNG definition + description of the XML format.
* ensure gtypes of flags/enum functions are correct
* Rename signal_autoconnect* -> connect_signals*
* Avoid an g_strdup() of the translation domain.
Comment 114 Matthias Clasen 2007-06-12 05:17:29 UTC
Thanks, starting to look good; although the documentation needs a lot more work.

What is the status of comment 68 ? That still needs doing, right ?

Error handling in general needs a careful look. It looks like there are a
lot of places where you simply use g_warning() for things which should better
be passed back up.


Cosmetics:

- Please use GTK_PARAM_READWRITE instead of G_PARAM_READWRITE

- GTK+ codingstyle asks for parameters to be aligned in functions like

static void
+gtk_builder_get_parameters (GtkBuilder *builder,
+                            GType object_type,
+                            const gchar *object_name,
+                            GSList *properties,
+                            GArray **parameters,
+                            GArray **construct_parameters)



+          g_print ("Unknown property: %s.%s\n",
+                   g_type_name (object_type), prop->name);

You probably want to use g_warning here, like the rest of the function.


+      if (gtk_debug_flags & GTK_DEBUG_BUILDER)
+        {
+          gchar *str = g_strdup_value_contents ((const GValue*)&param->value);
+          g_print ("set %s: %s = %s\n", info->id, param->name, str);
+          g_free (str);
+        }

Please include debug sections like this in 
#ifdef G_ENABLE_DEBUG
#endif


+  if (builder->priv->delayed_properties)
+    {
+      g_hash_table_foreach (builder->priv->delayed_properties,
+                            (GHFunc)apply_delayed_properties, builder);
+    }

GTK+ coding style calls for no {} here.


 * Returns: 1 on success, 0 if an error occurred

Please make gtk_builder_add_from_file/string return gboolean


+  if (tmp_error != NULL) {

Misplaced {


+  *list = g_slist_append (*list, object);

Might want to do the usual prepend/reverse thing here.


+void
+gtk_builder_set_translation_domain (GtkBuilder   *builder,
+                                    const gchar  *domain)

I wonder if we want to do the same set_translation_domain/set_translate_func
thing we do for action groups. Probably not.


+  if (!g_module_close (module))
+    g_warning ("failed to close GModule.");

That warning seems pointless; we don't check the return value of
any other module_close call in GTK+


+      else if (!strcmp (names[i], "translatable"))

I prefer "== 0" over "!" for string comparisons


+  g_assert (object != NULL);
+  g_assert (G_IS_OBJECT (object));

The first assertion is redundant, since G_IS_OBJECT checks for NULL


+#ifdef GTK_ENABLE_DEBUG

I believe this needs to be G_ENABLE_DEBUG


+        data->subparser->start = element_name;

Do you need a strdup() here ?


+    g_warning ("Invalid type specified: `%s'", type);

We could add a macro for this, similar to GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID





Comment 115 Matthias Clasen 2007-06-12 05:21:03 UTC
I see 3 major things that still need work:

- documentation
- error handling
- reference handling (comment 68)
Comment 116 Johan (not receiving bugmail) Dahlin 2007-06-12 10:11:11 UTC
(In reply to comment #115)
> I see 3 major things that still need work:

Agreed;

> 
> - documentation

This ncluding XML spec,
I could probably use some help/direction here, I'm quite blinded by the API by this point.

> - error handling

GError-ify most g_warnings inside gtkbuilderparser.c

> - reference handling (comment 68)

Right, still needs to be done.
Windows and the normal Widget hierarchy are pretty much working here afaik, the problem is GObjects and objects referencing them. 

I'd also like to add this;

- pixbuf and path manipulation, eg, load pixbufs from this path(s), similar to icon theme, can probably just copy a chunk of code from there.
Comment 117 Johan (not receiving bugmail) Dahlin 2007-06-12 15:16:21 UTC
(In reply to comment #114)
> Thanks, starting to look good; although the documentation needs a lot more
> work.
> 
> What is the status of comment 68 ? That still needs doing, right ?
> 
> Error handling in general needs a careful look. It looks like there are a
> lot of places where you simply use g_warning() for things which should better
> be passed back up.

These two items are on the TODO list, see comment 116

> Cosmetics:

>  * Returns: 1 on success, 0 if an error occurred
> 
> Please make gtk_builder_add_from_file/string return gboolean

I changed this to allow the functions to return a id which could be used to unmerge/remove the ui in the future. I will change the docstring to say that a positive value is considered success.

> +void
> +gtk_builder_set_translation_domain (GtkBuilder   *builder,
> +                                    const gchar  *domain)
> 
> I wonder if we want to do the same set_translation_domain/set_translate_func
> thing we do for action groups. Probably not.

I'm not so sure about the use case for set_translate_func, but it's possible to add this later on if someone asks for it.

> +        data->subparser->start = element_name;
> 
> Do you need a strdup() here ?

Not really, the start element name is only used during the lifetime of the GMarkupContext and is not exported, so it should be fine.

Comment 118 Johan (not receiving bugmail) Dahlin 2007-06-12 15:20:19 UTC
Created attachment 89815 [details] [review]
v13: all in one

Changes:
* !strmp() -> strcmp() == 0
* indentation, if/for {}, some print -> warnings, remove asserts
* Add G_ENABLE_DEBUG around GTK_DEBUG_BUILDER sections
* Small add_from_string/file documentation changes
* GTK_BUILDER_WARN_INVALID_CHILD_TYPE macro
Comment 119 Johan (not receiving bugmail) Dahlin 2007-06-12 20:02:23 UTC
Created attachment 89830 [details] [review]
v14: all in one

Changes:
* Refactored parser into a few more functions.
* Much improved parser error handling, including tests
Comment 120 Johan (not receiving bugmail) Dahlin 2007-06-14 14:31:54 UTC
Created attachment 89950 [details] [review]
v15: all in one

Changes:
* Move builderdemo into gtk-demo
* Add user_data to connect_signals after suggestion by Christian Perch
* Add swapped to <signal> tag
* Remove after arugment in GtkSignalConnectFunc
* Fix typos in documentation and enum
* Remove noop buildable implementation for cell renderer
Comment 121 Johan (not receiving bugmail) Dahlin 2007-06-15 17:55:12 UTC
Commited to trunk!

Sending       ChangeLog
Sending       demos/gtk-demo/Makefile.am
Adding    demos/gtk-demo/builder.c
Adding    demos/gtk-demo/demo.ui
Sending       docs/reference/gtk/gtk-docs.sgml
Sending       docs/reference/gtk/gtk-sections.txt
Sending       docs/reference/gtk/gtk.types
Adding    docs/reference/gtk/tmpl/gtkbuildable.sgml
Adding    docs/reference/gtk/tmpl/gtkbuilder.sgml
Sending       gtk/Makefile.am
Sending       gtk/gtk.h
Sending       gtk/gtk.symbols
Sending       gtk/gtkaction.c
Sending       gtk/gtkactiongroup.c
Adding    gtk/gtkbuildable.c
Adding    gtk/gtkbuildable.h
Adding    gtk/gtkbuilder.c
Adding    gtk/gtkbuilder.h
Adding    gtk/gtkbuilderparser.c
Adding    gtk/gtkbuilderprivate.h
Sending       gtk/gtkcelllayout.c
Sending       gtk/gtkcelllayout.h
Sending       gtk/gtkcellview.c
Sending       gtk/gtkcolorseldialog.c
Sending       gtk/gtkcombobox.c
Sending       gtk/gtkcomboboxentry.c
Sending       gtk/gtkcontainer.c
Sending       gtk/gtkdebug.h
Sending       gtk/gtkdialog.c
Sending       gtk/gtkentrycompletion.c
Sending       gtk/gtkexpander.c
Sending       gtk/gtkfontsel.c
Sending       gtk/gtkframe.c
Sending       gtk/gtkiconview.c
Sending       gtk/gtkliststore.c
Sending       gtk/gtkmain.c
Sending       gtk/gtknotebook.c
Sending       gtk/gtksizegroup.c
Sending       gtk/gtktreestore.c
Sending       gtk/gtktreeview.c
Sending       gtk/gtktreeviewcolumn.c
Sending       gtk/gtkuimanager.c
Sending       gtk/gtkwidget.c
Sending       gtk/gtkwindow.c
Sending       tests
Sending       tests/Makefile.am
Adding    tests/buildertest.c
Transmitting data ..............................................
Committed revision 18141.

Thanks everybody who helped reviewing!