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, see <http://www.gnu.org/licenses/>.
16  */
17 
18 /*
19  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
20  * file for a list of people on the GTK+ Team.  See the ChangeLog
21  * files for a list of changes.  These files are distributed with
22  * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
23  */
24 
25 #include "config.h"
26 
27 #define GDK_DISABLE_DEPRECATION_WARNINGS
28 #include "gtkcontainer.h"
29 #include "gtkcontainerprivate.h"
30 
31 #include <stdarg.h>
32 #include <string.h>
33 #include <stdlib.h>
34 
35 #include <gobject/gobjectnotifyqueue.c>
36 #include <gobject/gvaluecollector.h>
37 
38 #include "gtkadjustment.h"
39 #include "gtkbuildable.h"
40 #include "gtkbuilderprivate.h"
41 #include "gtktypebuiltins.h"
42 #include "gtkprivate.h"
43 #include "gtkmain.h"
44 #include "gtkmarshalers.h"
45 #include "gtksizerequest.h"
46 #include "gtksizerequestcacheprivate.h"
47 #include "gtkwidgetprivate.h"
48 #include "gtkwindow.h"
49 #include "gtkassistant.h"
50 #include "gtkintl.h"
51 #include "gtkstylecontextprivate.h"
52 #include "gtkwidgetpath.h"
53 #include "a11y/gtkcontaineraccessible.h"
54 #include "a11y/gtkcontaineraccessibleprivate.h"
55 #include "gtkpopovermenu.h"
56 #include "gtkshortcutswindow.h"
57 
58 /* A handful of containers inside GTK+ are cheating and widgets
59  * inside internal structure as direct children for the purpose
60  * of forall().
61  */
62 #define SPECIAL_CONTAINER(x) (GTK_IS_ASSISTANT (x) || \
63                               GTK_IS_ACTION_BAR (x) || \
64                               GTK_IS_POPOVER_MENU (x) || \
65                               GTK_IS_SHORTCUTS_SECTION (x) || \
66                               GTK_IS_SHORTCUTS_WINDOW (x))
67 
68 /**
69  * SECTION:gtkcontainer
70  * @Short_description: Base class for widgets which contain other widgets
71  * @Title: GtkContainer
72  *
73  * A GTK+ user interface is constructed by nesting widgets inside widgets.
74  * Container widgets are the inner nodes in the resulting tree of widgets:
75  * they contain other widgets. So, for example, you might have a #GtkWindow
76  * containing a #GtkFrame containing a #GtkLabel. If you wanted an image instead
77  * of a textual label inside the frame, you might replace the #GtkLabel widget
78  * with a #GtkImage widget.
79  *
80  * There are two major kinds of container widgets in GTK+. Both are subclasses
81  * of the abstract GtkContainer base class.
82  *
83  * The first type of container widget has a single child widget and derives
84  * from #GtkBin. These containers are decorators, which
85  * add some kind of functionality to the child. For example, a #GtkButton makes
86  * its child into a clickable button; a #GtkFrame draws a frame around its child
87  * and a #GtkWindow places its child widget inside a top-level window.
88  *
89  * The second type of container can have more than one child; its purpose is to
90  * manage layout. This means that these containers assign
91  * sizes and positions to their children. For example, a #GtkHBox arranges its
92  * children in a horizontal row, and a #GtkGrid arranges the widgets it contains
93  * in a two-dimensional grid.
94  *
95  * For implementations of #GtkContainer the virtual method #GtkContainerClass.forall()
96  * is always required, since it's used for drawing and other internal operations
97  * on the children.
98  * If the #GtkContainer implementation expect to have non internal children
99  * it's needed to implement both #GtkContainerClass.add() and #GtkContainerClass.remove().
100  * If the GtkContainer implementation has internal children, they should be added
101  * with gtk_widget_set_parent() on init() and removed with gtk_widget_unparent()
102  * in the #GtkWidgetClass.destroy() implementation.
103  * See more about implementing custom widgets at https://wiki.gnome.org/HowDoI/CustomWidgets
104  *
105  * # Height for width geometry management
106  *
107  * GTK+ uses a height-for-width (and width-for-height) geometry management system.
108  * Height-for-width means that a widget can change how much vertical space it needs,
109  * depending on the amount of horizontal space that it is given (and similar for
110  * width-for-height).
111  *
112  * There are some things to keep in mind when implementing container widgets
113  * that make use of GTK+’s height for width geometry management system. First,
114  * it’s important to note that a container must prioritize one of its
115  * dimensions, that is to say that a widget or container can only have a
116  * #GtkSizeRequestMode that is %GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH or
117  * %GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT. However, every widget and container
118  * must be able to respond to the APIs for both dimensions, i.e. even if a
119  * widget has a request mode that is height-for-width, it is possible that
120  * its parent will request its sizes using the width-for-height APIs.
121  *
122  * To ensure that everything works properly, here are some guidelines to follow
123  * when implementing height-for-width (or width-for-height) containers.
124  *
125  * Each request mode involves 2 virtual methods. Height-for-width apis run
126  * through gtk_widget_get_preferred_width() and then through gtk_widget_get_preferred_height_for_width().
127  * When handling requests in the opposite #GtkSizeRequestMode it is important that
128  * every widget request at least enough space to display all of its content at all times.
129  *
130  * When gtk_widget_get_preferred_height() is called on a container that is height-for-width,
131  * the container must return the height for its minimum width. This is easily achieved by
132  * simply calling the reverse apis implemented for itself as follows:
133  *
134  * |[<!-- language="C" -->
135  * static void
136  * foo_container_get_preferred_height (GtkWidget *widget,
137  *                                     gint *min_height,
138  *                                     gint *nat_height)
139  * {
140  *    if (i_am_in_height_for_width_mode)
141  *      {
142  *        gint min_width;
143  *
144  *        GTK_WIDGET_GET_CLASS (widget)->get_preferred_width (widget,
145  *                                                            &min_width,
146  *                                                            NULL);
147  *        GTK_WIDGET_GET_CLASS (widget)->get_preferred_height_for_width
148  *                                                           (widget,
149  *                                                            min_width,
150  *                                                            min_height,
151  *                                                            nat_height);
152  *      }
153  *    else
154  *      {
155  *        ... many containers support both request modes, execute the
156  *        real width-for-height request here by returning the
157  *        collective heights of all widgets that are stacked
158  *        vertically (or whatever is appropriate for this container)
159  *        ...
160  *      }
161  * }
162  * ]|
163  *
164  * Similarly, when gtk_widget_get_preferred_width_for_height() is called for a container or widget
165  * that is height-for-width, it then only needs to return the base minimum width like so:
166  *
167  * |[<!-- language="C" -->
168  * static void
169  * foo_container_get_preferred_width_for_height (GtkWidget *widget,
170  *                                               gint for_height,
171  *                                               gint *min_width,
172  *                                               gint *nat_width)
173  * {
174  *    if (i_am_in_height_for_width_mode)
175  *      {
176  *        GTK_WIDGET_GET_CLASS (widget)->get_preferred_width (widget,
177  *                                                            min_width,
178  *                                                            nat_width);
179  *      }
180  *    else
181  *      {
182  *        ... execute the real width-for-height request here based on
183  *        the required width of the children collectively if the
184  *        container were to be allocated the said height ...
185  *      }
186  * }
187  * ]|
188  *
189  * Height for width requests are generally implemented in terms of a virtual allocation
190  * of widgets in the input orientation. Assuming an height-for-width request mode, a container
191  * would implement the get_preferred_height_for_width() virtual function by first calling
192  * gtk_widget_get_preferred_width() for each of its children.
193  *
194  * For each potential group of children that are lined up horizontally, the values returned by
195  * gtk_widget_get_preferred_width() should be collected in an array of #GtkRequestedSize structures.
196  * Any child spacing should be removed from the input @for_width and then the collective size should be
197  * allocated using the gtk_distribute_natural_allocation() convenience function.
198  *
199  * The container will then move on to request the preferred height for each child by using
200  * gtk_widget_get_preferred_height_for_width() and using the sizes stored in the #GtkRequestedSize array.
201  *
202  * To allocate a height-for-width container, it’s again important
203  * to consider that a container must prioritize one dimension over the other. So if
204  * a container is a height-for-width container it must first allocate all widgets horizontally
205  * using a #GtkRequestedSize array and gtk_distribute_natural_allocation() and then add any
206  * extra space (if and where appropriate) for the widget to expand.
207  *
208  * After adding all the expand space, the container assumes it was allocated sufficient
209  * height to fit all of its content. At this time, the container must use the total horizontal sizes
210  * of each widget to request the height-for-width of each of its children and store the requests in a
211  * #GtkRequestedSize array for any widgets that stack vertically (for tabular containers this can
212  * be generalized into the heights and widths of rows and columns).
213  * The vertical space must then again be distributed using gtk_distribute_natural_allocation()
214  * while this time considering the allocated height of the widget minus any vertical spacing
215  * that the container adds. Then vertical expand space should be added where appropriate and available
216  * and the container should go on to actually allocating the child widgets.
217  *
218  * See [GtkWidget’s geometry management section][geometry-management]
219  * to learn more about implementing height-for-width geometry management for widgets.
220  *
221  * # Child properties
222  *
223  * GtkContainer introduces child properties.
224  * These are object properties that are not specific
225  * to either the container or the contained widget, but rather to their relation.
226  * Typical examples of child properties are the position or pack-type of a widget
227  * which is contained in a #GtkBox.
228  *
229  * Use gtk_container_class_install_child_property() to install child properties
230  * for a container class and gtk_container_class_find_child_property() or
231  * gtk_container_class_list_child_properties() to get information about existing
232  * child properties.
233  *
234  * To set the value of a child property, use gtk_container_child_set_property(),
235  * gtk_container_child_set() or gtk_container_child_set_valist().
236  * To obtain the value of a child property, use
237  * gtk_container_child_get_property(), gtk_container_child_get() or
238  * gtk_container_child_get_valist(). To emit notification about child property
239  * changes, use gtk_widget_child_notify().
240  *
241  * # GtkContainer as GtkBuildable
242  *
243  * The GtkContainer implementation of the GtkBuildable interface supports
244  * a `<packing>` element for children, which can contain multiple `<property>`
245  * elements that specify child properties for the child.
246  *
247  * Since 2.16, child properties can also be marked as translatable using
248  * the same “translatable”, “comments” and “context” attributes that are used
249  * for regular properties.
250  *
251  * Since 3.16, containers can have a `<focus-chain>` element containing multiple
252  * `<widget>` elements, one for each child that should be added to the focus
253  * chain. The ”name” attribute gives the id of the widget.
254  *
255  * An example of these properties in UI definitions:
256  *
257  * |[<!-- language="xml" -->
258  * <object class="GtkBox">
259  *   <child>
260  *     <object class="GtkEntry" id="entry1"/>
261  *     <packing>
262  *       <property name="pack-type">start</property>
263  *     </packing>
264  *   </child>
265  *   <child>
266  *     <object class="GtkEntry" id="entry2"/>
267  *   </child>
268  *   <focus-chain>
269  *     <widget name="entry1"/>
270  *     <widget name="entry2"/>
271  *   </focus-chain>
272  * </object>
273  * ]|
274  *
275  */
276 
277 
278 struct _GtkContainerPrivate
279 {
280   GtkWidget *focus_child;
281 
282   GdkFrameClock *resize_clock;
283   guint resize_handler;
284 
285   guint border_width : 16;
286   guint border_width_set   : 1;
287 
288   guint has_focus_chain    : 1;
289   guint reallocate_redraws : 1;
290   guint restyle_pending    : 1;
291   guint resize_mode        : 2;
292   guint resize_mode_set    : 1;
293   guint request_mode       : 2;
294 };
295 
296 enum {
297   ADD,
298   REMOVE,
299   CHECK_RESIZE,
300   SET_FOCUS_CHILD,
301   LAST_SIGNAL
302 };
303 
304 enum {
305   PROP_0,
306   PROP_BORDER_WIDTH,
307   PROP_RESIZE_MODE,
308   PROP_CHILD,
309   LAST_PROP
310 };
311 
312 static GParamSpec *container_props[LAST_PROP];
313 
314 #define PARAM_SPEC_PARAM_ID(pspec)              ((pspec)->param_id)
315 #define PARAM_SPEC_SET_PARAM_ID(pspec, id)      ((pspec)->param_id = (id))
316 
317 
318 /* --- prototypes --- */
319 static void     gtk_container_base_class_init      (GtkContainerClass *klass);
320 static void     gtk_container_base_class_finalize  (GtkContainerClass *klass);
321 static void     gtk_container_class_init           (GtkContainerClass *klass);
322 static void     gtk_container_init                 (GtkContainer      *container);
323 static void     gtk_container_destroy              (GtkWidget         *widget);
324 static void     gtk_container_set_property         (GObject         *object,
325                                                     guint            prop_id,
326                                                     const GValue    *value,
327                                                     GParamSpec      *pspec);
328 static void     gtk_container_get_property         (GObject         *object,
329                                                     guint            prop_id,
330                                                     GValue          *value,
331                                                     GParamSpec      *pspec);
332 static void     gtk_container_add_unimplemented    (GtkContainer      *container,
333                                                     GtkWidget         *widget);
334 static void     gtk_container_remove_unimplemented (GtkContainer      *container,
335                                                     GtkWidget         *widget);
336 static void     gtk_container_real_check_resize    (GtkContainer      *container);
337 static void     gtk_container_compute_expand       (GtkWidget         *widget,
338                                                     gboolean          *hexpand_p,
339                                                     gboolean          *vexpand_p);
340 static gboolean gtk_container_focus                (GtkWidget         *widget,
341                                                     GtkDirectionType   direction);
342 static void     gtk_container_real_set_focus_child (GtkContainer      *container,
343                                                     GtkWidget         *widget);
344 
345 static gboolean gtk_container_focus_move           (GtkContainer      *container,
346                                                     GList             *children,
347                                                     GtkDirectionType   direction);
348 static void     gtk_container_children_callback    (GtkWidget         *widget,
349                                                     gpointer           client_data);
350 static void     gtk_container_show_all             (GtkWidget         *widget);
351 static gint     gtk_container_draw                 (GtkWidget         *widget,
352                                                     cairo_t           *cr);
353 static void     gtk_container_map                  (GtkWidget         *widget);
354 static void     gtk_container_unmap                (GtkWidget         *widget);
355 static void     gtk_container_adjust_size_request  (GtkWidget         *widget,
356                                                     GtkOrientation     orientation,
357                                                     gint              *minimum_size,
358                                                     gint              *natural_size);
359 static void     gtk_container_adjust_baseline_request (GtkWidget      *widget,
360 						       gint           *minimum_baseline,
361 						       gint           *natural_baseline);
362 static void     gtk_container_adjust_size_allocation (GtkWidget       *widget,
363                                                       GtkOrientation   orientation,
364                                                       gint            *minimum_size,
365                                                       gint            *natural_size,
366                                                       gint            *allocated_pos,
367                                                       gint            *allocated_size);
368 static void     gtk_container_adjust_baseline_allocation (GtkWidget      *widget,
369 							  gint           *baseline);
370 static GtkSizeRequestMode gtk_container_get_request_mode (GtkWidget   *widget);
371 
372 static gchar* gtk_container_child_default_composite_name (GtkContainer *container,
373                                                           GtkWidget    *child);
374 
375 static GtkWidgetPath * gtk_container_real_get_path_for_child (GtkContainer *container,
376                                                               GtkWidget    *child);
377 
378 /* GtkBuildable */
379 static void gtk_container_buildable_init           (GtkBuildableIface *iface);
380 static void gtk_container_buildable_add_child      (GtkBuildable *buildable,
381                                                     GtkBuilder   *builder,
382                                                     GObject      *child,
383                                                     const gchar  *type);
384 static gboolean gtk_container_buildable_custom_tag_start (GtkBuildable  *buildable,
385                                                           GtkBuilder    *builder,
386                                                           GObject       *child,
387                                                           const gchar   *tagname,
388                                                           GMarkupParser *parser,
389                                                           gpointer      *data);
390 static void    gtk_container_buildable_custom_tag_end (GtkBuildable *buildable,
391                                                        GtkBuilder   *builder,
392                                                        GObject      *child,
393                                                        const gchar  *tagname,
394                                                        gpointer     *data);
395 static void    gtk_container_buildable_custom_finished (GtkBuildable *buildable,
396                                                         GtkBuilder   *builder,
397                                                         GObject      *child,
398                                                         const gchar  *tagname,
399                                                         gpointer      data);
400 
401 static gboolean gtk_container_should_propagate_draw (GtkContainer   *container,
402                                                      GtkWidget      *child,
403                                                      cairo_t        *cr);
404 
405 /* --- variables --- */
406 static GQuark                vadjustment_key_id;
407 static GQuark                hadjustment_key_id;
408 static GQuark                quark_focus_chain;
409 static guint                 container_signals[LAST_SIGNAL] = { 0 };
410 static gint                  GtkContainer_private_offset;
411 static GtkWidgetClass       *parent_class = NULL;
412 extern GParamSpecPool       *_gtk_widget_child_property_pool;
413 extern GObjectNotifyContext *_gtk_widget_child_property_notify_context;
414 static GtkBuildableIface    *parent_buildable_iface;
415 
416 
417 /* --- functions --- */
418 static inline gpointer
gtk_container_get_instance_private(GtkContainer * self)419 gtk_container_get_instance_private (GtkContainer *self)
420 {
421   return G_STRUCT_MEMBER_P (self, GtkContainer_private_offset);
422 }
423 
424 GType
gtk_container_get_type(void)425 gtk_container_get_type (void)
426 {
427   static GType container_type = 0;
428 
429   if (!container_type)
430     {
431       const GTypeInfo container_info =
432       {
433         sizeof (GtkContainerClass),
434         (GBaseInitFunc) gtk_container_base_class_init,
435         (GBaseFinalizeFunc) gtk_container_base_class_finalize,
436         (GClassInitFunc) gtk_container_class_init,
437         NULL        /* class_finalize */,
438         NULL        /* class_data */,
439         sizeof (GtkContainer),
440         0           /* n_preallocs */,
441         (GInstanceInitFunc) gtk_container_init,
442         NULL,       /* value_table */
443       };
444 
445       const GInterfaceInfo buildable_info =
446       {
447         (GInterfaceInitFunc) gtk_container_buildable_init,
448         NULL,
449         NULL
450       };
451 
452       container_type =
453         g_type_register_static (GTK_TYPE_WIDGET, I_("GtkContainer"),
454                                 &container_info, G_TYPE_FLAG_ABSTRACT);
455 
456       GtkContainer_private_offset =
457         g_type_add_instance_private (container_type, sizeof (GtkContainerPrivate));
458 
459       g_type_add_interface_static (container_type,
460                                    GTK_TYPE_BUILDABLE,
461                                    &buildable_info);
462 
463     }
464 
465   return container_type;
466 }
467 
468 static void
gtk_container_base_class_init(GtkContainerClass * class)469 gtk_container_base_class_init (GtkContainerClass *class)
470 {
471   /* reset instance specifc class fields that don't get inherited */
472   class->set_child_property = NULL;
473   class->get_child_property = NULL;
474 }
475 
476 static void
gtk_container_base_class_finalize(GtkContainerClass * class)477 gtk_container_base_class_finalize (GtkContainerClass *class)
478 {
479   GList *list, *node;
480 
481   list = g_param_spec_pool_list_owned (_gtk_widget_child_property_pool, G_OBJECT_CLASS_TYPE (class));
482   for (node = list; node; node = node->next)
483     {
484       GParamSpec *pspec = node->data;
485 
486       g_param_spec_pool_remove (_gtk_widget_child_property_pool, pspec);
487       PARAM_SPEC_SET_PARAM_ID (pspec, 0);
488       g_param_spec_unref (pspec);
489     }
490   g_list_free (list);
491 }
492 
493 static void
gtk_container_class_init(GtkContainerClass * class)494 gtk_container_class_init (GtkContainerClass *class)
495 {
496   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
497   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
498 
499   parent_class = g_type_class_peek_parent (class);
500 
501   vadjustment_key_id = g_quark_from_static_string ("gtk-vadjustment");
502   hadjustment_key_id = g_quark_from_static_string ("gtk-hadjustment");
503   quark_focus_chain = g_quark_from_static_string ("gtk-container-focus-chain");
504 
505   gobject_class->set_property = gtk_container_set_property;
506   gobject_class->get_property = gtk_container_get_property;
507 
508   widget_class->destroy = gtk_container_destroy;
509   widget_class->compute_expand = gtk_container_compute_expand;
510   widget_class->show_all = gtk_container_show_all;
511   widget_class->draw = gtk_container_draw;
512   widget_class->map = gtk_container_map;
513   widget_class->unmap = gtk_container_unmap;
514   widget_class->focus = gtk_container_focus;
515 
516   widget_class->adjust_size_request = gtk_container_adjust_size_request;
517   widget_class->adjust_baseline_request = gtk_container_adjust_baseline_request;
518   widget_class->adjust_size_allocation = gtk_container_adjust_size_allocation;
519   widget_class->adjust_baseline_allocation = gtk_container_adjust_baseline_allocation;
520   widget_class->get_request_mode = gtk_container_get_request_mode;
521 
522   class->add = gtk_container_add_unimplemented;
523   class->remove = gtk_container_remove_unimplemented;
524   class->check_resize = gtk_container_real_check_resize;
525   class->forall = NULL;
526   class->set_focus_child = gtk_container_real_set_focus_child;
527   class->child_type = NULL;
528   class->composite_name = gtk_container_child_default_composite_name;
529   class->get_path_for_child = gtk_container_real_get_path_for_child;
530 
531   container_props[PROP_RESIZE_MODE] =
532       g_param_spec_enum ("resize-mode",
533                          P_("Resize mode"),
534                          P_("Specify how resize events are handled"),
535                          GTK_TYPE_RESIZE_MODE,
536                          GTK_RESIZE_PARENT,
537                          GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_DEPRECATED);
538 
539   container_props[PROP_BORDER_WIDTH] =
540       g_param_spec_uint ("border-width",
541                          P_("Border width"),
542                          P_("The width of the empty border outside the containers children"),
543                          0, 65535,
544                          0,
545                          GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
546 
547   container_props[PROP_CHILD] =
548       g_param_spec_object ("child",
549                            P_("Child"),
550                            P_("Can be used to add a new child to the container"),
551                            GTK_TYPE_WIDGET,
552                            GTK_PARAM_WRITABLE|G_PARAM_DEPRECATED);
553 
554   g_object_class_install_properties (gobject_class, LAST_PROP, container_props);
555 
556   container_signals[ADD] =
557     g_signal_new (I_("add"),
558                   G_OBJECT_CLASS_TYPE (gobject_class),
559                   G_SIGNAL_RUN_FIRST,
560                   G_STRUCT_OFFSET (GtkContainerClass, add),
561                   NULL, NULL,
562                   NULL,
563                   G_TYPE_NONE, 1,
564                   GTK_TYPE_WIDGET);
565   container_signals[REMOVE] =
566     g_signal_new (I_("remove"),
567                   G_OBJECT_CLASS_TYPE (gobject_class),
568                   G_SIGNAL_RUN_FIRST,
569                   G_STRUCT_OFFSET (GtkContainerClass, remove),
570                   NULL, NULL,
571                   NULL,
572                   G_TYPE_NONE, 1,
573                   GTK_TYPE_WIDGET);
574   container_signals[CHECK_RESIZE] =
575     g_signal_new (I_("check-resize"),
576                   G_OBJECT_CLASS_TYPE (gobject_class),
577                   G_SIGNAL_RUN_LAST,
578                   G_STRUCT_OFFSET (GtkContainerClass, check_resize),
579                   NULL, NULL,
580                   NULL,
581                   G_TYPE_NONE, 0);
582   container_signals[SET_FOCUS_CHILD] =
583     g_signal_new (I_("set-focus-child"),
584                   G_OBJECT_CLASS_TYPE (gobject_class),
585                   G_SIGNAL_RUN_FIRST,
586                   G_STRUCT_OFFSET (GtkContainerClass, set_focus_child),
587                   NULL, NULL,
588                   NULL,
589                   G_TYPE_NONE, 1,
590                   GTK_TYPE_WIDGET);
591 
592   if (GtkContainer_private_offset != 0)
593     g_type_class_adjust_private_offset (class, &GtkContainer_private_offset);
594 
595   gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_CONTAINER_ACCESSIBLE);
596 }
597 
598 static void
gtk_container_buildable_init(GtkBuildableIface * iface)599 gtk_container_buildable_init (GtkBuildableIface *iface)
600 {
601   parent_buildable_iface = g_type_interface_peek_parent (iface);
602   iface->add_child = gtk_container_buildable_add_child;
603   iface->custom_tag_start = gtk_container_buildable_custom_tag_start;
604   iface->custom_tag_end = gtk_container_buildable_custom_tag_end;
605   iface->custom_finished = gtk_container_buildable_custom_finished;
606 }
607 
608 static void
gtk_container_buildable_add_child(GtkBuildable * buildable,GtkBuilder * builder,GObject * child,const gchar * type)609 gtk_container_buildable_add_child (GtkBuildable  *buildable,
610                                    GtkBuilder    *builder,
611                                    GObject       *child,
612                                    const gchar   *type)
613 {
614   if (type)
615     {
616       GTK_BUILDER_WARN_INVALID_CHILD_TYPE (buildable, type);
617     }
618   else if (GTK_IS_WIDGET (child) &&
619            _gtk_widget_get_parent (GTK_WIDGET (child)) == NULL)
620     {
621       gtk_container_add (GTK_CONTAINER (buildable), GTK_WIDGET (child));
622     }
623   else
624     g_warning ("Cannot add an object of type %s to a container of type %s",
625                g_type_name (G_OBJECT_TYPE (child)), g_type_name (G_OBJECT_TYPE (buildable)));
626 }
627 
628 static inline void
container_set_child_property(GtkContainer * container,GtkWidget * child,GParamSpec * pspec,const GValue * value,GObjectNotifyQueue * nqueue)629 container_set_child_property (GtkContainer       *container,
630                               GtkWidget          *child,
631                               GParamSpec         *pspec,
632                               const GValue       *value,
633                               GObjectNotifyQueue *nqueue)
634 {
635   GValue tmp_value = G_VALUE_INIT;
636   GtkContainerClass *class = g_type_class_peek (pspec->owner_type);
637 
638   /* provide a copy to work from, convert (if necessary) and validate */
639   g_value_init (&tmp_value, G_PARAM_SPEC_VALUE_TYPE (pspec));
640   if (!g_value_transform (value, &tmp_value))
641     g_warning ("unable to set child property '%s' of type '%s' from value of type '%s'",
642                pspec->name,
643                g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec)),
644                G_VALUE_TYPE_NAME (value));
645   else if (g_param_value_validate (pspec, &tmp_value) && !(pspec->flags & G_PARAM_LAX_VALIDATION))
646     {
647       gchar *contents = g_strdup_value_contents (value);
648 
649       g_warning ("value \"%s\" of type '%s' is invalid for property '%s' of type '%s'",
650                  contents,
651                  G_VALUE_TYPE_NAME (value),
652                  pspec->name,
653                  g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec)));
654       g_free (contents);
655     }
656   else
657     {
658       class->set_child_property (container, child, PARAM_SPEC_PARAM_ID (pspec), &tmp_value, pspec);
659       g_object_notify_queue_add (G_OBJECT (child), nqueue, pspec);
660     }
661   g_value_unset (&tmp_value);
662 }
663 
664 static void
gtk_container_buildable_set_child_property(GtkContainer * container,GtkBuilder * builder,GtkWidget * child,gchar * name,const gchar * value)665 gtk_container_buildable_set_child_property (GtkContainer *container,
666                                             GtkBuilder   *builder,
667                                             GtkWidget    *child,
668                                             gchar        *name,
669                                             const gchar  *value)
670 {
671   GParamSpec *pspec;
672   GValue gvalue = G_VALUE_INIT;
673   GError *error = NULL;
674   GObjectNotifyQueue *nqueue;
675 
676   if (_gtk_widget_get_parent (child) != (GtkWidget *)container &&
677       !SPECIAL_CONTAINER (container))
678     {
679       /* This can happen with internal children of complex widgets.
680        * Silently ignore the child properties in this case. We explicitly
681        * allow it for GtkAssistant, since that is how it works.
682        */
683       return;
684     }
685 
686   pspec = gtk_container_class_find_child_property (G_OBJECT_GET_CLASS (container), name);
687   if (!pspec)
688     {
689       g_warning ("%s does not have a child property called %s",
690                  G_OBJECT_TYPE_NAME (container), name);
691       return;
692     }
693   else if (!(pspec->flags & G_PARAM_WRITABLE))
694     {
695       g_warning ("Child property '%s' of container class '%s' is not writable",
696                  name, G_OBJECT_TYPE_NAME (container));
697       return;
698     }
699 
700   if (!gtk_builder_value_from_string (builder, pspec, value, &gvalue, &error))
701     {
702       g_warning ("Could not read property %s:%s with value %s of type %s: %s",
703                  g_type_name (G_OBJECT_TYPE (container)),
704                  name,
705                  value,
706                  g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec)),
707                  error->message);
708       g_error_free (error);
709       return;
710     }
711 
712   g_object_ref (container);
713   g_object_ref (child);
714   nqueue = g_object_notify_queue_freeze (G_OBJECT (child), _gtk_widget_child_property_notify_context);
715   container_set_child_property (container, child, pspec, &gvalue, nqueue);
716   g_object_notify_queue_thaw (G_OBJECT (child), nqueue);
717   g_object_unref (container);
718   g_object_unref (child);
719   g_value_unset (&gvalue);
720 }
721 
722 typedef struct {
723   GtkBuilder   *builder;
724   GtkContainer *container;
725   GtkWidget    *child;
726   GString      *string;
727   gchar        *child_prop_name;
728   gchar        *context;
729   gboolean      translatable;
730 } PackingData;
731 
732 static void
packing_start_element(GMarkupParseContext * context,const gchar * element_name,const gchar ** names,const gchar ** values,gpointer user_data,GError ** error)733 packing_start_element (GMarkupParseContext  *context,
734                        const gchar          *element_name,
735                        const gchar         **names,
736                        const gchar         **values,
737                        gpointer              user_data,
738                        GError              **error)
739 {
740   PackingData *data = (PackingData*)user_data;
741 
742   if (strcmp (element_name, "property") == 0)
743     {
744       const gchar *name;
745       gboolean translatable = FALSE;
746       const gchar *ctx = NULL;
747 
748       if (!_gtk_builder_check_parent (data->builder, context, "packing", error))
749         return;
750 
751       if (!g_markup_collect_attributes (element_name, names, values, error,
752                                         G_MARKUP_COLLECT_STRING, "name", &name,
753                                         G_MARKUP_COLLECT_BOOLEAN|G_MARKUP_COLLECT_OPTIONAL, "translatable", &translatable,
754                                         G_MARKUP_COLLECT_STRING|G_MARKUP_COLLECT_OPTIONAL, "comments", NULL,
755                                         G_MARKUP_COLLECT_STRING|G_MARKUP_COLLECT_OPTIONAL, "context", &ctx,
756                                         G_MARKUP_COLLECT_INVALID))
757        {
758          _gtk_builder_prefix_error (data->builder, context, error);
759          return;
760        }
761 
762      data->child_prop_name = g_strdup (name);
763      data->translatable = translatable;
764      data->context = g_strdup (ctx);
765     }
766   else if (strcmp (element_name, "packing") == 0)
767     {
768       if (!_gtk_builder_check_parent (data->builder, context, "child", error))
769         return;
770 
771       if (!g_markup_collect_attributes (element_name, names, values, error,
772                                         G_MARKUP_COLLECT_INVALID, NULL, NULL,
773                                         G_MARKUP_COLLECT_INVALID))
774         _gtk_builder_prefix_error (data->builder, context, error);
775     }
776   else
777     {
778       _gtk_builder_error_unhandled_tag (data->builder, context,
779                                         "GtkContainer", element_name,
780                                         error);
781     }
782 }
783 
784 static void
packing_text_element(GMarkupParseContext * context,const gchar * text,gsize text_len,gpointer user_data,GError ** error)785 packing_text_element (GMarkupParseContext  *context,
786                       const gchar          *text,
787                       gsize                 text_len,
788                       gpointer              user_data,
789                       GError              **error)
790 {
791   PackingData *data = (PackingData*)user_data;
792 
793   if (data->child_prop_name)
794     g_string_append_len (data->string, text, text_len);
795 }
796 
797 static void
packing_end_element(GMarkupParseContext * context,const gchar * element_name,gpointer user_data,GError ** error)798 packing_end_element (GMarkupParseContext  *context,
799                      const gchar          *element_name,
800                      gpointer              user_data,
801                      GError              **error)
802 {
803   PackingData *data = (PackingData*)user_data;
804 
805   /* translate the string */
806   if (data->string->len && data->translatable)
807     {
808       const gchar *translated;
809       const gchar *domain;
810 
811       domain = gtk_builder_get_translation_domain (data->builder);
812 
813       translated = _gtk_builder_parser_translate (domain,
814                                                   data->context,
815                                                   data->string->str);
816       g_string_assign (data->string, translated);
817     }
818 
819   if (data->child_prop_name)
820     gtk_container_buildable_set_child_property (data->container,
821                                                 data->builder,
822                                                 data->child,
823                                                 data->child_prop_name,
824                                                 data->string->str);
825 
826   g_string_set_size (data->string, 0);
827   g_clear_pointer (&data->child_prop_name, g_free);
828   g_clear_pointer (&data->context, g_free);
829   data->translatable = FALSE;
830 }
831 
832 static const GMarkupParser packing_parser =
833   {
834     packing_start_element,
835     packing_end_element,
836     packing_text_element,
837   };
838 
839 typedef struct
840   {
841     gchar *name;
842     gint line;
843     gint col;
844   } FocusChainWidget;
845 
846 static void
focus_chain_widget_free(gpointer data)847 focus_chain_widget_free (gpointer data)
848 {
849   FocusChainWidget *fcw = data;
850 
851   g_free (fcw->name);
852   g_free (fcw);
853 }
854 
855 typedef struct
856   {
857     GSList *items;
858     GObject *object;
859     GtkBuilder *builder;
860     gint line;
861     gint col;
862   } FocusChainData;
863 
864 static void
focus_chain_start_element(GMarkupParseContext * context,const gchar * element_name,const gchar ** names,const gchar ** values,gpointer user_data,GError ** error)865 focus_chain_start_element (GMarkupParseContext  *context,
866                            const gchar          *element_name,
867                            const gchar         **names,
868                            const gchar         **values,
869                            gpointer              user_data,
870                            GError              **error)
871 {
872   FocusChainData *data = (FocusChainData*)user_data;
873 
874   if (strcmp (element_name, "widget") == 0)
875     {
876       const gchar *name;
877       FocusChainWidget *fcw;
878 
879       if (!_gtk_builder_check_parent (data->builder, context, "focus-chain", error))
880         return;
881 
882       if (!g_markup_collect_attributes (element_name, names, values, error,
883                                         G_MARKUP_COLLECT_STRING, "name", &name,
884                                         G_MARKUP_COLLECT_INVALID))
885         {
886           _gtk_builder_prefix_error (data->builder, context, error);
887           return;
888         }
889 
890       fcw = g_new (FocusChainWidget, 1);
891       fcw->name = g_strdup (name);
892       g_markup_parse_context_get_position (context, &fcw->line, &fcw->col);
893       data->items = g_slist_prepend (data->items, fcw);
894     }
895   else if (strcmp (element_name, "focus-chain") == 0)
896     {
897       if (!_gtk_builder_check_parent (data->builder, context, "object", error))
898         return;
899 
900       if (!g_markup_collect_attributes (element_name, names, values, error,
901                                         G_MARKUP_COLLECT_INVALID, "", NULL,
902                                         G_MARKUP_COLLECT_INVALID))
903         _gtk_builder_prefix_error (data->builder, context, error);
904     }
905   else
906     {
907       _gtk_builder_error_unhandled_tag (data->builder, context,
908                                         "GtkContainer", element_name,
909                                         error);
910     }
911 }
912 
913 static const GMarkupParser focus_chain_parser =
914   {
915     focus_chain_start_element
916   };
917 
918 static gboolean
gtk_container_buildable_custom_tag_start(GtkBuildable * buildable,GtkBuilder * builder,GObject * child,const gchar * tagname,GMarkupParser * parser,gpointer * parser_data)919 gtk_container_buildable_custom_tag_start (GtkBuildable  *buildable,
920                                           GtkBuilder    *builder,
921                                           GObject       *child,
922                                           const gchar   *tagname,
923                                           GMarkupParser *parser,
924                                           gpointer      *parser_data)
925 {
926   if (parent_buildable_iface->custom_tag_start (buildable, builder, child,
927                                                 tagname, parser, parser_data))
928     return TRUE;
929 
930   if (child && strcmp (tagname, "packing") == 0)
931     {
932       PackingData *data;
933 
934       data = g_slice_new0 (PackingData);
935       data->string = g_string_new ("");
936       data->builder = builder;
937       data->container = GTK_CONTAINER (buildable);
938       data->child = GTK_WIDGET (child);
939       data->child_prop_name = NULL;
940 
941       *parser = packing_parser;
942       *parser_data = data;
943 
944       return TRUE;
945     }
946   else if (!child && strcmp (tagname, "focus-chain") == 0)
947     {
948       FocusChainData *data;
949 
950       data = g_slice_new0 (FocusChainData);
951       data->items = NULL;
952       data->object = G_OBJECT (buildable);
953       data->builder = builder;
954 
955       *parser = focus_chain_parser;
956       *parser_data = data;
957 
958       return TRUE;
959     }
960 
961   return FALSE;
962 }
963 
964 static void
gtk_container_buildable_custom_tag_end(GtkBuildable * buildable,GtkBuilder * builder,GObject * child,const gchar * tagname,gpointer * parser_data)965 gtk_container_buildable_custom_tag_end (GtkBuildable *buildable,
966                                         GtkBuilder   *builder,
967                                         GObject      *child,
968                                         const gchar  *tagname,
969                                         gpointer     *parser_data)
970 {
971   if (strcmp (tagname, "packing") == 0)
972     {
973       PackingData *data = (PackingData*)parser_data;
974 
975       g_string_free (data->string, TRUE);
976       g_slice_free (PackingData, data);
977 
978       return;
979     }
980 
981   if (parent_buildable_iface->custom_tag_end)
982     parent_buildable_iface->custom_tag_end (buildable, builder,
983                                             child, tagname, parser_data);
984 }
985 
986 static void
gtk_container_buildable_custom_finished(GtkBuildable * buildable,GtkBuilder * builder,GObject * child,const gchar * tagname,gpointer parser_data)987 gtk_container_buildable_custom_finished (GtkBuildable *buildable,
988                                          GtkBuilder   *builder,
989                                          GObject      *child,
990                                          const gchar  *tagname,
991                                          gpointer      parser_data)
992 {
993    if (strcmp (tagname, "focus-chain") == 0)
994     {
995       FocusChainData *data = (FocusChainData*)parser_data;
996       FocusChainWidget *fcw;
997       GSList *l;
998       GList *chain;
999       GObject *object;
1000 
1001       chain = NULL;
1002       for (l = data->items; l; l = l->next)
1003         {
1004           fcw = l->data;
1005           object = _gtk_builder_lookup_object (builder, fcw->name, fcw->line, fcw->col);
1006           if (!object)
1007             continue;
1008           chain = g_list_prepend (chain, object);
1009         }
1010 
1011       gtk_container_set_focus_chain (GTK_CONTAINER (data->object), chain);
1012       g_list_free (chain);
1013 
1014       g_slist_free_full (data->items, focus_chain_widget_free);
1015       g_slice_free (FocusChainData, data);
1016 
1017       return;
1018     }
1019 
1020   if (parent_buildable_iface->custom_finished)
1021     parent_buildable_iface->custom_finished (buildable, builder,
1022                                              child, tagname, parser_data);
1023 }
1024 
1025 /**
1026  * gtk_container_child_type:
1027  * @container: a #GtkContainer
1028  *
1029  * Returns the type of the children supported by the container.
1030  *
1031  * Note that this may return %G_TYPE_NONE to indicate that no more
1032  * children can be added, e.g. for a #GtkPaned which already has two
1033  * children.
1034  *
1035  * Returns: a #GType.
1036  **/
1037 GType
gtk_container_child_type(GtkContainer * container)1038 gtk_container_child_type (GtkContainer *container)
1039 {
1040   GType slot;
1041   GtkContainerClass *class;
1042 
1043   g_return_val_if_fail (GTK_IS_CONTAINER (container), 0);
1044 
1045   class = GTK_CONTAINER_GET_CLASS (container);
1046   if (class->child_type)
1047     slot = class->child_type (container);
1048   else
1049     slot = G_TYPE_NONE;
1050 
1051   return slot;
1052 }
1053 
1054 /* --- GtkContainer child property mechanism --- */
1055 
1056 /**
1057  * gtk_container_child_notify:
1058  * @container: the #GtkContainer
1059  * @child: the child widget
1060  * @child_property: the name of a child property installed on
1061  *     the class of @container
1062  *
1063  * Emits a #GtkWidget::child-notify signal for the
1064  * [child property][child-properties]
1065  * @child_property on the child.
1066  *
1067  * This is an analogue of g_object_notify() for child properties.
1068  *
1069  * Also see gtk_widget_child_notify().
1070  *
1071  * Since: 3.2
1072  */
1073 void
gtk_container_child_notify(GtkContainer * container,GtkWidget * child,const gchar * child_property)1074 gtk_container_child_notify (GtkContainer *container,
1075                             GtkWidget    *child,
1076                             const gchar  *child_property)
1077 {
1078   GObject *obj;
1079   GParamSpec *pspec;
1080 
1081   g_return_if_fail (GTK_IS_CONTAINER (container));
1082   g_return_if_fail (GTK_IS_WIDGET (child));
1083   g_return_if_fail (child_property != NULL);
1084 
1085   obj = G_OBJECT (child);
1086 
1087   if (obj->ref_count == 0)
1088     return;
1089 
1090   g_object_ref (obj);
1091 
1092   pspec = g_param_spec_pool_lookup (_gtk_widget_child_property_pool,
1093                                     child_property,
1094                                     G_OBJECT_TYPE (container),
1095                                     TRUE);
1096 
1097   if (pspec == NULL)
1098     {
1099       g_warning ("%s: container class '%s' has no child property named '%s'",
1100                  G_STRLOC,
1101                  G_OBJECT_TYPE_NAME (container),
1102                  child_property);
1103     }
1104   else
1105     {
1106       GObjectNotifyQueue *nqueue;
1107 
1108       nqueue = g_object_notify_queue_freeze (obj, _gtk_widget_child_property_notify_context);
1109 
1110       g_object_notify_queue_add (obj, nqueue, pspec);
1111       g_object_notify_queue_thaw (obj, nqueue);
1112     }
1113 
1114   g_object_unref (obj);
1115 }
1116 
1117 /**
1118  * gtk_container_child_notify_by_pspec:
1119  * @container: the #GtkContainer
1120  * @child: the child widget
1121  * @pspec: the #GParamSpec of a child property instealled on
1122  *     the class of @container
1123  *
1124  * Emits a #GtkWidget::child-notify signal for the
1125  * [child property][child-properties] specified by
1126  * @pspec on the child.
1127  *
1128  * This is an analogue of g_object_notify_by_pspec() for child properties.
1129  *
1130  * Since: 3.18
1131  */
1132 void
gtk_container_child_notify_by_pspec(GtkContainer * container,GtkWidget * child,GParamSpec * pspec)1133 gtk_container_child_notify_by_pspec (GtkContainer *container,
1134                                      GtkWidget    *child,
1135                                      GParamSpec   *pspec)
1136 {
1137   GObject *obj = G_OBJECT (child);
1138   GObjectNotifyQueue *nqueue;
1139 
1140   g_return_if_fail (GTK_IS_CONTAINER (container));
1141   g_return_if_fail (GTK_IS_WIDGET (child));
1142   g_return_if_fail (G_IS_PARAM_SPEC (pspec));
1143 
1144   if (obj->ref_count == 0)
1145     return;
1146 
1147   g_object_ref (obj);
1148 
1149   nqueue = g_object_notify_queue_freeze (obj, _gtk_widget_child_property_notify_context);
1150 
1151   g_object_notify_queue_add (obj, nqueue, pspec);
1152   g_object_notify_queue_thaw (obj, nqueue);
1153 
1154   g_object_unref (obj);
1155 }
1156 
1157 static inline void
container_get_child_property(GtkContainer * container,GtkWidget * child,GParamSpec * pspec,GValue * value)1158 container_get_child_property (GtkContainer *container,
1159                               GtkWidget    *child,
1160                               GParamSpec   *pspec,
1161                               GValue       *value)
1162 {
1163   GtkContainerClass *class = g_type_class_peek (pspec->owner_type);
1164 
1165   class->get_child_property (container, child, PARAM_SPEC_PARAM_ID (pspec), value, pspec);
1166 }
1167 
1168 /**
1169  * gtk_container_child_get_valist:
1170  * @container: a #GtkContainer
1171  * @child: a widget which is a child of @container
1172  * @first_property_name: the name of the first property to get
1173  * @var_args: return location for the first property, followed
1174  *     optionally by more name/return location pairs, followed by %NULL
1175  *
1176  * Gets the values of one or more child properties for @child and @container.
1177  **/
1178 void
gtk_container_child_get_valist(GtkContainer * container,GtkWidget * child,const gchar * first_property_name,va_list var_args)1179 gtk_container_child_get_valist (GtkContainer *container,
1180                                 GtkWidget    *child,
1181                                 const gchar  *first_property_name,
1182                                 va_list       var_args)
1183 {
1184   const gchar *name;
1185 
1186   g_return_if_fail (GTK_IS_CONTAINER (container));
1187   g_return_if_fail (GTK_IS_WIDGET (child));
1188 
1189   g_object_ref (container);
1190   g_object_ref (child);
1191 
1192   name = first_property_name;
1193   while (name)
1194     {
1195       GValue value = G_VALUE_INIT;
1196       GParamSpec *pspec;
1197       gchar *error;
1198 
1199       pspec = g_param_spec_pool_lookup (_gtk_widget_child_property_pool,
1200                                         name,
1201                                         G_OBJECT_TYPE (container),
1202                                         TRUE);
1203       if (!pspec)
1204         {
1205           g_warning ("%s: container class '%s' has no child property named '%s'",
1206                      G_STRLOC,
1207                      G_OBJECT_TYPE_NAME (container),
1208                      name);
1209           break;
1210         }
1211       if (!(pspec->flags & G_PARAM_READABLE))
1212         {
1213           g_warning ("%s: child property '%s' of container class '%s' is not readable",
1214                      G_STRLOC,
1215                      pspec->name,
1216                      G_OBJECT_TYPE_NAME (container));
1217           break;
1218         }
1219       g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec));
1220       container_get_child_property (container, child, pspec, &value);
1221       G_VALUE_LCOPY (&value, var_args, 0, &error);
1222       if (error)
1223         {
1224           g_warning ("%s: %s", G_STRLOC, error);
1225           g_free (error);
1226           g_value_unset (&value);
1227           break;
1228         }
1229       g_value_unset (&value);
1230       name = va_arg (var_args, gchar*);
1231     }
1232 
1233   g_object_unref (child);
1234   g_object_unref (container);
1235 }
1236 
1237 /**
1238  * gtk_container_child_get_property:
1239  * @container: a #GtkContainer
1240  * @child: a widget which is a child of @container
1241  * @property_name: the name of the property to get
1242  * @value: a location to return the value
1243  *
1244  * Gets the value of a child property for @child and @container.
1245  **/
1246 void
gtk_container_child_get_property(GtkContainer * container,GtkWidget * child,const gchar * property_name,GValue * value)1247 gtk_container_child_get_property (GtkContainer *container,
1248                                   GtkWidget    *child,
1249                                   const gchar  *property_name,
1250                                   GValue       *value)
1251 {
1252   GParamSpec *pspec;
1253 
1254   g_return_if_fail (GTK_IS_CONTAINER (container));
1255   g_return_if_fail (GTK_IS_WIDGET (child));
1256   g_return_if_fail (property_name != NULL);
1257   g_return_if_fail (G_IS_VALUE (value));
1258 
1259   g_object_ref (container);
1260   g_object_ref (child);
1261   pspec = g_param_spec_pool_lookup (_gtk_widget_child_property_pool, property_name,
1262                                     G_OBJECT_TYPE (container), TRUE);
1263   if (!pspec)
1264     g_warning ("%s: container class '%s' has no child property named '%s'",
1265                G_STRLOC,
1266                G_OBJECT_TYPE_NAME (container),
1267                property_name);
1268   else if (!(pspec->flags & G_PARAM_READABLE))
1269     g_warning ("%s: child property '%s' of container class '%s' is not readable",
1270                G_STRLOC,
1271                pspec->name,
1272                G_OBJECT_TYPE_NAME (container));
1273   else
1274     {
1275       GValue *prop_value, tmp_value = G_VALUE_INIT;
1276 
1277       /* auto-conversion of the callers value type
1278        */
1279       if (G_VALUE_TYPE (value) == G_PARAM_SPEC_VALUE_TYPE (pspec))
1280         {
1281           g_value_reset (value);
1282           prop_value = value;
1283         }
1284       else if (!g_value_type_transformable (G_PARAM_SPEC_VALUE_TYPE (pspec), G_VALUE_TYPE (value)))
1285         {
1286           g_warning ("can't retrieve child property '%s' of type '%s' as value of type '%s'",
1287                      pspec->name,
1288                      g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec)),
1289                      G_VALUE_TYPE_NAME (value));
1290           g_object_unref (child);
1291           g_object_unref (container);
1292           return;
1293         }
1294       else
1295         {
1296           g_value_init (&tmp_value, G_PARAM_SPEC_VALUE_TYPE (pspec));
1297           prop_value = &tmp_value;
1298         }
1299       container_get_child_property (container, child, pspec, prop_value);
1300       if (prop_value != value)
1301         {
1302           g_value_transform (prop_value, value);
1303           g_value_unset (&tmp_value);
1304         }
1305     }
1306   g_object_unref (child);
1307   g_object_unref (container);
1308 }
1309 
1310 /**
1311  * gtk_container_child_set_valist:
1312  * @container: a #GtkContainer
1313  * @child: a widget which is a child of @container
1314  * @first_property_name: the name of the first property to set
1315  * @var_args: a %NULL-terminated list of property names and values, starting
1316  *           with @first_prop_name
1317  *
1318  * Sets one or more child properties for @child and @container.
1319  **/
1320 void
gtk_container_child_set_valist(GtkContainer * container,GtkWidget * child,const gchar * first_property_name,va_list var_args)1321 gtk_container_child_set_valist (GtkContainer *container,
1322                                 GtkWidget    *child,
1323                                 const gchar  *first_property_name,
1324                                 va_list       var_args)
1325 {
1326   GObjectNotifyQueue *nqueue;
1327   const gchar *name;
1328 
1329   g_return_if_fail (GTK_IS_CONTAINER (container));
1330   g_return_if_fail (GTK_IS_WIDGET (child));
1331 
1332   g_object_ref (container);
1333   g_object_ref (child);
1334 
1335   nqueue = g_object_notify_queue_freeze (G_OBJECT (child), _gtk_widget_child_property_notify_context);
1336   name = first_property_name;
1337   while (name)
1338     {
1339       GValue value = G_VALUE_INIT;
1340       gchar *error = NULL;
1341       GParamSpec *pspec = g_param_spec_pool_lookup (_gtk_widget_child_property_pool,
1342                                                     name,
1343                                                     G_OBJECT_TYPE (container),
1344                                                     TRUE);
1345       if (!pspec)
1346         {
1347           g_warning ("%s: container class '%s' has no child property named '%s'",
1348                      G_STRLOC,
1349                      G_OBJECT_TYPE_NAME (container),
1350                      name);
1351           break;
1352         }
1353       if (!(pspec->flags & G_PARAM_WRITABLE))
1354         {
1355           g_warning ("%s: child property '%s' of container class '%s' is not writable",
1356                      G_STRLOC,
1357                      pspec->name,
1358                      G_OBJECT_TYPE_NAME (container));
1359           break;
1360         }
1361 
1362       G_VALUE_COLLECT_INIT (&value, G_PARAM_SPEC_VALUE_TYPE (pspec),
1363                             var_args, 0, &error);
1364       if (error)
1365         {
1366           g_warning ("%s: %s", G_STRLOC, error);
1367           g_free (error);
1368 
1369           /* we purposely leak the value here, it might not be
1370            * in a sane state if an error condition occoured
1371            */
1372           break;
1373         }
1374       container_set_child_property (container, child, pspec, &value, nqueue);
1375       g_value_unset (&value);
1376       name = va_arg (var_args, gchar*);
1377     }
1378   g_object_notify_queue_thaw (G_OBJECT (child), nqueue);
1379 
1380   g_object_unref (container);
1381   g_object_unref (child);
1382 }
1383 
1384 /**
1385  * gtk_container_child_set_property:
1386  * @container: a #GtkContainer
1387  * @child: a widget which is a child of @container
1388  * @property_name: the name of the property to set
1389  * @value: the value to set the property to
1390  *
1391  * Sets a child property for @child and @container.
1392  **/
1393 void
gtk_container_child_set_property(GtkContainer * container,GtkWidget * child,const gchar * property_name,const GValue * value)1394 gtk_container_child_set_property (GtkContainer *container,
1395                                   GtkWidget    *child,
1396                                   const gchar  *property_name,
1397                                   const GValue *value)
1398 {
1399   GObjectNotifyQueue *nqueue;
1400   GParamSpec *pspec;
1401 
1402   g_return_if_fail (GTK_IS_CONTAINER (container));
1403   g_return_if_fail (GTK_IS_WIDGET (child));
1404   g_return_if_fail (property_name != NULL);
1405   g_return_if_fail (G_IS_VALUE (value));
1406 
1407   g_object_ref (container);
1408   g_object_ref (child);
1409 
1410   nqueue = g_object_notify_queue_freeze (G_OBJECT (child), _gtk_widget_child_property_notify_context);
1411   pspec = g_param_spec_pool_lookup (_gtk_widget_child_property_pool, property_name,
1412                                     G_OBJECT_TYPE (container), TRUE);
1413   if (!pspec)
1414     g_warning ("%s: container class '%s' has no child property named '%s'",
1415                G_STRLOC,
1416                G_OBJECT_TYPE_NAME (container),
1417                property_name);
1418   else if (!(pspec->flags & G_PARAM_WRITABLE))
1419     g_warning ("%s: child property '%s' of container class '%s' is not writable",
1420                G_STRLOC,
1421                pspec->name,
1422                G_OBJECT_TYPE_NAME (container));
1423   else
1424     {
1425       container_set_child_property (container, child, pspec, value, nqueue);
1426     }
1427   g_object_notify_queue_thaw (G_OBJECT (child), nqueue);
1428   g_object_unref (container);
1429   g_object_unref (child);
1430 }
1431 
1432 /**
1433  * gtk_container_add_with_properties:
1434  * @container: a #GtkContainer
1435  * @widget: a widget to be placed inside @container
1436  * @first_prop_name: the name of the first child property to set
1437  * @...: a %NULL-terminated list of property names and values, starting
1438  *     with @first_prop_name
1439  *
1440  * Adds @widget to @container, setting child properties at the same time.
1441  * See gtk_container_add() and gtk_container_child_set() for more details.
1442  */
1443 void
gtk_container_add_with_properties(GtkContainer * container,GtkWidget * widget,const gchar * first_prop_name,...)1444 gtk_container_add_with_properties (GtkContainer *container,
1445                                    GtkWidget    *widget,
1446                                    const gchar  *first_prop_name,
1447                                    ...)
1448 {
1449   g_return_if_fail (GTK_IS_CONTAINER (container));
1450   g_return_if_fail (GTK_IS_WIDGET (widget));
1451   g_return_if_fail (_gtk_widget_get_parent (widget) == NULL);
1452 
1453   g_object_ref (container);
1454   g_object_ref (widget);
1455   gtk_widget_freeze_child_notify (widget);
1456 
1457   g_signal_emit (container, container_signals[ADD], 0, widget);
1458   if (_gtk_widget_get_parent (widget))
1459     {
1460       va_list var_args;
1461 
1462       va_start (var_args, first_prop_name);
1463       gtk_container_child_set_valist (container, widget, first_prop_name, var_args);
1464       va_end (var_args);
1465     }
1466 
1467   gtk_widget_thaw_child_notify (widget);
1468   g_object_unref (widget);
1469   g_object_unref (container);
1470 }
1471 
1472 /**
1473  * gtk_container_child_set:
1474  * @container: a #GtkContainer
1475  * @child: a widget which is a child of @container
1476  * @first_prop_name: the name of the first property to set
1477  * @...: a %NULL-terminated list of property names and values, starting
1478  *     with @first_prop_name
1479  *
1480  * Sets one or more child properties for @child and @container.
1481  */
1482 void
gtk_container_child_set(GtkContainer * container,GtkWidget * child,const gchar * first_prop_name,...)1483 gtk_container_child_set (GtkContainer      *container,
1484                          GtkWidget         *child,
1485                          const gchar       *first_prop_name,
1486                          ...)
1487 {
1488   va_list var_args;
1489 
1490   va_start (var_args, first_prop_name);
1491   gtk_container_child_set_valist (container, child, first_prop_name, var_args);
1492   va_end (var_args);
1493 }
1494 
1495 /**
1496  * gtk_container_child_get:
1497  * @container: a #GtkContainer
1498  * @child: a widget which is a child of @container
1499  * @first_prop_name: the name of the first property to get
1500  * @...: return location for the first property, followed
1501  *     optionally by more name/return location pairs, followed by %NULL
1502  *
1503  * Gets the values of one or more child properties for @child and @container.
1504  */
1505 void
gtk_container_child_get(GtkContainer * container,GtkWidget * child,const gchar * first_prop_name,...)1506 gtk_container_child_get (GtkContainer      *container,
1507                          GtkWidget         *child,
1508                          const gchar       *first_prop_name,
1509                          ...)
1510 {
1511   va_list var_args;
1512 
1513   va_start (var_args, first_prop_name);
1514   gtk_container_child_get_valist (container, child, first_prop_name, var_args);
1515   va_end (var_args);
1516 }
1517 
1518 static inline void
install_child_property_internal(GType g_type,guint property_id,GParamSpec * pspec)1519 install_child_property_internal (GType       g_type,
1520                                  guint       property_id,
1521                                  GParamSpec *pspec)
1522 {
1523   if (g_param_spec_pool_lookup (_gtk_widget_child_property_pool, pspec->name, g_type, FALSE))
1524     {
1525       g_warning ("Class '%s' already contains a child property named '%s'",
1526                  g_type_name (g_type),
1527                  pspec->name);
1528       return;
1529     }
1530   g_param_spec_ref (pspec);
1531   g_param_spec_sink (pspec);
1532   PARAM_SPEC_SET_PARAM_ID (pspec, property_id);
1533   g_param_spec_pool_insert (_gtk_widget_child_property_pool, pspec, g_type);
1534 }
1535 
1536 /**
1537  * gtk_container_class_install_child_property:
1538  * @cclass: a #GtkContainerClass
1539  * @property_id: the id for the property
1540  * @pspec: the #GParamSpec for the property
1541  *
1542  * Installs a child property on a container class.
1543  **/
1544 void
gtk_container_class_install_child_property(GtkContainerClass * cclass,guint property_id,GParamSpec * pspec)1545 gtk_container_class_install_child_property (GtkContainerClass *cclass,
1546                                             guint              property_id,
1547                                             GParamSpec        *pspec)
1548 {
1549   g_return_if_fail (GTK_IS_CONTAINER_CLASS (cclass));
1550   g_return_if_fail (G_IS_PARAM_SPEC (pspec));
1551   if (pspec->flags & G_PARAM_WRITABLE)
1552     g_return_if_fail (cclass->set_child_property != NULL);
1553   if (pspec->flags & G_PARAM_READABLE)
1554     g_return_if_fail (cclass->get_child_property != NULL);
1555   g_return_if_fail (property_id > 0);
1556   g_return_if_fail (PARAM_SPEC_PARAM_ID (pspec) == 0);  /* paranoid */
1557   if (pspec->flags & (G_PARAM_CONSTRUCT | G_PARAM_CONSTRUCT_ONLY))
1558     g_return_if_fail ((pspec->flags & (G_PARAM_CONSTRUCT | G_PARAM_CONSTRUCT_ONLY)) == 0);
1559 
1560   install_child_property_internal (G_OBJECT_CLASS_TYPE (cclass), property_id, pspec);
1561 }
1562 
1563 /**
1564  * gtk_container_class_install_child_properties:
1565  * @cclass: a #GtkContainerClass
1566  * @n_pspecs: the length of the #GParamSpec array
1567  * @pspecs: (array length=n_pspecs): the #GParamSpec array defining the new
1568  *     child properties
1569  *
1570  * Installs child properties on a container class.
1571  *
1572  * Since: 3.18
1573  */
1574 void
gtk_container_class_install_child_properties(GtkContainerClass * cclass,guint n_pspecs,GParamSpec ** pspecs)1575 gtk_container_class_install_child_properties (GtkContainerClass  *cclass,
1576                                               guint               n_pspecs,
1577                                               GParamSpec        **pspecs)
1578 {
1579   gint i;
1580 
1581   g_return_if_fail (GTK_IS_CONTAINER_CLASS (cclass));
1582   g_return_if_fail (n_pspecs > 1);
1583   g_return_if_fail (pspecs[0] == NULL);
1584 
1585   /* we skip the first element of the array as it would have a 0 prop_id */
1586   for (i = 1; i < n_pspecs; i++)
1587     {
1588       GParamSpec *pspec = pspecs[i];
1589 
1590       g_return_if_fail (G_IS_PARAM_SPEC (pspec));
1591       if (pspec->flags & G_PARAM_WRITABLE)
1592         g_return_if_fail (cclass->set_child_property != NULL);
1593       if (pspec->flags & G_PARAM_READABLE)
1594         g_return_if_fail (cclass->get_child_property != NULL);
1595       g_return_if_fail (PARAM_SPEC_PARAM_ID (pspec) == 0);  /* paranoid */
1596       if (pspec->flags & (G_PARAM_CONSTRUCT | G_PARAM_CONSTRUCT_ONLY))
1597         g_return_if_fail ((pspec->flags & (G_PARAM_CONSTRUCT | G_PARAM_CONSTRUCT_ONLY)) == 0);
1598 
1599       install_child_property_internal (G_OBJECT_CLASS_TYPE (cclass), i, pspec);
1600     }
1601 }
1602 
1603 /**
1604  * gtk_container_class_find_child_property:
1605  * @cclass: (type GtkContainerClass): a #GtkContainerClass
1606  * @property_name: the name of the child property to find
1607  *
1608  * Finds a child property of a container class by name.
1609  *
1610  * Returns: (nullable) (transfer none): the #GParamSpec of the child
1611  *     property or %NULL if @class has no child property with that
1612  *     name.
1613  */
1614 GParamSpec*
gtk_container_class_find_child_property(GObjectClass * cclass,const gchar * property_name)1615 gtk_container_class_find_child_property (GObjectClass *cclass,
1616                                          const gchar  *property_name)
1617 {
1618   g_return_val_if_fail (GTK_IS_CONTAINER_CLASS (cclass), NULL);
1619   g_return_val_if_fail (property_name != NULL, NULL);
1620 
1621   return g_param_spec_pool_lookup (_gtk_widget_child_property_pool,
1622                                    property_name,
1623                                    G_OBJECT_CLASS_TYPE (cclass),
1624                                    TRUE);
1625 }
1626 
1627 /**
1628  * gtk_container_class_list_child_properties:
1629  * @cclass: (type GtkContainerClass): a #GtkContainerClass
1630  * @n_properties: location to return the number of child properties found
1631  *
1632  * Returns all child properties of a container class.
1633  *
1634  * Returns: (array length=n_properties) (transfer container):
1635  *     a newly allocated %NULL-terminated array of #GParamSpec*.
1636  *     The array must be freed with g_free().
1637  */
1638 GParamSpec**
gtk_container_class_list_child_properties(GObjectClass * cclass,guint * n_properties)1639 gtk_container_class_list_child_properties (GObjectClass *cclass,
1640                                            guint        *n_properties)
1641 {
1642   GParamSpec **pspecs;
1643   guint n;
1644 
1645   g_return_val_if_fail (GTK_IS_CONTAINER_CLASS (cclass), NULL);
1646 
1647   pspecs = g_param_spec_pool_list (_gtk_widget_child_property_pool,
1648                                    G_OBJECT_CLASS_TYPE (cclass),
1649                                    &n);
1650   if (n_properties)
1651     *n_properties = n;
1652 
1653   return pspecs;
1654 }
1655 
1656 static void
gtk_container_add_unimplemented(GtkContainer * container,GtkWidget * widget)1657 gtk_container_add_unimplemented (GtkContainer     *container,
1658                                  GtkWidget        *widget)
1659 {
1660   g_warning ("GtkContainerClass::add not implemented for '%s'", g_type_name (G_TYPE_FROM_INSTANCE (container)));
1661 }
1662 
1663 static void
gtk_container_remove_unimplemented(GtkContainer * container,GtkWidget * widget)1664 gtk_container_remove_unimplemented (GtkContainer     *container,
1665                                     GtkWidget        *widget)
1666 {
1667   g_warning ("GtkContainerClass::remove not implemented for '%s'", g_type_name (G_TYPE_FROM_INSTANCE (container)));
1668 }
1669 
1670 static void
gtk_container_init(GtkContainer * container)1671 gtk_container_init (GtkContainer *container)
1672 {
1673   GtkContainerPrivate *priv;
1674 
1675   container->priv = gtk_container_get_instance_private (container);
1676   priv = container->priv;
1677 
1678   priv->focus_child = NULL;
1679   priv->border_width = 0;
1680   priv->resize_mode = GTK_RESIZE_PARENT;
1681   priv->reallocate_redraws = FALSE;
1682   priv->border_width_set = FALSE;
1683 }
1684 
1685 static void
gtk_container_destroy(GtkWidget * widget)1686 gtk_container_destroy (GtkWidget *widget)
1687 {
1688   GtkContainer *container = GTK_CONTAINER (widget);
1689   GtkContainerPrivate *priv = container->priv;
1690 
1691   if (priv->restyle_pending)
1692     priv->restyle_pending = FALSE;
1693 
1694   g_clear_object (&priv->focus_child);
1695 
1696   /* do this before walking child widgets, to avoid
1697    * removing children from focus chain one by one.
1698    */
1699   if (priv->has_focus_chain)
1700     gtk_container_unset_focus_chain (container);
1701 
1702   gtk_container_foreach (container, (GtkCallback) gtk_widget_destroy, NULL);
1703 
1704   GTK_WIDGET_CLASS (parent_class)->destroy (widget);
1705 }
1706 
1707 static void
gtk_container_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)1708 gtk_container_set_property (GObject         *object,
1709                             guint            prop_id,
1710                             const GValue    *value,
1711                             GParamSpec      *pspec)
1712 {
1713   GtkContainer *container = GTK_CONTAINER (object);
1714 
1715   switch (prop_id)
1716     {
1717     case PROP_BORDER_WIDTH:
1718       gtk_container_set_border_width (container, g_value_get_uint (value));
1719       break;
1720     case PROP_RESIZE_MODE:
1721       G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
1722       gtk_container_set_resize_mode (container, g_value_get_enum (value));
1723       G_GNUC_END_IGNORE_DEPRECATIONS;
1724       break;
1725     case PROP_CHILD:
1726       gtk_container_add (container, GTK_WIDGET (g_value_get_object (value)));
1727       break;
1728     default:
1729       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1730       break;
1731     }
1732 }
1733 
1734 static void
gtk_container_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)1735 gtk_container_get_property (GObject         *object,
1736                             guint            prop_id,
1737                             GValue          *value,
1738                             GParamSpec      *pspec)
1739 {
1740   GtkContainer *container = GTK_CONTAINER (object);
1741   GtkContainerPrivate *priv = container->priv;
1742 
1743   switch (prop_id)
1744     {
1745     case PROP_BORDER_WIDTH:
1746       g_value_set_uint (value, priv->border_width);
1747       break;
1748     case PROP_RESIZE_MODE:
1749       g_value_set_enum (value, priv->resize_mode);
1750       break;
1751     default:
1752       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1753       break;
1754     }
1755 }
1756 
1757 gboolean
_gtk_container_get_border_width_set(GtkContainer * container)1758 _gtk_container_get_border_width_set (GtkContainer *container)
1759 {
1760   GtkContainerPrivate *priv;
1761 
1762   g_return_val_if_fail (GTK_IS_CONTAINER (container), FALSE);
1763 
1764   priv = container->priv;
1765 
1766   return priv->border_width_set;
1767 }
1768 
1769 void
_gtk_container_set_border_width_set(GtkContainer * container,gboolean border_width_set)1770 _gtk_container_set_border_width_set (GtkContainer *container,
1771                                      gboolean      border_width_set)
1772 {
1773   GtkContainerPrivate *priv;
1774 
1775   g_return_if_fail (GTK_IS_CONTAINER (container));
1776 
1777   priv = container->priv;
1778 
1779   priv->border_width_set = border_width_set ? TRUE : FALSE;
1780 }
1781 
1782 /**
1783  * gtk_container_set_border_width:
1784  * @container: a #GtkContainer
1785  * @border_width: amount of blank space to leave outside
1786  *   the container. Valid values are in the range 0-65535 pixels.
1787  *
1788  * Sets the border width of the container.
1789  *
1790  * The border width of a container is the amount of space to leave
1791  * around the outside of the container. The only exception to this is
1792  * #GtkWindow; because toplevel windows can’t leave space outside,
1793  * they leave the space inside. The border is added on all sides of
1794  * the container. To add space to only one side, use a specific
1795  * #GtkWidget:margin property on the child widget, for example
1796  * #GtkWidget:margin-top.
1797  **/
1798 void
gtk_container_set_border_width(GtkContainer * container,guint border_width)1799 gtk_container_set_border_width (GtkContainer *container,
1800                                 guint         border_width)
1801 {
1802   GtkContainerPrivate *priv;
1803 
1804   g_return_if_fail (GTK_IS_CONTAINER (container));
1805 
1806   priv = container->priv;
1807 
1808   if (priv->border_width != border_width)
1809     {
1810       priv->border_width = border_width;
1811       _gtk_container_set_border_width_set (container, TRUE);
1812 
1813       g_object_notify_by_pspec (G_OBJECT (container), container_props[PROP_BORDER_WIDTH]);
1814 
1815       if (_gtk_widget_get_realized (GTK_WIDGET (container)))
1816         gtk_widget_queue_resize (GTK_WIDGET (container));
1817     }
1818 }
1819 
1820 /**
1821  * gtk_container_get_border_width:
1822  * @container: a #GtkContainer
1823  *
1824  * Retrieves the border width of the container. See
1825  * gtk_container_set_border_width().
1826  *
1827  * Returns: the current border width
1828  **/
1829 guint
gtk_container_get_border_width(GtkContainer * container)1830 gtk_container_get_border_width (GtkContainer *container)
1831 {
1832   g_return_val_if_fail (GTK_IS_CONTAINER (container), 0);
1833 
1834   return container->priv->border_width;
1835 }
1836 
1837 /**
1838  * gtk_container_add:
1839  * @container: a #GtkContainer
1840  * @widget: a widget to be placed inside @container
1841  *
1842  * Adds @widget to @container. Typically used for simple containers
1843  * such as #GtkWindow, #GtkFrame, or #GtkButton; for more complicated
1844  * layout containers such as #GtkBox or #GtkGrid, this function will
1845  * pick default packing parameters that may not be correct.  So
1846  * consider functions such as gtk_box_pack_start() and
1847  * gtk_grid_attach() as an alternative to gtk_container_add() in
1848  * those cases. A widget may be added to only one container at a time;
1849  * you can’t place the same widget inside two different containers.
1850  *
1851  * Note that some containers, such as #GtkScrolledWindow or #GtkListBox,
1852  * may add intermediate children between the added widget and the
1853  * container.
1854  */
1855 void
gtk_container_add(GtkContainer * container,GtkWidget * widget)1856 gtk_container_add (GtkContainer *container,
1857                    GtkWidget    *widget)
1858 {
1859   GtkWidget *parent;
1860 
1861   g_return_if_fail (GTK_IS_CONTAINER (container));
1862   g_return_if_fail (GTK_IS_WIDGET (widget));
1863 
1864   parent = _gtk_widget_get_parent (widget);
1865 
1866   if (parent != NULL)
1867     {
1868       g_warning ("Attempting to add a widget with type %s to a container of "
1869                  "type %s, but the widget is already inside a container of type %s, "
1870                  "please remove the widget from its existing container first." ,
1871                  g_type_name (G_OBJECT_TYPE (widget)),
1872                  g_type_name (G_OBJECT_TYPE (container)),
1873                  g_type_name (G_OBJECT_TYPE (parent)));
1874       return;
1875     }
1876 
1877   g_signal_emit (container, container_signals[ADD], 0, widget);
1878 
1879   _gtk_container_accessible_add (GTK_WIDGET (container), widget);
1880 }
1881 
1882 /**
1883  * gtk_container_remove:
1884  * @container: a #GtkContainer
1885  * @widget: a current child of @container
1886  *
1887  * Removes @widget from @container. @widget must be inside @container.
1888  * Note that @container will own a reference to @widget, and that this
1889  * may be the last reference held; so removing a widget from its
1890  * container can destroy that widget. If you want to use @widget
1891  * again, you need to add a reference to it before removing it from
1892  * a container, using g_object_ref(). If you don’t want to use @widget
1893  * again it’s usually more efficient to simply destroy it directly
1894  * using gtk_widget_destroy() since this will remove it from the
1895  * container and help break any circular reference count cycles.
1896  **/
1897 void
gtk_container_remove(GtkContainer * container,GtkWidget * widget)1898 gtk_container_remove (GtkContainer *container,
1899                       GtkWidget    *widget)
1900 {
1901   g_return_if_fail (GTK_IS_CONTAINER (container));
1902   g_return_if_fail (GTK_IS_WIDGET (widget));
1903 
1904   g_object_ref (container);
1905   g_object_ref (widget);
1906 
1907   g_signal_emit (container, container_signals[REMOVE], 0, widget);
1908 
1909   _gtk_container_accessible_remove (GTK_WIDGET (container), widget);
1910 
1911   g_object_unref (widget);
1912   g_object_unref (container);
1913 }
1914 
1915 static void
gtk_container_real_set_resize_mode(GtkContainer * container,GtkResizeMode resize_mode)1916 gtk_container_real_set_resize_mode (GtkContainer  *container,
1917                                     GtkResizeMode  resize_mode)
1918 {
1919   GtkWidget *widget = GTK_WIDGET (container);
1920   GtkContainerPrivate *priv = container->priv;
1921 
1922   if (_gtk_widget_is_toplevel (widget) &&
1923       resize_mode == GTK_RESIZE_PARENT)
1924     {
1925       resize_mode = GTK_RESIZE_QUEUE;
1926     }
1927 
1928   if (priv->resize_mode != resize_mode)
1929     {
1930       priv->resize_mode = resize_mode;
1931 
1932       gtk_widget_queue_resize (widget);
1933       g_object_notify_by_pspec (G_OBJECT (container), container_props[PROP_RESIZE_MODE]);
1934     }
1935 }
1936 
1937 /**
1938  * gtk_container_set_resize_mode:
1939  * @container: a #GtkContainer
1940  * @resize_mode: the new resize mode
1941  *
1942  * Sets the resize mode for the container.
1943  *
1944  * The resize mode of a container determines whether a resize request
1945  * will be passed to the container’s parent, queued for later execution
1946  * or executed immediately.
1947  *
1948  * Deprecated: 3.12: Resize modes are deprecated. They aren’t necessary
1949  *     anymore since frame clocks and might introduce obscure bugs if
1950  *     used.
1951  **/
1952 void
gtk_container_set_resize_mode(GtkContainer * container,GtkResizeMode resize_mode)1953 gtk_container_set_resize_mode (GtkContainer  *container,
1954                                GtkResizeMode  resize_mode)
1955 {
1956   GtkContainerPrivate *priv;
1957 
1958   g_return_if_fail (GTK_IS_CONTAINER (container));
1959   g_return_if_fail (resize_mode <= GTK_RESIZE_IMMEDIATE);
1960 
1961   priv = container->priv;
1962   priv->resize_mode_set = TRUE;
1963 
1964   gtk_container_real_set_resize_mode (container, resize_mode);
1965 }
1966 
1967 void
gtk_container_set_default_resize_mode(GtkContainer * container,GtkResizeMode resize_mode)1968 gtk_container_set_default_resize_mode (GtkContainer *container,
1969                                        GtkResizeMode resize_mode)
1970 {
1971   GtkContainerPrivate *priv = container->priv;
1972 
1973   if (priv->resize_mode_set)
1974     return;
1975 
1976   gtk_container_real_set_resize_mode (container, resize_mode);
1977 }
1978 
1979 /**
1980  * gtk_container_get_resize_mode:
1981  * @container: a #GtkContainer
1982  *
1983  * Returns the resize mode for the container. See
1984  * gtk_container_set_resize_mode ().
1985  *
1986  * Returns: the current resize mode
1987  *
1988  * Deprecated: 3.12: Resize modes are deprecated. They aren’t necessary
1989  *     anymore since frame clocks and might introduce obscure bugs if
1990  *     used.
1991  **/
1992 GtkResizeMode
gtk_container_get_resize_mode(GtkContainer * container)1993 gtk_container_get_resize_mode (GtkContainer *container)
1994 {
1995   g_return_val_if_fail (GTK_IS_CONTAINER (container), GTK_RESIZE_PARENT);
1996 
1997   return container->priv->resize_mode;
1998 }
1999 
2000 /**
2001  * gtk_container_set_reallocate_redraws:
2002  * @container: a #GtkContainer
2003  * @needs_redraws: the new value for the container’s @reallocate_redraws flag
2004  *
2005  * Sets the @reallocate_redraws flag of the container to the given value.
2006  *
2007  * Containers requesting reallocation redraws get automatically
2008  * redrawn if any of their children changed allocation.
2009  *
2010  * Deprecated: 3.14: Call gtk_widget_queue_draw() in your size_allocate handler.
2011  **/
2012 void
gtk_container_set_reallocate_redraws(GtkContainer * container,gboolean needs_redraws)2013 gtk_container_set_reallocate_redraws (GtkContainer *container,
2014                                       gboolean      needs_redraws)
2015 {
2016   g_return_if_fail (GTK_IS_CONTAINER (container));
2017 
2018   container->priv->reallocate_redraws = needs_redraws ? TRUE : FALSE;
2019 }
2020 
2021 static gboolean
gtk_container_needs_idle_sizer(GtkContainer * container)2022 gtk_container_needs_idle_sizer (GtkContainer *container)
2023 {
2024   GtkContainerPrivate *priv = container->priv;
2025 
2026   if (priv->resize_mode == GTK_RESIZE_PARENT)
2027     return FALSE;
2028 
2029   if (container->priv->restyle_pending)
2030     return TRUE;
2031 
2032   if (priv->resize_mode == GTK_RESIZE_IMMEDIATE)
2033     return FALSE;
2034 
2035   return gtk_widget_needs_allocate (GTK_WIDGET (container));
2036 }
2037 
2038 static void
gtk_container_idle_sizer(GdkFrameClock * clock,GtkContainer * container)2039 gtk_container_idle_sizer (GdkFrameClock *clock,
2040 			  GtkContainer  *container)
2041 {
2042   /* We validate the style contexts in a single loop before even trying
2043    * to handle resizes instead of doing validations inline.
2044    * This is mostly necessary for compatibility reasons with old code,
2045    * because both style_updated and size_allocate functions often change
2046    * styles and so could cause infinite loops in this function.
2047    *
2048    * It's important to note that even an invalid style context returns
2049    * sane values. So the result of an invalid style context will never be
2050    * a program crash, but only a wrong layout or rendering.
2051    */
2052   if (container->priv->restyle_pending)
2053     {
2054       container->priv->restyle_pending = FALSE;
2055       gtk_css_node_validate (gtk_widget_get_css_node (GTK_WIDGET (container)));
2056     }
2057 
2058   /* we may be invoked with a container_resize_queue of NULL, because
2059    * queue_resize could have been adding an extra idle function while
2060    * the queue still got processed. we better just ignore such case
2061    * than trying to explicitly work around them with some extra flags,
2062    * since it doesn't cause any actual harm.
2063    */
2064   if (gtk_widget_needs_allocate (GTK_WIDGET (container)))
2065     {
2066       gtk_container_check_resize (container);
2067     }
2068 
2069   if (!gtk_container_needs_idle_sizer (container))
2070     {
2071       _gtk_container_stop_idle_sizer (container);
2072     }
2073   else
2074     {
2075       gdk_frame_clock_request_phase (clock,
2076                                      GDK_FRAME_CLOCK_PHASE_LAYOUT);
2077     }
2078 }
2079 
2080 static void
gtk_container_start_idle_sizer(GtkContainer * container)2081 gtk_container_start_idle_sizer (GtkContainer *container)
2082 {
2083   GdkFrameClock *clock;
2084 
2085   if (container->priv->resize_handler != 0)
2086     return;
2087 
2088   clock = gtk_widget_get_frame_clock (GTK_WIDGET (container));
2089   if (clock == NULL)
2090     return;
2091 
2092   if (!GTK_WIDGET (container)->priv->frameclock_connected)
2093     return;
2094 
2095   container->priv->resize_clock = clock;
2096   container->priv->resize_handler = g_signal_connect (clock, "layout",
2097 						      G_CALLBACK (gtk_container_idle_sizer), container);
2098   gdk_frame_clock_request_phase (clock,
2099                                  GDK_FRAME_CLOCK_PHASE_LAYOUT);
2100 }
2101 
2102 void
_gtk_container_stop_idle_sizer(GtkContainer * container)2103 _gtk_container_stop_idle_sizer (GtkContainer *container)
2104 {
2105   if (container->priv->resize_handler == 0)
2106     return;
2107 
2108   g_signal_handler_disconnect (container->priv->resize_clock,
2109                                container->priv->resize_handler);
2110   container->priv->resize_handler = 0;
2111   container->priv->resize_clock = NULL;
2112 }
2113 
2114 void
gtk_container_queue_resize_handler(GtkContainer * container)2115 gtk_container_queue_resize_handler (GtkContainer *container)
2116 {
2117   GtkWidget *widget;
2118 
2119   G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
2120   g_return_if_fail (GTK_IS_RESIZE_CONTAINER (container));
2121   G_GNUC_END_IGNORE_DEPRECATIONS;
2122 
2123   widget = GTK_WIDGET (container);
2124 
2125   if (_gtk_widget_get_visible (widget) &&
2126       (_gtk_widget_is_toplevel (widget) ||
2127        _gtk_widget_get_realized (widget)))
2128     {
2129       switch (container->priv->resize_mode)
2130         {
2131         case GTK_RESIZE_QUEUE:
2132           if (gtk_widget_needs_allocate (widget))
2133             gtk_container_start_idle_sizer (container);
2134           break;
2135 
2136         case GTK_RESIZE_IMMEDIATE:
2137           gtk_container_check_resize (container);
2138           break;
2139 
2140         case GTK_RESIZE_PARENT:
2141         default:
2142           g_assert_not_reached ();
2143           break;
2144         }
2145     }
2146 }
2147 
2148 void
_gtk_container_queue_restyle(GtkContainer * container)2149 _gtk_container_queue_restyle (GtkContainer *container)
2150 {
2151   GtkContainerPrivate *priv;
2152 
2153   g_return_if_fail (GTK_CONTAINER (container));
2154 
2155   priv = container->priv;
2156 
2157   if (priv->restyle_pending)
2158     return;
2159 
2160   gtk_container_start_idle_sizer (container);
2161   priv->restyle_pending = TRUE;
2162 }
2163 
2164 void
_gtk_container_maybe_start_idle_sizer(GtkContainer * container)2165 _gtk_container_maybe_start_idle_sizer (GtkContainer *container)
2166 {
2167   if (gtk_container_needs_idle_sizer (container))
2168     gtk_container_start_idle_sizer (container);
2169 }
2170 
2171 void
gtk_container_check_resize(GtkContainer * container)2172 gtk_container_check_resize (GtkContainer *container)
2173 {
2174   g_return_if_fail (GTK_IS_CONTAINER (container));
2175 
2176   g_signal_emit (container, container_signals[CHECK_RESIZE], 0);
2177 }
2178 
2179 static void
gtk_container_real_check_resize(GtkContainer * container)2180 gtk_container_real_check_resize (GtkContainer *container)
2181 {
2182   GtkWidget *widget = GTK_WIDGET (container);
2183   GtkAllocation allocation;
2184   GtkRequisition requisition;
2185   int baseline;
2186 
2187   if (_gtk_widget_get_alloc_needed (widget))
2188     {
2189       gtk_widget_get_preferred_size (widget, &requisition, NULL);
2190       gtk_widget_get_allocated_size (widget, &allocation, &baseline);
2191 
2192       if (requisition.width > allocation.width ||
2193           requisition.height > allocation.height)
2194         {
2195           G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
2196           if (GTK_IS_RESIZE_CONTAINER (container))
2197             {
2198               gtk_widget_size_allocate (widget, &allocation);
2199             }
2200           else
2201             gtk_widget_queue_resize (widget);
2202           G_GNUC_END_IGNORE_DEPRECATIONS;
2203         }
2204       else
2205         {
2206           gtk_widget_size_allocate_with_baseline (widget, &allocation, baseline);
2207         }
2208     }
2209   else
2210     {
2211       gtk_widget_ensure_allocate (widget);
2212     }
2213 }
2214 
2215 /* The container hasn't changed size but one of its children
2216  *  queued a resize request. Which means that the allocation
2217  *  is not sufficient for the requisition of some child.
2218  *  We’ve already performed a size request at this point,
2219  *  so we simply need to reallocate and let the allocation
2220  *  trickle down via GTK_WIDGET_ALLOC_NEEDED flags.
2221  */
2222 /**
2223  * gtk_container_resize_children:
2224  * @container: a #GtkContainer
2225  *
2226  * Deprecated: 3.10
2227  **/
2228 void
gtk_container_resize_children(GtkContainer * container)2229 gtk_container_resize_children (GtkContainer *container)
2230 {
2231   GtkAllocation allocation;
2232   GtkWidget *widget;
2233   gint baseline;
2234 
2235   /* resizing invariants:
2236    * toplevels have *always* resize_mode != GTK_RESIZE_PARENT set.
2237    * containers that have an idle sizer pending must be flagged with
2238    * RESIZE_PENDING.
2239    */
2240   g_return_if_fail (GTK_IS_CONTAINER (container));
2241 
2242   widget = GTK_WIDGET (container);
2243   gtk_widget_get_allocated_size (widget, &allocation, &baseline);
2244 
2245   gtk_widget_size_allocate_with_baseline (widget, &allocation, baseline);
2246 }
2247 
2248 static void
gtk_container_adjust_size_request(GtkWidget * widget,GtkOrientation orientation,gint * minimum_size,gint * natural_size)2249 gtk_container_adjust_size_request (GtkWidget         *widget,
2250                                    GtkOrientation     orientation,
2251                                    gint              *minimum_size,
2252                                    gint              *natural_size)
2253 {
2254   GtkContainer *container;
2255 
2256   container = GTK_CONTAINER (widget);
2257 
2258   if (GTK_CONTAINER_GET_CLASS (widget)->_handle_border_width)
2259     {
2260       int border_width;
2261 
2262       border_width = container->priv->border_width;
2263 
2264       *minimum_size += border_width * 2;
2265       *natural_size += border_width * 2;
2266     }
2267 
2268   /* chain up last so gtk_widget_set_size_request() values
2269    * will have a chance to overwrite our border width.
2270    */
2271   parent_class->adjust_size_request (widget, orientation,
2272                                      minimum_size, natural_size);
2273 }
2274 
2275 static void
gtk_container_adjust_baseline_request(GtkWidget * widget,gint * minimum_baseline,gint * natural_baseline)2276 gtk_container_adjust_baseline_request (GtkWidget         *widget,
2277 				       gint              *minimum_baseline,
2278 				       gint              *natural_baseline)
2279 {
2280   GtkContainer *container;
2281 
2282   container = GTK_CONTAINER (widget);
2283 
2284   if (GTK_CONTAINER_GET_CLASS (widget)->_handle_border_width)
2285     {
2286       int border_width;
2287 
2288       border_width = container->priv->border_width;
2289 
2290       *minimum_baseline += border_width;
2291       *natural_baseline += border_width;
2292     }
2293 
2294   parent_class->adjust_baseline_request (widget, minimum_baseline, natural_baseline);
2295 }
2296 
2297 static void
gtk_container_adjust_size_allocation(GtkWidget * widget,GtkOrientation orientation,gint * minimum_size,gint * natural_size,gint * allocated_pos,gint * allocated_size)2298 gtk_container_adjust_size_allocation (GtkWidget         *widget,
2299                                       GtkOrientation     orientation,
2300                                       gint              *minimum_size,
2301                                       gint              *natural_size,
2302                                       gint              *allocated_pos,
2303                                       gint              *allocated_size)
2304 {
2305   GtkContainer *container;
2306   int border_width;
2307 
2308   container = GTK_CONTAINER (widget);
2309 
2310   if (GTK_CONTAINER_GET_CLASS (widget)->_handle_border_width)
2311     {
2312       border_width = container->priv->border_width;
2313 
2314       *allocated_size -= border_width * 2;
2315       *allocated_pos += border_width;
2316       *minimum_size -= border_width * 2;
2317       *natural_size -= border_width * 2;
2318     }
2319 
2320   /* Chain up to GtkWidgetClass *after* removing our border width from
2321    * the proposed allocation size. This is because it's possible that the
2322    * widget was allocated more space than it needs in a said orientation,
2323    * if GtkWidgetClass does any alignments and thus limits the size to the
2324    * natural size... then we need that to be done *after* removing any margins
2325    * and padding values.
2326    */
2327   parent_class->adjust_size_allocation (widget, orientation,
2328                                         minimum_size, natural_size, allocated_pos,
2329                                         allocated_size);
2330 }
2331 
2332 static void
gtk_container_adjust_baseline_allocation(GtkWidget * widget,gint * baseline)2333 gtk_container_adjust_baseline_allocation (GtkWidget         *widget,
2334 					  gint              *baseline)
2335 {
2336   GtkContainer *container;
2337   int border_width;
2338 
2339   container = GTK_CONTAINER (widget);
2340 
2341   if (GTK_CONTAINER_GET_CLASS (widget)->_handle_border_width)
2342     {
2343       border_width = container->priv->border_width;
2344 
2345       if (*baseline >= 0)
2346 	*baseline -= border_width;
2347     }
2348 
2349   parent_class->adjust_baseline_allocation (widget, baseline);
2350 }
2351 
2352 
2353 typedef struct {
2354   gint hfw;
2355   gint wfh;
2356 } RequestModeCount;
2357 
2358 static void
count_request_modes(GtkWidget * widget,RequestModeCount * count)2359 count_request_modes (GtkWidget        *widget,
2360 		     RequestModeCount *count)
2361 {
2362   GtkSizeRequestMode mode = gtk_widget_get_request_mode (widget);
2363 
2364   switch (mode)
2365     {
2366     case GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH:
2367       count->hfw++;
2368       break;
2369     case GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT:
2370       count->wfh++;
2371       break;
2372     case GTK_SIZE_REQUEST_CONSTANT_SIZE:
2373     default:
2374       break;
2375     }
2376 }
2377 
2378 static GtkSizeRequestMode
gtk_container_get_request_mode(GtkWidget * widget)2379 gtk_container_get_request_mode (GtkWidget *widget)
2380 {
2381   GtkContainer *container = GTK_CONTAINER (widget);
2382   RequestModeCount count = { 0, 0 };
2383 
2384   gtk_container_forall (container, (GtkCallback)count_request_modes, &count);
2385 
2386   if (!count.hfw && !count.wfh)
2387     return GTK_SIZE_REQUEST_CONSTANT_SIZE;
2388   else
2389     return count.wfh > count.hfw ?
2390         GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT :
2391 	GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH;
2392 }
2393 
2394 /**
2395  * gtk_container_class_handle_border_width:
2396  * @klass: the class struct of a #GtkContainer subclass
2397  *
2398  * Modifies a subclass of #GtkContainerClass to automatically add and
2399  * remove the border-width setting on GtkContainer.  This allows the
2400  * subclass to ignore the border width in its size request and
2401  * allocate methods. The intent is for a subclass to invoke this
2402  * in its class_init function.
2403  *
2404  * gtk_container_class_handle_border_width() is necessary because it
2405  * would break API too badly to make this behavior the default. So
2406  * subclasses must “opt in” to the parent class handling border_width
2407  * for them.
2408  */
2409 void
gtk_container_class_handle_border_width(GtkContainerClass * klass)2410 gtk_container_class_handle_border_width (GtkContainerClass *klass)
2411 {
2412   g_return_if_fail (GTK_IS_CONTAINER_CLASS (klass));
2413 
2414   klass->_handle_border_width = TRUE;
2415 }
2416 
2417 /**
2418  * gtk_container_forall: (virtual forall)
2419  * @container: a #GtkContainer
2420  * @callback: (scope call) (closure callback_data): a callback
2421  * @callback_data: callback user data
2422  *
2423  * Invokes @callback on each direct child of @container, including
2424  * children that are considered “internal” (implementation details
2425  * of the container). “Internal” children generally weren’t added
2426  * by the user of the container, but were added by the container
2427  * implementation itself.
2428  *
2429  * Most applications should use gtk_container_foreach(), rather
2430  * than gtk_container_forall().
2431  **/
2432 void
gtk_container_forall(GtkContainer * container,GtkCallback callback,gpointer callback_data)2433 gtk_container_forall (GtkContainer *container,
2434                       GtkCallback   callback,
2435                       gpointer      callback_data)
2436 {
2437   GtkContainerClass *class;
2438 
2439   g_return_if_fail (GTK_IS_CONTAINER (container));
2440   g_return_if_fail (callback != NULL);
2441 
2442   class = GTK_CONTAINER_GET_CLASS (container);
2443 
2444   if (class->forall)
2445     class->forall (container, TRUE, callback, callback_data);
2446 }
2447 
2448 /**
2449  * gtk_container_foreach:
2450  * @container: a #GtkContainer
2451  * @callback: (scope call):  a callback
2452  * @callback_data: callback user data
2453  *
2454  * Invokes @callback on each non-internal child of @container.
2455  * See gtk_container_forall() for details on what constitutes
2456  * an “internal” child. For all practical purposes, this function
2457  * should iterate over precisely those child widgets that were
2458  * added to the container by the application with explicit add()
2459  * calls.
2460  *
2461  * It is permissible to remove the child from the @callback handler.
2462  *
2463  * Most applications should use gtk_container_foreach(),
2464  * rather than gtk_container_forall().
2465  **/
2466 void
gtk_container_foreach(GtkContainer * container,GtkCallback callback,gpointer callback_data)2467 gtk_container_foreach (GtkContainer *container,
2468                        GtkCallback   callback,
2469                        gpointer      callback_data)
2470 {
2471   GtkContainerClass *class;
2472 
2473   g_return_if_fail (GTK_IS_CONTAINER (container));
2474   g_return_if_fail (callback != NULL);
2475 
2476   class = GTK_CONTAINER_GET_CLASS (container);
2477 
2478   if (class->forall)
2479     class->forall (container, FALSE, callback, callback_data);
2480 }
2481 
2482 /**
2483  * gtk_container_set_focus_child:
2484  * @container: a #GtkContainer
2485  * @child: (allow-none): a #GtkWidget, or %NULL
2486  *
2487  * Sets, or unsets if @child is %NULL, the focused child of @container.
2488  *
2489  * This function emits the GtkContainer::set_focus_child signal of
2490  * @container. Implementations of #GtkContainer can override the
2491  * default behaviour by overriding the class closure of this signal.
2492  *
2493  * This is function is mostly meant to be used by widgets. Applications can use
2494  * gtk_widget_grab_focus() to manually set the focus to a specific widget.
2495  */
2496 void
gtk_container_set_focus_child(GtkContainer * container,GtkWidget * child)2497 gtk_container_set_focus_child (GtkContainer *container,
2498                                GtkWidget    *child)
2499 {
2500   g_return_if_fail (GTK_IS_CONTAINER (container));
2501   if (child)
2502     g_return_if_fail (GTK_IS_WIDGET (child));
2503 
2504   g_signal_emit (container, container_signals[SET_FOCUS_CHILD], 0, child);
2505 }
2506 
2507 /**
2508  * gtk_container_get_focus_child:
2509  * @container: a #GtkContainer
2510  *
2511  * Returns the current focus child widget inside @container. This is not the
2512  * currently focused widget. That can be obtained by calling
2513  * gtk_window_get_focus().
2514  *
2515  * Returns: (nullable) (transfer none): The child widget which will receive the
2516  *          focus inside @container when the @container is focused,
2517  *          or %NULL if none is set.
2518  *
2519  * Since: 2.14
2520  **/
2521 GtkWidget *
gtk_container_get_focus_child(GtkContainer * container)2522 gtk_container_get_focus_child (GtkContainer *container)
2523 {
2524   g_return_val_if_fail (GTK_IS_CONTAINER (container), NULL);
2525 
2526   return container->priv->focus_child;
2527 }
2528 
2529 /**
2530  * gtk_container_get_children:
2531  * @container: a #GtkContainer
2532  *
2533  * Returns the container’s non-internal children. See
2534  * gtk_container_forall() for details on what constitutes an "internal" child.
2535  *
2536  * Returns: (element-type GtkWidget) (transfer container): a newly-allocated list of the container’s non-internal children.
2537  **/
2538 GList*
gtk_container_get_children(GtkContainer * container)2539 gtk_container_get_children (GtkContainer *container)
2540 {
2541   GList *children = NULL;
2542 
2543   gtk_container_foreach (container,
2544                          gtk_container_children_callback,
2545                          &children);
2546 
2547   return g_list_reverse (children);
2548 }
2549 
2550 static void
gtk_container_child_position_callback(GtkWidget * widget,gpointer client_data)2551 gtk_container_child_position_callback (GtkWidget *widget,
2552                                        gpointer   client_data)
2553 {
2554   struct {
2555     GtkWidget *child;
2556     guint i;
2557     guint index;
2558   } *data = client_data;
2559 
2560   data->i++;
2561   if (data->child == widget)
2562     data->index = data->i;
2563 }
2564 
2565 static gchar*
gtk_container_child_default_composite_name(GtkContainer * container,GtkWidget * child)2566 gtk_container_child_default_composite_name (GtkContainer *container,
2567                                             GtkWidget    *child)
2568 {
2569   struct {
2570     GtkWidget *child;
2571     guint i;
2572     guint index;
2573   } data;
2574   gchar *name;
2575 
2576   /* fallback implementation */
2577   data.child = child;
2578   data.i = 0;
2579   data.index = 0;
2580   gtk_container_forall (container,
2581                         gtk_container_child_position_callback,
2582                         &data);
2583 
2584   name = g_strdup_printf ("%s-%u",
2585                           g_type_name (G_TYPE_FROM_INSTANCE (child)),
2586                           data.index);
2587 
2588   return name;
2589 }
2590 
2591 gchar*
_gtk_container_child_composite_name(GtkContainer * container,GtkWidget * child)2592 _gtk_container_child_composite_name (GtkContainer *container,
2593                                     GtkWidget    *child)
2594 {
2595   gboolean composite_child;
2596 
2597   g_return_val_if_fail (GTK_IS_CONTAINER (container), NULL);
2598   g_return_val_if_fail (GTK_IS_WIDGET (child), NULL);
2599   g_return_val_if_fail (_gtk_widget_get_parent (child) == GTK_WIDGET (container), NULL);
2600 
2601   g_object_get (child, "composite-child", &composite_child, NULL);
2602   if (composite_child)
2603     {
2604       static GQuark quark_composite_name = 0;
2605       gchar *name;
2606 
2607       if (!quark_composite_name)
2608         quark_composite_name = g_quark_from_static_string ("gtk-composite-name");
2609 
2610       name = g_object_get_qdata (G_OBJECT (child), quark_composite_name);
2611       if (!name)
2612         {
2613           GtkContainerClass *class;
2614 
2615           class = GTK_CONTAINER_GET_CLASS (container);
2616           if (class->composite_name)
2617             name = class->composite_name (container, child);
2618         }
2619       else
2620         name = g_strdup (name);
2621 
2622       return name;
2623     }
2624 
2625   return NULL;
2626 }
2627 
2628 typedef struct {
2629   gboolean hexpand;
2630   gboolean vexpand;
2631 } ComputeExpandData;
2632 
2633 static void
gtk_container_compute_expand_callback(GtkWidget * widget,gpointer client_data)2634 gtk_container_compute_expand_callback (GtkWidget *widget,
2635                                        gpointer   client_data)
2636 {
2637   ComputeExpandData *data = client_data;
2638 
2639   /* note that we don't get_expand on the child if we already know we
2640    * have to expand, so we only recurse into children until we find
2641    * one that expands and then we basically don't do any more
2642    * work. This means that we can leave some children in a
2643    * need_compute_expand state, which is fine, as long as GtkWidget
2644    * doesn't rely on an invariant that "if a child has
2645    * need_compute_expand, its parents also do"
2646    *
2647    * gtk_widget_compute_expand() always returns FALSE if the
2648    * child is !visible so that's taken care of.
2649    */
2650   data->hexpand = data->hexpand ||
2651     gtk_widget_compute_expand (widget, GTK_ORIENTATION_HORIZONTAL);
2652 
2653   data->vexpand = data->vexpand ||
2654     gtk_widget_compute_expand (widget, GTK_ORIENTATION_VERTICAL);
2655 }
2656 
2657 static void
gtk_container_compute_expand(GtkWidget * widget,gboolean * hexpand_p,gboolean * vexpand_p)2658 gtk_container_compute_expand (GtkWidget         *widget,
2659                               gboolean          *hexpand_p,
2660                               gboolean          *vexpand_p)
2661 {
2662   ComputeExpandData data;
2663 
2664   data.hexpand = FALSE;
2665   data.vexpand = FALSE;
2666 
2667   gtk_container_forall (GTK_CONTAINER (widget),
2668                         gtk_container_compute_expand_callback,
2669                         &data);
2670 
2671   *hexpand_p = data.hexpand;
2672   *vexpand_p = data.vexpand;
2673 }
2674 
2675 static void
gtk_container_real_set_focus_child(GtkContainer * container,GtkWidget * child)2676 gtk_container_real_set_focus_child (GtkContainer     *container,
2677                                     GtkWidget        *child)
2678 {
2679   GtkContainerPrivate *priv;
2680 
2681   g_return_if_fail (GTK_IS_CONTAINER (container));
2682   g_return_if_fail (child == NULL || GTK_IS_WIDGET (child));
2683 
2684   priv = container->priv;
2685 
2686   if (child != priv->focus_child)
2687     {
2688       if (priv->focus_child)
2689         g_object_unref (priv->focus_child);
2690 
2691       priv->focus_child = child;
2692 
2693       if (priv->focus_child)
2694         g_object_ref (priv->focus_child);
2695     }
2696 
2697   /* Check for h/v adjustments and scroll to show the focus child if possible */
2698   if (priv->focus_child)
2699     {
2700       GtkAdjustment *hadj;
2701       GtkAdjustment *vadj;
2702       GtkAllocation allocation;
2703       GtkWidget *focus_child;
2704       gint x, y;
2705 
2706       hadj = g_object_get_qdata (G_OBJECT (container), hadjustment_key_id);
2707       vadj = g_object_get_qdata (G_OBJECT (container), vadjustment_key_id);
2708       if (hadj || vadj)
2709         {
2710           focus_child = priv->focus_child;
2711           while (GTK_IS_CONTAINER (focus_child) && gtk_container_get_focus_child (GTK_CONTAINER (focus_child)))
2712             {
2713               focus_child = gtk_container_get_focus_child (GTK_CONTAINER (focus_child));
2714             }
2715 
2716           if (!gtk_widget_translate_coordinates (focus_child, priv->focus_child,
2717                                                  0, 0, &x, &y))
2718             return;
2719 
2720           _gtk_widget_get_allocation (priv->focus_child, &allocation);
2721           x += allocation.x;
2722           y += allocation.y;
2723 
2724           _gtk_widget_get_allocation (focus_child, &allocation);
2725 
2726           if (vadj)
2727             gtk_adjustment_clamp_page (vadj, y, y + allocation.height);
2728 
2729           if (hadj)
2730             gtk_adjustment_clamp_page (hadj, x, x + allocation.width);
2731         }
2732     }
2733 }
2734 
2735 static GList*
get_focus_chain(GtkContainer * container)2736 get_focus_chain (GtkContainer *container)
2737 {
2738   return g_object_get_qdata (G_OBJECT (container), quark_focus_chain);
2739 }
2740 
2741 /* same as gtk_container_get_children, except it includes internals
2742  */
2743 GList *
gtk_container_get_all_children(GtkContainer * container)2744 gtk_container_get_all_children (GtkContainer *container)
2745 {
2746   GList *children = NULL;
2747 
2748   gtk_container_forall (container,
2749                          gtk_container_children_callback,
2750                          &children);
2751 
2752   return children;
2753 }
2754 
2755 static GtkWidgetPath *
gtk_container_real_get_path_for_child(GtkContainer * container,GtkWidget * child)2756 gtk_container_real_get_path_for_child (GtkContainer *container,
2757                                        GtkWidget    *child)
2758 {
2759   GtkWidgetPath *path;
2760   GtkWidget *widget = GTK_WIDGET (container);
2761 
2762   path = _gtk_widget_create_path (widget);
2763 
2764   gtk_widget_path_append_for_widget (path, child);
2765 
2766   return path;
2767 }
2768 
2769 static gboolean
gtk_container_focus(GtkWidget * widget,GtkDirectionType direction)2770 gtk_container_focus (GtkWidget        *widget,
2771                      GtkDirectionType  direction)
2772 {
2773   GList *children;
2774   GList *sorted_children;
2775   gint return_val;
2776   GtkContainer *container;
2777   GtkContainerPrivate *priv;
2778 
2779   g_return_val_if_fail (GTK_IS_CONTAINER (widget), FALSE);
2780 
2781   container = GTK_CONTAINER (widget);
2782   priv = container->priv;
2783 
2784   return_val = FALSE;
2785 
2786   if (gtk_widget_get_can_focus (widget))
2787     {
2788       if (!gtk_widget_has_focus (widget))
2789         {
2790           gtk_widget_grab_focus (widget);
2791           return_val = TRUE;
2792         }
2793     }
2794   else
2795     {
2796       /* Get a list of the containers children, allowing focus
2797        * chain to override.
2798        */
2799       if (priv->has_focus_chain)
2800         children = g_list_copy (get_focus_chain (container));
2801       else
2802         children = gtk_container_get_all_children (container);
2803 
2804       if (priv->has_focus_chain &&
2805           (direction == GTK_DIR_TAB_FORWARD ||
2806            direction == GTK_DIR_TAB_BACKWARD))
2807         {
2808           sorted_children = g_list_copy (children);
2809 
2810           if (direction == GTK_DIR_TAB_BACKWARD)
2811             sorted_children = g_list_reverse (sorted_children);
2812         }
2813       else
2814         sorted_children = _gtk_container_focus_sort (container, children, direction, NULL);
2815 
2816       return_val = gtk_container_focus_move (container, sorted_children, direction);
2817 
2818       g_list_free (sorted_children);
2819       g_list_free (children);
2820     }
2821 
2822   return return_val;
2823 }
2824 
2825 static gint
tab_compare(gconstpointer a,gconstpointer b,gpointer data)2826 tab_compare (gconstpointer a,
2827              gconstpointer b,
2828              gpointer      data)
2829 {
2830   GtkAllocation child1_allocation, child2_allocation;
2831   const GtkWidget *child1 = a;
2832   const GtkWidget *child2 = b;
2833   GtkTextDirection text_direction = GPOINTER_TO_INT (data);
2834   gint y1, y2;
2835 
2836   _gtk_widget_get_allocation ((GtkWidget *) child1, &child1_allocation);
2837   _gtk_widget_get_allocation ((GtkWidget *) child2, &child2_allocation);
2838 
2839   y1 = child1_allocation.y + child1_allocation.height / 2;
2840   y2 = child2_allocation.y + child2_allocation.height / 2;
2841 
2842   if (y1 == y2)
2843     {
2844       gint x1 = child1_allocation.x + child1_allocation.width / 2;
2845       gint x2 = child2_allocation.x + child2_allocation.width / 2;
2846 
2847       if (text_direction == GTK_TEXT_DIR_RTL)
2848         return (x1 < x2) ? 1 : ((x1 == x2) ? 0 : -1);
2849       else
2850         return (x1 < x2) ? -1 : ((x1 == x2) ? 0 : 1);
2851     }
2852   else
2853     return (y1 < y2) ? -1 : 1;
2854 }
2855 
2856 static GList *
gtk_container_focus_sort_tab(GtkContainer * container,GList * children,GtkDirectionType direction,GtkWidget * old_focus)2857 gtk_container_focus_sort_tab (GtkContainer     *container,
2858                               GList            *children,
2859                               GtkDirectionType  direction,
2860                               GtkWidget        *old_focus)
2861 {
2862   GtkTextDirection text_direction = _gtk_widget_get_direction (GTK_WIDGET (container));
2863   children = g_list_sort_with_data (children, tab_compare, GINT_TO_POINTER (text_direction));
2864 
2865   /* if we are going backwards then reverse the order
2866    *  of the children.
2867    */
2868   if (direction == GTK_DIR_TAB_BACKWARD)
2869     children = g_list_reverse (children);
2870 
2871   return children;
2872 }
2873 
2874 /* Get coordinates of @widget's allocation with respect to
2875  * allocation of @container.
2876  */
2877 static gboolean
get_allocation_coords(GtkContainer * container,GtkWidget * widget,GdkRectangle * allocation)2878 get_allocation_coords (GtkContainer  *container,
2879                        GtkWidget     *widget,
2880                        GdkRectangle  *allocation)
2881 {
2882   gtk_widget_get_allocation (widget, allocation);
2883 
2884   return gtk_widget_translate_coordinates (widget, GTK_WIDGET (container),
2885                                            0, 0, &allocation->x, &allocation->y);
2886 }
2887 
2888 /* Look for a child in @children that is intermediate between
2889  * the focus widget and container. This widget, if it exists,
2890  * acts as the starting widget for focus navigation.
2891  */
2892 static GtkWidget *
find_old_focus(GtkContainer * container,GList * children)2893 find_old_focus (GtkContainer *container,
2894                 GList        *children)
2895 {
2896   GList *tmp_list = children;
2897   while (tmp_list)
2898     {
2899       GtkWidget *child = tmp_list->data;
2900       GtkWidget *widget = child;
2901 
2902       while (widget && widget != (GtkWidget *)container)
2903         {
2904           GtkWidget *parent;
2905 
2906           parent = _gtk_widget_get_parent (widget);
2907 
2908           if (parent && (gtk_container_get_focus_child (GTK_CONTAINER (parent)) != widget))
2909             goto next;
2910 
2911           widget = parent;
2912         }
2913 
2914       return child;
2915 
2916     next:
2917       tmp_list = tmp_list->next;
2918     }
2919 
2920   return NULL;
2921 }
2922 
2923 static gboolean
old_focus_coords(GtkContainer * container,GdkRectangle * old_focus_rect)2924 old_focus_coords (GtkContainer *container,
2925                   GdkRectangle *old_focus_rect)
2926 {
2927   GtkWidget *widget = GTK_WIDGET (container);
2928   GtkWidget *toplevel = _gtk_widget_get_toplevel (widget);
2929   GtkWidget *old_focus;
2930 
2931   if (GTK_IS_WINDOW (toplevel))
2932     {
2933       old_focus = gtk_window_get_focus (GTK_WINDOW (toplevel));
2934       if (old_focus)
2935         return get_allocation_coords (container, old_focus, old_focus_rect);
2936     }
2937 
2938   return FALSE;
2939 }
2940 
2941 typedef struct _CompareInfo CompareInfo;
2942 
2943 struct _CompareInfo
2944 {
2945   GtkContainer *container;
2946   gint x;
2947   gint y;
2948   gboolean reverse;
2949 };
2950 
2951 static gint
up_down_compare(gconstpointer a,gconstpointer b,gpointer data)2952 up_down_compare (gconstpointer a,
2953                  gconstpointer b,
2954                  gpointer      data)
2955 {
2956   GdkRectangle allocation1;
2957   GdkRectangle allocation2;
2958   CompareInfo *compare = data;
2959   gint y1, y2;
2960 
2961   get_allocation_coords (compare->container, (GtkWidget *)a, &allocation1);
2962   get_allocation_coords (compare->container, (GtkWidget *)b, &allocation2);
2963 
2964   y1 = allocation1.y + allocation1.height / 2;
2965   y2 = allocation2.y + allocation2.height / 2;
2966 
2967   if (y1 == y2)
2968     {
2969       gint x1 = abs (allocation1.x + allocation1.width / 2 - compare->x);
2970       gint x2 = abs (allocation2.x + allocation2.width / 2 - compare->x);
2971 
2972       if (compare->reverse)
2973         return (x1 < x2) ? 1 : ((x1 == x2) ? 0 : -1);
2974       else
2975         return (x1 < x2) ? -1 : ((x1 == x2) ? 0 : 1);
2976     }
2977   else
2978     return (y1 < y2) ? -1 : 1;
2979 }
2980 
2981 static GList *
gtk_container_focus_sort_up_down(GtkContainer * container,GList * children,GtkDirectionType direction,GtkWidget * old_focus)2982 gtk_container_focus_sort_up_down (GtkContainer     *container,
2983                                   GList            *children,
2984                                   GtkDirectionType  direction,
2985                                   GtkWidget        *old_focus)
2986 {
2987   CompareInfo compare;
2988   GList *tmp_list;
2989   GdkRectangle old_allocation;
2990 
2991   compare.container = container;
2992   compare.reverse = (direction == GTK_DIR_UP);
2993 
2994   if (!old_focus)
2995       old_focus = find_old_focus (container, children);
2996 
2997   if (old_focus && get_allocation_coords (container, old_focus, &old_allocation))
2998     {
2999       gint compare_x1;
3000       gint compare_x2;
3001       gint compare_y;
3002 
3003       /* Delete widgets from list that don't match minimum criteria */
3004 
3005       compare_x1 = old_allocation.x;
3006       compare_x2 = old_allocation.x + old_allocation.width;
3007 
3008       if (direction == GTK_DIR_UP)
3009         compare_y = old_allocation.y;
3010       else
3011         compare_y = old_allocation.y + old_allocation.height;
3012 
3013       tmp_list = children;
3014       while (tmp_list)
3015         {
3016           GtkWidget *child = tmp_list->data;
3017           GList *next = tmp_list->next;
3018           gint child_x1, child_x2;
3019           GdkRectangle child_allocation;
3020 
3021           if (child != old_focus)
3022             {
3023               if (get_allocation_coords (container, child, &child_allocation))
3024                 {
3025                   child_x1 = child_allocation.x;
3026                   child_x2 = child_allocation.x + child_allocation.width;
3027 
3028                   if ((child_x2 <= compare_x1 || child_x1 >= compare_x2) /* No horizontal overlap */ ||
3029                       (direction == GTK_DIR_DOWN && child_allocation.y + child_allocation.height < compare_y) || /* Not below */
3030                       (direction == GTK_DIR_UP && child_allocation.y > compare_y)) /* Not above */
3031                     {
3032                       children = g_list_delete_link (children, tmp_list);
3033                     }
3034                 }
3035               else
3036                 children = g_list_delete_link (children, tmp_list);
3037             }
3038 
3039           tmp_list = next;
3040         }
3041 
3042       compare.x = (compare_x1 + compare_x2) / 2;
3043       compare.y = old_allocation.y + old_allocation.height / 2;
3044     }
3045   else
3046     {
3047       /* No old focus widget, need to figure out starting x,y some other way
3048        */
3049       GtkAllocation allocation;
3050       GtkWidget *widget = GTK_WIDGET (container);
3051       GdkRectangle old_focus_rect;
3052 
3053       _gtk_widget_get_allocation (widget, &allocation);
3054 
3055       if (old_focus_coords (container, &old_focus_rect))
3056         {
3057           compare.x = old_focus_rect.x + old_focus_rect.width / 2;
3058         }
3059       else
3060         {
3061           if (!_gtk_widget_get_has_window (widget))
3062             compare.x = allocation.x + allocation.width / 2;
3063           else
3064             compare.x = allocation.width / 2;
3065         }
3066 
3067       if (!_gtk_widget_get_has_window (widget))
3068         compare.y = (direction == GTK_DIR_DOWN) ? allocation.y : allocation.y + allocation.height;
3069       else
3070         compare.y = (direction == GTK_DIR_DOWN) ? 0 : + allocation.height;
3071     }
3072 
3073   children = g_list_sort_with_data (children, up_down_compare, &compare);
3074 
3075   if (compare.reverse)
3076     children = g_list_reverse (children);
3077 
3078   return children;
3079 }
3080 
3081 static gint
left_right_compare(gconstpointer a,gconstpointer b,gpointer data)3082 left_right_compare (gconstpointer a,
3083                     gconstpointer b,
3084                     gpointer      data)
3085 {
3086   GdkRectangle allocation1;
3087   GdkRectangle allocation2;
3088   CompareInfo *compare = data;
3089   gint x1, x2;
3090 
3091   get_allocation_coords (compare->container, (GtkWidget *)a, &allocation1);
3092   get_allocation_coords (compare->container, (GtkWidget *)b, &allocation2);
3093 
3094   x1 = allocation1.x + allocation1.width / 2;
3095   x2 = allocation2.x + allocation2.width / 2;
3096 
3097   if (x1 == x2)
3098     {
3099       gint y1 = abs (allocation1.y + allocation1.height / 2 - compare->y);
3100       gint y2 = abs (allocation2.y + allocation2.height / 2 - compare->y);
3101 
3102       if (compare->reverse)
3103         return (y1 < y2) ? 1 : ((y1 == y2) ? 0 : -1);
3104       else
3105         return (y1 < y2) ? -1 : ((y1 == y2) ? 0 : 1);
3106     }
3107   else
3108     return (x1 < x2) ? -1 : 1;
3109 }
3110 
3111 static GList *
gtk_container_focus_sort_left_right(GtkContainer * container,GList * children,GtkDirectionType direction,GtkWidget * old_focus)3112 gtk_container_focus_sort_left_right (GtkContainer     *container,
3113                                      GList            *children,
3114                                      GtkDirectionType  direction,
3115                                      GtkWidget        *old_focus)
3116 {
3117   CompareInfo compare;
3118   GList *tmp_list;
3119   GdkRectangle old_allocation;
3120 
3121   compare.container = container;
3122   compare.reverse = (direction == GTK_DIR_LEFT);
3123 
3124   if (!old_focus)
3125     old_focus = find_old_focus (container, children);
3126 
3127   if (old_focus && get_allocation_coords (container, old_focus, &old_allocation))
3128     {
3129       gint compare_y1;
3130       gint compare_y2;
3131       gint compare_x;
3132 
3133       /* Delete widgets from list that don't match minimum criteria */
3134 
3135       compare_y1 = old_allocation.y;
3136       compare_y2 = old_allocation.y + old_allocation.height;
3137 
3138       if (direction == GTK_DIR_LEFT)
3139         compare_x = old_allocation.x;
3140       else
3141         compare_x = old_allocation.x + old_allocation.width;
3142 
3143       tmp_list = children;
3144       while (tmp_list)
3145         {
3146           GtkWidget *child = tmp_list->data;
3147           GList *next = tmp_list->next;
3148           gint child_y1, child_y2;
3149           GdkRectangle child_allocation;
3150 
3151           if (child != old_focus)
3152             {
3153               if (get_allocation_coords (container, child, &child_allocation))
3154                 {
3155                   child_y1 = child_allocation.y;
3156                   child_y2 = child_allocation.y + child_allocation.height;
3157 
3158                   if ((child_y2 <= compare_y1 || child_y1 >= compare_y2) /* No vertical overlap */ ||
3159                       (direction == GTK_DIR_RIGHT && child_allocation.x + child_allocation.width < compare_x) || /* Not to left */
3160                       (direction == GTK_DIR_LEFT && child_allocation.x > compare_x)) /* Not to right */
3161                     {
3162                       children = g_list_delete_link (children, tmp_list);
3163                     }
3164                 }
3165               else
3166                 children = g_list_delete_link (children, tmp_list);
3167             }
3168 
3169           tmp_list = next;
3170         }
3171 
3172       compare.y = (compare_y1 + compare_y2) / 2;
3173       compare.x = old_allocation.x + old_allocation.width / 2;
3174     }
3175   else
3176     {
3177       /* No old focus widget, need to figure out starting x,y some other way
3178        */
3179       GtkAllocation allocation;
3180       GtkWidget *widget = GTK_WIDGET (container);
3181       GdkRectangle old_focus_rect;
3182 
3183       _gtk_widget_get_allocation (widget, &allocation);
3184 
3185       if (old_focus_coords (container, &old_focus_rect))
3186         {
3187           compare.y = old_focus_rect.y + old_focus_rect.height / 2;
3188         }
3189       else
3190         {
3191           if (!_gtk_widget_get_has_window (widget))
3192             compare.y = allocation.y + allocation.height / 2;
3193           else
3194             compare.y = allocation.height / 2;
3195         }
3196 
3197       if (!_gtk_widget_get_has_window (widget))
3198         compare.x = (direction == GTK_DIR_RIGHT) ? allocation.x : allocation.x + allocation.width;
3199       else
3200         compare.x = (direction == GTK_DIR_RIGHT) ? 0 : allocation.width;
3201     }
3202 
3203   children = g_list_sort_with_data (children, left_right_compare, &compare);
3204 
3205   if (compare.reverse)
3206     children = g_list_reverse (children);
3207 
3208   return children;
3209 }
3210 
3211 /**
3212  * gtk_container_focus_sort:
3213  * @container: a #GtkContainer
3214  * @children:  a list of descendents of @container (they don't
3215  *             have to be direct children)
3216  * @direction: focus direction
3217  * @old_focus: (allow-none): widget to use for the starting position, or %NULL
3218  *             to determine this automatically.
3219  *             (Note, this argument isn’t used for GTK_DIR_TAB_*,
3220  *              which is the only @direction we use currently,
3221  *              so perhaps this argument should be removed)
3222  *
3223  * Sorts @children in the correct order for focusing with
3224  * direction type @direction.
3225  *
3226  * Returns: a copy of @children, sorted in correct focusing order,
3227  *   with children that aren’t suitable for focusing in this direction
3228  *   removed.
3229  **/
3230 GList *
_gtk_container_focus_sort(GtkContainer * container,GList * children,GtkDirectionType direction,GtkWidget * old_focus)3231 _gtk_container_focus_sort (GtkContainer     *container,
3232                            GList            *children,
3233                            GtkDirectionType  direction,
3234                            GtkWidget        *old_focus)
3235 {
3236   GList *visible_children = NULL;
3237 
3238   while (children)
3239     {
3240       if (_gtk_widget_get_realized (children->data))
3241         visible_children = g_list_prepend (visible_children, children->data);
3242       children = children->next;
3243     }
3244 
3245   switch (direction)
3246     {
3247     case GTK_DIR_TAB_FORWARD:
3248     case GTK_DIR_TAB_BACKWARD:
3249       return gtk_container_focus_sort_tab (container, visible_children, direction, old_focus);
3250     case GTK_DIR_UP:
3251     case GTK_DIR_DOWN:
3252       return gtk_container_focus_sort_up_down (container, visible_children, direction, old_focus);
3253     case GTK_DIR_LEFT:
3254     case GTK_DIR_RIGHT:
3255       return gtk_container_focus_sort_left_right (container, visible_children, direction, old_focus);
3256     }
3257 
3258   g_assert_not_reached ();
3259 
3260   return NULL;
3261 }
3262 
3263 static gboolean
gtk_container_focus_move(GtkContainer * container,GList * children,GtkDirectionType direction)3264 gtk_container_focus_move (GtkContainer     *container,
3265                           GList            *children,
3266                           GtkDirectionType  direction)
3267 {
3268   GtkContainerPrivate *priv = container->priv;
3269   GtkWidget *focus_child;
3270   GtkWidget *child;
3271 
3272   focus_child = priv->focus_child;
3273 
3274   while (children)
3275     {
3276       child = children->data;
3277       children = children->next;
3278 
3279       if (!child)
3280         continue;
3281 
3282       if (focus_child)
3283         {
3284           if (focus_child == child)
3285             {
3286               focus_child = NULL;
3287 
3288                 if (gtk_widget_child_focus (child, direction))
3289                   return TRUE;
3290             }
3291         }
3292       else if (_gtk_widget_is_drawable (child) &&
3293                gtk_widget_is_ancestor (child, GTK_WIDGET (container)))
3294         {
3295           if (gtk_widget_child_focus (child, direction))
3296             return TRUE;
3297         }
3298     }
3299 
3300   return FALSE;
3301 }
3302 
3303 
3304 static void
gtk_container_children_callback(GtkWidget * widget,gpointer client_data)3305 gtk_container_children_callback (GtkWidget *widget,
3306                                  gpointer   client_data)
3307 {
3308   GList **children;
3309 
3310   children = (GList**) client_data;
3311   *children = g_list_prepend (*children, widget);
3312 }
3313 
3314 static void
chain_widget_destroyed(GtkWidget * widget,gpointer user_data)3315 chain_widget_destroyed (GtkWidget *widget,
3316                         gpointer   user_data)
3317 {
3318   GtkContainer *container;
3319   GList *chain;
3320 
3321   container = GTK_CONTAINER (user_data);
3322 
3323   chain = g_object_get_qdata (G_OBJECT (container), quark_focus_chain);
3324 
3325   chain = g_list_remove (chain, widget);
3326 
3327   g_signal_handlers_disconnect_by_func (widget,
3328                                         chain_widget_destroyed,
3329                                         user_data);
3330 
3331   g_object_set_qdata (G_OBJECT (container), quark_focus_chain, chain);
3332 }
3333 
3334 /**
3335  * gtk_container_set_focus_chain:
3336  * @container: a #GtkContainer
3337  * @focusable_widgets: (transfer none) (element-type GtkWidget):
3338  *     the new focus chain
3339  *
3340  * Sets a focus chain, overriding the one computed automatically by GTK+.
3341  *
3342  * In principle each widget in the chain should be a descendant of the
3343  * container, but this is not enforced by this method, since it’s allowed
3344  * to set the focus chain before you pack the widgets, or have a widget
3345  * in the chain that isn’t always packed. The necessary checks are done
3346  * when the focus chain is actually traversed.
3347  *
3348  * Deprecated: 3.24: For overriding focus behavior, use the
3349  *     GtkWidgetClass::focus signal.
3350  **/
3351 void
gtk_container_set_focus_chain(GtkContainer * container,GList * focusable_widgets)3352 gtk_container_set_focus_chain (GtkContainer *container,
3353                                GList        *focusable_widgets)
3354 {
3355   GList *chain;
3356   GList *tmp_list;
3357   GtkContainerPrivate *priv;
3358 
3359   g_return_if_fail (GTK_IS_CONTAINER (container));
3360 
3361   priv = container->priv;
3362 
3363   if (priv->has_focus_chain)
3364     gtk_container_unset_focus_chain (container);
3365 
3366   priv->has_focus_chain = TRUE;
3367 
3368   chain = NULL;
3369   tmp_list = focusable_widgets;
3370   while (tmp_list != NULL)
3371     {
3372       g_return_if_fail (GTK_IS_WIDGET (tmp_list->data));
3373 
3374       /* In principle each widget in the chain should be a descendant
3375        * of the container, but we don't want to check that here. It's
3376        * expensive and also it's allowed to set the focus chain before
3377        * you pack the widgets, or have a widget in the chain that isn't
3378        * always packed. So we check for ancestor during actual traversal.
3379        */
3380 
3381       chain = g_list_prepend (chain, tmp_list->data);
3382 
3383       g_signal_connect (tmp_list->data,
3384                         "destroy",
3385                         G_CALLBACK (chain_widget_destroyed),
3386                         container);
3387 
3388       tmp_list = tmp_list->next;
3389     }
3390 
3391   chain = g_list_reverse (chain);
3392 
3393   g_object_set_qdata (G_OBJECT (container), quark_focus_chain, chain);
3394 }
3395 
3396 /**
3397  * gtk_container_get_focus_chain:
3398  * @container:         a #GtkContainer
3399  * @focusable_widgets: (element-type GtkWidget) (out) (transfer container): location
3400  *                     to store the focus chain of the
3401  *                     container, or %NULL. You should free this list
3402  *                     using g_list_free() when you are done with it, however
3403  *                     no additional reference count is added to the
3404  *                     individual widgets in the focus chain.
3405  *
3406  * Retrieves the focus chain of the container, if one has been
3407  * set explicitly. If no focus chain has been explicitly
3408  * set, GTK+ computes the focus chain based on the positions
3409  * of the children. In that case, GTK+ stores %NULL in
3410  * @focusable_widgets and returns %FALSE.
3411  *
3412  * Returns: %TRUE if the focus chain of the container
3413  * has been set explicitly.
3414  *
3415  * Deprecated: 3.24: For overriding focus behavior, use the
3416  *     GtkWidgetClass::focus signal.
3417  **/
3418 gboolean
gtk_container_get_focus_chain(GtkContainer * container,GList ** focus_chain)3419 gtk_container_get_focus_chain (GtkContainer *container,
3420                                GList       **focus_chain)
3421 {
3422   GtkContainerPrivate *priv;
3423 
3424   g_return_val_if_fail (GTK_IS_CONTAINER (container), FALSE);
3425 
3426   priv = container->priv;
3427 
3428   if (focus_chain)
3429     {
3430       if (priv->has_focus_chain)
3431         *focus_chain = g_list_copy (get_focus_chain (container));
3432       else
3433         *focus_chain = NULL;
3434     }
3435 
3436   return priv->has_focus_chain;
3437 }
3438 
3439 /**
3440  * gtk_container_unset_focus_chain:
3441  * @container: a #GtkContainer
3442  *
3443  * Removes a focus chain explicitly set with gtk_container_set_focus_chain().
3444  *
3445  * Deprecated: 3.24: For overriding focus behavior, use the
3446  *     GtkWidgetClass::focus signal.
3447  **/
3448 void
gtk_container_unset_focus_chain(GtkContainer * container)3449 gtk_container_unset_focus_chain (GtkContainer  *container)
3450 {
3451   GtkContainerPrivate *priv;
3452 
3453   g_return_if_fail (GTK_IS_CONTAINER (container));
3454 
3455   priv = container->priv;
3456 
3457   if (priv->has_focus_chain)
3458     {
3459       GList *chain;
3460       GList *tmp_list;
3461 
3462       chain = get_focus_chain (container);
3463 
3464       priv->has_focus_chain = FALSE;
3465 
3466       g_object_set_qdata (G_OBJECT (container), quark_focus_chain, NULL);
3467 
3468       tmp_list = chain;
3469       while (tmp_list != NULL)
3470         {
3471           g_signal_handlers_disconnect_by_func (tmp_list->data,
3472                                                 chain_widget_destroyed,
3473                                                 container);
3474 
3475           tmp_list = tmp_list->next;
3476         }
3477 
3478       g_list_free (chain);
3479     }
3480 }
3481 
3482 /**
3483  * gtk_container_set_focus_vadjustment:
3484  * @container: a #GtkContainer
3485  * @adjustment: an adjustment which should be adjusted when the focus
3486  *   is moved among the descendents of @container
3487  *
3488  * Hooks up an adjustment to focus handling in a container, so when a
3489  * child of the container is focused, the adjustment is scrolled to
3490  * show that widget. This function sets the vertical alignment. See
3491  * gtk_scrolled_window_get_vadjustment() for a typical way of obtaining
3492  * the adjustment and gtk_container_set_focus_hadjustment() for setting
3493  * the horizontal adjustment.
3494  *
3495  * The adjustments have to be in pixel units and in the same coordinate
3496  * system as the allocation for immediate children of the container.
3497  */
3498 void
gtk_container_set_focus_vadjustment(GtkContainer * container,GtkAdjustment * adjustment)3499 gtk_container_set_focus_vadjustment (GtkContainer  *container,
3500                                      GtkAdjustment *adjustment)
3501 {
3502   g_return_if_fail (GTK_IS_CONTAINER (container));
3503   if (adjustment)
3504     g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
3505 
3506   if (adjustment)
3507     g_object_ref (adjustment);
3508 
3509   g_object_set_qdata_full (G_OBJECT (container),
3510                            vadjustment_key_id,
3511                            adjustment,
3512                            g_object_unref);
3513 }
3514 
3515 /**
3516  * gtk_container_get_focus_vadjustment:
3517  * @container: a #GtkContainer
3518  *
3519  * Retrieves the vertical focus adjustment for the container. See
3520  * gtk_container_set_focus_vadjustment().
3521  *
3522  * Returns: (nullable) (transfer none): the vertical focus adjustment, or
3523  *   %NULL if none has been set.
3524  **/
3525 GtkAdjustment *
gtk_container_get_focus_vadjustment(GtkContainer * container)3526 gtk_container_get_focus_vadjustment (GtkContainer *container)
3527 {
3528   GtkAdjustment *vadjustment;
3529 
3530   g_return_val_if_fail (GTK_IS_CONTAINER (container), NULL);
3531 
3532   vadjustment = g_object_get_qdata (G_OBJECT (container), vadjustment_key_id);
3533 
3534   return vadjustment;
3535 }
3536 
3537 /**
3538  * gtk_container_set_focus_hadjustment:
3539  * @container: a #GtkContainer
3540  * @adjustment: an adjustment which should be adjusted when the focus is
3541  *   moved among the descendents of @container
3542  *
3543  * Hooks up an adjustment to focus handling in a container, so when a child
3544  * of the container is focused, the adjustment is scrolled to show that
3545  * widget. This function sets the horizontal alignment.
3546  * See gtk_scrolled_window_get_hadjustment() for a typical way of obtaining
3547  * the adjustment and gtk_container_set_focus_vadjustment() for setting
3548  * the vertical adjustment.
3549  *
3550  * The adjustments have to be in pixel units and in the same coordinate
3551  * system as the allocation for immediate children of the container.
3552  */
3553 void
gtk_container_set_focus_hadjustment(GtkContainer * container,GtkAdjustment * adjustment)3554 gtk_container_set_focus_hadjustment (GtkContainer  *container,
3555                                      GtkAdjustment *adjustment)
3556 {
3557   g_return_if_fail (GTK_IS_CONTAINER (container));
3558   if (adjustment)
3559     g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
3560 
3561   if (adjustment)
3562     g_object_ref (adjustment);
3563 
3564   g_object_set_qdata_full (G_OBJECT (container),
3565                            hadjustment_key_id,
3566                            adjustment,
3567                            g_object_unref);
3568 }
3569 
3570 /**
3571  * gtk_container_get_focus_hadjustment:
3572  * @container: a #GtkContainer
3573  *
3574  * Retrieves the horizontal focus adjustment for the container. See
3575  * gtk_container_set_focus_hadjustment ().
3576  *
3577  * Returns: (nullable) (transfer none): the horizontal focus adjustment, or %NULL if
3578  *   none has been set.
3579  **/
3580 GtkAdjustment *
gtk_container_get_focus_hadjustment(GtkContainer * container)3581 gtk_container_get_focus_hadjustment (GtkContainer *container)
3582 {
3583   GtkAdjustment *hadjustment;
3584 
3585   g_return_val_if_fail (GTK_IS_CONTAINER (container), NULL);
3586 
3587   hadjustment = g_object_get_qdata (G_OBJECT (container), hadjustment_key_id);
3588 
3589   return hadjustment;
3590 }
3591 
3592 
3593 static void
gtk_container_show_all(GtkWidget * widget)3594 gtk_container_show_all (GtkWidget *widget)
3595 {
3596   g_return_if_fail (GTK_IS_CONTAINER (widget));
3597 
3598   gtk_container_foreach (GTK_CONTAINER (widget),
3599                          (GtkCallback) gtk_widget_show_all,
3600                          NULL);
3601   gtk_widget_show (widget);
3602 }
3603 
3604 typedef struct {
3605   GtkWidget *child;
3606   int window_depth;
3607 } ChildOrderInfo;
3608 
3609 static void
gtk_container_draw_forall(GtkWidget * widget,gpointer client_data)3610 gtk_container_draw_forall (GtkWidget *widget,
3611                            gpointer   client_data)
3612 {
3613   struct {
3614     GtkContainer *container;
3615     GArray *child_infos;
3616     cairo_t *cr;
3617   } *data = client_data;
3618   ChildOrderInfo info;
3619   GList *siblings;
3620   GdkWindow *window;
3621 
3622   if (gtk_container_should_propagate_draw (data->container, widget, data->cr))
3623     {
3624       info.child = widget;
3625       info.window_depth = G_MAXINT;
3626       window = _gtk_widget_get_window (widget);
3627       if (window != gtk_widget_get_window (GTK_WIDGET (data->container)))
3628         {
3629           siblings = gdk_window_peek_children (gdk_window_get_parent (window));
3630           info.window_depth = g_list_index (siblings, window);
3631         }
3632       g_array_append_val (data->child_infos, info);
3633     }
3634 }
3635 
3636 static gint
compare_children_for_draw(gconstpointer _a,gconstpointer _b)3637 compare_children_for_draw (gconstpointer  _a,
3638                            gconstpointer  _b)
3639 {
3640   const ChildOrderInfo *a = _a;
3641   const ChildOrderInfo *b = _b;
3642 
3643   return b->window_depth - a->window_depth;
3644 }
3645 
3646 static gint
gtk_container_draw(GtkWidget * widget,cairo_t * cr)3647 gtk_container_draw (GtkWidget *widget,
3648                     cairo_t   *cr)
3649 {
3650   GtkContainer *container = GTK_CONTAINER (widget);
3651   GArray *child_infos;
3652   int i;
3653   ChildOrderInfo *child_info;
3654   struct {
3655     GtkContainer *container;
3656     GArray *child_infos;
3657     cairo_t *cr;
3658   } data;
3659 
3660   child_infos = g_array_new (FALSE, TRUE, sizeof (ChildOrderInfo));
3661 
3662   data.container = container;
3663   data.cr = cr;
3664   data.child_infos = child_infos;
3665   gtk_container_forall (container,
3666                         gtk_container_draw_forall,
3667                         &data);
3668 
3669   g_array_sort (child_infos, compare_children_for_draw);
3670 
3671   for (i = 0; i < child_infos->len; i++)
3672     {
3673       child_info = &g_array_index (child_infos, ChildOrderInfo, i);
3674       gtk_container_propagate_draw (container, child_info->child, cr);
3675     }
3676 
3677   g_array_free (child_infos, TRUE);
3678 
3679   return FALSE;
3680 }
3681 
3682 static void
gtk_container_map_child(GtkWidget * child,gpointer client_data)3683 gtk_container_map_child (GtkWidget *child,
3684                          gpointer   client_data)
3685 {
3686   if (_gtk_widget_get_visible (child) &&
3687       _gtk_widget_get_child_visible (child) &&
3688       !_gtk_widget_get_mapped (child))
3689     gtk_widget_map (child);
3690 }
3691 
3692 static void
gtk_container_map(GtkWidget * widget)3693 gtk_container_map (GtkWidget *widget)
3694 {
3695   gtk_widget_set_mapped (widget, TRUE);
3696 
3697   gtk_container_forall (GTK_CONTAINER (widget),
3698                         gtk_container_map_child,
3699                         NULL);
3700 
3701   if (_gtk_widget_get_has_window (widget))
3702     gdk_window_show (_gtk_widget_get_window (widget));
3703 }
3704 
3705 static void
gtk_container_unmap(GtkWidget * widget)3706 gtk_container_unmap (GtkWidget *widget)
3707 {
3708   gtk_widget_set_mapped (widget, FALSE);
3709 
3710   /* hide our window first so user doesn't see all the child windows
3711    * vanishing one by one.  (only matters these days if one of the
3712    * children has an actual native window instead of client-side
3713    * window, e.g. a GtkSocket would)
3714    */
3715   if (_gtk_widget_get_has_window (widget))
3716     gdk_window_hide (_gtk_widget_get_window (widget));
3717 
3718   gtk_container_forall (GTK_CONTAINER (widget),
3719                         (GtkCallback)gtk_widget_unmap,
3720                         NULL);
3721 }
3722 
3723 static gboolean
gtk_container_should_propagate_draw(GtkContainer * container,GtkWidget * child,cairo_t * cr)3724 gtk_container_should_propagate_draw (GtkContainer   *container,
3725                                      GtkWidget      *child,
3726                                      cairo_t        *cr)
3727 {
3728   GdkWindow *child_in_window;
3729 
3730   if (!_gtk_widget_is_drawable (child))
3731     return FALSE;
3732 
3733   /* Never propagate to a child window when exposing a window
3734    * that is not the one the child widget is in.
3735    */
3736   if (_gtk_widget_get_has_window (child))
3737     child_in_window = gdk_window_get_parent (_gtk_widget_get_window (child));
3738   else
3739     child_in_window = _gtk_widget_get_window (child);
3740   if (!gtk_cairo_should_draw_window (cr, child_in_window))
3741     return FALSE;
3742 
3743   return TRUE;
3744 }
3745 
3746 static void
union_with_clip(GtkWidget * widget,gpointer data)3747 union_with_clip (GtkWidget *widget,
3748                  gpointer   data)
3749 {
3750   GdkRectangle *clip = data;
3751   GtkAllocation widget_clip;
3752 
3753   if (!gtk_widget_is_visible (widget) ||
3754       !_gtk_widget_get_child_visible (widget))
3755     return;
3756 
3757   gtk_widget_get_clip (widget, &widget_clip);
3758 
3759   if (clip->width == 0 || clip->height == 0)
3760     *clip = widget_clip;
3761   else
3762     gdk_rectangle_union (&widget_clip, clip, clip);
3763 }
3764 
3765 void
gtk_container_get_children_clip(GtkContainer * container,GtkAllocation * out_clip)3766 gtk_container_get_children_clip (GtkContainer  *container,
3767                                  GtkAllocation *out_clip)
3768 {
3769   memset (out_clip, 0, sizeof (GtkAllocation));
3770 
3771   gtk_container_forall (container, union_with_clip, out_clip);
3772 }
3773 
3774 /**
3775  * gtk_container_propagate_draw:
3776  * @container: a #GtkContainer
3777  * @child: a child of @container
3778  * @cr: Cairo context as passed to the container. If you want to use @cr
3779  *   in container’s draw function, consider using cairo_save() and
3780  *   cairo_restore() before calling this function.
3781  *
3782  * When a container receives a call to the draw function, it must send
3783  * synthetic #GtkWidget::draw calls to all children that don’t have their
3784  * own #GdkWindows. This function provides a convenient way of doing this.
3785  * A container, when it receives a call to its #GtkWidget::draw function,
3786  * calls gtk_container_propagate_draw() once for each child, passing in
3787  * the @cr the container received.
3788  *
3789  * gtk_container_propagate_draw() takes care of translating the origin of @cr,
3790  * and deciding whether the draw needs to be sent to the child. It is a
3791  * convenient and optimized way of getting the same effect as calling
3792  * gtk_widget_draw() on the child directly.
3793  *
3794  * In most cases, a container can simply either inherit the
3795  * #GtkWidget::draw implementation from #GtkContainer, or do some drawing
3796  * and then chain to the ::draw implementation from #GtkContainer.
3797  **/
3798 void
gtk_container_propagate_draw(GtkContainer * container,GtkWidget * child,cairo_t * cr)3799 gtk_container_propagate_draw (GtkContainer *container,
3800                               GtkWidget    *child,
3801                               cairo_t      *cr)
3802 {
3803   GtkAllocation allocation;
3804   GdkWindow *window, *w;
3805   int x, y;
3806 
3807   g_return_if_fail (GTK_IS_CONTAINER (container));
3808   g_return_if_fail (GTK_IS_WIDGET (child));
3809   g_return_if_fail (cr != NULL);
3810   g_return_if_fail (_gtk_widget_get_parent (child) == GTK_WIDGET (container));
3811 
3812   if (!gtk_container_should_propagate_draw (container, child, cr))
3813     return;
3814 
3815   /* translate coordinates. Ugly business, that. */
3816   if (!_gtk_widget_get_has_window (GTK_WIDGET (container)))
3817     {
3818       _gtk_widget_get_allocation (GTK_WIDGET (container), &allocation);
3819       x = -allocation.x;
3820       y = -allocation.y;
3821     }
3822   else
3823     {
3824       x = 0;
3825       y = 0;
3826     }
3827 
3828   window = _gtk_widget_get_window (GTK_WIDGET (container));
3829 
3830   for (w = _gtk_widget_get_window (child); w && w != window; w = gdk_window_get_parent (w))
3831     {
3832       int wx, wy;
3833       gdk_window_get_position (w, &wx, &wy);
3834       x += wx;
3835       y += wy;
3836     }
3837 
3838   if (w == NULL)
3839     {
3840       x = 0;
3841       y = 0;
3842     }
3843 
3844   if (!_gtk_widget_get_has_window (child))
3845     {
3846       _gtk_widget_get_allocation (child, &allocation);
3847       x += allocation.x;
3848       y += allocation.y;
3849     }
3850 
3851   cairo_save (cr);
3852   cairo_translate (cr, x, y);
3853 
3854   gtk_widget_draw_internal (child, cr, TRUE);
3855 
3856   cairo_restore (cr);
3857 }
3858 
3859 gboolean
_gtk_container_get_reallocate_redraws(GtkContainer * container)3860 _gtk_container_get_reallocate_redraws (GtkContainer *container)
3861 {
3862   return container->priv->reallocate_redraws;
3863 }
3864 
3865 /**
3866  * gtk_container_get_path_for_child:
3867  * @container: a #GtkContainer
3868  * @child: a child of @container
3869  *
3870  * Returns a newly created widget path representing all the widget hierarchy
3871  * from the toplevel down to and including @child.
3872  *
3873  * Returns: A newly created #GtkWidgetPath
3874  **/
3875 GtkWidgetPath *
gtk_container_get_path_for_child(GtkContainer * container,GtkWidget * child)3876 gtk_container_get_path_for_child (GtkContainer *container,
3877                                   GtkWidget    *child)
3878 {
3879   GtkWidgetPath *path;
3880 
3881   g_return_val_if_fail (GTK_IS_CONTAINER (container), NULL);
3882   g_return_val_if_fail (GTK_IS_WIDGET (child), NULL);
3883   g_return_val_if_fail (container == (GtkContainer *) _gtk_widget_get_parent (child), NULL);
3884 
3885   path = GTK_CONTAINER_GET_CLASS (container)->get_path_for_child (container, child);
3886   if (gtk_widget_path_get_object_type (path) != G_OBJECT_TYPE (child))
3887     {
3888       g_critical ("%s %p returned a widget path for type %s, but child is %s",
3889                   G_OBJECT_TYPE_NAME (container),
3890                   container,
3891                   g_type_name (gtk_widget_path_get_object_type (path)),
3892                   G_OBJECT_TYPE_NAME (child));
3893     }
3894 
3895   return path;
3896 }
3897