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