]> Pileus Git - ~andy/gtk/blob - gtk/gtkaspectframe.c
Fix typo: Italian uses ISO-8859-1, not -2. Add en_GB.
[~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 Library 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  * Library General Public License for more details.
18  *
19  * You should have received a copy of the GNU Library 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-1999.  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 #include "gtkaspectframe.h"
33
34 enum {
35   ARG_0,
36   ARG_XALIGN,
37   ARG_YALIGN,
38   ARG_RATIO,
39   ARG_OBEY_CHILD
40 };
41
42 static void gtk_aspect_frame_class_init    (GtkAspectFrameClass *klass);
43 static void gtk_aspect_frame_init          (GtkAspectFrame *aspect_frame);
44 static void gtk_aspect_frame_set_arg       (GtkObject      *object,
45                                             GtkArg         *arg,
46                                             guint           arg_id);
47 static void gtk_aspect_frame_get_arg       (GtkObject      *object,
48                                             GtkArg         *arg,
49                                             guint           arg_id);
50 static void gtk_aspect_frame_draw          (GtkWidget      *widget,
51                                             GdkRectangle   *area);
52 static void gtk_aspect_frame_paint         (GtkWidget      *widget,
53                                             GdkRectangle   *area);
54 static gint gtk_aspect_frame_expose        (GtkWidget      *widget,
55                                             GdkEventExpose *event);
56 static void gtk_aspect_frame_size_allocate (GtkWidget         *widget,
57                                             GtkAllocation     *allocation);
58
59 #define MAX_RATIO 10000.0
60 #define MIN_RATIO 0.0001
61
62 GtkType
63 gtk_aspect_frame_get_type (void)
64 {
65   static GtkType aspect_frame_type = 0;
66   
67   if (!aspect_frame_type)
68     {
69       static const GtkTypeInfo aspect_frame_info =
70       {
71         "GtkAspectFrame",
72         sizeof (GtkAspectFrame),
73         sizeof (GtkAspectFrameClass),
74         (GtkClassInitFunc) gtk_aspect_frame_class_init,
75         (GtkObjectInitFunc) gtk_aspect_frame_init,
76         /* reserved_1 */ NULL,
77         /* reserved_2 */ NULL,
78         (GtkClassInitFunc) NULL,
79       };
80       
81       aspect_frame_type = gtk_type_unique (GTK_TYPE_FRAME, &aspect_frame_info);
82     }
83   
84   return aspect_frame_type;
85 }
86
87 static void
88 gtk_aspect_frame_class_init (GtkAspectFrameClass *class)
89 {
90   GtkObjectClass *object_class;
91   GtkWidgetClass *widget_class;
92   
93   object_class = GTK_OBJECT_CLASS (class);
94   widget_class = GTK_WIDGET_CLASS (class);
95   
96   object_class->set_arg = gtk_aspect_frame_set_arg;
97   object_class->get_arg = gtk_aspect_frame_get_arg;
98
99   widget_class->draw = gtk_aspect_frame_draw;
100   widget_class->expose_event = gtk_aspect_frame_expose;
101   widget_class->size_allocate = gtk_aspect_frame_size_allocate;
102
103   gtk_object_add_arg_type ("GtkAspectFrame::xalign", GTK_TYPE_FLOAT,
104                            GTK_ARG_READWRITE, ARG_XALIGN);
105   gtk_object_add_arg_type ("GtkAspectFrame::yalign", GTK_TYPE_FLOAT,
106                            GTK_ARG_READWRITE, ARG_YALIGN);
107   gtk_object_add_arg_type ("GtkAspectFrame::ratio", GTK_TYPE_FLOAT,
108                            GTK_ARG_READWRITE, ARG_RATIO);
109   gtk_object_add_arg_type ("GtkAspectFrame::obey_child", GTK_TYPE_BOOL,
110                            GTK_ARG_READWRITE, ARG_OBEY_CHILD);  
111 }
112
113 static void
114 gtk_aspect_frame_init (GtkAspectFrame *aspect_frame)
115 {
116   aspect_frame->xalign = 0.5;
117   aspect_frame->yalign = 0.5;
118   aspect_frame->ratio = 1.0;
119   aspect_frame->obey_child = TRUE;
120   aspect_frame->center_allocation.x = -1;
121   aspect_frame->center_allocation.y = -1;
122   aspect_frame->center_allocation.width = 1;
123   aspect_frame->center_allocation.height = 1;
124 }
125
126 static void
127 gtk_aspect_frame_set_arg (GtkObject *object,
128                           GtkArg    *arg,
129                           guint      arg_id)
130 {
131   GtkAspectFrame *aspect_frame = GTK_ASPECT_FRAME (object);
132   
133   switch (arg_id)
134     {
135     case ARG_XALIGN:
136       gtk_aspect_frame_set (aspect_frame,
137                             GTK_VALUE_FLOAT (*arg),
138                             aspect_frame->yalign,
139                             aspect_frame->ratio,
140                             aspect_frame->obey_child);
141       break;
142     case ARG_YALIGN:
143       gtk_aspect_frame_set (aspect_frame,
144                             aspect_frame->xalign,
145                             GTK_VALUE_FLOAT (*arg),
146                             aspect_frame->ratio,
147                             aspect_frame->obey_child);
148       break;
149     case ARG_RATIO:
150       gtk_aspect_frame_set (aspect_frame,
151                             aspect_frame->xalign,
152                             aspect_frame->yalign,
153                             GTK_VALUE_FLOAT (*arg),
154                             aspect_frame->obey_child);
155       break;
156     case ARG_OBEY_CHILD:
157       gtk_aspect_frame_set (aspect_frame,
158                             aspect_frame->xalign,
159                             aspect_frame->yalign,
160                             aspect_frame->ratio,
161                             GTK_VALUE_BOOL (*arg));
162       break;
163     }
164 }
165
166 static void
167 gtk_aspect_frame_get_arg (GtkObject *object,
168                           GtkArg    *arg,
169                           guint      arg_id)
170 {
171   GtkAspectFrame *aspect_frame = GTK_ASPECT_FRAME (object);
172   
173   switch (arg_id)
174     {
175     case ARG_XALIGN:
176       GTK_VALUE_FLOAT (*arg) = aspect_frame->xalign;
177       break;
178     case ARG_YALIGN:
179       GTK_VALUE_FLOAT (*arg) = aspect_frame->yalign;
180       break;
181     case ARG_RATIO:
182       GTK_VALUE_FLOAT (*arg) = aspect_frame->ratio;
183       break;
184     case ARG_OBEY_CHILD:
185       GTK_VALUE_BOOL (*arg) = aspect_frame->obey_child;
186       break;
187     default:
188       arg->type = GTK_TYPE_INVALID;
189       break;
190     }
191 }
192
193 GtkWidget*
194 gtk_aspect_frame_new (const gchar *label,
195                       gfloat       xalign,
196                       gfloat       yalign,
197                       gfloat       ratio,
198                       gboolean     obey_child)
199 {
200   GtkAspectFrame *aspect_frame;
201
202   aspect_frame = gtk_type_new (gtk_aspect_frame_get_type ());
203
204   aspect_frame->xalign = CLAMP (xalign, 0.0, 1.0);
205   aspect_frame->yalign = CLAMP (yalign, 0.0, 1.0);
206   aspect_frame->ratio = CLAMP (ratio, MIN_RATIO, MAX_RATIO);
207   aspect_frame->obey_child = obey_child != FALSE;
208
209   gtk_frame_set_label (GTK_FRAME(aspect_frame), label);
210
211   return GTK_WIDGET (aspect_frame);
212 }
213
214 void
215 gtk_aspect_frame_set (GtkAspectFrame *aspect_frame,
216                       gfloat          xalign,
217                       gfloat          yalign,
218                       gfloat          ratio,
219                       gboolean        obey_child)
220 {
221   g_return_if_fail (aspect_frame != NULL);
222   g_return_if_fail (GTK_IS_ASPECT_FRAME (aspect_frame));
223   
224   xalign = CLAMP (xalign, 0.0, 1.0);
225   yalign = CLAMP (yalign, 0.0, 1.0);
226   ratio = CLAMP (ratio, MIN_RATIO, MAX_RATIO);
227   obey_child = obey_child != FALSE;
228   
229   if ((aspect_frame->xalign != xalign) ||
230       (aspect_frame->yalign != yalign) ||
231       (aspect_frame->ratio != ratio) ||
232       (aspect_frame->obey_child != obey_child))
233     {
234       GtkWidget *widget = GTK_WIDGET(aspect_frame);
235       
236       aspect_frame->xalign = xalign;
237       aspect_frame->yalign = yalign;
238       aspect_frame->ratio = ratio;
239       aspect_frame->obey_child = obey_child;
240       
241       if (GTK_WIDGET_DRAWABLE(widget))
242         gtk_widget_queue_clear (widget);
243       
244       gtk_widget_queue_resize (widget);
245     }
246 }
247
248 static void
249 gtk_aspect_frame_paint (GtkWidget    *widget,
250                         GdkRectangle *area)
251 {
252   GtkFrame *frame;
253   gint height_extra;
254   gint label_area_width;
255   gint x, y, x2, y2;
256   GtkAllocation *allocation;
257
258   g_return_if_fail (widget != NULL);
259   g_return_if_fail (GTK_IS_ASPECT_FRAME (widget));
260   g_return_if_fail (area != NULL);
261
262   if (GTK_WIDGET_DRAWABLE (widget))
263     {
264       frame = GTK_FRAME (widget);
265       allocation = &GTK_ASPECT_FRAME(widget)->center_allocation;
266
267       height_extra = frame->label_height - widget->style->klass->xthickness;
268       height_extra = MAX (height_extra, 0);
269
270       x = GTK_CONTAINER (frame)->border_width;
271       y = GTK_CONTAINER (frame)->border_width;
272
273       if (frame->label)
274         {
275           label_area_width = (allocation->width +
276                               GTK_CONTAINER (frame)->border_width * 2 -
277                               widget->style->klass->xthickness * 2);
278
279           x2 = ((label_area_width - frame->label_width) * frame->label_xalign +
280                 GTK_CONTAINER (frame)->border_width + widget->style->klass->xthickness);
281           y2 = (GTK_CONTAINER (frame)->border_width + widget->style->font->ascent);
282           
283           gtk_paint_shadow_gap (widget->style, widget->window,
284                                 GTK_STATE_NORMAL, frame->shadow_type,
285                                 area, widget, "frame",
286                                 allocation->x + x,
287                                 allocation->y + y + height_extra / 2,
288                                 allocation->width - x * 2,
289                                 allocation->height - y * 2 - height_extra / 2,
290                                 GTK_POS_TOP, 
291                                 x2 + 2 - x, frame->label_width - 4);
292           
293           gtk_paint_string (widget->style, widget->window, GTK_WIDGET_STATE (widget),
294                             area, widget, "frame",
295                             allocation->x + x2 + 3,
296                             allocation->y + y2,
297                             frame->label);
298         }
299       else
300         gtk_paint_shadow (widget->style, widget->window,
301                           GTK_STATE_NORMAL, frame->shadow_type,
302                           area, widget, "frame",
303                           allocation->x + x,
304                           allocation->y + y + height_extra / 2,
305                           allocation->width - x * 2,
306                           allocation->height - y * 2 - height_extra / 2);
307     }
308 }
309
310 /* the only modification to the next two routines is to call
311    gtk_aspect_frame_paint instead of gtk_frame_paint */
312
313 static void
314 gtk_aspect_frame_draw (GtkWidget    *widget,
315                        GdkRectangle *area)
316 {
317   GtkBin *bin;
318   GdkRectangle child_area;
319
320   g_return_if_fail (widget != NULL);
321   g_return_if_fail (GTK_IS_ASPECT_FRAME (widget));
322   g_return_if_fail (area != NULL);
323
324   if (GTK_WIDGET_DRAWABLE (widget))
325     {
326       bin = GTK_BIN (widget);
327
328       gtk_aspect_frame_paint (widget, area);
329
330       if (bin->child && gtk_widget_intersect (bin->child, area, &child_area))
331         gtk_widget_draw (bin->child, &child_area);
332     }
333 }
334
335 static gint
336 gtk_aspect_frame_expose (GtkWidget      *widget,
337                          GdkEventExpose *event)
338 {
339   GtkBin *bin;
340   GdkEventExpose child_event;
341
342   g_return_val_if_fail (widget != NULL, FALSE);
343   g_return_val_if_fail (GTK_IS_ASPECT_FRAME (widget), FALSE);
344   g_return_val_if_fail (event != NULL, FALSE);
345
346   if (GTK_WIDGET_DRAWABLE (widget))
347     {
348       bin = GTK_BIN (widget);
349
350       gtk_aspect_frame_paint (widget, &event->area);
351
352       child_event = *event;
353       if (bin->child &&
354           GTK_WIDGET_NO_WINDOW (bin->child) &&
355           gtk_widget_intersect (bin->child, &event->area, &child_event.area))
356         gtk_widget_event (bin->child, (GdkEvent*) &child_event);
357     }
358
359   return FALSE;
360 }
361
362 static void
363 gtk_aspect_frame_size_allocate (GtkWidget     *widget,
364                           GtkAllocation *allocation)
365 {
366   GtkFrame *frame;
367   GtkAspectFrame *aspect_frame;
368   GtkBin *bin;
369
370   GtkAllocation child_allocation;
371   gint x,y;
372   gint width,height;
373   gdouble ratio;
374
375   g_return_if_fail (widget != NULL);
376   g_return_if_fail (GTK_IS_ASPECT_FRAME (widget));
377   g_return_if_fail (allocation != NULL);
378
379   aspect_frame = GTK_ASPECT_FRAME (widget);
380   frame = GTK_FRAME (widget);
381   bin = GTK_BIN (widget);
382
383   if (GTK_WIDGET_DRAWABLE (widget) &&
384       ((widget->allocation.x != allocation->x) ||
385        (widget->allocation.y != allocation->y) ||
386        (widget->allocation.width != allocation->width) ||
387        (widget->allocation.height != allocation->height)) &&
388       (widget->allocation.width != 0) &&
389       (widget->allocation.height != 0))
390     gdk_window_clear_area (widget->window,
391                            widget->allocation.x,
392                            widget->allocation.y,
393                            widget->allocation.width,
394                            widget->allocation.height);
395
396   widget->allocation = *allocation;
397
398   if (bin->child && GTK_WIDGET_VISIBLE (bin->child))
399     {
400       if (aspect_frame->obey_child)
401         {
402           GtkRequisition child_requisition;
403
404           gtk_widget_get_child_requisition (bin->child, &child_requisition);
405           if (child_requisition.height != 0)
406             {
407               ratio = ((gdouble) child_requisition.width /
408                        child_requisition.height);
409               if (ratio < MIN_RATIO)
410                 ratio = MIN_RATIO;
411             }
412           else if (child_requisition.width != 0)
413             ratio = MAX_RATIO;
414           else
415             ratio = 1.0;
416         }
417       else
418         ratio = aspect_frame->ratio;
419       
420       x = (GTK_CONTAINER (frame)->border_width +
421            GTK_WIDGET (frame)->style->klass->xthickness);
422       width = allocation->width - x * 2;
423       
424       y = (GTK_CONTAINER (frame)->border_width +
425            MAX (frame->label_height, GTK_WIDGET (frame)->style->klass->ythickness));
426       height = (allocation->height - y -
427                 GTK_CONTAINER (frame)->border_width -
428                 GTK_WIDGET (frame)->style->klass->ythickness);
429       
430       /* make sure we don't allocate a negative width or height,
431        * since that will be cast to a (very big) guint16 */
432       width = MAX (1, width);
433       height = MAX (1, height);
434       
435       if (ratio * height > width)
436         {
437           child_allocation.width = width;
438           child_allocation.height = width/ratio + 0.5;
439         }
440       else
441         {
442           child_allocation.width = ratio*height + 0.5;
443           child_allocation.height = height;
444         }
445       
446       child_allocation.x = aspect_frame->xalign * (width - child_allocation.width) + allocation->x + x;
447       child_allocation.y = aspect_frame->yalign * (height - child_allocation.height) + allocation->y + y;
448
449       aspect_frame->center_allocation.width = child_allocation.width + 2*x;
450       aspect_frame->center_allocation.x = child_allocation.x - x;
451       aspect_frame->center_allocation.height = child_allocation.height + y +
452                                  GTK_CONTAINER (frame)->border_width +
453                                  GTK_WIDGET (frame)->style->klass->ythickness;
454       aspect_frame->center_allocation.y = child_allocation.y - y;
455
456       gtk_widget_size_allocate (bin->child, &child_allocation);
457     }
458 }