1/*
2* Copyright © 2018 Benjamin Otte
3*
4* This library is free software; you can redistribute it and/or
5* modify it under the terms of the GNU Lesser General Public
6* License as published by the Free Software Foundation; either
7* version 2.1 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* Lesser General Public License for more details.
13*
14* You should have received a copy of the GNU Lesser General Public
15* License along with this library. If not, see <http://www.gnu.org/licenses/>.
16*
17* Authors: Benjamin Otte <otte@gnome.org>
18*/
19
20/**
21* GtkShortcutAction:
22*
23* `GtkShortcutAction` encodes an action that can be triggered by a
24* keyboard shortcut.
25*
26* `GtkShortcutActions` contain functions that allow easy presentation
27* to end users as well as being printed for debugging.
28*
29* All `GtkShortcutActions` are immutable, you can only specify their
30* properties during construction. If you want to change a action, you
31* have to replace it with a new one. If you need to pass arguments to
32* an action, these are specified by the higher-level `GtkShortcut` object.
33*
34* To activate a `GtkShortcutAction` manually, [method@Gtk.ShortcutAction.activate]
35* can be called.
36*
37* GTK provides various actions:
38*
39* - [class@Gtk.MnemonicAction]: a shortcut action that calls
40* gtk_widget_mnemonic_activate()
41* - [class@Gtk.CallbackAction]: a shortcut action that invokes
42* a given callback
43* - [class@Gtk.SignalAction]: a shortcut action that emits a
44* given signal
45* - [class@Gtk.ActivateAction]: a shortcut action that calls
46* gtk_widget_activate()
47* - [class@Gtk.NamedAction]: a shortcut action that calls
48* gtk_widget_activate_action()
49* - [class@Gtk.NothingAction]: a shortcut action that does nothing
50*/
51
52#include "config.h"
53
54#include "gtkshortcutactionprivate.h"
55
56#include "gtkbuilder.h"
57#include "gtkintl.h"
58#include "gtkwidgetprivate.h"
59#include "gtkdebug.h"
60
61/* {{{ GtkShortcutAction */
62
63struct _GtkShortcutAction
64{
65GObject parent_instance;
66};
67
68struct _GtkShortcutActionClass
69{
70 GObjectClass parent_class;
71
72 gboolean (* activate) (GtkShortcutAction *action,
73 GtkShortcutActionFlags flags,
74 GtkWidget *widget,
75 GVariant *args);
76 void (* print) (GtkShortcutAction *action,
77 GString *string);
78};
79
80G_DEFINE_ABSTRACT_TYPE (GtkShortcutAction, gtk_shortcut_action, G_TYPE_OBJECT)
81
82static void
83gtk_shortcut_action_class_init (GtkShortcutActionClass *klass)
84{
85}
86
87static void
88gtk_shortcut_action_init (GtkShortcutAction *self)
89{
90}
91
92/**
93 * gtk_shortcut_action_to_string:
94 * @self: a `GtkShortcutAction`
95 *
96 * Prints the given action into a human-readable string.
97 *
98 * This is a small wrapper around [method@Gtk.ShortcutAction.print]
99 * to help when debugging.
100 *
101 * Returns: (transfer full): a new string
102 */
103char *
104gtk_shortcut_action_to_string (GtkShortcutAction *self)
105{
106 GString *string;
107
108 g_return_val_if_fail (GTK_IS_SHORTCUT_ACTION (self), NULL);
109
110 string = g_string_new (NULL);
111 gtk_shortcut_action_print (self, string);
112
113 return g_string_free (string, FALSE);
114}
115
116/**
117 * gtk_shortcut_action_print:
118 * @self: a `GtkShortcutAction`
119 * @string: a `GString` to print into
120 *
121 * Prints the given action into a string for the developer.
122 *
123 * This is meant for debugging and logging.
124 *
125 * The form of the representation may change at any time and is
126 * not guaranteed to stay identical.
127 */
128void
129gtk_shortcut_action_print (GtkShortcutAction *self,
130 GString *string)
131{
132 g_return_if_fail (GTK_IS_SHORTCUT_ACTION (self));
133 g_return_if_fail (string != NULL);
134
135 GTK_SHORTCUT_ACTION_GET_CLASS (ptr: self)->print (self, string);
136}
137
138/**
139 * gtk_shortcut_action_activate:
140 * @self: a `GtkShortcutAction`
141 * @flags: flags to activate with
142 * @widget: Target of the activation
143 * @args: (nullable): arguments to pass
144 *
145 * Activates the action on the @widget with the given @args.
146 *
147 * Note that some actions ignore the passed in @flags, @widget or @args.
148 *
149 * Activation of an action can fail for various reasons. If the action
150 * is not supported by the @widget, if the @args don't match the action
151 * or if the activation otherwise had no effect, %FALSE will be returned.
152 *
153 * Returns: %TRUE if this action was activated successfully
154 */
155gboolean
156gtk_shortcut_action_activate (GtkShortcutAction *self,
157 GtkShortcutActionFlags flags,
158 GtkWidget *widget,
159 GVariant *args)
160{
161 g_return_val_if_fail (GTK_IS_SHORTCUT_ACTION (self), FALSE);
162 g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
163
164 GTK_NOTE (KEYBINDINGS, {
165 char *act = gtk_shortcut_action_to_string (self);
166 g_print ("Shortcut action activate on %s: %s\n", G_OBJECT_TYPE_NAME (widget), act);
167 g_free (act);
168 });
169
170 return GTK_SHORTCUT_ACTION_GET_CLASS (ptr: self)->activate (self, flags, widget, args);
171}
172
173static char *
174string_is_function (const char *string,
175 const char *function_name)
176{
177 gsize len;
178
179 if (!g_str_has_prefix (str: string, prefix: function_name))
180 return NULL;
181 string += strlen (s: function_name);
182
183 if (string[0] != '(')
184 return NULL;
185 string ++;
186
187 len = strlen (s: string);
188 if (len == 0 || string[len - 1] != ')')
189 return NULL;
190
191 return g_strndup (str: string, n: len - 1);
192}
193
194/**
195 * gtk_shortcut_action_parse_string: (constructor)
196 * @string: the string to parse
197 *
198 * Tries to parse the given string into an action.
199 *
200 * On success, the parsed action is returned. When parsing
201 * failed, %NULL is returned.
202 *
203 * The accepted strings are:
204 *
205 * - `nothing`, for `GtkNothingAction`
206 * - `activate`, for `GtkActivateAction`
207 * - `mnemonic-activate`, for `GtkMnemonicAction`
208 * - `action(NAME)`, for a `GtkNamedAction` for the action named `NAME`
209 * - `signal(NAME)`, for a `GtkSignalAction` for the signal `NAME`
210 *
211 * Returns: (nullable) (transfer full): a new `GtkShortcutAction`
212 */
213GtkShortcutAction *
214gtk_shortcut_action_parse_string (const char *string)
215{
216 GtkShortcutAction *result;
217 char *arg;
218
219 if (g_str_equal (v1: string, v2: "nothing"))
220 return g_object_ref (gtk_nothing_action_get ());
221 if (g_str_equal (v1: string, v2: "activate"))
222 return g_object_ref (gtk_activate_action_get ());
223 if (g_str_equal (v1: string, v2: "mnemonic-activate"))
224 return g_object_ref (gtk_mnemonic_action_get ());
225
226 if ((arg = string_is_function (string, function_name: "action")))
227 {
228 result = gtk_named_action_new (name: arg);
229 g_free (mem: arg);
230 }
231 else if ((arg = string_is_function (string, function_name: "signal")))
232 {
233 result = gtk_signal_action_new (signal_name: arg);
234 g_free (mem: arg);
235 }
236 else
237 return NULL;
238
239 return result;
240}
241
242GtkShortcutAction *
243gtk_shortcut_action_parse_builder (GtkBuilder *builder,
244 const char *string,
245 GError **error)
246{
247 GtkShortcutAction *result;
248
249 result = gtk_shortcut_action_parse_string (string);
250
251 if (!result)
252 {
253 g_set_error (err: error,
254 GTK_BUILDER_ERROR, code: GTK_BUILDER_ERROR_INVALID_VALUE,
255 format: "String \"%s\" does not specify a GtkShortcutAction", string);
256 return NULL;
257 }
258
259 return result;
260}
261
262/* }}} */
263
264/* {{{ GtkNothingAction */
265
266struct _GtkNothingAction
267{
268 GtkShortcutAction parent_instance;
269};
270
271struct _GtkNothingActionClass
272{
273 GtkShortcutActionClass parent_class;
274};
275
276G_DEFINE_TYPE (GtkNothingAction, gtk_nothing_action, GTK_TYPE_SHORTCUT_ACTION)
277
278static void G_GNUC_NORETURN
279gtk_nothing_action_finalize (GObject *gobject)
280{
281 g_assert_not_reached ();
282
283 G_OBJECT_CLASS (gtk_nothing_action_parent_class)->finalize (gobject);
284}
285
286static gboolean
287gtk_nothing_action_activate (GtkShortcutAction *action,
288 GtkShortcutActionFlags flags,
289 GtkWidget *widget,
290 GVariant *args)
291{
292 return FALSE;
293}
294
295static void
296gtk_nothing_action_print (GtkShortcutAction *action,
297 GString *string)
298{
299 g_string_append (string, val: "nothing");
300}
301
302static void
303gtk_nothing_action_class_init (GtkNothingActionClass *klass)
304{
305 GtkShortcutActionClass *action_class = GTK_SHORTCUT_ACTION_CLASS (ptr: klass);
306
307 G_OBJECT_CLASS (klass)->finalize = gtk_nothing_action_finalize;
308
309 action_class->activate = gtk_nothing_action_activate;
310 action_class->print = gtk_nothing_action_print;
311}
312
313static void
314gtk_nothing_action_init (GtkNothingAction *self)
315{
316}
317
318/**
319 * gtk_nothing_action_get:
320 *
321 * Gets the nothing action.
322 *
323 * This is an action that does nothing and where
324 * activating it always fails.
325 *
326 * Returns: (transfer none) (type GtkNothingAction): The nothing action
327 */
328GtkShortcutAction *
329gtk_nothing_action_get (void)
330{
331 static GtkShortcutAction *nothing;
332
333 if (nothing == NULL)
334 nothing = g_object_new (GTK_TYPE_NOTHING_ACTION, NULL);
335
336 return nothing;
337}
338
339/* }}} */
340
341/* {{{ GtkCallbackAction */
342
343struct _GtkCallbackAction
344{
345 GtkShortcutAction parent_instance;
346
347 GtkShortcutFunc callback;
348 gpointer user_data;
349 GDestroyNotify destroy_notify;
350};
351
352struct _GtkCallbackActionClass
353{
354 GtkShortcutActionClass parent_class;
355};
356
357G_DEFINE_TYPE (GtkCallbackAction, gtk_callback_action, GTK_TYPE_SHORTCUT_ACTION)
358
359static void
360gtk_callback_action_finalize (GObject *gobject)
361{
362 GtkCallbackAction *self = GTK_CALLBACK_ACTION (ptr: gobject);
363
364 if (self->destroy_notify != NULL)
365 self->destroy_notify (self->user_data);
366
367 G_OBJECT_CLASS (gtk_callback_action_parent_class)->finalize (gobject);
368}
369
370static gboolean
371gtk_callback_action_activate (GtkShortcutAction *action,
372 GtkShortcutActionFlags flags,
373 GtkWidget *widget,
374 GVariant *args)
375{
376 GtkCallbackAction *self = GTK_CALLBACK_ACTION (ptr: action);
377
378 return self->callback (widget, args, self->user_data);
379}
380
381static void
382gtk_callback_action_print (GtkShortcutAction *action,
383 GString *string)
384{
385 GtkCallbackAction *self = GTK_CALLBACK_ACTION (ptr: action);
386
387 g_string_append_printf (string, format: "callback<%p>", self->callback);
388}
389
390static void
391gtk_callback_action_class_init (GtkCallbackActionClass *klass)
392{
393 GtkShortcutActionClass *action_class = GTK_SHORTCUT_ACTION_CLASS (ptr: klass);
394
395 G_OBJECT_CLASS (klass)->finalize = gtk_callback_action_finalize;
396
397 action_class->activate = gtk_callback_action_activate;
398 action_class->print = gtk_callback_action_print;
399}
400
401static void
402gtk_callback_action_init (GtkCallbackAction *self)
403{
404}
405
406/**
407 * gtk_callback_action_new:
408 * @callback: (scope notified): the callback to call
409 * @data: (closure callback): the data to be passed to @callback
410 * @destroy: (destroy data): the function to be called when the
411 * callback action is finalized
412 *
413 * Create a custom action that calls the given @callback when
414 * activated.
415 *
416 * Returns: (transfer full) (type GtkCallbackAction): A new shortcut action
417 */
418GtkShortcutAction *
419gtk_callback_action_new (GtkShortcutFunc callback,
420 gpointer data,
421 GDestroyNotify destroy)
422{
423 GtkCallbackAction *self;
424
425 g_return_val_if_fail (callback != NULL, NULL);
426
427 self = g_object_new (GTK_TYPE_CALLBACK_ACTION, NULL);
428
429 self->callback = callback;
430 self->user_data = data;
431 self->destroy_notify = destroy;
432
433 return GTK_SHORTCUT_ACTION (ptr: self);
434}
435
436/* }}} */
437
438/* {{{ GtkActivateAction */
439
440struct _GtkActivateAction
441{
442 GtkShortcutAction parent_instance;
443};
444
445struct _GtkActivateActionClass
446{
447 GtkShortcutActionClass parent_class;
448};
449
450G_DEFINE_TYPE (GtkActivateAction, gtk_activate_action, GTK_TYPE_SHORTCUT_ACTION)
451
452static void G_GNUC_NORETURN
453gtk_activate_action_finalize (GObject *gobject)
454{
455 g_assert_not_reached ();
456
457 G_OBJECT_CLASS (gtk_activate_action_parent_class)->finalize (gobject);
458}
459
460static gboolean
461gtk_activate_action_activate (GtkShortcutAction *action,
462 GtkShortcutActionFlags flags,
463 GtkWidget *widget,
464 GVariant *args)
465{
466 return gtk_widget_activate (widget);
467}
468
469static void
470gtk_activate_action_print (GtkShortcutAction *action,
471 GString *string)
472{
473 g_string_append (string, val: "activate");
474}
475
476static void
477gtk_activate_action_class_init (GtkActivateActionClass *klass)
478{
479 GtkShortcutActionClass *action_class = GTK_SHORTCUT_ACTION_CLASS (ptr: klass);
480
481 G_OBJECT_CLASS (klass)->finalize = gtk_activate_action_finalize;
482
483 action_class->activate = gtk_activate_action_activate;
484 action_class->print = gtk_activate_action_print;
485}
486
487static void
488gtk_activate_action_init (GtkActivateAction *self)
489{
490}
491
492/**
493 * gtk_activate_action_get:
494 *
495 * Gets the activate action.
496 *
497 * This is an action that calls gtk_widget_activate()
498 * on the given widget upon activation.
499 *
500 * Returns: (transfer none) (type GtkActivateAction): The activate action
501 */
502GtkShortcutAction *
503gtk_activate_action_get (void)
504{
505 static GtkShortcutAction *action;
506
507 if (action == NULL)
508 action = g_object_new (GTK_TYPE_ACTIVATE_ACTION, NULL);
509
510 return action;
511}
512
513/* }}} */
514
515/* {{{ GtkMnemonicAction */
516
517struct _GtkMnemonicAction
518{
519 GtkShortcutAction parent_instance;
520};
521
522struct _GtkMnemonicActionClass
523{
524 GtkShortcutActionClass parent_class;
525};
526
527G_DEFINE_TYPE (GtkMnemonicAction, gtk_mnemonic_action, GTK_TYPE_SHORTCUT_ACTION)
528
529static void G_GNUC_NORETURN
530gtk_mnemonic_action_finalize (GObject *gobject)
531{
532 g_assert_not_reached ();
533
534 G_OBJECT_CLASS (gtk_mnemonic_action_parent_class)->finalize (gobject);
535}
536
537static gboolean
538gtk_mnemonic_action_activate (GtkShortcutAction *action,
539 GtkShortcutActionFlags flags,
540 GtkWidget *widget,
541 GVariant *args)
542{
543 return gtk_widget_mnemonic_activate (widget, group_cycling: flags & GTK_SHORTCUT_ACTION_EXCLUSIVE ? FALSE : TRUE);
544}
545
546static void
547gtk_mnemonic_action_print (GtkShortcutAction *action,
548 GString *string)
549{
550 g_string_append (string, val: "mnemonic-activate");
551}
552
553static void
554gtk_mnemonic_action_class_init (GtkMnemonicActionClass *klass)
555{
556 GtkShortcutActionClass *action_class = GTK_SHORTCUT_ACTION_CLASS (ptr: klass);
557
558 G_OBJECT_CLASS (klass)->finalize = gtk_mnemonic_action_finalize;
559
560 action_class->activate = gtk_mnemonic_action_activate;
561 action_class->print = gtk_mnemonic_action_print;
562}
563
564static void
565gtk_mnemonic_action_init (GtkMnemonicAction *self)
566{
567}
568
569/**
570 * gtk_mnemonic_action_get:
571 *
572 * Gets the mnemonic action.
573 *
574 * This is an action that calls gtk_widget_mnemonic_activate()
575 * on the given widget upon activation.
576 *
577 * Returns: (transfer none) (type GtkMnemonicAction): The mnemonic action
578 */
579GtkShortcutAction *
580gtk_mnemonic_action_get (void)
581{
582 static GtkShortcutAction *mnemonic;
583
584 if (G_UNLIKELY (mnemonic == NULL))
585 mnemonic = g_object_new (GTK_TYPE_MNEMONIC_ACTION, NULL);
586
587 return mnemonic;
588}
589
590/* }}} */
591
592/* {{{ GtkSignalAction */
593
594struct _GtkSignalAction
595{
596 GtkShortcutAction parent_instance;
597
598 char *name;
599};
600
601struct _GtkSignalActionClass
602{
603 GtkShortcutActionClass parent_class;
604};
605
606enum
607{
608 SIGNAL_PROP_SIGNAL_NAME = 1,
609 SIGNAL_N_PROPS
610};
611
612static GParamSpec *signal_props[SIGNAL_N_PROPS];
613
614G_DEFINE_TYPE (GtkSignalAction, gtk_signal_action, GTK_TYPE_SHORTCUT_ACTION)
615
616static void
617gtk_signal_action_finalize (GObject *gobject)
618{
619 GtkSignalAction *self = GTK_SIGNAL_ACTION (ptr: gobject);
620
621 g_free (mem: self->name);
622
623 G_OBJECT_CLASS (gtk_signal_action_parent_class)->finalize (gobject);
624}
625
626static gboolean
627binding_compose_params (GtkWidget *widget,
628 GVariantIter *args,
629 GSignalQuery *query,
630 GValue **params_p)
631{
632 GValue *params;
633 const GType *types;
634 guint i;
635 gboolean valid;
636
637 params = g_new0 (GValue, query->n_params + 1);
638 *params_p = params;
639
640 /* The instance we emit on is the first object in the array
641 */
642 g_value_init (value: params, G_TYPE_OBJECT);
643 g_value_set_object (value: params, G_OBJECT (widget));
644 params++;
645
646 types = query->param_types;
647 valid = TRUE;
648 for (i = 1; i < query->n_params + 1 && valid; i++)
649 {
650 GValue tmp_value = G_VALUE_INIT;
651 GVariant *tmp_variant;
652
653 g_value_init (value: params, g_type: *types);
654 tmp_variant = g_variant_iter_next_value (iter: args);
655
656 switch ((guint) g_variant_classify (value: tmp_variant))
657 {
658 case G_VARIANT_CLASS_BOOLEAN:
659 g_value_init (value: &tmp_value, G_TYPE_BOOLEAN);
660 g_value_set_boolean (value: &tmp_value, v_boolean: g_variant_get_boolean (value: tmp_variant));
661 break;
662 case G_VARIANT_CLASS_DOUBLE:
663 g_value_init (value: &tmp_value, G_TYPE_DOUBLE);
664 g_value_set_double (value: &tmp_value, v_double: g_variant_get_double (value: tmp_variant));
665 break;
666 case G_VARIANT_CLASS_INT32:
667 g_value_init (value: &tmp_value, G_TYPE_LONG);
668 g_value_set_long (value: &tmp_value, v_long: g_variant_get_int32 (value: tmp_variant));
669 break;
670 case G_VARIANT_CLASS_UINT32:
671 g_value_init (value: &tmp_value, G_TYPE_LONG);
672 g_value_set_long (value: &tmp_value, v_long: g_variant_get_uint32 (value: tmp_variant));
673 break;
674 case G_VARIANT_CLASS_INT64:
675 g_value_init (value: &tmp_value, G_TYPE_LONG);
676 g_value_set_long (value: &tmp_value, v_long: g_variant_get_int64 (value: tmp_variant));
677 break;
678 case G_VARIANT_CLASS_STRING:
679 /* gtk_rc_parse_flags/enum() has fancier parsing for this; we can't call
680 * that since we don't have a GParamSpec, so just do something simple
681 */
682 if (G_TYPE_FUNDAMENTAL (*types) == G_TYPE_ENUM)
683 {
684 GEnumClass *class = G_ENUM_CLASS (g_type_class_ref (*types));
685 GEnumValue *enum_value;
686 const char *s = g_variant_get_string (value: tmp_variant, NULL);
687
688 valid = FALSE;
689
690 enum_value = g_enum_get_value_by_name (enum_class: class, name: s);
691 if (!enum_value)
692 enum_value = g_enum_get_value_by_nick (enum_class: class, nick: s);
693
694 if (enum_value)
695 {
696 g_value_init (value: &tmp_value, g_type: *types);
697 g_value_set_enum (value: &tmp_value, v_enum: enum_value->value);
698 valid = TRUE;
699 }
700
701 g_type_class_unref (g_class: class);
702 }
703 /* This is just a hack for compatibility with GTK 1.2 where a string
704 * could be used for a single flag value / without the support for multiple
705 * values in gtk_rc_parse_flags(), this isn't very useful.
706 */
707 else if (G_TYPE_FUNDAMENTAL (*types) == G_TYPE_FLAGS)
708 {
709 GFlagsClass *class = G_FLAGS_CLASS (g_type_class_ref (*types));
710 GFlagsValue *flags_value;
711 const char *s = g_variant_get_string (value: tmp_variant, NULL);
712
713 valid = FALSE;
714
715 flags_value = g_flags_get_value_by_name (flags_class: class, name: s);
716 if (!flags_value)
717 flags_value = g_flags_get_value_by_nick (flags_class: class, nick: s);
718 if (flags_value)
719 {
720 g_value_init (value: &tmp_value, g_type: *types);
721 g_value_set_flags (value: &tmp_value, v_flags: flags_value->value);
722 valid = TRUE;
723 }
724
725 g_type_class_unref (g_class: class);
726 }
727 else
728 {
729 g_value_init (value: &tmp_value, G_TYPE_STRING);
730 g_value_set_static_string (value: &tmp_value, v_string: g_variant_get_string (value: tmp_variant, NULL));
731 }
732 break;
733 default:
734 valid = FALSE;
735 break;
736 }
737
738 if (valid)
739 {
740 if (!g_value_transform (src_value: &tmp_value, dest_value: params))
741 valid = FALSE;
742
743 g_value_unset (value: &tmp_value);
744 }
745
746 g_variant_unref (value: tmp_variant);
747 types++;
748 params++;
749 }
750
751 if (!valid)
752 {
753 guint j;
754
755 for (j = 0; j < i; j++)
756 g_value_unset (value: &(*params_p)[j]);
757
758 g_free (mem: *params_p);
759 *params_p = NULL;
760 }
761
762 return valid;
763}
764
765static gboolean
766gtk_signal_action_emit_signal (GtkWidget *widget,
767 const char *signal,
768 GVariant *args,
769 gboolean *handled,
770 GError **error)
771{
772 GSignalQuery query;
773 guint signal_id;
774 GValue *params = NULL;
775 GValue return_val = G_VALUE_INIT;
776 GVariantIter args_iter;
777 gsize n_args;
778 guint i;
779
780 *handled = FALSE;
781
782 signal_id = g_signal_lookup (name: signal, G_OBJECT_TYPE (widget));
783 if (!signal_id)
784 {
785 g_set_error (err: error, G_IO_ERROR, code: G_IO_ERROR_FAILED,
786 format: "Could not find signal \"%s\" in the '%s' class ancestry",
787 signal,
788 g_type_name (G_OBJECT_TYPE (widget)));
789 return FALSE;
790 }
791
792 g_signal_query (signal_id, query: &query);
793 if (args == NULL)
794 n_args = 0;
795 else if (g_variant_is_of_type (value: args, G_VARIANT_TYPE_TUPLE))
796 n_args = g_variant_iter_init (iter: &args_iter, value: args);
797 else
798 {
799 g_set_error (err: error, G_IO_ERROR, code: G_IO_ERROR_FAILED,
800 format: "argument GVariant is not a tuple");
801 return FALSE;
802 }
803 if (query.n_params != n_args ||
804 (query.return_type != G_TYPE_NONE && query.return_type != G_TYPE_BOOLEAN) ||
805 !binding_compose_params (widget, args: &args_iter, query: &query, params_p: &params))
806 {
807 g_set_error (err: error, G_IO_ERROR, code: G_IO_ERROR_FAILED,
808 format: "signature mismatch for signal \"%s\" in the '%s' class ancestry",
809 signal,
810 g_type_name (G_OBJECT_TYPE (widget)));
811 return FALSE;
812 }
813 else if (!(query.signal_flags & G_SIGNAL_ACTION))
814 {
815 g_set_error (err: error, G_IO_ERROR, code: G_IO_ERROR_FAILED,
816 format: "signal \"%s\" in the '%s' class ancestry cannot be used for action emissions",
817 signal,
818 g_type_name (G_OBJECT_TYPE (widget)));
819 return FALSE;
820 }
821
822 if (query.return_type == G_TYPE_BOOLEAN)
823 g_value_init (value: &return_val, G_TYPE_BOOLEAN);
824
825 g_signal_emitv (instance_and_params: params, signal_id, detail: 0, return_value: &return_val);
826
827 if (query.return_type == G_TYPE_BOOLEAN)
828 {
829 if (g_value_get_boolean (value: &return_val))
830 *handled = TRUE;
831 g_value_unset (value: &return_val);
832 }
833 else
834 *handled = TRUE;
835
836 if (params != NULL)
837 {
838 for (i = 0; i < query.n_params + 1; i++)
839 g_value_unset (value: &params[i]);
840
841 g_free (mem: params);
842 }
843
844 return TRUE;
845}
846
847static gboolean
848gtk_signal_action_activate (GtkShortcutAction *action,
849 GtkShortcutActionFlags flags,
850 GtkWidget *widget,
851 GVariant *args)
852{
853 GtkSignalAction *self = GTK_SIGNAL_ACTION (ptr: action);
854 GError *error = NULL;
855 gboolean handled;
856
857 if (!gtk_signal_action_emit_signal (widget,
858 signal: self->name,
859 args,
860 handled: &handled,
861 error: &error))
862 {
863 g_warning ("gtk_signal_action_activate(): %s",
864 error->message);
865 g_clear_error (err: &error);
866 return FALSE;
867 }
868
869 return handled;
870}
871
872static void
873gtk_signal_action_print (GtkShortcutAction *action,
874 GString *string)
875{
876 GtkSignalAction *self = GTK_SIGNAL_ACTION (ptr: action);
877
878 g_string_append_printf (string, format: "signal(%s)", self->name);
879}
880
881static void
882gtk_signal_action_constructed (GObject *gobject)
883{
884 GtkSignalAction *self G_GNUC_UNUSED = GTK_SIGNAL_ACTION (ptr: gobject);
885
886 g_assert (self->name != NULL && self->name[0] != '\0');
887
888 G_OBJECT_CLASS (gtk_signal_action_parent_class)->constructed (gobject);
889}
890
891static void
892gtk_signal_action_set_property (GObject *gobject,
893 guint prop_id,
894 const GValue *value,
895 GParamSpec *pspec)
896{
897 GtkSignalAction *self = GTK_SIGNAL_ACTION (ptr: gobject);
898
899 switch (prop_id)
900 {
901 case SIGNAL_PROP_SIGNAL_NAME:
902 self->name = g_value_dup_string (value);
903 break;
904
905 default:
906 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
907 }
908}
909
910static void
911gtk_signal_action_get_property (GObject *gobject,
912 guint prop_id,
913 GValue *value,
914 GParamSpec *pspec)
915{
916 GtkSignalAction *self = GTK_SIGNAL_ACTION (ptr: gobject);
917
918 switch (prop_id)
919 {
920 case SIGNAL_PROP_SIGNAL_NAME:
921 g_value_set_string (value, v_string: self->name);
922 break;
923
924 default:
925 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
926 }
927}
928
929static void
930gtk_signal_action_class_init (GtkSignalActionClass *klass)
931{
932 GtkShortcutActionClass *action_class = GTK_SHORTCUT_ACTION_CLASS (ptr: klass);
933 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
934
935 gobject_class->constructed = gtk_signal_action_constructed;
936 gobject_class->set_property = gtk_signal_action_set_property;
937 gobject_class->get_property = gtk_signal_action_get_property;
938 gobject_class->finalize = gtk_signal_action_finalize;
939
940 action_class->activate = gtk_signal_action_activate;
941 action_class->print = gtk_signal_action_print;
942
943 /**
944 * GtkSignalAction:signal-name: (attributes org.gtk.Property.get=gtk_signal_action_get_signal_name)
945 *
946 * The name of the signal to emit.
947 */
948 signal_props[SIGNAL_PROP_SIGNAL_NAME] =
949 g_param_spec_string (I_("signal-name"),
950 P_("Signal Name"),
951 P_("The name of the signal to emit"),
952 NULL,
953 G_PARAM_STATIC_STRINGS |
954 G_PARAM_READWRITE |
955 G_PARAM_CONSTRUCT_ONLY);
956
957 g_object_class_install_properties (oclass: gobject_class, n_pspecs: SIGNAL_N_PROPS, pspecs: signal_props);
958}
959
960static void
961gtk_signal_action_init (GtkSignalAction *self)
962{
963}
964
965/**
966 * gtk_signal_action_new:
967 * @signal_name: name of the signal to emit
968 *
969 * Creates an action that when activated, emits the given action signal
970 * on the provided widget.
971 *
972 * It will also unpack the args into arguments passed to the signal.
973 *
974 * Returns: (transfer full) (type GtkSignalAction): a new `GtkShortcutAction`
975 */
976GtkShortcutAction *
977gtk_signal_action_new (const char *signal_name)
978{
979 g_return_val_if_fail (signal_name != NULL, NULL);
980
981 return g_object_new (GTK_TYPE_SIGNAL_ACTION,
982 first_property_name: "signal-name", signal_name,
983 NULL);
984}
985
986/**
987 * gtk_signal_action_get_signal_name: (attributes org.gtk.Method.get_property=signal-name)
988 * @self: a signal action
989 *
990 * Returns the name of the signal that will be emitted.
991 *
992 * Returns: (transfer none): the name of the signal to emit
993 */
994const char *
995gtk_signal_action_get_signal_name (GtkSignalAction *self)
996{
997 g_return_val_if_fail (GTK_IS_SIGNAL_ACTION (self), NULL);
998
999 return self->name;
1000}
1001
1002/* }}} */
1003
1004/* {{{ GtkNamedAction */
1005
1006struct _GtkNamedAction
1007{
1008 GtkShortcutAction parent_instance;
1009
1010 char *name;
1011};
1012
1013struct _GtkNamedActionClass
1014{
1015 GtkShortcutActionClass parent_class;
1016};
1017
1018enum
1019{
1020 NAMED_PROP_ACTION_NAME = 1,
1021 NAMED_N_PROPS
1022};
1023
1024static GParamSpec *named_props[NAMED_N_PROPS];
1025
1026G_DEFINE_TYPE (GtkNamedAction, gtk_named_action, GTK_TYPE_SHORTCUT_ACTION)
1027
1028static void
1029gtk_named_action_finalize (GObject *gobject)
1030{
1031 GtkNamedAction *self = GTK_NAMED_ACTION (ptr: gobject);
1032
1033 g_free (mem: self->name);
1034
1035 G_OBJECT_CLASS (gtk_named_action_parent_class)->finalize (gobject);
1036}
1037
1038static gboolean
1039check_parameter_type (GVariant *args,
1040 const GVariantType *parameter_type)
1041{
1042 if (args)
1043 {
1044 if (parameter_type == NULL)
1045 {
1046 g_warning ("Trying to invoke action with arguments, but action has no parameter");
1047 return FALSE;
1048 }
1049
1050 if (!g_variant_is_of_type (value: args, type: parameter_type))
1051 {
1052 char *typestr = g_variant_type_dup_string (type: parameter_type);
1053 char *targetstr = g_variant_print (value: args, TRUE);
1054 g_warning ("Trying to invoke action with target '%s',"
1055 " but action expects parameter with type '%s'", targetstr, typestr);
1056 g_free (mem: targetstr);
1057 g_free (mem: typestr);
1058 return FALSE;
1059 }
1060 }
1061 else
1062 {
1063 if (parameter_type != NULL)
1064 {
1065 char *typestr = g_variant_type_dup_string (type: parameter_type);
1066 g_warning ("Trying to invoke action without arguments,"
1067 " but action expects parameter with type '%s'", typestr);
1068 g_free (mem: typestr);
1069 return FALSE;
1070 }
1071 }
1072
1073 return TRUE;
1074}
1075
1076static gboolean
1077gtk_named_action_activate (GtkShortcutAction *action,
1078 GtkShortcutActionFlags flags,
1079 GtkWidget *widget,
1080 GVariant *args)
1081{
1082 GtkNamedAction *self = GTK_NAMED_ACTION (ptr: action);
1083 const GVariantType *parameter_type;
1084 GtkActionMuxer *muxer;
1085 gboolean enabled;
1086
1087 muxer = _gtk_widget_get_action_muxer (widget, FALSE);
1088 if (muxer == NULL)
1089 return FALSE;
1090
1091 if (!gtk_action_muxer_query_action (muxer, action_name: self->name,
1092 enabled: &enabled, parameter_type: &parameter_type,
1093 NULL, NULL, NULL))
1094 return FALSE;
1095
1096 if (!enabled)
1097 return FALSE;
1098
1099 /* We found an action with the correct name and it's enabled.
1100 * This is the action that we are going to try to invoke.
1101 *
1102 * There is still the possibility that the args don't
1103 * match the expected parameter type. In that case, we will print
1104 * a warning.
1105 */
1106 if (!check_parameter_type (args, parameter_type))
1107 return FALSE;
1108
1109 gtk_action_muxer_activate_action (muxer, action_name: self->name, parameter: args);
1110
1111 return TRUE;
1112}
1113
1114static void
1115gtk_named_action_print (GtkShortcutAction *action,
1116 GString *string)
1117{
1118 GtkNamedAction *self = GTK_NAMED_ACTION (ptr: action);
1119
1120 g_string_append_printf (string, format: "action(%s)", self->name);
1121}
1122
1123static void
1124gtk_named_action_set_property (GObject *gobject,
1125 guint prop_id,
1126 const GValue *value,
1127 GParamSpec *pspec)
1128{
1129 GtkNamedAction *self = GTK_NAMED_ACTION (ptr: gobject);
1130
1131 switch (prop_id)
1132 {
1133 case NAMED_PROP_ACTION_NAME:
1134 self->name = g_value_dup_string (value);
1135 break;
1136
1137 default:
1138 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
1139 }
1140}
1141
1142static void
1143gtk_named_action_get_property (GObject *gobject,
1144 guint prop_id,
1145 GValue *value,
1146 GParamSpec *pspec)
1147{
1148 GtkNamedAction *self = GTK_NAMED_ACTION (ptr: gobject);
1149
1150 switch (prop_id)
1151 {
1152 case NAMED_PROP_ACTION_NAME:
1153 g_value_set_string (value, v_string: self->name);
1154 break;
1155
1156 default:
1157 G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
1158 }
1159}
1160
1161static void
1162gtk_named_action_constructed (GObject *gobject)
1163{
1164 GtkNamedAction *self G_GNUC_UNUSED = GTK_NAMED_ACTION (ptr: gobject);
1165
1166 g_assert (self->name != NULL && self->name[0] != '\0');
1167
1168 G_OBJECT_CLASS (gtk_named_action_parent_class)->constructed (gobject);
1169}
1170
1171static void
1172gtk_named_action_class_init (GtkNamedActionClass *klass)
1173{
1174 GtkShortcutActionClass *action_class = GTK_SHORTCUT_ACTION_CLASS (ptr: klass);
1175 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
1176
1177 gobject_class->constructed = gtk_named_action_constructed;
1178 gobject_class->set_property = gtk_named_action_set_property;
1179 gobject_class->get_property = gtk_named_action_get_property;
1180 gobject_class->finalize = gtk_named_action_finalize;
1181
1182 action_class->activate = gtk_named_action_activate;
1183 action_class->print = gtk_named_action_print;
1184
1185 /**
1186 * GtkNamedAction:action-name: (attributes org.gtk.Property.get=gtk_named_action_get_action_name)
1187 *
1188 * The name of the action to activate.
1189 */
1190 named_props[NAMED_PROP_ACTION_NAME] =
1191 g_param_spec_string (I_("action-name"),
1192 P_("Action Name"),
1193 P_("The name of the action to activate"),
1194 NULL,
1195 G_PARAM_STATIC_STRINGS |
1196 G_PARAM_READWRITE |
1197 G_PARAM_CONSTRUCT_ONLY);
1198
1199 g_object_class_install_properties (oclass: gobject_class, n_pspecs: NAMED_N_PROPS, pspecs: named_props);
1200}
1201
1202static void
1203gtk_named_action_init (GtkNamedAction *self)
1204{
1205}
1206
1207/**
1208 * gtk_named_action_new:
1209 * @name: the detailed name of the action
1210 *
1211 * Creates an action that when activated, activates
1212 * the named action on the widget.
1213 *
1214 * It also passes the given arguments to it.
1215 *
1216 * See [method@Gtk.Widget.insert_action_group] for
1217 * how to add actions to widgets.
1218 *
1219 * Returns: (transfer full) (type GtkNamedAction): a new `GtkShortcutAction`
1220 */
1221GtkShortcutAction *
1222gtk_named_action_new (const char *name)
1223{
1224 g_return_val_if_fail (name != NULL, NULL);
1225
1226 return g_object_new (GTK_TYPE_NAMED_ACTION,
1227 first_property_name: "action-name", name,
1228 NULL);
1229}
1230
1231/**
1232 * gtk_named_action_get_action_name: (attributes org.gtk.Method.get_property=action-name)
1233 * @self: a named action
1234 *
1235 * Returns the name of the action that will be activated.
1236 *
1237 * Returns: the name of the action to activate
1238 */
1239const char *
1240gtk_named_action_get_action_name (GtkNamedAction *self)
1241{
1242 g_return_val_if_fail (GTK_IS_NAMED_ACTION (self), NULL);
1243
1244 return self->name;
1245}
1246

source code of gtk/gtk/gtkshortcutaction.c