summaryrefslogtreecommitdiff
path: root/Source/WebKit2/UIProcess/gtk/KeyBindingTranslator.cpp
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
committerLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
commit1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch)
tree46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/WebKit2/UIProcess/gtk/KeyBindingTranslator.cpp
parent32761a6cee1d0dee366b885b7b9c777e67885688 (diff)
downloadWebKitGtk-tarball-master.tar.gz
Diffstat (limited to 'Source/WebKit2/UIProcess/gtk/KeyBindingTranslator.cpp')
-rw-r--r--Source/WebKit2/UIProcess/gtk/KeyBindingTranslator.cpp218
1 files changed, 218 insertions, 0 deletions
diff --git a/Source/WebKit2/UIProcess/gtk/KeyBindingTranslator.cpp b/Source/WebKit2/UIProcess/gtk/KeyBindingTranslator.cpp
new file mode 100644
index 000000000..f179f16f0
--- /dev/null
+++ b/Source/WebKit2/UIProcess/gtk/KeyBindingTranslator.cpp
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2010, 2011 Igalia S.L.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "config.h"
+#include "KeyBindingTranslator.h"
+
+#include <gdk/gdkkeysyms.h>
+#include <gtk/gtk.h>
+
+namespace WebKit {
+
+static void backspaceCallback(GtkWidget* widget, KeyBindingTranslator* translator)
+{
+ g_signal_stop_emission_by_name(widget, "backspace");
+ translator->addPendingEditorCommand("DeleteBackward");
+}
+
+static void selectAllCallback(GtkWidget* widget, gboolean select, KeyBindingTranslator* translator)
+{
+ g_signal_stop_emission_by_name(widget, "select-all");
+ translator->addPendingEditorCommand(select ? "SelectAll" : "Unselect");
+}
+
+static void cutClipboardCallback(GtkWidget* widget, KeyBindingTranslator* translator)
+{
+ g_signal_stop_emission_by_name(widget, "cut-clipboard");
+ translator->addPendingEditorCommand("Cut");
+}
+
+static void copyClipboardCallback(GtkWidget* widget, KeyBindingTranslator* translator)
+{
+ g_signal_stop_emission_by_name(widget, "copy-clipboard");
+ translator->addPendingEditorCommand("Copy");
+}
+
+static void pasteClipboardCallback(GtkWidget* widget, KeyBindingTranslator* translator)
+{
+ g_signal_stop_emission_by_name(widget, "paste-clipboard");
+ translator->addPendingEditorCommand("Paste");
+}
+
+static void toggleOverwriteCallback(GtkWidget* widget, KeyBindingTranslator* translator)
+{
+ g_signal_stop_emission_by_name(widget, "toggle-overwrite");
+ translator->addPendingEditorCommand("OverWrite");
+}
+
+// GTK+ will still send these signals to the web view. So we can safely stop signal
+// emission without breaking accessibility.
+static void popupMenuCallback(GtkWidget* widget, KeyBindingTranslator*)
+{
+ g_signal_stop_emission_by_name(widget, "popup-menu");
+}
+
+static void showHelpCallback(GtkWidget* widget, KeyBindingTranslator*)
+{
+ g_signal_stop_emission_by_name(widget, "show-help");
+}
+
+static const char* const gtkDeleteCommands[][2] = {
+ { "DeleteBackward", "DeleteForward" }, // Characters
+ { "DeleteWordBackward", "DeleteWordForward" }, // Word ends
+ { "DeleteWordBackward", "DeleteWordForward" }, // Words
+ { "DeleteToBeginningOfLine", "DeleteToEndOfLine" }, // Lines
+ { "DeleteToBeginningOfLine", "DeleteToEndOfLine" }, // Line ends
+ { "DeleteToBeginningOfParagraph", "DeleteToEndOfParagraph" }, // Paragraph ends
+ { "DeleteToBeginningOfParagraph", "DeleteToEndOfParagraph" }, // Paragraphs
+ { 0, 0 } // Whitespace (M-\ in Emacs)
+};
+
+static void deleteFromCursorCallback(GtkWidget* widget, GtkDeleteType deleteType, gint count, KeyBindingTranslator* translator)
+{
+ g_signal_stop_emission_by_name(widget, "delete-from-cursor");
+ int direction = count > 0 ? 1 : 0;
+
+ // Ensuring that deleteType <= G_N_ELEMENTS here results in a compiler warning
+ // that the condition is always true.
+
+ if (deleteType == GTK_DELETE_WORDS) {
+ if (!direction) {
+ translator->addPendingEditorCommand("MoveWordForward");
+ translator->addPendingEditorCommand("MoveWordBackward");
+ } else {
+ translator->addPendingEditorCommand("MoveWordBackward");
+ translator->addPendingEditorCommand("MoveWordForward");
+ }
+ } else if (deleteType == GTK_DELETE_DISPLAY_LINES) {
+ if (!direction)
+ translator->addPendingEditorCommand("MoveToBeginningOfLine");
+ else
+ translator->addPendingEditorCommand("MoveToEndOfLine");
+ } else if (deleteType == GTK_DELETE_PARAGRAPHS) {
+ if (!direction)
+ translator->addPendingEditorCommand("MoveToBeginningOfParagraph");
+ else
+ translator->addPendingEditorCommand("MoveToEndOfParagraph");
+ }
+
+ const char* rawCommand = gtkDeleteCommands[deleteType][direction];
+ if (!rawCommand)
+ return;
+
+ for (int i = 0; i < abs(count); i++)
+ translator->addPendingEditorCommand(rawCommand);
+}
+
+static const char* const gtkMoveCommands[][4] = {
+ { "MoveBackward", "MoveForward",
+ "MoveBackwardAndModifySelection", "MoveForwardAndModifySelection" }, // Forward/backward grapheme
+ { "MoveLeft", "MoveRight",
+ "MoveBackwardAndModifySelection", "MoveForwardAndModifySelection" }, // Left/right grapheme
+ { "MoveWordBackward", "MoveWordForward",
+ "MoveWordBackwardAndModifySelection", "MoveWordForwardAndModifySelection" }, // Forward/backward word
+ { "MoveUp", "MoveDown",
+ "MoveUpAndModifySelection", "MoveDownAndModifySelection" }, // Up/down line
+ { "MoveToBeginningOfLine", "MoveToEndOfLine",
+ "MoveToBeginningOfLineAndModifySelection", "MoveToEndOfLineAndModifySelection" }, // Up/down line ends
+ { 0, 0,
+ "MoveParagraphBackwardAndModifySelection", "MoveParagraphForwardAndModifySelection" }, // Up/down paragraphs
+ { "MoveToBeginningOfParagraph", "MoveToEndOfParagraph",
+ "MoveToBeginningOfParagraphAndModifySelection", "MoveToEndOfParagraphAndModifySelection" }, // Up/down paragraph ends.
+ { "MovePageUp", "MovePageDown",
+ "MovePageUpAndModifySelection", "MovePageDownAndModifySelection" }, // Up/down page
+ { "MoveToBeginningOfDocument", "MoveToEndOfDocument",
+ "MoveToBeginningOfDocumentAndModifySelection", "MoveToEndOfDocumentAndModifySelection" }, // Begin/end of buffer
+ { 0, 0,
+ 0, 0 } // Horizontal page movement
+};
+
+static void moveCursorCallback(GtkWidget* widget, GtkMovementStep step, gint count, gboolean extendSelection, KeyBindingTranslator* translator)
+{
+ g_signal_stop_emission_by_name(widget, "move-cursor");
+ int direction = count > 0 ? 1 : 0;
+ if (extendSelection)
+ direction += 2;
+
+ if (static_cast<unsigned>(step) >= G_N_ELEMENTS(gtkMoveCommands))
+ return;
+
+ const char* rawCommand = gtkMoveCommands[step][direction];
+ if (!rawCommand)
+ return;
+
+ for (int i = 0; i < abs(count); i++)
+ translator->addPendingEditorCommand(rawCommand);
+}
+
+KeyBindingTranslator::KeyBindingTranslator()
+ : m_nativeWidget(gtk_text_view_new())
+{
+ g_signal_connect(m_nativeWidget.get(), "backspace", G_CALLBACK(backspaceCallback), this);
+ g_signal_connect(m_nativeWidget.get(), "cut-clipboard", G_CALLBACK(cutClipboardCallback), this);
+ g_signal_connect(m_nativeWidget.get(), "copy-clipboard", G_CALLBACK(copyClipboardCallback), this);
+ g_signal_connect(m_nativeWidget.get(), "paste-clipboard", G_CALLBACK(pasteClipboardCallback), this);
+ g_signal_connect(m_nativeWidget.get(), "select-all", G_CALLBACK(selectAllCallback), this);
+ g_signal_connect(m_nativeWidget.get(), "move-cursor", G_CALLBACK(moveCursorCallback), this);
+ g_signal_connect(m_nativeWidget.get(), "delete-from-cursor", G_CALLBACK(deleteFromCursorCallback), this);
+ g_signal_connect(m_nativeWidget.get(), "toggle-overwrite", G_CALLBACK(toggleOverwriteCallback), this);
+ g_signal_connect(m_nativeWidget.get(), "popup-menu", G_CALLBACK(popupMenuCallback), this);
+ g_signal_connect(m_nativeWidget.get(), "show-help", G_CALLBACK(showHelpCallback), this);
+}
+
+struct KeyCombinationEntry {
+ unsigned gdkKeyCode;
+ unsigned state;
+ const char* name;
+};
+
+static const KeyCombinationEntry customKeyBindings[] = {
+ { GDK_KEY_b, GDK_CONTROL_MASK, "ToggleBold" },
+ { GDK_KEY_i, GDK_CONTROL_MASK, "ToggleItalic" },
+ { GDK_KEY_Escape, 0, "Cancel" },
+ { GDK_KEY_greater, GDK_CONTROL_MASK, "Cancel" },
+ { GDK_KEY_Tab, 0, "InsertTab" },
+ { GDK_KEY_Tab, GDK_SHIFT_MASK, "InsertBacktab" },
+};
+
+Vector<String> KeyBindingTranslator::commandsForKeyEvent(GdkEventKey* event)
+{
+ ASSERT(m_pendingEditorCommands.isEmpty());
+
+ gtk_bindings_activate_event(G_OBJECT(m_nativeWidget.get()), event);
+ if (!m_pendingEditorCommands.isEmpty())
+ return WTFMove(m_pendingEditorCommands);
+
+ // Special-case enter keys for we want them to work regardless of modifier.
+ if ((event->keyval == GDK_KEY_Return || event->keyval == GDK_KEY_KP_Enter || event->keyval == GDK_KEY_ISO_Enter))
+ return { "InsertNewLine" };
+
+ // For keypress events, we want charCode(), but keyCode() does that.
+ unsigned mapKey = event->state << 16 | event->keyval;
+ if (!mapKey)
+ return { };
+
+ for (unsigned i = 0; i < G_N_ELEMENTS(customKeyBindings); ++i) {
+ if (mapKey == (customKeyBindings[i].state << 16 | customKeyBindings[i].gdkKeyCode))
+ return { customKeyBindings[i].name };
+ }
+
+ return { };
+}
+
+} // namespace WebKit