]> Pileus Git - ~andy/gtk/blob - gtk/gtkbox.c
Mark abstract types as G_TYPE_FLAG_ABSTRACT. (#72383)
[~andy/gtk] / gtk / gtkbox.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 /*
21  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
22  * file for a list of people on the GTK+ Team.  See the ChangeLog
23  * files for a list of changes.  These files are distributed with
24  * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
25  */
26
27 #include "gtkbox.h"
28 #include "gtkintl.h"
29
30 enum {
31   PROP_0,
32   PROP_SPACING,
33   PROP_HOMOGENEOUS
34 };
35
36 enum {
37   CHILD_PROP_0,
38   CHILD_PROP_EXPAND,
39   CHILD_PROP_FILL,
40   CHILD_PROP_PADDING,
41   CHILD_PROP_PACK_TYPE,
42   CHILD_PROP_POSITION
43 };
44
45 static void gtk_box_class_init (GtkBoxClass    *klass);
46 static void gtk_box_init       (GtkBox         *box);
47 static void gtk_box_set_property (GObject         *object,
48                                   guint            prop_id,
49                                   const GValue    *value,
50                                   GParamSpec      *pspec);
51 static void gtk_box_get_property (GObject         *object,
52                                   guint            prop_id,
53                                   GValue          *value,
54                                   GParamSpec      *pspec);
55 static void gtk_box_add        (GtkContainer   *container,
56                                 GtkWidget      *widget);
57 static void gtk_box_remove     (GtkContainer   *container,
58                                 GtkWidget      *widget);
59 static void gtk_box_forall     (GtkContainer   *container,
60                                 gboolean        include_internals,
61                                 GtkCallback     callback,
62                                 gpointer        callback_data);
63 static void gtk_box_set_child_property (GtkContainer    *container,
64                                         GtkWidget       *child,
65                                         guint            property_id,
66                                         const GValue    *value,
67                                         GParamSpec      *pspec);
68 static void gtk_box_get_child_property (GtkContainer    *container,
69                                         GtkWidget       *child,
70                                         guint            property_id,
71                                         GValue          *value,
72                                         GParamSpec      *pspec);
73 static GtkType gtk_box_child_type (GtkContainer   *container);
74      
75
76 static GtkContainerClass *parent_class = NULL;
77
78
79 GtkType
80 gtk_box_get_type (void)
81 {
82   static GtkType box_type = 0;
83
84   if (!box_type)
85     {
86       static const GTypeInfo box_info =
87       {
88         sizeof (GtkBoxClass),
89         NULL,            /* base_init */
90         NULL,            /* base_finalize */
91         (GClassInitFunc) gtk_box_class_init,
92         NULL,            /* class_finalize */
93         NULL,            /* class_data */
94         sizeof (GtkBox),
95         0,               /* n_preallocs */
96         (GInstanceInitFunc) gtk_box_init,
97         NULL,            /* value_table */
98       };
99
100       box_type = g_type_register_static (GTK_TYPE_CONTAINER, "GtkBox", 
101                                          &box_info, G_TYPE_FLAG_ABSTRACT);
102     }
103
104   return box_type;
105 }
106
107 static void
108 gtk_box_class_init (GtkBoxClass *class)
109 {
110   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
111   GtkContainerClass *container_class = GTK_CONTAINER_CLASS (class);
112
113   parent_class = g_type_class_peek_parent (class);
114
115   gobject_class->set_property = gtk_box_set_property;
116   gobject_class->get_property = gtk_box_get_property;
117    
118   container_class->add = gtk_box_add;
119   container_class->remove = gtk_box_remove;
120   container_class->forall = gtk_box_forall;
121   container_class->child_type = gtk_box_child_type;
122   container_class->set_child_property = gtk_box_set_child_property;
123   container_class->get_child_property = gtk_box_get_child_property;
124
125   g_object_class_install_property (gobject_class,
126                                    PROP_SPACING,
127                                    g_param_spec_int ("spacing",
128                                                      _("Spacing"),
129                                                      _("The amount of space between children"),
130                                                      0,
131                                                      G_MAXINT,
132                                                      0,
133                                                      G_PARAM_READABLE | G_PARAM_WRITABLE));
134   
135   g_object_class_install_property (gobject_class,
136                                    PROP_HOMOGENEOUS,
137                                    g_param_spec_boolean ("homogeneous",
138                                                          _("Homogeneous"),
139                                                          _("Whether the children should all be the same size"),
140                                                          FALSE,
141                                                          G_PARAM_READABLE | G_PARAM_WRITABLE));
142
143   gtk_container_class_install_child_property (container_class,
144                                               CHILD_PROP_EXPAND,
145                                               g_param_spec_boolean ("expand", 
146                                                                     _("Expand"), 
147                                                                     _("Whether the child should receive extra space when the parent grows"),
148                                                                     TRUE,
149                                                                     G_PARAM_READWRITE));
150   gtk_container_class_install_child_property (container_class,
151                                               CHILD_PROP_FILL,
152                                               g_param_spec_boolean ("fill", 
153                                                                     _("Fill"), 
154                                                                     _("Whether extra space given to the child should be allocated to the child or used as padding"),
155                                                                     TRUE,
156                                                                     G_PARAM_READWRITE));
157   gtk_container_class_install_child_property (container_class,
158                                               CHILD_PROP_PADDING,
159                                               g_param_spec_uint ("padding", 
160                                                                  _("Padding"), 
161                                                                  _("Extra space to put between the child and its neighbors, in pixels"),
162                                                                  0, G_MAXINT, 0,
163                                                                  G_PARAM_READWRITE));
164   gtk_container_class_install_child_property (container_class,
165                                               CHILD_PROP_PACK_TYPE,
166                                               g_param_spec_enum ("pack_type", 
167                                                                  _("Pack type"), 
168                                                                  _("A GtkPackType indicating whether the child is packed with reference to the start or end of the parent"),
169                                                                  GTK_TYPE_PACK_TYPE, GTK_PACK_START,
170                                                                  G_PARAM_READWRITE));
171   gtk_container_class_install_child_property (container_class,
172                                               CHILD_PROP_POSITION,
173                                               g_param_spec_int ("position", 
174                                                                 _("Position"), 
175                                                                 _("The index of the child in the parent"),
176                                                                 -1, G_MAXINT, 0,
177                                                                 G_PARAM_READWRITE));
178 }
179
180 static void
181 gtk_box_init (GtkBox *box)
182 {
183   GTK_WIDGET_SET_FLAGS (box, GTK_NO_WINDOW);
184   gtk_widget_set_redraw_on_allocate (GTK_WIDGET (box), FALSE);
185   
186   box->children = NULL;
187   box->spacing = 0;
188   box->homogeneous = FALSE;
189 }
190
191 static void 
192 gtk_box_set_property (GObject         *object,
193                       guint            prop_id,
194                       const GValue    *value,
195                       GParamSpec      *pspec)
196 {
197   GtkBox *box;
198
199   box = GTK_BOX (object);
200
201   switch (prop_id)
202     {
203     case PROP_SPACING:
204       gtk_box_set_spacing (box, g_value_get_int (value));
205       break;
206     case PROP_HOMOGENEOUS:
207       gtk_box_set_homogeneous (box, g_value_get_boolean (value));
208       break;
209     default:
210       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
211       break;
212     }
213 }
214
215 static void gtk_box_get_property (GObject         *object,
216                                   guint            prop_id,
217                                   GValue          *value,
218                                   GParamSpec      *pspec)
219 {
220   GtkBox *box;
221
222   box = GTK_BOX (object);
223
224   switch (prop_id)
225     {
226     case PROP_SPACING:
227       g_value_set_int (value, box->spacing);
228       break;
229     case PROP_HOMOGENEOUS:
230       g_value_set_boolean (value, box->homogeneous);
231       break;
232     default:
233       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
234       break;
235     }
236 }
237
238 static GtkType
239 gtk_box_child_type      (GtkContainer   *container)
240 {
241   return GTK_TYPE_WIDGET;
242 }
243
244 static void
245 gtk_box_set_child_property (GtkContainer    *container,
246                             GtkWidget       *child,
247                             guint            property_id,
248                             const GValue    *value,
249                             GParamSpec      *pspec)
250 {
251   gboolean expand = 0;
252   gboolean fill = 0;
253   guint padding = 0;
254   GtkPackType pack_type = 0;
255
256   if (property_id != CHILD_PROP_POSITION)
257     gtk_box_query_child_packing (GTK_BOX (container),
258                                  child,
259                                  &expand,
260                                  &fill,
261                                  &padding,
262                                  &pack_type);
263   switch (property_id)
264     {
265     case CHILD_PROP_EXPAND:
266       gtk_box_set_child_packing (GTK_BOX (container),
267                                  child,
268                                  g_value_get_boolean (value),
269                                  fill,
270                                  padding,
271                                  pack_type);
272       break;
273     case CHILD_PROP_FILL:
274       gtk_box_set_child_packing (GTK_BOX (container),
275                                  child,
276                                  expand,
277                                  g_value_get_boolean (value),
278                                  padding,
279                                  pack_type);
280       break;
281     case CHILD_PROP_PADDING:
282       gtk_box_set_child_packing (GTK_BOX (container),
283                                  child,
284                                  expand,
285                                  fill,
286                                  g_value_get_uint (value),
287                                  pack_type);
288       break;
289     case CHILD_PROP_PACK_TYPE:
290       gtk_box_set_child_packing (GTK_BOX (container),
291                                  child,
292                                  expand,
293                                  fill,
294                                  padding,
295                                  g_value_get_enum (value));
296       break;
297     case CHILD_PROP_POSITION:
298       gtk_box_reorder_child (GTK_BOX (container),
299                              child,
300                              g_value_get_int (value));
301       break;
302     default:
303       GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
304       break;
305     }
306 }
307
308 static void
309 gtk_box_get_child_property (GtkContainer *container,
310                             GtkWidget    *child,
311                             guint         property_id,
312                             GValue       *value,
313                             GParamSpec   *pspec)
314 {
315   gboolean expand = 0;
316   gboolean fill = 0;
317   guint padding = 0;
318   GtkPackType pack_type = 0;
319   GList *list;
320
321   if (property_id != CHILD_PROP_POSITION)
322     gtk_box_query_child_packing (GTK_BOX (container),
323                                  child,
324                                  &expand,
325                                  &fill,
326                                  &padding,
327                                  &pack_type);
328   switch (property_id)
329     {
330       guint i;
331     case CHILD_PROP_EXPAND:
332       g_value_set_boolean (value, expand);
333       break;
334     case CHILD_PROP_FILL:
335       g_value_set_boolean (value, fill);
336       break;
337     case CHILD_PROP_PADDING:
338       g_value_set_uint (value, padding);
339       break;
340     case CHILD_PROP_PACK_TYPE:
341       g_value_set_enum (value, pack_type);
342       break;
343     case CHILD_PROP_POSITION:
344       i = 0;
345       for (list = GTK_BOX (container)->children; list; list = list->next)
346         {
347           GtkBoxChild *child_entry;
348
349           child_entry = list->data;
350           if (child_entry->widget == child)
351             break;
352           i++;
353         }
354       g_value_set_int (value, list ? i : -1);
355       break;
356     default:
357       GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
358       break;
359     }
360 }
361
362 void
363 gtk_box_pack_start (GtkBox    *box,
364                     GtkWidget *child,
365                     gboolean   expand,
366                     gboolean   fill,
367                     guint      padding)
368 {
369   GtkBoxChild *child_info;
370
371   g_return_if_fail (GTK_IS_BOX (box));
372   g_return_if_fail (GTK_IS_WIDGET (child));
373   g_return_if_fail (child->parent == NULL);
374
375   child_info = g_new (GtkBoxChild, 1);
376   child_info->widget = child;
377   child_info->padding = padding;
378   child_info->expand = expand ? TRUE : FALSE;
379   child_info->fill = fill ? TRUE : FALSE;
380   child_info->pack = GTK_PACK_START;
381   child_info->is_secondary = FALSE;
382
383   box->children = g_list_append (box->children, child_info);
384
385   gtk_widget_freeze_child_notify (child);
386
387   gtk_widget_set_parent (child, GTK_WIDGET (box));
388   
389   gtk_widget_child_notify (child, "expand");
390   gtk_widget_child_notify (child, "fill");
391   gtk_widget_child_notify (child, "padding");
392   gtk_widget_child_notify (child, "pack_type");
393   gtk_widget_child_notify (child, "position");
394   gtk_widget_thaw_child_notify (child);
395 }
396
397 void
398 gtk_box_pack_end (GtkBox    *box,
399                   GtkWidget *child,
400                   gboolean   expand,
401                   gboolean   fill,
402                   guint      padding)
403 {
404   GtkBoxChild *child_info;
405
406   g_return_if_fail (GTK_IS_BOX (box));
407   g_return_if_fail (GTK_IS_WIDGET (child));
408   g_return_if_fail (child->parent == NULL);
409
410   child_info = g_new (GtkBoxChild, 1);
411   child_info->widget = child;
412   child_info->padding = padding;
413   child_info->expand = expand ? TRUE : FALSE;
414   child_info->fill = fill ? TRUE : FALSE;
415   child_info->pack = GTK_PACK_END;
416   child_info->is_secondary = FALSE;
417
418   box->children = g_list_append (box->children, child_info);
419
420   gtk_widget_freeze_child_notify (child);
421
422   gtk_widget_set_parent (child, GTK_WIDGET (box));
423
424   gtk_widget_child_notify (child, "expand");
425   gtk_widget_child_notify (child, "fill");
426   gtk_widget_child_notify (child, "padding");
427   gtk_widget_child_notify (child, "pack_type");
428   gtk_widget_child_notify (child, "position");
429   gtk_widget_thaw_child_notify (child);
430 }
431
432 void
433 gtk_box_pack_start_defaults (GtkBox    *box,
434                              GtkWidget *child)
435 {
436   gtk_box_pack_start (box, child, TRUE, TRUE, 0);
437 }
438
439 void
440 gtk_box_pack_end_defaults (GtkBox    *box,
441                            GtkWidget *child)
442 {
443   gtk_box_pack_end (box, child, TRUE, TRUE, 0);
444 }
445
446 void
447 gtk_box_set_homogeneous (GtkBox  *box,
448                          gboolean homogeneous)
449 {
450   g_return_if_fail (GTK_IS_BOX (box));
451
452   if ((homogeneous ? TRUE : FALSE) != box->homogeneous)
453     {
454       box->homogeneous = homogeneous ? TRUE : FALSE;
455       g_object_notify (G_OBJECT (box), "homogeneous");
456       gtk_widget_queue_resize (GTK_WIDGET (box));
457     }
458 }
459
460 /**
461  * gtk_box_get_homogeneous:
462  * @box: a #GtkBox
463  *
464  * Returns whether the box is homogeneous (all children are the
465  * same size). See gtk_box_set_homogeneous ().
466  *
467  * Return value: %TRUE if the box is homogeneous.
468  **/
469 gboolean
470 gtk_box_get_homogeneous (GtkBox *box)
471 {
472   g_return_val_if_fail (GTK_IS_BOX (box), FALSE);
473
474   return box->homogeneous;
475 }
476
477 void
478 gtk_box_set_spacing (GtkBox *box,
479                      gint    spacing)
480 {
481   g_return_if_fail (GTK_IS_BOX (box));
482
483   if (spacing != box->spacing)
484     {
485       box->spacing = spacing;
486       g_object_notify (G_OBJECT (box), "spacing");
487       gtk_widget_queue_resize (GTK_WIDGET (box));
488     }
489 }
490
491 /**
492  * gtk_box_get_spacing:
493  * @box: a #GtkBox
494  * 
495  * Gets the value set by gtk_box_set_spacing().
496  * 
497  * Return value: spacing between children
498  **/
499 gint
500 gtk_box_get_spacing (GtkBox *box)
501 {
502   g_return_val_if_fail (GTK_IS_BOX (box), 0);
503
504   return box->spacing;
505 }
506
507 void
508 gtk_box_reorder_child (GtkBox    *box,
509                        GtkWidget *child,
510                        gint       position)
511 {
512   GList *old_link;
513   GList *new_link;
514   GtkBoxChild *child_info = NULL;
515   gint old_position;
516
517   g_return_if_fail (GTK_IS_BOX (box));
518   g_return_if_fail (GTK_IS_WIDGET (child));
519
520   old_link = box->children;
521   old_position = 0;
522   while (old_link)
523     {
524       child_info = old_link->data;
525       if (child_info->widget == child)
526         break;
527
528       old_link = old_link->next;
529       old_position++;
530     }
531
532   g_return_if_fail (old_link != NULL);
533
534   if (position == old_position)
535     return;
536
537   box->children = g_list_delete_link (box->children, old_link);
538
539   if (position < 0)
540     new_link = NULL;
541   else
542     new_link = g_list_nth (box->children, position);
543
544   box->children = g_list_insert_before (box->children, new_link, child_info);
545
546   gtk_widget_child_notify (child, "position");
547   if (GTK_WIDGET_VISIBLE (child) && GTK_WIDGET_VISIBLE (box))
548     gtk_widget_queue_resize (child);
549 }
550
551 void
552 gtk_box_query_child_packing (GtkBox             *box,
553                              GtkWidget          *child,
554                              gboolean           *expand,
555                              gboolean           *fill,
556                              guint              *padding,
557                              GtkPackType        *pack_type)
558 {
559   GList *list;
560   GtkBoxChild *child_info = NULL;
561
562   g_return_if_fail (GTK_IS_BOX (box));
563   g_return_if_fail (GTK_IS_WIDGET (child));
564
565   list = box->children;
566   while (list)
567     {
568       child_info = list->data;
569       if (child_info->widget == child)
570         break;
571
572       list = list->next;
573     }
574
575   if (list)
576     {
577       if (expand)
578         *expand = child_info->expand;
579       if (fill)
580         *fill = child_info->fill;
581       if (padding)
582         *padding = child_info->padding;
583       if (pack_type)
584         *pack_type = child_info->pack;
585     }
586 }
587
588 void
589 gtk_box_set_child_packing (GtkBox               *box,
590                            GtkWidget            *child,
591                            gboolean              expand,
592                            gboolean              fill,
593                            guint                 padding,
594                            GtkPackType           pack_type)
595 {
596   GList *list;
597   GtkBoxChild *child_info = NULL;
598
599   g_return_if_fail (GTK_IS_BOX (box));
600   g_return_if_fail (GTK_IS_WIDGET (child));
601
602   list = box->children;
603   while (list)
604     {
605       child_info = list->data;
606       if (child_info->widget == child)
607         break;
608
609       list = list->next;
610     }
611
612   gtk_widget_freeze_child_notify (child);
613   if (list)
614     {
615       child_info->expand = expand != FALSE;
616       gtk_widget_child_notify (child, "expand");
617       child_info->fill = fill != FALSE;
618       gtk_widget_child_notify (child, "fill");
619       child_info->padding = padding;
620       gtk_widget_child_notify (child, "padding");
621       if (pack_type == GTK_PACK_END)
622         child_info->pack = GTK_PACK_END;
623       else
624         child_info->pack = GTK_PACK_START;
625       gtk_widget_child_notify (child, "pack_type");
626
627       if (GTK_WIDGET_VISIBLE (child) && GTK_WIDGET_VISIBLE (box))
628         gtk_widget_queue_resize (child);
629     }
630   gtk_widget_thaw_child_notify (child);
631 }
632
633 static void
634 gtk_box_add (GtkContainer *container,
635              GtkWidget    *widget)
636 {
637   gtk_box_pack_start_defaults (GTK_BOX (container), widget);
638 }
639
640 static void
641 gtk_box_remove (GtkContainer *container,
642                 GtkWidget    *widget)
643 {
644   GtkBox *box;
645   GtkBoxChild *child;
646   GList *children;
647
648   box = GTK_BOX (container);
649
650   children = box->children;
651   while (children)
652     {
653       child = children->data;
654
655       if (child->widget == widget)
656         {
657           gboolean was_visible;
658
659           was_visible = GTK_WIDGET_VISIBLE (widget);
660           gtk_widget_unparent (widget);
661
662           box->children = g_list_remove_link (box->children, children);
663           g_list_free (children);
664           g_free (child);
665
666           /* queue resize regardless of GTK_WIDGET_VISIBLE (container),
667            * since that's what is needed by toplevels.
668            */
669           if (was_visible)
670             gtk_widget_queue_resize (GTK_WIDGET (container));
671
672           break;
673         }
674
675       children = children->next;
676     }
677 }
678
679 static void
680 gtk_box_forall (GtkContainer *container,
681                 gboolean      include_internals,
682                 GtkCallback   callback,
683                 gpointer      callback_data)
684 {
685   GtkBox *box;
686   GtkBoxChild *child;
687   GList *children;
688
689   g_return_if_fail (callback != NULL);
690
691   box = GTK_BOX (container);
692
693   children = box->children;
694   while (children)
695     {
696       child = children->data;
697       children = children->next;
698
699       if (child->pack == GTK_PACK_START)
700         (* callback) (child->widget, callback_data);
701     }
702
703   children = g_list_last (box->children);
704   while (children)
705     {
706       child = children->data;
707       children = children->prev;
708
709       if (child->pack == GTK_PACK_END)
710         (* callback) (child->widget, callback_data);
711     }
712 }