From 53ca6eb0b5b821bde2d96b873f0e02588aa2a7fb Mon Sep 17 00:00:00 2001 From: Carlos Garnacho Date: Fri, 28 Jan 2011 01:50:14 +0100 Subject: [PATCH] Add gtk_binding_entry_add_signal_from_string() This function rescues part of the old parser (which is now standalone) to load a bind/unbind definition string into a GtkBindingSet. --- docs/reference/gtk/gtk3-sections.txt | 1 + gtk/gtk.symbols | 1 + gtk/gtkbindings.c | 321 +++++++++++++++++++++++++++ gtk/gtkbindings.h | 4 + 4 files changed, 327 insertions(+) diff --git a/docs/reference/gtk/gtk3-sections.txt b/docs/reference/gtk/gtk3-sections.txt index c75d5bbf68..2dab319651 100644 --- a/docs/reference/gtk/gtk3-sections.txt +++ b/docs/reference/gtk/gtk3-sections.txt @@ -6009,6 +6009,7 @@ gtk_bindings_activate gtk_bindings_activate_event gtk_binding_set_activate gtk_binding_entry_add_signal +gtk_binding_entry_add_signal_from_string gtk_binding_entry_skip gtk_binding_entry_remove gtk_binding_set_add_path diff --git a/gtk/gtk.symbols b/gtk/gtk.symbols index 166539bf6d..f29e38aa36 100644 --- a/gtk/gtk.symbols +++ b/gtk/gtk.symbols @@ -254,6 +254,7 @@ gtk_assistant_update_buttons_state gtk_attach_options_get_type G_GNUC_CONST gtk_binding_entry_add_signal gtk_binding_entry_add_signall +gtk_binding_entry_add_signal_from_string gtk_binding_entry_remove gtk_binding_entry_skip gtk_bindings_activate diff --git a/gtk/gtkbindings.c b/gtk/gtkbindings.c index 70a6418657..65eda703b3 100644 --- a/gtk/gtkbindings.c +++ b/gtk/gtkbindings.c @@ -53,6 +53,10 @@ typedef struct { guint seq_id; } PatternSpec; +typedef enum { + GTK_BINDING_TOKEN_BIND, + GTK_BINDING_TOKEN_UNBIND +} GtkBindingTokens; /* --- variables --- */ static GHashTable *binding_entry_hash_table = NULL; @@ -978,6 +982,323 @@ gtk_binding_entry_add_signal (GtkBindingSet *binding_set, g_slist_free (free_slist); } +static guint +gtk_binding_parse_signal (GScanner *scanner, + GtkBindingSet *binding_set, + guint keyval, + GdkModifierType modifiers) +{ + gchar *signal; + guint expected_token = 0; + GSList *args; + GSList *slist; + gboolean done; + gboolean negate; + gboolean need_arg; + gboolean seen_comma; + + g_return_val_if_fail (scanner != NULL, G_TOKEN_ERROR); + + g_scanner_get_next_token (scanner); + + if (scanner->token != G_TOKEN_STRING) + return G_TOKEN_STRING; + + g_scanner_peek_next_token (scanner); + + if (scanner->next_token != '(') + { + g_scanner_get_next_token (scanner); + return '('; + } + + signal = g_strdup (scanner->value.v_string); + g_scanner_get_next_token (scanner); + + negate = FALSE; + args = NULL; + done = FALSE; + need_arg = TRUE; + seen_comma = FALSE; + scanner->config->scan_symbols = FALSE; + + do + { + GtkBindingArg *arg; + + if (need_arg) + expected_token = G_TOKEN_INT; + else + expected_token = ')'; + + g_scanner_get_next_token (scanner); + + switch ((guint) scanner->token) + { + case G_TOKEN_FLOAT: + if (need_arg) + { + need_arg = FALSE; + arg = g_new (GtkBindingArg, 1); + arg->arg_type = G_TYPE_DOUBLE; + arg->d.double_data = scanner->value.v_float; + + if (negate) + { + arg->d.double_data = - arg->d.double_data; + negate = FALSE; + } + args = g_slist_prepend (args, arg); + } + else + done = TRUE; + + break; + case G_TOKEN_INT: + if (need_arg) + { + need_arg = FALSE; + arg = g_new (GtkBindingArg, 1); + arg->arg_type = G_TYPE_LONG; + arg->d.long_data = scanner->value.v_int; + + if (negate) + { + arg->d.long_data = - arg->d.long_data; + negate = FALSE; + } + args = g_slist_prepend (args, arg); + } + else + done = TRUE; + break; + case G_TOKEN_STRING: + if (need_arg && !negate) + { + need_arg = FALSE; + arg = g_new (GtkBindingArg, 1); + arg->arg_type = G_TYPE_STRING; + arg->d.string_data = g_strdup (scanner->value.v_string); + args = g_slist_prepend (args, arg); + } + else + done = TRUE; + + break; + case G_TOKEN_IDENTIFIER: + if (need_arg && !negate) + { + need_arg = FALSE; + arg = g_new (GtkBindingArg, 1); + arg->arg_type = GTK_TYPE_IDENTIFIER; + arg->d.string_data = g_strdup (scanner->value.v_identifier); + args = g_slist_prepend (args, arg); + } + else + done = TRUE; + + break; + case '-': + if (!need_arg) + done = TRUE; + else if (negate) + { + expected_token = G_TOKEN_INT; + done = TRUE; + } + else + negate = TRUE; + + break; + case ',': + seen_comma = TRUE; + if (need_arg) + done = TRUE; + else + need_arg = TRUE; + + break; + case ')': + if (!(need_arg && seen_comma) && !negate) + { + args = g_slist_reverse (args); + _gtk_binding_entry_add_signall (binding_set, + keyval, + modifiers, + signal, + args); + expected_token = G_TOKEN_NONE; + } + + done = TRUE; + break; + default: + done = TRUE; + break; + } + } + while (!done); + + scanner->config->scan_symbols = TRUE; + + for (slist = args; slist; slist = slist->next) + { + GtkBindingArg *arg; + + arg = slist->data; + + if (G_TYPE_FUNDAMENTAL (arg->arg_type) == G_TYPE_STRING) + g_free (arg->d.string_data); + g_free (arg); + } + + g_slist_free (args); + g_free (signal); + + return expected_token; +} + +static inline guint +gtk_binding_parse_bind (GScanner *scanner, + GtkBindingSet *binding_set) +{ + guint keyval = 0; + GdkModifierType modifiers = 0; + gboolean unbind = FALSE; + + g_return_val_if_fail (scanner != NULL, G_TOKEN_ERROR); + + g_scanner_get_next_token (scanner); + + if (scanner->token != G_TOKEN_SYMBOL) + return G_TOKEN_SYMBOL; + + if (scanner->value.v_symbol != GUINT_TO_POINTER (GTK_BINDING_TOKEN_BIND) && + scanner->value.v_symbol != GUINT_TO_POINTER (GTK_BINDING_TOKEN_UNBIND)) + return G_TOKEN_SYMBOL; + + unbind = (scanner->value.v_symbol == GUINT_TO_POINTER (GTK_BINDING_TOKEN_UNBIND)); + g_scanner_get_next_token (scanner); + + if (scanner->token != (guint) G_TOKEN_STRING) + return G_TOKEN_STRING; + + gtk_accelerator_parse (scanner->value.v_string, &keyval, &modifiers); + modifiers &= BINDING_MOD_MASK (); + + if (keyval == 0) + return G_TOKEN_STRING; + + if (unbind) + { + gtk_binding_entry_skip (binding_set, keyval, modifiers); + return G_TOKEN_NONE; + } + + g_scanner_get_next_token (scanner); + + if (scanner->token != '{') + return '{'; + + gtk_binding_entry_clear_internal (binding_set, keyval, modifiers); + g_scanner_peek_next_token (scanner); + + while (scanner->next_token != '}') + { + guint expected_token; + + switch (scanner->next_token) + { + case G_TOKEN_STRING: + expected_token = gtk_binding_parse_signal (scanner, + binding_set, + keyval, + modifiers); + if (expected_token != G_TOKEN_NONE) + return expected_token; + break; + default: + g_scanner_get_next_token (scanner); + return '}'; + } + + g_scanner_peek_next_token (scanner); + } + + g_scanner_get_next_token (scanner); + + return G_TOKEN_NONE; +} + +static GScanner * +create_signal_scanner (void) +{ + GScanner *scanner; + + scanner = g_scanner_new (NULL); + scanner->config->cset_identifier_nth = G_CSET_a_2_z G_CSET_A_2_Z G_CSET_DIGITS "-_"; + + g_scanner_scope_add_symbol (scanner, 0, "bind", GUINT_TO_POINTER (GTK_BINDING_TOKEN_BIND)); + g_scanner_scope_add_symbol (scanner, 0, "unbind", GUINT_TO_POINTER (GTK_BINDING_TOKEN_UNBIND)); + + g_scanner_set_scope (scanner, 0); + + return scanner; +} + +/** + * gtk_binding_entry_add_signal_from_string: + * @binding_set: a #GtkBindingSet + * @signal_desc: a signal description + * + * Parses a signal description from @signal_desc and incorporates + * it into @binding_set. + * + * signal descriptions may either bind a key combination to + * a signal: + * + * bind key { + * signalname (param, ...) + * ... + * } + * + * + * Or they may also unbind a key combination: + * + * unbind key + * + * + * Key combinations must be in a format that can be parsed by + * gtk_accelerator_parse(). + * + * Since: 3.0 + **/ +void +gtk_binding_entry_add_signal_from_string (GtkBindingSet *binding_set, + const gchar *signal_desc) +{ + static GScanner *scanner = NULL; + GTokenType ret; + + g_return_if_fail (binding_set != NULL); + g_return_if_fail (signal_desc != NULL); + + if (G_UNLIKELY (!scanner)) + scanner = create_signal_scanner (); + + g_scanner_input_text (scanner, signal_desc, + (guint) strlen (signal_desc)); + + ret = gtk_binding_parse_bind (scanner, binding_set); + + if (ret != G_TOKEN_NONE) + g_scanner_unexp_token (scanner, ret, NULL, NULL, NULL, + "Could not parse binding", FALSE); + + /* Reset for next use */ + g_scanner_set_scope (scanner, 0); +} + /** * gtk_binding_set_add_path: * @binding_set: a #GtkBindingSet to add a path to diff --git a/gtk/gtkbindings.h b/gtk/gtkbindings.h index 260eeb80b1..4529567a6c 100644 --- a/gtk/gtkbindings.h +++ b/gtk/gtkbindings.h @@ -124,6 +124,10 @@ void gtk_binding_entry_add_signall (GtkBindingSet *binding_set, GdkModifierType modifiers, const gchar *signal_name, GSList *binding_args); + +void gtk_binding_entry_add_signal_from_string (GtkBindingSet *binding_set, + const gchar *signal_desc); + void gtk_binding_entry_remove (GtkBindingSet *binding_set, guint keyval, GdkModifierType modifiers);