source: trunk/third/gtk/gtk/gtkaspectframe.c @ 14482

Revision 14482, 13.3 KB checked in by ghudson, 24 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r14481, which included commits to RCS files with non-trunk default branches.
Line 
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
34enum {
35  ARG_0,
36  ARG_XALIGN,
37  ARG_YALIGN,
38  ARG_RATIO,
39  ARG_OBEY_CHILD
40};
41
42static void gtk_aspect_frame_class_init    (GtkAspectFrameClass *klass);
43static void gtk_aspect_frame_init          (GtkAspectFrame *aspect_frame);
44static void gtk_aspect_frame_set_arg       (GtkObject      *object,
45                                            GtkArg         *arg,
46                                            guint           arg_id);
47static void gtk_aspect_frame_get_arg       (GtkObject      *object,
48                                            GtkArg         *arg,
49                                            guint           arg_id);
50static void gtk_aspect_frame_draw          (GtkWidget      *widget,
51                                            GdkRectangle   *area);
52static void gtk_aspect_frame_paint         (GtkWidget      *widget,
53                                            GdkRectangle   *area);
54static gint gtk_aspect_frame_expose        (GtkWidget      *widget,
55                                            GdkEventExpose *event);
56static 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
62GtkType
63gtk_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
87static void
88gtk_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
113static void
114gtk_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
126static void
127gtk_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
166static void
167gtk_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
193GtkWidget*
194gtk_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
214void
215gtk_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
248static void
249gtk_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
313static void
314gtk_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
335static gint
336gtk_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
362static void
363gtk_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}
Note: See TracBrowser for help on using the repository browser.