1/*
2 * Copyright (C) 2011 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. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25#import "CorrectionPanel.h"
26
27#import "WebViewInternal.h"
28
29#if USE(AUTOCORRECTION_PANEL)
30using namespace WebCore;
31
32static inline NSCorrectionIndicatorType correctionIndicatorType(AlternativeTextType alternativeTextType)
33{
34    switch (alternativeTextType) {
35    case AlternativeTextTypeCorrection:
36        return NSCorrectionIndicatorTypeDefault;
37    case AlternativeTextTypeReversion:
38        return NSCorrectionIndicatorTypeReversion;
39    case AlternativeTextTypeSpellingSuggestions:
40        return NSCorrectionIndicatorTypeGuesses;
41    case AlternativeTextTypeDictationAlternatives:
42        ASSERT_NOT_REACHED();
43        break;
44    }
45    ASSERT_NOT_REACHED();
46    return NSCorrectionIndicatorTypeDefault;
47}
48
49CorrectionPanel::CorrectionPanel()
50    : m_wasDismissedExternally(false)
51    , m_reasonForDismissing(ReasonForDismissingAlternativeTextIgnored)
52{
53}
54
55CorrectionPanel::~CorrectionPanel()
56{
57    dismissInternal(ReasonForDismissingAlternativeTextIgnored, false);
58}
59
60void CorrectionPanel::show(WebView* view, AlternativeTextType type, const FloatRect& boundingBoxOfReplacedString, const String& replacedString, const String& replacementString, const Vector<String>& alternativeReplacementStrings)
61{
62    dismissInternal(ReasonForDismissingAlternativeTextIgnored, false);
63
64    if (!view)
65        return;
66
67    NSString* replacedStringAsNSString = replacedString;
68    NSString* replacementStringAsNSString = replacementString;
69    m_view = view;
70    NSCorrectionIndicatorType indicatorType = correctionIndicatorType(type);
71
72    NSMutableArray* alternativeStrings = 0;
73    if (!alternativeReplacementStrings.isEmpty()) {
74        size_t size = alternativeReplacementStrings.size();
75        alternativeStrings = [NSMutableArray arrayWithCapacity:size];
76        for (size_t i = 0; i < size; ++i)
77            [alternativeStrings addObject:(NSString*)alternativeReplacementStrings[i]];
78    }
79
80    [[NSSpellChecker sharedSpellChecker] showCorrectionIndicatorOfType:indicatorType primaryString:replacementStringAsNSString alternativeStrings:alternativeStrings forStringInRect:[view _convertRectFromRootView:boundingBoxOfReplacedString] view:m_view.get() completionHandler:^(NSString* acceptedString) {
81        handleAcceptedReplacement(acceptedString, replacedStringAsNSString, replacementStringAsNSString, indicatorType);
82    }];
83}
84
85String CorrectionPanel::dismiss(ReasonForDismissingAlternativeText reason)
86{
87    return dismissInternal(reason, true);
88}
89
90String CorrectionPanel::dismissInternal(ReasonForDismissingAlternativeText reason, bool dismissingExternally)
91{
92    if (!isShowing())
93        return String();
94
95    m_wasDismissedExternally = dismissingExternally;
96    m_reasonForDismissing = reason;
97    m_resultForDismissal.clear();
98    [[NSSpellChecker sharedSpellChecker] dismissCorrectionIndicatorForView:m_view.get()];
99    return m_resultForDismissal.get();
100}
101
102void CorrectionPanel::recordAutocorrectionResponse(WebView* view, NSCorrectionResponse response, const String& replacedString, const String& replacementString)
103{
104    [[NSSpellChecker sharedSpellChecker] recordResponse:response toCorrection:replacementString forWord:replacedString language:nil inSpellDocumentWithTag:[view spellCheckerDocumentTag]];
105}
106
107void CorrectionPanel::handleAcceptedReplacement(NSString* acceptedReplacement, NSString* replaced, NSString* proposedReplacement,  NSCorrectionIndicatorType correctionIndicatorType)
108{
109    if (!m_view)
110        return;
111
112    NSSpellChecker* spellChecker = [NSSpellChecker sharedSpellChecker];
113    NSInteger documentTag = [m_view.get() spellCheckerDocumentTag];
114
115    switch (correctionIndicatorType) {
116    case NSCorrectionIndicatorTypeDefault:
117        if (acceptedReplacement)
118            [spellChecker recordResponse:NSCorrectionResponseAccepted toCorrection:acceptedReplacement forWord:replaced language:nil inSpellDocumentWithTag:documentTag];
119        else {
120            if (!m_wasDismissedExternally || m_reasonForDismissing == ReasonForDismissingAlternativeTextCancelled)
121                [spellChecker recordResponse:NSCorrectionResponseRejected toCorrection:proposedReplacement forWord:replaced language:nil inSpellDocumentWithTag:documentTag];
122            else
123                [spellChecker recordResponse:NSCorrectionResponseIgnored toCorrection:proposedReplacement forWord:replaced language:nil inSpellDocumentWithTag:documentTag];
124        }
125        break;
126    case NSCorrectionIndicatorTypeReversion:
127        if (acceptedReplacement)
128            [spellChecker recordResponse:NSCorrectionResponseReverted toCorrection:replaced forWord:acceptedReplacement language:nil inSpellDocumentWithTag:documentTag];
129        break;
130    case NSCorrectionIndicatorTypeGuesses:
131        if (acceptedReplacement)
132            [spellChecker recordResponse:NSCorrectionResponseAccepted toCorrection:acceptedReplacement forWord:replaced language:nil inSpellDocumentWithTag:documentTag];
133        break;
134    }
135
136    [m_view.get() handleAcceptedAlternativeText:acceptedReplacement];
137    m_view.clear();
138    if (acceptedReplacement)
139        m_resultForDismissal = adoptNS([acceptedReplacement copy]);
140}
141
142#endif //USE(AUTOCORRECTION_PANEL)
143
144