1/* 2 * Copyright (C) 2012 Apple Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR 17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26#include "config.h" 27#include "DictationCommand.h" 28 29#include "AlternativeTextController.h" 30#include "Document.h" 31#include "DocumentMarkerController.h" 32#include "Frame.h" 33#include "InsertParagraphSeparatorCommand.h" 34#include "InsertTextCommand.h" 35#include "Text.h" 36 37namespace WebCore { 38 39class DictationCommandLineOperation { 40public: 41 DictationCommandLineOperation(DictationCommand* dictationCommand) 42 : m_dictationCommand(dictationCommand) 43 { } 44 45 void operator()(size_t lineOffset, size_t lineLength, bool isLastLine) const 46 { 47 if (lineLength > 0) 48 m_dictationCommand->insertTextRunWithoutNewlines(lineOffset, lineLength); 49 if (!isLastLine) 50 m_dictationCommand->insertParagraphSeparator(); 51 } 52private: 53 DictationCommand* m_dictationCommand; 54}; 55 56class DictationMarkerSupplier : public TextInsertionMarkerSupplier { 57public: 58 static PassRefPtr<DictationMarkerSupplier> create(const Vector<DictationAlternative>& alternatives) 59 { 60 return adoptRef(new DictationMarkerSupplier(alternatives)); 61 } 62 63 virtual void addMarkersToTextNode(Text* textNode, unsigned offsetOfInsertion, const String& textToBeInserted) 64 { 65 DocumentMarkerController& markerController = textNode->document().markers(); 66 for (size_t i = 0; i < m_alternatives.size(); ++i) { 67 const DictationAlternative& alternative = m_alternatives[i]; 68 markerController.addMarkerToNode(textNode, alternative.rangeStart + offsetOfInsertion, alternative.rangeLength, DocumentMarker::DictationAlternatives, DictationMarkerDetails::create(textToBeInserted.substring(alternative.rangeStart, alternative.rangeLength), alternative.dictationContext)); 69 markerController.addMarkerToNode(textNode, alternative.rangeStart + offsetOfInsertion, alternative.rangeLength, DocumentMarker::SpellCheckingExemption); 70 } 71 } 72 73protected: 74 DictationMarkerSupplier(const Vector<DictationAlternative>& alternatives) 75 : m_alternatives(alternatives) 76 { 77 } 78private: 79 Vector<DictationAlternative> m_alternatives; 80}; 81 82DictationCommand::DictationCommand(Document& document, const String& text, const Vector<DictationAlternative>& alternatives) 83 : TextInsertionBaseCommand(document) 84 , m_textToInsert(text) 85 , m_alternatives(alternatives) 86{ 87} 88 89void DictationCommand::insertText(Document* document, const String& text, const Vector<DictationAlternative>& alternatives, const VisibleSelection& selectionForInsertion) 90{ 91 RefPtr<Frame> frame = document->frame(); 92 ASSERT(frame); 93 94 VisibleSelection currentSelection = frame->selection().selection(); 95 96 String newText = dispatchBeforeTextInsertedEvent(text, selectionForInsertion, false); 97 98 RefPtr<DictationCommand> cmd; 99 if (newText == text) 100 cmd = DictationCommand::create(*document, newText, alternatives); 101 else 102 // If the text was modified before insertion, the location of dictation alternatives 103 // will not be valid anymore. We will just drop the alternatives. 104 cmd = DictationCommand::create(*document, newText, Vector<DictationAlternative>()); 105 applyTextInsertionCommand(frame.get(), cmd, selectionForInsertion, currentSelection); 106} 107 108void DictationCommand::doApply() 109{ 110 DictationCommandLineOperation operation(this); 111 forEachLineInString(m_textToInsert, operation); 112} 113 114void DictationCommand::insertTextRunWithoutNewlines(size_t lineStart, size_t lineLength) 115{ 116 Vector<DictationAlternative> alternativesInLine; 117 collectDictationAlternativesInRange(lineStart, lineLength, alternativesInLine); 118 RefPtr<InsertTextCommand> command = InsertTextCommand::createWithMarkerSupplier(document(), m_textToInsert.substring(lineStart, lineLength), DictationMarkerSupplier::create(alternativesInLine)); 119 applyCommandToComposite(command, endingSelection()); 120} 121 122void DictationCommand::insertParagraphSeparator() 123{ 124 if (!canAppendNewLineFeedToSelection(endingSelection())) 125 return; 126 127 applyCommandToComposite(InsertParagraphSeparatorCommand::create(document())); 128} 129 130void DictationCommand::collectDictationAlternativesInRange(size_t rangeStart, size_t rangeLength, Vector<DictationAlternative>& alternatives) 131{ 132 for (size_t i = 0; i < m_alternatives.size(); ++i) { 133 const DictationAlternative& alternative = m_alternatives[i]; 134 if (alternative.rangeStart >= rangeStart && (alternative.rangeStart + alternative.rangeLength) <= rangeStart + rangeLength) 135 alternatives.append(DictationAlternative(alternative.rangeStart - rangeStart, alternative.rangeLength, alternative.dictationContext)); 136 } 137 138} 139 140} 141