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