WebCore:
[WebKit-https.git] / WebKit / gtk / WebCoreSupport / EditorClientGtk.cpp
1 /*
2  *  Copyright (C) 2007 Alp Toker <alp@atoker.com>
3  *  Copyright (C) 2008 Nuanti Ltd.
4  *
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.
9  *
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.
14  *
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
18  */
19
20 #include "config.h"
21 #include "EditorClientGtk.h"
22
23 #include "EditCommand.h"
24 #include "Editor.h"
25 #include "FocusController.h"
26 #include "Frame.h"
27 #include "KeyboardCodes.h"
28 #include "KeyboardEvent.h"
29 #include "NotImplemented.h"
30 #include "Page.h"
31 #include "PlatformKeyboardEvent.h"
32 #include "webkitprivate.h"
33
34 using namespace WebCore;
35
36 namespace WebKit {
37
38 static void imContextCommitted(GtkIMContext* context, const gchar* str, EditorClient* client)
39 {
40     Frame* targetFrame = core(client->m_webView)->focusController()->focusedOrMainFrame();
41
42     if (!targetFrame || !targetFrame->editor()->canEdit())
43         return;
44
45     Editor* editor = targetFrame->editor();
46
47     String commitString = String::fromUTF8(str);
48     editor->confirmComposition(commitString);
49 }
50
51 static void imContextPreeditChanged(GtkIMContext* context, EditorClient* client)
52 {
53     Frame* frame = core(client->m_webView)->focusController()->focusedOrMainFrame();
54     Editor* editor = frame->editor();
55
56     gchar* preedit = NULL;
57     gint cursorPos = 0;
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);
61     g_free(preedit);
62
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())
66         return;
67
68     Vector<CompositionUnderline> underlines;
69     underlines.append(CompositionUnderline(0, preeditString.length(), Color(0, 0, 0), false));
70     editor->setComposition(preeditString, underlines, cursorPos, 0);
71 }
72
73 void EditorClient::setInputMethodState(bool active)
74 {
75     WebKitWebViewPrivate* priv = m_webView->priv;
76
77     if (active)
78         gtk_im_context_focus_in(priv->imContext);
79     else
80         gtk_im_context_focus_out(priv->imContext);
81
82 #ifdef MAEMO_CHANGES
83     if (active)
84         hildon_gtk_im_context_show(priv->imContext);
85     else
86         hildon_gtk_im_context_hide(priv->imContext);
87 #endif
88 }
89
90 bool EditorClient::shouldDeleteRange(Range*)
91 {
92     notImplemented();
93     return true;
94 }
95
96 bool EditorClient::shouldShowDeleteInterface(HTMLElement*)
97 {
98     return false;
99 }
100
101 bool EditorClient::isContinuousSpellCheckingEnabled()
102 {
103     notImplemented();
104     return false;
105 }
106
107 bool EditorClient::isGrammarCheckingEnabled()
108 {
109     notImplemented();
110     return false;
111 }
112
113 int EditorClient::spellCheckerDocumentTag()
114 {
115     notImplemented();
116     return 0;
117 }
118
119 bool EditorClient::shouldBeginEditing(WebCore::Range*)
120 {
121     notImplemented();
122     return true;
123 }
124
125 bool EditorClient::shouldEndEditing(WebCore::Range*)
126 {
127     notImplemented();
128     return true;
129 }
130
131 bool EditorClient::shouldInsertText(const String&, Range*, EditorInsertAction)
132 {
133     notImplemented();
134     return true;
135 }
136
137 bool EditorClient::shouldChangeSelectedRange(Range*, Range*, EAffinity, bool)
138 {
139     notImplemented();
140     return true;
141 }
142
143 bool EditorClient::shouldApplyStyle(WebCore::CSSStyleDeclaration*, WebCore::Range*)
144 {
145     notImplemented();
146     return true;
147 }
148
149 bool EditorClient::shouldMoveRangeAfterDelete(WebCore::Range*, WebCore::Range*)
150 {
151     notImplemented();
152     return true;
153 }
154
155 void EditorClient::didBeginEditing()
156 {
157     notImplemented();
158 }
159
160 void EditorClient::respondToChangedContents()
161 {
162     notImplemented();
163 }
164
165 void EditorClient::respondToChangedSelection()
166 {
167     WebKitWebViewPrivate* priv = m_webView->priv;
168
169     Frame* targetFrame = core(m_webView)->focusController()->focusedOrMainFrame();
170     if (!targetFrame || !targetFrame->editor()->hasComposition())
171         return;
172
173     if (targetFrame->editor()->ignoreCompositionSelectionChange())
174         return;
175
176     unsigned start;
177     unsigned end;
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();
182     }
183 }
184
185 void EditorClient::didEndEditing()
186 {
187     notImplemented();
188 }
189
190 void EditorClient::didWriteSelectionToPasteboard()
191 {
192     notImplemented();
193 }
194
195 void EditorClient::didSetSelectionTypesForPasteboard()
196 {
197     notImplemented();
198 }
199
200 bool EditorClient::isEditable()
201 {
202     return webkit_web_view_get_editable(m_webView);
203 }
204
205 void EditorClient::registerCommandForUndo(WTF::PassRefPtr<WebCore::EditCommand>)
206 {
207     notImplemented();
208 }
209
210 void EditorClient::registerCommandForRedo(WTF::PassRefPtr<WebCore::EditCommand>)
211 {
212     notImplemented();
213 }
214
215 void EditorClient::clearUndoRedoOperations()
216 {
217     notImplemented();
218 }
219
220 bool EditorClient::canUndo() const
221 {
222     notImplemented();
223     return false;
224 }
225
226 bool EditorClient::canRedo() const
227 {
228     notImplemented();
229     return false;
230 }
231
232 void EditorClient::undo()
233 {
234     notImplemented();
235 }
236
237 void EditorClient::redo()
238 {
239     notImplemented();
240 }
241
242 bool EditorClient::shouldInsertNode(Node*, Range*, EditorInsertAction)
243 {
244     notImplemented();
245     return true;
246 }
247
248 void EditorClient::pageDestroyed()
249 {
250     delete this;
251 }
252
253 bool EditorClient::smartInsertDeleteEnabled()
254 {
255     notImplemented();
256     return false;
257 }
258
259 void EditorClient::toggleContinuousSpellChecking()
260 {
261     notImplemented();
262 }
263
264 void EditorClient::toggleGrammarChecking()
265 {
266 }
267
268 void EditorClient::handleKeyboardEvent(KeyboardEvent* event)
269 {
270     Frame* frame = core(m_webView)->focusController()->focusedOrMainFrame();
271     if (!frame || !frame->document()->focusedNode())
272         return;
273
274     const PlatformKeyboardEvent* kevent = event->keyEvent();
275     if (!kevent || kevent->type() == PlatformKeyboardEvent::KeyUp)
276         return;
277
278     Node* start = frame->selection()->start().node();
279     if (!start)
280         return;
281
282     // FIXME: Use GtkBindingSet instead of this hard-coded switch
283     // http://bugs.webkit.org/show_bug.cgi?id=15911
284
285     if (start->isContentEditable()) {
286         switch (kevent->windowsVirtualKeyCode()) {
287             case VK_BACK:
288                 frame->editor()->deleteWithDirection(SelectionController::BACKWARD,
289                         kevent->ctrlKey() ? WordGranularity : CharacterGranularity, false, true);
290                 break;
291             case VK_DELETE:
292                 frame->editor()->deleteWithDirection(SelectionController::FORWARD,
293                         kevent->ctrlKey() ? WordGranularity : CharacterGranularity, false, true);
294                 break;
295             case VK_LEFT:
296                 frame->selection()->modify(kevent->shiftKey() ? SelectionController::EXTEND : SelectionController::MOVE,
297                         SelectionController::LEFT,
298                         kevent->ctrlKey() ? WordGranularity : CharacterGranularity,
299                         true);
300                 break;
301             case VK_RIGHT:
302                 frame->selection()->modify(kevent->shiftKey() ? SelectionController::EXTEND : SelectionController::MOVE,
303                         SelectionController::RIGHT,
304                         kevent->ctrlKey() ? WordGranularity : CharacterGranularity,
305                         true);
306                 break;
307             case VK_UP:
308                 frame->selection()->modify(kevent->shiftKey() ? SelectionController::EXTEND : SelectionController::MOVE,
309                         SelectionController::BACKWARD,
310                         kevent->ctrlKey() ? ParagraphGranularity : LineGranularity,
311                         true);
312                 break;
313             case VK_DOWN:
314                 frame->selection()->modify(kevent->shiftKey() ? SelectionController::EXTEND : SelectionController::MOVE,
315                         SelectionController::FORWARD,
316                         kevent->ctrlKey() ? ParagraphGranularity : LineGranularity,
317                         true);
318                 break;
319             case VK_PRIOR:  // PageUp
320                 frame->editor()->command("MovePageUp").execute();
321                 break;
322             case VK_NEXT:  // PageDown
323                 frame->editor()->command("MovePageDown").execute();
324                 break;
325             case VK_HOME:
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();
332                 else
333                     frame->editor()->command("MoveToBeginningOfLine").execute();
334                 break;
335             case VK_END:
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();
342                 else
343                     frame->editor()->command("MoveToEndOfLine").execute();
344                 break;
345             case VK_RETURN:
346                 frame->editor()->command("InsertLineBreak").execute();
347                 break;
348             case VK_TAB:
349                 return;
350             default:
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
355                         if (ch < ' ')
356                             break;
357                     }
358                     frame->editor()->insertText(kevent->text(), event);
359                 } else if (kevent->ctrlKey()) {
360                     switch (kevent->windowsVirtualKeyCode()) {
361                         case VK_B:
362                             frame->editor()->command("ToggleBold").execute();
363                             break;
364                         case VK_I:
365                             frame->editor()->command("ToggleItalic").execute();
366                             break;
367                         case VK_Y:
368                             frame->editor()->command("Redo").execute();
369                             break;
370                         case VK_Z:
371                             frame->editor()->command("Undo").execute();
372                             break;
373                         default:
374                             return;
375                     }
376                 } else return;
377         }
378     } else {
379         switch (kevent->windowsVirtualKeyCode()) {
380             case VK_UP:
381                 frame->editor()->command("MoveUp").execute();
382                 break;
383             case VK_DOWN:
384                 frame->editor()->command("MoveDown").execute();
385                 break;
386             case VK_PRIOR:  // PageUp
387                 frame->editor()->command("MovePageUp").execute();
388                 break;
389             case VK_NEXT:  // PageDown
390                 frame->editor()->command("MovePageDown").execute();
391                 break;
392             case VK_HOME:
393                 if (kevent->ctrlKey())
394                     frame->editor()->command("MoveToBeginningOfDocument").execute();
395                 break;
396             case VK_END:
397                 if (kevent->ctrlKey())
398                     frame->editor()->command("MoveToEndOfDocument").execute();
399                 break;
400             default:
401                 return;
402         }
403     }
404     event->setDefaultHandled();
405 }
406
407 void EditorClient::handleInputMethodKeydown(KeyboardEvent* event)
408 {
409     WebKitWebViewPrivate* priv = m_webView->priv;
410
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();
414 }
415
416 EditorClient::EditorClient(WebKitWebView* webView)
417     : m_webView(webView)
418 {
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);
422 }
423
424 EditorClient::~EditorClient()
425 {
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);
429 }
430
431 void EditorClient::textFieldDidBeginEditing(Element*)
432 {
433 }
434
435 void EditorClient::textFieldDidEndEditing(Element*)
436 {
437 }
438
439 void EditorClient::textDidChangeInTextField(Element*)
440 {
441 }
442
443 bool EditorClient::doTextFieldCommandFromEvent(Element*, KeyboardEvent*)
444 {
445     return false;
446 }
447
448 void EditorClient::textWillBeDeletedInTextField(Element*)
449 {
450     notImplemented();
451 }
452
453 void EditorClient::textDidChangeInTextArea(Element*)
454 {
455     notImplemented();
456 }
457
458 void EditorClient::ignoreWordInSpellDocument(const String&)
459 {
460     notImplemented();
461 }
462
463 void EditorClient::learnWord(const String&)
464 {
465     notImplemented();
466 }
467
468 void EditorClient::checkSpellingOfString(const UChar*, int, int*, int*)
469 {
470     notImplemented();
471 }
472
473 void EditorClient::checkGrammarOfString(const UChar*, int, Vector<GrammarDetail>&, int*, int*)
474 {
475     notImplemented();
476 }
477
478 void EditorClient::updateSpellingUIWithGrammarString(const String&, const GrammarDetail&)
479 {
480     notImplemented();
481 }
482
483 void EditorClient::updateSpellingUIWithMisspelledWord(const String&)
484 {
485     notImplemented();
486 }
487
488 void EditorClient::showSpellingUI(bool)
489 {
490     notImplemented();
491 }
492
493 bool EditorClient::spellingUIIsShowing()
494 {
495     notImplemented();
496     return false;
497 }
498
499 void EditorClient::getGuessesForWord(const String&, Vector<String>&)
500 {
501     notImplemented();
502 }
503
504 }