]> Pileus Git - ~andy/gtk/blob - gtk/gtkcontainer.c
Add docs.
[~andy/gtk] / gtk / gtkcontainer.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 <stdarg.h>
28 #include <string.h>
29 #include <stdlib.h>
30
31 #include "gtkcontainer.h"
32 #include "gtkprivate.h"
33 #include "gtksignal.h"
34 #include "gtkmain.h"
35 #include "gtkmarshalers.h"
36 #include "gtkwindow.h"
37 #include "gtkintl.h"
38 #include <gobject/gobjectnotifyqueue.c>
39 #include <gobject/gvaluecollector.h>
40
41
42 enum {
43   ADD,
44   REMOVE,
45   CHECK_RESIZE,
46   SET_FOCUS_CHILD,
47   LAST_SIGNAL
48 };
49
50 enum {
51   PROP_0,
52   PROP_BORDER_WIDTH,
53   PROP_RESIZE_MODE,
54   PROP_CHILD,
55 };
56
57 #define PARAM_SPEC_PARAM_ID(pspec)              ((pspec)->param_id)
58 #define PARAM_SPEC_SET_PARAM_ID(pspec, id)      ((pspec)->param_id = (id))
59
60
61 /* --- prototypes --- */
62 static void     gtk_container_base_class_init      (GtkContainerClass *klass);
63 static void     gtk_container_base_class_finalize  (GtkContainerClass *klass);
64 static void     gtk_container_class_init           (GtkContainerClass *klass);
65 static void     gtk_container_init                 (GtkContainer      *container);
66 static void     gtk_container_destroy              (GtkObject         *object);
67 static void     gtk_container_set_property         (GObject         *object,
68                                                     guint            prop_id,
69                                                     const GValue    *value,
70                                                     GParamSpec      *pspec);
71 static void     gtk_container_get_property         (GObject         *object,
72                                                     guint            prop_id,
73                                                     GValue          *value,
74                                                     GParamSpec      *pspec);
75 static void     gtk_container_add_unimplemented    (GtkContainer      *container,
76                                                     GtkWidget         *widget);
77 static void     gtk_container_remove_unimplemented (GtkContainer      *container,
78                                                     GtkWidget         *widget);
79 static void     gtk_container_real_check_resize    (GtkContainer      *container);
80 static gboolean gtk_container_focus                (GtkWidget         *widget,
81                                                     GtkDirectionType   direction);
82 static void     gtk_container_real_set_focus_child (GtkContainer      *container,
83                                                     GtkWidget         *widget);
84 static GList *  gtk_container_focus_sort           (GtkContainer      *container,
85                                                     GList             *children,
86                                                     GtkDirectionType   direction);
87
88 static gboolean gtk_container_focus_move           (GtkContainer      *container,
89                                                     GList             *children,
90                                                     GtkDirectionType   direction);
91 static void     gtk_container_children_callback    (GtkWidget         *widget,
92                                                     gpointer           client_data);
93 static void     gtk_container_show_all             (GtkWidget         *widget);
94 static void     gtk_container_hide_all             (GtkWidget         *widget);
95 static gint     gtk_container_expose               (GtkWidget         *widget,
96                                                     GdkEventExpose    *event);
97 static void     gtk_container_map                  (GtkWidget         *widget);
98 static void     gtk_container_unmap                (GtkWidget         *widget);
99
100 static gchar* gtk_container_child_default_composite_name (GtkContainer *container,
101                                                           GtkWidget    *child);
102
103
104 /* --- variables --- */
105 static const gchar          *vadjustment_key = "gtk-vadjustment";
106 static guint                 vadjustment_key_id = 0;
107 static const gchar          *hadjustment_key = "gtk-hadjustment";
108 static guint                 hadjustment_key_id = 0;
109 static GSList               *container_resize_queue = NULL;
110 static guint                 container_signals[LAST_SIGNAL] = { 0 };
111 static GtkWidgetClass       *parent_class = NULL;
112 extern GParamSpecPool       *_gtk_widget_child_property_pool;
113 extern GObjectNotifyContext *_gtk_widget_child_property_notify_context;
114
115
116 /* --- functions --- */
117 GtkType
118 gtk_container_get_type (void)
119 {
120   static GtkType container_type = 0;
121
122   if (!container_type)
123     {
124       static GTypeInfo container_info = {
125         sizeof (GtkContainerClass),
126         (GBaseInitFunc) gtk_container_base_class_init,
127         (GBaseFinalizeFunc) gtk_container_base_class_finalize,
128         (GClassInitFunc) gtk_container_class_init,
129         NULL        /* class_destroy */,
130         NULL        /* class_data */,
131         sizeof (GtkContainer),
132         0           /* n_preallocs */,
133         (GInstanceInitFunc) gtk_container_init,
134         NULL,       /* value_table */
135       };
136
137       container_type = g_type_register_static (GTK_TYPE_WIDGET, "GtkContainer", &container_info, 0);
138     }
139
140   return container_type;
141 }
142
143 static void
144 gtk_container_base_class_init (GtkContainerClass *class)
145 {
146   /* reset instance specifc class fields that don't get inherited */
147   class->set_child_property = NULL;
148   class->get_child_property = NULL;
149 }
150
151 static void
152 gtk_container_base_class_finalize (GtkContainerClass *class)
153 {
154   GList *list, *node;
155
156   list = g_param_spec_pool_list_owned (_gtk_widget_child_property_pool, G_OBJECT_CLASS_TYPE (class));
157   for (node = list; node; node = node->next)
158     {
159       GParamSpec *pspec = node->data;
160
161       g_param_spec_pool_remove (_gtk_widget_child_property_pool, pspec);
162       PARAM_SPEC_SET_PARAM_ID (pspec, 0);
163       g_param_spec_unref (pspec);
164     }
165   g_list_free (list);
166 }
167
168 static void
169 gtk_container_class_init (GtkContainerClass *class)
170 {
171   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
172   GtkObjectClass *object_class = GTK_OBJECT_CLASS (class);
173   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
174
175   parent_class = g_type_class_peek_parent (class);
176
177   vadjustment_key_id = g_quark_from_static_string (vadjustment_key);
178   hadjustment_key_id = g_quark_from_static_string (hadjustment_key);
179   
180   gobject_class->set_property = gtk_container_set_property;
181   gobject_class->get_property = gtk_container_get_property;
182
183   object_class->destroy = gtk_container_destroy;
184
185   widget_class->show_all = gtk_container_show_all;
186   widget_class->hide_all = gtk_container_hide_all;
187   widget_class->expose_event = gtk_container_expose;
188   widget_class->map = gtk_container_map;
189   widget_class->unmap = gtk_container_unmap;
190   widget_class->focus = gtk_container_focus;
191   
192   class->add = gtk_container_add_unimplemented;
193   class->remove = gtk_container_remove_unimplemented;
194   class->check_resize = gtk_container_real_check_resize;
195   class->forall = NULL;
196   class->set_focus_child = gtk_container_real_set_focus_child;
197   class->child_type = NULL;
198   class->composite_name = gtk_container_child_default_composite_name;
199
200   g_object_class_install_property (gobject_class,
201                                    PROP_RESIZE_MODE,
202                                    g_param_spec_enum ("resize_mode",
203                                                       _("Resize mode"),
204                                                       _("Specify how resize events are handled"),
205                                                       GTK_TYPE_RESIZE_MODE,
206                                                       GTK_RESIZE_PARENT,
207                                                       G_PARAM_READWRITE));
208   g_object_class_install_property (gobject_class,
209                                    PROP_BORDER_WIDTH,
210                                    g_param_spec_uint ("border_width",
211                                                       _("Border width"),
212                                                       _("The width of the empty border outside the containers children."),
213                                                       0,
214                                                       G_MAXINT,
215                                                       0,
216                                                       G_PARAM_READWRITE));
217   g_object_class_install_property (gobject_class,
218                                    PROP_CHILD,
219                                    g_param_spec_object ("child",
220                                                       _("Child"),
221                                                       _("Can be used to add a new child to the container."),
222                                                       GTK_TYPE_WIDGET,
223                                                       G_PARAM_WRITABLE));
224   container_signals[ADD] =
225     gtk_signal_new ("add",
226                     GTK_RUN_FIRST,
227                     GTK_CLASS_TYPE (object_class),
228                     GTK_SIGNAL_OFFSET (GtkContainerClass, add),
229                     _gtk_marshal_VOID__OBJECT,
230                     GTK_TYPE_NONE, 1,
231                     GTK_TYPE_WIDGET);
232   container_signals[REMOVE] =
233     gtk_signal_new ("remove",
234                     GTK_RUN_FIRST,
235                     GTK_CLASS_TYPE (object_class),
236                     GTK_SIGNAL_OFFSET (GtkContainerClass, remove),
237                     _gtk_marshal_VOID__OBJECT,
238                     GTK_TYPE_NONE, 1,
239                     GTK_TYPE_WIDGET);
240   container_signals[CHECK_RESIZE] =
241     gtk_signal_new ("check_resize",
242                     GTK_RUN_LAST,
243                     GTK_CLASS_TYPE (object_class),
244                     GTK_SIGNAL_OFFSET (GtkContainerClass, check_resize),
245                     _gtk_marshal_VOID__VOID,
246                     GTK_TYPE_NONE, 0);
247   container_signals[SET_FOCUS_CHILD] =
248     gtk_signal_new ("set-focus-child",
249                     GTK_RUN_FIRST,
250                     GTK_CLASS_TYPE (object_class),
251                     GTK_SIGNAL_OFFSET (GtkContainerClass, set_focus_child),
252                     _gtk_marshal_VOID__OBJECT,
253                     GTK_TYPE_NONE, 1,
254                     GTK_TYPE_WIDGET);
255 }
256
257 /**
258  * gtk_container_child_type: 
259  * @container: a #GtkContainer.
260  *
261  * Returns the type of the children supported by the container.
262  *
263  * Note that this may return %GTK_TYPE_NONE to indicate that no more
264  * children can be added, e.g. for a #GtkPaned which already has two 
265  * children.
266  *
267  * Return value: a #GtkType.
268 GtkType
269 gtk_container_child_type (GtkContainer *container)
270 {
271   GtkType slot;
272   GtkContainerClass *class;
273
274   g_return_val_if_fail (GTK_IS_CONTAINER (container), 0);
275
276   class = GTK_CONTAINER_GET_CLASS (container);
277   if (class->child_type)
278     slot = class->child_type (container);
279   else
280     slot = GTK_TYPE_NONE;
281
282   return slot;
283 }
284
285 /* --- GtkContainer child property mechanism --- */
286 static inline void
287 container_get_child_property (GtkContainer *container,
288                               GtkWidget    *child,
289                               GParamSpec   *pspec,
290                               GValue       *value)
291 {
292   GtkContainerClass *class = g_type_class_peek (pspec->owner_type);
293   
294   class->get_child_property (container, child, PARAM_SPEC_PARAM_ID (pspec), value, pspec);
295 }
296
297 static inline void
298 container_set_child_property (GtkContainer       *container,
299                               GtkWidget          *child,
300                               GParamSpec         *pspec,
301                               const GValue       *value,
302                               GObjectNotifyQueue *nqueue)
303 {
304   GValue tmp_value = { 0, };
305   GtkContainerClass *class = g_type_class_peek (pspec->owner_type);
306
307   /* provide a copy to work from, convert (if necessary) and validate */
308   g_value_init (&tmp_value, G_PARAM_SPEC_VALUE_TYPE (pspec));
309   if (!g_value_transform (value, &tmp_value))
310     g_warning ("unable to set child property `%s' of type `%s' from value of type `%s'",
311                pspec->name,
312                g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec)),
313                G_VALUE_TYPE_NAME (value));
314   else if (g_param_value_validate (pspec, &tmp_value) && !(pspec->flags & G_PARAM_LAX_VALIDATION))
315     {
316       gchar *contents = g_strdup_value_contents (value);
317
318       g_warning ("value \"%s\" of type `%s' is invalid for property `%s' of type `%s'",
319                  contents,
320                  G_VALUE_TYPE_NAME (value),
321                  pspec->name,
322                  g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec)));
323       g_free (contents);
324     }
325   else
326     {
327       class->set_child_property (container, child, PARAM_SPEC_PARAM_ID (pspec), &tmp_value, pspec);
328       g_object_notify_queue_add (G_OBJECT (child), nqueue, pspec);
329     }
330   g_value_unset (&tmp_value);
331 }
332
333 void
334 gtk_container_child_get_valist (GtkContainer *container,
335                                 GtkWidget    *child,
336                                 const gchar  *first_property_name,
337                                 va_list       var_args)
338 {
339   const gchar *name;
340
341   g_return_if_fail (GTK_IS_CONTAINER (container));
342   g_return_if_fail (GTK_IS_WIDGET (child));
343   g_return_if_fail (child->parent == GTK_WIDGET (container));
344
345   g_object_ref (container);
346   g_object_ref (child);
347
348   name = first_property_name;
349   while (name)
350     {
351       GValue value = { 0, };
352       GParamSpec *pspec;
353       gchar *error;
354
355       pspec = g_param_spec_pool_lookup (_gtk_widget_child_property_pool,
356                                         name,
357                                         G_OBJECT_TYPE (container),
358                                         TRUE);
359       if (!pspec)
360         {
361           g_warning ("%s: container class `%s' has no child property named `%s'",
362                      G_STRLOC,
363                      G_OBJECT_TYPE_NAME (container),
364                      name);
365           break;
366         }
367       if (!(pspec->flags & G_PARAM_READABLE))
368         {
369           g_warning ("%s: child property `%s' of container class `%s' is not readable",
370                      G_STRLOC,
371                      pspec->name,
372                      G_OBJECT_TYPE_NAME (container));
373           break;
374         }
375       g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec));
376       container_get_child_property (container, child, pspec, &value);
377       G_VALUE_LCOPY (&value, var_args, 0, &error);
378       if (error)
379         {
380           g_warning ("%s: %s", G_STRLOC, error);
381           g_free (error);
382           g_value_unset (&value);
383           break;
384         }
385       g_value_unset (&value);
386       name = va_arg (var_args, gchar*);
387     }
388
389   g_object_unref (child);
390   g_object_unref (container);
391 }
392
393 void
394 gtk_container_child_get_property (GtkContainer *container,
395                                   GtkWidget    *child,
396                                   const gchar  *property_name,
397                                   GValue       *value)
398 {
399   GParamSpec *pspec;
400
401   g_return_if_fail (GTK_IS_CONTAINER (container));
402   g_return_if_fail (GTK_IS_WIDGET (child));
403   g_return_if_fail (child->parent == GTK_WIDGET (container));
404   g_return_if_fail (property_name != NULL);
405   g_return_if_fail (G_IS_VALUE (value));
406   
407   g_object_ref (container);
408   g_object_ref (child);
409   pspec = g_param_spec_pool_lookup (_gtk_widget_child_property_pool, property_name,
410                                     G_OBJECT_TYPE (container), TRUE);
411   if (!pspec)
412     g_warning ("%s: container class `%s' has no child property named `%s'",
413                G_STRLOC,
414                G_OBJECT_TYPE_NAME (container),
415                property_name);
416   else if (!(pspec->flags & G_PARAM_READABLE))
417     g_warning ("%s: child property `%s' of container class `%s' is not readable",
418                G_STRLOC,
419                pspec->name,
420                G_OBJECT_TYPE_NAME (container));
421   else
422     {
423       GValue *prop_value, tmp_value = { 0, };
424
425       /* auto-conversion of the callers value type
426        */
427       if (G_VALUE_TYPE (value) == G_PARAM_SPEC_VALUE_TYPE (pspec))
428         {
429           g_value_reset (value);
430           prop_value = value;
431         }
432       else if (!g_value_type_transformable (G_PARAM_SPEC_VALUE_TYPE (pspec), G_VALUE_TYPE (value)))
433         {
434           g_warning ("can't retrieve child property `%s' of type `%s' as value of type `%s'",
435                      pspec->name,
436                      g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec)),
437                      G_VALUE_TYPE_NAME (value));
438           g_object_unref (child);
439           g_object_unref (container);
440           return;
441         }
442       else
443         {
444           g_value_init (&tmp_value, G_PARAM_SPEC_VALUE_TYPE (pspec));
445           prop_value = &tmp_value;
446         }
447       container_get_child_property (container, child, pspec, prop_value);
448       if (prop_value != value)
449         {
450           g_value_transform (prop_value, value);
451           g_value_unset (&tmp_value);
452         }
453     }
454   g_object_unref (child);
455   g_object_unref (container);
456 }
457
458 void
459 gtk_container_child_set_valist (GtkContainer *container,
460                                 GtkWidget    *child,
461                                 const gchar  *first_property_name,
462                                 va_list       var_args)
463 {
464   GObject *object;
465   GObjectNotifyQueue *nqueue;
466   const gchar *name;
467
468   g_return_if_fail (GTK_IS_CONTAINER (container));
469   g_return_if_fail (GTK_IS_WIDGET (child));
470   g_return_if_fail (child->parent == GTK_WIDGET (container));
471
472   g_object_ref (container);
473   g_object_ref (child);
474
475   object = G_OBJECT (container);
476   nqueue = g_object_notify_queue_freeze (G_OBJECT (child), _gtk_widget_child_property_notify_context);
477   name = first_property_name;
478   while (name)
479     {
480       GValue value = { 0, };
481       gchar *error = NULL;
482       GParamSpec *pspec = g_param_spec_pool_lookup (_gtk_widget_child_property_pool,
483                                                     name,
484                                                     G_OBJECT_TYPE (container),
485                                                     TRUE);
486       if (!pspec)
487         {
488           g_warning ("%s: container class `%s' has no child property named `%s'",
489                      G_STRLOC,
490                      G_OBJECT_TYPE_NAME (container),
491                      name);
492           break;
493         }
494       if (!(pspec->flags & G_PARAM_WRITABLE))
495         {
496           g_warning ("%s: child property `%s' of container class `%s' is not writable",
497                      G_STRLOC,
498                      pspec->name,
499                      G_OBJECT_TYPE_NAME (container));
500           break;
501         }
502       g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec));
503       G_VALUE_COLLECT (&value, var_args, 0, &error);
504       if (error)
505         {
506           g_warning ("%s: %s", G_STRLOC, error);
507           g_free (error);
508
509           /* we purposely leak the value here, it might not be
510            * in a sane state if an error condition occoured
511            */
512           break;
513         }
514       container_set_child_property (container, child, pspec, &value, nqueue);
515       g_value_unset (&value);
516       name = va_arg (var_args, gchar*);
517     }
518   g_object_notify_queue_thaw (G_OBJECT (child), nqueue);
519
520   g_object_unref (container);
521   g_object_unref (child);
522 }
523
524 void
525 gtk_container_child_set_property (GtkContainer *container,
526                                   GtkWidget    *child,
527                                   const gchar  *property_name,
528                                   const GValue *value)
529 {
530   GObject *object;
531   GObjectNotifyQueue *nqueue;
532   GParamSpec *pspec;
533
534   g_return_if_fail (GTK_IS_CONTAINER (container));
535   g_return_if_fail (GTK_IS_WIDGET (child));
536   g_return_if_fail (child->parent == GTK_WIDGET (container));
537   g_return_if_fail (property_name != NULL);
538   g_return_if_fail (G_IS_VALUE (value));
539   
540   g_object_ref (container);
541   g_object_ref (child);
542
543   object = G_OBJECT (container);
544   nqueue = g_object_notify_queue_freeze (G_OBJECT (child), _gtk_widget_child_property_notify_context);
545   pspec = g_param_spec_pool_lookup (_gtk_widget_child_property_pool, property_name,
546                                     G_OBJECT_TYPE (container), TRUE);
547   if (!pspec)
548     g_warning ("%s: container class `%s' has no child property named `%s'",
549                G_STRLOC,
550                G_OBJECT_TYPE_NAME (container),
551                property_name);
552   else if (!(pspec->flags & G_PARAM_WRITABLE))
553     g_warning ("%s: child property `%s' of container class `%s' is not writable",
554                G_STRLOC,
555                pspec->name,
556                G_OBJECT_TYPE_NAME (container));
557   else
558     {
559       container_set_child_property (container, child, pspec, value, nqueue);
560     }
561   g_object_notify_queue_thaw (G_OBJECT (child), nqueue);
562   g_object_unref (container);
563   g_object_unref (child);
564 }
565
566 void
567 gtk_container_add_with_properties (GtkContainer *container,
568                                    GtkWidget    *widget,
569                                    const gchar  *first_prop_name,
570                                    ...)
571 {
572   g_return_if_fail (GTK_IS_CONTAINER (container));
573   g_return_if_fail (GTK_IS_WIDGET (widget));
574   g_return_if_fail (widget->parent == NULL);
575
576   gtk_widget_ref (GTK_WIDGET (container));
577   gtk_widget_ref (widget);
578   gtk_widget_freeze_child_notify (widget);
579
580   gtk_signal_emit (GTK_OBJECT (container), container_signals[ADD], widget);
581   if (widget->parent)
582     {
583       va_list var_args;
584
585       va_start (var_args, first_prop_name);
586       gtk_container_child_set_valist (container, widget, first_prop_name, var_args);
587       va_end (var_args);
588     }
589
590   gtk_widget_thaw_child_notify (widget);
591   gtk_widget_unref (widget);
592   gtk_widget_unref (GTK_WIDGET (container));
593 }
594
595 void
596 gtk_container_child_set (GtkContainer      *container,
597                          GtkWidget         *child,
598                          const gchar       *first_prop_name,
599                          ...)
600 {
601   va_list var_args;
602   
603   g_return_if_fail (GTK_IS_CONTAINER (container));
604   g_return_if_fail (GTK_IS_WIDGET (child));
605   g_return_if_fail (child->parent == GTK_WIDGET (container));
606
607   va_start (var_args, first_prop_name);
608   gtk_container_child_set_valist (container, child, first_prop_name, var_args);
609   va_end (var_args);
610 }
611
612 void
613 gtk_container_child_get (GtkContainer      *container,
614                          GtkWidget         *child,
615                          const gchar       *first_prop_name,
616                          ...)
617 {
618   va_list var_args;
619   
620   g_return_if_fail (container != NULL);
621   g_return_if_fail (GTK_IS_CONTAINER (container));
622   g_return_if_fail (child != NULL);
623   g_return_if_fail (GTK_IS_WIDGET (child));
624   g_return_if_fail (child->parent == GTK_WIDGET (container));
625
626   va_start (var_args, first_prop_name);
627   gtk_container_child_get_valist (container, child, first_prop_name, var_args);
628   va_end (var_args);
629 }
630
631 void
632 gtk_container_class_install_child_property (GtkContainerClass *class,
633                                             guint              property_id,
634                                             GParamSpec        *pspec)
635 {
636   g_return_if_fail (GTK_IS_CONTAINER_CLASS (class));
637   g_return_if_fail (G_IS_PARAM_SPEC (pspec));
638   if (pspec->flags & G_PARAM_WRITABLE)
639     g_return_if_fail (class->set_child_property != NULL);
640   if (pspec->flags & G_PARAM_READABLE)
641     g_return_if_fail (class->get_child_property != NULL);
642   g_return_if_fail (property_id > 0);
643   g_return_if_fail (PARAM_SPEC_PARAM_ID (pspec) == 0);  /* paranoid */
644   if (pspec->flags & (G_PARAM_CONSTRUCT | G_PARAM_CONSTRUCT_ONLY))
645     g_return_if_fail ((pspec->flags & (G_PARAM_CONSTRUCT | G_PARAM_CONSTRUCT_ONLY)) == 0);
646
647   if (g_param_spec_pool_lookup (_gtk_widget_child_property_pool, pspec->name, G_OBJECT_CLASS_TYPE (class), FALSE))
648     {
649       g_warning (G_STRLOC ": class `%s' already contains a property named `%s'",
650                  G_OBJECT_CLASS_NAME (class),
651                  pspec->name);
652       return;
653     }
654   g_param_spec_ref (pspec);
655   g_param_spec_sink (pspec);
656   PARAM_SPEC_SET_PARAM_ID (pspec, property_id);
657   g_param_spec_pool_insert (_gtk_widget_child_property_pool, pspec, G_OBJECT_CLASS_TYPE (class));
658 }
659
660 GParamSpec*
661 gtk_container_class_find_child_property (GObjectClass *class,
662                                          const gchar  *property_name)
663 {
664   g_return_val_if_fail (GTK_IS_CONTAINER_CLASS (class), NULL);
665   g_return_val_if_fail (property_name != NULL, NULL);
666
667   return g_param_spec_pool_lookup (_gtk_widget_child_property_pool,
668                                    property_name,
669                                    G_OBJECT_CLASS_TYPE (class),
670                                    TRUE);
671 }
672
673 GParamSpec** /* free result */
674 gtk_container_class_list_child_properties (GObjectClass *class,
675                                            guint        *n_properties)
676 {
677   GParamSpec **pspecs;
678   guint n;
679
680   g_return_val_if_fail (GTK_IS_CONTAINER_CLASS (class), NULL);
681
682   pspecs = g_param_spec_pool_list (_gtk_widget_child_property_pool,
683                                    G_OBJECT_CLASS_TYPE (class),
684                                    &n);
685   if (n_properties)
686     *n_properties = n;
687
688   return pspecs;
689 }
690
691 static void
692 gtk_container_add_unimplemented (GtkContainer     *container,
693                                  GtkWidget        *widget)
694 {
695   g_warning ("GtkContainerClass::add not implemented for `%s'", gtk_type_name (GTK_OBJECT_TYPE (container)));
696 }
697
698 static void
699 gtk_container_remove_unimplemented (GtkContainer     *container,
700                                     GtkWidget        *widget)
701 {
702   g_warning ("GtkContainerClass::remove not implemented for `%s'", gtk_type_name (GTK_OBJECT_TYPE (container)));
703 }
704
705 static void
706 gtk_container_init (GtkContainer *container)
707 {
708   container->focus_child = NULL;
709   container->border_width = 0;
710   container->need_resize = FALSE;
711   container->resize_mode = GTK_RESIZE_PARENT;
712   container->reallocate_redraws = FALSE;
713 }
714
715 static void
716 gtk_container_destroy (GtkObject *object)
717 {
718   GtkContainer *container;
719
720   g_return_if_fail (object != NULL);
721   g_return_if_fail (GTK_IS_CONTAINER (object));
722
723   container = GTK_CONTAINER (object);
724   
725   if (GTK_CONTAINER_RESIZE_PENDING (container))
726     _gtk_container_dequeue_resize_handler (container);
727
728   /* do this before walking child widgets, to avoid
729    * removing children from focus chain one by one.
730    */
731   if (container->has_focus_chain)
732     gtk_container_unset_focus_chain (container);
733   
734   gtk_container_foreach (container, (GtkCallback) gtk_widget_destroy, NULL);
735   
736   if (GTK_OBJECT_CLASS (parent_class)->destroy)
737     (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
738 }
739
740 static void
741 gtk_container_set_property (GObject         *object,
742                             guint            prop_id,
743                             const GValue    *value,
744                             GParamSpec      *pspec)
745 {
746   GtkContainer *container;
747
748   container = GTK_CONTAINER (object);
749
750   switch (prop_id)
751     {
752     case PROP_BORDER_WIDTH:
753       gtk_container_set_border_width (container, g_value_get_uint (value));
754       break;
755     case PROP_RESIZE_MODE:
756       gtk_container_set_resize_mode (container, g_value_get_enum (value));
757       break;
758     case PROP_CHILD:
759       gtk_container_add (container, GTK_WIDGET (g_value_get_object (value)));
760       break;
761     default:
762       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
763       break;
764     }
765 }
766
767 static void
768 gtk_container_get_property (GObject         *object,
769                             guint            prop_id,
770                             GValue          *value,
771                             GParamSpec      *pspec)
772 {
773   GtkContainer *container;
774
775   container = GTK_CONTAINER (object);
776   
777   switch (prop_id)
778     {
779     case PROP_BORDER_WIDTH:
780       g_value_set_uint (value, container->border_width);
781       break;
782     case PROP_RESIZE_MODE:
783       g_value_set_enum (value, container->resize_mode);
784       break;
785     default:
786       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
787       break;
788     }
789 }
790
791 /**
792  * gtk_container_set_border_width:
793  * @container: a #GtkContainer
794  * @border_width: amount of blank space to leave <emphasis>outside</emphasis> the container.
795  *   Valid values are in the range 0-65535 pixels.
796  *
797  * Sets the border width of the container.
798  *
799  * The border width of a container is the amount of space to leave
800  * around the outside of the container. The only exception to this is
801  * #GtkWindow; because toplevel windows can't leave space outside,
802  * they leave the space inside. The border is added on all sides of
803  * the container. To add space to only one side, one approach is to
804  * create a #GtkAlignment widget, call gtk_widget_set_usize() to give
805  * it a size, and place it on the side of the container as a spacer.
806  **/
807 void
808 gtk_container_set_border_width (GtkContainer *container,
809                                 guint         border_width)
810 {
811   g_return_if_fail (container != NULL);
812   g_return_if_fail (GTK_IS_CONTAINER (container));
813
814   if (container->border_width != border_width)
815     {
816       container->border_width = border_width;
817       g_object_notify (G_OBJECT (container), "border_width");
818       
819       if (GTK_WIDGET_REALIZED (container))
820         gtk_widget_queue_resize (GTK_WIDGET (container));
821     }
822 }
823
824 /**
825  * gtk_container_get_border_width:
826  * @container: a #GtkContainer
827  * 
828  * Retrieves the border width of the container. See
829  * gtk_container_set_border_width().
830  *
831  * Return value: the current border width
832  **/
833 guint
834 gtk_container_get_border_width (GtkContainer *container)
835 {
836   g_return_val_if_fail (GTK_IS_CONTAINER (container), 0);
837
838   return container->border_width;
839 }
840
841 /**
842  * gtk_container_add:
843  * @container: a #GtkContainer
844  * @widget: a widget to be placed inside @container
845  * 
846  * Adds @widget to @container. Typically used for simple containers
847  * such as #GtkWindow, #GtkFrame, or #GtkButton; for more complicated
848  * layout containers such as #GtkBox or #GtkTable, this function will
849  * pick default packing parameters that may not be correct.  So
850  * consider functions such as gtk_box_pack_start() and
851  * gtk_table_attach() as an alternative to gtk_container_add() in
852  * those cases. A widget may be added to only one container at a time;
853  * you can't place the same widget inside two different containers.
854  **/
855 void
856 gtk_container_add (GtkContainer *container,
857                    GtkWidget    *widget)
858 {
859   g_return_if_fail (container != NULL);
860   g_return_if_fail (GTK_IS_CONTAINER (container));
861   g_return_if_fail (widget != NULL);
862   g_return_if_fail (GTK_IS_WIDGET (widget));
863
864   if (widget->parent != NULL)
865     {
866       g_warning ("Attempting to add a widget with type %s to a container of "
867                  "type %s, but the widget is already inside a container of type %s, "
868                  "the GTK+ FAQ at http://www.gtk.org/faq/ explains how to reparent a widget.",
869                  g_type_name (G_OBJECT_TYPE (widget)),
870                  g_type_name (G_OBJECT_TYPE (container)),
871                  g_type_name (G_OBJECT_TYPE (widget->parent)));
872       return;
873     }
874
875   gtk_signal_emit (GTK_OBJECT (container), container_signals[ADD], widget);
876 }
877
878 /**
879  * gtk_container_remove:
880  * @container: a #GtkContainer
881  * @widget: a current child of @container
882  * 
883  * Removes @widget from @container. @widget must be inside @container.
884  * Note that @container will own a reference to @widget, and that this
885  * may be the last reference held; so removing a widget from its
886  * container can destroy that widget. If you want to use @widget
887  * again, you need to add a reference to it while it's not inside
888  * a container, using g_object_ref().
889  **/
890 void
891 gtk_container_remove (GtkContainer *container,
892                       GtkWidget    *widget)
893 {
894   g_return_if_fail (container != NULL);
895   g_return_if_fail (GTK_IS_CONTAINER (container));
896   g_return_if_fail (widget != NULL);
897   g_return_if_fail (GTK_IS_WIDGET (widget));
898   g_return_if_fail (widget->parent == GTK_WIDGET (container));
899   
900   gtk_signal_emit (GTK_OBJECT (container), container_signals[REMOVE], widget);
901 }
902
903 void
904 _gtk_container_dequeue_resize_handler (GtkContainer *container)
905 {
906   g_return_if_fail (GTK_IS_CONTAINER (container));
907   g_return_if_fail (GTK_CONTAINER_RESIZE_PENDING (container));
908
909   container_resize_queue = g_slist_remove (container_resize_queue, container);
910   GTK_PRIVATE_UNSET_FLAG (container, GTK_RESIZE_PENDING);
911 }
912
913 /**
914  * gtk_container_set_resize_mode:
915  * @container: a #GtkContainer.
916  * @resize_mode: the new resize mode.
917  * 
918  * Sets the resize mode for the container.
919  *
920  * The resize mode of a container determines whether a resize request 
921  * will be passed to the container's parent, queued for later execution
922  * or executed immediately.
923  **/
924 void
925 gtk_container_set_resize_mode (GtkContainer  *container,
926                                GtkResizeMode  resize_mode)
927 {
928   g_return_if_fail (container != NULL);
929   g_return_if_fail (GTK_IS_CONTAINER (container));
930   g_return_if_fail (resize_mode <= GTK_RESIZE_IMMEDIATE);
931   
932   if (GTK_WIDGET_TOPLEVEL (container) &&
933       resize_mode == GTK_RESIZE_PARENT)
934     {
935       resize_mode = GTK_RESIZE_QUEUE;
936       g_object_notify (G_OBJECT (container), "resize_mode");
937     }
938   
939   if (container->resize_mode != resize_mode)
940     {
941       container->resize_mode = resize_mode;
942       
943       gtk_widget_queue_resize (GTK_WIDGET (container));
944       g_object_notify (G_OBJECT (container), "resize_mode");
945     }
946 }
947
948 /**
949  * gtk_container_get_resize_mode:
950  * @container: a #GtkContainer
951  * 
952  * Returns the resize mode for the container. See
953  * gtk_container_set_resize_mode ().
954  *
955  * Return value: the current resize mode
956  **/
957 GtkResizeMode
958 gtk_container_get_resize_mode (GtkContainer *container)
959 {
960   g_return_val_if_fail (GTK_IS_CONTAINER (container), GTK_RESIZE_PARENT);
961
962   return container->resize_mode;
963 }
964
965 /**
966  * gtk_container_set_reallocate_redraws:
967  * @container: a #GtkContainer.
968  * @needs_redraws: the new value for the container's @reallocate_redraws flag.
969  *
970  * Sets the @reallocate_redraws flag of the container to the given value.
971  * 
972  * Containers requesting reallocation redraws get automatically
973  * redrawn if any of their children changed allocation. 
974  **/ 
975 void
976 gtk_container_set_reallocate_redraws (GtkContainer *container,
977                                       gboolean      needs_redraws)
978 {
979   g_return_if_fail (GTK_IS_CONTAINER (container));
980
981   container->reallocate_redraws = needs_redraws ? TRUE : FALSE;
982 }
983
984 static GtkContainer*
985 gtk_container_get_resize_container (GtkContainer *container)
986 {
987   GtkWidget *widget;
988
989   widget = GTK_WIDGET (container);
990
991   while (widget->parent)
992     {
993       widget = widget->parent;
994       if (GTK_IS_RESIZE_CONTAINER (widget))
995         break;
996     }
997
998   return GTK_IS_RESIZE_CONTAINER (widget) ? (GtkContainer*) widget : NULL;
999 }
1000
1001 static gboolean
1002 gtk_container_idle_sizer (gpointer data)
1003 {
1004   GDK_THREADS_ENTER ();
1005
1006   /* we may be invoked with a container_resize_queue of NULL, because
1007    * queue_resize could have been adding an extra idle function while
1008    * the queue still got processed. we better just ignore such case
1009    * than trying to explicitely work around them with some extra flags,
1010    * since it doesn't cause any actual harm.
1011    */
1012   while (container_resize_queue)
1013     {
1014       GSList *slist;
1015       GtkWidget *widget;
1016
1017       slist = container_resize_queue;
1018       container_resize_queue = slist->next;
1019       widget = slist->data;
1020       g_slist_free_1 (slist);
1021
1022       GTK_PRIVATE_UNSET_FLAG (widget, GTK_RESIZE_PENDING);
1023       gtk_container_check_resize (GTK_CONTAINER (widget));
1024     }
1025
1026   gdk_window_process_all_updates ();
1027
1028   GDK_THREADS_LEAVE ();
1029
1030   return FALSE;
1031 }
1032
1033 void
1034 _gtk_container_queue_resize (GtkContainer *container)
1035 {
1036   GtkContainer *resize_container;
1037   
1038   g_return_if_fail (container != NULL);
1039   g_return_if_fail (GTK_IS_CONTAINER (container));
1040
1041   resize_container = gtk_container_get_resize_container (container);
1042   
1043   if (resize_container)
1044     {
1045       GtkWidget *widget = GTK_WIDGET (container);
1046       
1047       while (!GTK_WIDGET_ALLOC_NEEDED (widget) || !GTK_WIDGET_REQUEST_NEEDED (widget))
1048         {
1049           GTK_PRIVATE_SET_FLAG (widget, GTK_ALLOC_NEEDED);
1050           GTK_PRIVATE_SET_FLAG (widget, GTK_REQUEST_NEEDED);
1051           if (widget == GTK_WIDGET (resize_container))
1052             break;
1053           
1054           widget = widget->parent;
1055         }
1056       
1057       if (GTK_WIDGET_VISIBLE (resize_container) &&
1058           (GTK_WIDGET_TOPLEVEL (resize_container) || GTK_WIDGET_DRAWABLE (resize_container)))
1059         {
1060           switch (resize_container->resize_mode)
1061             {
1062             case GTK_RESIZE_QUEUE:
1063               if (!GTK_CONTAINER_RESIZE_PENDING (resize_container))
1064                 {
1065                   GTK_PRIVATE_SET_FLAG (resize_container, GTK_RESIZE_PENDING);
1066                   if (container_resize_queue == NULL)
1067                     gtk_idle_add_priority (GTK_PRIORITY_RESIZE,
1068                                            gtk_container_idle_sizer,
1069                                            NULL);
1070                   container_resize_queue = g_slist_prepend (container_resize_queue, resize_container);
1071                 }
1072               break;
1073
1074             case GTK_RESIZE_IMMEDIATE:
1075               gtk_container_check_resize (resize_container);
1076               break;
1077
1078             case GTK_RESIZE_PARENT:
1079               /* Ignore, should not be reached */
1080               break;
1081             }
1082         }
1083       else
1084         {
1085           /* we need to let hidden resize containers know that something
1086            * changed while they where hidden (currently only evaluated by
1087            * toplevels).
1088            */
1089           resize_container->need_resize = TRUE;
1090         }
1091     }
1092 }
1093
1094 void
1095 gtk_container_check_resize (GtkContainer *container)
1096 {
1097   g_return_if_fail (container != NULL);
1098   g_return_if_fail (GTK_IS_CONTAINER (container));
1099   
1100   gtk_signal_emit (GTK_OBJECT (container), container_signals[CHECK_RESIZE]);
1101 }
1102
1103 static void
1104 gtk_container_real_check_resize (GtkContainer *container)
1105 {
1106   GtkWidget *widget;
1107   GtkRequisition requisition;
1108   
1109   g_return_if_fail (container != NULL);
1110   g_return_if_fail (GTK_IS_CONTAINER (container));
1111   
1112   widget = GTK_WIDGET (container);
1113   
1114   gtk_widget_size_request (widget, &requisition);
1115   
1116   if (requisition.width > widget->allocation.width ||
1117       requisition.height > widget->allocation.height)
1118     {
1119       if (GTK_IS_RESIZE_CONTAINER (container))
1120         gtk_widget_size_allocate (GTK_WIDGET (container),
1121                                   &GTK_WIDGET (container)->allocation);
1122       else
1123         gtk_widget_queue_resize (widget);
1124     }
1125   else
1126     {
1127       gtk_container_resize_children (container);
1128     }
1129 }
1130
1131 /* The container hasn't changed size but one of its children
1132  *  queued a resize request. Which means that the allocation
1133  *  is not sufficient for the requisition of some child.
1134  *  We've already performed a size request at this point,
1135  *  so we simply need to reallocate and let the allocation
1136  *  trickle down via GTK_WIDGET_ALLOC_NEEDED flags. 
1137  */
1138 void
1139 gtk_container_resize_children (GtkContainer *container)
1140 {
1141   GtkWidget *widget;
1142   
1143   /* resizing invariants:
1144    * toplevels have *always* resize_mode != GTK_RESIZE_PARENT set.
1145    * containers that have an idle sizer pending must be flagged with
1146    * RESIZE_PENDING.
1147    */
1148   g_return_if_fail (container != NULL);
1149   g_return_if_fail (GTK_IS_CONTAINER (container));
1150
1151   widget = GTK_WIDGET (container);
1152   gtk_widget_size_allocate (widget, &widget->allocation);
1153 }
1154
1155 /**
1156  * gtk_container_forall:
1157  * @container: a #GtkContainer
1158  * @callback: a callback
1159  * @callback_data: callback user data
1160  * 
1161  * Invokes @callback on each child of @container, including children
1162  * that are considered "internal" (implementation details of the
1163  * container). "Internal" children generally weren't added by the user
1164  * of the container, but were added by the container implementation
1165  * itself.  Most applications should use gtk_container_foreach(),
1166  * rather than gtk_container_forall().
1167  **/
1168 void
1169 gtk_container_forall (GtkContainer *container,
1170                       GtkCallback   callback,
1171                       gpointer      callback_data)
1172 {
1173   GtkContainerClass *class;
1174
1175   g_return_if_fail (container != NULL);
1176   g_return_if_fail (GTK_IS_CONTAINER (container));
1177   g_return_if_fail (callback != NULL);
1178
1179   class = GTK_CONTAINER_GET_CLASS (container);
1180
1181   if (class->forall)
1182     class->forall (container, TRUE, callback, callback_data);
1183 }
1184
1185 /**
1186  * gtk_container_foreach:
1187  * @container: a #GtkContainer
1188  * @callback: a callback
1189  * @callback_data: callback user data
1190  * 
1191  * Invokes @callback on each non-internal child of @container.  See
1192  * gtk_container_forall() for details on what constitutes an
1193  * "internal" child.  Most applications should use
1194  * gtk_container_foreach(), rather than gtk_container_forall().
1195  **/
1196 void
1197 gtk_container_foreach (GtkContainer *container,
1198                        GtkCallback   callback,
1199                        gpointer      callback_data)
1200 {
1201   GtkContainerClass *class;
1202   
1203   g_return_if_fail (container != NULL);
1204   g_return_if_fail (GTK_IS_CONTAINER (container));
1205   g_return_if_fail (callback != NULL);
1206
1207   class = GTK_CONTAINER_GET_CLASS (container);
1208
1209   if (class->forall)
1210     class->forall (container, FALSE, callback, callback_data);
1211 }
1212
1213 typedef struct _GtkForeachData  GtkForeachData;
1214 struct _GtkForeachData
1215 {
1216   GtkObject         *container;
1217   GtkCallbackMarshal callback;
1218   gpointer           callback_data;
1219 };
1220
1221 static void
1222 gtk_container_foreach_unmarshal (GtkWidget *child,
1223                                  gpointer data)
1224 {
1225   GtkForeachData *fdata = (GtkForeachData*) data;
1226   GtkArg args[2];
1227   
1228   /* first argument */
1229   args[0].name = NULL;
1230   args[0].type = GTK_OBJECT_TYPE (child);
1231   GTK_VALUE_OBJECT (args[0]) = GTK_OBJECT (child);
1232   
1233   /* location for return value */
1234   args[1].name = NULL;
1235   args[1].type = GTK_TYPE_NONE;
1236   
1237   fdata->callback (fdata->container, fdata->callback_data, 1, args);
1238 }
1239
1240 void
1241 gtk_container_foreach_full (GtkContainer       *container,
1242                             GtkCallback         callback,
1243                             GtkCallbackMarshal  marshal,
1244                             gpointer            callback_data,
1245                             GtkDestroyNotify    notify)
1246 {
1247   g_return_if_fail (container != NULL);
1248   g_return_if_fail (GTK_IS_CONTAINER (container));
1249
1250   if (marshal)
1251     {
1252       GtkForeachData fdata;
1253   
1254       fdata.container     = GTK_OBJECT (container);
1255       fdata.callback      = marshal;
1256       fdata.callback_data = callback_data;
1257
1258       gtk_container_foreach (container, gtk_container_foreach_unmarshal, &fdata);
1259     }
1260   else
1261     {
1262       g_return_if_fail (callback != NULL);
1263
1264       gtk_container_foreach (container, callback, &callback_data);
1265     }
1266
1267   if (notify)
1268     notify (callback_data);
1269 }
1270
1271 void
1272 gtk_container_set_focus_child (GtkContainer *container,
1273                                GtkWidget    *widget)
1274 {
1275   g_return_if_fail (container != NULL);
1276   g_return_if_fail (GTK_IS_CONTAINER (container));
1277   if (widget)
1278     g_return_if_fail (GTK_IS_WIDGET (widget));
1279
1280   gtk_signal_emit (GTK_OBJECT (container), container_signals[SET_FOCUS_CHILD], widget);
1281 }
1282
1283 /**
1284  * gtk_container_get_children:
1285  * @container: a #GtkContainer.
1286  * 
1287  * Returns the the container's non-internal children. See
1288  * gtk_container_forall() for details on what constitutes an "internal" child. 
1289  *
1290  * Return value: a newly-allocated list of the container's non-internal children.
1291  **/
1292 GList*
1293 gtk_container_get_children (GtkContainer *container)
1294 {
1295   GList *children;
1296
1297   children = NULL;
1298
1299   gtk_container_foreach (container,
1300                          gtk_container_children_callback,
1301                          &children);
1302
1303   return g_list_reverse (children);
1304 }
1305
1306 static void
1307 gtk_container_child_position_callback (GtkWidget *widget,
1308                                        gpointer   client_data)
1309 {
1310   struct {
1311     GtkWidget *child;
1312     guint i;
1313     guint index;
1314   } *data = client_data;
1315
1316   data->i++;
1317   if (data->child == widget)
1318     data->index = data->i;
1319 }
1320
1321 static gchar*
1322 gtk_container_child_default_composite_name (GtkContainer *container,
1323                                             GtkWidget    *child)
1324 {
1325   struct {
1326     GtkWidget *child;
1327     guint i;
1328     guint index;
1329   } data;
1330   gchar *name;
1331
1332   /* fallback implementation */
1333   data.child = child;
1334   data.i = 0;
1335   data.index = 0;
1336   gtk_container_forall (container,
1337                         gtk_container_child_position_callback,
1338                         &data);
1339   
1340   name = g_strdup_printf ("%s-%u",
1341                           gtk_type_name (GTK_OBJECT_TYPE (child)),
1342                           data.index);
1343
1344   return name;
1345 }
1346
1347 gchar*
1348 _gtk_container_child_composite_name (GtkContainer *container,
1349                                     GtkWidget    *child)
1350 {
1351   g_return_val_if_fail (container != NULL, NULL);
1352   g_return_val_if_fail (GTK_IS_CONTAINER (container), NULL);
1353   g_return_val_if_fail (child != NULL, NULL);
1354   g_return_val_if_fail (GTK_IS_WIDGET (child), NULL);
1355   g_return_val_if_fail (child->parent == GTK_WIDGET (container), NULL);
1356
1357   if (GTK_WIDGET_COMPOSITE_CHILD (child))
1358     {
1359       static GQuark quark_composite_name = 0;
1360       gchar *name;
1361
1362       if (!quark_composite_name)
1363         quark_composite_name = g_quark_from_static_string ("gtk-composite-name");
1364
1365       name = gtk_object_get_data_by_id (GTK_OBJECT (child), quark_composite_name);
1366       if (!name)
1367         {
1368           GtkContainerClass *class;
1369
1370           class = GTK_CONTAINER_GET_CLASS (container);
1371           if (class->composite_name)
1372             name = class->composite_name (container, child);
1373         }
1374       else
1375         name = g_strdup (name);
1376
1377       return name;
1378     }
1379   
1380   return NULL;
1381 }
1382
1383 static void
1384 gtk_container_real_set_focus_child (GtkContainer     *container,
1385                                     GtkWidget        *child)
1386 {
1387   g_return_if_fail (container != NULL);
1388   g_return_if_fail (GTK_IS_CONTAINER (container));
1389   if (child)
1390     g_return_if_fail (GTK_IS_WIDGET (child));
1391
1392   if (child != container->focus_child)
1393     {
1394       if (container->focus_child)
1395         gtk_widget_unref (container->focus_child);
1396       container->focus_child = child;
1397       if (container->focus_child)
1398         gtk_widget_ref (container->focus_child);
1399     }
1400
1401
1402   /* check for h/v adjustments
1403    */
1404   if (container->focus_child)
1405     {
1406       GtkAdjustment *adjustment;
1407       
1408       adjustment = gtk_object_get_data_by_id (GTK_OBJECT (container), vadjustment_key_id);
1409       if (adjustment)
1410         gtk_adjustment_clamp_page (adjustment,
1411                                    container->focus_child->allocation.y,
1412                                    (container->focus_child->allocation.y +
1413                                     container->focus_child->allocation.height));
1414
1415       adjustment = gtk_object_get_data_by_id (GTK_OBJECT (container), hadjustment_key_id);
1416       if (adjustment)
1417         gtk_adjustment_clamp_page (adjustment,
1418                                    container->focus_child->allocation.x,
1419                                    (container->focus_child->allocation.x +
1420                                     container->focus_child->allocation.width));
1421     }
1422 }
1423
1424 static GList*
1425 get_focus_chain (GtkContainer *container)
1426 {
1427   return g_object_get_data (G_OBJECT (container), "gtk-container-focus-chain");
1428 }
1429
1430 static gboolean
1431 gtk_container_focus (GtkWidget        *widget,
1432                      GtkDirectionType  direction)
1433 {
1434   GList *children;
1435   GList *sorted_children;
1436   gint return_val;
1437   GtkContainer *container;
1438
1439   g_return_val_if_fail (GTK_IS_CONTAINER (widget), FALSE);
1440
1441   container = GTK_CONTAINER (widget);
1442
1443   return_val = FALSE;
1444
1445   if (GTK_WIDGET_CAN_FOCUS (container))
1446     {
1447       if (!GTK_WIDGET_HAS_FOCUS (container))
1448         {
1449           gtk_widget_grab_focus (GTK_WIDGET (container));
1450           return_val = TRUE;
1451         }
1452     }
1453   else
1454     {
1455       /* Get a list of the containers children, allowing focus
1456        * chain to override.
1457        */
1458       if (container->has_focus_chain)
1459         children = g_list_copy (get_focus_chain (container));
1460       else
1461         children = gtk_container_get_children (container);
1462
1463       if (container->has_focus_chain &&
1464           (direction == GTK_DIR_TAB_FORWARD ||
1465            direction == GTK_DIR_TAB_BACKWARD))
1466         {
1467           sorted_children = g_list_copy (children);
1468           
1469           if (direction == GTK_DIR_TAB_BACKWARD)
1470             sorted_children = g_list_reverse (sorted_children);
1471         }
1472       else
1473         sorted_children = gtk_container_focus_sort (container, children, direction);
1474       
1475       return_val = gtk_container_focus_move (container, sorted_children, direction);
1476
1477       g_list_free (sorted_children);
1478       g_list_free (children);
1479     }
1480
1481   return return_val;
1482 }
1483
1484 static gint
1485 tab_compare (gconstpointer a,
1486              gconstpointer b)
1487 {
1488   const GtkWidget *child1 = a;
1489   const GtkWidget *child2 = b;
1490
1491   gint y1 = child1->allocation.y + child1->allocation.height / 2;
1492   gint y2 = child2->allocation.y + child2->allocation.height / 2;
1493
1494   if (y1 == y2)
1495     {
1496       gint x1 = child1->allocation.x + child1->allocation.width / 2;
1497       gint x2 = child2->allocation.x + child2->allocation.width / 2;
1498       
1499       return (x1 < x2) ? -1 : ((x1 == x2) ? 0 : 1);
1500     }
1501   else
1502     return (y1 < y2) ? -1 : 1;
1503 }
1504
1505 static GList *
1506 gtk_container_focus_sort_tab (GtkContainer     *container,
1507                               GList            *children,
1508                               GtkDirectionType  direction)
1509 {
1510   children = g_list_sort (children, tab_compare);
1511
1512   /* if we are going backwards then reverse the order
1513    *  of the children.
1514    */
1515   if (direction == GTK_DIR_TAB_BACKWARD)
1516     children = g_list_reverse (children);
1517
1518   return children;
1519 }
1520
1521 /* Get coordinates of @widget's allocation with respect to
1522  * allocation of @container.
1523  */
1524 static gboolean
1525 get_allocation_coords (GtkContainer  *container,
1526                        GtkWidget     *widget,
1527                        GdkRectangle  *allocation)
1528 {
1529   *allocation = widget->allocation;
1530
1531   return gtk_widget_translate_coordinates (widget, GTK_WIDGET (container),
1532                                            0, 0, &allocation->x, &allocation->y);
1533 }
1534
1535 /* Look for a child in @children that is intermediate between
1536  * the focus widget and container. This widget, if it exists,
1537  * acts as the starting widget for focus navigation.
1538  */
1539 static GtkWidget *
1540 find_old_focus (GtkContainer *container,
1541                 GList        *children)
1542 {
1543   GList *tmp_list = children;
1544   while (tmp_list)
1545     {
1546       GtkWidget *child = tmp_list->data;
1547       GtkWidget *widget = child;
1548
1549       while (widget && widget != (GtkWidget *)container)
1550         {
1551           GtkWidget *parent = widget->parent;
1552           if (parent && ((GtkContainer *)parent)->focus_child != widget)
1553             goto next;
1554
1555           widget = parent;
1556         }
1557
1558       return child;
1559
1560     next:
1561       tmp_list = tmp_list->next;
1562     }
1563
1564   return NULL;
1565 }
1566
1567 static gboolean
1568 old_focus_coords (GtkContainer *container,
1569                   GdkRectangle *old_focus_rect)
1570 {
1571   GtkWidget *widget = GTK_WIDGET (container);
1572   GtkWidget *toplevel = gtk_widget_get_toplevel (widget);
1573   
1574   if (toplevel && GTK_IS_WINDOW (toplevel) && GTK_WINDOW (toplevel)->focus_widget)
1575     {
1576       GtkWidget *old_focus = GTK_WINDOW (toplevel)->focus_widget;
1577       
1578       return get_allocation_coords (container, old_focus, old_focus_rect);
1579     }
1580   else
1581     return FALSE;
1582 }
1583
1584 typedef struct _CompareInfo CompareInfo;
1585
1586 struct _CompareInfo
1587 {
1588   GtkContainer *container;
1589   gint x;
1590   gint y;
1591   gboolean reverse;
1592 };
1593
1594 static gint
1595 up_down_compare (gconstpointer a,
1596                  gconstpointer b,
1597                  gpointer      data)
1598 {
1599   GdkRectangle allocation1;
1600   GdkRectangle allocation2;
1601   CompareInfo *compare = data;
1602   gint y1, y2;
1603
1604   get_allocation_coords (compare->container, (GtkWidget *)a, &allocation1);
1605   get_allocation_coords (compare->container, (GtkWidget *)b, &allocation2);
1606
1607   y1 = allocation1.y + allocation1.height / 2;
1608   y2 = allocation2.y + allocation2.height / 2;
1609
1610   if (y1 == y2)
1611     {
1612       gint x1 = abs (allocation1.x + allocation1.width / 2 - compare->x);
1613       gint x2 = abs (allocation2.x + allocation2.width / 2 - compare->x);
1614
1615       if (compare->reverse)
1616         return (x1 < x2) ? 1 : ((x1 == x2) ? 0 : -1);
1617       else
1618         return (x1 < x2) ? -1 : ((x1 == x2) ? 0 : 1);
1619     }
1620   else
1621     return (y1 < y2) ? -1 : 1;
1622 }
1623
1624 static GList *
1625 gtk_container_focus_sort_up_down (GtkContainer     *container,
1626                                   GList            *children,
1627                                   GtkDirectionType  direction)
1628 {
1629   CompareInfo compare;
1630   GList *tmp_list;
1631   GtkWidget *old_focus;
1632
1633   compare.container = container;
1634   compare.reverse = (direction == GTK_DIR_UP);
1635
1636   old_focus = find_old_focus (container, children);
1637   if (old_focus)
1638     {
1639       GdkRectangle old_allocation;
1640       gint compare_x1;
1641       gint compare_x2;
1642       gint compare_y;
1643
1644       /* Delete widgets from list that don't match minimum criteria */
1645
1646       get_allocation_coords (container, old_focus, &old_allocation);
1647
1648       compare_x1 = old_allocation.x;
1649       compare_x2 = old_allocation.x + old_allocation.width;
1650
1651       if (direction == GTK_DIR_UP)
1652         compare_y = old_allocation.y;
1653       else
1654         compare_y = old_allocation.y + old_allocation.height;
1655       
1656       tmp_list = children;
1657       while (tmp_list)
1658         {
1659           GtkWidget *child = tmp_list->data;
1660           GList *next = tmp_list->next;
1661           gint child_x1, child_x2;
1662           GdkRectangle child_allocation;
1663           
1664           if (child != old_focus)
1665             {
1666               get_allocation_coords (container, child, &child_allocation);
1667               
1668               child_x1 = child_allocation.x;
1669               child_x2 = child_allocation.x + child_allocation.width;
1670               
1671               if ((child_x2 <= compare_x1 || child_x1 >= compare_x2) /* No horizontal overlap */ ||
1672                   (direction == GTK_DIR_DOWN && child_allocation.y + child_allocation.height < compare_y) || /* Not below */
1673                   (direction == GTK_DIR_UP && child_allocation.y > compare_y)) /* Not above */
1674                 {
1675                   children = g_list_delete_link (children, tmp_list);
1676                 }
1677             }
1678           
1679           tmp_list = next;
1680         }
1681
1682       compare.x = (compare_x1 + compare_x2) / 2;
1683       compare.y = old_allocation.y + old_allocation.height / 2;
1684     }
1685   else
1686     {
1687       /* No old focus widget, need to figure out starting x,y some other way
1688        */
1689       GtkWidget *widget = GTK_WIDGET (container);
1690       GdkRectangle old_focus_rect;
1691
1692       if (old_focus_coords (container, &old_focus_rect))
1693         {
1694           compare.x = old_focus_rect.x + old_focus_rect.width / 2;
1695         }
1696       else
1697         {
1698           if (GTK_WIDGET_NO_WINDOW (widget))
1699             compare.x = widget->allocation.x + widget->allocation.width / 2;
1700           else
1701             compare.x = widget->allocation.width / 2;
1702         }
1703       
1704       if (GTK_WIDGET_NO_WINDOW (widget))
1705         compare.y = (direction == GTK_DIR_DOWN) ? widget->allocation.y : widget->allocation.y + widget->allocation.height;
1706       else
1707         compare.y = (direction == GTK_DIR_DOWN) ? 0 : + widget->allocation.height;
1708     }
1709
1710   children = g_list_sort_with_data (children, up_down_compare, &compare);
1711
1712   if (compare.reverse)
1713     children = g_list_reverse (children);
1714
1715   return children;
1716 }
1717
1718 static gint
1719 left_right_compare (gconstpointer a,
1720                     gconstpointer b,
1721                     gpointer      data)
1722 {
1723   GdkRectangle allocation1;
1724   GdkRectangle allocation2;
1725   CompareInfo *compare = data;
1726   gint x1, x2;
1727
1728   get_allocation_coords (compare->container, (GtkWidget *)a, &allocation1);
1729   get_allocation_coords (compare->container, (GtkWidget *)b, &allocation2);
1730
1731   x1 = allocation1.x + allocation1.width / 2;
1732   x2 = allocation2.x + allocation2.width / 2;
1733
1734   if (x1 == x2)
1735     {
1736       gint y1 = abs (allocation1.y + allocation1.height / 2 - compare->y);
1737       gint y2 = abs (allocation2.y + allocation2.height / 2 - compare->y);
1738
1739       if (compare->reverse)
1740         return (y1 < y2) ? 1 : ((y1 == y2) ? 0 : -1);
1741       else
1742         return (y1 < y2) ? -1 : ((y1 == y2) ? 0 : 1);
1743     }
1744   else
1745     return (x1 < x2) ? -1 : 1;
1746 }
1747
1748 static GList *
1749 gtk_container_focus_sort_left_right (GtkContainer     *container,
1750                                      GList            *children,
1751                                      GtkDirectionType  direction)
1752 {
1753   CompareInfo compare;
1754   GList *tmp_list;
1755   GtkWidget *old_focus;
1756
1757   compare.container = container;
1758   compare.reverse = (direction == GTK_DIR_LEFT);
1759   
1760   old_focus = find_old_focus (container, children);
1761   if (old_focus)
1762     {
1763       GdkRectangle old_allocation;
1764
1765       gint compare_y1;
1766       gint compare_y2;
1767       gint compare_x;
1768       
1769       /* Delete widgets from list that don't match minimum criteria */
1770
1771       get_allocation_coords (container, old_focus, &old_allocation);
1772
1773       compare_y1 = old_allocation.y;
1774       compare_y2 = old_allocation.y + old_allocation.height;
1775
1776       if (direction == GTK_DIR_LEFT)
1777         compare_x = old_allocation.x;
1778       else
1779         compare_x = old_allocation.x + old_allocation.width;
1780       
1781       tmp_list = children;
1782       while (tmp_list)
1783         {
1784           GtkWidget *child = tmp_list->data;
1785           GList *next = tmp_list->next;
1786           gint child_y1, child_y2;
1787           GdkRectangle child_allocation;
1788           
1789           if (child != old_focus)
1790             {
1791               get_allocation_coords (container, child, &child_allocation);
1792               
1793               child_y1 = child_allocation.y;
1794               child_y2 = child_allocation.y + child_allocation.height;
1795               
1796               if ((child_y2 <= compare_y1 || child_y1 >= compare_y2) /* No vertical overlap */ ||
1797                   (direction == GTK_DIR_RIGHT && child_allocation.x + child_allocation.width < compare_x) || /* Not to left */
1798                   (direction == GTK_DIR_LEFT && child_allocation.x > compare_x)) /* Not to right */
1799                 {
1800                   children = g_list_delete_link (children, tmp_list);
1801                 }
1802             }
1803           
1804           tmp_list = next;
1805         }
1806
1807       compare.y = (compare_y1 + compare_y2) / 2;
1808       compare.x = old_allocation.x + old_allocation.width / 2;
1809     }
1810   else
1811     {
1812       /* No old focus widget, need to figure out starting x,y some other way
1813        */
1814       GtkWidget *widget = GTK_WIDGET (container);
1815       GdkRectangle old_focus_rect;
1816
1817       if (old_focus_coords (container, &old_focus_rect))
1818         {
1819           compare.y = old_focus_rect.y + old_focus_rect.height / 2;
1820         }
1821       else
1822         {
1823           if (GTK_WIDGET_NO_WINDOW (widget))
1824             compare.y = widget->allocation.y + widget->allocation.height / 2;
1825           else
1826             compare.y = widget->allocation.height / 2;
1827         }
1828       
1829       if (GTK_WIDGET_NO_WINDOW (widget))
1830         compare.x = (direction == GTK_DIR_RIGHT) ? widget->allocation.x : widget->allocation.x + widget->allocation.width;
1831       else
1832         compare.x = (direction == GTK_DIR_RIGHT) ? 0 : widget->allocation.width;
1833     }
1834
1835   children = g_list_sort_with_data (children, left_right_compare, &compare);
1836
1837   if (compare.reverse)
1838     children = g_list_reverse (children);
1839
1840   return children;
1841 }
1842
1843 /**
1844  * gtk_container_focus_sort:
1845  * @container: a #GtkContainer
1846  * @children:  a list of descendents of @container (they don't
1847  *             have to be direct children.
1848  * @direction: focus direction
1849  * 
1850  * Sorts @children in the correct order for focusing with
1851  * direction type @direction.
1852  * 
1853  * Return value: a copy of @children, sorted in correct focusing order,
1854  *   with children that aren't suitable for focusing in this direction
1855  *   removed.
1856  **/
1857 static GList *
1858 gtk_container_focus_sort (GtkContainer     *container,
1859                           GList            *children,
1860                           GtkDirectionType  direction)
1861 {
1862   children = g_list_copy (children);
1863   
1864   switch (direction)
1865     {
1866     case GTK_DIR_TAB_FORWARD:
1867     case GTK_DIR_TAB_BACKWARD:
1868       return gtk_container_focus_sort_tab (container, children, direction);
1869     case GTK_DIR_UP:
1870     case GTK_DIR_DOWN:
1871       return gtk_container_focus_sort_up_down (container, children, direction);
1872     case GTK_DIR_LEFT:
1873     case GTK_DIR_RIGHT:
1874       return gtk_container_focus_sort_left_right (container, children, direction);
1875     }
1876
1877   g_assert_not_reached ();
1878
1879   return NULL;
1880 }
1881
1882 static gboolean
1883 gtk_container_focus_move (GtkContainer     *container,
1884                           GList            *children,
1885                           GtkDirectionType  direction)
1886 {
1887   GtkWidget *focus_child;
1888   GtkWidget *child;
1889
1890   focus_child = container->focus_child;
1891
1892   while (children)
1893     {
1894       child = children->data;
1895       children = children->next;
1896
1897       if (!child)
1898         continue;
1899       
1900       if (focus_child)
1901         {
1902           if (focus_child == child)
1903             {
1904               focus_child = NULL;
1905
1906                 if (gtk_widget_child_focus (child, direction))
1907                   return TRUE;
1908             }
1909         }
1910       else if (GTK_WIDGET_DRAWABLE (child) &&
1911                gtk_widget_is_ancestor (child, GTK_WIDGET (container)))
1912         {
1913           if (gtk_widget_child_focus (child, direction))
1914             return TRUE;
1915         }
1916     }
1917
1918   return FALSE;
1919 }
1920
1921
1922 static void
1923 gtk_container_children_callback (GtkWidget *widget,
1924                                  gpointer   client_data)
1925 {
1926   GList **children;
1927
1928   children = (GList**) client_data;
1929   *children = g_list_prepend (*children, widget);
1930 }
1931
1932 static void
1933 chain_widget_destroyed (GtkWidget *widget,
1934                         gpointer   user_data)
1935 {
1936   GtkContainer *container;
1937   GList *chain;
1938   
1939   container = GTK_CONTAINER (user_data);
1940
1941   chain = g_object_get_data (G_OBJECT (container),
1942                              "gtk-container-focus-chain");
1943
1944   chain = g_list_remove (chain, widget);
1945
1946   g_signal_handlers_disconnect_by_func (G_OBJECT (widget),
1947                                         chain_widget_destroyed,
1948                                         user_data);
1949   
1950   g_object_set_data (G_OBJECT (container),
1951                      "gtk-container-focus-chain",
1952                      chain);  
1953 }
1954
1955 /**
1956  * gtk_container_set_focus_chain: 
1957  * @container: a #GtkContainer.
1958  * @focusable_widgets: the new focus chain.
1959  *
1960  * Sets a focus chain, overriding the one computed automatically by GTK+.
1961  * 
1962  * In principle each widget in the chain should be a descendant of the 
1963  * container, but this is not enforced by this method, since it's allowed 
1964  * to set the focus chain before you pack the widgets, or have a widget 
1965  * in the chain that isn't always packed. The necessary checks are done 
1966  * when the focus chain is actually traversed.
1967  **/
1968 void
1969 gtk_container_set_focus_chain (GtkContainer *container,
1970                                GList        *focusable_widgets)
1971 {
1972   GList *chain;
1973   GList *tmp_list;
1974   
1975   g_return_if_fail (GTK_IS_CONTAINER (container));
1976   
1977   if (container->has_focus_chain)
1978     gtk_container_unset_focus_chain (container);
1979
1980   container->has_focus_chain = TRUE;
1981   
1982   chain = NULL;
1983   tmp_list = focusable_widgets;
1984   while (tmp_list != NULL)
1985     {
1986       g_return_if_fail (GTK_IS_WIDGET (tmp_list->data));
1987       
1988       /* In principle each widget in the chain should be a descendant
1989        * of the container, but we don't want to check that here, it's
1990        * expensive and also it's allowed to set the focus chain before
1991        * you pack the widgets, or have a widget in the chain that isn't
1992        * always packed. So we check for ancestor during actual traversal.
1993        */
1994
1995       chain = g_list_prepend (chain, tmp_list->data);
1996
1997       gtk_signal_connect (GTK_OBJECT (tmp_list->data),
1998                           "destroy",
1999                           GTK_SIGNAL_FUNC (chain_widget_destroyed),
2000                           container);
2001       
2002       tmp_list = g_list_next (tmp_list);
2003     }
2004
2005   chain = g_list_reverse (chain);
2006   
2007   g_object_set_data (G_OBJECT (container),
2008                      "gtk-container-focus-chain",
2009                      chain);
2010 }
2011
2012 /**
2013  * gtk_container_get_focus_chain:
2014  * @container:         a #GtkContainer
2015  * @focusable_widgets: location to store the focus chain of the
2016  *                     container, or %NULL. You should free this list
2017  *                     using g_list_free() when you are done with it, however
2018  *                     no additional reference count is added to the
2019  *                     individual widgets in the focus chain.
2020  * 
2021  * Retrieves the focus chain of the container, if one has been
2022  * set explicitly. If no focus chain has been explicitly
2023  * set, GTK+ computes the focus chain based on the positions
2024  * of the children. In that case, GTK+ stores %NULL in
2025  * @focusable_widgets and returns %FALSE.
2026  *
2027  * Return value: %TRUE if the focus chain of the container 
2028  * has been set explicitly.
2029  **/
2030 gboolean
2031 gtk_container_get_focus_chain (GtkContainer *container,
2032                                GList       **focus_chain)
2033 {
2034   g_return_val_if_fail (GTK_IS_CONTAINER (container), FALSE);
2035
2036   if (focus_chain)
2037     {
2038       if (container->has_focus_chain)
2039         *focus_chain = g_list_copy (get_focus_chain (container));
2040       else
2041         *focus_chain = NULL;
2042     }
2043
2044   return container->has_focus_chain;
2045 }
2046
2047 /**
2048  * gtk_container_unset_focus_chain:
2049  * @container: a #GtkContainer.
2050  * 
2051  * Removes a focus chain explicitly set with gtk_container_set_focus_chain().
2052  **/
2053 void
2054 gtk_container_unset_focus_chain (GtkContainer  *container)
2055 {  
2056   g_return_if_fail (GTK_IS_CONTAINER (container));
2057
2058   if (container->has_focus_chain)
2059     {
2060       GList *chain;
2061       GList *tmp_list;
2062       
2063       chain = get_focus_chain (container);
2064       
2065       container->has_focus_chain = FALSE;
2066       
2067       g_object_set_data (G_OBJECT (container), "gtk-container-focus-chain",
2068                          NULL);
2069
2070       tmp_list = chain;
2071       while (tmp_list != NULL)
2072         {
2073           g_signal_handlers_disconnect_by_func (G_OBJECT (tmp_list->data),
2074                                                 chain_widget_destroyed,
2075                                                 container);
2076           
2077           tmp_list = g_list_next (tmp_list);
2078         }
2079
2080       g_list_free (chain);
2081     }
2082 }
2083
2084 void
2085 gtk_container_set_focus_vadjustment (GtkContainer  *container,
2086                                      GtkAdjustment *adjustment)
2087 {
2088   g_return_if_fail (container != NULL);
2089   g_return_if_fail (GTK_IS_CONTAINER (container));
2090   if (adjustment)
2091     g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
2092
2093   if (adjustment)
2094     gtk_object_ref (GTK_OBJECT(adjustment));
2095
2096   gtk_object_set_data_by_id_full (GTK_OBJECT (container),
2097                                   vadjustment_key_id,
2098                                   adjustment,
2099                                   (GtkDestroyNotify) gtk_object_unref);
2100 }
2101
2102 /**
2103  * gtk_container_get_focus_vadjustment:
2104  * @container: a #GtkContainer
2105  *
2106  * Retrieves the vertical focus adjustment for the container. See
2107  * gtk_container_set_focus_vadjustment ().
2108  *
2109  * Return value: the vertical focus adjustment, or %NULL if
2110  *   none has been set.
2111  **/
2112 GtkAdjustment *
2113 gtk_container_get_focus_vadjustment (GtkContainer *container)
2114 {
2115   GtkAdjustment *vadjustment;
2116     
2117   g_return_val_if_fail (GTK_IS_CONTAINER (container), NULL);
2118
2119   vadjustment = gtk_object_get_data_by_id (GTK_OBJECT (container),
2120                                            vadjustment_key_id);
2121
2122   return vadjustment;
2123 }
2124
2125 void
2126 gtk_container_set_focus_hadjustment (GtkContainer  *container,
2127                                      GtkAdjustment *adjustment)
2128 {
2129   g_return_if_fail (container != NULL);
2130   g_return_if_fail (GTK_IS_CONTAINER (container));
2131   if (adjustment)
2132     g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
2133
2134   if (adjustment)
2135     gtk_object_ref (GTK_OBJECT (adjustment));
2136
2137   gtk_object_set_data_by_id_full (GTK_OBJECT (container),
2138                                   hadjustment_key_id,
2139                                   adjustment,
2140                                   (GtkDestroyNotify) gtk_object_unref);
2141 }
2142
2143 /**
2144  * gtk_container_get_focus_hadjustment:
2145  * @container: a #GtkContainer
2146  *
2147  * Retrieves the horizontal focus adjustment for the container. See
2148  * gtk_container_set_focus_hadjustment ().
2149  *
2150  * Return value: the horizontal focus adjustment, or %NULL if
2151  *   none has been set.
2152  **/
2153 GtkAdjustment *
2154 gtk_container_get_focus_hadjustment (GtkContainer *container)
2155 {
2156   GtkAdjustment *hadjustment;
2157
2158   g_return_val_if_fail (GTK_IS_CONTAINER (container), NULL);
2159
2160   hadjustment = gtk_object_get_data_by_id (GTK_OBJECT (container),
2161                                            hadjustment_key_id);
2162
2163   return hadjustment;
2164 }
2165
2166
2167 static void
2168 gtk_container_show_all (GtkWidget *widget)
2169 {
2170   g_return_if_fail (widget != NULL);
2171   g_return_if_fail (GTK_IS_CONTAINER (widget));
2172
2173   gtk_container_foreach (GTK_CONTAINER (widget),
2174                          (GtkCallback) gtk_widget_show_all,
2175                          NULL);
2176   gtk_widget_show (widget);
2177 }
2178
2179 static void
2180 gtk_container_hide_all (GtkWidget *widget)
2181 {
2182   g_return_if_fail (widget != NULL);
2183   g_return_if_fail (GTK_IS_CONTAINER (widget));
2184
2185   gtk_widget_hide (widget);
2186   gtk_container_foreach (GTK_CONTAINER (widget),
2187                          (GtkCallback) gtk_widget_hide_all,
2188                          NULL);
2189 }
2190
2191
2192 static void
2193 gtk_container_expose_child (GtkWidget *child,
2194                             gpointer   client_data)
2195 {
2196   struct {
2197     GtkWidget *container;
2198     GdkEventExpose *event;
2199   } *data = client_data;
2200   
2201   gtk_container_propagate_expose (GTK_CONTAINER (data->container),
2202                                   child,
2203                                   data->event);
2204 }
2205
2206 static gint 
2207 gtk_container_expose (GtkWidget      *widget,
2208                       GdkEventExpose *event)
2209 {
2210   struct {
2211     GtkWidget *container;
2212     GdkEventExpose *event;
2213   } data;
2214
2215   g_return_val_if_fail (widget != NULL, FALSE);
2216   g_return_val_if_fail (GTK_IS_CONTAINER (widget), FALSE);
2217   g_return_val_if_fail (event != NULL, FALSE);
2218
2219   
2220   if (GTK_WIDGET_DRAWABLE (widget)) 
2221     {
2222       data.container = widget;
2223       data.event = event;
2224       
2225       gtk_container_forall (GTK_CONTAINER (widget),
2226                             gtk_container_expose_child,
2227                             &data);
2228     }   
2229   
2230   return FALSE;
2231 }
2232
2233 static void
2234 gtk_container_map_child (GtkWidget *child,
2235                          gpointer   client_data)
2236 {
2237   if (GTK_WIDGET_VISIBLE (child) &&
2238       GTK_WIDGET_CHILD_VISIBLE (child) &&
2239       !GTK_WIDGET_MAPPED (child))
2240     gtk_widget_map (child);
2241 }
2242
2243 static void
2244 gtk_container_map (GtkWidget *widget)
2245 {
2246   GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
2247
2248   gtk_container_forall (GTK_CONTAINER (widget),
2249                         gtk_container_map_child,
2250                         NULL);
2251
2252   if (!GTK_WIDGET_NO_WINDOW (widget))
2253     gdk_window_show (widget->window);
2254 }
2255
2256 static void
2257 gtk_container_unmap (GtkWidget *widget)
2258 {
2259   GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED);
2260
2261   if (!GTK_WIDGET_NO_WINDOW (widget))
2262     gdk_window_hide (widget->window);
2263   else
2264     gtk_container_forall (GTK_CONTAINER (widget),
2265                           (GtkCallback)gtk_widget_unmap,
2266                           NULL);
2267 }
2268
2269 /**
2270  * gtk_container_propagate_expose:
2271  * @container: a #GtkContainer
2272  * @child: a child of @container
2273  * @event: a expose event sent to container
2274  *
2275  * When a container receives an expose event, it must send synthetic
2276  * expose events to all children that don't have their own #GdkWindows.
2277  * This function provides a convenient way of doing this. A container,
2278  * when it receives an expose event, calls gtk_container_propagate_expose() 
2279  * once for each child, passing in the event the container received.
2280  *
2281  * gtk_container_propagate_expose() takes care of deciding whether
2282  * an expose event needs to be sent to the child, intersecting
2283  * the event's area with the child area, and sending the event.
2284  * 
2285  * In most cases, a container can simply either simply inherit the
2286  * ::expose implementation from #GtkContainer, or, do some drawing 
2287  * and then chain to the ::expose implementation from #GtkContainer.
2288  **/
2289 void
2290 gtk_container_propagate_expose (GtkContainer   *container,
2291                                 GtkWidget      *child,
2292                                 GdkEventExpose *event)
2293 {
2294   GdkEventExpose child_event;
2295
2296   g_return_if_fail (GTK_IS_CONTAINER (container));
2297   g_return_if_fail (GTK_IS_WIDGET (child));
2298   g_return_if_fail (event != NULL);
2299
2300   g_assert (child->parent == GTK_WIDGET (container));
2301   
2302   if (GTK_WIDGET_DRAWABLE (child) &&
2303       GTK_WIDGET_NO_WINDOW (child) &&
2304       (child->window == event->window))
2305     {
2306       child_event = *event;
2307
2308       child_event.region = gtk_widget_region_intersect (child, event->region);
2309       if (!gdk_region_empty (child_event.region))
2310         {
2311           gdk_region_get_clipbox (child_event.region, &child_event.area);
2312           gtk_widget_send_expose (child, (GdkEvent *)&child_event);
2313         }
2314       gdk_region_destroy (child_event.region);
2315     }
2316 }