diff options
Diffstat (limited to 'WebCore/editing/TypingCommand.cpp')
-rw-r--r-- | WebCore/editing/TypingCommand.cpp | 535 |
1 files changed, 0 insertions, 535 deletions
diff --git a/WebCore/editing/TypingCommand.cpp b/WebCore/editing/TypingCommand.cpp deleted file mode 100644 index ef144a1..0000000 --- a/WebCore/editing/TypingCommand.cpp +++ /dev/null @@ -1,535 +0,0 @@ -/* - * Copyright (C) 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "TypingCommand.h" - -#include "BeforeTextInsertedEvent.h" -#include "BreakBlockquoteCommand.h" -#include "DeleteSelectionCommand.h" -#include "Document.h" -#include "Editor.h" -#include "Element.h" -#include "Frame.h" -#include "InsertLineBreakCommand.h" -#include "InsertParagraphSeparatorCommand.h" -#include "InsertTextCommand.h" -#include "SelectionController.h" -#include "VisiblePosition.h" -#include "htmlediting.h" -#include "visible_units.h" - -namespace WebCore { - -TypingCommand::TypingCommand(Document *document, ETypingCommand commandType, const String &textToInsert, bool selectInsertedText, TextGranularity granularity) - : CompositeEditCommand(document), - m_commandType(commandType), - m_textToInsert(textToInsert), - m_openForMoreTyping(true), - m_applyEditing(false), - m_selectInsertedText(selectInsertedText), - m_smartDelete(false), - m_granularity(granularity), - m_openedByBackwardDelete(false) -{ -} - -void TypingCommand::deleteSelection(Document* document, bool smartDelete) -{ - ASSERT(document); - - Frame* frame = document->frame(); - ASSERT(frame); - - if (!frame->selection()->isRange()) - return; - - EditCommand* lastEditCommand = frame->editor()->lastEditCommand(); - if (isOpenForMoreTypingCommand(lastEditCommand)) { - static_cast<TypingCommand*>(lastEditCommand)->deleteSelection(smartDelete); - return; - } - - RefPtr<TypingCommand> typingCommand = TypingCommand::create(document, DeleteSelection, "", false); - typingCommand->setSmartDelete(smartDelete); - typingCommand->apply(); -} - -void TypingCommand::deleteKeyPressed(Document *document, bool smartDelete, TextGranularity granularity) -{ - ASSERT(document); - - Frame *frame = document->frame(); - ASSERT(frame); - - EditCommand* lastEditCommand = frame->editor()->lastEditCommand(); - if (isOpenForMoreTypingCommand(lastEditCommand)) { - static_cast<TypingCommand*>(lastEditCommand)->deleteKeyPressed(granularity); - return; - } - - RefPtr<TypingCommand> typingCommand = TypingCommand::create(document, DeleteKey, "", false, granularity); - typingCommand->setSmartDelete(smartDelete); - typingCommand->apply(); -} - -void TypingCommand::forwardDeleteKeyPressed(Document *document, bool smartDelete, TextGranularity granularity) -{ - // FIXME: Forward delete in TextEdit appears to open and close a new typing command. - ASSERT(document); - - Frame *frame = document->frame(); - ASSERT(frame); - - EditCommand* lastEditCommand = frame->editor()->lastEditCommand(); - if (isOpenForMoreTypingCommand(lastEditCommand)) { - static_cast<TypingCommand*>(lastEditCommand)->forwardDeleteKeyPressed(granularity); - return; - } - - RefPtr<TypingCommand> typingCommand = TypingCommand::create(document, ForwardDeleteKey, "", false, granularity); - typingCommand->setSmartDelete(smartDelete); - typingCommand->apply(); -} - -void TypingCommand::insertText(Document* document, const String& text, bool selectInsertedText, bool insertedTextIsComposition) -{ - ASSERT(document); - - Frame* frame = document->frame(); - ASSERT(frame); - - insertText(document, text, frame->selection()->selection(), selectInsertedText, insertedTextIsComposition); -} - -void TypingCommand::insertText(Document* document, const String& text, const Selection& selectionForInsertion, bool selectInsertedText, bool insertedTextIsComposition) -{ - ASSERT(document); - - RefPtr<Frame> frame = document->frame(); - ASSERT(frame); - - Selection currentSelection = frame->selection()->selection(); - bool changeSelection = currentSelection != selectionForInsertion; - - String newText = text; - Node* startNode = selectionForInsertion.start().node(); - - if (startNode && startNode->rootEditableElement() && !insertedTextIsComposition) { - // Send BeforeTextInsertedEvent. The event handler will update text if necessary. - ExceptionCode ec = 0; - RefPtr<BeforeTextInsertedEvent> evt = BeforeTextInsertedEvent::create(text); - startNode->rootEditableElement()->dispatchEvent(evt, ec); - newText = evt->text(); - } - - if (newText.isEmpty()) - return; - - // Set the starting and ending selection appropriately if we are using a selection - // that is different from the current selection. In the future, we should change EditCommand - // to deal with custom selections in a general way that can be used by all of the commands. - RefPtr<EditCommand> lastEditCommand = frame->editor()->lastEditCommand(); - if (isOpenForMoreTypingCommand(lastEditCommand.get())) { - TypingCommand* lastTypingCommand = static_cast<TypingCommand*>(lastEditCommand.get()); - if (changeSelection) { - lastTypingCommand->setStartingSelection(selectionForInsertion); - lastTypingCommand->setEndingSelection(selectionForInsertion); - } - lastTypingCommand->insertText(newText, selectInsertedText); - if (changeSelection) { - lastTypingCommand->setEndingSelection(currentSelection); - frame->selection()->setSelection(currentSelection); - } - return; - } - - RefPtr<TypingCommand> cmd = TypingCommand::create(document, InsertText, newText, selectInsertedText); - if (changeSelection) { - cmd->setStartingSelection(selectionForInsertion); - cmd->setEndingSelection(selectionForInsertion); - } - applyCommand(cmd); - if (changeSelection) { - cmd->setEndingSelection(currentSelection); - frame->selection()->setSelection(currentSelection); - } -} - -void TypingCommand::insertLineBreak(Document *document) -{ - ASSERT(document); - - Frame *frame = document->frame(); - ASSERT(frame); - - EditCommand* lastEditCommand = frame->editor()->lastEditCommand(); - if (isOpenForMoreTypingCommand(lastEditCommand)) { - static_cast<TypingCommand*>(lastEditCommand)->insertLineBreak(); - return; - } - - applyCommand(TypingCommand::create(document, InsertLineBreak)); -} - -void TypingCommand::insertParagraphSeparatorInQuotedContent(Document *document) -{ - ASSERT(document); - - Frame *frame = document->frame(); - ASSERT(frame); - - EditCommand* lastEditCommand = frame->editor()->lastEditCommand(); - if (isOpenForMoreTypingCommand(lastEditCommand)) { - static_cast<TypingCommand*>(lastEditCommand)->insertParagraphSeparatorInQuotedContent(); - return; - } - - applyCommand(TypingCommand::create(document, InsertParagraphSeparatorInQuotedContent)); -} - -void TypingCommand::insertParagraphSeparator(Document *document) -{ - ASSERT(document); - - Frame *frame = document->frame(); - ASSERT(frame); - - EditCommand* lastEditCommand = frame->editor()->lastEditCommand(); - if (isOpenForMoreTypingCommand(lastEditCommand)) { - static_cast<TypingCommand*>(lastEditCommand)->insertParagraphSeparator(); - return; - } - - applyCommand(TypingCommand::create(document, InsertParagraphSeparator)); -} - -bool TypingCommand::isOpenForMoreTypingCommand(const EditCommand* cmd) -{ - return cmd && cmd->isTypingCommand() && static_cast<const TypingCommand*>(cmd)->isOpenForMoreTyping(); -} - -void TypingCommand::closeTyping(EditCommand* cmd) -{ - if (isOpenForMoreTypingCommand(cmd)) - static_cast<TypingCommand*>(cmd)->closeTyping(); -} - -void TypingCommand::doApply() -{ - if (endingSelection().isNone()) - return; - - if (m_commandType == DeleteKey) - if (m_commands.isEmpty()) - m_openedByBackwardDelete = true; - - switch (m_commandType) { - case DeleteSelection: - deleteSelection(m_smartDelete); - return; - case DeleteKey: - deleteKeyPressed(m_granularity); - return; - case ForwardDeleteKey: - forwardDeleteKeyPressed(m_granularity); - return; - case InsertLineBreak: - insertLineBreak(); - return; - case InsertParagraphSeparator: - insertParagraphSeparator(); - return; - case InsertParagraphSeparatorInQuotedContent: - insertParagraphSeparatorInQuotedContent(); - return; - case InsertText: - insertText(m_textToInsert, m_selectInsertedText); - return; - } - - ASSERT_NOT_REACHED(); -} - -EditAction TypingCommand::editingAction() const -{ - return EditActionTyping; -} - -void TypingCommand::markMisspellingsAfterTyping() -{ - if (!document()->frame()->editor()->isContinuousSpellCheckingEnabled()) - return; - // Take a look at the selection that results after typing and determine whether we need to spellcheck. - // Since the word containing the current selection is never marked, this does a check to - // see if typing made a new word that is not in the current selection. Basically, you - // get this by being at the end of a word and typing a space. - VisiblePosition start(endingSelection().start(), endingSelection().affinity()); - VisiblePosition previous = start.previous(); - if (previous.isNotNull()) { - VisiblePosition p1 = startOfWord(previous, LeftWordIfOnBoundary); - VisiblePosition p2 = startOfWord(start, LeftWordIfOnBoundary); - if (p1 != p2) - document()->frame()->editor()->markMisspellingsAfterTypingToPosition(p1); - } -} - -void TypingCommand::typingAddedToOpenCommand() -{ - markMisspellingsAfterTyping(); - // Do not apply editing to the frame on the first time through. - // The frame will get told in the same way as all other commands. - // But since this command stays open and is used for additional typing, - // we need to tell the frame here as other commands are added. - if (m_applyEditing) - document()->frame()->editor()->appliedEditing(this); - m_applyEditing = true; -} - -void TypingCommand::insertText(const String &text, bool selectInsertedText) -{ - // FIXME: Need to implement selectInsertedText for cases where more than one insert is involved. - // This requires support from insertTextRunWithoutNewlines and insertParagraphSeparator for extending - // an existing selection; at the moment they can either put the caret after what's inserted or - // select what's inserted, but there's no way to "extend selection" to include both an old selection - // that ends just before where we want to insert text and the newly inserted text. - int offset = 0; - int newline; - while ((newline = text.find('\n', offset)) != -1) { - if (newline != offset) - insertTextRunWithoutNewlines(text.substring(offset, newline - offset), false); - insertParagraphSeparator(); - offset = newline + 1; - } - if (offset == 0) - insertTextRunWithoutNewlines(text, selectInsertedText); - else { - int length = text.length(); - if (length != offset) { - insertTextRunWithoutNewlines(text.substring(offset, length - offset), selectInsertedText); - } - } -} - -void TypingCommand::insertTextRunWithoutNewlines(const String &text, bool selectInsertedText) -{ - RefPtr<InsertTextCommand> command; - if (!document()->frame()->typingStyle() && !m_commands.isEmpty()) { - EditCommand* lastCommand = m_commands.last().get(); - if (lastCommand->isInsertTextCommand()) - command = static_cast<InsertTextCommand*>(lastCommand); - } - if (!command) { - command = InsertTextCommand::create(document()); - applyCommandToComposite(command); - } - command->input(text, selectInsertedText); - typingAddedToOpenCommand(); -} - -void TypingCommand::insertLineBreak() -{ - applyCommandToComposite(InsertLineBreakCommand::create(document())); - typingAddedToOpenCommand(); -} - -void TypingCommand::insertParagraphSeparator() -{ - applyCommandToComposite(InsertParagraphSeparatorCommand::create(document())); - typingAddedToOpenCommand(); -} - -void TypingCommand::insertParagraphSeparatorInQuotedContent() -{ - applyCommandToComposite(BreakBlockquoteCommand::create(document())); - typingAddedToOpenCommand(); -} - -void TypingCommand::deleteKeyPressed(TextGranularity granularity) -{ - Selection selectionToDelete; - Selection selectionAfterUndo; - - switch (endingSelection().state()) { - case Selection::RANGE: - selectionToDelete = endingSelection(); - selectionAfterUndo = selectionToDelete; - break; - case Selection::CARET: { - m_smartDelete = false; - - SelectionController selection; - selection.setSelection(endingSelection()); - selection.modify(SelectionController::EXTEND, SelectionController::BACKWARD, granularity); - - // When the caret is at the start of the editable area in an empty list item, break out of the list item. - if (endingSelection().visibleStart().previous(true).isNull()) { - if (breakOutOfEmptyListItem()) { - typingAddedToOpenCommand(); - return; - } - } - - VisiblePosition visibleStart(endingSelection().visibleStart()); - // If the caret is at the start of a paragraph after a table, move content into the last table cell. - if (isStartOfParagraph(visibleStart) && isFirstPositionAfterTable(visibleStart.previous(true))) { - // Unless the caret is just before a table. We don't want to move a table into the last table cell. - if (isLastPositionBeforeTable(visibleStart)) - return; - // Extend the selection backward into the last cell, then deletion will handle the move. - selection.modify(SelectionController::EXTEND, SelectionController::BACKWARD, granularity); - // If the caret is just after a table, select the table and don't delete anything. - } else if (Node* table = isFirstPositionAfterTable(visibleStart)) { - setEndingSelection(Selection(Position(table, 0), endingSelection().start(), DOWNSTREAM)); - typingAddedToOpenCommand(); - return; - } - - selectionToDelete = selection.selection(); - if (!startingSelection().isRange() || selectionToDelete.base() != startingSelection().start()) - selectionAfterUndo = selectionToDelete; - else - // It's a little tricky to compute what the starting selection would have been in the original document. - // We can't let the Selection class's validation kick in or it'll adjust for us based on - // the current state of the document and we'll get the wrong result. - selectionAfterUndo.setWithoutValidation(startingSelection().end(), selectionToDelete.extent()); - break; - } - case Selection::NONE: - ASSERT_NOT_REACHED(); - break; - } - - if (selectionToDelete.isCaretOrRange() && document()->frame()->shouldDeleteSelection(selectionToDelete)) { - // Make undo select everything that has been deleted, unless an undo will undo more than just this deletion. - // FIXME: This behaves like TextEdit except for the case where you open with text insertion and then delete - // more text than you insert. In that case all of the text that was around originally should be selected. - if (m_openedByBackwardDelete) - setStartingSelection(selectionAfterUndo); - CompositeEditCommand::deleteSelection(selectionToDelete, m_smartDelete); - setSmartDelete(false); - typingAddedToOpenCommand(); - } -} - -void TypingCommand::forwardDeleteKeyPressed(TextGranularity granularity) -{ - Selection selectionToDelete; - Selection selectionAfterUndo; - - switch (endingSelection().state()) { - case Selection::RANGE: - selectionToDelete = endingSelection(); - selectionAfterUndo = selectionToDelete; - break; - case Selection::CARET: { - m_smartDelete = false; - - // Handle delete at beginning-of-block case. - // Do nothing in the case that the caret is at the start of a - // root editable element or at the start of a document. - SelectionController selection; - selection.setSelection(endingSelection()); - selection.modify(SelectionController::EXTEND, SelectionController::FORWARD, granularity); - Position downstreamEnd = endingSelection().end().downstream(); - VisiblePosition visibleEnd = endingSelection().visibleEnd(); - if (visibleEnd == endOfParagraph(visibleEnd)) - downstreamEnd = visibleEnd.next(true).deepEquivalent().downstream(); - // When deleting tables: Select the table first, then perform the deletion - if (downstreamEnd.node() && downstreamEnd.node()->renderer() && downstreamEnd.node()->renderer()->isTable() && downstreamEnd.offset() == 0) { - setEndingSelection(Selection(endingSelection().end(), Position(downstreamEnd.node(), maxDeepOffset(downstreamEnd.node())), DOWNSTREAM)); - typingAddedToOpenCommand(); - return; - } - - // deleting to end of paragraph when at end of paragraph needs to merge the next paragraph (if any) - if (granularity == ParagraphBoundary && selection.selection().isCaret() && isEndOfParagraph(selection.selection().visibleEnd())) - selection.modify(SelectionController::EXTEND, SelectionController::FORWARD, CharacterGranularity); - - selectionToDelete = selection.selection(); - if (!startingSelection().isRange() || selectionToDelete.base() != startingSelection().start()) - selectionAfterUndo = selectionToDelete; - else { - // It's a little tricky to compute what the starting selection would have been in the original document. - // We can't let the Selection class's validation kick in or it'll adjust for us based on - // the current state of the document and we'll get the wrong result. - Position extent = startingSelection().end(); - if (extent.node() != selectionToDelete.end().node()) - extent = selectionToDelete.extent(); - else { - int extraCharacters; - if (selectionToDelete.start().node() == selectionToDelete.end().node()) - extraCharacters = selectionToDelete.end().offset() - selectionToDelete.start().offset(); - else - extraCharacters = selectionToDelete.end().offset(); - extent = Position(extent.node(), extent.offset() + extraCharacters); - } - selectionAfterUndo.setWithoutValidation(startingSelection().start(), extent); - } - break; - } - case Selection::NONE: - ASSERT_NOT_REACHED(); - break; - } - - if (selectionToDelete.isCaretOrRange() && document()->frame()->shouldDeleteSelection(selectionToDelete)) { - // make undo select what was deleted - setStartingSelection(selectionAfterUndo); - CompositeEditCommand::deleteSelection(selectionToDelete, m_smartDelete); - setSmartDelete(false); - typingAddedToOpenCommand(); - } -} - -void TypingCommand::deleteSelection(bool smartDelete) -{ - CompositeEditCommand::deleteSelection(smartDelete); - typingAddedToOpenCommand(); -} - -bool TypingCommand::preservesTypingStyle() const -{ - switch (m_commandType) { - case DeleteSelection: - case DeleteKey: - case ForwardDeleteKey: - case InsertParagraphSeparator: - case InsertLineBreak: - return true; - case InsertParagraphSeparatorInQuotedContent: - case InsertText: - return false; - } - ASSERT_NOT_REACHED(); - return false; -} - -bool TypingCommand::isTypingCommand() const -{ - return true; -} - -} // namespace WebCore |