]> Pileus Git - ~andy/gtk/blob - gtk/gtkframe.c
documented necessary changes for 1.4 transition.
[~andy/gtk] / gtk / gtkframe.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 /*
21  * Modified by the GTK+ Team and others 1997-1999.  See the AUTHORS
22  * file for a list of people on the GTK+ Team.  See the ChangeLog
23  * files for a list of changes.  These files are distributed with
24  * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
25  */
26
27 #include <string.h>
28 #include "gtkframe.h"
29
30 enum {
31   ARG_0,
32   ARG_LABEL,
33   ARG_LABEL_XALIGN,
34   ARG_LABEL_YALIGN,
35   ARG_SHADOW
36 };
37
38
39 static void gtk_frame_class_init    (GtkFrameClass  *klass);
40 static void gtk_frame_init          (GtkFrame       *frame);
41 static void gtk_frame_set_arg       (GtkObject      *object,
42                                      GtkArg         *arg,
43                                      guint           arg_id);
44 static void gtk_frame_get_arg       (GtkObject      *object,
45                                      GtkArg         *arg,
46                                      guint           arg_id);
47 static void gtk_frame_finalize      (GObject        *object);
48 static void gtk_frame_paint         (GtkWidget      *widget,
49                                      GdkRectangle   *area);
50 static void gtk_frame_draw          (GtkWidget      *widget,
51                                      GdkRectangle   *area);
52 static gint gtk_frame_expose        (GtkWidget      *widget,
53                                      GdkEventExpose *event);
54 static void gtk_frame_size_request  (GtkWidget      *widget,
55                                      GtkRequisition *requisition);
56 static void gtk_frame_size_allocate (GtkWidget      *widget,
57                                      GtkAllocation  *allocation);
58 static void gtk_frame_style_set     (GtkWidget      *widget,
59                                      GtkStyle       *previous_style);
60
61
62 static GtkBinClass *parent_class = NULL;
63
64
65 GtkType
66 gtk_frame_get_type (void)
67 {
68   static GtkType frame_type = 0;
69
70   if (!frame_type)
71     {
72       static const GtkTypeInfo frame_info =
73       {
74         "GtkFrame",
75         sizeof (GtkFrame),
76         sizeof (GtkFrameClass),
77         (GtkClassInitFunc) gtk_frame_class_init,
78         (GtkObjectInitFunc) gtk_frame_init,
79         /* reserved_1 */ NULL,
80         /* reserved_2 */ NULL,
81         (GtkClassInitFunc) NULL,
82       };
83
84       frame_type = gtk_type_unique (gtk_bin_get_type (), &frame_info);
85     }
86
87   return frame_type;
88 }
89
90 static void
91 gtk_frame_class_init (GtkFrameClass *class)
92 {
93   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
94   GtkObjectClass *object_class;
95   GtkWidgetClass *widget_class;
96
97   object_class = (GtkObjectClass*) class;
98   widget_class = (GtkWidgetClass*) class;
99
100   parent_class = gtk_type_class (gtk_bin_get_type ());
101
102   gobject_class->finalize = gtk_frame_finalize;
103
104   gtk_object_add_arg_type ("GtkFrame::label", GTK_TYPE_STRING, GTK_ARG_READWRITE, ARG_LABEL);
105   gtk_object_add_arg_type ("GtkFrame::label_xalign", GTK_TYPE_FLOAT, GTK_ARG_READWRITE, ARG_LABEL_XALIGN);
106   gtk_object_add_arg_type ("GtkFrame::label_yalign", GTK_TYPE_FLOAT, GTK_ARG_READWRITE, ARG_LABEL_YALIGN);
107   gtk_object_add_arg_type ("GtkFrame::shadow", GTK_TYPE_SHADOW_TYPE, GTK_ARG_READWRITE, ARG_SHADOW);
108
109   object_class->set_arg = gtk_frame_set_arg;
110   object_class->get_arg = gtk_frame_get_arg;
111
112   widget_class->draw = gtk_frame_draw;
113   widget_class->expose_event = gtk_frame_expose;
114   widget_class->size_request = gtk_frame_size_request;
115   widget_class->size_allocate = gtk_frame_size_allocate;
116   widget_class->style_set = gtk_frame_style_set;
117 }
118
119 static void
120 gtk_frame_init (GtkFrame *frame)
121 {
122   frame->label = NULL;
123   frame->shadow_type = GTK_SHADOW_ETCHED_IN;
124   frame->label_width = 0;
125   frame->label_height = 0;
126   frame->label_xalign = 0.0;
127   frame->label_yalign = 0.5;
128 }
129
130 static void
131 gtk_frame_set_arg (GtkObject      *object,
132                    GtkArg         *arg,
133                    guint           arg_id)
134 {
135   GtkFrame *frame;
136
137   frame = GTK_FRAME (object);
138
139   switch (arg_id)
140     {
141     case ARG_LABEL:
142       gtk_frame_set_label (frame, GTK_VALUE_STRING (*arg));
143       break;
144     case ARG_LABEL_XALIGN:
145       gtk_frame_set_label_align (frame, GTK_VALUE_FLOAT (*arg), frame->label_yalign);
146       break;
147     case ARG_LABEL_YALIGN:
148       gtk_frame_set_label_align (frame, frame->label_xalign, GTK_VALUE_FLOAT (*arg));
149       break;
150     case ARG_SHADOW:
151       gtk_frame_set_shadow_type (frame, GTK_VALUE_ENUM (*arg));
152       break;
153     default:
154       break;
155     }
156 }
157
158 static void
159 gtk_frame_get_arg (GtkObject      *object,
160                    GtkArg         *arg,
161                    guint           arg_id)
162 {
163   GtkFrame *frame;
164
165   frame = GTK_FRAME (object);
166
167   switch (arg_id)
168     {
169     case ARG_LABEL:
170       GTK_VALUE_STRING (*arg) = g_strdup (frame->label);
171       break;
172     case ARG_LABEL_XALIGN:
173       GTK_VALUE_FLOAT (*arg) = frame->label_xalign;
174       break;
175     case ARG_LABEL_YALIGN:
176       GTK_VALUE_FLOAT (*arg) = frame->label_yalign;
177       break;
178     case ARG_SHADOW:
179       GTK_VALUE_ENUM (*arg) = frame->shadow_type;
180       break;
181     default:
182       arg->type = GTK_TYPE_INVALID;
183       break;
184     }
185 }
186
187 GtkWidget*
188 gtk_frame_new (const gchar *label)
189 {
190   GtkFrame *frame;
191
192   frame = gtk_type_new (gtk_frame_get_type ());
193
194   gtk_frame_set_label (frame, label);
195
196   return GTK_WIDGET (frame);
197 }
198
199 static void
200 gtk_frame_style_set (GtkWidget      *widget,
201                      GtkStyle       *previous_style)
202 {
203   GtkFrame *frame;
204
205   frame = GTK_FRAME (widget);
206
207   if (frame->label)
208     {
209       frame->label_width = gdk_string_measure (GTK_WIDGET (frame)->style->font, frame->label) + 7;
210       frame->label_height = (GTK_WIDGET (frame)->style->font->ascent +
211                              GTK_WIDGET (frame)->style->font->descent + 1);
212     }
213
214   if (GTK_WIDGET_CLASS (parent_class)->style_set)
215     GTK_WIDGET_CLASS (parent_class)->style_set (widget, previous_style);
216 }
217
218 void
219 gtk_frame_set_label (GtkFrame *frame,
220                      const gchar *label)
221 {
222   g_return_if_fail (frame != NULL);
223   g_return_if_fail (GTK_IS_FRAME (frame));
224
225   if ((label && frame->label && (strcmp (frame->label, label) == 0)) ||
226       (!label && !frame->label))
227     return;
228
229   if (frame->label)
230     g_free (frame->label);
231   frame->label = NULL;
232
233   if (label)
234     {
235       frame->label = g_strdup (label);
236       frame->label_width = gdk_string_measure (GTK_WIDGET (frame)->style->font, frame->label) + 7;
237       frame->label_height = (GTK_WIDGET (frame)->style->font->ascent +
238                              GTK_WIDGET (frame)->style->font->descent + 1);
239     }
240   else
241     {
242       frame->label_width = 0;
243       frame->label_height = 0;
244     }
245
246   if (GTK_WIDGET_DRAWABLE (frame))
247     {
248       GtkWidget *widget;
249
250       /* clear the old label area
251       */
252       widget = GTK_WIDGET (frame);
253       gtk_widget_queue_clear_area (widget,
254                                    widget->allocation.x + GTK_CONTAINER (frame)->border_width,
255                                    widget->allocation.y + GTK_CONTAINER (frame)->border_width,
256                                    widget->allocation.width - GTK_CONTAINER (frame)->border_width,
257                                    widget->allocation.y + frame->label_height);
258
259     }
260   
261   gtk_widget_queue_resize (GTK_WIDGET (frame));
262 }
263
264 void
265 gtk_frame_set_label_align (GtkFrame *frame,
266                            gfloat    xalign,
267                            gfloat    yalign)
268 {
269   g_return_if_fail (frame != NULL);
270   g_return_if_fail (GTK_IS_FRAME (frame));
271
272   xalign = CLAMP (xalign, 0.0, 1.0);
273   yalign = CLAMP (yalign, 0.0, 1.0);
274
275   if ((xalign != frame->label_xalign) || (yalign != frame->label_yalign))
276     {
277       frame->label_xalign = xalign;
278       frame->label_yalign = yalign;
279
280       if (GTK_WIDGET_DRAWABLE (frame))
281         {
282           GtkWidget *widget;
283
284           /* clear the old label area
285           */
286           widget = GTK_WIDGET (frame);
287           gtk_widget_queue_clear_area (widget,
288                                        widget->allocation.x + GTK_CONTAINER (frame)->border_width,
289                                        widget->allocation.y + GTK_CONTAINER (frame)->border_width,
290                                        widget->allocation.width - GTK_CONTAINER (frame)->border_width,
291                                        widget->allocation.y + frame->label_height);
292
293         }
294       gtk_widget_queue_resize (GTK_WIDGET (frame));
295     }
296 }
297
298 void
299 gtk_frame_set_shadow_type (GtkFrame      *frame,
300                            GtkShadowType  type)
301 {
302   g_return_if_fail (frame != NULL);
303   g_return_if_fail (GTK_IS_FRAME (frame));
304
305   if ((GtkShadowType) frame->shadow_type != type)
306     {
307       frame->shadow_type = type;
308
309       if (GTK_WIDGET_DRAWABLE (frame))
310         {
311           gtk_widget_queue_clear (GTK_WIDGET (frame));
312         }
313       gtk_widget_queue_resize (GTK_WIDGET (frame));
314     }
315 }
316
317
318 static void
319 gtk_frame_finalize (GObject *object)
320 {
321   GtkFrame *frame;
322
323   g_return_if_fail (GTK_IS_FRAME (object));
324
325   frame = GTK_FRAME (object);
326
327   if (frame->label)
328     g_free (frame->label);
329
330   G_OBJECT_CLASS (parent_class)->finalize (object);
331 }
332
333 static void
334 gtk_frame_paint (GtkWidget    *widget,
335                  GdkRectangle *area)
336 {
337   GtkFrame *frame;
338   gint height_extra;
339   gint label_area_width;
340   gint x, y, x2, y2;
341
342   g_return_if_fail (widget != NULL);
343   g_return_if_fail (GTK_IS_FRAME (widget));
344   g_return_if_fail (area != NULL);
345
346   if (GTK_WIDGET_DRAWABLE (widget))
347     {
348       frame = GTK_FRAME (widget);
349
350       height_extra = frame->label_height - widget->style->klass->xthickness;
351       height_extra = MAX (height_extra, 0);
352
353       x = GTK_CONTAINER (frame)->border_width;
354       y = GTK_CONTAINER (frame)->border_width;
355
356       if (frame->label)
357         {
358            label_area_width = (widget->allocation.width -
359                                GTK_CONTAINER (frame)->border_width * 2 -
360                                widget->style->klass->xthickness * 2);
361            
362            x2 = ((label_area_width - frame->label_width) * frame->label_xalign +
363                 GTK_CONTAINER (frame)->border_width + widget->style->klass->xthickness);
364            y2 = (GTK_CONTAINER (frame)->border_width + widget->style->font->ascent);
365
366            gtk_paint_shadow_gap (widget->style, widget->window,
367                                  GTK_STATE_NORMAL, frame->shadow_type,
368                                  area, widget, "frame",
369                                  widget->allocation.x + x,
370                                  widget->allocation.y + y + height_extra / 2,
371                                  widget->allocation.width - x * 2,
372                                  widget->allocation.height - y * 2 - height_extra / 2,
373                                  GTK_POS_TOP, 
374                                  x2 + 2 - x, frame->label_width - 4);
375            
376            gtk_paint_string (widget->style, widget->window, GTK_WIDGET_STATE (widget),
377                              area, widget, "frame",
378                              widget->allocation.x + x2 + 3,
379                              widget->allocation.y + y2,
380                              frame->label);
381         }
382        else
383          gtk_paint_shadow (widget->style, widget->window,
384                            GTK_STATE_NORMAL, frame->shadow_type,
385                            area, widget, "frame",
386                            widget->allocation.x + x,
387                            widget->allocation.y + y + height_extra / 2,
388                            widget->allocation.width - x * 2,
389                            widget->allocation.height - y * 2 - height_extra / 2);
390     }
391 }
392
393 static void
394 gtk_frame_draw (GtkWidget    *widget,
395                 GdkRectangle *area)
396 {
397   GtkBin *bin;
398   GdkRectangle child_area;
399
400   g_return_if_fail (widget != NULL);
401   g_return_if_fail (GTK_IS_FRAME (widget));
402   g_return_if_fail (area != NULL);
403
404   if (GTK_WIDGET_DRAWABLE (widget))
405     {
406       bin = GTK_BIN (widget);
407
408       gtk_frame_paint (widget, area);
409
410       if (bin->child && gtk_widget_intersect (bin->child, area, &child_area))
411         gtk_widget_draw (bin->child, &child_area);
412     }
413 }
414
415 static gint
416 gtk_frame_expose (GtkWidget      *widget,
417                   GdkEventExpose *event)
418 {
419   GtkBin *bin;
420   GdkEventExpose child_event;
421
422   g_return_val_if_fail (widget != NULL, FALSE);
423   g_return_val_if_fail (GTK_IS_FRAME (widget), FALSE);
424   g_return_val_if_fail (event != NULL, FALSE);
425
426   if (GTK_WIDGET_DRAWABLE (widget))
427     {
428       bin = GTK_BIN (widget);
429
430       gtk_frame_paint (widget, &event->area);
431
432       child_event = *event;
433       if (bin->child &&
434           GTK_WIDGET_NO_WINDOW (bin->child) &&
435           gtk_widget_intersect (bin->child, &event->area, &child_event.area))
436         gtk_widget_event (bin->child, (GdkEvent*) &child_event);
437     }
438
439   return FALSE;
440 }
441
442 static void
443 gtk_frame_size_request (GtkWidget      *widget,
444                         GtkRequisition *requisition)
445 {
446   GtkFrame *frame;
447   GtkBin *bin;
448   gint tmp_height;
449
450   g_return_if_fail (widget != NULL);
451   g_return_if_fail (GTK_IS_FRAME (widget));
452   g_return_if_fail (requisition != NULL);
453
454   frame = GTK_FRAME (widget);
455   bin = GTK_BIN (widget);
456
457   requisition->width = (GTK_CONTAINER (widget)->border_width +
458                         GTK_WIDGET (widget)->style->klass->xthickness) * 2;
459
460   tmp_height = frame->label_height - GTK_WIDGET (widget)->style->klass->ythickness;
461   tmp_height = MAX (tmp_height, 0);
462
463   requisition->height = tmp_height + (GTK_CONTAINER (widget)->border_width +
464                                       GTK_WIDGET (widget)->style->klass->ythickness) * 2;
465
466   if (bin->child && GTK_WIDGET_VISIBLE (bin->child))
467     {
468       GtkRequisition child_requisition;
469       
470       gtk_widget_size_request (bin->child, &child_requisition);
471
472       requisition->width += MAX (child_requisition.width, frame->label_width);
473       requisition->height += child_requisition.height;
474     }
475   else
476     {
477       requisition->width += frame->label_width;
478     }
479 }
480
481 static void
482 gtk_frame_size_allocate (GtkWidget     *widget,
483                          GtkAllocation *allocation)
484 {
485   GtkFrame *frame;
486   GtkBin *bin;
487   GtkAllocation child_allocation;
488
489   g_return_if_fail (widget != NULL);
490   g_return_if_fail (GTK_IS_FRAME (widget));
491   g_return_if_fail (allocation != NULL);
492
493   frame = GTK_FRAME (widget);
494   bin = GTK_BIN (widget);
495
496   if (GTK_WIDGET_MAPPED (widget) &&
497       ((widget->allocation.x != allocation->x) ||
498        (widget->allocation.y != allocation->y) ||
499        (widget->allocation.width != allocation->width) ||
500        (widget->allocation.height != allocation->height)) &&
501       (widget->allocation.width != 0) &&
502       (widget->allocation.height != 0))
503      gtk_widget_queue_clear (widget);
504
505   widget->allocation = *allocation;
506
507   if (bin->child && GTK_WIDGET_VISIBLE (bin->child))
508     {
509       child_allocation.x = (GTK_CONTAINER (frame)->border_width +
510                             GTK_WIDGET (frame)->style->klass->xthickness);
511       child_allocation.width = MAX(1, (gint)allocation->width - child_allocation.x * 2);
512
513       child_allocation.y = (GTK_CONTAINER (frame)->border_width +
514                             MAX (frame->label_height, GTK_WIDGET (frame)->style->klass->ythickness));
515       child_allocation.height = MAX (1, ((gint)allocation->height - child_allocation.y -
516                                          (gint)GTK_CONTAINER (frame)->border_width -
517                                          (gint)GTK_WIDGET (frame)->style->klass->ythickness));
518
519       child_allocation.x += allocation->x;
520       child_allocation.y += allocation->y;
521
522       gtk_widget_size_allocate (bin->child, &child_allocation);
523     }
524 }