2 * Copyright (C) 2007 Alp Toker <alp@atoker.com>
3 * Copyright (C) 2008 Nuanti Ltd.
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 #include "EditorClientGtk.h"
23 #include "EditCommand.h"
25 #include "FocusController.h"
27 #include "KeyboardCodes.h"
28 #include "KeyboardEvent.h"
29 #include "NotImplemented.h"
31 #include "PlatformKeyboardEvent.h"
32 #include "webkitprivate.h"
34 using namespace WebCore;
38 static void imContextCommitted(GtkIMContext* context, const gchar* str, EditorClient* client)
40 Frame* targetFrame = core(client->m_webView)->focusController()->focusedOrMainFrame();
42 if (!targetFrame || !targetFrame->editor()->canEdit())
45 Editor* editor = targetFrame->editor();
47 String commitString = String::fromUTF8(str);
48 editor->confirmComposition(commitString);
51 static void imContextPreeditChanged(GtkIMContext* context, EditorClient* client)
53 Frame* frame = core(client->m_webView)->focusController()->focusedOrMainFrame();
54 Editor* editor = frame->editor();
56 gchar* preedit = NULL;
58 // We ignore the provided PangoAttrList for now.
59 gtk_im_context_get_preedit_string(context, &preedit, NULL, &cursorPos);
60 String preeditString = String::fromUTF8(preedit);
63 // setComposition() will replace the user selection if passed an empty
64 // preedit. We don't want this to happen.
65 if (preeditString.isEmpty() && !editor->hasComposition())
68 Vector<CompositionUnderline> underlines;
69 underlines.append(CompositionUnderline(0, preeditString.length(), Color(0, 0, 0), false));
70 editor->setComposition(preeditString, underlines, cursorPos, 0);
73 void EditorClient::setInputMethodState(bool active)
75 WebKitWebViewPrivate* priv = m_webView->priv;
78 gtk_im_context_focus_in(priv->imContext);
80 gtk_im_context_focus_out(priv->imContext);
84 hildon_gtk_im_context_show(priv->imContext);
86 hildon_gtk_im_context_hide(priv->imContext);
90 bool EditorClient::shouldDeleteRange(Range*)
96 bool EditorClient::shouldShowDeleteInterface(HTMLElement*)
101 bool EditorClient::isContinuousSpellCheckingEnabled()
107 bool EditorClient::isGrammarCheckingEnabled()
113 int EditorClient::spellCheckerDocumentTag()
119 bool EditorClient::shouldBeginEditing(WebCore::Range*)
125 bool EditorClient::shouldEndEditing(WebCore::Range*)
131 bool EditorClient::shouldInsertText(const String&, Range*, EditorInsertAction)
137 bool EditorClient::shouldChangeSelectedRange(Range*, Range*, EAffinity, bool)
143 bool EditorClient::shouldApplyStyle(WebCore::CSSStyleDeclaration*, WebCore::Range*)
149 bool EditorClient::shouldMoveRangeAfterDelete(WebCore::Range*, WebCore::Range*)
155 void EditorClient::didBeginEditing()
160 void EditorClient::respondToChangedContents()
165 void EditorClient::respondToChangedSelection()
167 WebKitWebViewPrivate* priv = m_webView->priv;
169 Frame* targetFrame = core(m_webView)->focusController()->focusedOrMainFrame();
170 if (!targetFrame || !targetFrame->editor()->hasComposition())
173 if (targetFrame->editor()->ignoreCompositionSelectionChange())
178 if (!targetFrame->editor()->getCompositionSelection(start, end)) {
179 // gtk_im_context_reset() clears the composition for us.
180 gtk_im_context_reset(priv->imContext);
181 targetFrame->editor()->confirmCompositionWithoutDisturbingSelection();
185 void EditorClient::didEndEditing()
190 void EditorClient::didWriteSelectionToPasteboard()
195 void EditorClient::didSetSelectionTypesForPasteboard()
200 bool EditorClient::isEditable()
202 return webkit_web_view_get_editable(m_webView);
205 void EditorClient::registerCommandForUndo(WTF::PassRefPtr<WebCore::EditCommand>)
210 void EditorClient::registerCommandForRedo(WTF::PassRefPtr<WebCore::EditCommand>)
215 void EditorClient::clearUndoRedoOperations()
220 bool EditorClient::canUndo() const
226 bool EditorClient::canRedo() const
232 void EditorClient::undo()
237 void EditorClient::redo()
242 bool EditorClient::shouldInsertNode(Node*, Range*, EditorInsertAction)
248 void EditorClient::pageDestroyed()
253 bool EditorClient::smartInsertDeleteEnabled()
259 void EditorClient::toggleContinuousSpellChecking()
264 void EditorClient::toggleGrammarChecking()
268 void EditorClient::handleKeyboardEvent(KeyboardEvent* event)
270 Frame* frame = core(m_webView)->focusController()->focusedOrMainFrame();
271 if (!frame || !frame->document()->focusedNode())
274 const PlatformKeyboardEvent* kevent = event->keyEvent();
275 if (!kevent || kevent->type() == PlatformKeyboardEvent::KeyUp)
278 Node* start = frame->selection()->start().node();
282 // FIXME: Use GtkBindingSet instead of this hard-coded switch
283 // http://bugs.webkit.org/show_bug.cgi?id=15911
285 if (start->isContentEditable()) {
286 switch (kevent->windowsVirtualKeyCode()) {
288 frame->editor()->deleteWithDirection(SelectionController::BACKWARD,
289 kevent->ctrlKey() ? WordGranularity : CharacterGranularity, false, true);
292 frame->editor()->deleteWithDirection(SelectionController::FORWARD,
293 kevent->ctrlKey() ? WordGranularity : CharacterGranularity, false, true);
296 frame->selection()->modify(kevent->shiftKey() ? SelectionController::EXTEND : SelectionController::MOVE,
297 SelectionController::LEFT,
298 kevent->ctrlKey() ? WordGranularity : CharacterGranularity,
302 frame->selection()->modify(kevent->shiftKey() ? SelectionController::EXTEND : SelectionController::MOVE,
303 SelectionController::RIGHT,
304 kevent->ctrlKey() ? WordGranularity : CharacterGranularity,
308 frame->selection()->modify(kevent->shiftKey() ? SelectionController::EXTEND : SelectionController::MOVE,
309 SelectionController::BACKWARD,
310 kevent->ctrlKey() ? ParagraphGranularity : LineGranularity,
314 frame->selection()->modify(kevent->shiftKey() ? SelectionController::EXTEND : SelectionController::MOVE,
315 SelectionController::FORWARD,
316 kevent->ctrlKey() ? ParagraphGranularity : LineGranularity,
319 case VK_PRIOR: // PageUp
320 frame->editor()->command(kevent->shiftKey() ? "MovePageUpAndModifySelection" : "MovePageUp").execute();
322 case VK_NEXT: // PageDown
323 frame->editor()->command(kevent->shiftKey() ? "MovePageDownAndModifySelection" : "MovePageDown").execute();
326 if (kevent->ctrlKey() && kevent->shiftKey())
327 frame->editor()->command("MoveToBeginningOfDocumentAndModifySelection").execute();
328 else if (kevent->ctrlKey())
329 frame->editor()->command("MoveToBeginningOfDocument").execute();
330 else if (kevent->shiftKey())
331 frame->editor()->command("MoveToBeginningOfLineAndModifySelection").execute();
333 frame->editor()->command("MoveToBeginningOfLine").execute();
336 if (kevent->ctrlKey() && kevent->shiftKey())
337 frame->editor()->command("MoveToEndOfDocumentAndModifySelection").execute();
338 else if (kevent->ctrlKey())
339 frame->editor()->command("MoveToEndOfDocument").execute();
340 else if (kevent->shiftKey())
341 frame->editor()->command("MoveToEndOfLineAndModifySelection").execute();
343 frame->editor()->command("MoveToEndOfLine").execute();
346 frame->editor()->command("InsertLineBreak").execute();
351 if (!kevent->ctrlKey() && !kevent->altKey() && !kevent->text().isEmpty()) {
352 if (kevent->text().length() == 1) {
353 UChar ch = kevent->text()[0];
354 // Don't insert null or control characters as they can result in unexpected behaviour
358 frame->editor()->insertText(kevent->text(), event);
359 } else if (kevent->ctrlKey()) {
360 switch (kevent->windowsVirtualKeyCode()) {
362 frame->editor()->command("ToggleBold").execute();
365 frame->editor()->command("ToggleItalic").execute();
368 frame->editor()->command("Redo").execute();
371 frame->editor()->command("Undo").execute();
379 switch (kevent->windowsVirtualKeyCode()) {
381 frame->editor()->command("MoveUp").execute();
384 frame->editor()->command("MoveDown").execute();
386 case VK_PRIOR: // PageUp
387 frame->editor()->command("MovePageUp").execute();
389 case VK_NEXT: // PageDown
390 frame->editor()->command("MovePageDown").execute();
393 if (kevent->ctrlKey())
394 frame->editor()->command("MoveToBeginningOfDocument").execute();
397 if (kevent->ctrlKey())
398 frame->editor()->command("MoveToEndOfDocument").execute();
404 event->setDefaultHandled();
407 void EditorClient::handleInputMethodKeydown(KeyboardEvent* event)
409 WebKitWebViewPrivate* priv = m_webView->priv;
411 // TODO: Dispatch IE-compatible text input events for IM events.
412 if (gtk_im_context_filter_keypress(priv->imContext, event->keyEvent()->gdkEventKey()))
413 event->setDefaultHandled();
416 EditorClient::EditorClient(WebKitWebView* webView)
419 WebKitWebViewPrivate* priv = m_webView->priv;
420 g_signal_connect(priv->imContext, "commit", G_CALLBACK(imContextCommitted), this);
421 g_signal_connect(priv->imContext, "preedit-changed", G_CALLBACK(imContextPreeditChanged), this);
424 EditorClient::~EditorClient()
426 WebKitWebViewPrivate* priv = m_webView->priv;
427 g_signal_handlers_disconnect_by_func(priv->imContext, (gpointer)imContextCommitted, this);
428 g_signal_handlers_disconnect_by_func(priv->imContext, (gpointer)imContextPreeditChanged, this);
431 void EditorClient::textFieldDidBeginEditing(Element*)
435 void EditorClient::textFieldDidEndEditing(Element*)
439 void EditorClient::textDidChangeInTextField(Element*)
443 bool EditorClient::doTextFieldCommandFromEvent(Element*, KeyboardEvent*)
448 void EditorClient::textWillBeDeletedInTextField(Element*)
453 void EditorClient::textDidChangeInTextArea(Element*)
458 void EditorClient::ignoreWordInSpellDocument(const String&)
463 void EditorClient::learnWord(const String&)
468 void EditorClient::checkSpellingOfString(const UChar*, int, int*, int*)
473 void EditorClient::checkGrammarOfString(const UChar*, int, Vector<GrammarDetail>&, int*, int*)
478 void EditorClient::updateSpellingUIWithGrammarString(const String&, const GrammarDetail&)
483 void EditorClient::updateSpellingUIWithMisspelledWord(const String&)
488 void EditorClient::showSpellingUI(bool)
493 bool EditorClient::spellingUIIsShowing()
499 void EditorClient::getGuessesForWord(const String&, Vector<String>&)