]> Pileus Git - ~andy/gtk/blob - gtk/gtksizegroup.c
Use g_quark_from_static_string where possible
[~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_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
227             queue_resize_on_widget (tmp_list->data, FALSE);
228
229           tmp_list = tmp_list->next;
230         }
231       
232       g_slist_free (widgets);
233       g_slist_free (groups);
234               
235       groups = NULL;
236       widgets = NULL;
237               
238       add_widget_to_closure (parent, GTK_SIZE_GROUP_VERTICAL, &groups, &widgets);
239       g_slist_foreach (widgets, (GFunc)mark_unvisited, NULL);
240       g_slist_foreach (groups, (GFunc)mark_unvisited, NULL);
241
242       reset_group_sizes (groups);
243               
244       tmp_list = widgets;
245       while (tmp_list)
246         {
247           if (tmp_list->data == parent)
248             {
249               if (widget == parent)
250                 real_queue_resize (parent);
251             }
252           else
253             queue_resize_on_widget (tmp_list->data, FALSE);
254
255           tmp_list = tmp_list->next;
256         }
257       
258       g_slist_free (widgets);
259       g_slist_free (groups);
260       
261       parent = parent->parent;
262     }
263 }
264
265 static void
266 queue_resize_on_group (GtkSizeGroup *size_group)
267 {
268   if (size_group->widgets)
269     queue_resize_on_widget (size_group->widgets->data, TRUE);
270 }
271
272 static void
273 initialize_size_group_quarks (void)
274 {
275   if (!size_groups_quark)
276     {
277       size_groups_quark = g_quark_from_static_string (size_groups_tag);
278       visited_quark = g_quark_from_static_string (visited_tag);
279     }
280 }
281
282 static void
283 gtk_size_group_class_init (GtkSizeGroupClass *klass)
284 {
285   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
286
287   gobject_class->set_property = gtk_size_group_set_property;
288   gobject_class->get_property = gtk_size_group_get_property;
289   
290   g_object_class_install_property (gobject_class,
291                                    PROP_MODE,
292                                    g_param_spec_enum ("mode",
293                                                       P_("Mode"),
294                                                       P_("The directions in which the size group affects the requested sizes"
295                                                         " of its component widgets"),
296                                                       GTK_TYPE_SIZE_GROUP_MODE,
297                                                       GTK_SIZE_GROUP_HORIZONTAL,                                                      GTK_PARAM_READWRITE));
298
299   /**
300    * GtkSizeGroup:ignore-hidden:
301    *
302    * If %TRUE, unmapped widgets are ignored when determining 
303    * the size of the group.
304    *
305    * Since: 2.8
306    */
307   g_object_class_install_property (gobject_class,
308                                    PROP_IGNORE_HIDDEN,
309                                    g_param_spec_boolean ("ignore-hidden",
310                                                          P_("Ignore hidden"),
311                                                          P_("If TRUE, unmapped widgets are ignored "
312                                                             "when determining the size of the group"),
313                                                          FALSE,
314                                                          GTK_PARAM_READWRITE));
315   
316   initialize_size_group_quarks ();
317 }
318
319 static void
320 gtk_size_group_init (GtkSizeGroup *size_group)
321 {
322   size_group->widgets = NULL;
323   size_group->mode = GTK_SIZE_GROUP_HORIZONTAL;
324   size_group->have_width = 0;
325   size_group->have_height = 0;
326   size_group->ignore_hidden = 0;
327 }
328
329 static void
330 gtk_size_group_buildable_init (GtkBuildableIface *iface)
331 {
332   iface->custom_tag_start = gtk_size_group_buildable_custom_tag_start;
333   iface->custom_finished = gtk_size_group_buildable_custom_finished;
334 }
335
336 G_DEFINE_TYPE_WITH_CODE (GtkSizeGroup, gtk_size_group, G_TYPE_OBJECT,
337                          G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
338                                                 gtk_size_group_buildable_init))
339
340 static void
341 gtk_size_group_set_property (GObject      *object,
342                              guint         prop_id,
343                              const GValue *value,
344                              GParamSpec   *pspec)
345 {
346   GtkSizeGroup *size_group = GTK_SIZE_GROUP (object);
347
348   switch (prop_id)
349     {
350     case PROP_MODE:
351       gtk_size_group_set_mode (size_group, g_value_get_enum (value));
352       break;
353     case PROP_IGNORE_HIDDEN:
354       gtk_size_group_set_ignore_hidden (size_group, g_value_get_boolean (value));
355       break;
356     default:
357       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
358       break;
359     }
360 }
361
362 static void
363 gtk_size_group_get_property (GObject      *object,
364                              guint         prop_id,
365                              GValue       *value,
366                              GParamSpec   *pspec)
367 {
368   GtkSizeGroup *size_group = GTK_SIZE_GROUP (object);
369
370   switch (prop_id)
371     {
372     case PROP_MODE:
373       g_value_set_enum (value, size_group->mode);
374       break;
375     case PROP_IGNORE_HIDDEN:
376       g_value_set_boolean (value, size_group->ignore_hidden);
377       break;
378     default:
379       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
380       break;
381     }
382 }
383
384 /**
385  * gtk_size_group_new:
386  * @mode: the mode for the new size group.
387  * 
388  * Create a new #GtkSizeGroup.
389  
390  * Return value: a newly created #GtkSizeGroup
391  **/
392 GtkSizeGroup *
393 gtk_size_group_new (GtkSizeGroupMode mode)
394 {
395   GtkSizeGroup *size_group = g_object_new (GTK_TYPE_SIZE_GROUP, NULL);
396
397   size_group->mode = mode;
398
399   return size_group;
400 }
401
402 /**
403  * gtk_size_group_set_mode:
404  * @size_group: a #GtkSizeGroup
405  * @mode: the mode to set for the size group.
406  * 
407  * Sets the #GtkSizeGroupMode of the size group. The mode of the size
408  * group determines whether the widgets in the size group should
409  * all have the same horizontal requisition (%GTK_SIZE_GROUP_MODE_HORIZONTAL)
410  * all have the same vertical requisition (%GTK_SIZE_GROUP_MODE_VERTICAL),
411  * or should all have the same requisition in both directions
412  * (%GTK_SIZE_GROUP_MODE_BOTH).
413  **/
414 void
415 gtk_size_group_set_mode (GtkSizeGroup     *size_group,
416                          GtkSizeGroupMode  mode)
417 {
418   g_return_if_fail (GTK_IS_SIZE_GROUP (size_group));
419
420   if (size_group->mode != mode)
421     {
422       if (size_group->mode != GTK_SIZE_GROUP_NONE)
423         queue_resize_on_group (size_group);
424       size_group->mode = mode;
425       if (size_group->mode != GTK_SIZE_GROUP_NONE)
426         queue_resize_on_group (size_group);
427
428       g_object_notify (G_OBJECT (size_group), "mode");
429     }
430 }
431
432 /**
433  * gtk_size_group_get_mode:
434  * @size_group: a #GtkSizeGroup
435  * 
436  * Gets the current mode of the size group. See gtk_size_group_set_mode().
437  * 
438  * Return value: the current mode of the size group.
439  **/
440 GtkSizeGroupMode
441 gtk_size_group_get_mode (GtkSizeGroup *size_group)
442 {
443   g_return_val_if_fail (GTK_IS_SIZE_GROUP (size_group), GTK_SIZE_GROUP_BOTH);
444
445   return size_group->mode;
446 }
447
448 /**
449  * gtk_size_group_set_ignore_hidden:
450  * @size_group: a #GtkSizeGroup
451  * @ignore_hidden: whether unmapped widgets should be ignored
452  *   when calculating the size
453  * 
454  * Sets whether unmapped widgets should be ignored when
455  * calculating the size.
456  *
457  * Since: 2.8 
458  */
459 void
460 gtk_size_group_set_ignore_hidden (GtkSizeGroup *size_group,
461                                   gboolean      ignore_hidden)
462 {
463   g_return_if_fail (GTK_IS_SIZE_GROUP (size_group));
464   
465   ignore_hidden = ignore_hidden != FALSE;
466
467   if (size_group->ignore_hidden != ignore_hidden)
468     {
469       size_group->ignore_hidden = ignore_hidden;
470
471       g_object_notify (G_OBJECT (size_group), "ignore-hidden");
472     }
473 }
474
475 /**
476  * gtk_size_group_get_ignore_hidden:
477  * @size_group: a #GtkSizeGroup
478  *
479  * Returns if invisible widgets are ignored when calculating the size.
480  *
481  * Returns: %TRUE if invisible widgets are ignored.
482  *
483  * Since: 2.8
484  */
485 gboolean
486 gtk_size_group_get_ignore_hidden (GtkSizeGroup *size_group)
487 {
488   g_return_val_if_fail (GTK_IS_SIZE_GROUP (size_group), FALSE);
489
490   return size_group->ignore_hidden;
491 }
492
493 static void
494 gtk_size_group_widget_destroyed (GtkWidget    *widget,
495                                  GtkSizeGroup *size_group)
496 {
497   gtk_size_group_remove_widget (size_group, widget);
498 }
499
500 /**
501  * gtk_size_group_add_widget:
502  * @size_group: a #GtkSizeGroup
503  * @widget: the #GtkWidget to add
504  * 
505  * Adds a widget to a #GtkSizeGroup. In the future, the requisition
506  * of the widget will be determined as the maximum of its requisition
507  * and the requisition of the other widgets in the size group.
508  * Whether this applies horizontally, vertically, or in both directions
509  * depends on the mode of the size group. See gtk_size_group_set_mode().
510  *
511  * When the widget is destroyed or no longer referenced elsewhere, it will 
512  * be removed from the size group.
513  */
514 void
515 gtk_size_group_add_widget (GtkSizeGroup     *size_group,
516                            GtkWidget        *widget)
517 {
518   GSList *groups;
519   
520   g_return_if_fail (GTK_IS_SIZE_GROUP (size_group));
521   g_return_if_fail (GTK_IS_WIDGET (widget));
522   
523   groups = get_size_groups (widget);
524
525   if (!g_slist_find (groups, size_group))
526     {
527       groups = g_slist_prepend (groups, size_group);
528       set_size_groups (widget, groups);
529
530       size_group->widgets = g_slist_prepend (size_group->widgets, widget);
531
532       g_signal_connect (widget, "destroy",
533                         G_CALLBACK (gtk_size_group_widget_destroyed),
534                         size_group);
535
536       g_object_ref (size_group);
537     }
538   
539   queue_resize_on_group (size_group);
540 }
541
542 /**
543  * gtk_size_group_remove_widget:
544  * @size_group: a #GtkSizeGrup
545  * @widget: the #GtkWidget to remove
546  * 
547  * Removes a widget from a #GtkSizeGroup.
548  **/
549 void
550 gtk_size_group_remove_widget (GtkSizeGroup *size_group,
551                               GtkWidget    *widget)
552 {
553   GSList *groups;
554   
555   g_return_if_fail (GTK_IS_SIZE_GROUP (size_group));
556   g_return_if_fail (GTK_IS_WIDGET (widget));
557   g_return_if_fail (g_slist_find (size_group->widgets, widget));
558
559   g_signal_handlers_disconnect_by_func (widget,
560                                         gtk_size_group_widget_destroyed,
561                                         size_group);
562   
563   groups = get_size_groups (widget);
564   groups = g_slist_remove (groups, size_group);
565   set_size_groups (widget, groups);
566
567   size_group->widgets = g_slist_remove (size_group->widgets, widget);
568   queue_resize_on_group (size_group);
569   gtk_widget_queue_resize (widget);
570
571   g_object_unref (size_group);
572 }
573
574 /**
575  * gtk_size_group_get_widgets:
576  * @size_group: a #GtkSizeGrup
577  * 
578  * Returns the list of widgets associated with @size_group.
579  *
580  * Return value: a #GSList of widgets. The list is owned by GTK+ 
581  *   and should not be modified.
582  *
583  * Since: 2.10
584  **/
585 GSList *
586 gtk_size_group_get_widgets (GtkSizeGroup *size_group)
587 {
588   return size_group->widgets;
589 }
590
591 static gint
592 get_base_dimension (GtkWidget        *widget,
593                     GtkSizeGroupMode  mode)
594 {
595   GtkWidgetAuxInfo *aux_info = _gtk_widget_get_aux_info (widget, FALSE);
596
597   if (mode == GTK_SIZE_GROUP_HORIZONTAL)
598     {
599       if (aux_info && aux_info->width > 0)
600         return aux_info->width;
601       else
602         return widget->requisition.width;
603     }
604   else
605     {
606       if (aux_info && aux_info->height > 0)
607         return aux_info->height;
608       else
609         return widget->requisition.height;
610     }
611 }
612
613 static void
614 do_size_request (GtkWidget *widget)
615 {
616   if (GTK_WIDGET_REQUEST_NEEDED (widget))
617     {
618       gtk_widget_ensure_style (widget);      
619       GTK_PRIVATE_UNSET_FLAG (widget, GTK_REQUEST_NEEDED);
620       g_signal_emit_by_name (widget,
621                              "size-request",
622                              &widget->requisition);
623     }
624 }
625
626 static gint
627 compute_base_dimension (GtkWidget        *widget,
628                         GtkSizeGroupMode  mode)
629 {
630   do_size_request (widget);
631
632   return get_base_dimension (widget, mode);
633 }
634
635 static gint
636 compute_dimension (GtkWidget        *widget,
637                    GtkSizeGroupMode  mode)
638 {
639   GSList *widgets = NULL;
640   GSList *groups = NULL;
641   GSList *tmp_list;
642   gint result = 0;
643
644   add_widget_to_closure (widget, mode, &groups, &widgets);
645
646   g_slist_foreach (widgets, (GFunc)mark_unvisited, NULL);
647   g_slist_foreach (groups, (GFunc)mark_unvisited, NULL);
648   
649   g_slist_foreach (widgets, (GFunc)g_object_ref, NULL);
650   
651   if (!groups)
652     {
653       result = compute_base_dimension (widget, mode);
654     }
655   else
656     {
657       GtkSizeGroup *group = groups->data;
658
659       if (mode == GTK_SIZE_GROUP_HORIZONTAL && group->have_width)
660         result = group->requisition.width;
661       else if (mode == GTK_SIZE_GROUP_VERTICAL && group->have_height)
662         result = group->requisition.height;
663       else
664         {
665           tmp_list = widgets;
666           while (tmp_list)
667             {
668               GtkWidget *tmp_widget = tmp_list->data;
669
670               gint dimension = compute_base_dimension (tmp_widget, mode);
671
672               if (GTK_WIDGET_MAPPED (tmp_widget) || !group->ignore_hidden)
673                 {
674                   if (dimension > result)
675                     result = dimension;
676                 }
677
678               tmp_list = tmp_list->next;
679             }
680
681           tmp_list = groups;
682           while (tmp_list)
683             {
684               GtkSizeGroup *tmp_group = tmp_list->data;
685
686               if (mode == GTK_SIZE_GROUP_HORIZONTAL)
687                 {
688                   tmp_group->have_width = TRUE;
689                   tmp_group->requisition.width = result;
690                 }
691               else
692                 {
693                   tmp_group->have_height = TRUE;
694                   tmp_group->requisition.height = result;
695                 }
696               
697               tmp_list = tmp_list->next;
698             }
699         }
700     }
701
702   g_slist_foreach (widgets, (GFunc)g_object_unref, NULL);
703
704   g_slist_free (widgets);
705   g_slist_free (groups);
706
707   return result;
708 }
709
710 static gint
711 get_dimension (GtkWidget        *widget,
712                GtkSizeGroupMode  mode)
713 {
714   GSList *widgets = NULL;
715   GSList *groups = NULL;
716   gint result = 0;
717
718   add_widget_to_closure (widget, mode, &groups, &widgets);
719
720   g_slist_foreach (widgets, (GFunc)mark_unvisited, NULL);
721   g_slist_foreach (groups, (GFunc)mark_unvisited, NULL);  
722
723   if (!groups)
724     {
725       result = get_base_dimension (widget, mode);
726     }
727   else
728     {
729       GtkSizeGroup *group = groups->data;
730
731       if (mode == GTK_SIZE_GROUP_HORIZONTAL && group->have_width)
732         result = group->requisition.width;
733       else if (mode == GTK_SIZE_GROUP_VERTICAL && group->have_height)
734         result = group->requisition.height;
735     }
736
737   g_slist_free (widgets);
738   g_slist_free (groups);
739
740   return result;
741 }
742
743 static void
744 get_fast_child_requisition (GtkWidget      *widget,
745                             GtkRequisition *requisition)
746 {
747   GtkWidgetAuxInfo *aux_info = _gtk_widget_get_aux_info (widget, FALSE);
748   
749   *requisition = widget->requisition;
750   
751   if (aux_info)
752     {
753       if (aux_info->width > 0)
754         requisition->width = aux_info->width;
755       if (aux_info && aux_info->height > 0)
756         requisition->height = aux_info->height;
757     }
758 }
759
760 /**
761  * _gtk_size_group_get_child_requisition:
762  * @widget: a #GtkWidget
763  * @requisition: location to store computed requisition.
764  * 
765  * Retrieve the "child requisition" of the widget, taking account grouping
766  * of the widget's requisition with other widgets.
767  **/
768 void
769 _gtk_size_group_get_child_requisition (GtkWidget      *widget,
770                                        GtkRequisition *requisition)
771 {
772   initialize_size_group_quarks ();
773
774   if (requisition)
775     {
776       if (get_size_groups (widget))
777         {
778           requisition->width = get_dimension (widget, GTK_SIZE_GROUP_HORIZONTAL);
779           requisition->height = get_dimension (widget, GTK_SIZE_GROUP_VERTICAL);
780
781           /* Only do the full computation if we actually have size groups */
782         }
783       else
784         get_fast_child_requisition (widget, requisition);
785     }
786 }
787
788 /**
789  * _gtk_size_group_compute_requisition:
790  * @widget: a #GtkWidget
791  * @requisition: location to store computed requisition.
792  * 
793  * Compute the requisition of a widget taking into account grouping of
794  * the widget's requisition with other widgets.
795  **/
796 void
797 _gtk_size_group_compute_requisition (GtkWidget      *widget,
798                                      GtkRequisition *requisition)
799 {
800   gint width;
801   gint height;
802
803   initialize_size_group_quarks ();
804
805   if (get_size_groups (widget))
806     {
807       /* Only do the full computation if we actually have size groups */
808       
809       width = compute_dimension (widget, GTK_SIZE_GROUP_HORIZONTAL);
810       height = compute_dimension (widget, GTK_SIZE_GROUP_VERTICAL);
811
812       if (requisition)
813         {
814           requisition->width = width;
815           requisition->height = height;
816         }
817     }
818   else
819     {
820       do_size_request (widget);
821       
822       if (requisition)
823         get_fast_child_requisition (widget, requisition);
824     }
825 }
826
827 /**
828  * _gtk_size_group_queue_resize:
829  * @widget: a #GtkWidget
830  * 
831  * Queue a resize on a widget, and on all other widgets grouped with this widget.
832  **/
833 void
834 _gtk_size_group_queue_resize (GtkWidget *widget)
835 {
836   initialize_size_group_quarks ();
837
838   queue_resize_on_widget (widget, TRUE);
839 }
840
841 typedef struct {
842   GObject *object;
843   GSList *items;
844 } GSListSubParserData;
845
846 static void
847 size_group_start_element (GMarkupParseContext *context,
848                           const gchar         *element_name,
849                           const gchar        **names,
850                           const gchar        **values,
851                           gpointer            user_data,
852                           GError            **error)
853 {
854   guint i;
855   GSListSubParserData *data = (GSListSubParserData*)user_data;
856
857   if (strcmp (element_name, "widget") == 0)
858     for (i = 0; names[i]; i++)
859       if (strcmp (names[i], "name") == 0)
860         data->items = g_slist_prepend (data->items, g_strdup (values[i]));
861   else if (strcmp (element_name, "widgets") == 0)
862     return;
863   else
864     g_warning ("Unsupported type tag for GtkSizeGroup: %s\n",
865                element_name);
866
867 }
868
869 static const GMarkupParser size_group_parser =
870   {
871     size_group_start_element
872   };
873
874 static gboolean
875 gtk_size_group_buildable_custom_tag_start (GtkBuildable  *buildable,
876                                            GtkBuilder    *builder,
877                                            GObject       *child,
878                                            const gchar   *tagname,
879                                            GMarkupParser *parser,
880                                            gpointer      *data)
881 {
882   GSListSubParserData *parser_data;
883
884   if (child)
885     return FALSE;
886
887   if (strcmp (tagname, "widgets") == 0)
888     {
889       parser_data = g_slice_new0 (GSListSubParserData);
890       parser_data->items = NULL;
891       parser_data->object = G_OBJECT (buildable);
892
893       *parser = size_group_parser;
894       *data = parser_data;
895       return TRUE;
896     }
897
898   return FALSE;
899 }
900
901 static void
902 gtk_size_group_buildable_custom_finished (GtkBuildable  *buildable,
903                                           GtkBuilder    *builder,
904                                           GObject       *child,
905                                           const gchar   *tagname,
906                                           gpointer       user_data)
907 {
908   GSList *l;
909   GSListSubParserData *data;
910   GObject *object;
911
912   if (strcmp (tagname, "widgets"))
913     return;
914   
915   data = (GSListSubParserData*)user_data;
916   data->items = g_slist_reverse (data->items);
917
918   for (l = data->items; l; l = l->next)
919     {
920       object = gtk_builder_get_object (builder, l->data);
921       if (!object)
922         {
923           g_warning ("Unknown object %s specified in sizegroup %s",
924                      (const gchar*)l->data,
925                      gtk_buildable_get_name (GTK_BUILDABLE (data->object)));
926           continue;
927         }
928       gtk_size_group_add_widget (GTK_SIZE_GROUP (data->object),
929                                  GTK_WIDGET (object));
930       g_free (l->data);
931     }
932   g_slist_free (data->items);
933   g_slice_free (GSListSubParserData, data);
934 }
935
936
937 #define __GTK_SIZE_GROUP_C__
938 #include "gtkaliasdef.c"