]> Pileus Git - ~andy/gtk/blob - gtk/gtkframe.c
Added notice to look in AUTHORS and ChangeLog files for a list of changes.
[~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      (GtkObject      *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   GtkObjectClass *object_class;
94   GtkWidgetClass *widget_class;
95
96   object_class = (GtkObjectClass*) class;
97   widget_class = (GtkWidgetClass*) class;
98
99   parent_class = gtk_type_class (gtk_bin_get_type ());
100
101   gtk_object_add_arg_type ("GtkFrame::label", GTK_TYPE_STRING, GTK_ARG_READWRITE, ARG_LABEL);
102   gtk_object_add_arg_type ("GtkFrame::label_xalign", GTK_TYPE_FLOAT, GTK_ARG_READWRITE, ARG_LABEL_XALIGN);
103   gtk_object_add_arg_type ("GtkFrame::label_yalign", GTK_TYPE_FLOAT, GTK_ARG_READWRITE, ARG_LABEL_YALIGN);
104   gtk_object_add_arg_type ("GtkFrame::shadow", GTK_TYPE_SHADOW_TYPE, GTK_ARG_READWRITE, ARG_SHADOW);
105
106   object_class->set_arg = gtk_frame_set_arg;
107   object_class->get_arg = gtk_frame_get_arg;
108   object_class->finalize = gtk_frame_finalize;
109
110   widget_class->draw = gtk_frame_draw;
111   widget_class->expose_event = gtk_frame_expose;
112   widget_class->size_request = gtk_frame_size_request;
113   widget_class->size_allocate = gtk_frame_size_allocate;
114   widget_class->style_set = gtk_frame_style_set;
115 }
116
117 static void
118 gtk_frame_init (GtkFrame *frame)
119 {
120   frame->label = NULL;
121   frame->shadow_type = GTK_SHADOW_ETCHED_IN;
122   frame->label_width = 0;
123   frame->label_height = 0;
124   frame->label_xalign = 0.0;
125   frame->label_yalign = 0.5;
126 }
127
128 static void
129 gtk_frame_set_arg (GtkObject      *object,
130                    GtkArg         *arg,
131                    guint           arg_id)
132 {
133   GtkFrame *frame;
134
135   frame = GTK_FRAME (object);
136
137   switch (arg_id)
138     {
139     case ARG_LABEL:
140       gtk_frame_set_label (frame, GTK_VALUE_STRING (*arg));
141       break;
142     case ARG_LABEL_XALIGN:
143       gtk_frame_set_label_align (frame, GTK_VALUE_FLOAT (*arg), frame->label_yalign);
144       break;
145     case ARG_LABEL_YALIGN:
146       gtk_frame_set_label_align (frame, frame->label_xalign, GTK_VALUE_FLOAT (*arg));
147       break;
148     case ARG_SHADOW:
149       gtk_frame_set_shadow_type (frame, GTK_VALUE_ENUM (*arg));
150       break;
151     default:
152       break;
153     }
154 }
155
156 static void
157 gtk_frame_get_arg (GtkObject      *object,
158                    GtkArg         *arg,
159                    guint           arg_id)
160 {
161   GtkFrame *frame;
162
163   frame = GTK_FRAME (object);
164
165   switch (arg_id)
166     {
167     case ARG_LABEL:
168       GTK_VALUE_STRING (*arg) = g_strdup (frame->label);
169       break;
170     case ARG_LABEL_XALIGN:
171       GTK_VALUE_FLOAT (*arg) = frame->label_xalign;
172       break;
173     case ARG_LABEL_YALIGN:
174       GTK_VALUE_FLOAT (*arg) = frame->label_yalign;
175       break;
176     case ARG_SHADOW:
177       GTK_VALUE_ENUM (*arg) = frame->shadow_type;
178       break;
179     default:
180       arg->type = GTK_TYPE_INVALID;
181       break;
182     }
183 }
184
185 GtkWidget*
186 gtk_frame_new (const gchar *label)
187 {
188   GtkFrame *frame;
189
190   frame = gtk_type_new (gtk_frame_get_type ());
191
192   gtk_frame_set_label (frame, label);
193
194   return GTK_WIDGET (frame);
195 }
196
197 static void
198 gtk_frame_style_set (GtkWidget      *widget,
199                      GtkStyle       *previous_style)
200 {
201   GtkFrame *frame;
202
203   frame = GTK_FRAME (widget);
204
205   if (frame->label)
206     {
207       frame->label_width = gdk_string_measure (GTK_WIDGET (frame)->style->font, frame->label) + 7;
208       frame->label_height = (GTK_WIDGET (frame)->style->font->ascent +
209                              GTK_WIDGET (frame)->style->font->descent + 1);
210     }
211
212   if (GTK_WIDGET_CLASS (parent_class)->style_set)
213     GTK_WIDGET_CLASS (parent_class)->style_set (widget, previous_style);
214 }
215
216 void
217 gtk_frame_set_label (GtkFrame *frame,
218                      const gchar *label)
219 {
220   g_return_if_fail (frame != NULL);
221   g_return_if_fail (GTK_IS_FRAME (frame));
222
223   if ((label && frame->label && (strcmp (frame->label, label) == 0)) ||
224       (!label && !frame->label))
225     return;
226
227   if (frame->label)
228     g_free (frame->label);
229   frame->label = NULL;
230
231   if (label)
232     {
233       frame->label = g_strdup (label);
234       frame->label_width = gdk_string_measure (GTK_WIDGET (frame)->style->font, frame->label) + 7;
235       frame->label_height = (GTK_WIDGET (frame)->style->font->ascent +
236                              GTK_WIDGET (frame)->style->font->descent + 1);
237     }
238   else
239     {
240       frame->label_width = 0;
241       frame->label_height = 0;
242     }
243
244   if (GTK_WIDGET_DRAWABLE (frame))
245     {
246       GtkWidget *widget;
247
248       /* clear the old label area
249       */
250       widget = GTK_WIDGET (frame);
251       gtk_widget_queue_clear_area (widget,
252                                    widget->allocation.x + GTK_CONTAINER (frame)->border_width,
253                                    widget->allocation.y + GTK_CONTAINER (frame)->border_width,
254                                    widget->allocation.width - GTK_CONTAINER (frame)->border_width,
255                                    widget->allocation.y + frame->label_height);
256
257     }
258   
259   gtk_widget_queue_resize (GTK_WIDGET (frame));
260 }
261
262 void
263 gtk_frame_set_label_align (GtkFrame *frame,
264                            gfloat    xalign,
265                            gfloat    yalign)
266 {
267   g_return_if_fail (frame != NULL);
268   g_return_if_fail (GTK_IS_FRAME (frame));
269
270   xalign = CLAMP (xalign, 0.0, 1.0);
271   yalign = CLAMP (yalign, 0.0, 1.0);
272
273   if ((xalign != frame->label_xalign) || (yalign != frame->label_yalign))
274     {
275       frame->label_xalign = xalign;
276       frame->label_yalign = yalign;
277
278       if (GTK_WIDGET_DRAWABLE (frame))
279         {
280           GtkWidget *widget;
281
282           /* clear the old label area
283           */
284           widget = GTK_WIDGET (frame);
285           gtk_widget_queue_clear_area (widget,
286                                        widget->allocation.x + GTK_CONTAINER (frame)->border_width,
287                                        widget->allocation.y + GTK_CONTAINER (frame)->border_width,
288                                        widget->allocation.width - GTK_CONTAINER (frame)->border_width,
289                                        widget->allocation.y + frame->label_height);
290
291         }
292       gtk_widget_queue_resize (GTK_WIDGET (frame));
293     }
294 }
295
296 void
297 gtk_frame_set_shadow_type (GtkFrame      *frame,
298                            GtkShadowType  type)
299 {
300   g_return_if_fail (frame != NULL);
301   g_return_if_fail (GTK_IS_FRAME (frame));
302
303   if ((GtkShadowType) frame->shadow_type != type)
304     {
305       frame->shadow_type = type;
306
307       if (GTK_WIDGET_DRAWABLE (frame))
308         {
309           gtk_widget_queue_clear (GTK_WIDGET (frame));
310         }
311       gtk_widget_queue_resize (GTK_WIDGET (frame));
312     }
313 }
314
315
316 static void
317 gtk_frame_finalize (GtkObject *object)
318 {
319   GtkFrame *frame;
320
321   g_return_if_fail (object != NULL);
322   g_return_if_fail (GTK_IS_FRAME (object));
323
324   frame = GTK_FRAME (object);
325
326   if (frame->label)
327     g_free (frame->label);
328
329   (* GTK_OBJECT_CLASS (parent_class)->finalize) (object);
330 }
331
332 static void
333 gtk_frame_paint (GtkWidget    *widget,
334                  GdkRectangle *area)
335 {
336   GtkFrame *frame;
337   gint height_extra;
338   gint label_area_width;
339   gint x, y, x2, y2;
340
341   g_return_if_fail (widget != NULL);
342   g_return_if_fail (GTK_IS_FRAME (widget));
343   g_return_if_fail (area != NULL);
344
345   if (GTK_WIDGET_DRAWABLE (widget))
346     {
347       frame = GTK_FRAME (widget);
348
349       height_extra = frame->label_height - widget->style->klass->xthickness;
350       height_extra = MAX (height_extra, 0);
351
352       x = GTK_CONTAINER (frame)->border_width;
353       y = GTK_CONTAINER (frame)->border_width;
354
355       if (frame->label)
356         {
357            label_area_width = (widget->allocation.width -
358                                GTK_CONTAINER (frame)->border_width * 2 -
359                                widget->style->klass->xthickness * 2);
360            
361            x2 = ((label_area_width - frame->label_width) * frame->label_xalign +
362                 GTK_CONTAINER (frame)->border_width + widget->style->klass->xthickness);
363            y2 = (GTK_CONTAINER (frame)->border_width + widget->style->font->ascent);
364
365            gtk_paint_shadow_gap (widget->style, widget->window,
366                                  GTK_STATE_NORMAL, frame->shadow_type,
367                                  area, widget, "frame",
368                                  widget->allocation.x + x,
369                                  widget->allocation.y + y + height_extra / 2,
370                                  widget->allocation.width - x * 2,
371                                  widget->allocation.height - y * 2 - height_extra / 2,
372                                  GTK_POS_TOP, 
373                                  x2 + 2 - x, frame->label_width - 4);
374            
375            gtk_paint_string (widget->style, widget->window, GTK_WIDGET_STATE (widget),
376                              area, widget, "frame",
377                              widget->allocation.x + x2 + 3,
378                              widget->allocation.y + y2,
379                              frame->label);
380         }
381        else
382          gtk_paint_shadow (widget->style, widget->window,
383                            GTK_STATE_NORMAL, frame->shadow_type,
384                            area, widget, "frame",
385                            widget->allocation.x + x,
386                            widget->allocation.y + y + height_extra / 2,
387                            widget->allocation.width - x * 2,
388                            widget->allocation.height - y * 2 - height_extra / 2);
389     }
390 }
391
392 static void
393 gtk_frame_draw (GtkWidget    *widget,
394                 GdkRectangle *area)
395 {
396   GtkBin *bin;
397   GdkRectangle child_area;
398
399   g_return_if_fail (widget != NULL);
400   g_return_if_fail (GTK_IS_FRAME (widget));
401   g_return_if_fail (area != NULL);
402
403   if (GTK_WIDGET_DRAWABLE (widget))
404     {
405       bin = GTK_BIN (widget);
406
407       gtk_frame_paint (widget, area);
408
409       if (bin->child && gtk_widget_intersect (bin->child, area, &child_area))
410         gtk_widget_draw (bin->child, &child_area);
411     }
412 }
413
414 static gint
415 gtk_frame_expose (GtkWidget      *widget,
416                   GdkEventExpose *event)
417 {
418   GtkBin *bin;
419   GdkEventExpose child_event;
420
421   g_return_val_if_fail (widget != NULL, FALSE);
422   g_return_val_if_fail (GTK_IS_FRAME (widget), FALSE);
423   g_return_val_if_fail (event != NULL, FALSE);
424
425   if (GTK_WIDGET_DRAWABLE (widget))
426     {
427       bin = GTK_BIN (widget);
428
429       gtk_frame_paint (widget, &event->area);
430
431       child_event = *event;
432       if (bin->child &&
433           GTK_WIDGET_NO_WINDOW (bin->child) &&
434           gtk_widget_intersect (bin->child, &event->area, &child_event.area))
435         gtk_widget_event (bin->child, (GdkEvent*) &child_event);
436     }
437
438   return FALSE;
439 }
440
441 static void
442 gtk_frame_size_request (GtkWidget      *widget,
443                         GtkRequisition *requisition)
444 {
445   GtkFrame *frame;
446   GtkBin *bin;
447   gint tmp_height;
448
449   g_return_if_fail (widget != NULL);
450   g_return_if_fail (GTK_IS_FRAME (widget));
451   g_return_if_fail (requisition != NULL);
452
453   frame = GTK_FRAME (widget);
454   bin = GTK_BIN (widget);
455
456   requisition->width = (GTK_CONTAINER (widget)->border_width +
457                         GTK_WIDGET (widget)->style->klass->xthickness) * 2;
458
459   tmp_height = frame->label_height - GTK_WIDGET (widget)->style->klass->ythickness;
460   tmp_height = MAX (tmp_height, 0);
461
462   requisition->height = tmp_height + (GTK_CONTAINER (widget)->border_width +
463                                       GTK_WIDGET (widget)->style->klass->ythickness) * 2;
464
465   if (bin->child && GTK_WIDGET_VISIBLE (bin->child))
466     {
467       GtkRequisition child_requisition;
468       
469       gtk_widget_size_request (bin->child, &child_requisition);
470
471       requisition->width += MAX (child_requisition.width, frame->label_width);
472       requisition->height += child_requisition.height;
473     }
474   else
475     {
476       requisition->width += frame->label_width;
477     }
478 }
479
480 static void
481 gtk_frame_size_allocate (GtkWidget     *widget,
482                          GtkAllocation *allocation)
483 {
484   GtkFrame *frame;
485   GtkBin *bin;
486   GtkAllocation child_allocation;
487
488   g_return_if_fail (widget != NULL);
489   g_return_if_fail (GTK_IS_FRAME (widget));
490   g_return_if_fail (allocation != NULL);
491
492   frame = GTK_FRAME (widget);
493   bin = GTK_BIN (widget);
494
495   if (GTK_WIDGET_MAPPED (widget) &&
496       ((widget->allocation.x != allocation->x) ||
497        (widget->allocation.y != allocation->y) ||
498        (widget->allocation.width != allocation->width) ||
499        (widget->allocation.height != allocation->height)) &&
500       (widget->allocation.width != 0) &&
501       (widget->allocation.height != 0))
502      gtk_widget_queue_clear (widget);
503
504   widget->allocation = *allocation;
505
506   if (bin->child && GTK_WIDGET_VISIBLE (bin->child))
507     {
508       child_allocation.x = (GTK_CONTAINER (frame)->border_width +
509                             GTK_WIDGET (frame)->style->klass->xthickness);
510       child_allocation.width = MAX(1, (gint)allocation->width - child_allocation.x * 2);
511
512       child_allocation.y = (GTK_CONTAINER (frame)->border_width +
513                             MAX (frame->label_height, GTK_WIDGET (frame)->style->klass->ythickness));
514       child_allocation.height = MAX (1, ((gint)allocation->height - child_allocation.y -
515                                          (gint)GTK_CONTAINER (frame)->border_width -
516                                          (gint)GTK_WIDGET (frame)->style->klass->ythickness));
517
518       child_allocation.x += allocation->x;
519       child_allocation.y += allocation->y;
520
521       gtk_widget_size_allocate (bin->child, &child_allocation);
522     }
523 }