Commit 800f02d5 authored by mrobinson@webkit.org's avatar mrobinson@webkit.org

2011-05-13 Martin Robinson <mrobinson@igalia.com>

        Reviewed by Eric Seidel.

        [GTK] Share the GTK+ key binding code between WebKit1 and WebKit2
        https://bugs.webkit.org/show_bug.cgi?id=59765

        Move the keybinding code form WebKit1/WebKit2 to this utility class. This code is
        almost directly moved from the WebKit layer.

        No new tests. This is covered by the editing tests.

        * GNUmakefile.list.am:
        * platform/gtk/KeyBindingTranslator.cpp: Added.
        (WebCore::backspaceCallback):Added.
        (WebCore::selectAllCallback):Added.
        (WebCore::cutClipboardCallback):Added.
        (WebCore::copyClipboardCallback):Added.
        (WebCore::pasteClipboardCallback):Added.
        (WebCore::toggleOverwriteCallback):Added.
        (WebCore::popupMenuCallback):Added.
        (WebCore::showHelpCallback):Added.
        (WebCore::deleteFromCursorCallback):Added.
        (WebCore::moveCursorCallback):Added.
        (WebCore::KeyBindingTranslator::KeyBindingTranslator):Added.
        (WebCore::KeyBindingTranslator::getEditorCommandsForKeyEvent):Added.
        * platform/gtk/KeyBindingTranslator.h: Added.
        (WebCore::KeyBindingTranslator::addPendingEditorCommand):Added.
2011-05-13  Martin Robinson  <mrobinson@igalia.com>

        Reviewed by Eric Seidel.

        [GTK] Share the GTK+ key binding code between WebKit1 and WebKit2
        https://bugs.webkit.org/show_bug.cgi?id=59765

        Use the new KeyBindingTranslator class from WebCore/platform/gtk. With this utility
        class we can now share the code with WebKit2.

        * WebCoreSupport/EditorClientGtk.cpp:
        (WebKit::EditorClient::executePendingEditorCommands): Updated to reflect the fact that
        the command vector now contains WTF::string.
        (WebKit::EditorClient::handleKeyboardEvent): Use the new helper utility class.
        (WebKit::EditorClient::EditorClient):
        * WebCoreSupport/EditorClientGtk.h: Remove unnecessary member. Add the utility class.
