]> Pileus Git - ~andy/gtk/blob - gtk/gtksizegroup.c
5a686a2d4ee3274307c67dc1b5ce287a5a3193d9
[~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, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20
21 #include "config.h"
22 #include <string.h>
23
24 #include "gtkbuildable.h"
25 #include "gtkcontainer.h"
26 #include "gtkintl.h"
27 #include "gtktypebuiltins.h"
28 #include "gtkprivate.h"
29 #include "gtksizegroup-private.h"
30 #include "gtkwidgetprivate.h"
31
32 struct _GtkSizeGroupPrivate
33 {
34   GtkRequisition  minimum_req;
35   GtkRequisition  natural_req;
36
37   GSList         *widgets;
38
39   guint8          mode;
40
41   guint           have_width    : 1;
42   guint           have_height   : 1;
43   guint           ignore_hidden : 1;
44   guint           visited       : 1;
45 };
46
47 enum {
48   PROP_0,
49   PROP_MODE,
50   PROP_IGNORE_HIDDEN
51 };
52
53 static void gtk_size_group_set_property (GObject      *object,
54                                          guint         prop_id,
55                                          const GValue *value,
56                                          GParamSpec   *pspec);
57 static void gtk_size_group_get_property (GObject      *object,
58                                          guint         prop_id,
59                                          GValue       *value,
60                                          GParamSpec   *pspec);
61
62 static void add_group_to_closure  (GtkSizeGroup      *group,
63                                    GtkSizeGroupMode   mode,
64                                    GSList           **groups,
65                                    GSList           **widgets);
66 static void add_widget_to_closure (GtkWidget         *widget,
67                                    GtkSizeGroupMode   mode,
68                                    GSList           **groups,
69                                    GSList           **widgets);
70
71 /* GtkBuildable */
72 static void gtk_size_group_buildable_init (GtkBuildableIface *iface);
73 static gboolean gtk_size_group_buildable_custom_tag_start (GtkBuildable  *buildable,
74                                                            GtkBuilder    *builder,
75                                                            GObject       *child,
76                                                            const gchar   *tagname,
77                                                            GMarkupParser *parser,
78                                                            gpointer      *data);
79 static void gtk_size_group_buildable_custom_finished (GtkBuildable  *buildable,
80                                                       GtkBuilder    *builder,
81                                                       GObject       *child,
82                                                       const gchar   *tagname,
83                                                       gpointer       user_data);
84
85 static void
86 mark_group_unvisited (GtkSizeGroup *group)
87 {
88   group->priv->visited = FALSE;
89 }
90
91 static void
92 mark_widget_unvisited (GtkWidget *widget)
93 {
94   _gtk_widget_set_sizegroup_visited (widget, FALSE);
95 }
96
97 static void
98 add_group_to_closure (GtkSizeGroup    *group,
99                       GtkSizeGroupMode mode,
100                       GSList         **groups,
101                       GSList         **widgets)
102 {
103   GtkSizeGroupPrivate *priv = group->priv;
104   GSList *tmp_widgets;
105   
106   *groups = g_slist_prepend (*groups, group);
107   priv->visited = TRUE;
108
109   tmp_widgets = priv->widgets;
110   while (tmp_widgets)
111     {
112       GtkWidget *tmp_widget = tmp_widgets->data;
113       
114       if (!_gtk_widget_get_sizegroup_visited (tmp_widget))
115         add_widget_to_closure (tmp_widget, mode, groups, widgets);
116       
117       tmp_widgets = tmp_widgets->next;
118     }
119 }
120
121 static void
122 add_widget_to_closure (GtkWidget       *widget,
123                        GtkSizeGroupMode mode,
124                        GSList         **groups,
125                        GSList         **widgets)
126 {
127   GSList *tmp_groups;
128
129   *widgets = g_slist_prepend (*widgets, widget);
130   _gtk_widget_set_sizegroup_visited (widget, TRUE);
131
132   tmp_groups = _gtk_widget_get_sizegroups (widget);
133   while (tmp_groups)
134     {
135       GtkSizeGroup        *tmp_group = tmp_groups->data;
136       GtkSizeGroupPrivate *tmp_priv  = tmp_group->priv;
137
138       if ((tmp_priv->mode == GTK_SIZE_GROUP_BOTH || tmp_priv->mode == mode) &&
139           !tmp_group->priv->visited)
140         add_group_to_closure (tmp_group, mode, groups, widgets);
141
142       tmp_groups = tmp_groups->next;
143     }
144 }
145
146 static void
147 real_queue_resize (GtkWidget          *widget,
148                    GtkQueueResizeFlags flags)
149 {
150   GtkWidget *container;
151
152   _gtk_widget_set_alloc_needed (widget, TRUE);
153   _gtk_widget_set_width_request_needed (widget, TRUE);
154   _gtk_widget_set_height_request_needed (widget, TRUE);
155
156   container = gtk_widget_get_parent (widget);
157   if (!container &&
158       gtk_widget_is_toplevel (widget) && GTK_IS_CONTAINER (widget))
159     container = widget;
160
161   if (container)
162     {
163       if (flags & GTK_QUEUE_RESIZE_INVALIDATE_ONLY)
164         _gtk_container_resize_invalidate (GTK_CONTAINER (container));
165       else
166         _gtk_container_queue_resize (GTK_CONTAINER (container));
167     }
168 }
169
170 static void
171 reset_group_sizes (GSList *groups)
172 {
173   GSList *tmp_list = groups;
174   while (tmp_list)
175     {
176       GtkSizeGroup *tmp_group = tmp_list->data;
177       GtkSizeGroupPrivate *tmp_priv = tmp_group->priv;
178
179       tmp_priv->have_width = FALSE;
180       tmp_priv->have_height = FALSE;
181
182       tmp_list = tmp_list->next;
183     }
184 }
185
186 static void
187 queue_resize_on_widget (GtkWidget          *widget,
188                         gboolean            check_siblings,
189                         GtkQueueResizeFlags flags)
190 {
191   GtkWidget *parent = widget;
192   GSList *tmp_list;
193
194   while (parent)
195     {
196       GSList *widget_groups;
197       GSList *groups;
198       GSList *widgets;
199       
200       if (widget == parent && !check_siblings)
201         {
202           real_queue_resize (widget, flags);
203           parent = gtk_widget_get_parent (parent);
204           continue;
205         }
206       
207       widget_groups = _gtk_widget_get_sizegroups (parent);
208       if (!widget_groups)
209         {
210           if (widget == parent)
211             real_queue_resize (widget, flags);
212
213           parent = gtk_widget_get_parent (parent);
214           continue;
215         }
216
217       groups = NULL;
218       widgets = NULL;
219           
220       add_widget_to_closure (parent, GTK_SIZE_GROUP_HORIZONTAL, &groups, &widgets);
221       g_slist_foreach (widgets, (GFunc)mark_widget_unvisited, NULL);
222       g_slist_foreach (groups, (GFunc)mark_group_unvisited, NULL);
223
224       reset_group_sizes (groups);
225               
226       tmp_list = widgets;
227       while (tmp_list)
228         {
229           if (tmp_list->data == parent)
230             {
231               if (widget == parent)
232                 real_queue_resize (parent, flags);
233             }
234           else if (tmp_list->data == widget)
235             {
236               g_warning ("A container and its child are part of this SizeGroup");
237             }
238           else
239             queue_resize_on_widget (tmp_list->data, FALSE, flags);
240
241           tmp_list = tmp_list->next;
242         }
243       
244       g_slist_free (widgets);
245       g_slist_free (groups);
246               
247       groups = NULL;
248       widgets = NULL;
249               
250       add_widget_to_closure (parent, GTK_SIZE_GROUP_VERTICAL, &groups, &widgets);
251       g_slist_foreach (widgets, (GFunc)mark_widget_unvisited, NULL);
252       g_slist_foreach (groups, (GFunc)mark_group_unvisited, NULL);
253
254       reset_group_sizes (groups);
255               
256       tmp_list = widgets;
257       while (tmp_list)
258         {
259           if (tmp_list->data == parent)
260             {
261               if (widget == parent)
262                 real_queue_resize (parent, flags);
263             }
264           else if (tmp_list->data == widget)
265             {
266               g_warning ("A container and its child are part of this SizeGroup");
267             }
268           else
269             queue_resize_on_widget (tmp_list->data, FALSE, flags);
270
271           tmp_list = tmp_list->next;
272         }
273       
274       g_slist_free (widgets);
275       g_slist_free (groups);
276
277       parent = gtk_widget_get_parent (parent);
278     }
279 }
280
281 static void
282 queue_resize_on_group (GtkSizeGroup       *size_group)
283 {
284   GtkSizeGroupPrivate *priv = size_group->priv;
285
286   if (priv->widgets)
287     queue_resize_on_widget (priv->widgets->data, TRUE, 0);
288 }
289
290 static void
291 gtk_size_group_class_init (GtkSizeGroupClass *klass)
292 {
293   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
294
295   gobject_class->set_property = gtk_size_group_set_property;
296   gobject_class->get_property = gtk_size_group_get_property;
297   
298   g_object_class_install_property (gobject_class,
299                                    PROP_MODE,
300                                    g_param_spec_enum ("mode",
301                                                       P_("Mode"),
302                                                       P_("The directions in which the size group affects the requested sizes"
303                                                         " of its component widgets"),
304                                                       GTK_TYPE_SIZE_GROUP_MODE,
305                                                       GTK_SIZE_GROUP_HORIZONTAL,                                                      GTK_PARAM_READWRITE));
306
307   /**
308    * GtkSizeGroup:ignore-hidden:
309    *
310    * If %TRUE, unmapped widgets are ignored when determining 
311    * the size of the group.
312    *
313    * Since: 2.8
314    */
315   g_object_class_install_property (gobject_class,
316                                    PROP_IGNORE_HIDDEN,
317                                    g_param_spec_boolean ("ignore-hidden",
318                                                          P_("Ignore hidden"),
319                                                          P_("If TRUE, unmapped widgets are ignored "
320                                                             "when determining the size of the group"),
321                                                          FALSE,
322                                                          GTK_PARAM_READWRITE));
323
324   g_type_class_add_private (klass, sizeof (GtkSizeGroupPrivate));
325 }
326
327 static void
328 gtk_size_group_init (GtkSizeGroup *size_group)
329 {
330   GtkSizeGroupPrivate *priv;
331
332   size_group->priv = G_TYPE_INSTANCE_GET_PRIVATE (size_group,
333                                                   GTK_TYPE_SIZE_GROUP,
334                                                   GtkSizeGroupPrivate);
335   priv = size_group->priv;
336
337   priv->widgets = NULL;
338   priv->mode = GTK_SIZE_GROUP_HORIZONTAL;
339   priv->have_width = 0;
340   priv->have_height = 0;
341   priv->ignore_hidden = 0;
342   priv->visited  = FALSE;
343 }
344
345 static void
346 gtk_size_group_buildable_init (GtkBuildableIface *iface)
347 {
348   iface->custom_tag_start = gtk_size_group_buildable_custom_tag_start;
349   iface->custom_finished = gtk_size_group_buildable_custom_finished;
350 }
351
352 G_DEFINE_TYPE_WITH_CODE (GtkSizeGroup, gtk_size_group, G_TYPE_OBJECT,
353                          G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
354                                                 gtk_size_group_buildable_init))
355
356 static void
357 gtk_size_group_set_property (GObject      *object,
358                              guint         prop_id,
359                              const GValue *value,
360                              GParamSpec   *pspec)
361 {
362   GtkSizeGroup *size_group = GTK_SIZE_GROUP (object);
363
364   switch (prop_id)
365     {
366     case PROP_MODE:
367       gtk_size_group_set_mode (size_group, g_value_get_enum (value));
368       break;
369     case PROP_IGNORE_HIDDEN:
370       gtk_size_group_set_ignore_hidden (size_group, g_value_get_boolean (value));
371       break;
372     default:
373       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
374       break;
375     }
376 }
377
378 static void
379 gtk_size_group_get_property (GObject      *object,
380                              guint         prop_id,
381                              GValue       *value,
382                              GParamSpec   *pspec)
383 {
384   GtkSizeGroup *size_group = GTK_SIZE_GROUP (object);
385   GtkSizeGroupPrivate *priv = size_group->priv;
386
387   switch (prop_id)
388     {
389     case PROP_MODE:
390       g_value_set_enum (value, priv->mode);
391       break;
392     case PROP_IGNORE_HIDDEN:
393       g_value_set_boolean (value, priv->ignore_hidden);
394       break;
395     default:
396       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
397       break;
398     }
399 }
400
401 /**
402  * gtk_size_group_new:
403  * @mode: the mode for the new size group.
404  * 
405  * Create a new #GtkSizeGroup.
406  
407  * Return value: a newly created #GtkSizeGroup
408  **/
409 GtkSizeGroup *
410 gtk_size_group_new (GtkSizeGroupMode mode)
411 {
412   GtkSizeGroup *size_group = g_object_new (GTK_TYPE_SIZE_GROUP, NULL);
413   GtkSizeGroupPrivate *priv = size_group->priv;
414
415   priv->mode = mode;
416
417   return size_group;
418 }
419
420 /**
421  * gtk_size_group_set_mode:
422  * @size_group: a #GtkSizeGroup
423  * @mode: the mode to set for the size group.
424  * 
425  * Sets the #GtkSizeGroupMode of the size group. The mode of the size
426  * group determines whether the widgets in the size group should
427  * all have the same horizontal requisition (%GTK_SIZE_GROUP_HORIZONTAL)
428  * all have the same vertical requisition (%GTK_SIZE_GROUP_VERTICAL),
429  * or should all have the same requisition in both directions
430  * (%GTK_SIZE_GROUP_BOTH).
431  **/
432 void
433 gtk_size_group_set_mode (GtkSizeGroup     *size_group,
434                          GtkSizeGroupMode  mode)
435 {
436   GtkSizeGroupPrivate *priv;
437
438   g_return_if_fail (GTK_IS_SIZE_GROUP (size_group));
439
440   priv = size_group->priv;
441
442   if (priv->mode != mode)
443     {
444       if (priv->mode != GTK_SIZE_GROUP_NONE)
445         queue_resize_on_group (size_group);
446       priv->mode = mode;
447       if (priv->mode != GTK_SIZE_GROUP_NONE)
448         queue_resize_on_group (size_group);
449
450       g_object_notify (G_OBJECT (size_group), "mode");
451     }
452 }
453
454 /**
455  * gtk_size_group_get_mode:
456  * @size_group: a #GtkSizeGroup
457  * 
458  * Gets the current mode of the size group. See gtk_size_group_set_mode().
459  * 
460  * Return value: the current mode of the size group.
461  **/
462 GtkSizeGroupMode
463 gtk_size_group_get_mode (GtkSizeGroup *size_group)
464 {
465   g_return_val_if_fail (GTK_IS_SIZE_GROUP (size_group), GTK_SIZE_GROUP_BOTH);
466
467   return size_group->priv->mode;
468 }
469
470 /**
471  * gtk_size_group_set_ignore_hidden:
472  * @size_group: a #GtkSizeGroup
473  * @ignore_hidden: whether unmapped widgets should be ignored
474  *   when calculating the size
475  * 
476  * Sets whether unmapped widgets should be ignored when
477  * calculating the size.
478  *
479  * Since: 2.8 
480  */
481 void
482 gtk_size_group_set_ignore_hidden (GtkSizeGroup *size_group,
483                                   gboolean      ignore_hidden)
484 {
485   GtkSizeGroupPrivate *priv;
486
487   g_return_if_fail (GTK_IS_SIZE_GROUP (size_group));
488
489   priv = size_group->priv;
490
491   ignore_hidden = ignore_hidden != FALSE;
492
493   if (priv->ignore_hidden != ignore_hidden)
494     {
495       priv->ignore_hidden = ignore_hidden;
496
497       g_object_notify (G_OBJECT (size_group), "ignore-hidden");
498     }
499 }
500
501 /**
502  * gtk_size_group_get_ignore_hidden:
503  * @size_group: a #GtkSizeGroup
504  *
505  * Returns if invisible widgets are ignored when calculating the size.
506  *
507  * Returns: %TRUE if invisible widgets are ignored.
508  *
509  * Since: 2.8
510  */
511 gboolean
512 gtk_size_group_get_ignore_hidden (GtkSizeGroup *size_group)
513 {
514   g_return_val_if_fail (GTK_IS_SIZE_GROUP (size_group), FALSE);
515
516   return size_group->priv->ignore_hidden;
517 }
518
519 static void
520 gtk_size_group_widget_destroyed (GtkWidget    *widget,
521                                  GtkSizeGroup *size_group)
522 {
523   gtk_size_group_remove_widget (size_group, widget);
524 }
525
526 /**
527  * gtk_size_group_add_widget:
528  * @size_group: a #GtkSizeGroup
529  * @widget: the #GtkWidget to add
530  * 
531  * Adds a widget to a #GtkSizeGroup. In the future, the requisition
532  * of the widget will be determined as the maximum of its requisition
533  * and the requisition of the other widgets in the size group.
534  * Whether this applies horizontally, vertically, or in both directions
535  * depends on the mode of the size group. See gtk_size_group_set_mode().
536  *
537  * When the widget is destroyed or no longer referenced elsewhere, it will 
538  * be removed from the size group.
539  */
540 void
541 gtk_size_group_add_widget (GtkSizeGroup     *size_group,
542                            GtkWidget        *widget)
543 {
544   GtkSizeGroupPrivate *priv;
545   GSList *groups;
546   
547   g_return_if_fail (GTK_IS_SIZE_GROUP (size_group));
548   g_return_if_fail (GTK_IS_WIDGET (widget));
549
550   priv = size_group->priv;
551
552   groups = _gtk_widget_get_sizegroups (widget);
553
554   if (!g_slist_find (groups, size_group))
555     {
556       _gtk_widget_add_sizegroup (widget, size_group);
557
558       priv->widgets = g_slist_prepend (priv->widgets, widget);
559
560       g_signal_connect (widget, "destroy",
561                         G_CALLBACK (gtk_size_group_widget_destroyed),
562                         size_group);
563
564       g_object_ref (size_group);
565     }
566   
567   queue_resize_on_group (size_group);
568 }
569
570 /**
571  * gtk_size_group_remove_widget:
572  * @size_group: a #GtkSizeGroup
573  * @widget: the #GtkWidget to remove
574  * 
575  * Removes a widget from a #GtkSizeGroup.
576  **/
577 void
578 gtk_size_group_remove_widget (GtkSizeGroup *size_group,
579                               GtkWidget    *widget)
580 {
581   GtkSizeGroupPrivate *priv;
582   
583   g_return_if_fail (GTK_IS_SIZE_GROUP (size_group));
584   g_return_if_fail (GTK_IS_WIDGET (widget));
585
586   priv = size_group->priv;
587
588   g_return_if_fail (g_slist_find (priv->widgets, widget));
589
590   g_signal_handlers_disconnect_by_func (widget,
591                                         gtk_size_group_widget_destroyed,
592                                         size_group);
593   
594   _gtk_widget_remove_sizegroup (widget, size_group);
595
596   priv->widgets = g_slist_remove (priv->widgets, widget);
597   queue_resize_on_group (size_group);
598   gtk_widget_queue_resize (widget);
599
600   g_object_unref (size_group);
601 }
602
603 /**
604  * gtk_size_group_get_widgets:
605  * @size_group: a #GtkSizeGroup
606  * 
607  * Returns the list of widgets associated with @size_group.
608  *
609  * Return value:  (element-type GtkWidget) (transfer none): a #GSList of
610  *   widgets. The list is owned by GTK+ and should not be modified.
611  *
612  * Since: 2.10
613  **/
614 GSList *
615 gtk_size_group_get_widgets (GtkSizeGroup *size_group)
616 {
617   return size_group->priv->widgets;
618 }
619
620 static void
621 compute_dimension (GtkWidget        *widget,
622                    GtkSizeGroupMode  mode,
623                    gint             *minimum, /* in-out */
624                    gint             *natural) /* in-out */
625 {
626   GSList *widgets = NULL;
627   GSList *groups = NULL;
628   GSList *tmp_list;
629   gint    min_result = 0, nat_result = 0;
630
631   add_widget_to_closure (widget, mode, &groups, &widgets);
632   g_slist_foreach (widgets, (GFunc)mark_widget_unvisited, NULL);
633   g_slist_foreach (groups, (GFunc)mark_group_unvisited, NULL);
634
635   g_slist_foreach (widgets, (GFunc)g_object_ref, NULL);
636   
637   if (!groups)
638     {
639       min_result = *minimum;
640       nat_result = *natural;
641     }
642   else
643     {
644       GtkSizeGroup *group = groups->data;
645       GtkSizeGroupPrivate *priv = group->priv;
646
647       if (mode == GTK_SIZE_GROUP_HORIZONTAL && priv->have_width)
648         {
649           min_result = priv->minimum_req.width;
650           nat_result = priv->natural_req.width;
651         }
652       else if (mode == GTK_SIZE_GROUP_VERTICAL && priv->have_height)
653         {
654           min_result = priv->minimum_req.height;
655           nat_result = priv->natural_req.height;
656         }
657       else
658         {
659           tmp_list = widgets;
660           while (tmp_list)
661             {
662               GtkWidget *tmp_widget = tmp_list->data;
663               gint min_dimension, nat_dimension;
664
665               if (tmp_widget == widget)
666                 {
667                   min_dimension = *minimum;
668                   nat_dimension = *natural;
669                 }
670               else
671                 {
672                   if (mode == GTK_SIZE_GROUP_HORIZONTAL)
673                     gtk_widget_get_preferred_width (tmp_widget, &min_dimension, &nat_dimension);
674                   else
675                     gtk_widget_get_preferred_height (tmp_widget, &min_dimension, &nat_dimension);
676                 }
677
678               if (gtk_widget_get_mapped (tmp_widget) || !priv->ignore_hidden)
679                 {
680                   min_result = MAX (min_result, min_dimension);
681                   nat_result = MAX (nat_result, nat_dimension);
682                 }
683
684               tmp_list = tmp_list->next;
685             }
686
687           tmp_list = groups;
688           while (tmp_list)
689             {
690               GtkSizeGroup *tmp_group = tmp_list->data;
691               GtkSizeGroupPrivate *tmp_priv = tmp_group->priv;
692
693               if (mode == GTK_SIZE_GROUP_HORIZONTAL)
694                 {
695                   tmp_priv->have_width = TRUE;
696                   tmp_priv->minimum_req.width = min_result;
697                   tmp_priv->natural_req.width = nat_result;
698                 }
699               else
700                 {
701                   tmp_priv->have_height = TRUE;
702                   tmp_priv->minimum_req.height = min_result;
703                   tmp_priv->natural_req.height = nat_result;
704                 }
705               
706               tmp_list = tmp_list->next;
707             }
708         }
709     }
710
711   g_slist_foreach (widgets, (GFunc)g_object_unref, NULL);
712
713   g_slist_free (widgets);
714   g_slist_free (groups);
715
716   *minimum = min_result;
717   *natural = nat_result;
718 }
719
720 /**
721  * _gtk_size_group_bump_requisition:
722  * @widget: a #GtkWidget
723  * @mode: either %GTK_SIZE_GROUP_HORIZONTAL or %GTK_SIZE_GROUP_VERTICAL, depending
724  *        on the dimension in which to bump the size.
725  * @minimum: a pointer to the widget's minimum size
726  * @natural: a pointer to the widget's natural size
727  *
728  * Refreshes the sizegroup while returning the groups requested
729  * value in the dimension @mode.
730  *
731  * This function is used both to update sizegroup minimum and natural size 
732  * information and widget minimum and natural sizes in multiple passes from 
733  * the size request apis.
734  */
735 void
736 _gtk_size_group_bump_requisition (GtkWidget        *widget,
737                                   GtkSizeGroupMode  mode,
738                                   gint             *minimum,
739                                   gint             *natural)
740 {
741   if (!_gtk_widget_get_sizegroup_bumping (widget))
742     {
743       /* Avoid recursion here */
744       _gtk_widget_set_sizegroup_bumping (widget, TRUE);
745
746       if (_gtk_widget_get_sizegroups (widget))
747         compute_dimension (widget, mode, minimum, natural);
748
749       _gtk_widget_set_sizegroup_bumping (widget, FALSE);
750     }
751 }
752
753
754
755 /**
756  * _gtk_size_group_queue_resize:
757  * @widget: a #GtkWidget
758  * 
759  * Queue a resize on a widget, and on all other widgets grouped with this widget.
760  **/
761 void
762 _gtk_size_group_queue_resize (GtkWidget           *widget,
763                               GtkQueueResizeFlags  flags)
764 {
765   queue_resize_on_widget (widget, TRUE, flags);
766 }
767
768 typedef struct {
769   GObject *object;
770   GSList *items;
771 } GSListSubParserData;
772
773 static void
774 size_group_start_element (GMarkupParseContext *context,
775                           const gchar         *element_name,
776                           const gchar        **names,
777                           const gchar        **values,
778                           gpointer            user_data,
779                           GError            **error)
780 {
781   guint i;
782   GSListSubParserData *data = (GSListSubParserData*)user_data;
783
784   if (strcmp (element_name, "widget") == 0)
785     for (i = 0; names[i]; i++)
786       if (strcmp (names[i], "name") == 0)
787         data->items = g_slist_prepend (data->items, g_strdup (values[i]));
788   else if (strcmp (element_name, "widgets") == 0)
789     return;
790   else
791     g_warning ("Unsupported type tag for GtkSizeGroup: %s\n",
792                element_name);
793
794 }
795
796 static const GMarkupParser size_group_parser =
797   {
798     size_group_start_element
799   };
800
801 static gboolean
802 gtk_size_group_buildable_custom_tag_start (GtkBuildable  *buildable,
803                                            GtkBuilder    *builder,
804                                            GObject       *child,
805                                            const gchar   *tagname,
806                                            GMarkupParser *parser,
807                                            gpointer      *data)
808 {
809   GSListSubParserData *parser_data;
810
811   if (child)
812     return FALSE;
813
814   if (strcmp (tagname, "widgets") == 0)
815     {
816       parser_data = g_slice_new0 (GSListSubParserData);
817       parser_data->items = NULL;
818       parser_data->object = G_OBJECT (buildable);
819
820       *parser = size_group_parser;
821       *data = parser_data;
822       return TRUE;
823     }
824
825   return FALSE;
826 }
827
828 static void
829 gtk_size_group_buildable_custom_finished (GtkBuildable  *buildable,
830                                           GtkBuilder    *builder,
831                                           GObject       *child,
832                                           const gchar   *tagname,
833                                           gpointer       user_data)
834 {
835   GSList *l;
836   GSListSubParserData *data;
837   GObject *object;
838
839   if (strcmp (tagname, "widgets"))
840     return;
841   
842   data = (GSListSubParserData*)user_data;
843   data->items = g_slist_reverse (data->items);
844
845   for (l = data->items; l; l = l->next)
846     {
847       object = gtk_builder_get_object (builder, l->data);
848       if (!object)
849         {
850           g_warning ("Unknown object %s specified in sizegroup %s",
851                      (const gchar*)l->data,
852                      gtk_buildable_get_name (GTK_BUILDABLE (data->object)));
853           continue;
854         }
855       gtk_size_group_add_widget (GTK_SIZE_GROUP (data->object),
856                                  GTK_WIDGET (object));
857       g_free (l->data);
858     }
859   g_slist_free (data->items);
860   g_slice_free (GSListSubParserData, data);
861 }