]> Pileus Git - ~andy/gtk/blob - gtk/gtkbin.c
331ae5376680ea98415ebb232fdf8ba683251d52
[~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 void               gtk_bin_get_preferred_width_for_height  (GtkWidget           *widget,
63                                                                    gint                 height,
64                                                                    gint                *minimum_width,
65                                                                    gint                *natural_width);
66 static void               gtk_bin_get_preferred_height_for_width  (GtkWidget           *widget,
67                                                                    gint                 width,
68                                                                    gint                *minimum_height,
69                                                                    gint                *natural_height);
70
71 G_DEFINE_ABSTRACT_TYPE (GtkBin, gtk_bin, GTK_TYPE_CONTAINER)
72
73 static void
74 gtk_bin_class_init (GtkBinClass *class)
75 {
76   GtkWidgetClass *widget_class = (GtkWidgetClass*) class;
77   GtkContainerClass *container_class = (GtkContainerClass*) class;
78
79   widget_class->get_preferred_width_for_height = gtk_bin_get_preferred_width_for_height;
80   widget_class->get_preferred_height_for_width = gtk_bin_get_preferred_height_for_width;
81
82   container_class->add = gtk_bin_add;
83   container_class->remove = gtk_bin_remove;
84   container_class->forall = gtk_bin_forall;
85   container_class->child_type = gtk_bin_child_type;
86
87   g_type_class_add_private (class, sizeof (GtkBinPrivate));
88 }
89
90 static void
91 gtk_bin_init (GtkBin *bin)
92 {
93   GtkBinPrivate *priv;
94
95   bin->priv = G_TYPE_INSTANCE_GET_PRIVATE (bin,
96                                            GTK_TYPE_BIN,
97                                            GtkBinPrivate);
98   priv = bin->priv;
99
100   gtk_widget_set_has_window (GTK_WIDGET (bin), FALSE);
101
102   priv->child = NULL;
103 }
104
105
106 static GType
107 gtk_bin_child_type (GtkContainer *container)
108 {
109   GtkBinPrivate *priv = GTK_BIN (container)->priv;
110
111   if (!priv->child)
112     return GTK_TYPE_WIDGET;
113   else
114     return G_TYPE_NONE;
115 }
116
117 static void
118 gtk_bin_add (GtkContainer *container,
119              GtkWidget    *child)
120 {
121   GtkBin *bin = GTK_BIN (container);
122   GtkBinPrivate *priv = bin->priv;
123
124   if (priv->child != NULL)
125     {
126       g_warning ("Attempting to add a widget with type %s to a %s, "
127                  "but as a GtkBin subclass a %s can only contain one widget at a time; "
128                  "it already contains a widget of type %s",
129                  g_type_name (G_OBJECT_TYPE (child)),
130                  g_type_name (G_OBJECT_TYPE (bin)),
131                  g_type_name (G_OBJECT_TYPE (bin)),
132                  g_type_name (G_OBJECT_TYPE (priv->child)));
133       return;
134     }
135
136   gtk_widget_set_parent (child, GTK_WIDGET (bin));
137   priv->child = child;
138 }
139
140 static void
141 gtk_bin_remove (GtkContainer *container,
142                 GtkWidget    *child)
143 {
144   GtkBin *bin = GTK_BIN (container);
145   GtkBinPrivate *priv = bin->priv;
146   gboolean widget_was_visible;
147
148   g_return_if_fail (priv->child == child);
149
150   widget_was_visible = gtk_widget_get_visible (child);
151   
152   gtk_widget_unparent (child);
153   priv->child = NULL;
154   
155   /* queue resize regardless of gtk_widget_get_visible (container),
156    * since that's what is needed by toplevels, which derive from GtkBin.
157    */
158   if (widget_was_visible)
159     gtk_widget_queue_resize (GTK_WIDGET (container));
160 }
161
162 static void
163 gtk_bin_forall (GtkContainer *container,
164                 gboolean      include_internals,
165                 GtkCallback   callback,
166                 gpointer      callback_data)
167 {
168   GtkBin *bin = GTK_BIN (container);
169   GtkBinPrivate *priv = bin->priv;
170
171   if (priv->child)
172     (* callback) (priv->child, callback_data);
173 }
174
175
176 /* GtkBin widgets define the padding and borders independantly so
177  * we cannot provide a generic get_size() for the same reason
178  * we never implemented size_request() here.
179  *
180  * But for cases where the GtkBin class's padding is constant and
181  * does not vary based on allocation (most cases), we can at least 
182  * deduce a common code path for the get_width_for_height()/get_height_for_width()
183  * cases by using the delta of the base size requsts.
184  */
185 static void
186 get_child_padding_delta (GtkBin *bin,
187                          gint   *delta_h,
188                          gint   *delta_v)
189 {
190   GtkBinPrivate *priv = bin->priv;
191   gint hmin, vmin, hnat, vnat, child_hmin, child_vmin;
192
193   /* we can't use gtk_widget_get_preferred_width() wrapper 
194    * because we want our "original" request, not any external
195    * adjustments from set_size_request() or whatever.  we have
196    * to ask for natural also because NULL isn't allowed for the
197    * direct vfuncs
198    */
199   GTK_WIDGET_GET_CLASS (bin)->get_preferred_width (GTK_WIDGET (bin), &hmin, &hnat);
200   GTK_WIDGET_GET_CLASS (bin)->adjust_size_request (GTK_WIDGET (bin), 
201                                                    GTK_ORIENTATION_HORIZONTAL, &hmin, &hnat);
202
203   GTK_WIDGET_GET_CLASS (bin)->get_preferred_height (GTK_WIDGET (bin), &vmin, &vnat);
204   GTK_WIDGET_GET_CLASS (bin)->adjust_size_request (GTK_WIDGET (bin), 
205                                                    GTK_ORIENTATION_VERTICAL, &vmin, &vnat);
206
207   gtk_widget_get_preferred_width (priv->child, &child_hmin, NULL);
208   gtk_widget_get_preferred_height (priv->child, &child_vmin, NULL);
209
210   *delta_h = hmin - child_hmin;
211   *delta_v = vmin - child_vmin;
212 }
213
214 static void 
215 gtk_bin_get_preferred_width_for_height (GtkWidget *widget,
216                                         gint       height,
217                                         gint      *minimum_width,
218                                         gint      *natural_width)
219 {
220   GtkBin *bin = GTK_BIN (widget);
221   GtkBinPrivate *priv = bin->priv;
222   gint    hdelta, vdelta, child_min, child_nat;
223
224   if (priv->child)
225     {
226       get_child_padding_delta (bin, &hdelta, &vdelta);
227
228       gtk_widget_get_preferred_width_for_height (priv->child,
229                                                  height - vdelta,
230                                                  &child_min, &child_nat);
231
232       if (minimum_width)
233         *minimum_width = child_min + hdelta;
234       
235       if (natural_width)
236         *natural_width = child_nat + hdelta;
237     }
238   else
239     GTK_WIDGET_GET_CLASS (widget)->get_preferred_width (widget, minimum_width, natural_width);
240 }
241
242 static void
243 gtk_bin_get_preferred_height_for_width  (GtkWidget *widget,
244                                          gint       width,
245                                          gint      *minimum_height,
246                                          gint      *natural_height)
247 {
248   GtkBin *bin = GTK_BIN (widget);
249   GtkBinPrivate *priv = bin->priv;
250   gint    hdelta, vdelta, child_min, child_nat;
251
252   if (priv->child)
253     {
254       get_child_padding_delta (bin, &hdelta, &vdelta);
255
256       gtk_widget_get_preferred_height_for_width (priv->child,
257                                                  width - hdelta,
258                                                  &child_min, &child_nat);
259
260       if (minimum_height)
261         *minimum_height = child_min + vdelta;
262       
263       if (natural_height)
264         *natural_height = child_nat + vdelta;
265     }
266   else
267     GTK_WIDGET_GET_CLASS (widget)->get_preferred_height (widget, minimum_height, natural_height);
268 }
269
270
271 /**
272  * gtk_bin_get_child:
273  * @bin: a #GtkBin
274  * 
275  * Gets the child of the #GtkBin, or %NULL if the bin contains
276  * no child widget. The returned widget does not have a reference
277  * added, so you do not need to unref it.
278  *
279  * Return value: (transfer none): pointer to child of the #GtkBin
280  **/
281 GtkWidget*
282 gtk_bin_get_child (GtkBin *bin)
283 {
284   g_return_val_if_fail (GTK_IS_BIN (bin), NULL);
285
286   return bin->priv->child;
287 }
288
289 void
290 _gtk_bin_set_child (GtkBin    *bin,
291                     GtkWidget *widget)
292 {
293   bin->priv->child = widget;
294 }