diff options
Diffstat (limited to 'WebKit/gtk/WebCoreSupport')
-rw-r--r-- | WebKit/gtk/WebCoreSupport/ContextMenuClientGtk.cpp | 2 | ||||
-rw-r--r-- | WebKit/gtk/WebCoreSupport/EditorClientGtk.cpp | 268 | ||||
-rw-r--r-- | WebKit/gtk/WebCoreSupport/EditorClientGtk.h | 10 | ||||
-rw-r--r-- | WebKit/gtk/WebCoreSupport/FrameLoaderClientGtk.cpp | 9 |
4 files changed, 211 insertions, 78 deletions
diff --git a/WebKit/gtk/WebCoreSupport/ContextMenuClientGtk.cpp b/WebKit/gtk/WebCoreSupport/ContextMenuClientGtk.cpp index 5c1bc0b..069fb19 100644 --- a/WebKit/gtk/WebCoreSupport/ContextMenuClientGtk.cpp +++ b/WebKit/gtk/WebCoreSupport/ContextMenuClientGtk.cpp @@ -62,7 +62,7 @@ static GtkWidget* inputMethodsMenuItem (WebKitWebView* webView) WebKitWebViewPrivate* priv = WEBKIT_WEB_VIEW_GET_PRIVATE(webView); GtkWidget* imContextMenu = gtk_menu_new(); - gtk_im_multicontext_append_menuitems(GTK_IM_MULTICONTEXT(priv->imContext), GTK_MENU_SHELL(imContextMenu)); + gtk_im_multicontext_append_menuitems(GTK_IM_MULTICONTEXT(priv->imContext.get()), GTK_MENU_SHELL(imContextMenu)); gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), imContextMenu); diff --git a/WebKit/gtk/WebCoreSupport/EditorClientGtk.cpp b/WebKit/gtk/WebCoreSupport/EditorClientGtk.cpp index 51172b4..d3f8fa1 100644 --- a/WebKit/gtk/WebCoreSupport/EditorClientGtk.cpp +++ b/WebKit/gtk/WebCoreSupport/EditorClientGtk.cpp @@ -87,6 +87,124 @@ 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 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 + { "MoveBackward", "MoveForward", + "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) { // The IMContext may signal more than one completed composition in a row, @@ -102,15 +220,15 @@ void EditorClient::setInputMethodState(bool active) WebKitWebViewPrivate* priv = m_webView->priv; if (active) - gtk_im_context_focus_in(priv->imContext); + gtk_im_context_focus_in(priv->imContext.get()); else - gtk_im_context_focus_out(priv->imContext); + gtk_im_context_focus_out(priv->imContext.get()); #ifdef MAEMO_CHANGES if (active) - hildon_gtk_im_context_show(priv->imContext); + hildon_gtk_im_context_show(priv->imContext.get()); else - hildon_gtk_im_context_hide(priv->imContext); + hildon_gtk_im_context_hide(priv->imContext.get()); #endif } @@ -249,7 +367,7 @@ void EditorClient::respondToChangedSelection() unsigned end; if (!targetFrame->editor()->getCompositionSelection(start, end)) { // gtk_im_context_reset() clears the composition for us. - gtk_im_context_reset(priv->imContext); + gtk_im_context_reset(priv->imContext.get()); targetFrame->editor()->confirmCompositionWithoutDisturbingSelection(); } } @@ -382,41 +500,8 @@ struct KeyPressEntry { }; static const KeyDownEntry keyDownEntries[] = { - { VK_LEFT, 0, "MoveLeft" }, - { VK_LEFT, ShiftKey, "MoveLeftAndModifySelection" }, - { VK_LEFT, CtrlKey, "MoveWordLeft" }, - { VK_LEFT, CtrlKey | ShiftKey, "MoveWordLeftAndModifySelection" }, - { VK_RIGHT, 0, "MoveRight" }, - { VK_RIGHT, ShiftKey, "MoveRightAndModifySelection" }, - { VK_RIGHT, CtrlKey, "MoveWordRight" }, - { VK_RIGHT, CtrlKey | ShiftKey, "MoveWordRightAndModifySelection" }, - { VK_UP, 0, "MoveUp" }, - { VK_UP, ShiftKey, "MoveUpAndModifySelection" }, - { VK_PRIOR, ShiftKey, "MovePageUpAndModifySelection" }, - { VK_DOWN, 0, "MoveDown" }, - { VK_DOWN, ShiftKey, "MoveDownAndModifySelection" }, - { VK_NEXT, ShiftKey, "MovePageDownAndModifySelection" }, - { VK_PRIOR, 0, "MovePageUp" }, - { VK_NEXT, 0, "MovePageDown" }, - { VK_HOME, 0, "MoveToBeginningOfLine" }, - { VK_HOME, ShiftKey, "MoveToBeginningOfLineAndModifySelection" }, - { VK_HOME, CtrlKey, "MoveToBeginningOfDocument" }, - { VK_HOME, CtrlKey | ShiftKey, "MoveToBeginningOfDocumentAndModifySelection" }, - - { VK_END, 0, "MoveToEndOfLine" }, - { VK_END, ShiftKey, "MoveToEndOfLineAndModifySelection" }, - { VK_END, CtrlKey, "MoveToEndOfDocument" }, - { VK_END, CtrlKey | ShiftKey, "MoveToEndOfDocumentAndModifySelection" }, - - { VK_BACK, 0, "DeleteBackward" }, - { VK_BACK, ShiftKey, "DeleteBackward" }, - { VK_DELETE, 0, "DeleteForward" }, - { VK_BACK, CtrlKey, "DeleteWordBackward" }, - { VK_DELETE, CtrlKey, "DeleteWordForward" }, - { 'B', CtrlKey, "ToggleBold" }, { 'I', CtrlKey, "ToggleItalic" }, - { VK_ESCAPE, 0, "Cancel" }, { VK_OEM_PERIOD, CtrlKey, "Cancel" }, { VK_TAB, 0, "InsertTab" }, @@ -436,39 +521,76 @@ static const KeyPressEntry keyPressEntries[] = { { '\r', AltKey | ShiftKey, "InsertNewline" }, }; -static const char* interpretEditorCommandKeyEvent(const KeyboardEvent* evt) +void EditorClient::generateEditorCommands(const KeyboardEvent* event) { - ASSERT(evt->type() == eventNames().keydownEvent || evt->type() == eventNames().keypressEvent); + ASSERT(event->type() == eventNames().keydownEvent || event->type() == eventNames().keypressEvent); - static HashMap<int, const char*>* keyDownCommandsMap = 0; - static HashMap<int, const char*>* keyPressCommandsMap = 0; + m_pendingEditorCommands.clear(); + + // First try to interpret the command as a native GTK+ key binding. + gtk_bindings_activate_event(GTK_OBJECT(m_nativeWidget.get()), event->keyEvent()->gdkEventKey()); + if (m_pendingEditorCommands.size() > 0) + return; - if (!keyDownCommandsMap) { - keyDownCommandsMap = new HashMap<int, const char*>; - keyPressCommandsMap = new HashMap<int, const char*>; + 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); + 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); + keyPressCommandsMap.set(keyPressEntries[i].modifiers << 16 | keyPressEntries[i].charCode, keyPressEntries[i].name); } unsigned modifiers = 0; - if (evt->shiftKey()) + if (event->shiftKey()) modifiers |= ShiftKey; - if (evt->altKey()) + if (event->altKey()) modifiers |= AltKey; - if (evt->ctrlKey()) + if (event->ctrlKey()) modifiers |= CtrlKey; - if (evt->type() == eventNames().keydownEvent) { - int mapKey = modifiers << 16 | evt->keyCode(); - return mapKey ? keyDownCommandsMap->get(mapKey) : 0; + + if (event->type() == eventNames().keydownEvent) { + int mapKey = modifiers << 16 | event->keyCode(); + if (mapKey) + m_pendingEditorCommands.append(keyDownCommandsMap.get(mapKey)); + return; } - int mapKey = modifiers << 16 | evt->charCode(); - return mapKey ? keyPressCommandsMap->get(mapKey) : 0; + int mapKey = modifiers << 16 | event->charCode(); + if (mapKey) + m_pendingEditorCommands.append(keyPressCommandsMap.get(mapKey)); +} + +bool EditorClient::executePendingEditorCommands(Frame* frame, bool allowTextInsertion) +{ + Vector<Editor::Command> commands; + for (size_t i = 0; i < m_pendingEditorCommands.size(); i++) { + Editor::Command command = frame->editor()->command(m_pendingEditorCommands.at(i)); + if (command.isTextInsertion() && !allowTextInsertion) + return false; + + commands.append(command); + } + + bool success = true; + for (size_t i = 0; i < commands.size(); i++) { + if (!commands.at(i).execute()) { + success = false; + break; + } + } + + m_pendingEditorCommands.clear(); + + // If we successfully completed all editor commands, then + // this signals a canceling of the composition. + if (success) + clearPendingComposition(); + + return success; } void EditorClient::handleKeyboardEvent(KeyboardEvent* event) @@ -487,23 +609,20 @@ void EditorClient::handleKeyboardEvent(KeyboardEvent* event) if (!frame->editor()->canEdit() && !(frame->settings() && frame->settings()->caretBrowsingEnabled())) return; - const gchar* editorCommandString = interpretEditorCommandKeyEvent(event); - if (editorCommandString) { - Editor::Command command = frame->editor()->command(editorCommandString); + generateEditorCommands(event); + if (m_pendingEditorCommands.size() > 0) { - // On editor commands from key down events, we only want to let the event bubble up to - // the DOM if it inserts text. If it doesn't insert text (e.g. Tab that changes focus) - // we just want WebKit to handle it immediately without a DOM event. + // During RawKeyDown events if an editor command will insert text, defer + // the insertion until the keypress event. We want keydown to bubble up + // through the DOM first. if (platformEvent->type() == PlatformKeyboardEvent::RawKeyDown) { - if (!command.isTextInsertion() && command.execute(event)) + if (executePendingEditorCommands(frame, false)) event->setDefaultHandled(); - clearPendingComposition(); return; } - if (command.execute(event)) { - clearPendingComposition(); + if (executePendingEditorCommands(frame, true)) { event->setDefaultHandled(); return; } @@ -581,7 +700,7 @@ void EditorClient::handleInputMethodKeydown(KeyboardEvent* event) m_treatContextCommitAsKeyEvent = (!targetFrame->editor()->hasComposition()) && event->keyEvent()->gdkEventKey()->keyval; clearPendingComposition(); - if ((gtk_im_context_filter_keypress(priv->imContext, event->keyEvent()->gdkEventKey()) && !m_pendingComposition) + if ((gtk_im_context_filter_keypress(priv->imContext.get(), event->keyEvent()->gdkEventKey()) && !m_pendingComposition) || (!m_treatContextCommitAsKeyEvent && !targetFrame->editor()->hasComposition())) event->preventDefault(); @@ -592,17 +711,26 @@ EditorClient::EditorClient(WebKitWebView* webView) : m_isInRedo(false) , m_webView(webView) , m_treatContextCommitAsKeyEvent(false) + , m_nativeWidget(gtk_text_view_new()) { WebKitWebViewPrivate* priv = m_webView->priv; - g_signal_connect(priv->imContext, "commit", G_CALLBACK(imContextCommitted), this); - g_signal_connect(priv->imContext, "preedit-changed", G_CALLBACK(imContextPreeditChanged), this); + g_signal_connect(priv->imContext.get(), "commit", G_CALLBACK(imContextCommitted), this); + g_signal_connect(priv->imContext.get(), "preedit-changed", G_CALLBACK(imContextPreeditChanged), this); + + 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); } EditorClient::~EditorClient() { WebKitWebViewPrivate* priv = m_webView->priv; - g_signal_handlers_disconnect_by_func(priv->imContext, (gpointer)imContextCommitted, this); - g_signal_handlers_disconnect_by_func(priv->imContext, (gpointer)imContextPreeditChanged, this); + g_signal_handlers_disconnect_by_func(priv->imContext.get(), (gpointer)imContextCommitted, this); + g_signal_handlers_disconnect_by_func(priv->imContext.get(), (gpointer)imContextPreeditChanged, this); } void EditorClient::textFieldDidBeginEditing(Element*) diff --git a/WebKit/gtk/WebCoreSupport/EditorClientGtk.h b/WebKit/gtk/WebCoreSupport/EditorClientGtk.h index 825c146..2e5c359 100644 --- a/WebKit/gtk/WebCoreSupport/EditorClientGtk.h +++ b/WebKit/gtk/WebCoreSupport/EditorClientGtk.h @@ -36,12 +36,15 @@ #include <wtf/Deque.h> #include <wtf/Forward.h> #include <wtf/gobject/GOwnPtr.h> +#include <wtf/gobject/GRefPtr.h> typedef struct _WebKitWebView WebKitWebView; +typedef struct _GtkWidget GtkWidget; typedef char gchar; namespace WebCore { - class Page; +class Frame; +class KeyboardEvent; } namespace WebKit { @@ -60,7 +63,10 @@ namespace WebKit { bool treatContextCommitAsKeyEvent() { return m_treatContextCommitAsKeyEvent; } void clearPendingComposition() { m_pendingComposition.set(0); } bool hasPendingComposition() { return m_pendingComposition; } + void addPendingEditorCommand(const char* command) { m_pendingEditorCommands.append(command); } void updatePendingComposition(const char*); + void generateEditorCommands(const WebCore::KeyboardEvent*); + bool executePendingEditorCommands(WebCore::Frame*, bool); // from EditorClient virtual void pageDestroyed(); @@ -130,6 +136,8 @@ namespace WebKit { WebKitWebView* m_webView; bool m_treatContextCommitAsKeyEvent; GOwnPtr<gchar> m_pendingComposition; + Vector<const char*> m_pendingEditorCommands; + GRefPtr<GtkWidget> m_nativeWidget; }; } diff --git a/WebKit/gtk/WebCoreSupport/FrameLoaderClientGtk.cpp b/WebKit/gtk/WebCoreSupport/FrameLoaderClientGtk.cpp index fd2d7ed..1fe6b0f 100644 --- a/WebKit/gtk/WebCoreSupport/FrameLoaderClientGtk.cpp +++ b/WebKit/gtk/WebCoreSupport/FrameLoaderClientGtk.cpp @@ -1135,14 +1135,11 @@ static void postCommitFrameViewSetup(WebKitWebFrame *frame, FrameView *view, boo { WebKitWebView* containingWindow = getViewFromFrame(frame); WebKitWebViewPrivate* priv = WEBKIT_WEB_VIEW_GET_PRIVATE(containingWindow); - view->setGtkAdjustments(priv->horizontalAdjustment, priv->verticalAdjustment, resetValues); + view->setGtkAdjustments(priv->horizontalAdjustment.get(), priv->verticalAdjustment.get(), resetValues); if (priv->currentMenu) { - GtkMenu* menu = priv->currentMenu; - priv->currentMenu = 0; - - gtk_menu_popdown(menu); - g_object_unref(menu); + gtk_menu_popdown(priv->currentMenu.get()); + priv->currentMenu.clear(); } } |