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