1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
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
7 * Copyright Owen Taylor 4/9/97
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.
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.
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.
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/.
32 #include "gtkaspectframe.h"
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,
47 static void gtk_aspect_frame_get_arg (GtkObject *object,
50 static void gtk_aspect_frame_draw (GtkWidget *widget,
52 static void gtk_aspect_frame_paint (GtkWidget *widget,
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);
59 #define MAX_RATIO 10000.0
60 #define MIN_RATIO 0.0001
63 gtk_aspect_frame_get_type (void)
65 static GtkType aspect_frame_type = 0;
67 if (!aspect_frame_type)
69 static const GtkTypeInfo aspect_frame_info =
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,
81 aspect_frame_type = gtk_type_unique (GTK_TYPE_FRAME, &aspect_frame_info);
84 return aspect_frame_type;
88 gtk_aspect_frame_class_init (GtkAspectFrameClass *class)
90 GtkObjectClass *object_class;
91 GtkWidgetClass *widget_class;
93 object_class = GTK_OBJECT_CLASS (class);
94 widget_class = GTK_WIDGET_CLASS (class);
96 object_class->set_arg = gtk_aspect_frame_set_arg;
97 object_class->get_arg = gtk_aspect_frame_get_arg;
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;
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);
114 gtk_aspect_frame_init (GtkAspectFrame *aspect_frame)
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;
127 gtk_aspect_frame_set_arg (GtkObject *object,
131 GtkAspectFrame *aspect_frame = GTK_ASPECT_FRAME (object);
136 gtk_aspect_frame_set (aspect_frame,
137 GTK_VALUE_FLOAT (*arg),
138 aspect_frame->yalign,
140 aspect_frame->obey_child);
143 gtk_aspect_frame_set (aspect_frame,
144 aspect_frame->xalign,
145 GTK_VALUE_FLOAT (*arg),
147 aspect_frame->obey_child);
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);
157 gtk_aspect_frame_set (aspect_frame,
158 aspect_frame->xalign,
159 aspect_frame->yalign,
161 GTK_VALUE_BOOL (*arg));
167 gtk_aspect_frame_get_arg (GtkObject *object,
171 GtkAspectFrame *aspect_frame = GTK_ASPECT_FRAME (object);
176 GTK_VALUE_FLOAT (*arg) = aspect_frame->xalign;
179 GTK_VALUE_FLOAT (*arg) = aspect_frame->yalign;
182 GTK_VALUE_FLOAT (*arg) = aspect_frame->ratio;
185 GTK_VALUE_BOOL (*arg) = aspect_frame->obey_child;
188 arg->type = GTK_TYPE_INVALID;
194 gtk_aspect_frame_new (const gchar *label,
200 GtkAspectFrame *aspect_frame;
202 aspect_frame = gtk_type_new (gtk_aspect_frame_get_type ());
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;
209 gtk_frame_set_label (GTK_FRAME(aspect_frame), label);
211 return GTK_WIDGET (aspect_frame);
215 gtk_aspect_frame_set (GtkAspectFrame *aspect_frame,
221 g_return_if_fail (aspect_frame != NULL);
222 g_return_if_fail (GTK_IS_ASPECT_FRAME (aspect_frame));
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;
229 if ((aspect_frame->xalign != xalign) ||
230 (aspect_frame->yalign != yalign) ||
231 (aspect_frame->ratio != ratio) ||
232 (aspect_frame->obey_child != obey_child))
234 GtkWidget *widget = GTK_WIDGET(aspect_frame);
236 aspect_frame->xalign = xalign;
237 aspect_frame->yalign = yalign;
238 aspect_frame->ratio = ratio;
239 aspect_frame->obey_child = obey_child;
241 if (GTK_WIDGET_DRAWABLE(widget))
242 gtk_widget_queue_clear (widget);
244 gtk_widget_queue_resize (widget);
249 gtk_aspect_frame_paint (GtkWidget *widget,
254 gint label_area_width;
256 GtkAllocation *allocation;
258 g_return_if_fail (widget != NULL);
259 g_return_if_fail (GTK_IS_ASPECT_FRAME (widget));
260 g_return_if_fail (area != NULL);
262 if (GTK_WIDGET_DRAWABLE (widget))
264 frame = GTK_FRAME (widget);
265 allocation = >K_ASPECT_FRAME(widget)->center_allocation;
267 height_extra = frame->label_height - widget->style->klass->xthickness;
268 height_extra = MAX (height_extra, 0);
270 x = GTK_CONTAINER (frame)->border_width;
271 y = GTK_CONTAINER (frame)->border_width;
275 label_area_width = (allocation->width +
276 GTK_CONTAINER (frame)->border_width * 2 -
277 widget->style->klass->xthickness * 2);
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);
283 gtk_paint_shadow_gap (widget->style, widget->window,
284 GTK_STATE_NORMAL, frame->shadow_type,
285 area, widget, "frame",
287 allocation->y + y + height_extra / 2,
288 allocation->width - x * 2,
289 allocation->height - y * 2 - height_extra / 2,
291 x2 + 2 - x, frame->label_width - 4);
293 gtk_paint_string (widget->style, widget->window, GTK_WIDGET_STATE (widget),
294 area, widget, "frame",
295 allocation->x + x2 + 3,
300 gtk_paint_shadow (widget->style, widget->window,
301 GTK_STATE_NORMAL, frame->shadow_type,
302 area, widget, "frame",
304 allocation->y + y + height_extra / 2,
305 allocation->width - x * 2,
306 allocation->height - y * 2 - height_extra / 2);
310 /* the only modification to the next two routines is to call
311 gtk_aspect_frame_paint instead of gtk_frame_paint */
314 gtk_aspect_frame_draw (GtkWidget *widget,
318 GdkRectangle child_area;
320 g_return_if_fail (widget != NULL);
321 g_return_if_fail (GTK_IS_ASPECT_FRAME (widget));
322 g_return_if_fail (area != NULL);
324 if (GTK_WIDGET_DRAWABLE (widget))
326 bin = GTK_BIN (widget);
328 gtk_aspect_frame_paint (widget, area);
330 if (bin->child && gtk_widget_intersect (bin->child, area, &child_area))
331 gtk_widget_draw (bin->child, &child_area);
336 gtk_aspect_frame_expose (GtkWidget *widget,
337 GdkEventExpose *event)
340 GdkEventExpose child_event;
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);
346 if (GTK_WIDGET_DRAWABLE (widget))
348 bin = GTK_BIN (widget);
350 gtk_aspect_frame_paint (widget, &event->area);
352 child_event = *event;
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);
363 gtk_aspect_frame_size_allocate (GtkWidget *widget,
364 GtkAllocation *allocation)
367 GtkAspectFrame *aspect_frame;
370 GtkAllocation child_allocation;
375 g_return_if_fail (widget != NULL);
376 g_return_if_fail (GTK_IS_ASPECT_FRAME (widget));
377 g_return_if_fail (allocation != NULL);
379 aspect_frame = GTK_ASPECT_FRAME (widget);
380 frame = GTK_FRAME (widget);
381 bin = GTK_BIN (widget);
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);
396 widget->allocation = *allocation;
398 if (bin->child && GTK_WIDGET_VISIBLE (bin->child))
400 if (aspect_frame->obey_child)
402 GtkRequisition child_requisition;
404 gtk_widget_get_child_requisition (bin->child, &child_requisition);
405 if (child_requisition.height != 0)
407 ratio = ((gdouble) child_requisition.width /
408 child_requisition.height);
409 if (ratio < MIN_RATIO)
412 else if (child_requisition.width != 0)
418 ratio = aspect_frame->ratio;
420 x = (GTK_CONTAINER (frame)->border_width +
421 GTK_WIDGET (frame)->style->klass->xthickness);
422 width = allocation->width - x * 2;
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);
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);
435 if (ratio * height > width)
437 child_allocation.width = width;
438 child_allocation.height = width/ratio + 0.5;
442 child_allocation.width = ratio*height + 0.5;
443 child_allocation.height = height;
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;
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;
456 gtk_widget_size_allocate (bin->child, &child_allocation);