]> Pileus Git - ~andy/gtk/blob - gtk/gtksizegroup.c
stylecontext: Do invalidation on first resize container
[~andy/gtk] / gtk / gtksizegroup.c
1 /* GTK - The GIMP Toolkit
2  * gtksizegroup.c: 
3  * Copyright (C) 2001 Red Hat Software
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
17  */
18
19 #include "config.h"
20 #include <string.h>
21
22 #include "gtkbuildable.h"
23 #include "gtkcontainer.h"
24 #include "gtkintl.h"
25 #include "gtktypebuiltins.h"
26 #include "gtkprivate.h"
27 #include "gtksizegroup-private.h"
28 #include "gtksizerequestcacheprivate.h"
29 #include "gtkwidgetprivate.h"
30 #include "gtkcontainerprivate.h"
31
32
33 /**
34  * SECTION:gtksizegroup
35  * @Short_description: Grouping widgets so they request the same size
36  * @Title: GtkSizeGroup
37  *
38  * #GtkSizeGroup provides a mechanism for grouping a number of widgets
39  * together so they all request the same amount of space.  This is
40  * typically useful when you want a column of widgets to have the same
41  * size, but you can't use a #GtkGrid widget.
42  *
43  * In detail, the size requested for each widget in a #GtkSizeGroup is
44  * the maximum of the sizes that would have been requested for each
45  * widget in the size group if they were not in the size group. The mode
46  * of the size group (see gtk_size_group_set_mode()) determines whether
47  * this applies to the horizontal size, the vertical size, or both sizes.
48  *
49  * Note that size groups only affect the amount of space requested, not
50  * the size that the widgets finally receive. If you want the widgets in
51  * a #GtkSizeGroup to actually be the same size, you need to pack them in
52  * such a way that they get the size they request and not more. For
53  * example, if you are packing your widgets into a table, you would not
54  * include the %GTK_FILL flag.
55  *
56  * #GtkSizeGroup objects are referenced by each widget in the size group,
57  * so once you have added all widgets to a #GtkSizeGroup, you can drop
58  * the initial reference to the size group with g_object_unref(). If the
59  * widgets in the size group are subsequently destroyed, then they will
60  * be removed from the size group and drop their references on the size
61  * group; when all widgets have been removed, the size group will be
62  * freed.
63  *
64  * Widgets can be part of multiple size groups; GTK+ will compute the
65  * horizontal size of a widget from the horizontal requisition of all
66  * widgets that can be reached from the widget by a chain of size groups
67  * of type %GTK_SIZE_GROUP_HORIZONTAL or %GTK_SIZE_GROUP_BOTH, and the
68  * vertical size from the vertical requisition of all widgets that can be
69  * reached from the widget by a chain of size groups of type
70  * %GTK_SIZE_GROUP_VERTICAL or %GTK_SIZE_GROUP_BOTH.
71  *
72  * Note that only non-contextual sizes of every widget are ever consulted
73  * by size groups (since size groups have no knowledge of what size a widget
74  * will be allocated in one dimension, it cannot derive how much height
75  * a widget will receive for a given width). When grouping widgets that
76  * trade height for width in mode %GTK_SIZE_GROUP_VERTICAL or %GTK_SIZE_GROUP_BOTH:
77  * the height for the minimum width will be the requested height for all
78  * widgets in the group. The same is of course true when horizontally grouping
79  * width for height widgets.
80  *
81  * Widgets that trade height-for-width should set a reasonably large minimum width
82  * by way of #GtkLabel:width-chars for instance. Widgets with static sizes as well
83  * as widgets that grow (such as ellipsizing text) need no such considerations.
84  *
85  * <refsect2 id="GtkSizeGroup-BUILDER-UI">
86  * <title>GtkSizeGroup as GtkBuildable</title>
87  * <para>
88  * Size groups can be specified in a UI definition by placing an
89  * &lt;object&gt; element with <literal>class="GtkSizeGroup"</literal>
90  * somewhere in the UI definition. The widgets that belong to the
91  * size group are specified by a &lt;widgets&gt; element that may
92  * contain multiple &lt;widget&gt; elements, one for each member
93  * of the size group. The name attribute gives the id of the widget.
94  *
95  * <example>
96  * <title>A UI definition fragment with GtkSizeGroup</title>
97  * <programlisting><![CDATA[
98  * <object class="GtkSizeGroup">
99  *   <property name="mode">GTK_SIZE_GROUP_HORIZONTAL</property>
100  *   <widgets>
101  *     <widget name="radio1"/>
102  *     <widget name="radio2"/>
103  *   </widgets>
104  * </object>
105  * ]]></programlisting>
106  * </example>
107  * </para>
108  * </refsect2>
109  */
110
111
112 struct _GtkSizeGroupPrivate
113 {
114   GSList         *widgets;
115
116   guint8          mode;
117
118   guint           ignore_hidden : 1;
119 };
120
121 enum {
122   PROP_0,
123   PROP_MODE,
124   PROP_IGNORE_HIDDEN
125 };
126
127 static void gtk_size_group_set_property (GObject      *object,
128                                          guint         prop_id,
129                                          const GValue *value,
130                                          GParamSpec   *pspec);
131 static void gtk_size_group_get_property (GObject      *object,
132                                          guint         prop_id,
133                                          GValue       *value,
134                                          GParamSpec   *pspec);
135
136 /* GtkBuildable */
137 static void gtk_size_group_buildable_init (GtkBuildableIface *iface);
138 static gboolean gtk_size_group_buildable_custom_tag_start (GtkBuildable  *buildable,
139                                                            GtkBuilder    *builder,
140                                                            GObject       *child,
141                                                            const gchar   *tagname,
142                                                            GMarkupParser *parser,
143                                                            gpointer      *data);
144 static void gtk_size_group_buildable_custom_finished (GtkBuildable  *buildable,
145                                                       GtkBuilder    *builder,
146                                                       GObject       *child,
147                                                       const gchar   *tagname,
148                                                       gpointer       user_data);
149
150 G_STATIC_ASSERT (GTK_SIZE_GROUP_HORIZONTAL == (1 << GTK_ORIENTATION_HORIZONTAL));
151 G_STATIC_ASSERT (GTK_SIZE_GROUP_VERTICAL == (1 << GTK_ORIENTATION_VERTICAL));
152 G_STATIC_ASSERT (GTK_SIZE_GROUP_BOTH == (GTK_SIZE_GROUP_HORIZONTAL | GTK_SIZE_GROUP_VERTICAL));
153
154 static void
155 add_widget_to_closure (GHashTable     *widgets,
156                        GHashTable     *groups,
157                        GtkWidget      *widget,
158                        GtkOrientation  orientation)
159 {
160   GSList *tmp_groups, *tmp_widgets;
161   gboolean hidden;
162
163   if (g_hash_table_lookup (widgets, widget))
164     return;
165
166   g_hash_table_add (widgets, widget);
167   hidden = !gtk_widget_is_visible (widget);
168
169   for (tmp_groups = _gtk_widget_get_sizegroups (widget); tmp_groups; tmp_groups = tmp_groups->next)
170     {
171       GtkSizeGroup        *tmp_group = tmp_groups->data;
172       GtkSizeGroupPrivate *tmp_priv  = tmp_group->priv;
173
174       if (g_hash_table_lookup (groups, tmp_group))
175         continue;
176
177       if (tmp_priv->ignore_hidden && hidden)
178         continue;
179
180       if (!(tmp_priv->mode & (1 << orientation)))
181         continue;
182
183       g_hash_table_add (groups, tmp_group);
184
185       for (tmp_widgets = tmp_priv->widgets; tmp_widgets; tmp_widgets = tmp_widgets->next)
186         add_widget_to_closure (widgets, groups, tmp_widgets->data, orientation);
187     }
188 }
189
190 GHashTable *
191 _gtk_size_group_get_widget_peers (GtkWidget      *for_widget,
192                                   GtkOrientation  orientation)
193 {
194   GHashTable *widgets, *groups;
195
196   widgets = g_hash_table_new (g_direct_hash, g_direct_equal);
197   groups = g_hash_table_new (g_direct_hash, g_direct_equal);
198
199   add_widget_to_closure (widgets, groups, for_widget, orientation);
200
201   g_hash_table_unref (groups);
202
203   return widgets;
204 }
205                                      
206 static void
207 real_queue_resize (GtkWidget          *widget,
208                    GtkQueueResizeFlags flags)
209 {
210   GtkWidget *container;
211
212   _gtk_widget_set_alloc_needed (widget, TRUE);
213   _gtk_size_request_cache_clear (_gtk_widget_peek_request_cache (widget));
214
215   container = gtk_widget_get_parent (widget);
216   if (!container &&
217       gtk_widget_is_toplevel (widget) && GTK_IS_CONTAINER (widget))
218     container = widget;
219
220   if (container)
221     {
222       if (flags & GTK_QUEUE_RESIZE_INVALIDATE_ONLY)
223         _gtk_container_resize_invalidate (GTK_CONTAINER (container));
224       else
225         _gtk_container_queue_resize (GTK_CONTAINER (container));
226     }
227 }
228
229 static void
230 queue_resize_on_widget (GtkWidget          *widget,
231                         gboolean            check_siblings,
232                         GtkQueueResizeFlags flags)
233 {
234   GtkWidget *parent = widget;
235
236   while (parent)
237     {
238       GSList *widget_groups;
239       GHashTable *widgets;
240       GHashTableIter iter;
241       gpointer current;
242       
243       if (widget == parent && !check_siblings)
244         {
245           real_queue_resize (widget, flags);
246           parent = gtk_widget_get_parent (parent);
247           continue;
248         }
249       
250       widget_groups = _gtk_widget_get_sizegroups (parent);
251       if (!widget_groups)
252         {
253           if (widget == parent)
254             real_queue_resize (widget, flags);
255
256           parent = gtk_widget_get_parent (parent);
257           continue;
258         }
259
260       widgets = _gtk_size_group_get_widget_peers (parent, GTK_ORIENTATION_HORIZONTAL);
261
262       g_hash_table_iter_init (&iter, widgets);
263       while (g_hash_table_iter_next (&iter, &current, NULL))
264         {
265           if (current == parent)
266             {
267               if (widget == parent)
268                 real_queue_resize (parent, flags);
269             }
270           else if (current == widget)
271             {
272               g_warning ("A container and its child are part of this SizeGroup");
273             }
274           else
275             queue_resize_on_widget (current, FALSE, flags);
276         }
277       
278       g_hash_table_destroy (widgets);
279       
280       widgets = _gtk_size_group_get_widget_peers (parent, GTK_ORIENTATION_VERTICAL);
281
282       g_hash_table_iter_init (&iter, widgets);
283       while (g_hash_table_iter_next (&iter, &current, NULL))
284         {
285           if (current == parent)
286             {
287               if (widget == parent)
288                 real_queue_resize (parent, flags);
289             }
290           else if (current == widget)
291             {
292               g_warning ("A container and its child are part of this SizeGroup");
293             }
294           else
295             queue_resize_on_widget (current, FALSE, flags);
296         }
297       
298       g_hash_table_destroy (widgets);
299
300       parent = gtk_widget_get_parent (parent);
301     }
302 }
303
304 static void
305 queue_resize_on_group (GtkSizeGroup       *size_group)
306 {
307   GtkSizeGroupPrivate *priv = size_group->priv;
308
309   if (priv->widgets)
310     queue_resize_on_widget (priv->widgets->data, TRUE, 0);
311 }
312
313 static void
314 gtk_size_group_class_init (GtkSizeGroupClass *klass)
315 {
316   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
317
318   gobject_class->set_property = gtk_size_group_set_property;
319   gobject_class->get_property = gtk_size_group_get_property;
320   
321   g_object_class_install_property (gobject_class,
322                                    PROP_MODE,
323                                    g_param_spec_enum ("mode",
324                                                       P_("Mode"),
325                                                       P_("The directions in which the size group affects the requested sizes"
326                                                         " of its component widgets"),
327                                                       GTK_TYPE_SIZE_GROUP_MODE,
328                                                       GTK_SIZE_GROUP_HORIZONTAL,                                                      GTK_PARAM_READWRITE));
329
330   /**
331    * GtkSizeGroup:ignore-hidden:
332    *
333    * If %TRUE, unmapped widgets are ignored when determining 
334    * the size of the group.
335    *
336    * Since: 2.8
337    */
338   g_object_class_install_property (gobject_class,
339                                    PROP_IGNORE_HIDDEN,
340                                    g_param_spec_boolean ("ignore-hidden",
341                                                          P_("Ignore hidden"),
342                                                          P_("If TRUE, unmapped widgets are ignored "
343                                                             "when determining the size of the group"),
344                                                          FALSE,
345                                                          GTK_PARAM_READWRITE));
346
347   g_type_class_add_private (klass, sizeof (GtkSizeGroupPrivate));
348 }
349
350 static void
351 gtk_size_group_init (GtkSizeGroup *size_group)
352 {
353   GtkSizeGroupPrivate *priv;
354
355   size_group->priv = G_TYPE_INSTANCE_GET_PRIVATE (size_group,
356                                                   GTK_TYPE_SIZE_GROUP,
357                                                   GtkSizeGroupPrivate);
358   priv = size_group->priv;
359
360   priv->widgets = NULL;
361   priv->mode = GTK_SIZE_GROUP_HORIZONTAL;
362   priv->ignore_hidden = FALSE;
363 }
364
365 static void
366 gtk_size_group_buildable_init (GtkBuildableIface *iface)
367 {
368   iface->custom_tag_start = gtk_size_group_buildable_custom_tag_start;
369   iface->custom_finished = gtk_size_group_buildable_custom_finished;
370 }
371
372 G_DEFINE_TYPE_WITH_CODE (GtkSizeGroup, gtk_size_group, G_TYPE_OBJECT,
373                          G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
374                                                 gtk_size_group_buildable_init))
375
376 static void
377 gtk_size_group_set_property (GObject      *object,
378                              guint         prop_id,
379                              const GValue *value,
380                              GParamSpec   *pspec)
381 {
382   GtkSizeGroup *size_group = GTK_SIZE_GROUP (object);
383
384   switch (prop_id)
385     {
386     case PROP_MODE:
387       gtk_size_group_set_mode (size_group, g_value_get_enum (value));
388       break;
389     case PROP_IGNORE_HIDDEN:
390       gtk_size_group_set_ignore_hidden (size_group, g_value_get_boolean (value));
391       break;
392     default:
393       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
394       break;
395     }
396 }
397
398 static void
399 gtk_size_group_get_property (GObject      *object,
400                              guint         prop_id,
401                              GValue       *value,
402                              GParamSpec   *pspec)
403 {
404   GtkSizeGroup *size_group = GTK_SIZE_GROUP (object);
405   GtkSizeGroupPrivate *priv = size_group->priv;
406
407   switch (prop_id)
408     {
409     case PROP_MODE:
410       g_value_set_enum (value, priv->mode);
411       break;
412     case PROP_IGNORE_HIDDEN:
413       g_value_set_boolean (value, priv->ignore_hidden);
414       break;
415     default:
416       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
417       break;
418     }
419 }
420
421 /**
422  * gtk_size_group_new:
423  * @mode: the mode for the new size group.
424  * 
425  * Create a new #GtkSizeGroup.
426  
427  * Return value: a newly created #GtkSizeGroup
428  **/
429 GtkSizeGroup *
430 gtk_size_group_new (GtkSizeGroupMode mode)
431 {
432   GtkSizeGroup *size_group = g_object_new (GTK_TYPE_SIZE_GROUP, NULL);
433   GtkSizeGroupPrivate *priv = size_group->priv;
434
435   priv->mode = mode;
436
437   return size_group;
438 }
439
440 /**
441  * gtk_size_group_set_mode:
442  * @size_group: a #GtkSizeGroup
443  * @mode: the mode to set for the size group.
444  * 
445  * Sets the #GtkSizeGroupMode of the size group. The mode of the size
446  * group determines whether the widgets in the size group should
447  * all have the same horizontal requisition (%GTK_SIZE_GROUP_HORIZONTAL)
448  * all have the same vertical requisition (%GTK_SIZE_GROUP_VERTICAL),
449  * or should all have the same requisition in both directions
450  * (%GTK_SIZE_GROUP_BOTH).
451  **/
452 void
453 gtk_size_group_set_mode (GtkSizeGroup     *size_group,
454                          GtkSizeGroupMode  mode)
455 {
456   GtkSizeGroupPrivate *priv;
457
458   g_return_if_fail (GTK_IS_SIZE_GROUP (size_group));
459
460   priv = size_group->priv;
461
462   if (priv->mode != mode)
463     {
464       if (priv->mode != GTK_SIZE_GROUP_NONE)
465         queue_resize_on_group (size_group);
466       priv->mode = mode;
467       if (priv->mode != GTK_SIZE_GROUP_NONE)
468         queue_resize_on_group (size_group);
469
470       g_object_notify (G_OBJECT (size_group), "mode");
471     }
472 }
473
474 /**
475  * gtk_size_group_get_mode:
476  * @size_group: a #GtkSizeGroup
477  * 
478  * Gets the current mode of the size group. See gtk_size_group_set_mode().
479  * 
480  * Return value: the current mode of the size group.
481  **/
482 GtkSizeGroupMode
483 gtk_size_group_get_mode (GtkSizeGroup *size_group)
484 {
485   g_return_val_if_fail (GTK_IS_SIZE_GROUP (size_group), GTK_SIZE_GROUP_BOTH);
486
487   return size_group->priv->mode;
488 }
489
490 /**
491  * gtk_size_group_set_ignore_hidden:
492  * @size_group: a #GtkSizeGroup
493  * @ignore_hidden: whether unmapped widgets should be ignored
494  *   when calculating the size
495  * 
496  * Sets whether unmapped widgets should be ignored when
497  * calculating the size.
498  *
499  * Since: 2.8 
500  */
501 void
502 gtk_size_group_set_ignore_hidden (GtkSizeGroup *size_group,
503                                   gboolean      ignore_hidden)
504 {
505   GtkSizeGroupPrivate *priv;
506
507   g_return_if_fail (GTK_IS_SIZE_GROUP (size_group));
508
509   priv = size_group->priv;
510
511   ignore_hidden = ignore_hidden != FALSE;
512
513   if (priv->ignore_hidden != ignore_hidden)
514     {
515       priv->ignore_hidden = ignore_hidden;
516
517       g_object_notify (G_OBJECT (size_group), "ignore-hidden");
518     }
519 }
520
521 /**
522  * gtk_size_group_get_ignore_hidden:
523  * @size_group: a #GtkSizeGroup
524  *
525  * Returns if invisible widgets are ignored when calculating the size.
526  *
527  * Returns: %TRUE if invisible widgets are ignored.
528  *
529  * Since: 2.8
530  */
531 gboolean
532 gtk_size_group_get_ignore_hidden (GtkSizeGroup *size_group)
533 {
534   g_return_val_if_fail (GTK_IS_SIZE_GROUP (size_group), FALSE);
535
536   return size_group->priv->ignore_hidden;
537 }
538
539 static void
540 gtk_size_group_widget_destroyed (GtkWidget    *widget,
541                                  GtkSizeGroup *size_group)
542 {
543   gtk_size_group_remove_widget (size_group, widget);
544 }
545
546 /**
547  * gtk_size_group_add_widget:
548  * @size_group: a #GtkSizeGroup
549  * @widget: the #GtkWidget to add
550  * 
551  * Adds a widget to a #GtkSizeGroup. In the future, the requisition
552  * of the widget will be determined as the maximum of its requisition
553  * and the requisition of the other widgets in the size group.
554  * Whether this applies horizontally, vertically, or in both directions
555  * depends on the mode of the size group. See gtk_size_group_set_mode().
556  *
557  * When the widget is destroyed or no longer referenced elsewhere, it will 
558  * be removed from the size group.
559  */
560 void
561 gtk_size_group_add_widget (GtkSizeGroup     *size_group,
562                            GtkWidget        *widget)
563 {
564   GtkSizeGroupPrivate *priv;
565   GSList *groups;
566   
567   g_return_if_fail (GTK_IS_SIZE_GROUP (size_group));
568   g_return_if_fail (GTK_IS_WIDGET (widget));
569
570   priv = size_group->priv;
571
572   groups = _gtk_widget_get_sizegroups (widget);
573
574   if (!g_slist_find (groups, size_group))
575     {
576       _gtk_widget_add_sizegroup (widget, size_group);
577
578       priv->widgets = g_slist_prepend (priv->widgets, widget);
579
580       g_signal_connect (widget, "destroy",
581                         G_CALLBACK (gtk_size_group_widget_destroyed),
582                         size_group);
583
584       g_object_ref (size_group);
585     }
586   
587   queue_resize_on_group (size_group);
588 }
589
590 /**
591  * gtk_size_group_remove_widget:
592  * @size_group: a #GtkSizeGroup
593  * @widget: the #GtkWidget to remove
594  * 
595  * Removes a widget from a #GtkSizeGroup.
596  **/
597 void
598 gtk_size_group_remove_widget (GtkSizeGroup *size_group,
599                               GtkWidget    *widget)
600 {
601   GtkSizeGroupPrivate *priv;
602   
603   g_return_if_fail (GTK_IS_SIZE_GROUP (size_group));
604   g_return_if_fail (GTK_IS_WIDGET (widget));
605
606   priv = size_group->priv;
607
608   g_return_if_fail (g_slist_find (priv->widgets, widget));
609
610   g_signal_handlers_disconnect_by_func (widget,
611                                         gtk_size_group_widget_destroyed,
612                                         size_group);
613   
614   _gtk_widget_remove_sizegroup (widget, size_group);
615
616   priv->widgets = g_slist_remove (priv->widgets, widget);
617   queue_resize_on_group (size_group);
618   gtk_widget_queue_resize (widget);
619
620   g_object_unref (size_group);
621 }
622
623 /**
624  * gtk_size_group_get_widgets:
625  * @size_group: a #GtkSizeGroup
626  * 
627  * Returns the list of widgets associated with @size_group.
628  *
629  * Return value:  (element-type GtkWidget) (transfer none): a #GSList of
630  *   widgets. The list is owned by GTK+ and should not be modified.
631  *
632  * Since: 2.10
633  **/
634 GSList *
635 gtk_size_group_get_widgets (GtkSizeGroup *size_group)
636 {
637   return size_group->priv->widgets;
638 }
639
640 /**
641  * _gtk_size_group_queue_resize:
642  * @widget: a #GtkWidget
643  * 
644  * Queue a resize on a widget, and on all other widgets grouped with this widget.
645  **/
646 void
647 _gtk_size_group_queue_resize (GtkWidget           *widget,
648                               GtkQueueResizeFlags  flags)
649 {
650   queue_resize_on_widget (widget, TRUE, flags);
651 }
652
653 typedef struct {
654   GObject *object;
655   GSList *items;
656 } GSListSubParserData;
657
658 static void
659 size_group_start_element (GMarkupParseContext *context,
660                           const gchar         *element_name,
661                           const gchar        **names,
662                           const gchar        **values,
663                           gpointer            user_data,
664                           GError            **error)
665 {
666   guint i;
667   GSListSubParserData *data = (GSListSubParserData*)user_data;
668
669   if (strcmp (element_name, "widget") == 0)
670     for (i = 0; names[i]; i++)
671       if (strcmp (names[i], "name") == 0)
672         data->items = g_slist_prepend (data->items, g_strdup (values[i]));
673   else if (strcmp (element_name, "widgets") == 0)
674     return;
675   else
676     g_warning ("Unsupported type tag for GtkSizeGroup: %s\n",
677                element_name);
678
679 }
680
681 static const GMarkupParser size_group_parser =
682   {
683     size_group_start_element
684   };
685
686 static gboolean
687 gtk_size_group_buildable_custom_tag_start (GtkBuildable  *buildable,
688                                            GtkBuilder    *builder,
689                                            GObject       *child,
690                                            const gchar   *tagname,
691                                            GMarkupParser *parser,
692                                            gpointer      *data)
693 {
694   GSListSubParserData *parser_data;
695
696   if (child)
697     return FALSE;
698
699   if (strcmp (tagname, "widgets") == 0)
700     {
701       parser_data = g_slice_new0 (GSListSubParserData);
702       parser_data->items = NULL;
703       parser_data->object = G_OBJECT (buildable);
704
705       *parser = size_group_parser;
706       *data = parser_data;
707       return TRUE;
708     }
709
710   return FALSE;
711 }
712
713 static void
714 gtk_size_group_buildable_custom_finished (GtkBuildable  *buildable,
715                                           GtkBuilder    *builder,
716                                           GObject       *child,
717                                           const gchar   *tagname,
718                                           gpointer       user_data)
719 {
720   GSList *l;
721   GSListSubParserData *data;
722   GObject *object;
723
724   if (strcmp (tagname, "widgets"))
725     return;
726   
727   data = (GSListSubParserData*)user_data;
728   data->items = g_slist_reverse (data->items);
729
730   for (l = data->items; l; l = l->next)
731     {
732       object = gtk_builder_get_object (builder, l->data);
733       if (!object)
734         {
735           g_warning ("Unknown object %s specified in sizegroup %s",
736                      (const gchar*)l->data,
737                      gtk_buildable_get_name (GTK_BUILDABLE (data->object)));
738           continue;
739         }
740       gtk_size_group_add_widget (GTK_SIZE_GROUP (data->object),
741                                  GTK_WIDGET (object));
742       g_free (l->data);
743     }
744   g_slist_free (data->items);
745   g_slice_free (GSListSubParserData, data);
746 }