summaryrefslogtreecommitdiff
path: root/gtk/gtkeventcontrollerkey.c
diff options
context:
space:
mode:
Diffstat (limited to 'gtk/gtkeventcontrollerkey.c')
-rw-r--r--gtk/gtkeventcontrollerkey.c271
1 files changed, 271 insertions, 0 deletions
diff --git a/gtk/gtkeventcontrollerkey.c b/gtk/gtkeventcontrollerkey.c
new file mode 100644
index 0000000000..70c34bd4e6
--- /dev/null
+++ b/gtk/gtkeventcontrollerkey.c
@@ -0,0 +1,271 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 2017, Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author(s): Carlos Garnacho <carlosg@gnome.org>
+ */
+
+#include "config.h"
+
+#include "gtkintl.h"
+#include "gtkprivate.h"
+#include "gtkwidgetprivate.h"
+#include "gtkeventcontrollerprivate.h"
+#include "gtkeventcontrollerkey.h"
+#include "gtkbindings.h"
+
+#include <gdk/gdk.h>
+
+struct _GtkEventControllerKey
+{
+ GtkEventController parent_instance;
+ GtkIMContext *im_context;
+ GHashTable *pressed_keys;
+
+ const GdkEvent *current_event;
+};
+
+struct _GtkEventControllerKeyClass
+{
+ GtkEventControllerClass parent_class;
+};
+
+enum {
+ KEY_PRESSED,
+ KEY_RELEASED,
+ MODIFIERS,
+ IM_UPDATE,
+ FOCUS_IN,
+ FOCUS_OUT,
+ N_SIGNALS
+};
+
+static guint signals[N_SIGNALS] = { 0 };
+
+G_DEFINE_TYPE (GtkEventControllerKey, gtk_event_controller_key,
+ GTK_TYPE_EVENT_CONTROLLER)
+
+static void
+gtk_event_controller_finalize (GObject *object)
+{
+ GtkEventControllerKey *key = GTK_EVENT_CONTROLLER_KEY (object);
+
+ g_hash_table_destroy (key->pressed_keys);
+ g_clear_object (&key->im_context);
+
+ G_OBJECT_CLASS (gtk_event_controller_key_parent_class)->finalize (object);
+}
+
+static gboolean
+gtk_event_controller_key_handle_event (GtkEventController *controller,
+ const GdkEvent *event)
+{
+ GtkEventControllerKey *key = GTK_EVENT_CONTROLLER_KEY (controller);
+ GdkEventType event_type = gdk_event_get_event_type (event);
+ gboolean handled;
+ GdkModifierType state;
+ guint16 keycode;
+ guint keyval;
+
+ if (event_type == GDK_FOCUS_CHANGE)
+ {
+ if (event->focus_change.in)
+ g_signal_emit (controller, signals[FOCUS_IN], 0);
+ else
+ g_signal_emit (controller, signals[FOCUS_OUT], 0);
+
+ return FALSE;
+ }
+
+ if (event_type != GDK_KEY_PRESS && event_type != GDK_KEY_RELEASE)
+ return FALSE;
+
+ if (key->im_context &&
+ gtk_im_context_filter_keypress (key->im_context, (GdkEventKey *) event))
+ {
+ g_signal_emit (controller, signals[IM_UPDATE], 0);
+ return TRUE;
+ }
+
+ if (!gdk_event_get_state (event, &state) || !event->key.is_modifier)
+ return FALSE;
+
+ key->current_event = event;
+
+ if (event->key.is_modifier)
+ {
+ if (event_type == GDK_KEY_PRESS)
+ g_signal_emit (controller, signals[MODIFIERS], 0, state, &handled);
+ else
+ handled = TRUE;
+
+ if (handled == TRUE)
+ {
+ key->current_event = NULL;
+ return TRUE;
+ }
+ }
+
+ gdk_event_get_keycode (event, &keycode);
+ gdk_event_get_keyval (event, &keyval);
+
+ if (event_type == GDK_KEY_PRESS)
+ {
+ g_signal_emit (controller, signals[KEY_PRESSED], 0,
+ keyval, keycode, state, &handled);
+ if (handled)
+ g_hash_table_add (key->pressed_keys, GUINT_TO_POINTER (keyval));
+ }
+ else if (event_type == GDK_KEY_RELEASE)
+ {
+ g_signal_emit (controller, signals[KEY_RELEASED], 0,
+ keyval, keycode, state);
+
+ handled = g_hash_table_lookup (key->pressed_keys, GUINT_TO_POINTER (keyval)) != NULL;
+ g_hash_table_remove (key->pressed_keys, GUINT_TO_POINTER (keyval));
+ }
+ else
+ handled = FALSE;
+
+ key->current_event = NULL;
+
+ return handled;
+}
+
+static void
+gtk_event_controller_key_class_init (GtkEventControllerKeyClass *klass)
+{
+ GtkEventControllerClass *controller_class = GTK_EVENT_CONTROLLER_CLASS (klass);
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = gtk_event_controller_finalize;
+ controller_class->handle_event = gtk_event_controller_key_handle_event;
+
+ signals[KEY_PRESSED] =
+ g_signal_new (I_("key-pressed"),
+ GTK_TYPE_EVENT_CONTROLLER_KEY,
+ G_SIGNAL_RUN_LAST,
+ 0, _gtk_boolean_handled_accumulator, NULL, NULL,
+ G_TYPE_BOOLEAN, 3, G_TYPE_UINT, G_TYPE_UINT, GDK_TYPE_MODIFIER_TYPE);
+ signals[KEY_RELEASED] =
+ g_signal_new (I_("key-released"),
+ GTK_TYPE_EVENT_CONTROLLER_KEY,
+ G_SIGNAL_RUN_LAST,
+ 0, NULL, NULL, NULL,
+ G_TYPE_NONE, 3, G_TYPE_UINT, G_TYPE_UINT, GDK_TYPE_MODIFIER_TYPE);
+ signals[MODIFIERS] =
+ g_signal_new (I_("modifiers"),
+ GTK_TYPE_EVENT_CONTROLLER_KEY,
+ G_SIGNAL_RUN_LAST,
+ 0, NULL, NULL,
+ g_cclosure_marshal_BOOLEAN__FLAGS,
+ G_TYPE_BOOLEAN, 1, GDK_TYPE_MODIFIER_TYPE);
+ signals[IM_UPDATE] =
+ g_signal_new (I_("im-update"),
+ GTK_TYPE_EVENT_CONTROLLER_KEY,
+ G_SIGNAL_RUN_LAST,
+ 0, NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+ signals[FOCUS_IN] =
+ g_signal_new (I_("focus-in"),
+ GTK_TYPE_EVENT_CONTROLLER_KEY,
+ G_SIGNAL_RUN_LAST,
+ 0, NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+ signals[FOCUS_OUT] =
+ g_signal_new (I_("focus-out"),
+ GTK_TYPE_EVENT_CONTROLLER_KEY,
+ G_SIGNAL_RUN_LAST,
+ 0, NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+}
+
+static void
+gtk_event_controller_key_init (GtkEventControllerKey *controller)
+{
+ controller->pressed_keys = g_hash_table_new (NULL, NULL);
+}
+
+GtkEventController *
+gtk_event_controller_key_new (GtkWidget *widget)
+{
+ g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
+
+ return g_object_new (GTK_TYPE_EVENT_CONTROLLER_KEY,
+ "widget", widget,
+ NULL);
+}
+
+void
+gtk_event_controller_key_set_im_context (GtkEventControllerKey *controller,
+ GtkIMContext *im_context)
+{
+ g_return_if_fail (GTK_IS_EVENT_CONTROLLER_KEY (controller));
+ g_return_if_fail (!im_context || GTK_IS_IM_CONTEXT (im_context));
+
+ if (controller->im_context)
+ gtk_im_context_reset (controller->im_context);
+
+ g_set_object (&controller->im_context, im_context);
+}
+
+/**
+ * gtk_event_controller_key_get_im_context:
+ * @controller: a #GtkEventControllerKey
+ *
+ * Gets the IM context of a key controller.
+ *
+ * Returns: (transfer none): the IM context
+ *
+ * Since: 3.94
+ **/
+GtkIMContext *
+gtk_event_controller_key_get_im_context (GtkEventControllerKey *controller)
+{
+ g_return_val_if_fail (GTK_IS_EVENT_CONTROLLER_KEY (controller), NULL);
+
+ return controller->im_context;
+}
+
+gboolean
+gtk_event_controller_key_forward (GtkEventControllerKey *controller,
+ GtkWidget *widget)
+{
+ g_return_val_if_fail (GTK_IS_EVENT_CONTROLLER_KEY (controller), FALSE);
+ g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
+ g_return_val_if_fail (controller->current_event != NULL, FALSE);
+
+ if (!gtk_widget_get_realized (widget))
+ gtk_widget_realize (widget);
+
+ if (_gtk_widget_captured_event (widget, (GdkEvent *) controller->current_event))
+ return TRUE;
+ if (gtk_widget_event (widget, (GdkEvent *) controller->current_event))
+ return TRUE;
+
+ return FALSE;
+}
+
+guint
+gtk_event_controller_key_get_group (GtkEventControllerKey *controller)
+{
+ g_return_val_if_fail (GTK_IS_EVENT_CONTROLLER_KEY (controller), FALSE);
+ g_return_val_if_fail (controller->current_event != NULL, FALSE);
+
+ return controller->current_event->key.group;
+}