1 /* LIBGIMP - The GIMP Library
2 * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
3 *
4 * gimpframe.c
5 * Copyright (C) 2004 Sven Neumann <sven@gimp.org>
6 *
7 * This library is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 3 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library. If not, see
19 * <https://www.gnu.org/licenses/>.
20 */
21
22 #include "config.h"
23
24 #include <string.h>
25
26 #include <gtk/gtk.h>
27
28 #include "gimpwidgetstypes.h"
29
30 #include "gimp3migration.h"
31 #include "gimpframe.h"
32
33
34 /**
35 * SECTION: gimpframe
36 * @title: GimpFrame
37 * @short_description: A widget providing a HIG-compliant subclass
38 * of #GtkFrame.
39 *
40 * A widget providing a HIG-compliant subclass of #GtkFrame.
41 **/
42
43
44 #define DEFAULT_LABEL_SPACING 6
45 #define DEFAULT_LABEL_BOLD TRUE
46
47 #define GIMP_FRAME_INDENT_KEY "gimp-frame-indent"
48 #define GIMP_FRAME_IN_EXPANDER_KEY "gimp-frame-in-expander"
49
50
51 static void gimp_frame_size_request (GtkWidget *widget,
52 GtkRequisition *requisition);
53 static void gimp_frame_size_allocate (GtkWidget *widget,
54 GtkAllocation *allocation);
55 static void gimp_frame_style_set (GtkWidget *widget,
56 GtkStyle *previous);
57 static gboolean gimp_frame_expose_event (GtkWidget *widget,
58 GdkEventExpose *event);
59 static void gimp_frame_child_allocate (GtkFrame *frame,
60 GtkAllocation *allocation);
61 static void gimp_frame_label_widget_notify (GtkFrame *frame);
62 static gint gimp_frame_get_indent (GtkWidget *widget);
63 static gint gimp_frame_get_label_spacing (GtkFrame *frame);
64
65
G_DEFINE_TYPE(GimpFrame,gimp_frame,GTK_TYPE_FRAME)66 G_DEFINE_TYPE (GimpFrame, gimp_frame, GTK_TYPE_FRAME)
67
68 #define parent_class gimp_frame_parent_class
69
70
71 static void
72 gimp_frame_class_init (GimpFrameClass *klass)
73 {
74 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
75
76 widget_class->size_request = gimp_frame_size_request;
77 widget_class->size_allocate = gimp_frame_size_allocate;
78 widget_class->style_set = gimp_frame_style_set;
79 widget_class->expose_event = gimp_frame_expose_event;
80
81 gtk_widget_class_install_style_property (widget_class,
82 g_param_spec_boolean ("label-bold",
83 "Label Bold",
84 "Whether the frame's label should be bold",
85 DEFAULT_LABEL_BOLD,
86 G_PARAM_READABLE));
87 gtk_widget_class_install_style_property (widget_class,
88 g_param_spec_int ("label-spacing",
89 "Label Spacing",
90 "The spacing between the label and the frame content",
91 0,
92 G_MAXINT,
93 DEFAULT_LABEL_SPACING,
94 G_PARAM_READABLE));
95 }
96
97
98 static void
gimp_frame_init(GimpFrame * frame)99 gimp_frame_init (GimpFrame *frame)
100 {
101 g_signal_connect (frame, "notify::label-widget",
102 G_CALLBACK (gimp_frame_label_widget_notify),
103 NULL);
104 }
105
106 static void
gimp_frame_size_request(GtkWidget * widget,GtkRequisition * requisition)107 gimp_frame_size_request (GtkWidget *widget,
108 GtkRequisition *requisition)
109 {
110 GtkFrame *frame = GTK_FRAME (widget);
111 GtkWidget *label_widget = gtk_frame_get_label_widget (frame);
112 GtkWidget *child = gtk_bin_get_child (GTK_BIN (widget));
113 GtkRequisition child_requisition;
114 gint border_width;
115
116 if (label_widget && gtk_widget_get_visible (label_widget))
117 {
118 gtk_widget_size_request (label_widget, requisition);
119 }
120 else
121 {
122 requisition->width = 0;
123 requisition->height = 0;
124 }
125
126 requisition->height += gimp_frame_get_label_spacing (frame);
127
128 if (child && gtk_widget_get_visible (child))
129 {
130 gint indent = gimp_frame_get_indent (widget);
131
132 gtk_widget_size_request (child, &child_requisition);
133
134 requisition->width = MAX (requisition->width,
135 child_requisition.width + indent);
136 requisition->height += child_requisition.height;
137 }
138
139 border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
140
141 requisition->width += 2 * border_width;
142 requisition->height += 2 * border_width;
143 }
144
145 static void
gimp_frame_size_allocate(GtkWidget * widget,GtkAllocation * allocation)146 gimp_frame_size_allocate (GtkWidget *widget,
147 GtkAllocation *allocation)
148 {
149 GtkFrame *frame = GTK_FRAME (widget);
150 GtkWidget *label_widget = gtk_frame_get_label_widget (frame);
151 GtkWidget *child = gtk_bin_get_child (GTK_BIN (widget));
152 GtkAllocation child_allocation;
153
154 /* must not chain up here */
155 gtk_widget_set_allocation (widget, allocation);
156
157 gimp_frame_child_allocate (frame, &child_allocation);
158
159 if (child && gtk_widget_get_visible (child))
160 gtk_widget_size_allocate (child, &child_allocation);
161
162 if (label_widget && gtk_widget_get_visible (label_widget))
163 {
164 GtkAllocation label_allocation;
165 GtkRequisition label_requisition;
166 gint border_width;
167
168 border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
169
170 gtk_widget_get_child_requisition (label_widget, &label_requisition);
171
172 label_allocation.x = allocation->x + border_width;
173 label_allocation.y = allocation->y + border_width;
174 label_allocation.width = MAX (label_requisition.width,
175 allocation->width - 2 * border_width);
176 label_allocation.height = label_requisition.height;
177
178 gtk_widget_size_allocate (label_widget, &label_allocation);
179 }
180 }
181
182 static void
gimp_frame_child_allocate(GtkFrame * frame,GtkAllocation * child_allocation)183 gimp_frame_child_allocate (GtkFrame *frame,
184 GtkAllocation *child_allocation)
185 {
186 GtkWidget *widget = GTK_WIDGET (frame);
187 GtkWidget *label_widget = gtk_frame_get_label_widget (frame);
188 GtkAllocation allocation;
189 gint border_width;
190 gint spacing = 0;
191 gint indent = gimp_frame_get_indent (widget);
192
193 gtk_widget_get_allocation (widget, &allocation);
194
195 border_width = gtk_container_get_border_width (GTK_CONTAINER (frame));
196
197 if (label_widget && gtk_widget_get_visible (label_widget))
198 {
199 GtkRequisition child_requisition;
200
201 gtk_widget_get_child_requisition (label_widget, &child_requisition);
202 spacing += child_requisition.height;
203 }
204
205 spacing += gimp_frame_get_label_spacing (frame);
206
207 if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
208 child_allocation->x = border_width + indent;
209 else
210 child_allocation->x = border_width;
211
212 child_allocation->y = border_width + spacing;
213 child_allocation->width = MAX (1,
214 allocation.width - 2 * border_width - indent);
215 child_allocation->height = MAX (1,
216 allocation.height -
217 child_allocation->y - border_width);
218
219 child_allocation->x += allocation.x;
220 child_allocation->y += allocation.y;
221 }
222
223 static void
gimp_frame_style_set(GtkWidget * widget,GtkStyle * previous)224 gimp_frame_style_set (GtkWidget *widget,
225 GtkStyle *previous)
226 {
227 /* font changes invalidate the indentation */
228 g_object_set_data (G_OBJECT (widget), GIMP_FRAME_INDENT_KEY, NULL);
229
230 /* for "label_bold" */
231 gimp_frame_label_widget_notify (GTK_FRAME (widget));
232 }
233
234 static gboolean
gimp_frame_expose_event(GtkWidget * widget,GdkEventExpose * event)235 gimp_frame_expose_event (GtkWidget *widget,
236 GdkEventExpose *event)
237 {
238 if (gtk_widget_is_drawable (widget))
239 {
240 GtkWidgetClass *widget_class = g_type_class_peek_parent (parent_class);
241
242 return widget_class->expose_event (widget, event);
243 }
244
245 return FALSE;
246 }
247
248 static void
gimp_frame_label_widget_notify(GtkFrame * frame)249 gimp_frame_label_widget_notify (GtkFrame *frame)
250 {
251 GtkWidget *label_widget = gtk_frame_get_label_widget (frame);
252
253 if (label_widget)
254 {
255 GtkLabel *label = NULL;
256
257 if (GTK_IS_LABEL (label_widget))
258 {
259 gfloat xalign, yalign;
260
261 label = GTK_LABEL (label_widget);
262
263 gtk_frame_get_label_align (frame, &xalign, &yalign);
264 gtk_label_set_xalign (GTK_LABEL (label), xalign);
265 gtk_label_set_yalign (GTK_LABEL (label), yalign);
266 }
267 else if (GTK_IS_BIN (label_widget))
268 {
269 GtkWidget *child = gtk_bin_get_child (GTK_BIN (label_widget));
270
271 if (GTK_IS_LABEL (child))
272 label = GTK_LABEL (child);
273 }
274
275 if (label)
276 {
277 PangoAttrList *attrs = pango_attr_list_new ();
278 PangoAttribute *attr;
279 gboolean bold;
280
281 gtk_widget_style_get (GTK_WIDGET (frame), "label_bold", &bold, NULL);
282
283 attr = pango_attr_weight_new (bold ?
284 PANGO_WEIGHT_BOLD :
285 PANGO_WEIGHT_NORMAL);
286 attr->start_index = 0;
287 attr->end_index = -1;
288 pango_attr_list_insert (attrs, attr);
289
290 gtk_label_set_attributes (label, attrs);
291
292 pango_attr_list_unref (attrs);
293 }
294 }
295 }
296
297 static gint
gimp_frame_get_indent(GtkWidget * widget)298 gimp_frame_get_indent (GtkWidget *widget)
299 {
300 gint width = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget),
301 GIMP_FRAME_INDENT_KEY));
302
303 if (! width)
304 {
305 PangoLayout *layout;
306
307 /* the HIG suggests to use four spaces so do just that */
308 layout = gtk_widget_create_pango_layout (widget, " ");
309 pango_layout_get_pixel_size (layout, &width, NULL);
310 g_object_unref (layout);
311
312 g_object_set_data (G_OBJECT (widget),
313 GIMP_FRAME_INDENT_KEY, GINT_TO_POINTER (width));
314 }
315
316 return width;
317 }
318
319 static gint
gimp_frame_get_label_spacing(GtkFrame * frame)320 gimp_frame_get_label_spacing (GtkFrame *frame)
321 {
322 GtkWidget *label_widget = gtk_frame_get_label_widget (frame);
323 gint spacing = 0;
324
325 if ((label_widget && gtk_widget_get_visible (label_widget)) ||
326 (g_object_get_data (G_OBJECT (frame), GIMP_FRAME_IN_EXPANDER_KEY)))
327 {
328 gtk_widget_style_get (GTK_WIDGET (frame),
329 "label_spacing", &spacing,
330 NULL);
331 }
332
333 return spacing;
334 }
335
336 /**
337 * gimp_frame_new:
338 * @label: text to set as the frame's title label (or %NULL for no title)
339 *
340 * Creates a #GimpFrame widget. A #GimpFrame is a HIG-compliant
341 * variant of #GtkFrame. It doesn't render a frame at all but
342 * otherwise behaves like a frame. The frame's title is rendered in
343 * bold and the frame content is indented four spaces as suggested by
344 * the GNOME HIG (see https://developer.gnome.org/hig/stable/).
345 *
346 * Return value: a new #GimpFrame widget
347 *
348 * Since: 2.2
349 **/
350 GtkWidget *
gimp_frame_new(const gchar * label)351 gimp_frame_new (const gchar *label)
352 {
353 GtkWidget *frame;
354 gboolean expander = FALSE;
355
356 /* somewhat hackish, should perhaps be an object property of GimpFrame */
357 if (label && strcmp (label, "<expander>") == 0)
358 {
359 expander = TRUE;
360 label = NULL;
361 }
362
363 frame = g_object_new (GIMP_TYPE_FRAME,
364 "label", label,
365 NULL);
366
367 if (expander)
368 g_object_set_data (G_OBJECT (frame),
369 GIMP_FRAME_IN_EXPANDER_KEY, (gpointer) TRUE);
370
371 return frame;
372 }
373