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