[gtk/wip/otte/listview: 110/169] bindings: Add gtk_binding_entry_add_action()
- From: Benjamin Otte <otte src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk/wip/otte/listview: 110/169] bindings: Add gtk_binding_entry_add_action()
- Date: Tue, 15 Oct 2019 05:18:57 +0000 (UTC)
commit 19304c1d2c8df4ca1e8ba11337ece80553911761
Author: Benjamin Otte <otte redhat com>
Date: Mon Oct 14 21:34:40 2019 +0200
bindings: Add gtk_binding_entry_add_action()
Allows registering bindings for activating widget actions, as an
alternative to signal emissions.
docs/reference/gtk/gtk4-sections.txt | 2 +
gtk/gtkbindings.c | 318 ++++++++++++++++++++++++++---------
gtk/gtkbindings.h | 14 ++
3 files changed, 258 insertions(+), 76 deletions(-)
---
diff --git a/docs/reference/gtk/gtk4-sections.txt b/docs/reference/gtk/gtk4-sections.txt
index 6ba53c0c56..2ad5b92fcc 100644
--- a/docs/reference/gtk/gtk4-sections.txt
+++ b/docs/reference/gtk/gtk4-sections.txt
@@ -5247,6 +5247,8 @@ gtk_binding_set_find
gtk_bindings_activate
gtk_bindings_activate_event
gtk_binding_set_activate
+gtk_binding_entry_add_action
+gtk_binding_entry_add_action_variant
gtk_binding_entry_add_signal
gtk_binding_entry_add_signal_from_string
gtk_binding_entry_skip
diff --git a/gtk/gtkbindings.c b/gtk/gtkbindings.c
index db9d6bd2a7..59c1815772 100644
--- a/gtk/gtkbindings.c
+++ b/gtk/gtkbindings.c
@@ -138,6 +138,12 @@ struct _GtkBindingArg
} d;
};
+typedef enum
+{
+ GTK_BINDING_SIGNAL,
+ GTK_BINDING_ACTION
+} GtkBindingActionType;
+
/**
* GtkBindingSignal:
* @next: implementation detail
@@ -151,10 +157,16 @@ struct _GtkBindingArg
*/
struct _GtkBindingSignal
{
- GtkBindingSignal *next;
- gchar *signal_name;
- guint n_args;
- GtkBindingArg *args;
+ GtkBindingSignal *next;
+ gchar *signal_name;
+ GtkBindingActionType action_type;
+ union {
+ struct {
+ guint n_args;
+ GtkBindingArg *args;
+ };
+ GVariant *variant;
+ };
};
/* --- variables --- */
@@ -168,13 +180,14 @@ static GQuark key_id_class_binding_set = 0;
/* --- functions --- */
static GtkBindingSignal*
-binding_signal_new (const gchar *signal_name,
- guint n_args)
+binding_signal_new_signal (const gchar *signal_name,
+ guint n_args)
{
GtkBindingSignal *signal;
signal = (GtkBindingSignal *) g_slice_alloc0 (sizeof (GtkBindingSignal) + n_args * sizeof (GtkBindingArg));
signal->next = NULL;
+ signal->action_type = GTK_BINDING_SIGNAL;
signal->signal_name = (gchar *)g_intern_string (signal_name);
signal->n_args = n_args;
signal->args = (GtkBindingArg *)(signal + 1);
@@ -182,17 +195,48 @@ binding_signal_new (const gchar *signal_name,
return signal;
}
+static GtkBindingSignal*
+binding_signal_new_action (const gchar *signal_name,
+ GVariant *variant)
+{
+ GtkBindingSignal *signal;
+
+ signal = g_slice_new0 (GtkBindingSignal);
+ signal->next = NULL;
+ signal->action_type = GTK_BINDING_ACTION;
+ signal->signal_name = (gchar *)g_intern_string (signal_name);
+ signal->variant = variant;
+ if (variant)
+ g_variant_ref_sink (variant);
+
+ return signal;
+}
+
static void
binding_signal_free (GtkBindingSignal *sig)
{
guint i;
- for (i = 0; i < sig->n_args; i++)
+ switch (sig->action_type)
{
- if (G_TYPE_FUNDAMENTAL (sig->args[i].arg_type) == G_TYPE_STRING)
- g_free (sig->args[i].d.string_data);
+ case GTK_BINDING_SIGNAL:
+ for (i = 0; i < sig->n_args; i++)
+ {
+ if (G_TYPE_FUNDAMENTAL (sig->args[i].arg_type) == G_TYPE_STRING)
+ g_free (sig->args[i].d.string_data);
+ }
+ g_slice_free1 (sizeof (GtkBindingSignal) + sig->n_args * sizeof (GtkBindingArg), sig);
+ break;
+
+ case GTK_BINDING_ACTION:
+ g_clear_pointer (&sig->variant, g_variant_unref);
+ g_slice_free (GtkBindingSignal, sig);
+ break;
+
+ default:
+ g_assert_not_reached ();
+ break;
}
- g_slice_free1 (sizeof (GtkBindingSignal) + sig->n_args * sizeof (GtkBindingArg), sig);
}
static guint
@@ -559,6 +603,97 @@ binding_compose_params (GObject *object,
return valid;
}
+static gboolean
+binding_signal_activate_signal (GtkBindingSignal *sig,
+ GObject *object)
+{
+ GSignalQuery query;
+ guint signal_id;
+ GValue *params = NULL;
+ GValue return_val = G_VALUE_INIT;
+ gboolean handled = FALSE;
+
+ signal_id = g_signal_lookup (sig->signal_name, G_OBJECT_TYPE (object));
+ if (!signal_id)
+ {
+ g_warning ("gtk_binding_entry_activate(): "
+ "could not find signal \"%s\" in the '%s' class ancestry",
+ sig->signal_name,
+ g_type_name (G_OBJECT_TYPE (object)));
+ return FALSE;
+ }
+
+ g_signal_query (signal_id, &query);
+ if (query.n_params != sig->n_args ||
+ (query.return_type != G_TYPE_NONE && query.return_type != G_TYPE_BOOLEAN) ||
+ !binding_compose_params (object, sig->args, &query, ¶ms))
+ {
+ g_warning ("gtk_binding_entry_activate(): "
+ "signature mismatch for signal \"%s\" in the '%s' class ancestry",
+ sig->signal_name,
+ g_type_name (G_OBJECT_TYPE (object)));
+ return FALSE;
+ }
+ else if (!(query.signal_flags & G_SIGNAL_ACTION))
+ {
+ g_warning ("gtk_binding_entry_activate(): "
+ "signal \"%s\" in the '%s' class ancestry cannot be used for action emissions",
+ sig->signal_name,
+ g_type_name (G_OBJECT_TYPE (object)));
+ return FALSE;
+ }
+
+ if (query.return_type == G_TYPE_BOOLEAN)
+ g_value_init (&return_val, G_TYPE_BOOLEAN);
+
+ g_signal_emitv (params, signal_id, 0, &return_val);
+
+ if (query.return_type == G_TYPE_BOOLEAN)
+ {
+ if (g_value_get_boolean (&return_val))
+ handled = TRUE;
+ g_value_unset (&return_val);
+ }
+ else
+ handled = TRUE;
+
+ if (params != NULL)
+ {
+ guint i;
+
+ for (i = 0; i < query.n_params + 1; i++)
+ g_value_unset (¶ms[i]);
+
+ g_free (params);
+ }
+
+ return handled;
+}
+
+static gboolean
+binding_signal_activate_action (GtkBindingSignal *sig,
+ GObject *object)
+{
+ if (!GTK_IS_WIDGET (object))
+ {
+ g_warning ("gtk_binding_entry_activate(): "
+ "actions must be emitted on GtkWidget subtypes, %s is not supported",
+ G_OBJECT_TYPE_NAME (object));
+ return FALSE;
+ }
+
+ if (!gtk_widget_activate_action_variant (GTK_WIDGET (object), sig->signal_name, sig->variant))
+ {
+ g_warning ("gtk_binding_entry_activate(): "
+ "action \"%s\" does not exist on class \"%s\"",
+ sig->signal_name,
+ G_OBJECT_TYPE_NAME (object));
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
static gboolean
gtk_binding_entry_activate (GtkBindingEntry *entry,
GObject *object)
@@ -566,7 +701,6 @@ gtk_binding_entry_activate (GtkBindingEntry *entry,
GtkBindingSignal *sig;
gboolean old_emission;
gboolean handled = FALSE;
- gint i;
old_emission = entry->in_emission;
entry->in_emission = TRUE;
@@ -575,73 +709,19 @@ gtk_binding_entry_activate (GtkBindingEntry *entry,
for (sig = entry->signals; sig; sig = sig->next)
{
- GSignalQuery query;
- guint signal_id;
- GValue *params = NULL;
- GValue return_val = G_VALUE_INIT;
- gchar *accelerator = NULL;
-
- signal_id = g_signal_lookup (sig->signal_name, G_OBJECT_TYPE (object));
- if (!signal_id)
- {
- accelerator = gtk_accelerator_name (entry->keyval, entry->modifiers);
- g_warning ("gtk_binding_entry_activate(): binding \"%s::%s\": "
- "could not find signal \"%s\" in the '%s' class ancestry",
- entry->binding_set->set_name,
- accelerator,
- sig->signal_name,
- g_type_name (G_OBJECT_TYPE (object)));
- g_free (accelerator);
- continue;
- }
-
- g_signal_query (signal_id, &query);
- if (query.n_params != sig->n_args ||
- (query.return_type != G_TYPE_NONE && query.return_type != G_TYPE_BOOLEAN) ||
- !binding_compose_params (object, sig->args, &query, ¶ms))
- {
- accelerator = gtk_accelerator_name (entry->keyval, entry->modifiers);
- g_warning ("gtk_binding_entry_activate(): binding \"%s::%s\": "
- "signature mismatch for signal \"%s\" in the '%s' class ancestry",
- entry->binding_set->set_name,
- accelerator,
- sig->signal_name,
- g_type_name (G_OBJECT_TYPE (object)));
- }
- else if (!(query.signal_flags & G_SIGNAL_ACTION))
- {
- accelerator = gtk_accelerator_name (entry->keyval, entry->modifiers);
- g_warning ("gtk_binding_entry_activate(): binding \"%s::%s\": "
- "signal \"%s\" in the '%s' class ancestry cannot be used for action emissions",
- entry->binding_set->set_name,
- accelerator,
- sig->signal_name,
- g_type_name (G_OBJECT_TYPE (object)));
- }
- g_free (accelerator);
- if (accelerator)
- continue;
-
- if (query.return_type == G_TYPE_BOOLEAN)
- g_value_init (&return_val, G_TYPE_BOOLEAN);
-
- g_signal_emitv (params, signal_id, 0, &return_val);
-
- if (query.return_type == G_TYPE_BOOLEAN)
+ switch (sig->action_type)
{
- if (g_value_get_boolean (&return_val))
- handled = TRUE;
- g_value_unset (&return_val);
- }
- else
- handled = TRUE;
+ case GTK_BINDING_SIGNAL:
+ handled = binding_signal_activate_signal (sig, object);
+ break;
- if (params != NULL)
- {
- for (i = 0; i < query.n_params + 1; i++)
- g_value_unset (¶ms[i]);
+ case GTK_BINDING_ACTION:
+ handled = binding_signal_activate_action (sig, object);
+ break;
- g_free (params);
+ default:
+ g_assert_not_reached ();
+ break;
}
if (entry->destroyed)
@@ -889,7 +969,7 @@ gtk_binding_entry_add_signall (GtkBindingSet *binding_set,
keyval = gdk_keyval_to_lower (keyval);
modifiers = modifiers & BINDING_MOD_MASK ();
- signal = binding_signal_new (signal_name, g_slist_length (binding_args));
+ signal = binding_signal_new_signal (signal_name, g_slist_length (binding_args));
arg = signal->args;
for (slist = binding_args; slist; slist = slist->next)
@@ -1060,6 +1140,92 @@ gtk_binding_entry_add_signal (GtkBindingSet *binding_set,
g_slist_free (free_slist);
}
+/**
+ * gtk_binding_entry_add_action_variant:
+ * @binding_set: a #GtkBindingSet to install an entry for
+ * @keyval: key value of binding to install
+ * @modifiers: key modifier of binding to install
+ * @action_name: signal to execute upon activation
+ * @args: #GVariant of the arguments or %NULL if none
+ *
+ * Override or install a new key binding for @keyval with @modifiers on
+ * @binding_set. When the binding is activated, @action_name will be
+ * activated on the target widget, with @args used as arguments.
+ */
+void
+gtk_binding_entry_add_action_variant (GtkBindingSet *binding_set,
+ guint keyval,
+ GdkModifierType modifiers,
+ const gchar *action_name,
+ GVariant *args)
+{
+ GtkBindingEntry *entry;
+ GtkBindingSignal *signal, **signal_p;
+
+ g_return_if_fail (binding_set != NULL);
+ g_return_if_fail (action_name != NULL);
+
+ keyval = gdk_keyval_to_lower (keyval);
+ modifiers = modifiers & BINDING_MOD_MASK ();
+
+ signal = binding_signal_new_action (action_name, args);
+
+ entry = binding_ht_lookup_entry (binding_set, keyval, modifiers);
+ if (!entry)
+ {
+ gtk_binding_entry_clear_internal (binding_set, keyval, modifiers);
+ entry = binding_ht_lookup_entry (binding_set, keyval, modifiers);
+ }
+ signal_p = &entry->signals;
+ while (*signal_p)
+ signal_p = &(*signal_p)->next;
+ *signal_p = signal;
+}
+
+/**
+ * gtk_binding_entry_add_action:
+ * @binding_set: a #GtkBindingSet to install an entry for
+ * @keyval: key value of binding to install
+ * @modifiers: key modifier of binding to install
+ * @action_name: signal to execute upon activation
+ * @format_string: GVariant format string for arguments or %NULL
+ * for no arguments
+ * @...: arguments, as given by format string
+ *
+ * Override or install a new key binding for @keyval with @modifiers on
+ * @binding_set. When the binding is activated, @action_name will be
+ * activated on the target widget, with arguments read according to
+ * @format_string.
+ */
+void
+gtk_binding_entry_add_action (GtkBindingSet *binding_set,
+ guint keyval,
+ GdkModifierType modifiers,
+ const char *action_name,
+ const char *format_string,
+ ...)
+{
+ GVariant *parameters = NULL;
+
+ g_return_if_fail (binding_set != NULL);
+ g_return_if_fail (action_name != NULL);
+
+ if (format_string != NULL)
+ {
+ va_list args;
+
+ va_start (args, format_string);
+ parameters = g_variant_new_va (format_string, NULL, &args);
+ va_end (args);
+
+ g_variant_ref_sink (parameters);
+ }
+
+ gtk_binding_entry_add_action_variant (binding_set, keyval, modifiers, action_name, parameters);
+
+ g_clear_pointer (¶meters, g_variant_unref);
+}
+
static guint
gtk_binding_parse_signal (GScanner *scanner,
GtkBindingSet *binding_set,
diff --git a/gtk/gtkbindings.h b/gtk/gtkbindings.h
index 887869f981..5f2b019006 100644
--- a/gtk/gtkbindings.h
+++ b/gtk/gtkbindings.h
@@ -76,6 +76,20 @@ GDK_AVAILABLE_IN_ALL
GTokenType gtk_binding_entry_add_signal_from_string
(GtkBindingSet *binding_set,
const gchar *signal_desc);
+GDK_AVAILABLE_IN_ALL
+void gtk_binding_entry_add_action_variant
+ (GtkBindingSet *binding_set,
+ guint keyval,
+ GdkModifierType modifiers,
+ const char *action_name,
+ GVariant *args);
+GDK_AVAILABLE_IN_ALL
+void gtk_binding_entry_add_action (GtkBindingSet *binding_set,
+ guint keyval,
+ GdkModifierType modifiers,
+ const char *action_name,
+ const char *format_string,
+ ...);
GDK_AVAILABLE_IN_ALL
void gtk_binding_entry_remove (GtkBindingSet *binding_set,
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]