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