]> Pileus Git - ~andy/gtk/blob - gtk/gtkbin.c
Fixed generic height-for-width implementation of GtkBin to consider request adjustments
[~andy/gtk] / gtk / gtkbin.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 /*
21  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
22  * file for a list of people on the GTK+ Team.  See the ChangeLog
23  * files for a list of changes.  These files are distributed with
24  * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
25  */
26
27 /**
28  * SECTION:gtkbin
29  * @Short_description: A container with just one child
30  * @Title: GtkBin
31  *
32  * The #GtkBin widget is a container with just one child.
33  * It is not very useful itself, but it is useful for deriving subclasses,
34  * since it provides common code needed for handling a single child widget.
35  *
36  * Many GTK+ widgets are subclasses of #GtkBin, including #GtkWindow,
37  * #GtkButton, #GtkFrame, #GtkHandleBox or #GtkScrolledWindow.
38  */
39
40 #include "config.h"
41 #include "gtkbin.h"
42 #include "gtksizerequest.h"
43 #include "gtkintl.h"
44
45
46 struct _GtkBinPrivate
47 {
48   GtkWidget *child;
49 };
50
51 static void gtk_bin_add         (GtkContainer   *container,
52                                  GtkWidget      *widget);
53 static void gtk_bin_remove      (GtkContainer   *container,
54                                  GtkWidget      *widget);
55 static void gtk_bin_forall      (GtkContainer   *container,
56                                  gboolean       include_internals,
57                                  GtkCallback     callback,
58                                  gpointer        callback_data);
59 static GType gtk_bin_child_type (GtkContainer   *container);
60
61
62 static GtkSizeRequestMode gtk_bin_get_request_mode                (GtkWidget           *widget);
63 static void               gtk_bin_get_preferred_width_for_height  (GtkWidget           *widget,
64                                                                    gint                 height,
65                                                                    gint                *minimum_width,
66                                                                    gint                *natural_width);
67 static void               gtk_bin_get_preferred_height_for_width  (GtkWidget           *widget,
68                                                                    gint                 width,
69                                                                    gint                *minimum_height,
70                                                                    gint                *natural_height);
71
72 G_DEFINE_ABSTRACT_TYPE (GtkBin, gtk_bin, GTK_TYPE_CONTAINER)
73
74 static void
75 gtk_bin_class_init (GtkBinClass *class)
76 {
77   GtkWidgetClass *widget_class = (GtkWidgetClass*) class;
78   GtkContainerClass *container_class = (GtkContainerClass*) class;
79
80   widget_class->get_request_mode               = gtk_bin_get_request_mode;
81   widget_class->get_preferred_width_for_height = gtk_bin_get_preferred_width_for_height;
82   widget_class->get_preferred_height_for_width = gtk_bin_get_preferred_height_for_width;
83
84   container_class->add = gtk_bin_add;
85   container_class->remove = gtk_bin_remove;
86   container_class->forall = gtk_bin_forall;
87   container_class->child_type = gtk_bin_child_type;
88
89   g_type_class_add_private (class, sizeof (GtkBinPrivate));
90 }
91
92 static void
93 gtk_bin_init (GtkBin *bin)
94 {
95   GtkBinPrivate *priv;
96
97   bin->priv = G_TYPE_INSTANCE_GET_PRIVATE (bin,
98                                            GTK_TYPE_BIN,
99                                            GtkBinPrivate);
100   priv = bin->priv;
101
102   gtk_widget_set_has_window (GTK_WIDGET (bin), FALSE);
103
104   priv->child = NULL;
105 }
106
107
108 static GType
109 gtk_bin_child_type (GtkContainer *container)
110 {
111   GtkBinPrivate *priv = GTK_BIN (container)->priv;
112
113   if (!priv->child)
114     return GTK_TYPE_WIDGET;
115   else
116     return G_TYPE_NONE;
117 }
118
119 static void
120 gtk_bin_add (GtkContainer *container,
121              GtkWidget    *child)
122 {
123   GtkBin *bin = GTK_BIN (container);
124   GtkBinPrivate *priv = bin->priv;
125
126   if (priv->child != NULL)
127     {
128       g_warning ("Attempting to add a widget with type %s to a %s, "
129                  "but as a GtkBin subclass a %s can only contain one widget at a time; "
130                  "it already contains a widget of type %s",
131                  g_type_name (G_OBJECT_TYPE (child)),
132                  g_type_name (G_OBJECT_TYPE (bin)),
133                  g_type_name (G_OBJECT_TYPE (bin)),
134                  g_type_name (G_OBJECT_TYPE (priv->child)));
135       return;
136     }
137
138   gtk_widget_set_parent (child, GTK_WIDGET (bin));
139   priv->child = child;
140 }
141
142 static void
143 gtk_bin_remove (GtkContainer *container,
144                 GtkWidget    *child)
145 {
146   GtkBin *bin = GTK_BIN (container);
147   GtkBinPrivate *priv = bin->priv;
148   gboolean widget_was_visible;
149
150   g_return_if_fail (priv->child == child);
151
152   widget_was_visible = gtk_widget_get_visible (child);
153   
154   gtk_widget_unparent (child);
155   priv->child = NULL;
156   
157   /* queue resize regardless of gtk_widget_get_visible (container),
158    * since that's what is needed by toplevels, which derive from GtkBin.
159    */
160   if (widget_was_visible)
161     gtk_widget_queue_resize (GTK_WIDGET (container));
162 }
163
164 static void
165 gtk_bin_forall (GtkContainer *container,
166                 gboolean      include_internals,
167                 GtkCallback   callback,
168                 gpointer      callback_data)
169 {
170   GtkBin *bin = GTK_BIN (container);
171   GtkBinPrivate *priv = bin->priv;
172
173   if (priv->child)
174     (* callback) (priv->child, callback_data);
175 }
176
177
178 /* GtkBin widgets define the padding and borders independantly so
179  * we cannot provide a generic get_size() for the same reason
180  * we never implemented size_request() here.
181  *
182  * But for cases where the GtkBin class's padding is constant and
183  * does not vary based on allocation (most cases), we can at least 
184  * deduce a common code path for the get_width_for_height()/get_height_for_width()
185  * cases by using the delta of the base size requsts.
186  */
187 static GtkSizeRequestMode
188 gtk_bin_get_request_mode (GtkWidget           *widget)
189 {
190   GtkBin *bin = GTK_BIN (widget);
191   GtkBinPrivate *priv = bin->priv;
192
193   if (priv->child)
194     return gtk_widget_get_request_mode (priv->child);
195
196   return GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH;
197 }
198
199 static void
200 get_child_padding_delta (GtkBin *bin,
201                          gint   *delta_h,
202                          gint   *delta_v)
203 {
204   GtkBinPrivate *priv = bin->priv;
205   gint hmin, vmin, hnat, vnat, child_hmin, child_vmin;
206
207   /* we can't use gtk_widget_get_preferred_width() wrapper 
208    * because we want our "original" request, not any external
209    * adjustments from set_size_request() or whatever.  we have
210    * to ask for natural also because NULL isn't allowed for the
211    * direct vfuncs
212    */
213   GTK_WIDGET_GET_CLASS (bin)->get_preferred_width (GTK_WIDGET (bin), &hmin, &hnat);
214   GTK_WIDGET_GET_CLASS (bin)->adjust_size_request (GTK_WIDGET (bin), 
215                                                    GTK_ORIENTATION_HORIZONTAL, &hmin, &hnat);
216
217   GTK_WIDGET_GET_CLASS (bin)->get_preferred_height (GTK_WIDGET (bin), &vmin, &vnat);
218   GTK_WIDGET_GET_CLASS (bin)->adjust_size_request (GTK_WIDGET (bin), 
219                                                    GTK_ORIENTATION_VERTICAL, &vmin, &vnat);
220
221   gtk_widget_get_preferred_width (priv->child, &child_hmin, NULL);
222   gtk_widget_get_preferred_height (priv->child, &child_vmin, NULL);
223
224   *delta_h = hmin - child_hmin;
225   *delta_v = vmin - child_vmin;
226 }
227
228 static void 
229 gtk_bin_get_preferred_width_for_height (GtkWidget *widget,
230                                         gint       height,
231                                         gint      *minimum_width,
232                                         gint      *natural_width)
233 {
234   GtkBin *bin = GTK_BIN (widget);
235   GtkBinPrivate *priv = bin->priv;
236   gint    hdelta, vdelta, child_min, child_nat;
237
238   if (priv->child)
239     {
240       get_child_padding_delta (bin, &hdelta, &vdelta);
241
242       gtk_widget_get_preferred_width_for_height (priv->child,
243                                                  height - vdelta,
244                                                  &child_min, &child_nat);
245
246       if (minimum_width)
247         *minimum_width = child_min + hdelta;
248       
249       if (natural_width)
250         *natural_width = child_nat + hdelta;
251     }
252   else
253     GTK_WIDGET_GET_CLASS (widget)->get_preferred_width (widget, minimum_width, natural_width);
254 }
255
256 static void
257 gtk_bin_get_preferred_height_for_width  (GtkWidget *widget,
258                                          gint       width,
259                                          gint      *minimum_height,
260                                          gint      *natural_height)
261 {
262   GtkBin *bin = GTK_BIN (widget);
263   GtkBinPrivate *priv = bin->priv;
264   gint    hdelta, vdelta, child_min, child_nat;
265
266   if (priv->child)
267     {
268       get_child_padding_delta (bin, &hdelta, &vdelta);
269
270       gtk_widget_get_preferred_height_for_width (priv->child,
271                                                  width - hdelta,
272                                                  &child_min, &child_nat);
273
274       if (minimum_height)
275         *minimum_height = child_min + vdelta;
276       
277       if (natural_height)
278         *natural_height = child_nat + vdelta;
279     }
280   else
281     GTK_WIDGET_GET_CLASS (widget)->get_preferred_height (widget, minimum_height, natural_height);
282 }
283
284
285 /**
286  * gtk_bin_get_child:
287  * @bin: a #GtkBin
288  * 
289  * Gets the child of the #GtkBin, or %NULL if the bin contains
290  * no child widget. The returned widget does not have a reference
291  * added, so you do not need to unref it.
292  *
293  * Return value: (transfer none): pointer to child of the #GtkBin
294  **/
295 GtkWidget*
296 gtk_bin_get_child (GtkBin *bin)
297 {
298   g_return_val_if_fail (GTK_IS_BIN (bin), NULL);
299
300   return bin->priv->child;
301 }
302
303 void
304 _gtk_bin_set_child (GtkBin    *bin,
305                     GtkWidget *widget)
306 {
307   bin->priv->child = widget;
308 }