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