summaryrefslogtreecommitdiff
path: root/gtk/gtkbindings.c
diff options
context:
space:
mode:
authorCarlos Garnacho <carlosg@gnome.org>2011-01-28 01:50:14 +0100
committerMatthias Clasen <mclasen@redhat.com>2011-01-30 21:24:51 -0500
commit53ca6eb0b5b821bde2d96b873f0e02588aa2a7fb (patch)
treeea5d95bc82801f5e885e0f34f59b12439d3de6a3 /gtk/gtkbindings.c
parentb98caf938594504508dfc4546d6f606e6edeab9d (diff)
downloadgtk+-53ca6eb0b5b821bde2d96b873f0e02588aa2a7fb.tar.gz
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.
Diffstat (limited to 'gtk/gtkbindings.c')
-rw-r--r--gtk/gtkbindings.c321
1 files changed, 321 insertions, 0 deletions
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:
+ * <informalexample><programlisting>
+ * bind <replaceable>key</replaceable> {
+ * <replaceable>signalname</replaceable> (<replaceable>param</replaceable>, ...)
+ * ...
+ * }
+ * </programlisting></informalexample>
+ *
+ * Or they may also unbind a key combination:
+ * <informalexample><programlisting>
+ * unbind <replaceable>key</replaceable>
+ * </programlisting></informalexample>
+ *
+ * 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