]> Pileus Git - ~andy/gtk/blob - gtk/gtkframe.c
ad01a879fd68bdae25df08481bb72009892db011
[~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 Free
16  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  */
18 #include <string.h>
19 #include "gtkframe.h"
20
21
22 static void gtk_frame_class_init    (GtkFrameClass  *klass);
23 static void gtk_frame_init          (GtkFrame       *frame);
24 static void gtk_frame_destroy       (GtkObject      *object);
25 static void gtk_frame_paint         (GtkWidget      *widget,
26                                      GdkRectangle   *area);
27 static void gtk_frame_draw          (GtkWidget      *widget,
28                                      GdkRectangle   *area);
29 static gint gtk_frame_expose        (GtkWidget      *widget,
30                                      GdkEventExpose *event);
31 static void gtk_frame_size_request  (GtkWidget      *widget,
32                                      GtkRequisition *requisition);
33 static void gtk_frame_size_allocate (GtkWidget      *widget,
34                                      GtkAllocation  *allocation);
35
36
37 static GtkBinClass *parent_class = NULL;
38
39
40 guint
41 gtk_frame_get_type ()
42 {
43   static guint frame_type = 0;
44
45   if (!frame_type)
46     {
47       GtkTypeInfo frame_info =
48       {
49         "GtkFrame",
50         sizeof (GtkFrame),
51         sizeof (GtkFrameClass),
52         (GtkClassInitFunc) gtk_frame_class_init,
53         (GtkObjectInitFunc) gtk_frame_init,
54         (GtkArgFunc) NULL,
55       };
56
57       frame_type = gtk_type_unique (gtk_bin_get_type (), &frame_info);
58     }
59
60   return frame_type;
61 }
62
63 static void
64 gtk_frame_class_init (GtkFrameClass *class)
65 {
66   GtkObjectClass *object_class;
67   GtkWidgetClass *widget_class;
68
69   object_class = (GtkObjectClass*) class;
70   widget_class = (GtkWidgetClass*) class;
71
72   parent_class = gtk_type_class (gtk_bin_get_type ());
73
74   object_class->destroy = gtk_frame_destroy;
75
76   widget_class->draw = gtk_frame_draw;
77   widget_class->expose_event = gtk_frame_expose;
78   widget_class->size_request = gtk_frame_size_request;
79   widget_class->size_allocate = gtk_frame_size_allocate;
80 }
81
82 static void
83 gtk_frame_init (GtkFrame *frame)
84 {
85   GTK_WIDGET_SET_FLAGS (frame, GTK_BASIC);
86
87   frame->label = NULL;
88   frame->shadow_type = GTK_SHADOW_ETCHED_IN;
89   frame->label_width = 0;
90   frame->label_height = 0;
91   frame->label_xalign = 0.0;
92   frame->label_yalign = 0.5;
93 }
94
95 GtkWidget*
96 gtk_frame_new (const gchar *label)
97 {
98   GtkFrame *frame;
99
100   frame = gtk_type_new (gtk_frame_get_type ());
101
102   gtk_frame_set_label (frame, label);
103
104   return GTK_WIDGET (frame);
105 }
106
107 void
108 gtk_frame_set_label (GtkFrame *frame,
109                      const gchar *label)
110 {
111   g_return_if_fail (frame != NULL);
112   g_return_if_fail (GTK_IS_FRAME (frame));
113
114   if ((label && frame->label && (strcmp (frame->label, label) == 0)) ||
115       (!label && !frame->label))
116     return;
117
118   if (frame->label)
119     g_free (frame->label);
120   frame->label = NULL;
121
122   if (label)
123     {
124       frame->label = g_strdup (label);
125       frame->label_width = gdk_string_measure (GTK_WIDGET (frame)->style->font, frame->label) + 7;
126       frame->label_height = (GTK_WIDGET (frame)->style->font->ascent +
127                              GTK_WIDGET (frame)->style->font->descent + 1);
128     }
129   else
130     {
131       frame->label_width = 0;
132       frame->label_height = 0;
133     }
134
135   if (GTK_WIDGET_DRAWABLE (frame))
136     {
137       GtkWidget *widget;
138
139       /* clear the old label area
140       */
141       widget = GTK_WIDGET (frame);
142       gdk_window_clear_area (widget->window,
143                              widget->allocation.x + GTK_CONTAINER (frame)->border_width,
144                              widget->allocation.y + GTK_CONTAINER (frame)->border_width,
145                              widget->allocation.width - GTK_CONTAINER (frame)->border_width,
146                              widget->allocation.y + frame->label_height);
147
148     }
149   
150   gtk_widget_queue_resize (GTK_WIDGET (frame));
151 }
152
153 void
154 gtk_frame_set_label_align (GtkFrame *frame,
155                            gfloat    xalign,
156                            gfloat    yalign)
157 {
158   g_return_if_fail (frame != NULL);
159   g_return_if_fail (GTK_IS_FRAME (frame));
160
161   xalign = CLAMP (xalign, 0.0, 1.0);
162   yalign = CLAMP (yalign, 0.0, 1.0);
163
164   if ((xalign != frame->label_xalign) || (yalign != frame->label_yalign))
165     {
166       frame->label_xalign = xalign;
167       frame->label_yalign = yalign;
168
169       if (GTK_WIDGET_DRAWABLE (frame))
170         {
171           GtkWidget *widget;
172
173           /* clear the old label area
174           */
175           widget = GTK_WIDGET (frame);
176           gdk_window_clear_area (widget->window,
177                                  widget->allocation.x + GTK_CONTAINER (frame)->border_width,
178                                  widget->allocation.y + GTK_CONTAINER (frame)->border_width,
179                                  widget->allocation.width - GTK_CONTAINER (frame)->border_width,
180                                  widget->allocation.y + frame->label_height);
181
182         }
183       gtk_widget_queue_resize (GTK_WIDGET (frame));
184     }
185 }
186
187 void
188 gtk_frame_set_shadow_type (GtkFrame      *frame,
189                            GtkShadowType  type)
190 {
191   g_return_if_fail (frame != NULL);
192   g_return_if_fail (GTK_IS_FRAME (frame));
193
194   if ((GtkShadowType) frame->shadow_type != type)
195     {
196       frame->shadow_type = type;
197
198       if (GTK_WIDGET_DRAWABLE (frame))
199         {
200           gdk_window_clear_area (GTK_WIDGET (frame)->window,
201                                  GTK_WIDGET (frame)->allocation.x,
202                                  GTK_WIDGET (frame)->allocation.y,
203                                  GTK_WIDGET (frame)->allocation.width,
204                                  GTK_WIDGET (frame)->allocation.height);
205         }
206       gtk_widget_queue_resize (GTK_WIDGET (frame));
207     }
208 }
209
210
211 static void
212 gtk_frame_destroy (GtkObject *object)
213 {
214   GtkFrame *frame;
215
216   g_return_if_fail (object != NULL);
217   g_return_if_fail (GTK_IS_FRAME (object));
218
219   frame = GTK_FRAME (object);
220
221   if (frame->label)
222     g_free (frame->label);
223
224   if (GTK_OBJECT_CLASS (parent_class)->destroy)
225     (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
226 }
227
228 static void
229 gtk_frame_paint (GtkWidget    *widget,
230                  GdkRectangle *area)
231 {
232   GtkFrame *frame;
233   GtkStateType state;
234   gint height_extra;
235   gint label_area_width;
236   gint x, y;
237
238   g_return_if_fail (widget != NULL);
239   g_return_if_fail (GTK_IS_FRAME (widget));
240   g_return_if_fail (area != NULL);
241
242   if (GTK_WIDGET_DRAWABLE (widget))
243     {
244       frame = GTK_FRAME (widget);
245
246       state = widget->state;
247       if (!GTK_WIDGET_IS_SENSITIVE (widget))
248         state = GTK_STATE_INSENSITIVE;
249
250       height_extra = frame->label_height - widget->style->klass->xthickness;
251       height_extra = MAX (height_extra, 0);
252
253       x = GTK_CONTAINER (frame)->border_width;
254       y = GTK_CONTAINER (frame)->border_width;
255
256       gtk_draw_shadow (widget->style, widget->window,
257                        GTK_STATE_NORMAL, frame->shadow_type,
258                        widget->allocation.x + x,
259                        widget->allocation.y + y + height_extra / 2,
260                        widget->allocation.width - x * 2,
261                        widget->allocation.height - y * 2 - height_extra / 2);
262
263       if (frame->label)
264         {
265           label_area_width = (widget->allocation.width -
266                               GTK_CONTAINER (frame)->border_width * 2 -
267                               widget->style->klass->xthickness * 2);
268
269           x = ((label_area_width - frame->label_width) * frame->label_xalign +
270                GTK_CONTAINER (frame)->border_width + widget->style->klass->xthickness);
271           y = (GTK_CONTAINER (frame)->border_width + widget->style->font->ascent);
272
273           gdk_window_clear_area (widget->window,
274                                  widget->allocation.x + x + 2,
275                                  widget->allocation.y + GTK_CONTAINER (frame)->border_width,
276                                  frame->label_width - 4,
277                                  frame->label_height);
278           gtk_draw_string (widget->style, widget->window, state,
279                            widget->allocation.x + x + 3,
280                            widget->allocation.y + y,
281                            frame->label);
282         }
283     }
284 }
285
286 static void
287 gtk_frame_draw (GtkWidget    *widget,
288                 GdkRectangle *area)
289 {
290   GtkBin *bin;
291   GdkRectangle child_area;
292
293   g_return_if_fail (widget != NULL);
294   g_return_if_fail (GTK_IS_FRAME (widget));
295   g_return_if_fail (area != NULL);
296
297   if (GTK_WIDGET_DRAWABLE (widget))
298     {
299       bin = GTK_BIN (widget);
300
301       gtk_frame_paint (widget, area);
302
303       if (bin->child && gtk_widget_intersect (bin->child, area, &child_area))
304         gtk_widget_draw (bin->child, &child_area);
305     }
306 }
307
308 static gint
309 gtk_frame_expose (GtkWidget      *widget,
310                   GdkEventExpose *event)
311 {
312   GtkBin *bin;
313   GdkEventExpose child_event;
314
315   g_return_val_if_fail (widget != NULL, FALSE);
316   g_return_val_if_fail (GTK_IS_FRAME (widget), FALSE);
317   g_return_val_if_fail (event != NULL, FALSE);
318
319   if (GTK_WIDGET_DRAWABLE (widget))
320     {
321       bin = GTK_BIN (widget);
322
323       gtk_frame_paint (widget, &event->area);
324
325       child_event = *event;
326       if (bin->child &&
327           GTK_WIDGET_NO_WINDOW (bin->child) &&
328           gtk_widget_intersect (bin->child, &event->area, &child_event.area))
329         gtk_widget_event (bin->child, (GdkEvent*) &child_event);
330     }
331
332   return FALSE;
333 }
334
335 static void
336 gtk_frame_size_request (GtkWidget      *widget,
337                         GtkRequisition *requisition)
338 {
339   GtkFrame *frame;
340   GtkBin *bin;
341   gint tmp_height;
342
343   g_return_if_fail (widget != NULL);
344   g_return_if_fail (GTK_IS_FRAME (widget));
345   g_return_if_fail (requisition != NULL);
346
347   frame = GTK_FRAME (widget);
348   bin = GTK_BIN (widget);
349
350   requisition->width = (GTK_CONTAINER (widget)->border_width +
351                         GTK_WIDGET (widget)->style->klass->xthickness) * 2;
352
353   tmp_height = frame->label_height - GTK_WIDGET (widget)->style->klass->ythickness;
354   tmp_height = MAX (tmp_height, 0);
355
356   requisition->height = tmp_height + (GTK_CONTAINER (widget)->border_width +
357                                       GTK_WIDGET (widget)->style->klass->ythickness) * 2;
358
359   if (bin->child && GTK_WIDGET_VISIBLE (bin->child))
360     {
361       gtk_widget_size_request (bin->child, &bin->child->requisition);
362
363       requisition->width += MAX (bin->child->requisition.width, frame->label_width);
364       requisition->height += bin->child->requisition.height;
365     }
366   else
367     {
368       requisition->width += frame->label_width;
369     }
370 }
371
372 static void
373 gtk_frame_size_allocate (GtkWidget     *widget,
374                          GtkAllocation *allocation)
375 {
376   GtkFrame *frame;
377   GtkBin *bin;
378   GtkAllocation child_allocation;
379
380   g_return_if_fail (widget != NULL);
381   g_return_if_fail (GTK_IS_FRAME (widget));
382   g_return_if_fail (allocation != NULL);
383
384   frame = GTK_FRAME (widget);
385   bin = GTK_BIN (widget);
386
387   if (GTK_WIDGET_MAPPED (widget) &&
388       ((widget->allocation.x != allocation->x) ||
389        (widget->allocation.y != allocation->y) ||
390        (widget->allocation.width != allocation->width) ||
391        (widget->allocation.height != allocation->height)) &&
392       (widget->allocation.width != 0) &&
393       (widget->allocation.height != 0))
394     gdk_window_clear_area (widget->window,
395                            widget->allocation.x,
396                            widget->allocation.y,
397                            widget->allocation.width,
398                            widget->allocation.height);
399
400   widget->allocation = *allocation;
401
402   if (bin->child && GTK_WIDGET_VISIBLE (bin->child))
403     {
404       child_allocation.x = (GTK_CONTAINER (frame)->border_width +
405                             GTK_WIDGET (frame)->style->klass->xthickness);
406       child_allocation.width = MAX(0, allocation->width - child_allocation.x * 2);
407
408       child_allocation.y = (GTK_CONTAINER (frame)->border_width +
409                             MAX (frame->label_height, GTK_WIDGET (frame)->style->klass->ythickness));
410       child_allocation.height = MAX (0, (allocation->height - child_allocation.y -
411                                          GTK_CONTAINER (frame)->border_width -
412                                          GTK_WIDGET (frame)->style->klass->ythickness));
413
414       child_allocation.x += allocation->x;
415       child_allocation.y += allocation->y;
416
417       gtk_widget_size_allocate (bin->child, &child_allocation);
418     }
419 }