]> Pileus Git - ~andy/gtk/blob - gtk/gtksizegroup.c
Fixed regression in GtkImage size requests
[~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 #include "gtkcontainer.h"
24 #include "gtkintl.h"
25 #include "gtkprivate.h"
26 #include "gtksizegroup.h"
27 #include "gtkbuildable.h"
28 #include "gtkextendedlayout.h"
29 #include "gtkalias.h"
30
31 #define GTK_SIZE_GROUP_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GTK_TYPE_SIZE_GROUP, GtkSizeGroupPrivate))
32
33 typedef struct _GtkSizeGroupPrivate GtkSizeGroupPrivate;
34
35 struct _GtkSizeGroupPrivate
36 {
37   GtkRequisition natural_size;
38 };
39
40 enum {
41   PROP_0,
42   PROP_MODE,
43   PROP_IGNORE_HIDDEN
44 };
45
46 static void gtk_size_group_set_property (GObject      *object,
47                                          guint         prop_id,
48                                          const GValue *value,
49                                          GParamSpec   *pspec);
50 static void gtk_size_group_get_property (GObject      *object,
51                                          guint         prop_id,
52                                          GValue       *value,
53                                          GParamSpec   *pspec);
54
55 static void add_group_to_closure  (GtkSizeGroup      *group,
56                                    GtkSizeGroupMode   mode,
57                                    GSList           **groups,
58                                    GSList           **widgets);
59 static void add_widget_to_closure (GtkWidget         *widget,
60                                    GtkSizeGroupMode   mode,
61                                    GSList           **groups,
62                                    GSList           **widgets);
63
64 /* GtkBuildable */
65 static void gtk_size_group_buildable_init (GtkBuildableIface *iface);
66 static gboolean gtk_size_group_buildable_custom_tag_start (GtkBuildable  *buildable,
67                                                            GtkBuilder    *builder,
68                                                            GObject       *child,
69                                                            const gchar   *tagname,
70                                                            GMarkupParser *parser,
71                                                            gpointer      *data);
72 static void gtk_size_group_buildable_custom_finished (GtkBuildable  *buildable,
73                                                       GtkBuilder    *builder,
74                                                       GObject       *child,
75                                                       const gchar   *tagname,
76                                                       gpointer       user_data);
77
78 static GQuark size_groups_quark;
79 static const gchar size_groups_tag[] = "gtk-size-groups";
80
81 static GQuark visited_quark;
82 static const gchar visited_tag[] = "gtk-size-group-visited";
83
84 static GSList *
85 get_size_groups (GtkWidget *widget)
86 {
87   return g_object_get_qdata (G_OBJECT (widget), size_groups_quark);
88 }
89
90 static void
91 set_size_groups (GtkWidget *widget,
92                  GSList    *groups)
93 {
94   g_object_set_qdata (G_OBJECT (widget), size_groups_quark, groups);
95 }
96
97 static void
98 mark_visited (gpointer object)
99 {
100   g_object_set_qdata (object, visited_quark, "visited");
101 }
102
103 static void
104 mark_unvisited (gpointer object)
105 {
106   g_object_set_qdata (object, visited_quark, NULL);
107 }
108
109 static gboolean
110 is_visited (gpointer object)
111 {
112   return g_object_get_qdata (object, visited_quark) != NULL;
113 }
114
115 static void
116 add_group_to_closure (GtkSizeGroup    *group,
117                       GtkSizeGroupMode mode,
118                       GSList         **groups,
119                       GSList         **widgets)
120 {
121   GSList *tmp_widgets;
122   
123   *groups = g_slist_prepend (*groups, group);
124   mark_visited (group);
125
126   tmp_widgets = group->widgets;
127   while (tmp_widgets)
128     {
129       GtkWidget *tmp_widget = tmp_widgets->data;
130       
131       if (!is_visited (tmp_widget))
132         add_widget_to_closure (tmp_widget, mode, groups, widgets);
133       
134       tmp_widgets = tmp_widgets->next;
135     }
136 }
137
138 static void
139 add_widget_to_closure (GtkWidget       *widget,
140                        GtkSizeGroupMode mode,
141                        GSList         **groups,
142                        GSList         **widgets)
143 {
144   GSList *tmp_groups;
145
146   *widgets = g_slist_prepend (*widgets, widget);
147   mark_visited (widget);
148
149   tmp_groups = get_size_groups (widget);
150   while (tmp_groups)
151     {
152       GtkSizeGroup *tmp_group = tmp_groups->data;
153       
154       if ((tmp_group->mode == GTK_SIZE_GROUP_BOTH || tmp_group->mode == mode) &&
155           !is_visited (tmp_group))
156         add_group_to_closure (tmp_group, mode, groups, widgets);
157
158       tmp_groups = tmp_groups->next;
159     }
160 }
161
162 static void
163 real_queue_resize (GtkWidget *widget)
164 {
165   GTK_PRIVATE_SET_FLAG (widget, GTK_ALLOC_NEEDED);
166   GTK_PRIVATE_SET_FLAG (widget, GTK_REQUEST_NEEDED);
167   
168   if (widget->parent)
169     _gtk_container_queue_resize (GTK_CONTAINER (widget->parent));
170   else if (gtk_widget_is_toplevel (widget) && GTK_IS_CONTAINER (widget))
171     _gtk_container_queue_resize (GTK_CONTAINER (widget));
172 }
173
174 static void
175 reset_group_sizes (GSList *groups)
176 {
177   GSList *tmp_list = groups;
178   while (tmp_list)
179     {
180       GtkSizeGroup *tmp_group = tmp_list->data;
181
182       tmp_group->have_width = FALSE;
183       tmp_group->have_height = FALSE;
184       
185       tmp_list = tmp_list->next;
186     }
187 }
188
189 static void
190 queue_resize_on_widget (GtkWidget *widget,
191                         gboolean   check_siblings)
192 {
193   GtkWidget *parent = widget;
194   GSList *tmp_list;
195
196   while (parent)
197     {
198       GSList *widget_groups;
199       GSList *groups;
200       GSList *widgets;
201       
202       if (widget == parent && !check_siblings)
203         {
204           real_queue_resize (widget);
205           parent = parent->parent;
206           continue;
207         }
208       
209       widget_groups = get_size_groups (parent);
210       if (!widget_groups)
211         {
212           if (widget == parent)
213             real_queue_resize (widget);
214
215           parent = parent->parent;
216           continue;
217         }
218
219       groups = NULL;
220       widgets = NULL;
221           
222       add_widget_to_closure (parent, GTK_SIZE_GROUP_HORIZONTAL, &groups, &widgets);
223       g_slist_foreach (widgets, (GFunc)mark_unvisited, NULL);
224       g_slist_foreach (groups, (GFunc)mark_unvisited, NULL);
225
226       reset_group_sizes (groups);
227               
228       tmp_list = widgets;
229       while (tmp_list)
230         {
231           if (tmp_list->data == parent)
232             {
233               if (widget == parent)
234                 real_queue_resize (parent);
235             }
236           else if (tmp_list->data == widget)
237             {
238               g_warning ("A container and its child are part of this SizeGroup");
239             }
240           else
241             queue_resize_on_widget (tmp_list->data, FALSE);
242
243           tmp_list = tmp_list->next;
244         }
245       
246       g_slist_free (widgets);
247       g_slist_free (groups);
248               
249       groups = NULL;
250       widgets = NULL;
251               
252       add_widget_to_closure (parent, GTK_SIZE_GROUP_VERTICAL, &groups, &widgets);
253       g_slist_foreach (widgets, (GFunc)mark_unvisited, NULL);
254       g_slist_foreach (groups, (GFunc)mark_unvisited, NULL);
255
256       reset_group_sizes (groups);
257               
258       tmp_list = widgets;
259       while (tmp_list)
260         {
261           if (tmp_list->data == parent)
262             {
263               if (widget == parent)
264                 real_queue_resize (parent);
265             }
266           else if (tmp_list->data == widget)
267             {
268               g_warning ("A container and its child are part of this SizeGroup");
269             }
270           else
271             queue_resize_on_widget (tmp_list->data, FALSE);
272
273           tmp_list = tmp_list->next;
274         }
275       
276       g_slist_free (widgets);
277       g_slist_free (groups);
278       
279       parent = parent->parent;
280     }
281 }
282
283 static void
284 queue_resize_on_group (GtkSizeGroup *size_group)
285 {
286   if (size_group->widgets)
287     queue_resize_on_widget (size_group->widgets->data, TRUE);
288 }
289
290 static void
291 initialize_size_group_quarks (void)
292 {
293   if (!size_groups_quark)
294     {
295       size_groups_quark = g_quark_from_static_string (size_groups_tag);
296       visited_quark = g_quark_from_static_string (visited_tag);
297     }
298 }
299
300 static void
301 gtk_size_group_class_init (GtkSizeGroupClass *klass)
302 {
303   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
304
305   gobject_class->set_property = gtk_size_group_set_property;
306   gobject_class->get_property = gtk_size_group_get_property;
307   
308   g_object_class_install_property (gobject_class,
309                                    PROP_MODE,
310                                    g_param_spec_enum ("mode",
311                                                       P_("Mode"),
312                                                       P_("The directions in which the size group affects the requested sizes"
313                                                         " of its component widgets"),
314                                                       GTK_TYPE_SIZE_GROUP_MODE,
315                                                       GTK_SIZE_GROUP_HORIZONTAL,                                                      GTK_PARAM_READWRITE));
316
317   /**
318    * GtkSizeGroup:ignore-hidden:
319    *
320    * If %TRUE, unmapped widgets are ignored when determining 
321    * the size of the group.
322    *
323    * Since: 2.8
324    */
325   g_object_class_install_property (gobject_class,
326                                    PROP_IGNORE_HIDDEN,
327                                    g_param_spec_boolean ("ignore-hidden",
328                                                          P_("Ignore hidden"),
329                                                          P_("If TRUE, unmapped widgets are ignored "
330                                                             "when determining the size of the group"),
331                                                          FALSE,
332                                                          GTK_PARAM_READWRITE));
333   
334   initialize_size_group_quarks ();
335   g_type_class_add_private (klass, sizeof (GtkSizeGroupPrivate));
336 }
337
338 static void
339 gtk_size_group_init (GtkSizeGroup *size_group)
340 {
341   size_group->widgets = NULL;
342   size_group->mode = GTK_SIZE_GROUP_HORIZONTAL;
343   size_group->have_width = 0;
344   size_group->have_height = 0;
345   size_group->ignore_hidden = 0;
346 }
347
348 static void
349 gtk_size_group_buildable_init (GtkBuildableIface *iface)
350 {
351   iface->custom_tag_start = gtk_size_group_buildable_custom_tag_start;
352   iface->custom_finished = gtk_size_group_buildable_custom_finished;
353 }
354
355 G_DEFINE_TYPE_WITH_CODE (GtkSizeGroup, gtk_size_group, G_TYPE_OBJECT,
356                          G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
357                                                 gtk_size_group_buildable_init))
358
359 static void
360 gtk_size_group_set_property (GObject      *object,
361                              guint         prop_id,
362                              const GValue *value,
363                              GParamSpec   *pspec)
364 {
365   GtkSizeGroup *size_group = GTK_SIZE_GROUP (object);
366
367   switch (prop_id)
368     {
369     case PROP_MODE:
370       gtk_size_group_set_mode (size_group, g_value_get_enum (value));
371       break;
372     case PROP_IGNORE_HIDDEN:
373       gtk_size_group_set_ignore_hidden (size_group, g_value_get_boolean (value));
374       break;
375     default:
376       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
377       break;
378     }
379 }
380
381 static void
382 gtk_size_group_get_property (GObject      *object,
383                              guint         prop_id,
384                              GValue       *value,
385                              GParamSpec   *pspec)
386 {
387   GtkSizeGroup *size_group = GTK_SIZE_GROUP (object);
388
389   switch (prop_id)
390     {
391     case PROP_MODE:
392       g_value_set_enum (value, size_group->mode);
393       break;
394     case PROP_IGNORE_HIDDEN:
395       g_value_set_boolean (value, size_group->ignore_hidden);
396       break;
397     default:
398       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
399       break;
400     }
401 }
402
403 /**
404  * gtk_size_group_new:
405  * @mode: the mode for the new size group.
406  * 
407  * Create a new #GtkSizeGroup.
408  
409  * Return value: a newly created #GtkSizeGroup
410  **/
411 GtkSizeGroup *
412 gtk_size_group_new (GtkSizeGroupMode mode)
413 {
414   GtkSizeGroup *size_group = g_object_new (GTK_TYPE_SIZE_GROUP, NULL);
415
416   size_group->mode = mode;
417
418   return size_group;
419 }
420
421 /**
422  * gtk_size_group_set_mode:
423  * @size_group: a #GtkSizeGroup
424  * @mode: the mode to set for the size group.
425  * 
426  * Sets the #GtkSizeGroupMode of the size group. The mode of the size
427  * group determines whether the widgets in the size group should
428  * all have the same horizontal requisition (%GTK_SIZE_GROUP_MODE_HORIZONTAL)
429  * all have the same vertical requisition (%GTK_SIZE_GROUP_MODE_VERTICAL),
430  * or should all have the same requisition in both directions
431  * (%GTK_SIZE_GROUP_MODE_BOTH).
432  **/
433 void
434 gtk_size_group_set_mode (GtkSizeGroup     *size_group,
435                          GtkSizeGroupMode  mode)
436 {
437   g_return_if_fail (GTK_IS_SIZE_GROUP (size_group));
438
439   if (size_group->mode != mode)
440     {
441       if (size_group->mode != GTK_SIZE_GROUP_NONE)
442         queue_resize_on_group (size_group);
443       size_group->mode = mode;
444       if (size_group->mode != GTK_SIZE_GROUP_NONE)
445         queue_resize_on_group (size_group);
446
447       g_object_notify (G_OBJECT (size_group), "mode");
448     }
449 }
450
451 /**
452  * gtk_size_group_get_mode:
453  * @size_group: a #GtkSizeGroup
454  * 
455  * Gets the current mode of the size group. See gtk_size_group_set_mode().
456  * 
457  * Return value: the current mode of the size group.
458  **/
459 GtkSizeGroupMode
460 gtk_size_group_get_mode (GtkSizeGroup *size_group)
461 {
462   g_return_val_if_fail (GTK_IS_SIZE_GROUP (size_group), GTK_SIZE_GROUP_BOTH);
463
464   return size_group->mode;
465 }
466
467 /**
468  * gtk_size_group_set_ignore_hidden:
469  * @size_group: a #GtkSizeGroup
470  * @ignore_hidden: whether unmapped widgets should be ignored
471  *   when calculating the size
472  * 
473  * Sets whether unmapped widgets should be ignored when
474  * calculating the size.
475  *
476  * Since: 2.8 
477  */
478 void
479 gtk_size_group_set_ignore_hidden (GtkSizeGroup *size_group,
480                                   gboolean      ignore_hidden)
481 {
482   g_return_if_fail (GTK_IS_SIZE_GROUP (size_group));
483   
484   ignore_hidden = ignore_hidden != FALSE;
485
486   if (size_group->ignore_hidden != ignore_hidden)
487     {
488       size_group->ignore_hidden = ignore_hidden;
489
490       g_object_notify (G_OBJECT (size_group), "ignore-hidden");
491     }
492 }
493
494 /**
495  * gtk_size_group_get_ignore_hidden:
496  * @size_group: a #GtkSizeGroup
497  *
498  * Returns if invisible widgets are ignored when calculating the size.
499  *
500  * Returns: %TRUE if invisible widgets are ignored.
501  *
502  * Since: 2.8
503  */
504 gboolean
505 gtk_size_group_get_ignore_hidden (GtkSizeGroup *size_group)
506 {
507   g_return_val_if_fail (GTK_IS_SIZE_GROUP (size_group), FALSE);
508
509   return size_group->ignore_hidden;
510 }
511
512 static void
513 gtk_size_group_widget_destroyed (GtkWidget    *widget,
514                                  GtkSizeGroup *size_group)
515 {
516   gtk_size_group_remove_widget (size_group, widget);
517 }
518
519 /**
520  * gtk_size_group_add_widget:
521  * @size_group: a #GtkSizeGroup
522  * @widget: the #GtkWidget to add
523  * 
524  * Adds a widget to a #GtkSizeGroup. In the future, the requisition
525  * of the widget will be determined as the maximum of its requisition
526  * and the requisition of the other widgets in the size group.
527  * Whether this applies horizontally, vertically, or in both directions
528  * depends on the mode of the size group. See gtk_size_group_set_mode().
529  *
530  * When the widget is destroyed or no longer referenced elsewhere, it will 
531  * be removed from the size group.
532  */
533 void
534 gtk_size_group_add_widget (GtkSizeGroup     *size_group,
535                            GtkWidget        *widget)
536 {
537   GSList *groups;
538   
539   g_return_if_fail (GTK_IS_SIZE_GROUP (size_group));
540   g_return_if_fail (GTK_IS_WIDGET (widget));
541   
542   groups = get_size_groups (widget);
543
544   if (!g_slist_find (groups, size_group))
545     {
546       groups = g_slist_prepend (groups, size_group);
547       set_size_groups (widget, groups);
548
549       size_group->widgets = g_slist_prepend (size_group->widgets, widget);
550
551       g_signal_connect (widget, "destroy",
552                         G_CALLBACK (gtk_size_group_widget_destroyed),
553                         size_group);
554
555       g_object_ref (size_group);
556     }
557   
558   queue_resize_on_group (size_group);
559 }
560
561 /**
562  * gtk_size_group_remove_widget:
563  * @size_group: a #GtkSizeGrup
564  * @widget: the #GtkWidget to remove
565  * 
566  * Removes a widget from a #GtkSizeGroup.
567  **/
568 void
569 gtk_size_group_remove_widget (GtkSizeGroup *size_group,
570                               GtkWidget    *widget)
571 {
572   GSList *groups;
573   
574   g_return_if_fail (GTK_IS_SIZE_GROUP (size_group));
575   g_return_if_fail (GTK_IS_WIDGET (widget));
576   g_return_if_fail (g_slist_find (size_group->widgets, widget));
577
578   g_signal_handlers_disconnect_by_func (widget,
579                                         gtk_size_group_widget_destroyed,
580                                         size_group);
581   
582   groups = get_size_groups (widget);
583   groups = g_slist_remove (groups, size_group);
584   set_size_groups (widget, groups);
585
586   size_group->widgets = g_slist_remove (size_group->widgets, widget);
587   queue_resize_on_group (size_group);
588   gtk_widget_queue_resize (widget);
589
590   g_object_unref (size_group);
591 }
592
593 /**
594  * gtk_size_group_get_widgets:
595  * @size_group: a #GtkSizeGrup
596  * 
597  * Returns the list of widgets associated with @size_group.
598  *
599  * Return value:  (element-type GtkWidget) (transfer none): a #GSList of
600  *   widgets. The list is owned by GTK+ and should not be modified.
601  *
602  * Since: 2.10
603  **/
604 GSList *
605 gtk_size_group_get_widgets (GtkSizeGroup *size_group)
606 {
607   return size_group->widgets;
608 }
609
610 static void
611 get_base_dimensions (GtkWidget        *widget,
612                      GtkSizeGroupMode  mode,
613                      gint             *minimum_size,
614                      gint             *natural_size)
615 {
616   GtkWidgetAuxInfo *aux_info = _gtk_widget_get_aux_info (widget, FALSE);
617
618   if (mode == GTK_SIZE_GROUP_HORIZONTAL)
619     {
620       if (minimum_size)
621         {
622           if (aux_info && aux_info->width > 0)
623             *minimum_size = aux_info->width;
624           else
625             *minimum_size = widget->requisition.width;
626         }
627
628       if (natural_size)
629         {
630           if (aux_info)
631             *natural_size = aux_info->natural_size.width;
632           else
633             *natural_size = widget->requisition.width;
634         }
635     }
636   else
637     {
638       if (minimum_size)
639         {
640           if (aux_info && aux_info->height > 0)
641             *minimum_size = aux_info->height;
642           else
643             *minimum_size = widget->requisition.height;
644         }
645
646       if (natural_size)
647         {
648           if (aux_info)
649             *natural_size = aux_info->natural_size.height;
650           else
651             *natural_size = widget->requisition.height;
652         }
653     }
654 }
655
656 static void
657 do_size_request (GtkWidget *widget)
658 {
659   if (GTK_WIDGET_REQUEST_NEEDED (widget))
660     {
661       GtkWidgetAuxInfo *aux_info = _gtk_widget_get_aux_info (widget, TRUE);
662       GtkRequisition extended_minimum;
663
664       gtk_widget_ensure_style (widget);      
665       GTK_PRIVATE_UNSET_FLAG (widget, GTK_REQUEST_NEEDED);
666
667       /* First, allow client code to; extended classes or signal connections; to 
668        * modify the initial size request. 
669        *
670        * Note here that there is no convention of filling the argument or widget->requisition,
671        * so we have no choice but to fire size request with this pointer.
672        */
673       g_signal_emit_by_name (widget, "size-request", &widget->requisition);
674
675       /* Now get the extended layout minimum and natural size
676        */
677       extended_minimum.width  = 0;
678       extended_minimum.height = 0;
679
680       GTK_EXTENDED_LAYOUT_GET_IFACE
681         (widget)->get_desired_size (GTK_EXTENDED_LAYOUT (widget),
682                                     &extended_minimum,
683                                     &aux_info->natural_size);
684       
685       /* Base the base widget requisition on both the size-requst and the extended layout size
686        */
687       widget->requisition.width  = MAX (widget->requisition.width,  extended_minimum.width);
688       widget->requisition.height = MAX (widget->requisition.height, extended_minimum.height);
689
690       /* Additionally allow a "size-request" to overflow the natural size.
691        */
692       aux_info->natural_size.width  = MAX (aux_info->natural_size.width,  widget->requisition.width);
693       aux_info->natural_size.height = MAX (aux_info->natural_size.height, widget->requisition.height);
694
695       /* Assert that pure extended layout cases return initial minimum sizes smaller or equal
696        * to their possible natural size.
697        *
698        * Note that this only determines the return of gtk_widget_get_desired_size() and caches
699        * the initial hints. Height for width cases will further be addressed in containers
700        * using gtk_extended_layout_get_height_for_width().
701        */
702       g_assert (widget->requisition.width <= aux_info->natural_size.width);
703       g_assert (widget->requisition.height <= aux_info->natural_size.height);
704
705     }
706 }
707
708 static void
709 compute_base_dimensions (GtkWidget        *widget,
710                          GtkSizeGroupMode  mode,
711                          gint             *minimum_size,
712                          gint             *natural_size)
713 {
714   do_size_request (widget);
715   get_base_dimensions (widget, mode, minimum_size, natural_size);
716 }
717
718 static void
719 compute_dimension (GtkWidget        *widget,
720                    GtkSizeGroupMode  mode,
721                    gint             *minimum_size,
722                    gint             *natural_size)
723 {
724   GSList *widgets = NULL;
725   GSList *groups = NULL;
726   GSList *tmp_list;
727
728   add_widget_to_closure (widget, mode, &groups, &widgets);
729
730   g_slist_foreach (widgets, (GFunc)mark_unvisited, NULL);
731   g_slist_foreach (groups, (GFunc)mark_unvisited, NULL);
732   
733   g_slist_foreach (widgets, (GFunc)g_object_ref, NULL);
734   
735   if (!groups)
736     {
737       compute_base_dimensions (widget, mode, minimum_size, natural_size);
738     }
739   else
740     {
741       GtkSizeGroup *group = groups->data;
742       GtkSizeGroupPrivate *priv = GTK_SIZE_GROUP_GET_PRIVATE (group);
743
744       gint result_minimum_size = 0;
745       gint result_natural_size = 0;
746
747       if (mode == GTK_SIZE_GROUP_HORIZONTAL && group->have_width)
748         {
749           result_minimum_size = group->requisition.width;
750           result_natural_size = priv->natural_size.width;
751         }
752       else if (mode == GTK_SIZE_GROUP_VERTICAL && group->have_height)
753         {
754           result_minimum_size = group->requisition.height;
755           result_natural_size = priv->natural_size.height;
756         }
757       else
758         {
759           tmp_list = widgets;
760           while (tmp_list)
761             {
762               GtkWidget *tmp_widget = tmp_list->data;
763
764               gint tmp_widget_minimum_size;
765               gint tmp_widget_natural_size;
766
767               compute_base_dimensions (tmp_widget, mode,
768                                        &tmp_widget_minimum_size,
769                                        &tmp_widget_natural_size);
770
771               if (gtk_widget_get_mapped (tmp_widget) || !group->ignore_hidden)
772                 {
773                   if (result_minimum_size < tmp_widget_minimum_size)
774                     result_minimum_size = tmp_widget_minimum_size;
775                   if (result_natural_size < tmp_widget_natural_size)
776                     result_natural_size = tmp_widget_natural_size;
777                 }
778
779               tmp_list = tmp_list->next;
780             }
781
782           tmp_list = groups;
783           while (tmp_list)
784             {
785               GtkSizeGroup *tmp_group = tmp_list->data;
786               GtkSizeGroupPrivate *tmp_priv = GTK_SIZE_GROUP_GET_PRIVATE (tmp_group);
787
788               if (mode == GTK_SIZE_GROUP_HORIZONTAL)
789                 {
790                   tmp_group->have_width = TRUE;
791                   tmp_group->requisition.width = result_minimum_size;
792                   tmp_priv->natural_size.width = result_natural_size;
793                 }
794               else
795                 {
796                   tmp_group->have_height = TRUE;
797                   tmp_group->requisition.height = result_minimum_size;
798                   tmp_priv->natural_size.height = result_natural_size;
799                 }
800               
801               tmp_list = tmp_list->next;
802             }
803         }
804
805       if (minimum_size)
806         *minimum_size = result_minimum_size;
807       if (natural_size)
808         *natural_size = result_natural_size;
809     }
810
811   g_slist_foreach (widgets, (GFunc)g_object_unref, NULL);
812
813   g_slist_free (widgets);
814   g_slist_free (groups);
815 }
816
817 static void
818 get_dimensions (GtkWidget        *widget,
819                 GtkSizeGroupMode  mode,
820                 gint             *minimum_size,
821                 gint             *natural_size)
822 {
823   GSList *widgets = NULL;
824   GSList *groups = NULL;
825
826   add_widget_to_closure (widget, mode, &groups, &widgets);
827
828   g_slist_foreach (widgets, (GFunc)mark_unvisited, NULL);
829   g_slist_foreach (groups, (GFunc)mark_unvisited, NULL);  
830
831   if (!groups)
832     {
833       get_base_dimensions (widget, mode, minimum_size, natural_size);
834     }
835   else
836     {
837       GtkSizeGroup *group = groups->data;
838       GtkSizeGroupPrivate *priv = GTK_SIZE_GROUP_GET_PRIVATE (group);
839
840       if (mode == GTK_SIZE_GROUP_HORIZONTAL && group->have_width)
841         {
842           if (minimum_size)
843             *minimum_size = group->requisition.width;
844           if (natural_size)
845             *natural_size = priv->natural_size.width;
846         }
847       else if (mode == GTK_SIZE_GROUP_VERTICAL && group->have_height)
848         {
849           if (minimum_size)
850             *minimum_size = group->requisition.height;
851           if (natural_size)
852             *natural_size = priv->natural_size.height;
853         }
854     }
855
856   g_slist_free (widgets);
857   g_slist_free (groups);
858 }
859
860 static void
861 get_fast_size (GtkWidget      *widget,
862                GtkRequisition *minimum_size,
863                GtkRequisition *natural_size)
864 {
865   GtkWidgetAuxInfo *aux_info = _gtk_widget_get_aux_info (widget, FALSE);
866   
867   if (minimum_size)
868     {
869       *minimum_size = widget->requisition;
870   
871       if (aux_info)
872         {
873           if (aux_info->width > 0)
874             minimum_size->width = aux_info->width;
875           if (aux_info->height > 0)
876             minimum_size->height = aux_info->height;
877         }
878     }
879
880   if (natural_size)
881     {
882       if (aux_info)
883         {
884           *natural_size = aux_info->natural_size;
885
886           /* Explicit size request sets the baseline for natural size 
887            * as well as the minimum size 
888            */
889           if (aux_info->width > natural_size->width)
890             natural_size->width = aux_info->width;
891           if (aux_info->height > natural_size->height)
892             natural_size->height = aux_info->height;
893         }
894       else
895         *natural_size = widget->requisition;
896     }
897 }
898
899 /**
900  * _gtk_size_group_get_child_requisition:
901  * @widget: a #GtkWidget
902  * @requisition: location to store computed requisition.
903  * 
904  * Retrieve the "child requisition" of the widget, taking account grouping
905  * of the widget's requisition with other widgets.
906  *
907  * Deprecated: 3.0: Use _gtk_size_group_compute_desired_size() instead
908  **/
909 void
910 _gtk_size_group_get_child_requisition (GtkWidget      *widget,
911                                        GtkRequisition *requisition)
912 {
913   initialize_size_group_quarks ();
914
915   if (requisition)
916     {
917       if (get_size_groups (widget))
918         {
919           get_dimensions (widget, GTK_SIZE_GROUP_HORIZONTAL, &requisition->width, NULL);
920           get_dimensions (widget, GTK_SIZE_GROUP_VERTICAL, &requisition->height, NULL);
921
922           /* Only do the full computation if we actually have size groups */
923         }
924       else
925         get_fast_size (widget, requisition, NULL);
926     }
927 }
928
929 /**
930  * _gtk_size_group_compute_desired_size:
931  * @widget: a #GtkWidget
932  * @minimum_size: location to store computed minimum size
933  * @natural_size: location to store computed natural size
934  * 
935  * Compute the desired size of a widget taking into account grouping of
936  * the widget's requisition with other widgets.
937  **/
938 void
939 _gtk_size_group_compute_desired_size (GtkWidget      *widget,
940                                       GtkRequisition *minimum_size,
941                                       GtkRequisition *natural_size)
942 {
943   initialize_size_group_quarks ();
944
945   if (get_size_groups (widget))
946     {
947       /* Only do the full computation if we actually have size groups */
948
949       compute_dimension (widget, GTK_SIZE_GROUP_HORIZONTAL,
950                          minimum_size ? &minimum_size->width : NULL,
951                          natural_size ? &natural_size->width : NULL);
952       compute_dimension (widget, GTK_SIZE_GROUP_VERTICAL,
953                          minimum_size ? &minimum_size->height : NULL,
954                          natural_size ? &natural_size->height : NULL);
955     }
956   else
957     {
958       do_size_request (widget);
959
960       get_fast_size (widget, minimum_size, natural_size);
961     }
962 }
963
964 /**
965  * _gtk_size_group_queue_resize:
966  * @widget: a #GtkWidget
967  * 
968  * Queue a resize on a widget, and on all other widgets grouped with this widget.
969  **/
970 void
971 _gtk_size_group_queue_resize (GtkWidget *widget)
972 {
973   initialize_size_group_quarks ();
974
975   queue_resize_on_widget (widget, TRUE);
976 }
977
978 typedef struct {
979   GObject *object;
980   GSList *items;
981 } GSListSubParserData;
982
983 static void
984 size_group_start_element (GMarkupParseContext *context,
985                           const gchar         *element_name,
986                           const gchar        **names,
987                           const gchar        **values,
988                           gpointer            user_data,
989                           GError            **error)
990 {
991   guint i;
992   GSListSubParserData *data = (GSListSubParserData*)user_data;
993
994   if (strcmp (element_name, "widget") == 0)
995     for (i = 0; names[i]; i++)
996       if (strcmp (names[i], "name") == 0)
997         data->items = g_slist_prepend (data->items, g_strdup (values[i]));
998   else if (strcmp (element_name, "widgets") == 0)
999     return;
1000   else
1001     g_warning ("Unsupported type tag for GtkSizeGroup: %s\n",
1002                element_name);
1003
1004 }
1005
1006 static const GMarkupParser size_group_parser =
1007   {
1008     size_group_start_element
1009   };
1010
1011 static gboolean
1012 gtk_size_group_buildable_custom_tag_start (GtkBuildable  *buildable,
1013                                            GtkBuilder    *builder,
1014                                            GObject       *child,
1015                                            const gchar   *tagname,
1016                                            GMarkupParser *parser,
1017                                            gpointer      *data)
1018 {
1019   GSListSubParserData *parser_data;
1020
1021   if (child)
1022     return FALSE;
1023
1024   if (strcmp (tagname, "widgets") == 0)
1025     {
1026       parser_data = g_slice_new0 (GSListSubParserData);
1027       parser_data->items = NULL;
1028       parser_data->object = G_OBJECT (buildable);
1029
1030       *parser = size_group_parser;
1031       *data = parser_data;
1032       return TRUE;
1033     }
1034
1035   return FALSE;
1036 }
1037
1038 static void
1039 gtk_size_group_buildable_custom_finished (GtkBuildable  *buildable,
1040                                           GtkBuilder    *builder,
1041                                           GObject       *child,
1042                                           const gchar   *tagname,
1043                                           gpointer       user_data)
1044 {
1045   GSList *l;
1046   GSListSubParserData *data;
1047   GObject *object;
1048
1049   if (strcmp (tagname, "widgets"))
1050     return;
1051   
1052   data = (GSListSubParserData*)user_data;
1053   data->items = g_slist_reverse (data->items);
1054
1055   for (l = data->items; l; l = l->next)
1056     {
1057       object = gtk_builder_get_object (builder, l->data);
1058       if (!object)
1059         {
1060           g_warning ("Unknown object %s specified in sizegroup %s",
1061                      (const gchar*)l->data,
1062                      gtk_buildable_get_name (GTK_BUILDABLE (data->object)));
1063           continue;
1064         }
1065       gtk_size_group_add_widget (GTK_SIZE_GROUP (data->object),
1066                                  GTK_WIDGET (object));
1067       g_free (l->data);
1068     }
1069   g_slist_free (data->items);
1070   g_slice_free (GSListSubParserData, data);
1071 }
1072
1073
1074 #define __GTK_SIZE_GROUP_C__
1075 #include "gtkaliasdef.c"