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