2011-05-13  Martin Robinson  <mrobinson@igalia.com>

        Reviewed by Eric Seidel.

        [GTK] Share the GTK+ key binding code between WebKit1 and WebKit2
        https://bugs.webkit.org/show_bug.cgi?id=59765

        Use the new KeyBindingTranslator class from WebCore/platform/gtk. With this utility
        class we can now share the code with WebKit2.

        * UIProcess/API/gtk/PageClientImpl.cpp:
        (WebKit::PageClientImpl::PageClientImpl): No longer need to initialize keybinding code.
        (WebKit::PageClientImpl::getEditorCommandsForKeyEvent): Use the new utility class.
        * UIProcess/API/gtk/PageClientImpl.h: Remove unnecessary members. Add utility class member.

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@86436 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent 5ed523f8
2011-05-13 Martin Robinson <mrobinson@igalia.com>
Reviewed by Eric Seidel.
[GTK] Share the GTK+ key binding code between WebKit1 and WebKit2
https://bugs.webkit.org/show_bug.cgi?id=59765
Move the keybinding code form WebKit1/WebKit2 to this utility class. This code is
almost directly moved from the WebKit layer.
No new tests. This is covered by the editing tests.
* GNUmakefile.list.am:
* platform/gtk/KeyBindingTranslator.cpp: Added.
(WebCore::backspaceCallback):Added.
(WebCore::selectAllCallback):Added.
(WebCore::cutClipboardCallback):Added.
(WebCore::copyClipboardCallback):Added.
(WebCore::pasteClipboardCallback):Added.
(WebCore::toggleOverwriteCallback):Added.
(WebCore::popupMenuCallback):Added.
(WebCore::showHelpCallback):Added.
(WebCore::deleteFromCursorCallback):Added.
(WebCore::moveCursorCallback):Added.
(WebCore::KeyBindingTranslator::KeyBindingTranslator):Added.
(WebCore::KeyBindingTranslator::getEditorCommandsForKeyEvent):Added.
* platform/gtk/KeyBindingTranslator.h: Added.
(WebCore::KeyBindingTranslator::addPendingEditorCommand):Added.
2011-05-01 Holger Hans Peter Freyther <holger@moiji-mobile.com>
Reviewed by Adam Barth.
......@@ -3750,6 +3750,8 @@ webcoregtk_sources += \
Source/WebCore/platform/gtk/GtkPluginWidget.h \
Source/WebCore/platform/gtk/GtkVersioning.c \
Source/WebCore/platform/gtk/GtkVersioning.h \
Source/WebCore/platform/gtk/KeyBindingTranslator.cpp \
Source/WebCore/platform/gtk/KeyBindingTranslator.h \
Source/WebCore/platform/gtk/KURLGtk.cpp \
Source/WebCore/platform/gtk/LanguageGtk.cpp \
Source/WebCore/platform/gtk/LocalizedStringsGtk.cpp \
......
/*
* 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 "GtkVersioning.h"
#include <gdk/gdkkeysyms.h>
#include <wtf/HashMap.h>
namespace WebCore {
typedef HashMap<int, const char*> IntConstCharHashMap;
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*)
{
// We don't support toggling the overwrite mode, but the default callback expects
// the GtkTextView to have a layout, so we handle this signal just to stop it.
g_signal_stop_emission_by_name(widget, "toggle-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
{ "MoveParagraphForward", "MoveParagraphBackward",
"MoveParagraphForwardAndModifySelection", "MoveParagraphBackwardAndModifySelection" }, // 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);
}
static const unsigned CtrlKey = 1 << 0;
static const unsigned AltKey = 1 << 1;
static const unsigned ShiftKey = 1 << 2;
struct KeyCombinationEntry {
unsigned gdkKeyCode;
unsigned state;
const char* name;
};
static const KeyCombinationEntry keyDownEntries[] = {
{ GDK_b, GDK_CONTROL_MASK, "ToggleBold" },
{ GDK_i, GDK_CONTROL_MASK, "ToggleItalic" },
{ GDK_Escape, 0, "Cancel" },
{ GDK_greater, GDK_CONTROL_MASK, "Cancel" },
};
// These commands are text insertion commands, so should take place
// while handling the KeyPress event.
static const KeyCombinationEntry keyPressEntries[] = {
{ GDK_Tab, 0, "InsertTab" },
{ GDK_Tab, GDK_SHIFT_MASK, "InsertBacktab" },
{ GDK_Tab, 0, "InsertTab" },
{ GDK_Tab, GDK_SHIFT_MASK, "InsertBacktab" },
{ GDK_Return, 0, "InsertNewline" },
{ GDK_Return, GDK_CONTROL_MASK, "InsertNewline" },
{ GDK_Return, GDK_MOD1_MASK, "InsertNewline" },
{ GDK_Return, GDK_MOD1_MASK | GDK_SHIFT_MASK, "InsertNewline" },
{ GDK_KP_Enter, 0, "InsertNewline" },
{ GDK_KP_Enter, GDK_CONTROL_MASK, "InsertNewline" },
{ GDK_KP_Enter, GDK_MOD1_MASK, "InsertNewline" },
{ GDK_KP_Enter, GDK_MOD1_MASK | GDK_SHIFT_MASK, "InsertNewline" },
{ GDK_ISO_Enter, 0, "InsertNewline" },
{ GDK_ISO_Enter, GDK_CONTROL_MASK, "InsertNewline" },
{ GDK_ISO_Enter, GDK_MOD1_MASK, "InsertNewline" },
{ GDK_ISO_Enter, GDK_MOD1_MASK | GDK_SHIFT_MASK, "InsertNewline" },
};
void KeyBindingTranslator::getEditorCommandsForKeyEvent(GdkEventKey* event, EventType type, Vector<WTF::String>& commandList)
{
m_pendingEditorCommands.clear();
#ifdef GTK_API_VERSION_2
gtk_bindings_activate_event(GTK_OBJECT(m_nativeWidget.get()), event);
#else
gtk_bindings_activate_event(G_OBJECT(m_nativeWidget.get()), event);
#endif
if (!m_pendingEditorCommands.isEmpty()) {
commandList.append(m_pendingEditorCommands);
return;
}
DEFINE_STATIC_LOCAL(IntConstCharHashMap, keyDownCommandsMap, ());
DEFINE_STATIC_LOCAL(IntConstCharHashMap, keyPressCommandsMap, ());
if (keyDownCommandsMap.isEmpty()) {
for (unsigned i = 0; i < G_N_ELEMENTS(keyDownEntries); i++)
keyDownCommandsMap.set(keyDownEntries[i].state << 16 | keyDownEntries[i].gdkKeyCode, keyDownEntries[i].name);
for (unsigned i = 0; i < G_N_ELEMENTS(keyPressEntries); i++)
keyPressCommandsMap.set(keyPressEntries[i].state << 16 | keyPressEntries[i].gdkKeyCode, keyPressEntries[i].name);
}
// For keypress events, we want charCode(), but keyCode() does that.
int mapKey = event->state << 16 | event->keyval;
if (mapKey) {
HashMap<int, const char*>* commandMap = type == KeyDown ? &keyDownCommandsMap : &keyPressCommandsMap;
if (const char* commandString = commandMap->get(mapKey)) {
commandList.append(commandString);
return;
}
}
}
} // namespace WebCore
/*
* 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
*/
#ifndef KeyBindingTranslator_h
#define KeyBindingTranslator_h
#include "GRefPtrGtk.h"
#include "PlatformString.h"
#include <wtf/Vector.h>
typedef struct _GdkEventKey GdkEventKey;
namespace WebCore {
class KeyBindingTranslator {
public:
KeyBindingTranslator();
enum EventType { KeyDown, KeyPress };
void getEditorCommandsForKeyEvent(GdkEventKey*, EventType, Vector<WTF::String>&);
void addPendingEditorCommand(const char* command) { m_pendingEditorCommands.append(WTF::String(command)); }
private:
GRefPtr<GtkWidget> m_nativeWidget;
Vector<WTF::String> m_pendingEditorCommands;
};
} // namespace WebCore
#endif
2011-05-13 Martin Robinson <mrobinson@igalia.com>
Reviewed by Eric Seidel.
[GTK] Share the GTK+ key binding code between WebKit1 and WebKit2
https://bugs.webkit.org/show_bug.cgi?id=59765
Use the new KeyBindingTranslator class from WebCore/platform/gtk. With this utility
class we can now share the code with WebKit2.
* WebCoreSupport/EditorClientGtk.cpp:
(WebKit::EditorClient::executePendingEditorCommands): Updated to reflect the fact that
the command vector now contains WTF::string.
(WebKit::EditorClient::handleKeyboardEvent): Use the new helper utility class.
(WebKit::EditorClient::EditorClient):
* WebCoreSupport/EditorClientGtk.h: Remove unnecessary member. Add the utility class.
2011-05-12 Martin Robinson <mrobinson@igalia.com>
Attempt to fix the GTK+ unit test testwebview on the 32-bit Release
......
......@@ -99,142 +99,6 @@ static void imContextPreeditChanged(GtkIMContext* context, EditorClient* client)
frame->editor()->setComposition(preeditString, underlines, 0, 0);
}
static void backspaceCallback(GtkWidget* widget, EditorClient* client)
{
g_signal_stop_emission_by_name(widget, "backspace");
client->addPendingEditorCommand("DeleteBackward");
}
static void selectAllCallback(GtkWidget* widget, gboolean select, EditorClient* client)
{
g_signal_stop_emission_by_name(widget, "select-all");
client->addPendingEditorCommand(select ? "SelectAll" : "Unselect");
}
static void cutClipboardCallback(GtkWidget* widget, EditorClient* client)
{
g_signal_stop_emission_by_name(widget, "cut-clipboard");
client->addPendingEditorCommand("Cut");
}
static void copyClipboardCallback(GtkWidget* widget, EditorClient* client)
{
g_signal_stop_emission_by_name(widget, "copy-clipboard");
client->addPendingEditorCommand("Copy");
}
static void pasteClipboardCallback(GtkWidget* widget, EditorClient* client)
{
g_signal_stop_emission_by_name(widget, "paste-clipboard");
client->addPendingEditorCommand("Paste");
}
static void toggleOverwriteCallback(GtkWidget* widget, EditorClient*)
{
// We don't support toggling the overwrite mode, but the default callback expects
// the GtkTextView to have a layout, so we handle this signal just to stop it.
g_signal_stop_emission_by_name(widget, "toggle-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, EditorClient*)
{
g_signal_stop_emission_by_name(widget, "popup-menu");
}
static void showHelpCallback(GtkWidget* widget, EditorClient*)
{
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, EditorClient* client)
{
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) {
client->addPendingEditorCommand("MoveWordForward");
client->addPendingEditorCommand("MoveWordBackward");
} else {
client->addPendingEditorCommand("MoveWordBackward");
client->addPendingEditorCommand("MoveWordForward");
}
} else if (deleteType == GTK_DELETE_DISPLAY_LINES) {
if (!direction)
client->addPendingEditorCommand("MoveToBeginningOfLine");
else
client->addPendingEditorCommand("MoveToEndOfLine");
} else if (deleteType == GTK_DELETE_PARAGRAPHS) {
if (!direction)
client->addPendingEditorCommand("MoveToBeginningOfParagraph");
else
client->addPendingEditorCommand("MoveToEndOfParagraph");
}
const char* rawCommand = gtkDeleteCommands[deleteType][direction];
if (!rawCommand)
return;
for (int i = 0; i < abs(count); i++)
client->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
{ "MoveParagraphForward", "MoveParagraphBackward",
"MoveParagraphForwardAndModifySelection", "MoveParagraphBackwardAndModifySelection" }, // 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, EditorClient* client)
{
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++)
client->addPendingEditorCommand(rawCommand);
}
void EditorClient::updatePendingComposition(const gchar* newComposition)
{
......@@ -586,95 +450,11 @@ void EditorClient::toggleGrammarChecking()
{
}
static const unsigned CtrlKey = 1 << 0;
static const unsigned AltKey = 1 << 1;
static const unsigned ShiftKey = 1 << 2;
struct KeyDownEntry {
unsigned virtualKey;
unsigned modifiers;
const char* name;
};
struct KeyPressEntry {
unsigned charCode;
unsigned modifiers;
const char* name;
};
static const KeyDownEntry keyDownEntries[] = {
{ 'B', CtrlKey, "ToggleBold" },
{ 'I', CtrlKey, "ToggleItalic" },
{ VK_ESCAPE, 0, "Cancel" },
{ VK_OEM_PERIOD, CtrlKey, "Cancel" },
{ VK_TAB, 0, "InsertTab" },
{ VK_TAB, ShiftKey, "InsertBacktab" },
{ VK_RETURN, 0, "InsertNewline" },
{ VK_RETURN, CtrlKey, "InsertNewline" },
{ VK_RETURN, AltKey, "InsertNewline" },
{ VK_RETURN, AltKey | ShiftKey, "InsertNewline" },
};
static const KeyPressEntry keyPressEntries[] = {
{ '\t', 0, "InsertTab" },
{ '\t', ShiftKey, "InsertBacktab" },
{ '\r', 0, "InsertNewline" },
{ '\r', CtrlKey, "InsertNewline" },
{ '\r', AltKey, "InsertNewline" },
{ '\r', AltKey | ShiftKey, "InsertNewline" },
};
void EditorClient::generateEditorCommands(const KeyboardEvent* event)
{
ASSERT(event->type() == eventNames().keydownEvent || event->type() == eventNames().keypressEvent);
m_pendingEditorCommands.clear();
// First try to interpret the command as a native GTK+ key binding.
#ifdef GTK_API_VERSION_2
gtk_bindings_activate_event(GTK_OBJECT(m_nativeWidget.get()), event->keyEvent()->gdkEventKey());
#else
gtk_bindings_activate_event(G_OBJECT(m_nativeWidget.get()), event->keyEvent()->gdkEventKey());
#endif
if (m_pendingEditorCommands.size() > 0)
return;
static HashMap<int, const char*> keyDownCommandsMap;
static HashMap<int, const char*> keyPressCommandsMap;
if (keyDownCommandsMap.isEmpty()) {
for (unsigned i = 0; i < G_N_ELEMENTS(keyDownEntries); i++)
keyDownCommandsMap.set(keyDownEntries[i].modifiers << 16 | keyDownEntries[i].virtualKey, keyDownEntries[i].name);
for (unsigned i = 0; i < G_N_ELEMENTS(keyPressEntries); i++)
keyPressCommandsMap.set(keyPressEntries[i].modifiers << 16 | keyPressEntries[i].charCode, keyPressEntries[i].name);
}
unsigned modifiers = 0;
if (event->shiftKey())
modifiers |= ShiftKey;
if (event->altKey())
modifiers |= AltKey;
if (event->ctrlKey())
modifiers |= CtrlKey;
// For keypress events, we want charCode(), but keyCode() does that.
int mapKey = modifiers << 16 | event->keyCode();
if (!mapKey)
return;
HashMap<int, const char*>* commandMap = event->type() == eventNames().keydownEvent ?
&keyDownCommandsMap : &keyPressCommandsMap;
if (const char* commandString = commandMap->get(mapKey))
m_pendingEditorCommands.append(commandString);
}
bool EditorClient::executePendingEditorCommands(Frame* frame, bool allowTextInsertion)
{
Vector<Editor::Command> commands;
for (size_t i = 0; i < m_pendingEditorCommands.size(); i++) {
const char* commandString = m_pendingEditorCommands.at(i);
ASSERT(commandString);
Editor::Command command = frame->editor()->command(commandString);
Editor::Command command = frame->editor()->command(m_pendingEditorCommands.at(i).utf8().data());
if (command.isTextInsertion() && !allowTextInsertion)
return false;