1 /* dzl-shortcuts-shortcut.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-shortcut-label.h"
24 #include "shortcuts/dzl-shortcuts-shortcut.h"
25 #include "shortcuts/dzl-shortcuts-window-private.h"
26 
27 /**
28  * SECTION:dzl-shortcuts-shortcut
29  * @Title: DzlShortcutsShortcut
30  * @Short_description: Represents a keyboard shortcut in a DzlShortcutsWindow
31  *
32  * A DzlShortcutsShortcut represents a single keyboard shortcut or gesture
33  * with a short text. This widget is only meant to be used with #DzlShortcutsWindow.
34  */
35 
36 struct _DzlShortcutsShortcut
37 {
38   GtkBox            parent_instance;
39 
40   GtkImage         *image;
41   DzlShortcutLabel *accelerator;
42   GtkLabel         *title;
43   GtkLabel         *subtitle;
44   GtkLabel         *title_box;
45 
46   GtkSizeGroup     *accel_size_group;
47   GtkSizeGroup     *title_size_group;
48 
49   gboolean          subtitle_set;
50   gboolean          icon_set;
51   GtkTextDirection  direction;
52   gchar            *action_name;
53   GtkShortcutType   shortcut_type;
54 };
55 
56 struct _DzlShortcutsShortcutClass
57 {
58   GtkBoxClass parent_class;
59 };
60 
61 G_DEFINE_TYPE (DzlShortcutsShortcut, dzl_shortcuts_shortcut, GTK_TYPE_BOX)
62 
63 enum {
64   PROP_0,
65   PROP_ACCELERATOR,
66   PROP_ICON,
67   PROP_ICON_SET,
68   PROP_TITLE,
69   PROP_SUBTITLE,
70   PROP_SUBTITLE_SET,
71   PROP_ACCEL_SIZE_GROUP,
72   PROP_TITLE_SIZE_GROUP,
73   PROP_DIRECTION,
74   PROP_SHORTCUT_TYPE,
75   PROP_ACTION_NAME,
76   LAST_PROP
77 };
78 
79 static GParamSpec *properties[LAST_PROP];
80 
81 static void
dzl_shortcuts_shortcut_set_accelerator(DzlShortcutsShortcut * self,const gchar * accelerator)82 dzl_shortcuts_shortcut_set_accelerator (DzlShortcutsShortcut *self,
83                                         const gchar          *accelerator)
84 {
85   dzl_shortcut_label_set_accelerator (self->accelerator, accelerator);
86 }
87 
88 static void
dzl_shortcuts_shortcut_set_accel_size_group(DzlShortcutsShortcut * self,GtkSizeGroup * group)89 dzl_shortcuts_shortcut_set_accel_size_group (DzlShortcutsShortcut *self,
90                                              GtkSizeGroup         *group)
91 {
92   if (self->accel_size_group)
93     {
94       gtk_size_group_remove_widget (self->accel_size_group, GTK_WIDGET (self->accelerator));
95       gtk_size_group_remove_widget (self->accel_size_group, GTK_WIDGET (self->image));
96     }
97 
98   if (group)
99     {
100       gtk_size_group_add_widget (group, GTK_WIDGET (self->accelerator));
101       gtk_size_group_add_widget (group, GTK_WIDGET (self->image));
102     }
103 
104   g_set_object (&self->accel_size_group, group);
105 }
106 
107 static void
dzl_shortcuts_shortcut_set_title_size_group(DzlShortcutsShortcut * self,GtkSizeGroup * group)108 dzl_shortcuts_shortcut_set_title_size_group (DzlShortcutsShortcut *self,
109                                              GtkSizeGroup         *group)
110 {
111   if (self->title_size_group)
112     gtk_size_group_remove_widget (self->title_size_group, GTK_WIDGET (self->title_box));
113   if (group)
114     gtk_size_group_add_widget (group, GTK_WIDGET (self->title_box));
115 
116   g_set_object (&self->title_size_group, group);
117 }
118 
119 static void
update_subtitle_from_type(DzlShortcutsShortcut * self)120 update_subtitle_from_type (DzlShortcutsShortcut *self)
121 {
122   const gchar *subtitle;
123 
124   if (self->subtitle_set)
125     return;
126 
127   switch (self->shortcut_type)
128     {
129     case GTK_SHORTCUT_ACCELERATOR:
130     case GTK_SHORTCUT_GESTURE:
131       subtitle = NULL;
132       break;
133 
134     case GTK_SHORTCUT_GESTURE_PINCH:
135       subtitle = _("Two finger pinch");
136       break;
137 
138     case GTK_SHORTCUT_GESTURE_STRETCH:
139       subtitle = _("Two finger stretch");
140       break;
141 
142     case GTK_SHORTCUT_GESTURE_ROTATE_CLOCKWISE:
143       subtitle = _("Rotate clockwise");
144       break;
145 
146     case GTK_SHORTCUT_GESTURE_ROTATE_COUNTERCLOCKWISE:
147       subtitle = _("Rotate counterclockwise");
148       break;
149 
150     case GTK_SHORTCUT_GESTURE_TWO_FINGER_SWIPE_LEFT:
151       subtitle = _("Two finger swipe left");
152       break;
153 
154     case GTK_SHORTCUT_GESTURE_TWO_FINGER_SWIPE_RIGHT:
155       subtitle = _("Two finger swipe right");
156       break;
157 
158     default:
159       subtitle = NULL;
160       break;
161     }
162 
163   gtk_label_set_label (self->subtitle, subtitle);
164   gtk_widget_set_visible (GTK_WIDGET (self->subtitle), subtitle != NULL);
165   g_object_notify (G_OBJECT (self), "subtitle");
166 }
167 
168 static void
dzl_shortcuts_shortcut_set_subtitle_set(DzlShortcutsShortcut * self,gboolean subtitle_set)169 dzl_shortcuts_shortcut_set_subtitle_set (DzlShortcutsShortcut *self,
170                                          gboolean              subtitle_set)
171 {
172   if (self->subtitle_set != subtitle_set)
173     {
174       self->subtitle_set = subtitle_set;
175       g_object_notify (G_OBJECT (self), "subtitle-set");
176     }
177   update_subtitle_from_type (self);
178 }
179 
180 static void
dzl_shortcuts_shortcut_set_subtitle(DzlShortcutsShortcut * self,const gchar * subtitle)181 dzl_shortcuts_shortcut_set_subtitle (DzlShortcutsShortcut *self,
182                                      const gchar          *subtitle)
183 {
184   gtk_label_set_label (self->subtitle, subtitle);
185   gtk_widget_set_visible (GTK_WIDGET (self->subtitle), subtitle && subtitle[0]);
186   dzl_shortcuts_shortcut_set_subtitle_set (self, subtitle && subtitle[0]);
187 
188   g_object_notify (G_OBJECT (self), "subtitle");
189 }
190 
191 static void
update_icon_from_type(DzlShortcutsShortcut * self)192 update_icon_from_type (DzlShortcutsShortcut *self)
193 {
194   GIcon *icon;
195 
196   if (self->icon_set)
197     return;
198 
199   switch (self->shortcut_type)
200     {
201     case GTK_SHORTCUT_GESTURE_PINCH:
202       icon = g_themed_icon_new ("gesture-pinch-symbolic");
203       break;
204 
205     case GTK_SHORTCUT_GESTURE_STRETCH:
206       icon = g_themed_icon_new ("gesture-stretch-symbolic");
207       break;
208 
209     case GTK_SHORTCUT_GESTURE_ROTATE_CLOCKWISE:
210       icon = g_themed_icon_new ("gesture-rotate-clockwise-symbolic");
211       break;
212 
213     case GTK_SHORTCUT_GESTURE_ROTATE_COUNTERCLOCKWISE:
214       icon = g_themed_icon_new ("gesture-rotate-anticlockwise-symbolic");
215       break;
216 
217     case GTK_SHORTCUT_GESTURE_TWO_FINGER_SWIPE_LEFT:
218       icon = g_themed_icon_new ("gesture-two-finger-swipe-left-symbolic");
219       break;
220 
221     case GTK_SHORTCUT_GESTURE_TWO_FINGER_SWIPE_RIGHT:
222       icon = g_themed_icon_new ("gesture-two-finger-swipe-right-symbolic");
223       break;
224 
225     case GTK_SHORTCUT_ACCELERATOR:
226     case GTK_SHORTCUT_GESTURE:
227     default: ;
228       icon = NULL;
229       break;
230     }
231 
232   if (icon)
233     {
234       gtk_image_set_from_gicon (self->image, icon, GTK_ICON_SIZE_DIALOG);
235       gtk_image_set_pixel_size (self->image, 64);
236       g_object_unref (icon);
237     }
238 }
239 
240 static void
dzl_shortcuts_shortcut_set_icon_set(DzlShortcutsShortcut * self,gboolean icon_set)241 dzl_shortcuts_shortcut_set_icon_set (DzlShortcutsShortcut *self,
242                                      gboolean              icon_set)
243 {
244   if (self->icon_set != icon_set)
245     {
246       self->icon_set = icon_set;
247       g_object_notify (G_OBJECT (self), "icon-set");
248     }
249   update_icon_from_type (self);
250 }
251 
252 static void
dzl_shortcuts_shortcut_set_icon(DzlShortcutsShortcut * self,GIcon * gicon)253 dzl_shortcuts_shortcut_set_icon (DzlShortcutsShortcut *self,
254                                  GIcon                *gicon)
255 {
256   gtk_image_set_from_gicon (self->image, gicon, GTK_ICON_SIZE_DIALOG);
257   dzl_shortcuts_shortcut_set_icon_set (self, gicon != NULL);
258   g_object_notify (G_OBJECT (self), "icon");
259 }
260 
261 static void
update_visible_from_direction(DzlShortcutsShortcut * self)262 update_visible_from_direction (DzlShortcutsShortcut *self)
263 {
264   if (self->direction == GTK_TEXT_DIR_NONE ||
265       self->direction == gtk_widget_get_direction (GTK_WIDGET (self)))
266     {
267       gtk_widget_set_visible (GTK_WIDGET (self), TRUE);
268       /* When porting to gtk4, we'll have to update all this
269        * code anyway, so fine to disable warnings.
270        */
271       G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
272       gtk_widget_set_no_show_all (GTK_WIDGET (self), FALSE);
273       G_GNUC_END_IGNORE_DEPRECATIONS;
274     }
275   else
276     {
277       gtk_widget_set_visible (GTK_WIDGET (self), FALSE);
278       G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
279       gtk_widget_set_no_show_all (GTK_WIDGET (self), TRUE);
280       G_GNUC_END_IGNORE_DEPRECATIONS;
281     }
282 }
283 
284 static void
dzl_shortcuts_shortcut_set_direction(DzlShortcutsShortcut * self,GtkTextDirection direction)285 dzl_shortcuts_shortcut_set_direction (DzlShortcutsShortcut *self,
286                                       GtkTextDirection      direction)
287 {
288   if (self->direction == direction)
289     return;
290 
291   self->direction = direction;
292 
293   update_visible_from_direction (self);
294 
295   g_object_notify (G_OBJECT (self), "direction");
296 }
297 
298 static void
dzl_shortcuts_shortcut_direction_changed(GtkWidget * widget,GtkTextDirection previous_dir)299 dzl_shortcuts_shortcut_direction_changed (GtkWidget        *widget,
300                                           GtkTextDirection  previous_dir)
301 {
302   update_visible_from_direction (DZL_SHORTCUTS_SHORTCUT (widget));
303 
304   GTK_WIDGET_CLASS (dzl_shortcuts_shortcut_parent_class)->direction_changed (widget, previous_dir);
305 }
306 
307 static void
dzl_shortcuts_shortcut_set_type(DzlShortcutsShortcut * self,GtkShortcutType type)308 dzl_shortcuts_shortcut_set_type (DzlShortcutsShortcut *self,
309                                  GtkShortcutType       type)
310 {
311   if (self->shortcut_type == type)
312     return;
313 
314   self->shortcut_type = type;
315 
316   update_subtitle_from_type (self);
317   update_icon_from_type (self);
318 
319   gtk_widget_set_visible (GTK_WIDGET (self->accelerator), type == GTK_SHORTCUT_ACCELERATOR);
320   gtk_widget_set_visible (GTK_WIDGET (self->image), type != GTK_SHORTCUT_ACCELERATOR);
321 
322 
323   g_object_notify (G_OBJECT (self), "shortcut-type");
324 }
325 
326 static void
dzl_shortcuts_shortcut_set_action_name(DzlShortcutsShortcut * self,const gchar * action_name)327 dzl_shortcuts_shortcut_set_action_name (DzlShortcutsShortcut *self,
328                                         const gchar          *action_name)
329 {
330   g_free (self->action_name);
331   self->action_name = g_strdup (action_name);
332 
333   g_object_notify (G_OBJECT (self), "action-name");
334 }
335 
336 static void
dzl_shortcuts_shortcut_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)337 dzl_shortcuts_shortcut_get_property (GObject    *object,
338                                      guint       prop_id,
339                                      GValue     *value,
340                                      GParamSpec *pspec)
341 {
342   DzlShortcutsShortcut *self = DZL_SHORTCUTS_SHORTCUT (object);
343 
344   switch (prop_id)
345     {
346     case PROP_TITLE:
347       g_value_set_string (value, gtk_label_get_label (self->title));
348       break;
349 
350     case PROP_SUBTITLE:
351       g_value_set_string (value, gtk_label_get_label (self->subtitle));
352       break;
353 
354     case PROP_SUBTITLE_SET:
355       g_value_set_boolean (value, self->subtitle_set);
356       break;
357 
358     case PROP_ACCELERATOR:
359       g_value_set_string (value, dzl_shortcut_label_get_accelerator (self->accelerator));
360       break;
361 
362     case PROP_ICON:
363       {
364         GIcon *icon;
365 
366         gtk_image_get_gicon (self->image, &icon, NULL);
367         g_value_set_object (value, icon);
368       }
369       break;
370 
371     case PROP_ICON_SET:
372       g_value_set_boolean (value, self->icon_set);
373       break;
374 
375     case PROP_DIRECTION:
376       g_value_set_enum (value, self->direction);
377       break;
378 
379     case PROP_SHORTCUT_TYPE:
380       g_value_set_enum (value, self->shortcut_type);
381       break;
382 
383     case PROP_ACTION_NAME:
384       g_value_set_string (value, self->action_name);
385       break;
386 
387     default:
388       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
389     }
390 }
391 
392 static void
dzl_shortcuts_shortcut_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)393 dzl_shortcuts_shortcut_set_property (GObject      *object,
394                                      guint         prop_id,
395                                      const GValue *value,
396                                      GParamSpec   *pspec)
397 {
398   DzlShortcutsShortcut *self = DZL_SHORTCUTS_SHORTCUT (object);
399 
400   switch (prop_id)
401     {
402     case PROP_ACCELERATOR:
403       dzl_shortcuts_shortcut_set_accelerator (self, g_value_get_string (value));
404       break;
405 
406     case PROP_ICON:
407       dzl_shortcuts_shortcut_set_icon (self, g_value_get_object (value));
408       break;
409 
410     case PROP_ICON_SET:
411       dzl_shortcuts_shortcut_set_icon_set (self, g_value_get_boolean (value));
412       break;
413 
414     case PROP_ACCEL_SIZE_GROUP:
415       dzl_shortcuts_shortcut_set_accel_size_group (self, GTK_SIZE_GROUP (g_value_get_object (value)));
416       break;
417 
418     case PROP_TITLE:
419       gtk_label_set_label (self->title, g_value_get_string (value));
420       break;
421 
422     case PROP_SUBTITLE:
423       dzl_shortcuts_shortcut_set_subtitle (self, g_value_get_string (value));
424       break;
425 
426     case PROP_SUBTITLE_SET:
427       dzl_shortcuts_shortcut_set_subtitle_set (self, g_value_get_boolean (value));
428       break;
429 
430     case PROP_TITLE_SIZE_GROUP:
431       dzl_shortcuts_shortcut_set_title_size_group (self, GTK_SIZE_GROUP (g_value_get_object (value)));
432       break;
433 
434     case PROP_DIRECTION:
435       dzl_shortcuts_shortcut_set_direction (self, g_value_get_enum (value));
436       break;
437 
438     case PROP_SHORTCUT_TYPE:
439       dzl_shortcuts_shortcut_set_type (self, g_value_get_enum (value));
440       break;
441 
442     case PROP_ACTION_NAME:
443       dzl_shortcuts_shortcut_set_action_name (self, g_value_get_string (value));
444       break;
445 
446     default:
447       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
448       break;
449     }
450 }
451 
452 static void
dzl_shortcuts_shortcut_finalize(GObject * object)453 dzl_shortcuts_shortcut_finalize (GObject *object)
454 {
455   DzlShortcutsShortcut *self = DZL_SHORTCUTS_SHORTCUT (object);
456 
457   g_clear_object (&self->accel_size_group);
458   g_clear_object (&self->title_size_group);
459   g_free (self->action_name);
460 
461   G_OBJECT_CLASS (dzl_shortcuts_shortcut_parent_class)->finalize (object);
462 }
463 
464 static void
dzl_shortcuts_shortcut_add(GtkContainer * container,GtkWidget * widget)465 dzl_shortcuts_shortcut_add (GtkContainer *container,
466                             GtkWidget    *widget)
467 {
468   g_warning ("Can't add children to %s", G_OBJECT_TYPE_NAME (container));
469 }
470 
471 static GType
dzl_shortcuts_shortcut_child_type(GtkContainer * container)472 dzl_shortcuts_shortcut_child_type (GtkContainer *container)
473 {
474   return G_TYPE_NONE;
475 }
476 
477 void
dzl_shortcuts_shortcut_update_accel(DzlShortcutsShortcut * self,GtkWindow * window)478 dzl_shortcuts_shortcut_update_accel (DzlShortcutsShortcut *self,
479                                      GtkWindow            *window)
480 {
481   GtkApplication *app;
482   gchar **accels;
483   gchar *str;
484 
485   if (self->action_name == NULL)
486     return;
487 
488   app = gtk_window_get_application (window);
489   if (app == NULL)
490     return;
491 
492   accels = gtk_application_get_accels_for_action (app, self->action_name);
493   str = g_strjoinv (" ", accels);
494 
495   dzl_shortcuts_shortcut_set_accelerator (self, str);
496 
497   g_free (str);
498   g_strfreev (accels);
499 }
500 
501 static void
dzl_shortcuts_shortcut_class_init(DzlShortcutsShortcutClass * klass)502 dzl_shortcuts_shortcut_class_init (DzlShortcutsShortcutClass *klass)
503 {
504   GObjectClass *object_class = G_OBJECT_CLASS (klass);
505   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
506   GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
507 
508   object_class->finalize = dzl_shortcuts_shortcut_finalize;
509   object_class->get_property = dzl_shortcuts_shortcut_get_property;
510   object_class->set_property = dzl_shortcuts_shortcut_set_property;
511 
512   widget_class->direction_changed = dzl_shortcuts_shortcut_direction_changed;
513 
514   container_class->add = dzl_shortcuts_shortcut_add;
515   container_class->child_type = dzl_shortcuts_shortcut_child_type;
516 
517   /**
518    * DzlShortcutsShortcut:accelerator:
519    *
520    * The accelerator(s) represented by this object. This property is used
521    * if #DzlShortcutsShortcut:shortcut-type is set to #GTK_SHORTCUT_ACCELERATOR.
522    *
523    * The syntax of this property is (an extension of) the syntax understood by
524    * gtk_accelerator_parse(). Multiple accelerators can be specified by separating
525    * them with a space, but keep in mind that the available width is limited.
526    * It is also possible to specify ranges of shortcuts, using ... between the keys.
527    * Sequences of keys can be specified using a + or & between the keys.
528    *
529    * Examples:
530    * - A single shortcut: &lt;ctl&gt;&lt;alt&gt;delete
531    * - Two alternative shortcuts: &lt;shift&gt;a Home
532    * - A range of shortcuts: &lt;alt&gt;1...&lt;alt&gt;9
533    * - Several keys pressed together: Control_L&Control_R
534    * - A sequence of shortcuts or keys: &lt;ctl&gt;c+&lt;ctl&gt;x
535    *
536    * Use + instead of & when the keys may (or have to be) pressed sequentially (e.g
537    * use t+t for 'press the t key twice').
538    *
539    * Note that <, > and & need to be escaped as &lt;, &gt; and &amp; when used
540    * in .ui files.
541    */
542   properties[PROP_ACCELERATOR] =
543     g_param_spec_string ("accelerator",
544                          "Accelerator",
545                          "The accelerator keys for shortcuts of type 'Accelerator'",
546                          NULL,
547                          (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
548 
549   /**
550    * DzlShortcutsShortcut:icon:
551    *
552    * An icon to represent the shortcut or gesture. This property is used if
553    * #DzlShortcutsShortcut:shortcut-type is set to #GTK_SHORTCUT_GESTURE.
554    * For the other predefined gesture types, GTK+ provides an icon on its own.
555    */
556   properties[PROP_ICON] =
557     g_param_spec_object ("icon",
558                          "Icon",
559                          "The icon to show for shortcuts of type 'Other Gesture'",
560                          G_TYPE_ICON,
561                          (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
562 
563   /**
564    * DzlShortcutsShortcut:icon-set:
565    *
566    * %TRUE if an icon has been set.
567    */
568   properties[PROP_ICON_SET] =
569     g_param_spec_boolean ("icon-set",
570                           "Icon Set",
571                           "Whether an icon has been set",
572                           FALSE,
573                           (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
574 
575   /**
576    * DzlShortcutsShortcut:title:
577    *
578    * The textual description for the shortcut or gesture represented by
579    * this object. This should be a short string that can fit in a single line.
580    */
581   properties[PROP_TITLE] =
582     g_param_spec_string ("title",
583                          "Title",
584                          "A short description for the shortcut",
585                          "",
586                          (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
587 
588   /**
589    * DzlShortcutsShortcut:subtitle:
590    *
591    * The subtitle for the shortcut or gesture.
592    *
593    * This is typically used for gestures and should be a short, one-line
594    * text that describes the gesture itself. For the predefined gesture
595    * types, GTK+ provides a subtitle on its own.
596    */
597   properties[PROP_SUBTITLE] =
598     g_param_spec_string ("subtitle",
599                          "Subtitle",
600                          "A short description for the gesture",
601                          "",
602                          (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
603 
604   /**
605    * DzlShortcutsShortcut:subtitle-set:
606    *
607    * %TRUE if a subtitle has been set.
608    */
609   properties[PROP_SUBTITLE_SET] =
610     g_param_spec_boolean ("subtitle-set",
611                           "Subtitle Set",
612                           "Whether a subtitle has been set",
613                           FALSE,
614                           (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
615 
616   /**
617    * DzlShortcutsShortcut:accel-size-group:
618    *
619    * The size group for the accelerator portion of this shortcut.
620    *
621    * This is used internally by GTK+, and must not be modified by applications.
622    */
623   properties[PROP_ACCEL_SIZE_GROUP] =
624     g_param_spec_object ("accel-size-group",
625                          "Accelerator Size Group",
626                          "Accelerator Size Group",
627                          GTK_TYPE_SIZE_GROUP,
628                          (G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
629 
630   /**
631    * DzlShortcutsShortcut:title-size-group:
632    *
633    * The size group for the textual portion of this shortcut.
634    *
635    * This is used internally by GTK+, and must not be modified by applications.
636    */
637   properties[PROP_TITLE_SIZE_GROUP] =
638     g_param_spec_object ("title-size-group",
639                          "Title Size Group",
640                          "Title Size Group",
641                          GTK_TYPE_SIZE_GROUP,
642                          (G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
643 
644   /**
645    * DzlShortcutsShortcut:direction:
646    *
647    * The text direction for which this shortcut is active. If the shortcut
648    * is used regardless of the text direction, set this property to
649    * #GTK_TEXT_DIR_NONE.
650    */
651   properties[PROP_DIRECTION] =
652     g_param_spec_enum ("direction",
653                        "Direction",
654                        "Text direction for which this shortcut is active",
655                        GTK_TYPE_TEXT_DIRECTION,
656                        GTK_TEXT_DIR_NONE,
657                        (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY));
658 
659   /**
660    * DzlShortcutsShortcut:shortcut-type:
661    *
662    * The type of shortcut that is represented.
663    */
664   properties[PROP_SHORTCUT_TYPE] =
665     g_param_spec_enum ("shortcut-type",
666                        "Shortcut Type",
667                        "The type of shortcut that is represented",
668                        GTK_TYPE_SHORTCUT_TYPE,
669                        GTK_SHORTCUT_ACCELERATOR,
670                        (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY));
671 
672   /**
673    * DzlShortcutsShortcut:action-name:
674    *
675    * A detailed action name. If this is set for a shortcut
676    * of type %GTK_SHORTCUT_ACCELERATOR, then GTK+ will use
677    * the accelerators that are associated with the action
678    * via gtk_application_set_accels_for_action(), and setting
679    * #DzlShortcutsShortcut::accelerator is not necessary.
680    *
681    * Since: 3.22
682    */
683   properties[PROP_ACTION_NAME] =
684     g_param_spec_string ("action-name",
685                          "Action Name",
686                          "The name of the action",
687                          NULL,
688                          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
689 
690   g_object_class_install_properties (object_class, LAST_PROP, properties);
691   gtk_widget_class_set_css_name (widget_class, "shortcut");
692 }
693 
694 static void
dzl_shortcuts_shortcut_init(DzlShortcutsShortcut * self)695 dzl_shortcuts_shortcut_init (DzlShortcutsShortcut *self)
696 {
697   gtk_orientable_set_orientation (GTK_ORIENTABLE (self), GTK_ORIENTATION_HORIZONTAL);
698   gtk_box_set_spacing (GTK_BOX (self), 12);
699 
700   self->direction = GTK_TEXT_DIR_NONE;
701   self->shortcut_type = GTK_SHORTCUT_ACCELERATOR;
702 
703   self->image = g_object_new (GTK_TYPE_IMAGE,
704                               "visible", FALSE,
705                               "valign", GTK_ALIGN_CENTER,
706                               "no-show-all", TRUE,
707                               NULL);
708   GTK_CONTAINER_CLASS (dzl_shortcuts_shortcut_parent_class)->add (GTK_CONTAINER (self), GTK_WIDGET (self->image));
709 
710   self->accelerator = g_object_new (DZL_TYPE_SHORTCUT_LABEL,
711                                     "visible", TRUE,
712                                     "valign", GTK_ALIGN_CENTER,
713                                     "no-show-all", TRUE,
714                                     NULL);
715   GTK_CONTAINER_CLASS (dzl_shortcuts_shortcut_parent_class)->add (GTK_CONTAINER (self), GTK_WIDGET (self->accelerator));
716 
717   self->title_box = g_object_new (GTK_TYPE_BOX,
718                                   "visible", TRUE,
719                                   "valign", GTK_ALIGN_CENTER,
720                                   "hexpand", TRUE,
721                                   "orientation", GTK_ORIENTATION_VERTICAL,
722                                   NULL);
723   GTK_CONTAINER_CLASS (dzl_shortcuts_shortcut_parent_class)->add (GTK_CONTAINER (self), GTK_WIDGET (self->title_box));
724 
725   self->title = g_object_new (GTK_TYPE_LABEL,
726                               "visible", TRUE,
727                               "xalign", 0.0f,
728                               NULL);
729   gtk_container_add (GTK_CONTAINER (self->title_box), GTK_WIDGET (self->title));
730 
731   self->subtitle = g_object_new (GTK_TYPE_LABEL,
732                                  "visible", FALSE,
733                                  "no-show-all", TRUE,
734                                  "xalign", 0.0f,
735                                  NULL);
736   gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET (self->subtitle)),
737                                GTK_STYLE_CLASS_DIM_LABEL);
738   gtk_container_add (GTK_CONTAINER (self->title_box), GTK_WIDGET (self->subtitle));
739 }
740