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 /**
26 * SECTION:gtkbin
27 * @Short_description: A container with just one child
28 * @Title: GtkBin
29 *
30 * The #GtkBin widget is a container with just one child.
31 * It is not very useful itself, but it is useful for deriving subclasses,
32 * since it provides common code needed for handling a single child widget.
33 *
34 * Many GTK+ widgets are subclasses of #GtkBin, including #GtkWindow,
35 * #GtkButton, #GtkFrame, #GtkHandleBox or #GtkScrolledWindow.
36 */
37
38 #include "config.h"
39 #include "gtkbin.h"
40 #include "gtksizerequest.h"
41 #include "gtkintl.h"
42
43
44 struct _GtkBinPrivate
45 {
46 GtkWidget *child;
47 };
48
49 static void gtk_bin_add (GtkContainer *container,
50 GtkWidget *widget);
51 static void gtk_bin_remove (GtkContainer *container,
52 GtkWidget *widget);
53 static void gtk_bin_forall (GtkContainer *container,
54 gboolean include_internals,
55 GtkCallback callback,
56 gpointer callback_data);
57 static GType gtk_bin_child_type (GtkContainer *container);
58
59 static void gtk_bin_get_preferred_width (GtkWidget *widget,
60 gint *minimum_width,
61 gint *natural_width);
62 static void gtk_bin_get_preferred_height (GtkWidget *widget,
63 gint *minimum_height,
64 gint *natural_height);
65 static void gtk_bin_get_preferred_width_for_height (GtkWidget *widget,
66 gint height,
67 gint *minimum_width,
68 gint *natural_width);
69 static void gtk_bin_get_preferred_height_for_width (GtkWidget *widget,
70 gint width,
71 gint *minimum_height,
72 gint *natural_height);
73 static void gtk_bin_size_allocate (GtkWidget *widget,
74 GtkAllocation *allocation);
75
G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE(GtkBin,gtk_bin,GTK_TYPE_CONTAINER)76 G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GtkBin, gtk_bin, GTK_TYPE_CONTAINER)
77
78 static void
79 gtk_bin_class_init (GtkBinClass *class)
80 {
81 GtkWidgetClass *widget_class = (GtkWidgetClass*) class;
82 GtkContainerClass *container_class = (GtkContainerClass*) class;
83
84 widget_class->get_preferred_width = gtk_bin_get_preferred_width;
85 widget_class->get_preferred_height = gtk_bin_get_preferred_height;
86 widget_class->get_preferred_width_for_height = gtk_bin_get_preferred_width_for_height;
87 widget_class->get_preferred_height_for_width = gtk_bin_get_preferred_height_for_width;
88 widget_class->size_allocate = gtk_bin_size_allocate;
89
90 container_class->add = gtk_bin_add;
91 container_class->remove = gtk_bin_remove;
92 container_class->forall = gtk_bin_forall;
93 container_class->child_type = gtk_bin_child_type;
94 }
95
96 static void
gtk_bin_init(GtkBin * bin)97 gtk_bin_init (GtkBin *bin)
98 {
99 bin->priv = gtk_bin_get_instance_private (bin);
100
101 gtk_widget_set_has_window (GTK_WIDGET (bin), FALSE);
102 }
103
104
105 static GType
gtk_bin_child_type(GtkContainer * container)106 gtk_bin_child_type (GtkContainer *container)
107 {
108 GtkBinPrivate *priv = GTK_BIN (container)->priv;
109
110 if (!priv->child)
111 return GTK_TYPE_WIDGET;
112 else
113 return G_TYPE_NONE;
114 }
115
116 static void
gtk_bin_add(GtkContainer * container,GtkWidget * child)117 gtk_bin_add (GtkContainer *container,
118 GtkWidget *child)
119 {
120 GtkBin *bin = GTK_BIN (container);
121 GtkBinPrivate *priv = bin->priv;
122
123 if (priv->child != NULL)
124 {
125 g_warning ("Attempting to add a widget with type %s to a %s, "
126 "but as a GtkBin subclass a %s can only contain one widget at a time; "
127 "it already contains a widget of type %s",
128 g_type_name (G_OBJECT_TYPE (child)),
129 g_type_name (G_OBJECT_TYPE (bin)),
130 g_type_name (G_OBJECT_TYPE (bin)),
131 g_type_name (G_OBJECT_TYPE (priv->child)));
132 return;
133 }
134
135 gtk_widget_set_parent (child, GTK_WIDGET (bin));
136 priv->child = child;
137 }
138
139 static void
gtk_bin_remove(GtkContainer * container,GtkWidget * child)140 gtk_bin_remove (GtkContainer *container,
141 GtkWidget *child)
142 {
143 GtkBin *bin = GTK_BIN (container);
144 GtkBinPrivate *priv = bin->priv;
145 gboolean widget_was_visible;
146
147 g_return_if_fail (priv->child == child);
148
149 widget_was_visible = gtk_widget_get_visible (child);
150
151 gtk_widget_unparent (child);
152 priv->child = NULL;
153
154 /* queue resize regardless of gtk_widget_get_visible (container),
155 * since that's what is needed by toplevels, which derive from GtkBin.
156 */
157 if (widget_was_visible)
158 gtk_widget_queue_resize (GTK_WIDGET (container));
159 }
160
161 static void
gtk_bin_forall(GtkContainer * container,gboolean include_internals,GtkCallback callback,gpointer callback_data)162 gtk_bin_forall (GtkContainer *container,
163 gboolean include_internals,
164 GtkCallback callback,
165 gpointer callback_data)
166 {
167 GtkBin *bin = GTK_BIN (container);
168 GtkBinPrivate *priv = bin->priv;
169
170 if (priv->child)
171 (* callback) (priv->child, callback_data);
172 }
173
174 static int
gtk_bin_get_effective_border_width(GtkBin * bin)175 gtk_bin_get_effective_border_width (GtkBin *bin)
176 {
177 if (GTK_CONTAINER_CLASS (GTK_BIN_GET_CLASS (bin))->_handle_border_width)
178 return 0;
179
180 return gtk_container_get_border_width (GTK_CONTAINER (bin));
181 }
182
183 static void
gtk_bin_get_preferred_width(GtkWidget * widget,gint * minimum_width,gint * natural_width)184 gtk_bin_get_preferred_width (GtkWidget *widget,
185 gint *minimum_width,
186 gint *natural_width)
187 {
188 GtkBin *bin = GTK_BIN (widget);
189 GtkBinPrivate *priv = bin->priv;
190 gint border_width;
191
192 *minimum_width = 0;
193 *natural_width = 0;
194
195 if (priv->child && gtk_widget_get_visible (priv->child))
196 {
197 gint child_min, child_nat;
198 gtk_widget_get_preferred_width (priv->child,
199 &child_min, &child_nat);
200 *minimum_width = child_min;
201 *natural_width = child_nat;
202 }
203
204 border_width = gtk_bin_get_effective_border_width (bin);
205 *minimum_width += 2 * border_width;
206 *natural_width += 2 * border_width;
207 }
208
209 static void
gtk_bin_get_preferred_height(GtkWidget * widget,gint * minimum_height,gint * natural_height)210 gtk_bin_get_preferred_height (GtkWidget *widget,
211 gint *minimum_height,
212 gint *natural_height)
213 {
214 GtkBin *bin = GTK_BIN (widget);
215 GtkBinPrivate *priv = bin->priv;
216 gint border_width;
217
218 *minimum_height = 0;
219 *natural_height = 0;
220
221 if (priv->child && gtk_widget_get_visible (priv->child))
222 {
223 gint child_min, child_nat;
224 gtk_widget_get_preferred_height (priv->child,
225 &child_min, &child_nat);
226 *minimum_height = child_min;
227 *natural_height = child_nat;
228 }
229
230 border_width = gtk_bin_get_effective_border_width (bin);
231 *minimum_height += 2 * border_width;
232 *natural_height += 2 * border_width;
233 }
234
235 static void
gtk_bin_get_preferred_width_for_height(GtkWidget * widget,gint height,gint * minimum_width,gint * natural_width)236 gtk_bin_get_preferred_width_for_height (GtkWidget *widget,
237 gint height,
238 gint *minimum_width,
239 gint *natural_width)
240 {
241 GtkBin *bin = GTK_BIN (widget);
242 GtkBinPrivate *priv = bin->priv;
243 gint border_width;
244
245 *minimum_width = 0;
246 *natural_width = 0;
247
248 border_width = gtk_bin_get_effective_border_width (bin);
249
250 if (priv->child && gtk_widget_get_visible (priv->child))
251 {
252 gint child_min, child_nat;
253 gtk_widget_get_preferred_width_for_height (priv->child, height - 2 * border_width,
254 &child_min, &child_nat);
255
256 *minimum_width = child_min;
257 *natural_width = child_nat;
258 }
259
260 *minimum_width += 2 * border_width;
261 *natural_width += 2 * border_width;
262 }
263
264 static void
gtk_bin_get_preferred_height_for_width(GtkWidget * widget,gint width,gint * minimum_height,gint * natural_height)265 gtk_bin_get_preferred_height_for_width (GtkWidget *widget,
266 gint width,
267 gint *minimum_height,
268 gint *natural_height)
269 {
270 GtkBin *bin = GTK_BIN (widget);
271 GtkBinPrivate *priv = bin->priv;
272 gint border_width;
273
274 *minimum_height = 0;
275 *natural_height = 0;
276
277 border_width = gtk_bin_get_effective_border_width (bin);
278
279 if (priv->child && gtk_widget_get_visible (priv->child))
280 {
281 gint child_min, child_nat;
282 gtk_widget_get_preferred_height_for_width (priv->child, width - 2 * border_width,
283 &child_min, &child_nat);
284
285 *minimum_height = child_min;
286 *natural_height = child_nat;
287 }
288
289 *minimum_height += 2 * border_width;
290 *natural_height += 2 * border_width;
291 }
292
293 static void
gtk_bin_size_allocate(GtkWidget * widget,GtkAllocation * allocation)294 gtk_bin_size_allocate (GtkWidget *widget,
295 GtkAllocation *allocation)
296 {
297 GtkBin *bin = GTK_BIN (widget);
298 GtkBinPrivate *priv = bin->priv;
299
300 gtk_widget_set_allocation (widget, allocation);
301
302 if (priv->child && gtk_widget_get_visible (priv->child))
303 {
304 GtkAllocation child_allocation;
305 gint border_width = gtk_bin_get_effective_border_width (bin);
306
307 child_allocation.x = allocation->x + border_width;
308 child_allocation.y = allocation->y + border_width;
309 child_allocation.width = allocation->width - 2 * border_width;
310 child_allocation.height = allocation->height - 2 * border_width;
311
312 gtk_widget_size_allocate (priv->child, &child_allocation);
313 }
314 }
315
316 /**
317 * gtk_bin_get_child:
318 * @bin: a #GtkBin
319 *
320 * Gets the child of the #GtkBin, or %NULL if the bin contains
321 * no child widget. The returned widget does not have a reference
322 * added, so you do not need to unref it.
323 *
324 * Returns: (transfer none) (nullable): the child of @bin, or %NULL if it does
325 * not have a child.
326 **/
327 GtkWidget*
gtk_bin_get_child(GtkBin * bin)328 gtk_bin_get_child (GtkBin *bin)
329 {
330 g_return_val_if_fail (GTK_IS_BIN (bin), NULL);
331
332 return bin->priv->child;
333 }
334
335 void
_gtk_bin_set_child(GtkBin * bin,GtkWidget * widget)336 _gtk_bin_set_child (GtkBin *bin,
337 GtkWidget *widget)
338 {
339 bin->priv->child = widget;
340 }
341