]> Pileus Git - ~andy/gtk/blob - gtk/gtkaspectframe.c
Use gtk_size_request_get_size() instead deprecated gtk_widget_get_child_requisition()
[~andy/gtk] / gtk / gtkaspectframe.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * GtkAspectFrame: Ensure that the child window has a specified aspect ratio
5  *    or, if obey_child, has the same aspect ratio as its requested size
6  *
7  *     Copyright Owen Taylor                          4/9/97
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the
21  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22  * Boston, MA 02111-1307, USA.
23  */
24
25 /*
26  * Modified by the GTK+ Team and others 1997-2001.  See the AUTHORS
27  * file for a list of people on the GTK+ Team.  See the ChangeLog
28  * files for a list of changes.  These files are distributed with
29  * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
30  */
31
32 /**
33  * SECTION:gtkaspectframe
34  * @Short_description: A frame that constrains its child to a particular aspect ratio
35  * @Title: GtkAspectFrame
36  *
37  * The #GtkAspectFrame is useful when you want
38  * pack a widget so that it can resize but always retains
39  * the same aspect ratio. For instance, one might be
40  * drawing a small preview of a larger image. #GtkAspectFrame
41  * derives from #GtkFrame, so it can draw a label and
42  * a frame around the child. The frame will be
43  * "shrink-wrapped" to the size of the child.
44  */
45
46 #include "config.h"
47
48 #include "gtkaspectframe.h"
49
50 #include "gtksizerequest.h"
51
52 #include "gtkprivate.h"
53 #include "gtkintl.h"
54
55
56
57 struct _GtkAspectFramePrivate
58 {
59   GtkAllocation center_allocation;
60
61   gboolean      obey_child;
62   gfloat        xalign;
63   gfloat        yalign;
64   gfloat        ratio;
65 };
66
67
68 enum {
69   PROP_0,
70   PROP_XALIGN,
71   PROP_YALIGN,
72   PROP_RATIO,
73   PROP_OBEY_CHILD
74 };
75
76 static void gtk_aspect_frame_set_property (GObject         *object,
77                                            guint            prop_id,
78                                            const GValue    *value,
79                                            GParamSpec      *pspec);
80 static void gtk_aspect_frame_get_property (GObject         *object,
81                                            guint            prop_id,
82                                            GValue          *value,
83                                            GParamSpec      *pspec);
84 static void gtk_aspect_frame_compute_child_allocation (GtkFrame            *frame,
85                                                        GtkAllocation       *child_allocation);
86
87 #define MAX_RATIO 10000.0
88 #define MIN_RATIO 0.0001
89
90 G_DEFINE_TYPE (GtkAspectFrame, gtk_aspect_frame, GTK_TYPE_FRAME)
91
92 static void
93 gtk_aspect_frame_class_init (GtkAspectFrameClass *class)
94 {
95   GObjectClass *gobject_class;
96   GtkFrameClass *frame_class;
97   
98   gobject_class = (GObjectClass*) class;
99   frame_class = (GtkFrameClass*) class;
100   
101   gobject_class->set_property = gtk_aspect_frame_set_property;
102   gobject_class->get_property = gtk_aspect_frame_get_property;
103
104   frame_class->compute_child_allocation = gtk_aspect_frame_compute_child_allocation;
105
106   g_object_class_install_property (gobject_class,
107                                    PROP_XALIGN,
108                                    g_param_spec_float ("xalign",
109                                                        P_("Horizontal Alignment"),
110                                                        P_("X alignment of the child"),
111                                                        0.0, 1.0, 0.5,
112                                                        GTK_PARAM_READWRITE));
113   g_object_class_install_property (gobject_class,
114                                    PROP_YALIGN,
115                                    g_param_spec_float ("yalign",
116                                                        P_("Vertical Alignment"),
117                                                        P_("Y alignment of the child"),
118                                                        0.0, 1.0, 0.5,
119                                                        GTK_PARAM_READWRITE));
120   g_object_class_install_property (gobject_class,
121                                    PROP_RATIO,
122                                    g_param_spec_float ("ratio",
123                                                        P_("Ratio"),
124                                                        P_("Aspect ratio if obey_child is FALSE"),
125                                                        MIN_RATIO, MAX_RATIO, 1.0,
126                                                        GTK_PARAM_READWRITE));
127   g_object_class_install_property (gobject_class,
128                                    PROP_OBEY_CHILD,
129                                    g_param_spec_boolean ("obey-child",
130                                                          P_("Obey child"),
131                                                          P_("Force aspect ratio to match that of the frame's child"),
132                                                          TRUE,
133                                                          GTK_PARAM_READWRITE));
134
135   g_type_class_add_private (class, sizeof (GtkAspectFramePrivate));
136 }
137
138 static void
139 gtk_aspect_frame_init (GtkAspectFrame *aspect_frame)
140 {
141   GtkAspectFramePrivate *priv;
142
143   aspect_frame->priv = G_TYPE_INSTANCE_GET_PRIVATE (aspect_frame,
144                                                     GTK_TYPE_ASPECT_FRAME,
145                                                     GtkAspectFramePrivate);
146   priv = aspect_frame->priv;
147
148   priv->xalign = 0.5;
149   priv->yalign = 0.5;
150   priv->ratio = 1.0;
151   priv->obey_child = TRUE;
152 }
153
154 static void
155 gtk_aspect_frame_set_property (GObject         *object,
156                                guint            prop_id,
157                                const GValue    *value,
158                                GParamSpec      *pspec)
159 {
160   GtkAspectFrame *aspect_frame = GTK_ASPECT_FRAME (object);
161   GtkAspectFramePrivate *priv = aspect_frame->priv;
162   
163   switch (prop_id)
164     {
165       /* g_object_notify is handled by the _frame_set function */
166     case PROP_XALIGN:
167       gtk_aspect_frame_set (aspect_frame,
168                             g_value_get_float (value),
169                             priv->yalign,
170                             priv->ratio,
171                             priv->obey_child);
172       break;
173     case PROP_YALIGN:
174       gtk_aspect_frame_set (aspect_frame,
175                             priv->xalign,
176                             g_value_get_float (value),
177                             priv->ratio,
178                             priv->obey_child);
179       break;
180     case PROP_RATIO:
181       gtk_aspect_frame_set (aspect_frame,
182                             priv->xalign,
183                             priv->yalign,
184                             g_value_get_float (value),
185                             priv->obey_child);
186       break;
187     case PROP_OBEY_CHILD:
188       gtk_aspect_frame_set (aspect_frame,
189                             priv->xalign,
190                             priv->yalign,
191                             priv->ratio,
192                             g_value_get_boolean (value));
193       break;
194     default:
195        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
196       break;
197     }
198 }
199
200 static void
201 gtk_aspect_frame_get_property (GObject         *object,
202                                guint            prop_id,
203                                GValue          *value,
204                                GParamSpec      *pspec)
205 {
206   GtkAspectFrame *aspect_frame = GTK_ASPECT_FRAME (object);
207   GtkAspectFramePrivate *priv = aspect_frame->priv;
208   
209   switch (prop_id)
210     {
211     case PROP_XALIGN:
212       g_value_set_float (value, priv->xalign);
213       break;
214     case PROP_YALIGN:
215       g_value_set_float (value, priv->yalign);
216       break;
217     case PROP_RATIO:
218       g_value_set_float (value, priv->ratio);
219       break;
220     case PROP_OBEY_CHILD:
221       g_value_set_boolean (value, priv->obey_child);
222       break;
223     default:
224        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
225       break;
226     }
227 }
228
229 /**
230  * gtk_aspect_frame_new:
231  * @label: Label text.
232  * @xalign: Horizontal alignment of the child within the allocation of
233  *  the #GtkAspectFrame. This ranges from 0.0 (left aligned)
234  *  to 1.0 (right aligned)
235  * @yalign: Vertical alignment of the child within the allocation of
236  *  the #GtkAspectFrame. This ranges from 0.0 (left aligned)
237  *  to 1.0 (right aligned)
238  * @ratio: The desired aspect ratio.
239  * @obey_child: If %TRUE, @ratio is ignored, and the aspect
240  *  ratio is taken from the requistion of the child.
241  *
242  * Create a new #GtkAspectFrame.
243  *
244  * Returns: the new #GtkAspectFrame.
245  */
246 GtkWidget*
247 gtk_aspect_frame_new (const gchar *label,
248                       gfloat       xalign,
249                       gfloat       yalign,
250                       gfloat       ratio,
251                       gboolean     obey_child)
252 {
253   GtkAspectFrame *aspect_frame;
254   GtkAspectFramePrivate *priv;
255
256   aspect_frame = g_object_new (GTK_TYPE_ASPECT_FRAME, NULL);
257
258   priv = aspect_frame->priv;
259
260   priv->xalign = CLAMP (xalign, 0.0, 1.0);
261   priv->yalign = CLAMP (yalign, 0.0, 1.0);
262   priv->ratio = CLAMP (ratio, MIN_RATIO, MAX_RATIO);
263   priv->obey_child = obey_child != FALSE;
264
265   gtk_frame_set_label (GTK_FRAME(aspect_frame), label);
266
267   return GTK_WIDGET (aspect_frame);
268 }
269
270 /**
271  * gtk_aspect_frame_set:
272  * @aspect_frame: a #GtkAspectFrame
273  * @xalign: Horizontal alignment of the child within the allocation of
274  *  the #GtkAspectFrame. This ranges from 0.0 (left aligned)
275  *  to 1.0 (right aligned)
276  * @yalign: Vertical alignment of the child within the allocation of
277  *  the #GtkAspectFrame. This ranges from 0.0 (left aligned)
278  *  to 1.0 (right aligned)
279  * @ratio: The desired aspect ratio.
280  * @obey_child: If %TRUE, @ratio is ignored, and the aspect
281  *  ratio is taken from the requistion of the child.
282  *
283  * Set parameters for an existing #GtkAspectFrame.
284  */
285 void
286 gtk_aspect_frame_set (GtkAspectFrame *aspect_frame,
287                       gfloat          xalign,
288                       gfloat          yalign,
289                       gfloat          ratio,
290                       gboolean        obey_child)
291 {
292   GtkAspectFramePrivate *priv;
293
294   g_return_if_fail (GTK_IS_ASPECT_FRAME (aspect_frame));
295
296   priv = aspect_frame->priv;
297   
298   xalign = CLAMP (xalign, 0.0, 1.0);
299   yalign = CLAMP (yalign, 0.0, 1.0);
300   ratio = CLAMP (ratio, MIN_RATIO, MAX_RATIO);
301   obey_child = obey_child != FALSE;
302   
303   if (priv->xalign != xalign
304       || priv->yalign != yalign
305       || priv->ratio != ratio
306       || priv->obey_child != obey_child)
307     {
308       g_object_freeze_notify (G_OBJECT (aspect_frame));
309
310       if (priv->xalign != xalign)
311         {
312           priv->xalign = xalign;
313           g_object_notify (G_OBJECT (aspect_frame), "xalign");
314         }
315       if (priv->yalign != yalign)
316         {
317           priv->yalign = yalign;
318           g_object_notify (G_OBJECT (aspect_frame), "yalign");
319         }
320       if (priv->ratio != ratio)
321         {
322           priv->ratio = ratio;
323           g_object_notify (G_OBJECT (aspect_frame), "ratio");
324         }
325       if (priv->obey_child != obey_child)
326         {
327           priv->obey_child = obey_child;
328           g_object_notify (G_OBJECT (aspect_frame), "obey-child");
329         }
330       g_object_thaw_notify (G_OBJECT (aspect_frame));
331
332       gtk_widget_queue_resize (GTK_WIDGET (aspect_frame));
333     }
334 }
335
336 static void
337 gtk_aspect_frame_compute_child_allocation (GtkFrame      *frame,
338                                            GtkAllocation *child_allocation)
339 {
340   GtkAspectFrame *aspect_frame = GTK_ASPECT_FRAME (frame);
341   GtkAspectFramePrivate *priv = aspect_frame->priv;
342   GtkBin *bin = GTK_BIN (frame);
343   GtkWidget *child;
344   gdouble ratio;
345
346   child = gtk_bin_get_child (bin);
347   if (child && gtk_widget_get_visible (child))
348     {
349       GtkAllocation full_allocation;
350       
351       if (priv->obey_child)
352         {
353           GtkRequisition child_requisition;
354
355           gtk_size_request_get_size (GTK_SIZE_REQUEST (child),
356                                      &child_requisition, NULL);
357           if (child_requisition.height != 0)
358             {
359               ratio = ((gdouble) child_requisition.width /
360                        child_requisition.height);
361               if (ratio < MIN_RATIO)
362                 ratio = MIN_RATIO;
363             }
364           else if (child_requisition.width != 0)
365             ratio = MAX_RATIO;
366           else
367             ratio = 1.0;
368         }
369       else
370         ratio = priv->ratio;
371
372       GTK_FRAME_CLASS (gtk_aspect_frame_parent_class)->compute_child_allocation (frame, &full_allocation);
373       
374       if (ratio * full_allocation.height > full_allocation.width)
375         {
376           child_allocation->width = full_allocation.width;
377           child_allocation->height = full_allocation.width / ratio + 0.5;
378         }
379       else
380         {
381           child_allocation->width = ratio * full_allocation.height + 0.5;
382           child_allocation->height = full_allocation.height;
383         }
384       
385       child_allocation->x = full_allocation.x + priv->xalign * (full_allocation.width - child_allocation->width);
386       child_allocation->y = full_allocation.y + priv->yalign * (full_allocation.height - child_allocation->height);
387     }
388   else
389     GTK_FRAME_CLASS (gtk_aspect_frame_parent_class)->compute_child_allocation (frame, child_allocation);
390 }