Home | History | Annotate | Download | only in WebCoreSupport
      1 /*
      2  * Copyright (C) 2006 Nikolas Zimmermann <zimmermann (at) kde.org>
      3  * Copyright (C) 2006 Zack Rusin <zack (at) kde.org>
      4  * Copyright (C) 2006, 2008 Apple Inc. All rights reserved.
      5  * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies)
      6  *
      7  * All rights reserved.
      8  *
      9  * Redistribution and use in source and binary forms, with or without
     10  * modification, are permitted provided that the following conditions
     11  * are met:
     12  * 1. Redistributions of source code must retain the above copyright
     13  *    notice, this list of conditions and the following disclaimer.
     14  * 2. Redistributions in binary form must reproduce the above copyright
     15  *    notice, this list of conditions and the following disclaimer in the
     16  *    documentation and/or other materials provided with the distribution.
     17  *
     18  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
     19  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     21  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
     22  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     23  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     24  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     25  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
     26  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     29  */
     30 
     31 #include "config.h"
     32 #include "EditorClientQt.h"
     33 
     34 #include "qwebpage.h"
     35 #include "qwebpage_p.h"
     36 
     37 #include "CSSStyleDeclaration.h"
     38 #include "Document.h"
     39 #include "EditCommandQt.h"
     40 #include "Editor.h"
     41 #include "FocusController.h"
     42 #include "Frame.h"
     43 #include "HTMLElement.h"
     44 #include "HTMLInputElement.h"
     45 #include "HTMLNames.h"
     46 #include "KeyboardCodes.h"
     47 #include "KeyboardEvent.h"
     48 #include "NotImplemented.h"
     49 #include "Page.h"
     50 #include "Page.h"
     51 #include "PlatformKeyboardEvent.h"
     52 #include "QWebPageClient.h"
     53 #include "Range.h"
     54 
     55 #include <stdio.h>
     56 
     57 #include <QUndoStack>
     58 #define methodDebug() qDebug("EditorClientQt: %s", __FUNCTION__);
     59 
     60 static bool dumpEditingCallbacks = false;
     61 static bool acceptsEditing = true;
     62 void QWEBKIT_EXPORT qt_dump_editing_callbacks(bool b)
     63 {
     64     dumpEditingCallbacks = 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     WebCore::ExceptionCode code;
     91 
     92     QString str = QString("range from %1 of %2 to %3 of %4")
     93         .arg(range->startOffset(code)).arg(dumpPath(range->startContainer(code)))
     94         .arg(range->endOffset(code)).arg(dumpPath(range->endContainer(code)));
     95 
     96     return str;
     97 }
     98 
     99 
    100 namespace WebCore {
    101 
    102 using namespace HTMLNames;
    103 
    104 bool EditorClientQt::shouldDeleteRange(Range* range)
    105 {
    106     if (dumpEditingCallbacks)
    107         printf("EDITING DELEGATE: shouldDeleteDOMRange:%s\n", dumpRange(range).toUtf8().constData());
    108 
    109     return true;
    110 }
    111 
    112 bool EditorClientQt::shouldShowDeleteInterface(HTMLElement* element)
    113 {
    114     if (QWebPagePrivate::drtRun)
    115         return element->getAttribute(classAttr) == "needsDeletionUI";
    116     return false;
    117 }
    118 
    119 bool EditorClientQt::isContinuousSpellCheckingEnabled()
    120 {
    121     return false;
    122 }
    123 
    124 bool EditorClientQt::isGrammarCheckingEnabled()
    125 {
    126     return false;
    127 }
    128 
    129 int EditorClientQt::spellCheckerDocumentTag()
    130 {
    131     return 0;
    132 }
    133 
    134 bool EditorClientQt::shouldBeginEditing(WebCore::Range* range)
    135 {
    136     if (dumpEditingCallbacks)
    137         printf("EDITING DELEGATE: shouldBeginEditingInDOMRange:%s\n", dumpRange(range).toUtf8().constData());
    138     return true;
    139 }
    140 
    141 bool EditorClientQt::shouldEndEditing(WebCore::Range* range)
    142 {
    143     if (dumpEditingCallbacks)
    144         printf("EDITING DELEGATE: shouldEndEditingInDOMRange:%s\n", dumpRange(range).toUtf8().constData());
    145     return true;
    146 }
    147 
    148 bool EditorClientQt::shouldInsertText(const String& string, Range* range, EditorInsertAction action)
    149 {
    150     if (dumpEditingCallbacks) {
    151         static const char *insertactionstring[] = {
    152             "WebViewInsertActionTyped",
    153             "WebViewInsertActionPasted",
    154             "WebViewInsertActionDropped",
    155         };
    156 
    157         printf("EDITING DELEGATE: shouldInsertText:%s replacingDOMRange:%s givenAction:%s\n",
    158                QString(string).toUtf8().constData(), dumpRange(range).toUtf8().constData(), insertactionstring[action]);
    159     }
    160     return acceptsEditing;
    161 }
    162 
    163 bool EditorClientQt::shouldChangeSelectedRange(Range* currentRange, Range* proposedRange, EAffinity selectionAffinity, bool stillSelecting)
    164 {
    165     if (dumpEditingCallbacks) {
    166         static const char *affinitystring[] = {
    167             "NSSelectionAffinityUpstream",
    168             "NSSelectionAffinityDownstream"
    169         };
    170         static const char *boolstring[] = {
    171             "FALSE",
    172             "TRUE"
    173         };
    174 
    175         printf("EDITING DELEGATE: shouldChangeSelectedDOMRange:%s toDOMRange:%s affinity:%s stillSelecting:%s\n",
    176                dumpRange(currentRange).toUtf8().constData(),
    177                dumpRange(proposedRange).toUtf8().constData(),
    178                affinitystring[selectionAffinity], boolstring[stillSelecting]);
    179     }
    180     return acceptsEditing;
    181 }
    182 
    183 bool EditorClientQt::shouldApplyStyle(WebCore::CSSStyleDeclaration* style,
    184                                       WebCore::Range* range)
    185 {
    186     if (dumpEditingCallbacks)
    187         printf("EDITING DELEGATE: shouldApplyStyle:%s toElementsInDOMRange:%s\n",
    188                QString(style->cssText()).toUtf8().constData(), dumpRange(range).toUtf8().constData());
    189     return acceptsEditing;
    190 }
    191 
    192 bool EditorClientQt::shouldMoveRangeAfterDelete(WebCore::Range*, WebCore::Range*)
    193 {
    194     notImplemented();
    195     return true;
    196 }
    197 
    198 void EditorClientQt::didBeginEditing()
    199 {
    200     if (dumpEditingCallbacks)
    201         printf("EDITING DELEGATE: webViewDidBeginEditing:WebViewDidBeginEditingNotification\n");
    202     m_editing = true;
    203 }
    204 
    205 void EditorClientQt::respondToChangedContents()
    206 {
    207     if (dumpEditingCallbacks)
    208         printf("EDITING DELEGATE: webViewDidChange:WebViewDidChangeNotification\n");
    209     m_page->d->updateEditorActions();
    210 
    211     emit m_page->contentsChanged();
    212 }
    213 
    214 void EditorClientQt::respondToChangedSelection()
    215 {
    216     if (dumpEditingCallbacks)
    217         printf("EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification\n");
    218 //     const Selection &selection = m_page->d->page->selection();
    219 //     char buffer[1024];
    220 //     selection.formatForDebugger(buffer, sizeof(buffer));
    221 //     printf("%s\n", buffer);
    222 
    223     m_page->d->updateEditorActions();
    224     emit m_page->selectionChanged();
    225     Frame* frame = m_page->d->page->focusController()->focusedOrMainFrame();
    226     if (!frame->editor()->ignoreCompositionSelectionChange())
    227         emit m_page->microFocusChanged();
    228 }
    229 
    230 void EditorClientQt::didEndEditing()
    231 {
    232     if (dumpEditingCallbacks)
    233         printf("EDITING DELEGATE: webViewDidEndEditing:WebViewDidEndEditingNotification\n");
    234     m_editing = false;
    235 }
    236 
    237 void EditorClientQt::didWriteSelectionToPasteboard()
    238 {
    239 }
    240 
    241 void EditorClientQt::didSetSelectionTypesForPasteboard()
    242 {
    243 }
    244 
    245 bool EditorClientQt::selectWordBeforeMenuEvent()
    246 {
    247     notImplemented();
    248     return false;
    249 }
    250 
    251 bool EditorClientQt::isEditable()
    252 {
    253     return m_page->isContentEditable();
    254 }
    255 
    256 void EditorClientQt::registerCommandForUndo(WTF::PassRefPtr<WebCore::EditCommand> cmd)
    257 {
    258 #ifndef QT_NO_UNDOSTACK
    259     Frame* frame = m_page->d->page->focusController()->focusedOrMainFrame();
    260     if (m_inUndoRedo || (frame && !frame->editor()->lastEditCommand() /* HACK!! Don't recreate undos */)) {
    261         return;
    262     }
    263     m_page->undoStack()->push(new EditCommandQt(cmd));
    264 #endif // QT_NO_UNDOSTACK
    265 }
    266 
    267 void EditorClientQt::registerCommandForRedo(WTF::PassRefPtr<WebCore::EditCommand>)
    268 {
    269 }
    270 
    271 void EditorClientQt::clearUndoRedoOperations()
    272 {
    273 #ifndef QT_NO_UNDOSTACK
    274     return m_page->undoStack()->clear();
    275 #endif
    276 }
    277 
    278 bool EditorClientQt::canUndo() const
    279 {
    280 #ifdef QT_NO_UNDOSTACK
    281     return false;
    282 #else
    283     return m_page->undoStack()->canUndo();
    284 #endif
    285 }
    286 
    287 bool EditorClientQt::canRedo() const
    288 {
    289 #ifdef QT_NO_UNDOSTACK
    290     return false;
    291 #else
    292     return m_page->undoStack()->canRedo();
    293 #endif
    294 }
    295 
    296 void EditorClientQt::undo()
    297 {
    298 #ifndef QT_NO_UNDOSTACK
    299     m_inUndoRedo = true;
    300     m_page->undoStack()->undo();
    301     m_inUndoRedo = false;
    302 #endif
    303 }
    304 
    305 void EditorClientQt::redo()
    306 {
    307 #ifndef QT_NO_UNDOSTACK
    308     m_inUndoRedo = true;
    309     m_page->undoStack()->redo();
    310     m_inUndoRedo = false;
    311 #endif
    312 }
    313 
    314 bool EditorClientQt::shouldInsertNode(Node* node, Range* range, EditorInsertAction action)
    315 {
    316     if (dumpEditingCallbacks) {
    317         static const char *insertactionstring[] = {
    318             "WebViewInsertActionTyped",
    319             "WebViewInsertActionPasted",
    320             "WebViewInsertActionDropped",
    321         };
    322 
    323         printf("EDITING DELEGATE: shouldInsertNode:%s replacingDOMRange:%s givenAction:%s\n", dumpPath(node).toUtf8().constData(),
    324                dumpRange(range).toUtf8().constData(), insertactionstring[action]);
    325     }
    326     return acceptsEditing;
    327 }
    328 
    329 void EditorClientQt::pageDestroyed()
    330 {
    331     delete this;
    332 }
    333 
    334 bool EditorClientQt::smartInsertDeleteEnabled()
    335 {
    336     notImplemented();
    337     return false;
    338 }
    339 
    340 bool EditorClientQt::isSelectTrailingWhitespaceEnabled()
    341 {
    342     notImplemented();
    343     return false;
    344 }
    345 
    346 void EditorClientQt::toggleContinuousSpellChecking()
    347 {
    348     notImplemented();
    349 }
    350 
    351 void EditorClientQt::toggleGrammarChecking()
    352 {
    353     notImplemented();
    354 }
    355 
    356 void EditorClientQt::handleKeyboardEvent(KeyboardEvent* event)
    357 {
    358     Frame* frame = m_page->d->page->focusController()->focusedOrMainFrame();
    359     if (!frame || !frame->document()->focusedNode())
    360         return;
    361 
    362     const PlatformKeyboardEvent* kevent = event->keyEvent();
    363     if (!kevent || kevent->type() == PlatformKeyboardEvent::KeyUp)
    364         return;
    365 
    366     Node* start = frame->selection()->start().node();
    367     if (!start)
    368         return;
    369 
    370     // FIXME: refactor all of this to use Actions or something like them
    371     if (start->isContentEditable()) {
    372 #ifndef QT_NO_SHORTCUT
    373         QWebPage::WebAction action = QWebPagePrivate::editorActionForKeyEvent(kevent->qtEvent());
    374         if (action != QWebPage::NoWebAction) {
    375             const char* cmd = QWebPagePrivate::editorCommandForWebActions(action);
    376             // WebKit doesn't have enough information about mode to decide how commands that just insert text if executed via Editor should be treated,
    377             // so we leave it upon WebCore to either handle them immediately (e.g. Tab that changes focus) or let a keypress event be generated
    378             // (e.g. Tab that inserts a Tab character, or Enter).
    379             if (cmd && frame->editor()->command(cmd).isTextInsertion()
    380                 && kevent->type() == PlatformKeyboardEvent::RawKeyDown)
    381                 return;
    382 
    383             m_page->triggerAction(action);
    384         } else
    385 #endif // QT_NO_SHORTCUT
    386         switch (kevent->windowsVirtualKeyCode()) {
    387 #if QT_VERSION < 0x040500
    388             case VK_RETURN:
    389 #ifdef QT_WS_MAC
    390                 if (kevent->shiftKey() || kevent->metaKey())
    391 #else
    392                 if (kevent->shiftKey())
    393 #endif
    394                     frame->editor()->command("InsertLineBreak").execute();
    395                 else
    396                     frame->editor()->command("InsertNewline").execute();
    397                 break;
    398 #endif
    399             case VK_BACK:
    400                 frame->editor()->deleteWithDirection(SelectionController::BACKWARD,
    401                         CharacterGranularity, false, true);
    402                 break;
    403             case VK_DELETE:
    404                 frame->editor()->deleteWithDirection(SelectionController::FORWARD,
    405                         CharacterGranularity, false, true);
    406                 break;
    407             case VK_LEFT:
    408                 if (kevent->shiftKey())
    409                     frame->editor()->command("MoveLeftAndModifySelection").execute();
    410                 else
    411                     frame->editor()->command("MoveLeft").execute();
    412                 break;
    413             case VK_RIGHT:
    414                 if (kevent->shiftKey())
    415                     frame->editor()->command("MoveRightAndModifySelection").execute();
    416                 else
    417                     frame->editor()->command("MoveRight").execute();
    418                 break;
    419             case VK_UP:
    420                 if (kevent->shiftKey())
    421                     frame->editor()->command("MoveUpAndModifySelection").execute();
    422                 else
    423                     frame->editor()->command("MoveUp").execute();
    424                 break;
    425             case VK_DOWN:
    426                 if (kevent->shiftKey())
    427                     frame->editor()->command("MoveDownAndModifySelection").execute();
    428                 else
    429                     frame->editor()->command("MoveDown").execute();
    430                 break;
    431             case VK_PRIOR:  // PageUp
    432                 if (kevent->shiftKey())
    433                     frame->editor()->command("MovePageUpAndModifySelection").execute();
    434                 else
    435                     frame->editor()->command("MovePageUp").execute();
    436                 break;
    437             case VK_NEXT:  // PageDown
    438                 if (kevent->shiftKey())
    439                     frame->editor()->command("MovePageDownAndModifySelection").execute();
    440                 else
    441                     frame->editor()->command("MovePageDown").execute();
    442                 break;
    443             case VK_TAB:
    444                 return;
    445             default:
    446                 if (kevent->type() != PlatformKeyboardEvent::KeyDown && !kevent->ctrlKey()
    447 #ifndef Q_WS_MAC
    448                     // We need to exclude checking for Alt because it is just a different Shift
    449                     && !kevent->altKey()
    450 #endif
    451                     && !kevent->text().isEmpty()) {
    452                     frame->editor()->insertText(kevent->text(), event);
    453                 } else if (kevent->ctrlKey()) {
    454                     switch (kevent->windowsVirtualKeyCode()) {
    455                         case VK_A:
    456                             frame->editor()->command("SelectAll").execute();
    457                             break;
    458                         case VK_B:
    459                             frame->editor()->command("ToggleBold").execute();
    460                             break;
    461                         case VK_I:
    462                             frame->editor()->command("ToggleItalic").execute();
    463                             break;
    464                         default:
    465                             // catch combination AltGr+key or Ctrl+Alt+key
    466                             if (kevent->type() != PlatformKeyboardEvent::KeyDown && kevent->altKey() && !kevent->text().isEmpty()) {
    467                                 frame->editor()->insertText(kevent->text(), event);
    468                                 break;
    469                             }
    470                             return;
    471                     }
    472                 } else return;
    473         }
    474     } else {
    475 #ifndef QT_NO_SHORTCUT
    476         if (kevent->qtEvent() == QKeySequence::Copy) {
    477             m_page->triggerAction(QWebPage::Copy);
    478         } else
    479 #endif // QT_NO_SHORTCUT
    480         switch (kevent->windowsVirtualKeyCode()) {
    481             case VK_UP:
    482                 frame->editor()->command("MoveUp").execute();
    483                 break;
    484             case VK_DOWN:
    485                 frame->editor()->command("MoveDown").execute();
    486                 break;
    487             case VK_PRIOR:  // PageUp
    488                 frame->editor()->command("MovePageUp").execute();
    489                 break;
    490             case VK_NEXT:  // PageDown
    491                 frame->editor()->command("MovePageDown").execute();
    492                 break;
    493             case VK_HOME:
    494                 if (kevent->ctrlKey())
    495                     frame->editor()->command("MoveToBeginningOfDocument").execute();
    496                 break;
    497             case VK_END:
    498                 if (kevent->ctrlKey())
    499                     frame->editor()->command("MoveToEndOfDocument").execute();
    500                 break;
    501             default:
    502                 if (kevent->ctrlKey()) {
    503                     switch (kevent->windowsVirtualKeyCode()) {
    504                         case VK_A:
    505                             frame->editor()->command("SelectAll").execute();
    506                             break;
    507                         default:
    508                             return;
    509                     }
    510                 } else return;
    511         }
    512     }
    513     event->setDefaultHandled();
    514 }
    515 
    516 void EditorClientQt::handleInputMethodKeydown(KeyboardEvent*)
    517 {
    518 }
    519 
    520 EditorClientQt::EditorClientQt(QWebPage* page)
    521     : m_page(page), m_editing(false), m_inUndoRedo(false)
    522 {
    523 }
    524 
    525 void EditorClientQt::textFieldDidBeginEditing(Element*)
    526 {
    527     m_editing = true;
    528 }
    529 
    530 void EditorClientQt::textFieldDidEndEditing(Element*)
    531 {
    532     m_editing = false;
    533 }
    534 
    535 void EditorClientQt::textDidChangeInTextField(Element*)
    536 {
    537 }
    538 
    539 bool EditorClientQt::doTextFieldCommandFromEvent(Element*, KeyboardEvent*)
    540 {
    541     return false;
    542 }
    543 
    544 void EditorClientQt::textWillBeDeletedInTextField(Element*)
    545 {
    546 }
    547 
    548 void EditorClientQt::textDidChangeInTextArea(Element*)
    549 {
    550 }
    551 
    552 void EditorClientQt::ignoreWordInSpellDocument(const String&)
    553 {
    554     notImplemented();
    555 }
    556 
    557 void EditorClientQt::learnWord(const String&)
    558 {
    559     notImplemented();
    560 }
    561 
    562 void EditorClientQt::checkSpellingOfString(const UChar*, int, int*, int*)
    563 {
    564     notImplemented();
    565 }
    566 
    567 String EditorClientQt::getAutoCorrectSuggestionForMisspelledWord(const String&)
    568 {
    569     notImplemented();
    570     return String();
    571 }
    572 
    573 void EditorClientQt::checkGrammarOfString(const UChar*, int, Vector<GrammarDetail>&, int*, int*)
    574 {
    575     notImplemented();
    576 }
    577 
    578 void EditorClientQt::updateSpellingUIWithGrammarString(const String&, const GrammarDetail&)
    579 {
    580     notImplemented();
    581 }
    582 
    583 void EditorClientQt::updateSpellingUIWithMisspelledWord(const String&)
    584 {
    585     notImplemented();
    586 }
    587 
    588 void EditorClientQt::showSpellingUI(bool)
    589 {
    590     notImplemented();
    591 }
    592 
    593 bool EditorClientQt::spellingUIIsShowing()
    594 {
    595     notImplemented();
    596     return false;
    597 }
    598 
    599 void EditorClientQt::getGuessesForWord(const String&, Vector<String>&)
    600 {
    601     notImplemented();
    602 }
    603 
    604 bool EditorClientQt::isEditing() const
    605 {
    606     return m_editing;
    607 }
    608 
    609 void EditorClientQt::setInputMethodState(bool active)
    610 {
    611     QWebPageClient* webPageClient = m_page->d->client;
    612     if (webPageClient) {
    613 #if QT_VERSION >= 0x040600
    614         bool isPasswordField = false;
    615         if (!active) {
    616             // Setting the Qt::WA_InputMethodEnabled attribute true and Qt::ImhHiddenText flag
    617             // for password fields. The Qt platform is responsible for determining which widget
    618             // will receive input method events for password fields.
    619             Frame* frame = m_page->d->page->focusController()->focusedOrMainFrame();
    620             if (frame && frame->document() && frame->document()->focusedNode()) {
    621                 if (frame->document()->focusedNode()->hasTagName(HTMLNames::inputTag)) {
    622                     HTMLInputElement* inputElement = static_cast<HTMLInputElement*>(frame->document()->focusedNode());
    623                     active = isPasswordField = inputElement->isPasswordField();
    624               }
    625             }
    626         }
    627         webPageClient->setInputMethodHint(Qt::ImhHiddenText, isPasswordField);
    628 #ifdef Q_WS_MAEMO_5
    629         // Maemo 5 MicroB Browser disables auto-uppercase and predictive text, thus, so do we.
    630         webPageClient->setInputMethodHint(Qt::ImhNoAutoUppercase, true);
    631         webPageClient->setInputMethodHint(Qt::ImhNoPredictiveText, true);
    632 #endif // Q_WS_MAEMO_5
    633 #endif // QT_VERSION check
    634         webPageClient->setInputMethodEnabled(active);
    635     }
    636     emit m_page->microFocusChanged();
    637 }
    638 
    639 }
    640 
    641 // vim: ts=4 sw=4 et
    642