diff --git a/gtk/gtkshortcuttrigger.c b/gtk/gtkshortcuttrigger.c index 50f6958b18..0fdc5edb17 100644 --- a/gtk/gtkshortcuttrigger.c +++ b/gtk/gtkshortcuttrigger.c @@ -120,24 +120,63 @@ gtk_shortcut_trigger_trigger (GtkShortcutTrigger *self, * - `never`, for #GtkNeverTrigger * - a string parsed by gtk_accelerator_parse(), for a #GtkKeyvalTrigger * - underscore, followed by a single character, for #GtkMnemonicTrigger + * - two valid trigger strings, separated by a `|` character, for a + * #GtkAlternativeTrigger * - * Returns: (nullable): a new #GtkShortcutTrigger or %NULL on error + * Returns: (nullable) (transfer full): a new #GtkShortcutTrigger + * or %NULL on error */ GtkShortcutTrigger * gtk_shortcut_trigger_parse_string (const char *string) { GdkModifierType modifiers; guint keyval; + const char *sep; g_return_val_if_fail (string != NULL, NULL); + if ((sep = strchr (string, '|')) != NULL) + { + char *frag_a = g_strndup (string, sep - string); + const char *frag_b = sep + 1; + GtkShortcutTrigger *t1, *t2; + + /* empty first slot */ + if (*frag_a == '\0') + return NULL; + + /* empty second slot */ + if (*frag_b == '\0') + return NULL; + + t1 = gtk_shortcut_trigger_parse_string (frag_a); + if (t1 == NULL) + { + g_free (frag_a); + return NULL; + } + + t2 = gtk_shortcut_trigger_parse_string (frag_b); + if (t2 == NULL) + { + g_object_unref (t1); + g_free (frag_a); + return NULL; + } + + g_free (frag_a); + + return gtk_alternative_trigger_new (t1, t2); + } + if (g_str_equal (string, "never")) - return gtk_never_trigger_get (); + return g_object_ref (gtk_never_trigger_get ()); if (string[0] == '_') { - if (gtk_accelerator_parse (string + 1, &keyval, &modifiers)) - return gtk_mnemonic_trigger_new (keyval); + keyval = gdk_keyval_from_name (string + 1); + if (keyval != GDK_KEY_VoidSymbol) + return gtk_mnemonic_trigger_new (gdk_keyval_to_lower (keyval)); } if (gtk_accelerator_parse (string, &keyval, &modifiers)) @@ -396,7 +435,7 @@ static void gtk_never_trigger_print (GtkShortcutTrigger *trigger, GString *string) { - g_string_append (string, ""); + g_string_append (string, "never"); } static gboolean @@ -1089,7 +1128,7 @@ gtk_alternative_trigger_print (GtkShortcutTrigger *trigger, GtkAlternativeTrigger *self = GTK_ALTERNATIVE_TRIGGER (trigger); gtk_shortcut_trigger_print (self->first, string); - g_string_append (string, ", "); + g_string_append (string, "|"); gtk_shortcut_trigger_print (self->second, string); } diff --git a/testsuite/gtk/shortcuts.c b/testsuite/gtk/shortcuts.c index 27df0c839e..1f235b71ff 100644 --- a/testsuite/gtk/shortcuts.c +++ b/testsuite/gtk/shortcuts.c @@ -151,35 +151,183 @@ test_trigger_equal (void) } static void -test_trigger_parse (void) +test_trigger_parse_never (void) { - struct { + GtkShortcutTrigger *trigger; + + trigger = gtk_shortcut_trigger_parse_string ("never"); + g_assert_true (GTK_IS_NEVER_TRIGGER (trigger)); + + g_object_unref (trigger); +} + +static void +test_trigger_parse_keyval (void) +{ + const struct + { const char *str; GdkModifierType modifiers; guint keyval; + int trigger_type; } tests[] = { - { "z", GDK_CONTROL_MASK|GDK_MOD1_MASK, 'z' }, + { "z", GDK_CONTROL_MASK | GDK_MOD1_MASK, 'z' }, { "U", GDK_CONTROL_MASK, 'u' }, { "x", GDK_HYPER_MASK, 'x' }, { "y", GDK_META_MASK, 'y' }, { "KP_7", 0, GDK_KEY_KP_7 }, { "exclam", GDK_SHIFT_MASK, '!' }, }; - GtkShortcutTrigger *trigger; - int i; - for (i = 0; i < G_N_ELEMENTS (tests); i++) + for (int i = 0; i < G_N_ELEMENTS (tests); i++) { - trigger = gtk_shortcut_trigger_parse_string (tests[i].str); + g_test_message ("Checking: '%s'", tests[i].str); + + GtkShortcutTrigger *trigger = gtk_shortcut_trigger_parse_string (tests[i].str); g_assert_true (GTK_IS_KEYVAL_TRIGGER (trigger)); - g_assert_cmpint (gtk_keyval_trigger_get_modifiers (GTK_KEYVAL_TRIGGER (trigger)), ==, tests[i].modifiers); - g_assert_cmpuint (gtk_keyval_trigger_get_keyval (GTK_KEYVAL_TRIGGER (trigger)), ==, tests[i].keyval); + g_assert_cmpint (gtk_keyval_trigger_get_modifiers (GTK_KEYVAL_TRIGGER (trigger)), + ==, + tests[i].modifiers); + g_assert_cmpuint (gtk_keyval_trigger_get_keyval (GTK_KEYVAL_TRIGGER (trigger)), + ==, + tests[i].keyval); + g_object_unref (trigger); + } +} + +static void +test_trigger_parse_mnemonic (void) +{ + struct + { + const char *str; + guint keyval; + } tests[] = { + { "_A", GDK_KEY_a }, + { "_s", GDK_KEY_s }, + }; + + for (int i = 0; i < G_N_ELEMENTS (tests); i++) + { + g_test_message ("Checking: '%s'", tests[i].str); + + GtkShortcutTrigger *trigger = gtk_shortcut_trigger_parse_string (tests[i].str); + + g_assert_true (GTK_IS_MNEMONIC_TRIGGER (trigger)); + g_assert_cmpuint (gtk_mnemonic_trigger_get_keyval (GTK_MNEMONIC_TRIGGER (trigger)), + ==, + tests[i].keyval); + g_object_unref (trigger); + } +} + +static void +test_trigger_parse_alternative (void) +{ + enum + { + TRIGGER_NEVER, + TRIGGER_KEYVAL, + TRIGGER_MNEMONIC, + TRIGGER_ALTERNATIVE + }; + + const struct + { + const char *str; + int first; + int second; + } tests[] = { + { "U|U", TRIGGER_KEYVAL, TRIGGER_KEYVAL }, + { "_U|u", TRIGGER_MNEMONIC, TRIGGER_KEYVAL }, + { "x|_x|x", TRIGGER_KEYVAL, TRIGGER_ALTERNATIVE }, + }; + + for (int i = 0; i < G_N_ELEMENTS (tests); i++) + { + g_test_message ("Checking: '%s'", tests[i].str); + + GtkShortcutTrigger *trigger = gtk_shortcut_trigger_parse_string (tests[i].str); + + g_assert_true (GTK_IS_ALTERNATIVE_TRIGGER (trigger)); + + GtkShortcutTrigger *t1 = gtk_alternative_trigger_get_first (GTK_ALTERNATIVE_TRIGGER (trigger)); + + switch (tests[i].first) + { + case TRIGGER_NEVER: + g_assert_true (GTK_IS_NEVER_TRIGGER (t1)); + break; + + case TRIGGER_KEYVAL: + g_assert_true (GTK_IS_KEYVAL_TRIGGER (t1)); + break; + + case TRIGGER_MNEMONIC: + g_assert_true (GTK_IS_MNEMONIC_TRIGGER (t1)); + break; + + case TRIGGER_ALTERNATIVE: + g_assert_true (GTK_IS_ALTERNATIVE_TRIGGER (t1)); + break; + + default: + g_assert_not_reached (); + break; + } + + GtkShortcutTrigger *t2 = gtk_alternative_trigger_get_second (GTK_ALTERNATIVE_TRIGGER (trigger)); + + switch (tests[i].second) + { + case TRIGGER_NEVER: + g_assert_true (GTK_IS_NEVER_TRIGGER (t2)); + break; + + case TRIGGER_KEYVAL: + g_assert_true (GTK_IS_KEYVAL_TRIGGER (t2)); + break; + + case TRIGGER_MNEMONIC: + g_assert_true (GTK_IS_MNEMONIC_TRIGGER (t2)); + break; + + case TRIGGER_ALTERNATIVE: + g_assert_true (GTK_IS_ALTERNATIVE_TRIGGER (t2)); + break; + + default: + g_assert_not_reached (); + break; + } g_object_unref (trigger); } } +static void +test_trigger_parse_invalid (void) +{ + const char *tests[] = { + "", + "Never", + "Foo", + "Nyaa", + "never|", + "|never", + }; + + for (int i = 0; i < G_N_ELEMENTS (tests); i++) + { + g_test_message ("Checking: '%s'", tests[i]); + + GtkShortcutTrigger *trigger = gtk_shortcut_trigger_parse_string (tests[i]); + + g_assert_null (trigger); + } +} + static void test_trigger_trigger (void) { @@ -300,7 +448,11 @@ main (int argc, char *argv[]) g_test_add_func ("/shortcuts/trigger/basic", test_trigger_basic); g_test_add_func ("/shortcuts/trigger/equal", test_trigger_equal); - g_test_add_func ("/shortcuts/trigger/parse", test_trigger_parse); + g_test_add_func ("/shortcuts/trigger/parse/never", test_trigger_parse_never); + g_test_add_func ("/shortcuts/trigger/parse/keyval", test_trigger_parse_keyval); + g_test_add_func ("/shortcuts/trigger/parse/mnemonic", test_trigger_parse_mnemonic); + g_test_add_func ("/shortcuts/trigger/parse/alternative", test_trigger_parse_alternative); + g_test_add_func ("/shortcuts/trigger/parse/invalid", test_trigger_parse_invalid); g_test_add_func ("/shortcuts/trigger/trigger", test_trigger_trigger); g_test_add_func ("/shortcuts/action/basic", test_action_basic); g_test_add_func ("/shortcuts/action/activate", test_action_activate);