]> Pileus Git - ~andy/gtk/blob - gtk/gtkaspectframe.c
Mon Nov 24 1997 Jay Painter <jpaint@serv.net>
[~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 Free
21  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22  */
23 #include "gtkaspectframe.h"
24
25 static void gtk_aspect_frame_class_init    (GtkAspectFrameClass *klass);
26 static void gtk_aspect_frame_init          (GtkAspectFrame      *aspect_frame);
27 static void gtk_aspect_frame_draw          (GtkWidget      *widget,
28                                             GdkRectangle   *area);
29 static void gtk_aspect_frame_paint         (GtkWidget      *widget,
30                                             GdkRectangle   *area);
31 static gint gtk_aspect_frame_expose        (GtkWidget      *widget,
32                                             GdkEventExpose *event);
33 static void gtk_aspect_frame_size_allocate (GtkWidget         *widget,
34                                             GtkAllocation     *allocation);
35
36 #define MAX_RATIO 10000.0
37 #define MIN_RATIO 0.0001
38
39 guint
40 gtk_aspect_frame_get_type ()
41 {
42   static guint aspect_frame_type = 0;
43
44   if (!aspect_frame_type)
45     {
46       GtkTypeInfo aspect_frame_info =
47       {
48         "GtkAspectFrame",
49         sizeof (GtkAspectFrame),
50         sizeof (GtkAspectFrameClass),
51         (GtkClassInitFunc) gtk_aspect_frame_class_init,
52         (GtkObjectInitFunc) gtk_aspect_frame_init,
53         (GtkArgFunc) NULL,
54       };
55
56       aspect_frame_type = gtk_type_unique (gtk_frame_get_type (), &aspect_frame_info);
57     }
58
59   return aspect_frame_type;
60 }
61
62 static void
63 gtk_aspect_frame_class_init (GtkAspectFrameClass *class)
64 {
65   GtkWidgetClass *widget_class;
66
67   widget_class = (GtkWidgetClass*) class;
68
69   widget_class->draw = gtk_aspect_frame_draw;
70   widget_class->expose_event = gtk_aspect_frame_expose;
71   widget_class->size_allocate = gtk_aspect_frame_size_allocate;
72 }
73
74 static void
75 gtk_aspect_frame_init (GtkAspectFrame *aspect_frame)
76 {
77   aspect_frame->xalign = 0.5;
78   aspect_frame->yalign = 0.5;
79   aspect_frame->ratio = 1.0;
80   aspect_frame->obey_child = 1;
81   aspect_frame->center_allocation.x = -1;
82   aspect_frame->center_allocation.y = -1;
83   aspect_frame->center_allocation.width = 1;
84   aspect_frame->center_allocation.height = 1;
85 }
86
87 GtkWidget*
88 gtk_aspect_frame_new (const gchar *label,
89                       gfloat xalign,
90                       gfloat yalign,
91                       gfloat ratio,
92                       gint   obey_child)
93 {
94   GtkAspectFrame *aspect_frame;
95
96   aspect_frame = gtk_type_new (gtk_aspect_frame_get_type ());
97
98   aspect_frame->xalign = CLAMP (xalign, 0.0, 1.0);
99   aspect_frame->yalign = CLAMP (yalign, 0.0, 1.0);
100   aspect_frame->ratio = CLAMP (ratio, MIN_RATIO, MAX_RATIO);
101   aspect_frame->obey_child = obey_child;
102
103   gtk_frame_set_label (GTK_FRAME(aspect_frame), label);
104
105   return GTK_WIDGET (aspect_frame);
106 }
107
108 void
109 gtk_aspect_frame_set (GtkAspectFrame *aspect_frame,
110                       gfloat        xalign,
111                       gfloat        yalign,
112                       gfloat        ratio,
113                       gint          obey_child)
114 {
115   g_return_if_fail (aspect_frame != NULL);
116   g_return_if_fail (GTK_IS_ASPECT_FRAME (aspect_frame));
117
118   xalign = CLAMP (xalign, 0.0, 1.0);
119   yalign = CLAMP (yalign, 0.0, 1.0);
120   ratio = CLAMP (ratio, MIN_RATIO, MAX_RATIO);
121
122   if ((aspect_frame->xalign != xalign) ||
123       (aspect_frame->yalign != yalign) ||
124       (aspect_frame->ratio != ratio) ||
125       (aspect_frame->obey_child != obey_child))
126     {
127       GtkWidget * this = GTK_WIDGET(aspect_frame);
128   
129       aspect_frame->xalign = xalign;
130       aspect_frame->yalign = yalign;
131       aspect_frame->ratio = ratio;
132       aspect_frame->obey_child = obey_child;
133
134       if (GTK_WIDGET_MAPPED(this))
135           gdk_window_clear_area (this->window,
136                                  this->allocation.x,
137                                  this->allocation.y,
138                                  this->allocation.width,
139                                  this->allocation.height);
140
141       gtk_widget_size_allocate (this, &this->allocation);
142       gtk_widget_queue_draw (this);
143     }
144 }
145
146 static void
147 gtk_aspect_frame_paint (GtkWidget    *widget,
148                         GdkRectangle *area)
149 {
150   GtkFrame *frame;
151   GtkStateType state;
152   gint height_extra;
153   gint label_area_width;
154   gint x, y;
155   GtkAllocation *allocation;
156
157   g_return_if_fail (widget != NULL);
158   g_return_if_fail (GTK_IS_ASPECT_FRAME (widget));
159   g_return_if_fail (area != NULL);
160
161   if (GTK_WIDGET_DRAWABLE (widget))
162     {
163       frame = GTK_FRAME (widget);
164       allocation = &GTK_ASPECT_FRAME(widget)->center_allocation;
165
166       state = widget->state;
167       if (!GTK_WIDGET_IS_SENSITIVE (widget))
168         state = GTK_STATE_INSENSITIVE;
169
170       height_extra = frame->label_height - widget->style->klass->xthickness;
171       height_extra = MAX (height_extra, 0);
172
173       x = GTK_CONTAINER (frame)->border_width;
174       y = GTK_CONTAINER (frame)->border_width;
175
176       gtk_draw_shadow (widget->style, widget->window,
177                        GTK_STATE_NORMAL, frame->shadow_type,
178                        allocation->x + x,
179                        allocation->y + y + height_extra / 2,
180                        allocation->width - x * 2,
181                        allocation->height - y * 2 - height_extra / 2);
182
183       if (frame->label)
184         {
185           label_area_width = (allocation->width +
186                               GTK_CONTAINER (frame)->border_width * 2 -
187                               widget->style->klass->xthickness * 2);
188
189           x = ((label_area_width - frame->label_width) * frame->label_xalign +
190                GTK_CONTAINER (frame)->border_width + widget->style->klass->xthickness);
191           y = (GTK_CONTAINER (frame)->border_width + widget->style->font->ascent);
192
193           gdk_window_clear_area (widget->window,
194                                  allocation->x + x + 2,
195                                  allocation->y + GTK_CONTAINER (frame)->border_width,
196                                  frame->label_width - 4, frame->label_height);
197           gtk_draw_string (widget->style, widget->window, state,
198                            allocation->x + x + 3,
199                            allocation->y + y,
200                            frame->label);
201         }
202     }
203 }
204
205 /* the only modification to the next two routines is to call
206    gtk_aspect_frame_paint instead of gtk_frame_paint */
207
208 static void
209 gtk_aspect_frame_draw (GtkWidget    *widget,
210                        GdkRectangle *area)
211 {
212   GtkBin *bin;
213   GdkRectangle child_area;
214
215   g_return_if_fail (widget != NULL);
216   g_return_if_fail (GTK_IS_ASPECT_FRAME (widget));
217   g_return_if_fail (area != NULL);
218
219   if (GTK_WIDGET_DRAWABLE (widget))
220     {
221       bin = GTK_BIN (widget);
222
223       gtk_aspect_frame_paint (widget, area);
224
225       if (bin->child && gtk_widget_intersect (bin->child, area, &child_area))
226         gtk_widget_draw (bin->child, &child_area);
227     }
228 }
229
230 static gint
231 gtk_aspect_frame_expose (GtkWidget      *widget,
232                          GdkEventExpose *event)
233 {
234   GtkBin *bin;
235   GdkEventExpose child_event;
236
237   g_return_val_if_fail (widget != NULL, FALSE);
238   g_return_val_if_fail (GTK_IS_ASPECT_FRAME (widget), FALSE);
239   g_return_val_if_fail (event != NULL, FALSE);
240
241   if (GTK_WIDGET_DRAWABLE (widget))
242     {
243       bin = GTK_BIN (widget);
244
245       gtk_aspect_frame_paint (widget, &event->area);
246
247       child_event = *event;
248       if (bin->child &&
249           GTK_WIDGET_NO_WINDOW (bin->child) &&
250           gtk_widget_intersect (bin->child, &event->area, &child_event.area))
251         gtk_widget_event (bin->child, (GdkEvent*) &child_event);
252     }
253
254   return FALSE;
255 }
256
257 static void
258 gtk_aspect_frame_size_allocate (GtkWidget     *widget,
259                           GtkAllocation *allocation)
260 {
261   GtkFrame *frame;
262   GtkAspectFrame *aspect_frame;
263   GtkBin *bin;
264
265   GtkAllocation child_allocation;
266   gint x,y;
267   gint width,height;
268   gdouble ratio;
269
270   g_return_if_fail (widget != NULL);
271   g_return_if_fail (GTK_IS_ASPECT_FRAME (widget));
272   g_return_if_fail (allocation != NULL);
273
274   aspect_frame = GTK_ASPECT_FRAME (widget);
275   frame = GTK_FRAME (widget);
276   bin = GTK_BIN (widget);
277
278   if (GTK_WIDGET_MAPPED (widget) &&
279       ((widget->allocation.x != allocation->x) ||
280        (widget->allocation.y != allocation->y) ||
281        (widget->allocation.width != allocation->width) ||
282        (widget->allocation.height != allocation->height)) &&
283       (widget->allocation.width != 0) &&
284       (widget->allocation.height != 0))
285     gdk_window_clear_area (widget->window,
286                            widget->allocation.x,
287                            widget->allocation.y,
288                            widget->allocation.width,
289                            widget->allocation.height);
290
291   widget->allocation = *allocation;
292
293   if (bin->child && GTK_WIDGET_VISIBLE (bin->child))
294     {
295       if (aspect_frame->obey_child)
296         {
297           if (bin->child->requisition.height != 0)
298             {
299               ratio = (gdouble)bin->child->requisition.width /
300                 bin->child->requisition.height;
301               if (ratio < MIN_RATIO) ratio = MIN_RATIO;
302             }
303           else
304             if (bin->child->requisition.height != 0)
305               ratio = MAX_RATIO;
306           else
307             ratio = 1.0;
308         }
309       else
310         ratio = aspect_frame->ratio;
311
312       x = (GTK_CONTAINER (frame)->border_width +
313            GTK_WIDGET (frame)->style->klass->xthickness);
314       width = allocation->width - x * 2;
315
316       y = (GTK_CONTAINER (frame)->border_width +
317                             MAX (frame->label_height, GTK_WIDGET (frame)->style->klass->ythickness));
318       height = (allocation->height - y -
319                                  GTK_CONTAINER (frame)->border_width -
320                                  GTK_WIDGET (frame)->style->klass->ythickness);
321
322       if (height > width / ratio)
323         {
324           child_allocation.width = width;
325           child_allocation.height = width/ratio + 0.5;
326         }
327       else if (width > height * ratio)
328         {
329           child_allocation.width = ratio * height + 0.5;
330           child_allocation.height = height;
331         }
332
333       child_allocation.x = aspect_frame->xalign * (width - child_allocation.width) + allocation->x + x;
334       child_allocation.y = aspect_frame->yalign * (height - child_allocation.height) + allocation->y + y;
335
336       aspect_frame->center_allocation.width = child_allocation.width + 2*x;
337       aspect_frame->center_allocation.x = child_allocation.x - x;
338       aspect_frame->center_allocation.height = child_allocation.height + y +
339                                  GTK_CONTAINER (frame)->border_width +
340                                  GTK_WIDGET (frame)->style->klass->ythickness;
341       aspect_frame->center_allocation.y = child_allocation.y - y;
342
343       gtk_widget_size_allocate (bin->child, &child_allocation);
344     }
345 }