1 /* 2 * Copyright (C) 2007 Kevin Ollivier <kevino (at) theolliviers.com> 3 * 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY 16 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 18 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR 19 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 20 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 22 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 23 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 #include "config.h" 28 #include "EditorClientWx.h" 29 30 #include "EditCommand.h" 31 #include "Editor.h" 32 #include "FocusController.h" 33 #include "Frame.h" 34 #include "FrameView.h" 35 #include "HostWindow.h" 36 #include "KeyboardEvent.h" 37 #include "KeyboardCodes.h" 38 #include "NotImplemented.h" 39 #include "Page.h" 40 #include "PlatformKeyboardEvent.h" 41 #include "PlatformString.h" 42 #include "SelectionController.h" 43 44 #include "WebFrame.h" 45 #include "WebFramePrivate.h" 46 #include "WebView.h" 47 #include "WebViewPrivate.h" 48 49 #include <stdio.h> 50 51 namespace WebCore { 52 53 static const unsigned CtrlKey = 1 << 0; 54 static const unsigned AltKey = 1 << 1; 55 static const unsigned ShiftKey = 1 << 2; 56 57 struct KeyDownEntry { 58 unsigned virtualKey; 59 unsigned modifiers; 60 const char* name; 61 }; 62 63 struct KeyPressEntry { 64 unsigned charCode; 65 unsigned modifiers; 66 const char* name; 67 }; 68 69 static const KeyDownEntry keyDownEntries[] = { 70 { VK_LEFT, 0, "MoveLeft" }, 71 { VK_LEFT, ShiftKey, "MoveLeftAndModifySelection" }, 72 { VK_LEFT, CtrlKey, "MoveWordLeft" }, 73 { VK_LEFT, CtrlKey | ShiftKey, "MoveWordLeftAndModifySelection" }, 74 { VK_RIGHT, 0, "MoveRight" }, 75 { VK_RIGHT, ShiftKey, "MoveRightAndModifySelection" }, 76 { VK_RIGHT, CtrlKey, "MoveWordRight" }, 77 { VK_RIGHT, CtrlKey | ShiftKey, "MoveWordRightAndModifySelection" }, 78 { VK_UP, 0, "MoveUp" }, 79 { VK_UP, ShiftKey, "MoveUpAndModifySelection" }, 80 { VK_PRIOR, ShiftKey, "MovePageUpAndModifySelection" }, 81 { VK_DOWN, 0, "MoveDown" }, 82 { VK_DOWN, ShiftKey, "MoveDownAndModifySelection" }, 83 { VK_NEXT, ShiftKey, "MovePageDownAndModifySelection" }, 84 { VK_PRIOR, 0, "MovePageUp" }, 85 { VK_NEXT, 0, "MovePageDown" }, 86 { VK_HOME, 0, "MoveToBeginningOfLine" }, 87 { VK_HOME, ShiftKey, "MoveToBeginningOfLineAndModifySelection" }, 88 { VK_HOME, CtrlKey, "MoveToBeginningOfDocument" }, 89 { VK_HOME, CtrlKey | ShiftKey, "MoveToBeginningOfDocumentAndModifySelection" }, 90 91 { VK_END, 0, "MoveToEndOfLine" }, 92 { VK_END, ShiftKey, "MoveToEndOfLineAndModifySelection" }, 93 { VK_END, CtrlKey, "MoveToEndOfDocument" }, 94 { VK_END, CtrlKey | ShiftKey, "MoveToEndOfDocumentAndModifySelection" }, 95 96 { VK_BACK, 0, "DeleteBackward" }, 97 { VK_BACK, ShiftKey, "DeleteBackward" }, 98 { VK_DELETE, 0, "DeleteForward" }, 99 { VK_BACK, CtrlKey, "DeleteWordBackward" }, 100 { VK_DELETE, CtrlKey, "DeleteWordForward" }, 101 102 { 'B', CtrlKey, "ToggleBold" }, 103 { 'I', CtrlKey, "ToggleItalic" }, 104 105 { VK_ESCAPE, 0, "Cancel" }, 106 //FIXME: this'll never happen. We can trash it or make it a normal period 107 { VK_OEM_PERIOD, CtrlKey, "Cancel" }, 108 { VK_TAB, 0, "InsertTab" }, 109 { VK_TAB, ShiftKey, "InsertBacktab" }, 110 { VK_RETURN, 0, "InsertNewline" }, 111 { VK_RETURN, CtrlKey, "InsertNewline" }, 112 { VK_RETURN, AltKey, "InsertNewline" }, 113 { VK_RETURN, AltKey | ShiftKey, "InsertNewline" }, 114 { 'A', CtrlKey, "SelectAll" }, 115 { 'Z', CtrlKey, "Undo" }, 116 { 'Z', CtrlKey | ShiftKey, "Redo" }, 117 }; 118 119 static const KeyPressEntry keyPressEntries[] = { 120 { '\t', 0, "InsertTab" }, 121 { '\t', ShiftKey, "InsertBacktab" }, 122 { '\r', 0, "InsertNewline" }, 123 { '\r', CtrlKey, "InsertNewline" }, 124 { '\r', AltKey, "InsertNewline" }, 125 { '\r', AltKey | ShiftKey, "InsertNewline" }, 126 }; 127 128 EditorClientWx::~EditorClientWx() 129 { 130 m_page = NULL; 131 } 132 133 void EditorClientWx::setPage(Page* page) 134 { 135 m_page = page; 136 } 137 138 void EditorClientWx::pageDestroyed() 139 { 140 delete this; 141 } 142 143 bool EditorClientWx::shouldDeleteRange(Range*) 144 { 145 notImplemented(); 146 return true; 147 } 148 149 bool EditorClientWx::shouldShowDeleteInterface(HTMLElement*) 150 { 151 notImplemented(); 152 return false; 153 } 154 155 bool EditorClientWx::smartInsertDeleteEnabled() 156 { 157 notImplemented(); 158 return false; 159 } 160 161 bool EditorClientWx::isSelectTrailingWhitespaceEnabled() 162 { 163 notImplemented(); 164 return false; 165 } 166 167 bool EditorClientWx::isContinuousSpellCheckingEnabled() 168 { 169 notImplemented(); 170 return false; 171 } 172 173 void EditorClientWx::toggleContinuousSpellChecking() 174 { 175 notImplemented(); 176 } 177 178 bool EditorClientWx::isGrammarCheckingEnabled() 179 { 180 notImplemented(); 181 return false; 182 } 183 184 void EditorClientWx::toggleGrammarChecking() 185 { 186 notImplemented(); 187 } 188 189 int EditorClientWx::spellCheckerDocumentTag() 190 { 191 notImplemented(); 192 return 0; 193 } 194 195 bool EditorClientWx::selectWordBeforeMenuEvent() 196 { 197 notImplemented(); 198 return false; 199 } 200 201 bool EditorClientWx::isEditable() 202 { 203 Frame* frame = m_page->focusController()->focusedOrMainFrame(); 204 205 if (frame) { 206 wxWebView* webKitWin = dynamic_cast<wxWebView*>(frame->view()->hostWindow()->platformPageClient()); 207 if (webKitWin) 208 return webKitWin->IsEditable(); 209 } 210 return false; 211 } 212 213 bool EditorClientWx::shouldBeginEditing(Range*) 214 { 215 notImplemented(); 216 return true; 217 } 218 219 bool EditorClientWx::shouldEndEditing(Range*) 220 { 221 notImplemented(); 222 return true; 223 } 224 225 bool EditorClientWx::shouldInsertNode(Node*, Range*, 226 EditorInsertAction) 227 { 228 notImplemented(); 229 return true; 230 } 231 232 bool EditorClientWx::shouldInsertText(const String&, Range*, 233 EditorInsertAction) 234 { 235 notImplemented(); 236 return true; 237 } 238 239 bool EditorClientWx::shouldApplyStyle(CSSStyleDeclaration*, 240 Range*) 241 { 242 notImplemented(); 243 return true; 244 } 245 246 bool EditorClientWx::shouldMoveRangeAfterDelete(Range*, Range*) 247 { 248 notImplemented(); 249 return true; 250 } 251 252 bool EditorClientWx::shouldChangeSelectedRange(Range* fromRange, Range* toRange, 253 EAffinity, bool stillSelecting) 254 { 255 notImplemented(); 256 return true; 257 } 258 259 void EditorClientWx::didBeginEditing() 260 { 261 notImplemented(); 262 } 263 264 void EditorClientWx::respondToChangedContents() 265 { 266 notImplemented(); 267 } 268 269 void EditorClientWx::didEndEditing() 270 { 271 notImplemented(); 272 } 273 274 void EditorClientWx::didWriteSelectionToPasteboard() 275 { 276 notImplemented(); 277 } 278 279 void EditorClientWx::didSetSelectionTypesForPasteboard() 280 { 281 notImplemented(); 282 } 283 284 void EditorClientWx::registerCommandForUndo(PassRefPtr<EditCommand> command) 285 { 286 Frame* frame = m_page->focusController()->focusedOrMainFrame(); 287 288 if (frame) { 289 wxWebView* webKitWin = dynamic_cast<wxWebView*>(frame->view()->hostWindow()->platformPageClient()); 290 if (webKitWin) { 291 webKitWin->m_impl->undoStack.append(EditCommandWx(command)); 292 } 293 } 294 } 295 296 void EditorClientWx::registerCommandForRedo(PassRefPtr<EditCommand> command) 297 { 298 Frame* frame = m_page->focusController()->focusedOrMainFrame(); 299 300 if (frame) { 301 wxWebView* webKitWin = dynamic_cast<wxWebView*>(frame->view()->hostWindow()->platformPageClient()); 302 if (webKitWin) { 303 webKitWin->m_impl->redoStack.insert(0, EditCommandWx(command)); 304 } 305 } 306 } 307 308 void EditorClientWx::clearUndoRedoOperations() 309 { 310 Frame* frame = m_page->focusController()->focusedOrMainFrame(); 311 312 if (frame) { 313 wxWebView* webKitWin = dynamic_cast<wxWebView*>(frame->view()->hostWindow()->platformPageClient()); 314 if (webKitWin) { 315 webKitWin->m_impl->redoStack.clear(); 316 webKitWin->m_impl->undoStack.clear(); 317 } 318 } 319 } 320 321 bool EditorClientWx::canUndo() const 322 { 323 Frame* frame = m_page->focusController()->focusedOrMainFrame(); 324 325 if (frame) { 326 wxWebView* webKitWin = dynamic_cast<wxWebView*>(frame->view()->hostWindow()->platformPageClient()); 327 if (webKitWin) { 328 return webKitWin->m_impl->undoStack.size() != 0; 329 } 330 } 331 return false; 332 } 333 334 bool EditorClientWx::canRedo() const 335 { 336 Frame* frame = m_page->focusController()->focusedOrMainFrame(); 337 338 if (frame) { 339 wxWebView* webKitWin = dynamic_cast<wxWebView*>(frame->view()->hostWindow()->platformPageClient()); 340 if (webKitWin && webKitWin) { 341 return webKitWin->m_impl->redoStack.size() != 0; 342 } 343 } 344 return false; 345 } 346 347 void EditorClientWx::undo() 348 { 349 Frame* frame = m_page->focusController()->focusedOrMainFrame(); 350 351 if (frame) { 352 wxWebView* webKitWin = dynamic_cast<wxWebView*>(frame->view()->hostWindow()->platformPageClient()); 353 if (webKitWin) { 354 webKitWin->m_impl->undoStack.last().editCommand()->unapply(); 355 webKitWin->m_impl->undoStack.removeLast(); 356 } 357 } 358 } 359 360 void EditorClientWx::redo() 361 { 362 Frame* frame = m_page->focusController()->focusedOrMainFrame(); 363 364 if (frame) { 365 wxWebView* webKitWin = dynamic_cast<wxWebView*>(frame->view()->hostWindow()->platformPageClient()); 366 if (webKitWin) { 367 webKitWin->m_impl->redoStack.first().editCommand()->reapply(); 368 webKitWin->m_impl->redoStack.remove(0); 369 } 370 } 371 } 372 373 bool EditorClientWx::handleEditingKeyboardEvent(KeyboardEvent* event) 374 { 375 Node* node = event->target()->toNode(); 376 ASSERT(node); 377 Frame* frame = node->document()->frame(); 378 ASSERT(frame); 379 380 const PlatformKeyboardEvent* keyEvent = event->keyEvent(); 381 382 //NB: this is what windows does, but they also have a keypress event for Alt+Enter which clearly won't get hit with this 383 if (!keyEvent || keyEvent->altKey()) // do not treat this as text input if Alt is down 384 return false; 385 386 Editor::Command command = frame->editor()->command(interpretKeyEvent(event)); 387 388 if (keyEvent->type() == PlatformKeyboardEvent::RawKeyDown) { 389 // WebKit doesn't have enough information about mode to decide how commands that just insert text if executed via Editor should be treated, 390 // so we leave it upon WebCore to either handle them immediately (e.g. Tab that changes focus) or if not to let a CHAR event be generated 391 // (e.g. Tab that inserts a Tab character, or Enter). 392 return !command.isTextInsertion() && command.execute(event); 393 } 394 395 if (command.execute(event)) 396 return true; 397 398 // Don't insert null or control characters as they can result in unexpected behaviour 399 if (event->charCode() < ' ') 400 return false; 401 402 return frame->editor()->insertText(event->keyEvent()->text(), event); 403 } 404 405 const char* EditorClientWx::interpretKeyEvent(const KeyboardEvent* evt) 406 { 407 ASSERT(evt->keyEvent()->type() == PlatformKeyboardEvent::RawKeyDown || evt->keyEvent()->type() == PlatformKeyboardEvent::Char); 408 409 static HashMap<int, const char*>* keyDownCommandsMap = 0; 410 static HashMap<int, const char*>* keyPressCommandsMap = 0; 411 412 if (!keyDownCommandsMap) { 413 keyDownCommandsMap = new HashMap<int, const char*>; 414 keyPressCommandsMap = new HashMap<int, const char*>; 415 416 for (unsigned i = 0; i < WXSIZEOF(keyDownEntries); i++) 417 keyDownCommandsMap->set(keyDownEntries[i].modifiers << 16 | keyDownEntries[i].virtualKey, keyDownEntries[i].name); 418 419 for (unsigned i = 0; i < WXSIZEOF(keyPressEntries); i++) 420 keyPressCommandsMap->set(keyPressEntries[i].modifiers << 16 | keyPressEntries[i].charCode, keyPressEntries[i].name); 421 } 422 423 unsigned modifiers = 0; 424 if (evt->shiftKey()) 425 modifiers |= ShiftKey; 426 if (evt->altKey()) 427 modifiers |= AltKey; 428 if (evt->ctrlKey()) 429 modifiers |= CtrlKey; 430 431 if (evt->keyEvent()->type() == PlatformKeyboardEvent::RawKeyDown) { 432 int mapKey = modifiers << 16 | evt->keyCode(); 433 return mapKey ? keyDownCommandsMap->get(mapKey) : 0; 434 } 435 436 int mapKey = modifiers << 16 | evt->charCode(); 437 return mapKey ? keyPressCommandsMap->get(mapKey) : 0; 438 } 439 440 441 void EditorClientWx::handleInputMethodKeydown(KeyboardEvent* event) 442 { 443 // NOTE: we don't currently need to handle this. When key events occur, 444 // both this method and handleKeyboardEvent get a chance at handling them. 445 // We might use this method later on for IME-specific handling. 446 } 447 448 void EditorClientWx::handleKeyboardEvent(KeyboardEvent* event) 449 { 450 if (handleEditingKeyboardEvent(event)) 451 event->setDefaultHandled(); 452 } 453 454 void EditorClientWx::textFieldDidBeginEditing(Element*) 455 { 456 notImplemented(); 457 } 458 459 void EditorClientWx::textFieldDidEndEditing(Element*) 460 { 461 notImplemented(); 462 } 463 464 void EditorClientWx::textDidChangeInTextField(Element*) 465 { 466 notImplemented(); 467 } 468 469 bool EditorClientWx::doTextFieldCommandFromEvent(Element*, KeyboardEvent*) 470 { 471 notImplemented(); 472 return false; 473 } 474 475 void EditorClientWx::textWillBeDeletedInTextField(Element*) 476 { 477 notImplemented(); 478 } 479 480 void EditorClientWx::textDidChangeInTextArea(Element*) 481 { 482 notImplemented(); 483 } 484 485 void EditorClientWx::respondToChangedSelection() 486 { 487 notImplemented(); 488 } 489 490 void EditorClientWx::ignoreWordInSpellDocument(const String&) 491 { 492 notImplemented(); 493 } 494 495 void EditorClientWx::learnWord(const String&) 496 { 497 notImplemented(); 498 } 499 500 void EditorClientWx::checkSpellingOfString(const UChar*, int length, int* misspellingLocation, int* misspellingLength) 501 { 502 notImplemented(); 503 } 504 505 void EditorClientWx::checkGrammarOfString(const UChar*, int length, Vector<GrammarDetail>&, int* badGrammarLocation, int* badGrammarLength) 506 { 507 notImplemented(); 508 } 509 510 void EditorClientWx::updateSpellingUIWithGrammarString(const String&, const GrammarDetail& detail) 511 { 512 notImplemented(); 513 } 514 515 void EditorClientWx::updateSpellingUIWithMisspelledWord(const String&) 516 { 517 notImplemented(); 518 } 519 520 void EditorClientWx::showSpellingUI(bool show) 521 { 522 notImplemented(); 523 } 524 525 bool EditorClientWx::spellingUIIsShowing() 526 { 527 notImplemented(); 528 return false; 529 } 530 531 void EditorClientWx::getGuessesForWord(const String&, Vector<String>& guesses) 532 { 533 notImplemented(); 534 } 535 536 String EditorClientWx::getAutoCorrectSuggestionForMisspelledWord(const WebCore::String&) 537 { 538 notImplemented(); 539 return String(); 540 } 541 542 void EditorClientWx::setInputMethodState(bool enabled) 543 { 544 notImplemented(); 545 } 546 547 } 548