]> Pileus Git - ~andy/gtk/blob - gtk/gtkbox.c
GTK_MENU_DIR_CHILD: check for the existance of
[~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 Library 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  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library 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 #include "gtkbox.h"
20
21 enum {
22   ARG_0,
23   ARG_SPACING,
24   ARG_HOMOGENEOUS
25 };
26
27 enum {
28   CHILD_ARG_0,
29   CHILD_ARG_EXPAND,
30   CHILD_ARG_FILL,
31   CHILD_ARG_PADDING,
32   CHILD_ARG_PACK_TYPE,
33   CHILD_ARG_POSITION
34 };
35
36 static void gtk_box_class_init (GtkBoxClass    *klass);
37 static void gtk_box_init       (GtkBox         *box);
38 static void gtk_box_get_arg    (GtkObject      *object,
39                                 GtkArg         *arg,
40                                 guint           arg_id);
41 static void gtk_box_set_arg    (GtkObject      *object,
42                                 GtkArg         *arg,
43                                 guint           arg_id);
44 static void gtk_box_map        (GtkWidget      *widget);
45 static void gtk_box_unmap      (GtkWidget      *widget);
46 static void gtk_box_draw       (GtkWidget      *widget,
47                                 GdkRectangle   *area);
48 static gint gtk_box_expose     (GtkWidget      *widget,
49                                 GdkEventExpose *event);
50 static void gtk_box_add        (GtkContainer   *container,
51                                 GtkWidget      *widget);
52 static void gtk_box_remove     (GtkContainer   *container,
53                                 GtkWidget      *widget);
54 static void gtk_box_forall     (GtkContainer   *container,
55                                 gboolean        include_internals,
56                                 GtkCallback     callback,
57                                 gpointer        callback_data);
58 static void gtk_box_set_child_arg (GtkContainer   *container,
59                                    GtkWidget      *child,
60                                    GtkArg         *arg,
61                                    guint           arg_id);
62 static void gtk_box_get_child_arg (GtkContainer   *container,
63                                    GtkWidget      *child,
64                                    GtkArg         *arg,
65                                    guint           arg_id);
66 static GtkType gtk_box_child_type (GtkContainer   *container);
67      
68
69 static GtkContainerClass *parent_class = NULL;
70
71
72 GtkType
73 gtk_box_get_type (void)
74 {
75   static GtkType box_type = 0;
76
77   if (!box_type)
78     {
79       GtkTypeInfo box_info =
80       {
81         "GtkBox",
82         sizeof (GtkBox),
83         sizeof (GtkBoxClass),
84         (GtkClassInitFunc) gtk_box_class_init,
85         (GtkObjectInitFunc) gtk_box_init,
86         /* reserved_1 */ NULL,
87         /* reserved_2 */ NULL,
88         (GtkClassInitFunc) NULL,
89       };
90
91       box_type = gtk_type_unique (GTK_TYPE_CONTAINER, &box_info);
92     }
93
94   return box_type;
95 }
96
97 static void
98 gtk_box_class_init (GtkBoxClass *class)
99 {
100   GtkObjectClass *object_class;
101   GtkWidgetClass *widget_class;
102   GtkContainerClass *container_class;
103
104   object_class = (GtkObjectClass*) class;
105   widget_class = (GtkWidgetClass*) class;
106   container_class = (GtkContainerClass*) class;
107
108   parent_class = gtk_type_class (GTK_TYPE_CONTAINER);
109
110   gtk_object_add_arg_type ("GtkBox::spacing", GTK_TYPE_INT, GTK_ARG_READWRITE, ARG_SPACING);
111   gtk_object_add_arg_type ("GtkBox::homogeneous", GTK_TYPE_BOOL, GTK_ARG_READWRITE, ARG_HOMOGENEOUS);
112   gtk_container_add_child_arg_type ("GtkBox::expand", GTK_TYPE_BOOL, GTK_ARG_READWRITE, CHILD_ARG_EXPAND);
113   gtk_container_add_child_arg_type ("GtkBox::fill", GTK_TYPE_BOOL, GTK_ARG_READWRITE, CHILD_ARG_FILL);
114   gtk_container_add_child_arg_type ("GtkBox::padding", GTK_TYPE_ULONG, GTK_ARG_READWRITE, CHILD_ARG_PADDING);
115   gtk_container_add_child_arg_type ("GtkBox::pack_type", GTK_TYPE_PACK_TYPE, GTK_ARG_READWRITE, CHILD_ARG_PACK_TYPE);
116   gtk_container_add_child_arg_type ("GtkBox::position", GTK_TYPE_LONG, GTK_ARG_READWRITE, CHILD_ARG_POSITION);
117
118   object_class->set_arg = gtk_box_set_arg;
119   object_class->get_arg = gtk_box_get_arg;
120
121   widget_class->map = gtk_box_map;
122   widget_class->unmap = gtk_box_unmap;
123   widget_class->draw = gtk_box_draw;
124   widget_class->expose_event = gtk_box_expose;
125
126   container_class->add = gtk_box_add;
127   container_class->remove = gtk_box_remove;
128   container_class->forall = gtk_box_forall;
129   container_class->child_type = gtk_box_child_type;
130   container_class->set_child_arg = gtk_box_set_child_arg;
131   container_class->get_child_arg = gtk_box_get_child_arg;
132 }
133
134 static void
135 gtk_box_init (GtkBox *box)
136 {
137   GTK_WIDGET_SET_FLAGS (box, GTK_NO_WINDOW | GTK_BASIC);
138
139   box->children = NULL;
140   box->spacing = 0;
141   box->homogeneous = FALSE;
142 }
143
144 static void
145 gtk_box_set_arg (GtkObject    *object,
146                  GtkArg       *arg,
147                  guint         arg_id)
148 {
149   GtkBox *box;
150
151   box = GTK_BOX (object);
152
153   switch (arg_id)
154     {
155     case ARG_SPACING:
156       gtk_box_set_spacing (box, GTK_VALUE_INT (*arg));
157       break;
158     case ARG_HOMOGENEOUS:
159       gtk_box_set_homogeneous (box, GTK_VALUE_BOOL (*arg));
160       break;
161     default:
162       break;
163     }
164 }
165
166 static void
167 gtk_box_get_arg (GtkObject    *object,
168                  GtkArg       *arg,
169                  guint         arg_id)
170 {
171   GtkBox *box;
172
173   box = GTK_BOX (object);
174
175   switch (arg_id)
176     {
177     case ARG_SPACING:
178       GTK_VALUE_INT (*arg) = box->spacing;
179       break;
180     case ARG_HOMOGENEOUS:
181       GTK_VALUE_BOOL (*arg) = box->homogeneous;
182       break;
183     default:
184       arg->type = GTK_TYPE_INVALID;
185       break;
186     }
187 }
188
189 static GtkType
190 gtk_box_child_type      (GtkContainer   *container)
191 {
192   return GTK_TYPE_WIDGET;
193 }
194
195 static void
196 gtk_box_set_child_arg (GtkContainer   *container,
197                        GtkWidget      *child,
198                        GtkArg         *arg,
199                        guint           arg_id)
200 {
201   gboolean expand = 0;
202   gboolean fill = 0;
203   guint padding = 0;
204   GtkPackType pack_type = 0;
205
206   if (arg_id != CHILD_ARG_POSITION)
207     gtk_box_query_child_packing (GTK_BOX (container),
208                                  child,
209                                  &expand,
210                                  &fill,
211                                  &padding,
212                                  &pack_type);
213   
214   switch (arg_id)
215     {
216     case CHILD_ARG_EXPAND:
217       gtk_box_set_child_packing (GTK_BOX (container),
218                                  child,
219                                  GTK_VALUE_BOOL (*arg),
220                                  fill,
221                                  padding,
222                                  pack_type);
223       break;
224     case CHILD_ARG_FILL:
225       gtk_box_set_child_packing (GTK_BOX (container),
226                                  child,
227                                  expand,
228                                  GTK_VALUE_BOOL (*arg),
229                                  padding,
230                                  pack_type);
231       break;
232     case CHILD_ARG_PADDING:
233       gtk_box_set_child_packing (GTK_BOX (container),
234                                  child,
235                                  expand,
236                                  fill,
237                                  GTK_VALUE_ULONG (*arg),
238                                  pack_type);
239       break;
240     case CHILD_ARG_PACK_TYPE:
241       gtk_box_set_child_packing (GTK_BOX (container),
242                                  child,
243                                  expand,
244                                  fill,
245                                  padding,
246                                  GTK_VALUE_ENUM (*arg));
247       break;
248     case CHILD_ARG_POSITION:
249       gtk_box_reorder_child (GTK_BOX (container),
250                              child,
251                              GTK_VALUE_LONG (*arg));
252       break;
253     default:
254       break;
255     }
256 }
257
258 static void
259 gtk_box_get_child_arg (GtkContainer   *container,
260                        GtkWidget      *child,
261                        GtkArg         *arg,
262                        guint           arg_id)
263 {
264   gboolean expand = 0;
265   gboolean fill = 0;
266   guint padding = 0;
267   GtkPackType pack_type = 0;
268   GList *list;
269
270   if (arg_id != CHILD_ARG_POSITION)
271     gtk_box_query_child_packing (GTK_BOX (container),
272                                  child,
273                                  &expand,
274                                  &fill,
275                                  &padding,
276                                  &pack_type);
277   
278   switch (arg_id)
279     {
280     case CHILD_ARG_EXPAND:
281       GTK_VALUE_BOOL (*arg) = expand;
282       break;
283     case CHILD_ARG_FILL:
284       GTK_VALUE_BOOL (*arg) = fill;
285       break;
286     case CHILD_ARG_PADDING:
287       GTK_VALUE_ULONG (*arg) = padding;
288       break;
289     case CHILD_ARG_PACK_TYPE:
290       GTK_VALUE_ENUM (*arg) = pack_type;
291       break;
292     case CHILD_ARG_POSITION:
293       GTK_VALUE_LONG (*arg) = 0;
294       for (list = GTK_BOX (container)->children; list; list = list->next)
295         {
296           GtkBoxChild *child_entry;
297
298           child_entry = list->data;
299           if (child_entry->widget == child)
300             break;
301           GTK_VALUE_LONG (*arg)++;
302         }
303       if (!list)
304         GTK_VALUE_LONG (*arg) = -1;
305       break;
306     default:
307       arg->type = GTK_TYPE_INVALID;
308       break;
309     }
310 }
311
312 void
313 gtk_box_pack_start (GtkBox    *box,
314                     GtkWidget *child,
315                     gboolean   expand,
316                     gboolean   fill,
317                     guint      padding)
318 {
319   GtkBoxChild *child_info;
320
321   g_return_if_fail (box != NULL);
322   g_return_if_fail (GTK_IS_BOX (box));
323   g_return_if_fail (child != NULL);
324   g_return_if_fail (child->parent == NULL);
325
326   child_info = g_new (GtkBoxChild, 1);
327   child_info->widget = child;
328   child_info->padding = padding;
329   child_info->expand = expand ? TRUE : FALSE;
330   child_info->fill = fill ? TRUE : FALSE;
331   child_info->pack = GTK_PACK_START;
332
333   box->children = g_list_append (box->children, child_info);
334
335   gtk_widget_set_parent (child, GTK_WIDGET (box));
336
337   if (GTK_WIDGET_VISIBLE (GTK_WIDGET (box)))
338     {
339       if (GTK_WIDGET_REALIZED (GTK_WIDGET (box)) &&
340           !GTK_WIDGET_REALIZED (child))
341         gtk_widget_realize (child);
342       
343       if (GTK_WIDGET_MAPPED (GTK_WIDGET (box)) &&
344           !GTK_WIDGET_MAPPED (child))
345         gtk_widget_map (child);
346     }
347
348   if (GTK_WIDGET_VISIBLE (child) && GTK_WIDGET_VISIBLE (box))
349     gtk_widget_queue_resize (child);
350 }
351
352 void
353 gtk_box_pack_end (GtkBox    *box,
354                   GtkWidget *child,
355                   gboolean   expand,
356                   gboolean   fill,
357                   guint      padding)
358 {
359   GtkBoxChild *child_info;
360
361   g_return_if_fail (box != NULL);
362   g_return_if_fail (GTK_IS_BOX (box));
363   g_return_if_fail (child != NULL);
364   g_return_if_fail (child->parent == NULL);
365
366   child_info = g_new (GtkBoxChild, 1);
367   child_info->widget = child;
368   child_info->padding = padding;
369   child_info->expand = expand ? TRUE : FALSE;
370   child_info->fill = fill ? TRUE : FALSE;
371   child_info->pack = GTK_PACK_END;
372
373   box->children = g_list_append (box->children, child_info);
374
375   gtk_widget_set_parent (child, GTK_WIDGET (box));
376
377   if (GTK_WIDGET_VISIBLE (GTK_WIDGET (box)))
378     {
379       if (GTK_WIDGET_REALIZED (GTK_WIDGET (box)) &&
380           !GTK_WIDGET_REALIZED (child))
381         gtk_widget_realize (child);
382       
383       if (GTK_WIDGET_MAPPED (GTK_WIDGET (box)) &&
384           !GTK_WIDGET_MAPPED (child))
385         gtk_widget_map (child);
386     }
387   
388   if (GTK_WIDGET_VISIBLE (child) && GTK_WIDGET_VISIBLE (box))
389     gtk_widget_queue_resize (child);
390 }
391
392 void
393 gtk_box_pack_start_defaults (GtkBox    *box,
394                              GtkWidget *child)
395 {
396   g_return_if_fail (box != NULL);
397   g_return_if_fail (GTK_IS_BOX (box));
398   g_return_if_fail (child != NULL);
399
400   gtk_box_pack_start (box, child, TRUE, TRUE, 0);
401 }
402
403 void
404 gtk_box_pack_end_defaults (GtkBox    *box,
405                            GtkWidget *child)
406 {
407   g_return_if_fail (box != NULL);
408   g_return_if_fail (GTK_IS_BOX (box));
409   g_return_if_fail (child != NULL);
410
411   gtk_box_pack_end (box, child, TRUE, TRUE, 0);
412 }
413
414 void
415 gtk_box_set_homogeneous (GtkBox  *box,
416                          gboolean homogeneous)
417 {
418   g_return_if_fail (box != NULL);
419   g_return_if_fail (GTK_IS_BOX (box));
420
421   if ((homogeneous ? TRUE : FALSE) != box->homogeneous)
422     {
423       box->homogeneous = homogeneous ? TRUE : FALSE;
424       gtk_widget_queue_resize (GTK_WIDGET (box));
425     }
426 }
427
428 void
429 gtk_box_set_spacing (GtkBox *box,
430                      gint    spacing)
431 {
432   g_return_if_fail (box != NULL);
433   g_return_if_fail (GTK_IS_BOX (box));
434
435   if (spacing != box->spacing)
436     {
437       box->spacing = spacing;
438       gtk_widget_queue_resize (GTK_WIDGET (box));
439     }
440 }
441
442 void
443 gtk_box_reorder_child (GtkBox                   *box,
444                        GtkWidget                *child,
445                        gint                      position)
446 {
447   GList *list;
448
449   g_return_if_fail (box != NULL);
450   g_return_if_fail (GTK_IS_BOX (box));
451   g_return_if_fail (child != NULL);
452
453   list = box->children;
454   while (list)
455     {
456       GtkBoxChild *child_info;
457
458       child_info = list->data;
459       if (child_info->widget == child)
460         break;
461
462       list = list->next;
463     }
464
465   if (list && box->children->next)
466     {
467       GList *tmp_list;
468
469       if (list->next)
470         list->next->prev = list->prev;
471       if (list->prev)
472         list->prev->next = list->next;
473       else
474         box->children = list->next;
475
476       tmp_list = box->children;
477       while (position && tmp_list->next)
478         {
479           position--;
480           tmp_list = tmp_list->next;
481         }
482
483       if (position)
484         {
485           tmp_list->next = list;
486           list->prev = tmp_list;
487           list->next = NULL;
488         }
489       else
490         {
491           if (tmp_list->prev)
492             tmp_list->prev->next = list;
493           else
494             box->children = list;
495           list->prev = tmp_list->prev;
496           tmp_list->prev = list;
497           list->next = tmp_list;
498         }
499
500       if (GTK_WIDGET_VISIBLE (child) && GTK_WIDGET_VISIBLE (box))
501         gtk_widget_queue_resize (child);
502     }
503 }
504
505 void
506 gtk_box_query_child_packing (GtkBox             *box,
507                              GtkWidget          *child,
508                              gboolean           *expand,
509                              gboolean           *fill,
510                              guint              *padding,
511                              GtkPackType        *pack_type)
512 {
513   GList *list;
514   GtkBoxChild *child_info;
515
516   g_return_if_fail (box != NULL);
517   g_return_if_fail (GTK_IS_BOX (box));
518   g_return_if_fail (child != NULL);
519
520   list = box->children;
521   while (list)
522     {
523       child_info = list->data;
524       if (child_info->widget == child)
525         break;
526
527       list = list->next;
528     }
529
530   if (list)
531     {
532       if (expand)
533         *expand = child_info->expand;
534       if (fill)
535         *fill = child_info->fill;
536       if (padding)
537         *padding = child_info->padding;
538       if (pack_type)
539         *pack_type = child_info->pack;
540     }
541 }
542
543 void
544 gtk_box_set_child_packing (GtkBox               *box,
545                            GtkWidget            *child,
546                            gboolean              expand,
547                            gboolean              fill,
548                            guint                 padding,
549                            GtkPackType           pack_type)
550 {
551   GList *list;
552   GtkBoxChild *child_info;
553
554   g_return_if_fail (box != NULL);
555   g_return_if_fail (GTK_IS_BOX (box));
556   g_return_if_fail (child != NULL);
557
558   list = box->children;
559   while (list)
560     {
561       child_info = list->data;
562       if (child_info->widget == child)
563         break;
564
565       list = list->next;
566     }
567
568   if (list)
569     {
570       child_info->expand = expand != FALSE;
571       child_info->fill = fill != FALSE;
572       child_info->padding = padding;
573       if (pack_type == GTK_PACK_END)
574         child_info->pack = GTK_PACK_END;
575       else
576         child_info->pack = GTK_PACK_START;
577
578       if (GTK_WIDGET_VISIBLE (child) && GTK_WIDGET_VISIBLE (box))
579         gtk_widget_queue_resize (child);
580     }
581 }
582
583 static void
584 gtk_box_map (GtkWidget *widget)
585 {
586   GtkBox *box;
587   GtkBoxChild *child;
588   GList *children;
589
590   g_return_if_fail (widget != NULL);
591   g_return_if_fail (GTK_IS_BOX (widget));
592
593   box = GTK_BOX (widget);
594   GTK_WIDGET_SET_FLAGS (box, GTK_MAPPED);
595
596   children = box->children;
597   while (children)
598     {
599       child = children->data;
600       children = children->next;
601
602       if (GTK_WIDGET_VISIBLE (child->widget) &&
603           !GTK_WIDGET_MAPPED (child->widget))
604         gtk_widget_map (child->widget);
605     }
606 }
607
608 static void
609 gtk_box_unmap (GtkWidget *widget)
610 {
611   GtkBox *box;
612   GtkBoxChild *child;
613   GList *children;
614
615   g_return_if_fail (widget != NULL);
616   g_return_if_fail (GTK_IS_BOX (widget));
617
618   box = GTK_BOX (widget);
619   GTK_WIDGET_UNSET_FLAGS (box, GTK_MAPPED);
620
621   children = box->children;
622   while (children)
623     {
624       child = children->data;
625       children = children->next;
626
627       if (GTK_WIDGET_VISIBLE (child->widget) &&
628           GTK_WIDGET_MAPPED (child->widget))
629         gtk_widget_unmap (child->widget);
630     }
631 }
632
633 static void
634 gtk_box_draw (GtkWidget    *widget,
635               GdkRectangle *area)
636 {
637   GtkBox *box;
638   GtkBoxChild *child;
639   GdkRectangle child_area;
640   GList *children;
641
642   g_return_if_fail (widget != NULL);
643   g_return_if_fail (GTK_IS_BOX (widget));
644
645   if (GTK_WIDGET_DRAWABLE (widget))
646     {
647       box = GTK_BOX (widget);
648
649       children = box->children;
650       while (children)
651         {
652           child = children->data;
653           children = children->next;
654
655           if (gtk_widget_intersect (child->widget, area, &child_area))
656             gtk_widget_draw (child->widget, &child_area);
657         }
658     }
659 }
660
661 static gint
662 gtk_box_expose (GtkWidget      *widget,
663                 GdkEventExpose *event)
664 {
665   GtkBox *box;
666   GtkBoxChild *child;
667   GdkEventExpose child_event;
668   GList *children;
669
670   g_return_val_if_fail (widget != NULL, FALSE);
671   g_return_val_if_fail (GTK_IS_BOX (widget), FALSE);
672   g_return_val_if_fail (event != NULL, FALSE);
673
674   if (GTK_WIDGET_DRAWABLE (widget))
675     {
676       box = GTK_BOX (widget);
677
678       child_event = *event;
679
680       children = box->children;
681       while (children)
682         {
683           child = children->data;
684           children = children->next;
685
686           if (GTK_WIDGET_NO_WINDOW (child->widget) &&
687               gtk_widget_intersect (child->widget, &event->area, &child_event.area))
688             gtk_widget_event (child->widget, (GdkEvent*) &child_event);
689         }
690     }
691
692   return FALSE;
693 }
694
695 static void
696 gtk_box_add (GtkContainer *container,
697              GtkWidget    *widget)
698 {
699   g_return_if_fail (container != NULL);
700   g_return_if_fail (GTK_IS_BOX (container));
701   g_return_if_fail (widget != NULL);
702
703   gtk_box_pack_start_defaults (GTK_BOX (container), widget);
704 }
705
706 static void
707 gtk_box_remove (GtkContainer *container,
708                 GtkWidget    *widget)
709 {
710   GtkBox *box;
711   GtkBoxChild *child;
712   GList *children;
713
714   g_return_if_fail (container != NULL);
715   g_return_if_fail (GTK_IS_BOX (container));
716   g_return_if_fail (widget != NULL);
717
718   box = GTK_BOX (container);
719
720   children = box->children;
721   while (children)
722     {
723       child = children->data;
724
725       if (child->widget == widget)
726         {
727           gboolean was_visible;
728
729           was_visible = GTK_WIDGET_VISIBLE (widget);
730           gtk_widget_unparent (widget);
731
732           box->children = g_list_remove_link (box->children, children);
733           g_list_free (children);
734           g_free (child);
735
736           /* queue resize regardless of GTK_WIDGET_VISIBLE (container),
737            * since that's what is needed by toplevels.
738            */
739           if (was_visible)
740             gtk_widget_queue_resize (GTK_WIDGET (container));
741
742           break;
743         }
744
745       children = children->next;
746     }
747 }
748
749 static void
750 gtk_box_forall (GtkContainer *container,
751                 gboolean      include_internals,
752                 GtkCallback   callback,
753                 gpointer      callback_data)
754 {
755   GtkBox *box;
756   GtkBoxChild *child;
757   GList *children;
758
759   g_return_if_fail (container != NULL);
760   g_return_if_fail (GTK_IS_BOX (container));
761   g_return_if_fail (callback != NULL);
762
763   box = GTK_BOX (container);
764
765   children = box->children;
766   while (children)
767     {
768       child = children->data;
769       children = children->next;
770
771       if (child->pack == GTK_PACK_START)
772         (* callback) (child->widget, callback_data);
773     }
774
775   children = g_list_last (box->children);
776   while (children)
777     {
778       child = children->data;
779       children = children->prev;
780
781       if (child->pack == GTK_PACK_END)
782         (* callback) (child->widget, callback_data);
783     }
784 }