1 /* dzl-shortcuts-group.c
2 *
3 * Copyright (C) 2015 Christian Hergert <christian@hergert.me>
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public License as
7 * published by the Free Software Foundation; either version 2 of the
8 * License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
14 *
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include <glib/gi18n.h>
20
21 #include "config.h"
22
23 #include "shortcuts/dzl-shortcuts-group.h"
24 #include "shortcuts/dzl-shortcuts-shortcut.h"
25
26 /**
27 * SECTION:dzl-shortcuts-group
28 * @Title: DzlShortcutsGroup
29 * @Short_description: Represents a group of shortcuts in a DzlShortcutsWindow
30 *
31 * A DzlShortcutsGroup represents a group of related keyboard shortcuts
32 * or gestures. The group has a title. It may optionally be associated with
33 * a view of the application, which can be used to show only relevant shortcuts
34 * depending on the application context.
35 *
36 * This widget is only meant to be used with #DzlShortcutsWindow.
37 */
38
39 struct _DzlShortcutsGroup
40 {
41 GtkBox parent_instance;
42
43 GtkLabel *title;
44 gchar *view;
45 guint height;
46
47 GtkSizeGroup *accel_size_group;
48 GtkSizeGroup *title_size_group;
49 };
50
51 struct _DzlShortcutsGroupClass
52 {
53 GtkBoxClass parent_class;
54 };
55
56 G_DEFINE_TYPE (DzlShortcutsGroup, dzl_shortcuts_group, GTK_TYPE_BOX)
57
58 enum {
59 PROP_0,
60 PROP_TITLE,
61 PROP_VIEW,
62 PROP_ACCEL_SIZE_GROUP,
63 PROP_TITLE_SIZE_GROUP,
64 PROP_HEIGHT,
65 LAST_PROP
66 };
67
68 static GParamSpec *properties[LAST_PROP];
69
70 static void
dzl_shortcuts_group_apply_accel_size_group(DzlShortcutsGroup * group,GtkWidget * child)71 dzl_shortcuts_group_apply_accel_size_group (DzlShortcutsGroup *group,
72 GtkWidget *child)
73 {
74 if (DZL_IS_SHORTCUTS_SHORTCUT (child))
75 g_object_set (child, "accel-size-group", group->accel_size_group, NULL);
76 }
77
78 static void
dzl_shortcuts_group_apply_title_size_group(DzlShortcutsGroup * group,GtkWidget * child)79 dzl_shortcuts_group_apply_title_size_group (DzlShortcutsGroup *group,
80 GtkWidget *child)
81 {
82 if (DZL_IS_SHORTCUTS_SHORTCUT (child))
83 g_object_set (child, "title-size-group", group->title_size_group, NULL);
84 }
85
86 static void
dzl_shortcuts_group_set_accel_size_group(DzlShortcutsGroup * group,GtkSizeGroup * size_group)87 dzl_shortcuts_group_set_accel_size_group (DzlShortcutsGroup *group,
88 GtkSizeGroup *size_group)
89 {
90 GList *children, *l;
91
92 g_set_object (&group->accel_size_group, size_group);
93
94 children = gtk_container_get_children (GTK_CONTAINER (group));
95 for (l = children; l; l = l->next)
96 dzl_shortcuts_group_apply_accel_size_group (group, GTK_WIDGET (l->data));
97 g_list_free (children);
98 }
99
100 static void
dzl_shortcuts_group_set_title_size_group(DzlShortcutsGroup * group,GtkSizeGroup * size_group)101 dzl_shortcuts_group_set_title_size_group (DzlShortcutsGroup *group,
102 GtkSizeGroup *size_group)
103 {
104 GList *children, *l;
105
106 g_set_object (&group->title_size_group, size_group);
107
108 children = gtk_container_get_children (GTK_CONTAINER (group));
109 for (l = children; l; l = l->next)
110 dzl_shortcuts_group_apply_title_size_group (group, GTK_WIDGET (l->data));
111 g_list_free (children);
112 }
113
114 static guint
dzl_shortcuts_group_get_height(DzlShortcutsGroup * group)115 dzl_shortcuts_group_get_height (DzlShortcutsGroup *group)
116 {
117 GList *children, *l;
118 guint height;
119
120 height = 1;
121
122 children = gtk_container_get_children (GTK_CONTAINER (group));
123 for (l = children; l; l = l->next)
124 {
125 GtkWidget *child = l->data;
126
127 if (!gtk_widget_get_visible (child))
128 continue;
129 else if (DZL_IS_SHORTCUTS_SHORTCUT (child))
130 height += 1;
131 }
132 g_list_free (children);
133
134 return height;
135 }
136
137 static void
dzl_shortcuts_group_add(GtkContainer * container,GtkWidget * widget)138 dzl_shortcuts_group_add (GtkContainer *container,
139 GtkWidget *widget)
140 {
141 if (DZL_IS_SHORTCUTS_SHORTCUT (widget))
142 {
143 GTK_CONTAINER_CLASS (dzl_shortcuts_group_parent_class)->add (container, widget);
144 dzl_shortcuts_group_apply_accel_size_group (DZL_SHORTCUTS_GROUP (container), widget);
145 dzl_shortcuts_group_apply_title_size_group (DZL_SHORTCUTS_GROUP (container), widget);
146 }
147 else
148 g_warning ("Can't add children of type %s to %s",
149 G_OBJECT_TYPE_NAME (widget),
150 G_OBJECT_TYPE_NAME (container));
151 }
152
153 typedef struct {
154 GtkCallback callback;
155 gpointer data;
156 gboolean include_internal;
157 } CallbackData;
158
159 static void
forall_cb(GtkWidget * widget,gpointer data)160 forall_cb (GtkWidget *widget, gpointer data)
161 {
162 DzlShortcutsGroup *self;
163 CallbackData *cbdata = data;
164
165 self = DZL_SHORTCUTS_GROUP (gtk_widget_get_parent (widget));
166 if (cbdata->include_internal || widget != (GtkWidget*)self->title)
167 cbdata->callback (widget, cbdata->data);
168 }
169
170 static void
dzl_shortcuts_group_forall(GtkContainer * container,gboolean include_internal,GtkCallback callback,gpointer callback_data)171 dzl_shortcuts_group_forall (GtkContainer *container,
172 gboolean include_internal,
173 GtkCallback callback,
174 gpointer callback_data)
175 {
176 CallbackData cbdata;
177
178 cbdata.include_internal = include_internal;
179 cbdata.callback = callback;
180 cbdata.data = callback_data;
181
182 GTK_CONTAINER_CLASS (dzl_shortcuts_group_parent_class)->forall (container, include_internal, forall_cb, &cbdata);
183 }
184
185 static void
dzl_shortcuts_group_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)186 dzl_shortcuts_group_get_property (GObject *object,
187 guint prop_id,
188 GValue *value,
189 GParamSpec *pspec)
190 {
191 DzlShortcutsGroup *self = DZL_SHORTCUTS_GROUP (object);
192
193 switch (prop_id)
194 {
195 case PROP_TITLE:
196 g_value_set_string (value, gtk_label_get_label (self->title));
197 break;
198
199 case PROP_VIEW:
200 g_value_set_string (value, self->view);
201 break;
202
203 case PROP_HEIGHT:
204 g_value_set_uint (value, dzl_shortcuts_group_get_height (self));
205 break;
206
207 default:
208 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
209 }
210 }
211
212 static void
dzl_shortcuts_group_direction_changed(GtkWidget * widget,GtkTextDirection previous_dir)213 dzl_shortcuts_group_direction_changed (GtkWidget *widget,
214 GtkTextDirection previous_dir)
215 {
216 GTK_WIDGET_CLASS (dzl_shortcuts_group_parent_class)->direction_changed (widget, previous_dir);
217 g_object_notify (G_OBJECT (widget), "height");
218 }
219
220 static void
dzl_shortcuts_group_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)221 dzl_shortcuts_group_set_property (GObject *object,
222 guint prop_id,
223 const GValue *value,
224 GParamSpec *pspec)
225 {
226 DzlShortcutsGroup *self = DZL_SHORTCUTS_GROUP (object);
227
228 switch (prop_id)
229 {
230 case PROP_TITLE:
231 gtk_label_set_label (self->title, g_value_get_string (value));
232 break;
233
234 case PROP_VIEW:
235 g_free (self->view);
236 self->view = g_value_dup_string (value);
237 break;
238
239 case PROP_ACCEL_SIZE_GROUP:
240 dzl_shortcuts_group_set_accel_size_group (self, GTK_SIZE_GROUP (g_value_get_object (value)));
241 break;
242
243 case PROP_TITLE_SIZE_GROUP:
244 dzl_shortcuts_group_set_title_size_group (self, GTK_SIZE_GROUP (g_value_get_object (value)));
245 break;
246
247 default:
248 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
249 }
250 }
251
252 static void
dzl_shortcuts_group_finalize(GObject * object)253 dzl_shortcuts_group_finalize (GObject *object)
254 {
255 DzlShortcutsGroup *self = DZL_SHORTCUTS_GROUP (object);
256
257 g_free (self->view);
258 g_set_object (&self->accel_size_group, NULL);
259 g_set_object (&self->title_size_group, NULL);
260
261 G_OBJECT_CLASS (dzl_shortcuts_group_parent_class)->finalize (object);
262 }
263
264 static void
dzl_shortcuts_group_dispose(GObject * object)265 dzl_shortcuts_group_dispose (GObject *object)
266 {
267 DzlShortcutsGroup *self = DZL_SHORTCUTS_GROUP (object);
268
269 /*
270 * Since we overload forall(), the inherited destroy() won't work as normal.
271 * Remove internal widgets ourself.
272 */
273 if (self->title)
274 {
275 gtk_widget_destroy (GTK_WIDGET (self->title));
276 self->title = NULL;
277 }
278
279 G_OBJECT_CLASS (dzl_shortcuts_group_parent_class)->dispose (object);
280 }
281
282 static void
dzl_shortcuts_group_class_init(DzlShortcutsGroupClass * klass)283 dzl_shortcuts_group_class_init (DzlShortcutsGroupClass *klass)
284 {
285 GObjectClass *object_class = G_OBJECT_CLASS (klass);
286 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
287 GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
288
289 object_class->finalize = dzl_shortcuts_group_finalize;
290 object_class->get_property = dzl_shortcuts_group_get_property;
291 object_class->set_property = dzl_shortcuts_group_set_property;
292 object_class->dispose = dzl_shortcuts_group_dispose;
293
294 widget_class->direction_changed = dzl_shortcuts_group_direction_changed;
295 container_class->add = dzl_shortcuts_group_add;
296 container_class->forall = dzl_shortcuts_group_forall;
297
298 /**
299 * DzlShortcutsGroup:title:
300 *
301 * The title for this group of shortcuts.
302 */
303 properties[PROP_TITLE] =
304 g_param_spec_string ("title", _("Title"), _("Title"),
305 "",
306 (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
307
308 /**
309 * DzlShortcutsGroup:view:
310 *
311 * An optional view that the shortcuts in this group are relevant for.
312 * The group will be hidden if the #DzlShortcutsWindow:view-name property
313 * does not match the view of this group.
314 *
315 * Set this to %NULL to make the group always visible.
316 */
317 properties[PROP_VIEW] =
318 g_param_spec_string ("view", _("View"), _("View"),
319 NULL,
320 (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
321
322 /**
323 * DzlShortcutsGroup:accel-size-group:
324 *
325 * The size group for the accelerator portion of shortcuts in this group.
326 *
327 * This is used internally by GTK+, and must not be modified by applications.
328 */
329 properties[PROP_ACCEL_SIZE_GROUP] =
330 g_param_spec_object ("accel-size-group",
331 _("Accelerator Size Group"),
332 _("Accelerator Size Group"),
333 GTK_TYPE_SIZE_GROUP,
334 (G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
335
336 /**
337 * DzlShortcutsGroup:title-size-group:
338 *
339 * The size group for the textual portion of shortcuts in this group.
340 *
341 * This is used internally by GTK+, and must not be modified by applications.
342 */
343 properties[PROP_TITLE_SIZE_GROUP] =
344 g_param_spec_object ("title-size-group",
345 _("Title Size Group"),
346 _("Title Size Group"),
347 GTK_TYPE_SIZE_GROUP,
348 (G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
349
350 /**
351 * DzlShortcutsGroup:height:
352 *
353 * A rough measure for the number of lines in this group.
354 *
355 * This is used internally by GTK+, and is not useful for applications.
356 */
357 properties[PROP_HEIGHT] =
358 g_param_spec_uint ("height", _("Height"), _("Height"),
359 0, G_MAXUINT, 1,
360 (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
361
362 g_object_class_install_properties (object_class, LAST_PROP, properties);
363 }
364
365 static void
dzl_shortcuts_group_init(DzlShortcutsGroup * self)366 dzl_shortcuts_group_init (DzlShortcutsGroup *self)
367 {
368 PangoAttrList *attrs;
369
370 gtk_orientable_set_orientation (GTK_ORIENTABLE (self), GTK_ORIENTATION_VERTICAL);
371 gtk_box_set_spacing (GTK_BOX (self), 10);
372
373 attrs = pango_attr_list_new ();
374 pango_attr_list_insert (attrs, pango_attr_weight_new (PANGO_WEIGHT_BOLD));
375 self->title = g_object_new (GTK_TYPE_LABEL,
376 "attributes", attrs,
377 "visible", TRUE,
378 "xalign", 0.0f,
379 NULL);
380 pango_attr_list_unref (attrs);
381
382 GTK_CONTAINER_CLASS (dzl_shortcuts_group_parent_class)->add (GTK_CONTAINER (self), GTK_WIDGET (self->title));
383 }
384