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