1 | /* gtkshortcutsgroup.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 "config.h" |
20 | |
21 | #include "gtkshortcutsgroup.h" |
22 | |
23 | #include "gtkbox.h" |
24 | #include "gtkbuildable.h" |
25 | #include "gtkintl.h" |
26 | #include "gtklabel.h" |
27 | #include "gtkorientable.h" |
28 | #include "gtkprivate.h" |
29 | #include "gtkshortcutsshortcut.h" |
30 | #include "gtksizegroup.h" |
31 | |
32 | /** |
33 | * GtkShortcutsGroup: |
34 | * |
35 | * A `GtkShortcutsGroup` represents a group of related keyboard shortcuts |
36 | * or gestures. |
37 | * |
38 | * The group has a title. It may optionally be associated with a view |
39 | * of the application, which can be used to show only relevant shortcuts |
40 | * depending on the application context. |
41 | * |
42 | * This widget is only meant to be used with [class@Gtk.ShortcutsWindow]. |
43 | */ |
44 | |
45 | struct _GtkShortcutsGroup |
46 | { |
47 | GtkBox parent_instance; |
48 | |
49 | GtkLabel *title; |
50 | char *view; |
51 | guint height; |
52 | |
53 | GtkSizeGroup *accel_size_group; |
54 | GtkSizeGroup *title_size_group; |
55 | }; |
56 | |
57 | struct _GtkShortcutsGroupClass |
58 | { |
59 | GtkBoxClass parent_class; |
60 | }; |
61 | |
62 | static void gtk_shortcuts_group_buildable_iface_init (GtkBuildableIface *iface); |
63 | |
64 | G_DEFINE_TYPE_WITH_CODE (GtkShortcutsGroup, gtk_shortcuts_group, GTK_TYPE_BOX, |
65 | G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, |
66 | gtk_shortcuts_group_buildable_iface_init)) |
67 | |
68 | enum { |
69 | PROP_0, |
70 | PROP_TITLE, |
71 | PROP_VIEW, |
72 | PROP_ACCEL_SIZE_GROUP, |
73 | PROP_TITLE_SIZE_GROUP, |
74 | PROP_HEIGHT, |
75 | LAST_PROP |
76 | }; |
77 | |
78 | static GParamSpec *properties[LAST_PROP]; |
79 | |
80 | static void |
81 | gtk_shortcuts_group_apply_accel_size_group (GtkShortcutsGroup *group, |
82 | GtkWidget *child) |
83 | { |
84 | if (GTK_IS_SHORTCUTS_SHORTCUT (child)) |
85 | g_object_set (object: child, first_property_name: "accel-size-group" , group->accel_size_group, NULL); |
86 | } |
87 | |
88 | static void |
89 | gtk_shortcuts_group_apply_title_size_group (GtkShortcutsGroup *group, |
90 | GtkWidget *child) |
91 | { |
92 | if (GTK_IS_SHORTCUTS_SHORTCUT (child)) |
93 | g_object_set (object: child, first_property_name: "title-size-group" , group->title_size_group, NULL); |
94 | } |
95 | |
96 | static void |
97 | gtk_shortcuts_group_set_accel_size_group (GtkShortcutsGroup *group, |
98 | GtkSizeGroup *size_group) |
99 | { |
100 | GtkWidget *child; |
101 | |
102 | g_set_object (&group->accel_size_group, size_group); |
103 | |
104 | for (child = gtk_widget_get_first_child (GTK_WIDGET (group)); |
105 | child != NULL; |
106 | child = gtk_widget_get_next_sibling (widget: child)) |
107 | gtk_shortcuts_group_apply_accel_size_group (group, child); |
108 | } |
109 | |
110 | static void |
111 | gtk_shortcuts_group_set_title_size_group (GtkShortcutsGroup *group, |
112 | GtkSizeGroup *size_group) |
113 | { |
114 | GtkWidget *child; |
115 | |
116 | g_set_object (&group->title_size_group, size_group); |
117 | |
118 | for (child = gtk_widget_get_first_child (GTK_WIDGET (group)); |
119 | child != NULL; |
120 | child = gtk_widget_get_next_sibling (widget: child)) |
121 | gtk_shortcuts_group_apply_title_size_group (group, child); |
122 | } |
123 | |
124 | static guint |
125 | gtk_shortcuts_group_get_height (GtkShortcutsGroup *group) |
126 | { |
127 | GtkWidget *child; |
128 | guint height; |
129 | |
130 | height = 1; |
131 | |
132 | for (child = gtk_widget_get_first_child (GTK_WIDGET (group)); |
133 | child != NULL; |
134 | child = gtk_widget_get_next_sibling (widget: child)) |
135 | { |
136 | if (!gtk_widget_get_visible (widget: child)) |
137 | continue; |
138 | else if (GTK_IS_SHORTCUTS_SHORTCUT (child)) |
139 | height += 1; |
140 | } |
141 | |
142 | return height; |
143 | } |
144 | |
145 | static GtkBuildableIface *parent_buildable_iface; |
146 | |
147 | static void |
148 | gtk_shortcuts_group_buildable_add_child (GtkBuildable *buildable, |
149 | GtkBuilder *builder, |
150 | GObject *child, |
151 | const char *type) |
152 | { |
153 | if (GTK_IS_SHORTCUTS_SHORTCUT (child)) |
154 | { |
155 | gtk_box_append (GTK_BOX (buildable), GTK_WIDGET (child)); |
156 | gtk_shortcuts_group_apply_accel_size_group (GTK_SHORTCUTS_GROUP (buildable), GTK_WIDGET (child)); |
157 | gtk_shortcuts_group_apply_title_size_group (GTK_SHORTCUTS_GROUP (buildable), GTK_WIDGET (child)); |
158 | } |
159 | else |
160 | parent_buildable_iface->add_child (buildable, builder, child, type); |
161 | } |
162 | |
163 | static void |
164 | gtk_shortcuts_group_buildable_iface_init (GtkBuildableIface *iface) |
165 | { |
166 | parent_buildable_iface = g_type_interface_peek_parent (g_iface: iface); |
167 | |
168 | iface->add_child = gtk_shortcuts_group_buildable_add_child; |
169 | } |
170 | |
171 | static void |
172 | gtk_shortcuts_group_get_property (GObject *object, |
173 | guint prop_id, |
174 | GValue *value, |
175 | GParamSpec *pspec) |
176 | { |
177 | GtkShortcutsGroup *self = GTK_SHORTCUTS_GROUP (object); |
178 | |
179 | switch (prop_id) |
180 | { |
181 | case PROP_TITLE: |
182 | g_value_set_string (value, v_string: gtk_label_get_label (self: self->title)); |
183 | break; |
184 | |
185 | case PROP_VIEW: |
186 | g_value_set_string (value, v_string: self->view); |
187 | break; |
188 | |
189 | case PROP_HEIGHT: |
190 | g_value_set_uint (value, v_uint: gtk_shortcuts_group_get_height (group: self)); |
191 | break; |
192 | |
193 | default: |
194 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
195 | } |
196 | } |
197 | |
198 | static void |
199 | gtk_shortcuts_group_direction_changed (GtkWidget *widget, |
200 | GtkTextDirection previous_dir) |
201 | { |
202 | GTK_WIDGET_CLASS (gtk_shortcuts_group_parent_class)->direction_changed (widget, previous_dir); |
203 | g_object_notify (G_OBJECT (widget), property_name: "height" ); |
204 | } |
205 | |
206 | static void |
207 | gtk_shortcuts_group_set_property (GObject *object, |
208 | guint prop_id, |
209 | const GValue *value, |
210 | GParamSpec *pspec) |
211 | { |
212 | GtkShortcutsGroup *self = GTK_SHORTCUTS_GROUP (object); |
213 | |
214 | switch (prop_id) |
215 | { |
216 | case PROP_TITLE: |
217 | gtk_label_set_label (self: self->title, str: g_value_get_string (value)); |
218 | break; |
219 | |
220 | case PROP_VIEW: |
221 | g_free (mem: self->view); |
222 | self->view = g_value_dup_string (value); |
223 | break; |
224 | |
225 | case PROP_ACCEL_SIZE_GROUP: |
226 | gtk_shortcuts_group_set_accel_size_group (group: self, GTK_SIZE_GROUP (g_value_get_object (value))); |
227 | break; |
228 | |
229 | case PROP_TITLE_SIZE_GROUP: |
230 | gtk_shortcuts_group_set_title_size_group (group: self, GTK_SIZE_GROUP (g_value_get_object (value))); |
231 | break; |
232 | |
233 | default: |
234 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
235 | } |
236 | } |
237 | |
238 | static void |
239 | gtk_shortcuts_group_finalize (GObject *object) |
240 | { |
241 | GtkShortcutsGroup *self = GTK_SHORTCUTS_GROUP (object); |
242 | |
243 | g_free (mem: self->view); |
244 | g_set_object (&self->accel_size_group, NULL); |
245 | g_set_object (&self->title_size_group, NULL); |
246 | |
247 | G_OBJECT_CLASS (gtk_shortcuts_group_parent_class)->finalize (object); |
248 | } |
249 | |
250 | static void |
251 | gtk_shortcuts_group_dispose (GObject *object) |
252 | { |
253 | GtkShortcutsGroup *self = GTK_SHORTCUTS_GROUP (object); |
254 | |
255 | g_clear_pointer ((GtkWidget **)&self->title, gtk_widget_unparent); |
256 | |
257 | G_OBJECT_CLASS (gtk_shortcuts_group_parent_class)->dispose (object); |
258 | } |
259 | |
260 | static void |
261 | gtk_shortcuts_group_class_init (GtkShortcutsGroupClass *klass) |
262 | { |
263 | GObjectClass *object_class = G_OBJECT_CLASS (klass); |
264 | GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); |
265 | |
266 | object_class->finalize = gtk_shortcuts_group_finalize; |
267 | object_class->get_property = gtk_shortcuts_group_get_property; |
268 | object_class->set_property = gtk_shortcuts_group_set_property; |
269 | object_class->dispose = gtk_shortcuts_group_dispose; |
270 | |
271 | widget_class->direction_changed = gtk_shortcuts_group_direction_changed; |
272 | |
273 | /** |
274 | * GtkShortcutsGroup:title: |
275 | * |
276 | * The title for this group of shortcuts. |
277 | */ |
278 | properties[PROP_TITLE] = |
279 | g_param_spec_string (name: "title" , P_("Title" ), P_("Title" ), |
280 | default_value: "" , |
281 | flags: (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); |
282 | |
283 | /** |
284 | * GtkShortcutsGroup:view: |
285 | * |
286 | * An optional view that the shortcuts in this group are relevant for. |
287 | * |
288 | * The group will be hidden if the [property@Gtk.ShortcutsWindow:view-name] |
289 | * property does not match the view of this group. |
290 | * |
291 | * Set this to %NULL to make the group always visible. |
292 | */ |
293 | properties[PROP_VIEW] = |
294 | g_param_spec_string (name: "view" , P_("View" ), P_("View" ), |
295 | NULL, |
296 | flags: (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); |
297 | |
298 | /** |
299 | * GtkShortcutsGroup:accel-size-group: |
300 | * |
301 | * The size group for the accelerator portion of shortcuts in this group. |
302 | * |
303 | * This is used internally by GTK, and must not be modified by applications. |
304 | */ |
305 | properties[PROP_ACCEL_SIZE_GROUP] = |
306 | g_param_spec_object (name: "accel-size-group" , |
307 | P_("Accelerator Size Group" ), |
308 | P_("Accelerator Size Group" ), |
309 | GTK_TYPE_SIZE_GROUP, |
310 | flags: (G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS)); |
311 | |
312 | /** |
313 | * GtkShortcutsGroup:title-size-group: |
314 | * |
315 | * The size group for the textual portion of shortcuts in this group. |
316 | * |
317 | * This is used internally by GTK, and must not be modified by applications. |
318 | */ |
319 | properties[PROP_TITLE_SIZE_GROUP] = |
320 | g_param_spec_object (name: "title-size-group" , |
321 | P_("Title Size Group" ), |
322 | P_("Title Size Group" ), |
323 | GTK_TYPE_SIZE_GROUP, |
324 | flags: (G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS)); |
325 | |
326 | /** |
327 | * GtkShortcutsGroup:height: |
328 | * |
329 | * A rough measure for the number of lines in this group. |
330 | * |
331 | * This is used internally by GTK, and is not useful for applications. |
332 | */ |
333 | properties[PROP_HEIGHT] = |
334 | g_param_spec_uint (name: "height" , P_("Height" ), P_("Height" ), |
335 | minimum: 0, G_MAXUINT, default_value: 1, |
336 | flags: (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); |
337 | |
338 | g_object_class_install_properties (oclass: object_class, n_pspecs: LAST_PROP, pspecs: properties); |
339 | |
340 | gtk_widget_class_set_css_name (widget_class, I_("shortcuts-group" )); |
341 | } |
342 | |
343 | static void |
344 | gtk_shortcuts_group_init (GtkShortcutsGroup *self) |
345 | { |
346 | PangoAttrList *attrs; |
347 | |
348 | gtk_orientable_set_orientation (GTK_ORIENTABLE (self), orientation: GTK_ORIENTATION_VERTICAL); |
349 | gtk_box_set_spacing (GTK_BOX (self), spacing: 10); |
350 | |
351 | attrs = pango_attr_list_new (); |
352 | pango_attr_list_insert (list: attrs, attr: pango_attr_weight_new (weight: PANGO_WEIGHT_BOLD)); |
353 | self->title = g_object_new (GTK_TYPE_LABEL, |
354 | first_property_name: "attributes" , attrs, |
355 | "visible" , TRUE, |
356 | "xalign" , 0.0f, |
357 | NULL); |
358 | pango_attr_list_unref (list: attrs); |
359 | |
360 | gtk_box_append (GTK_BOX (self), GTK_WIDGET (self->title)); |
361 | } |
362 | |