2008-08-05 Tor Arne Vestbø <tavestbo@trolltech.com>
[WebKit-https.git] / WebKit / qt / WebCoreSupport / EditorClientQt.cpp
1 /*
2  * Copyright (C) 2006 Nikolas Zimmermann <zimmermann@kde.org> 
3  * Copyright (C) 2006 Zack Rusin <zack@kde.org>
4  * Copyright (C) 2006 Apple Computer, Inc.
5  *
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
21  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
25  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29
30 #include "config.h"
31 #include "EditorClientQt.h"
32
33 #include "qwebpage.h"
34 #include "qwebpage_p.h"
35
36 #include "Document.h"
37 #include "EditCommandQt.h"
38 #include "Page.h"
39 #include "Editor.h"
40 #include "FocusController.h"
41 #include "Frame.h"
42 #include "KeyboardCodes.h"
43 #include "KeyboardEvent.h"
44 #include "Page.h"
45 #include "PlatformKeyboardEvent.h"
46 #include "NotImplemented.h"
47 #include "Node.h"
48 #include "Range.h"
49
50 #include <stdio.h>
51
52 #include <QUndoStack>
53 #define methodDebug() qDebug("EditorClientQt: %s", __FUNCTION__);
54
55 static bool dumpEditingCallbacks = false;
56 static bool drt_run = false;
57 static bool acceptsEditing = true;
58 void QWEBKIT_EXPORT qt_dump_editing_callbacks(bool b)
59 {
60     dumpEditingCallbacks = b;
61 }
62 void QWEBKIT_EXPORT qt_drt_run(bool b)
63 {
64     drt_run = b;
65 }
66
67 void QWEBKIT_EXPORT qt_dump_set_accepts_editing(bool b)
68 {
69     acceptsEditing = b;
70 }
71
72
73 static QString dumpPath(WebCore::Node *node)
74 {
75     QString str = node->nodeName();
76     
77     WebCore::Node *parent = node->parentNode();
78     while (parent) {
79         str.append(QLatin1String(" > "));
80         str.append(parent->nodeName());
81         parent = parent->parentNode();
82     }
83     return str;
84 }
85
86 static QString dumpRange(WebCore::Range *range)
87 {
88     if (!range)
89         return QLatin1String("(null)");
90     QString str;
91     WebCore::ExceptionCode code;
92     str.sprintf("range from %ld of %ls to %ld of %ls",
93                 range->startOffset(code), dumpPath(range->startContainer(code)).unicode(),
94                 range->endOffset(code), dumpPath(range->endContainer(code)).unicode());
95     return str;
96 }
97     
98
99 namespace WebCore {
100
101
102 bool EditorClientQt::shouldDeleteRange(Range* range)
103 {
104     if (dumpEditingCallbacks) 
105         printf("EDITING DELEGATE: shouldDeleteDOMRange:%s\n", dumpRange(range).toUtf8().constData());
106
107     return true;
108 }
109
110 bool EditorClientQt::shouldShowDeleteInterface(HTMLElement* element)
111 {
112     if (drt_run) 
113         return element->className() == "needsDeletionUI";
114     return false;
115 }
116
117 bool EditorClientQt::isContinuousSpellCheckingEnabled()
118 {
119     return false;
120 }
121
122 bool EditorClientQt::isGrammarCheckingEnabled()
123 {
124     return false;
125 }
126
127 int EditorClientQt::spellCheckerDocumentTag()
128 {
129     return 0;
130 }
131
132 bool EditorClientQt::shouldBeginEditing(WebCore::Range* range)
133 {
134     if (dumpEditingCallbacks)
135         printf("EDITING DELEGATE: shouldBeginEditingInDOMRange:%s\n", dumpRange(range).toUtf8().constData());
136     return true;
137 }
138
139 bool EditorClientQt::shouldEndEditing(WebCore::Range* range)
140 {
141     if (dumpEditingCallbacks)
142         printf("EDITING DELEGATE: shouldEndEditingInDOMRange:%s\n", dumpRange(range).toUtf8().constData());
143     return true;
144 }
145
146 bool EditorClientQt::shouldInsertText(const String& string, Range* range, EditorInsertAction action)
147 {
148     if (dumpEditingCallbacks) {
149         static const char *insertactionstring[] = {
150             "WebViewInsertActionTyped",
151             "WebViewInsertActionPasted",
152             "WebViewInsertActionDropped",
153         };
154         
155         printf("EDITING DELEGATE: shouldInsertText:%s replacingDOMRange:%s givenAction:%s\n",
156                QString(string).toUtf8().constData(), dumpRange(range).toUtf8().constData(), insertactionstring[action]);
157     }
158     return acceptsEditing;
159 }
160
161 bool EditorClientQt::shouldChangeSelectedRange(Range* currentRange, Range* proposedRange, EAffinity selectionAffinity, bool stillSelecting)
162 {
163     if (dumpEditingCallbacks) {
164         static const char *affinitystring[] = {
165             "NSSelectionAffinityUpstream",
166             "NSSelectionAffinityDownstream"
167         };
168         static const char *boolstring[] = {
169             "FALSE",
170             "TRUE"
171         };
172         
173         printf("EDITING DELEGATE: shouldChangeSelectedDOMRange:%s toDOMRange:%s affinity:%s stillSelecting:%s\n",
174                dumpRange(currentRange).toUtf8().constData(),
175                dumpRange(proposedRange).toUtf8().constData(),
176                affinitystring[selectionAffinity], boolstring[stillSelecting]);
177     }
178     return acceptsEditing;
179 }
180
181 bool EditorClientQt::shouldApplyStyle(WebCore::CSSStyleDeclaration* style,
182                                       WebCore::Range* range)
183 {
184     if (dumpEditingCallbacks)
185         printf("EDITING DELEGATE: shouldApplyStyle:%s toElementsInDOMRange:%s\n",
186                QString(style->cssText()).toUtf8().constData(), dumpRange(range).toUtf8().constData());
187     return acceptsEditing;
188 }
189
190 bool EditorClientQt::shouldMoveRangeAfterDelete(WebCore::Range*, WebCore::Range*)
191 {
192     notImplemented();
193     return true;
194 }
195
196 void EditorClientQt::didBeginEditing()
197 {
198     if (dumpEditingCallbacks)
199         printf("EDITING DELEGATE: webViewDidBeginEditing:WebViewDidBeginEditingNotification\n");
200     m_editing = true;
201 }
202
203 void EditorClientQt::respondToChangedContents()
204 {
205     if (dumpEditingCallbacks)
206         printf("EDITING DELEGATE: webViewDidChange:WebViewDidChangeNotification\n");
207     m_page->d->modified = true;
208
209     m_page->d->updateEditorActions();
210 }
211
212 void EditorClientQt::respondToChangedSelection()
213 {
214     if (dumpEditingCallbacks)
215         printf("EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification\n");
216 //     const Selection &selection = m_page->d->page->selection();
217 //     char buffer[1024];
218 //     selection.formatForDebugger(buffer, sizeof(buffer));
219 //     printf("%s\n", buffer);
220
221     m_page->d->updateEditorActions();
222     emit m_page->selectionChanged();
223     emit m_page->microFocusChanged();
224 }
225
226 void EditorClientQt::didEndEditing()
227 {
228     if (dumpEditingCallbacks)
229         printf("EDITING DELEGATE: webViewDidEndEditing:WebViewDidEndEditingNotification\n");
230     m_editing = false;
231 }
232
233 void EditorClientQt::didWriteSelectionToPasteboard()
234 {
235 }
236
237 void EditorClientQt::didSetSelectionTypesForPasteboard()
238 {
239 }
240
241 bool EditorClientQt::selectWordBeforeMenuEvent()
242 {
243     notImplemented();
244     return false;
245 }
246
247 bool EditorClientQt::isEditable()
248
249     return m_page->isEditable();
250 }
251
252 void EditorClientQt::registerCommandForUndo(WTF::PassRefPtr<WebCore::EditCommand> cmd)
253 {
254     Frame* frame = m_page->d->page->focusController()->focusedOrMainFrame();
255     if (m_inUndoRedo || (frame && !frame->editor()->lastEditCommand() /* HACK!! Don't recreate undos */)) {
256         return;
257     }
258     m_page->undoStack()->push(new EditCommandQt(cmd));
259 }
260
261 void EditorClientQt::registerCommandForRedo(WTF::PassRefPtr<WebCore::EditCommand>)
262 {
263 }
264
265 void EditorClientQt::clearUndoRedoOperations()
266 {
267     return m_page->undoStack()->clear();
268 }
269
270 bool EditorClientQt::canUndo() const
271 {
272     return m_page->undoStack()->canUndo();
273 }
274
275 bool EditorClientQt::canRedo() const
276 {
277     return m_page->undoStack()->canRedo();
278 }
279
280 void EditorClientQt::undo()
281 {
282     m_inUndoRedo = true;
283     m_page->undoStack()->undo();
284     m_inUndoRedo = false;
285 }
286
287 void EditorClientQt::redo()
288 {
289     m_inUndoRedo = true;
290     m_page->undoStack()->redo();
291     m_inUndoRedo = false;
292 }
293
294 bool EditorClientQt::shouldInsertNode(Node* node, Range* range, EditorInsertAction action)
295 {
296     if (dumpEditingCallbacks) {
297         static const char *insertactionstring[] = {
298             "WebViewInsertActionTyped",
299             "WebViewInsertActionPasted",
300             "WebViewInsertActionDropped",
301         };
302         
303         printf("EDITING DELEGATE: shouldInsertNode:%s replacingDOMRange:%s givenAction:%s\n", dumpPath(node).toUtf8().constData(),
304                dumpRange(range).toUtf8().constData(), insertactionstring[action]);
305     }
306     return acceptsEditing;
307 }
308
309 void EditorClientQt::pageDestroyed()
310 {
311     delete this;
312 }
313
314 bool EditorClientQt::smartInsertDeleteEnabled()
315 {
316     notImplemented();
317     return false;
318 }
319
320 void EditorClientQt::toggleContinuousSpellChecking()
321 {
322     notImplemented();
323 }
324
325 void EditorClientQt::toggleGrammarChecking()
326 {
327     notImplemented();
328 }
329
330 void EditorClientQt::handleKeyboardEvent(KeyboardEvent* event)
331 {
332     Frame* frame = m_page->d->page->focusController()->focusedOrMainFrame();
333     if (!frame || !frame->document()->focusedNode())
334         return;
335
336     const PlatformKeyboardEvent* kevent = event->keyEvent();
337     if (!kevent || kevent->type() == PlatformKeyboardEvent::KeyUp)
338         return;
339
340     Node* start = frame->selection()->start().node();
341     if (!start)
342         return;
343
344     // FIXME: refactor all of this to use Actions or something like them
345     if (start->isContentEditable()) {
346         switch (kevent->windowsVirtualKeyCode()) {
347 #if QT_VERSION < 0x040500
348             case VK_RETURN:
349 #ifdef QT_WS_MAC
350                 if (kevent->shiftKey() || kevent->metaKey())
351 #else
352                 if (kevent->shiftKey())
353 #endif
354                     frame->editor()->command("InsertLineBreak").execute();
355                 else
356                     frame->editor()->command("InsertNewline").execute();
357                 break;
358 #endif
359             case VK_BACK:
360                 frame->editor()->deleteWithDirection(SelectionController::BACKWARD,
361                         CharacterGranularity, false, true);
362                 break;
363             case VK_DELETE:
364                 frame->editor()->deleteWithDirection(SelectionController::FORWARD,
365                         CharacterGranularity, false, true);
366                 break;
367             case VK_LEFT:
368                 if (kevent->shiftKey())
369                     frame->editor()->command("MoveLeftAndModifySelection").execute();
370                 else frame->editor()->command("MoveLeft").execute();
371                 break;
372             case VK_RIGHT:
373                 if (kevent->shiftKey())
374                     frame->editor()->command("MoveRightAndModifySelection").execute();
375                 else frame->editor()->command("MoveRight").execute();
376                 break;
377             case VK_UP:
378                 if (kevent->shiftKey())
379                     frame->editor()->command("MoveUpAndModifySelection").execute();
380                 else frame->editor()->command("MoveUp").execute();
381                 break;
382             case VK_DOWN:
383                 if (kevent->shiftKey())
384                     frame->editor()->command("MoveDownAndModifySelection").execute();
385                 else frame->editor()->command("MoveDown").execute();
386                 break;
387             case VK_PRIOR:  // PageUp
388                 frame->editor()->command("MovePageUp").execute();
389                 break;
390             case VK_NEXT:  // PageDown
391                 frame->editor()->command("MovePageDown").execute();
392                 break;
393             case VK_TAB:
394                 return;
395             default:
396                 if (kevent->type() != PlatformKeyboardEvent::KeyDown
397                         && !kevent->ctrlKey() && !kevent->altKey() && !kevent->text().isEmpty()) {
398                     frame->editor()->insertText(kevent->text(), event);
399                 } else if (kevent->ctrlKey()) {
400                     switch (kevent->windowsVirtualKeyCode()) {
401                         case VK_A:
402                             frame->editor()->command("SelectAll").execute();
403                             break;
404                         case VK_B:
405                             frame->editor()->command("ToggleBold").execute();
406                             break;
407                         case VK_C:
408                             frame->editor()->command("Copy").execute();
409                             break;
410                         case VK_I:
411                             frame->editor()->command("ToggleItalic").execute();
412                             break;
413                         case VK_V:
414                             frame->editor()->command("Paste").execute();
415                             break;
416                         case VK_X:
417                             frame->editor()->command("Cut").execute();
418                             break;
419                         case VK_Y:
420                             frame->editor()->command("Redo").execute();
421                             break;
422                         case VK_Z:
423                             frame->editor()->command("Undo").execute();
424                             break;
425                         default:
426                             // catch combination AltGr+key or Ctrl+Alt+key
427                             if (kevent->type() != PlatformKeyboardEvent::KeyDown && !kevent->text().isEmpty()) {
428                                 frame->editor()->insertText(kevent->text(), event);
429                                 break;
430                             }
431                             return;
432                     }
433                 } else return;
434         }
435     } else {
436         switch (kevent->windowsVirtualKeyCode()) {
437             case VK_UP:
438                 frame->editor()->command("MoveUp").execute();
439                 break;
440             case VK_DOWN:
441                 frame->editor()->command("MoveDown").execute();
442                 break;
443             case VK_PRIOR:  // PageUp
444                 frame->editor()->command("MovePageUp").execute();
445                 break;
446             case VK_NEXT:  // PageDown
447                 frame->editor()->command("MovePageDown").execute();
448                 break;
449             case VK_HOME:
450                 if (kevent->ctrlKey())
451                     frame->editor()->command("MoveToBeginningOfDocument").execute();
452                 break;
453             case VK_END:
454                 if (kevent->ctrlKey())
455                     frame->editor()->command("MoveToEndOfDocument").execute();
456                 break;
457             default:
458                 if (kevent->ctrlKey()) {
459                     switch (kevent->windowsVirtualKeyCode()) {
460                         case VK_A:
461                             frame->editor()->command("SelectAll").execute();
462                             break;
463                         case VK_C: case VK_X:
464                             frame->editor()->command("Copy").execute();
465                             break;
466                         default:
467                             return;
468                     }
469                 } else return;
470         }
471     }
472     event->setDefaultHandled();
473 }
474
475 void EditorClientQt::handleInputMethodKeydown(KeyboardEvent*)
476 {
477 }
478
479 EditorClientQt::EditorClientQt(QWebPage* page)
480     : m_page(page), m_editing(false), m_inUndoRedo(false)
481 {
482 }
483
484 void EditorClientQt::textFieldDidBeginEditing(Element*)
485 {
486     m_editing = true;
487 }
488
489 void EditorClientQt::textFieldDidEndEditing(Element*)
490 {
491     m_editing = false;
492 }
493
494 void EditorClientQt::textDidChangeInTextField(Element*)
495 {
496 }
497
498 bool EditorClientQt::doTextFieldCommandFromEvent(Element*, KeyboardEvent*)
499 {
500     return false;
501 }
502
503 void EditorClientQt::textWillBeDeletedInTextField(Element*)
504 {
505 }
506
507 void EditorClientQt::textDidChangeInTextArea(Element*)
508 {
509 }
510
511 void EditorClientQt::ignoreWordInSpellDocument(const String&)
512 {
513     notImplemented();
514 }
515
516 void EditorClientQt::learnWord(const String&)
517 {
518     notImplemented();
519 }
520
521 void EditorClientQt::checkSpellingOfString(const UChar*, int, int*, int*)
522 {
523     notImplemented();
524 }
525
526 void EditorClientQt::checkGrammarOfString(const UChar*, int, Vector<GrammarDetail>&, int*, int*)
527 {
528     notImplemented();
529 }
530
531 void EditorClientQt::updateSpellingUIWithGrammarString(const String&, const GrammarDetail&)
532 {
533     notImplemented();
534 }
535
536 void EditorClientQt::updateSpellingUIWithMisspelledWord(const String&)
537 {
538     notImplemented();
539 }
540
541 void EditorClientQt::showSpellingUI(bool)
542 {
543     notImplemented();
544 }
545
546 bool EditorClientQt::spellingUIIsShowing()
547 {
548     notImplemented();
549     return false;
550 }
551
552 void EditorClientQt::getGuessesForWord(const String&, Vector<String>&)
553 {
554     notImplemented();
555 }
556
557 bool EditorClientQt::isEditing() const
558 {
559     return m_editing;
560 }
561
562 void EditorClientQt::setInputMethodState(bool active)
563 {
564     QWidget *view = m_page->view();
565     if (view) {
566         view->setAttribute(Qt::WA_InputMethodEnabled, active);
567         emit m_page->microFocusChanged();
568     }
569 }
570
571 }
572
573 // vim: ts=4 sw=4 et