2
* Copyright (C) 2010, 2011 Apple Inc. All rights reserved.
4
* Redistribution and use in source and binary forms, with or without
5
* modification, are permitted provided that the following conditions
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.
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.
27
#import "TextChecker.h"
29
#import "TextCheckerState.h"
30
#import <WebCore/NotImplemented.h>
31
#import <wtf/RetainPtr.h>
33
#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070
34
@interface NSSpellChecker (WebNSSpellCheckerDetails)
35
- (NSString *)languageForWordRange:(NSRange)range inString:(NSString *)string orthography:(NSOrthography *)orthography;
39
static NSString* const WebAutomaticSpellingCorrectionEnabled = @"WebAutomaticSpellingCorrectionEnabled";
40
static NSString* const WebContinuousSpellCheckingEnabled = @"WebContinuousSpellCheckingEnabled";
41
static NSString* const WebGrammarCheckingEnabled = @"WebGrammarCheckingEnabled";
42
static NSString* const WebSmartInsertDeleteEnabled = @"WebSmartInsertDeleteEnabled";
43
static NSString* const WebAutomaticQuoteSubstitutionEnabled = @"WebAutomaticQuoteSubstitutionEnabled";
44
static NSString* const WebAutomaticDashSubstitutionEnabled = @"WebAutomaticDashSubstitutionEnabled";
45
static NSString* const WebAutomaticLinkDetectionEnabled = @"WebAutomaticLinkDetectionEnabled";
46
static NSString* const WebAutomaticTextReplacementEnabled = @"WebAutomaticTextReplacementEnabled";
48
using namespace WebCore;
52
TextCheckerState textCheckerState;
54
static void initializeState()
56
static bool didInitializeState;
57
if (didInitializeState)
60
textCheckerState.isContinuousSpellCheckingEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:WebContinuousSpellCheckingEnabled] && TextChecker::isContinuousSpellCheckingAllowed();
61
textCheckerState.isGrammarCheckingEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:WebGrammarCheckingEnabled];
62
textCheckerState.isAutomaticSpellingCorrectionEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:WebAutomaticSpellingCorrectionEnabled];
63
textCheckerState.isAutomaticQuoteSubstitutionEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:WebAutomaticQuoteSubstitutionEnabled];
64
textCheckerState.isAutomaticDashSubstitutionEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:WebAutomaticDashSubstitutionEnabled];
65
textCheckerState.isAutomaticLinkDetectionEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:WebAutomaticLinkDetectionEnabled];
66
textCheckerState.isAutomaticTextReplacementEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:WebAutomaticTextReplacementEnabled];
68
#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070
69
if (![[NSUserDefaults standardUserDefaults] objectForKey:WebAutomaticSpellingCorrectionEnabled])
70
textCheckerState.isAutomaticSpellingCorrectionEnabled = [NSSpellChecker isAutomaticSpellingCorrectionEnabled];
73
didInitializeState = true;
76
const TextCheckerState& TextChecker::state()
79
return textCheckerState;
82
bool TextChecker::isContinuousSpellCheckingAllowed()
84
static bool allowContinuousSpellChecking = true;
85
static bool readAllowContinuousSpellCheckingDefault = false;
87
if (!readAllowContinuousSpellCheckingDefault) {
88
if ([[NSUserDefaults standardUserDefaults] objectForKey:@"NSAllowContinuousSpellChecking"])
89
allowContinuousSpellChecking = [[NSUserDefaults standardUserDefaults] boolForKey:@"NSAllowContinuousSpellChecking"];
91
readAllowContinuousSpellCheckingDefault = true;
94
return allowContinuousSpellChecking;
97
void TextChecker::setContinuousSpellCheckingEnabled(bool isContinuousSpellCheckingEnabled)
99
if (state().isContinuousSpellCheckingEnabled == isContinuousSpellCheckingEnabled)
102
textCheckerState.isContinuousSpellCheckingEnabled = isContinuousSpellCheckingEnabled;
103
[[NSUserDefaults standardUserDefaults] setBool:isContinuousSpellCheckingEnabled forKey:WebContinuousSpellCheckingEnabled];
105
// FIXME: preflight the spell checker.
108
void TextChecker::setGrammarCheckingEnabled(bool isGrammarCheckingEnabled)
110
if (state().isGrammarCheckingEnabled == isGrammarCheckingEnabled)
113
textCheckerState.isGrammarCheckingEnabled = isGrammarCheckingEnabled;
114
[[NSUserDefaults standardUserDefaults] setBool:isGrammarCheckingEnabled forKey:WebGrammarCheckingEnabled];
115
[[NSSpellChecker sharedSpellChecker] updatePanels];
117
// We call preflightSpellChecker() when turning continuous spell checking on, but we don't need to do that here
118
// because grammar checking only occurs on code paths that already preflight spell checking appropriately.
121
void TextChecker::setAutomaticSpellingCorrectionEnabled(bool isAutomaticSpellingCorrectionEnabled)
123
if (state().isAutomaticSpellingCorrectionEnabled == isAutomaticSpellingCorrectionEnabled)
126
textCheckerState.isAutomaticSpellingCorrectionEnabled = isAutomaticSpellingCorrectionEnabled;
127
[[NSUserDefaults standardUserDefaults] setBool:isAutomaticSpellingCorrectionEnabled forKey:WebAutomaticSpellingCorrectionEnabled];
129
[[NSSpellChecker sharedSpellChecker] updatePanels];
132
void TextChecker::setAutomaticQuoteSubstitutionEnabled(bool isAutomaticQuoteSubstitutionEnabled)
134
if (state().isAutomaticQuoteSubstitutionEnabled == isAutomaticQuoteSubstitutionEnabled)
137
textCheckerState.isAutomaticQuoteSubstitutionEnabled = isAutomaticQuoteSubstitutionEnabled;
138
[[NSUserDefaults standardUserDefaults] setBool:isAutomaticQuoteSubstitutionEnabled forKey:WebAutomaticQuoteSubstitutionEnabled];
140
[[NSSpellChecker sharedSpellChecker] updatePanels];
143
void TextChecker::setAutomaticDashSubstitutionEnabled(bool isAutomaticDashSubstitutionEnabled)
145
if (state().isAutomaticDashSubstitutionEnabled == isAutomaticDashSubstitutionEnabled)
148
textCheckerState.isAutomaticDashSubstitutionEnabled = isAutomaticDashSubstitutionEnabled;
149
[[NSUserDefaults standardUserDefaults] setBool:isAutomaticDashSubstitutionEnabled forKey:WebAutomaticDashSubstitutionEnabled];
151
[[NSSpellChecker sharedSpellChecker] updatePanels];
154
void TextChecker::setAutomaticLinkDetectionEnabled(bool isAutomaticLinkDetectionEnabled)
156
if (state().isAutomaticLinkDetectionEnabled == isAutomaticLinkDetectionEnabled)
159
textCheckerState.isAutomaticLinkDetectionEnabled = isAutomaticLinkDetectionEnabled;
160
[[NSUserDefaults standardUserDefaults] setBool:isAutomaticLinkDetectionEnabled forKey:WebAutomaticLinkDetectionEnabled];
162
[[NSSpellChecker sharedSpellChecker] updatePanels];
165
void TextChecker::setAutomaticTextReplacementEnabled(bool isAutomaticTextReplacementEnabled)
167
if (state().isAutomaticTextReplacementEnabled == isAutomaticTextReplacementEnabled)
170
textCheckerState.isAutomaticTextReplacementEnabled = isAutomaticTextReplacementEnabled;
171
[[NSUserDefaults standardUserDefaults] setBool:isAutomaticTextReplacementEnabled forKey:WebAutomaticTextReplacementEnabled];
173
[[NSSpellChecker sharedSpellChecker] updatePanels];
176
static bool smartInsertDeleteEnabled;
178
bool TextChecker::isSmartInsertDeleteEnabled()
180
static bool readSmartInsertDeleteEnabledDefault;
182
if (!readSmartInsertDeleteEnabledDefault) {
183
smartInsertDeleteEnabled = ![[NSUserDefaults standardUserDefaults] objectForKey:WebSmartInsertDeleteEnabled] || [[NSUserDefaults standardUserDefaults] boolForKey:WebSmartInsertDeleteEnabled];
185
readSmartInsertDeleteEnabledDefault = true;
188
return smartInsertDeleteEnabled;
191
void TextChecker::setSmartInsertDeleteEnabled(bool flag)
193
if (flag == isSmartInsertDeleteEnabled())
196
smartInsertDeleteEnabled = flag;
198
[[NSUserDefaults standardUserDefaults] setBool:flag forKey:WebSmartInsertDeleteEnabled];
201
bool TextChecker::substitutionsPanelIsShowing()
203
return [[[NSSpellChecker sharedSpellChecker] substitutionsPanel] isVisible];
206
void TextChecker::toggleSubstitutionsPanelIsShowing()
208
NSPanel *substitutionsPanel = [[NSSpellChecker sharedSpellChecker] substitutionsPanel];
209
if ([substitutionsPanel isVisible]) {
210
[substitutionsPanel orderOut:nil];
213
[substitutionsPanel orderFront:nil];
216
void TextChecker::continuousSpellCheckingEnabledStateChanged(bool enabled)
218
textCheckerState.isContinuousSpellCheckingEnabled = enabled;
221
void TextChecker::grammarCheckingEnabledStateChanged(bool enabled)
223
textCheckerState.isGrammarCheckingEnabled = enabled;
226
int64_t TextChecker::uniqueSpellDocumentTag(WebPageProxy*)
228
return [NSSpellChecker uniqueSpellDocumentTag];
231
void TextChecker::closeSpellDocumentWithTag(int64_t tag)
233
[[NSSpellChecker sharedSpellChecker] closeSpellDocumentWithTag:tag];
236
#if USE(UNIFIED_TEXT_CHECKING)
238
Vector<TextCheckingResult> TextChecker::checkTextOfParagraph(int64_t spellDocumentTag, const UChar* text, int length, uint64_t checkingTypes)
240
Vector<TextCheckingResult> results;
242
RetainPtr<NSString> textString(AdoptNS, [[NSString alloc] initWithCharactersNoCopy:const_cast<UChar*>(text) length:length freeWhenDone:NO]);
243
NSArray *incomingResults = [[NSSpellChecker sharedSpellChecker] checkString:textString .get()
244
range:NSMakeRange(0, length)
245
types:checkingTypes | NSTextCheckingTypeOrthography
247
inSpellDocumentWithTag:spellDocumentTag
250
for (NSTextCheckingResult *incomingResult in incomingResults) {
251
NSRange resultRange = [incomingResult range];
252
NSTextCheckingType resultType = [incomingResult resultType];
253
ASSERT(resultRange.location != NSNotFound);
254
ASSERT(resultRange.length > 0);
255
if (resultType == NSTextCheckingTypeSpelling && (checkingTypes & NSTextCheckingTypeSpelling)) {
256
TextCheckingResult result;
257
result.type = TextCheckingTypeSpelling;
258
result.location = resultRange.location;
259
result.length = resultRange.length;
260
results.append(result);
261
} else if (resultType == NSTextCheckingTypeGrammar && (checkingTypes & NSTextCheckingTypeGrammar)) {
262
TextCheckingResult result;
263
NSArray *details = [incomingResult grammarDetails];
264
result.type = TextCheckingTypeGrammar;
265
result.location = resultRange.location;
266
result.length = resultRange.length;
267
for (NSDictionary *incomingDetail in details) {
268
ASSERT(incomingDetail);
269
GrammarDetail detail;
270
NSValue *detailRangeAsNSValue = [incomingDetail objectForKey:NSGrammarRange];
271
ASSERT(detailRangeAsNSValue);
272
NSRange detailNSRange = [detailRangeAsNSValue rangeValue];
273
ASSERT(detailNSRange.location != NSNotFound);
274
ASSERT(detailNSRange.length > 0);
275
detail.location = detailNSRange.location;
276
detail.length = detailNSRange.length;
277
detail.userDescription = [incomingDetail objectForKey:NSGrammarUserDescription];
278
NSArray *guesses = [incomingDetail objectForKey:NSGrammarCorrections];
279
for (NSString *guess in guesses)
280
detail.guesses.append(String(guess));
281
result.details.append(detail);
283
results.append(result);
284
} else if (resultType == NSTextCheckingTypeLink && (checkingTypes & NSTextCheckingTypeLink)) {
285
TextCheckingResult result;
286
result.type = TextCheckingTypeLink;
287
result.location = resultRange.location;
288
result.length = resultRange.length;
289
result.replacement = [[incomingResult URL] absoluteString];
290
results.append(result);
291
} else if (resultType == NSTextCheckingTypeQuote && (checkingTypes & NSTextCheckingTypeQuote)) {
292
TextCheckingResult result;
293
result.type = TextCheckingTypeQuote;
294
result.location = resultRange.location;
295
result.length = resultRange.length;
296
result.replacement = [incomingResult replacementString];
297
results.append(result);
298
} else if (resultType == NSTextCheckingTypeDash && (checkingTypes & NSTextCheckingTypeDash)) {
299
TextCheckingResult result;
300
result.type = TextCheckingTypeDash;
301
result.location = resultRange.location;
302
result.length = resultRange.length;
303
result.replacement = [incomingResult replacementString];
304
results.append(result);
305
} else if (resultType == NSTextCheckingTypeReplacement && (checkingTypes & NSTextCheckingTypeReplacement)) {
306
TextCheckingResult result;
307
result.type = TextCheckingTypeReplacement;
308
result.location = resultRange.location;
309
result.length = resultRange.length;
310
result.replacement = [incomingResult replacementString];
311
results.append(result);
312
} else if (resultType == NSTextCheckingTypeCorrection && (checkingTypes & NSTextCheckingTypeCorrection)) {
313
TextCheckingResult result;
314
result.type = TextCheckingTypeCorrection;
315
result.location = resultRange.location;
316
result.length = resultRange.length;
317
result.replacement = [incomingResult replacementString];
318
results.append(result);
327
void TextChecker::checkSpellingOfString(int64_t, const UChar*, uint32_t, int32_t&, int32_t&)
329
// Mac uses checkTextOfParagraph instead.
333
void TextChecker::checkGrammarOfString(int64_t, const UChar*, uint32_t, Vector<WebCore::GrammarDetail>&, int32_t&, int32_t&)
335
// Mac uses checkTextOfParagraph instead.
339
bool TextChecker::spellingUIIsShowing()
341
return [[[NSSpellChecker sharedSpellChecker] spellingPanel] isVisible];
344
void TextChecker::toggleSpellingUIIsShowing()
346
NSPanel *spellingPanel = [[NSSpellChecker sharedSpellChecker] spellingPanel];
347
if ([spellingPanel isVisible])
348
[spellingPanel orderOut:nil];
350
[spellingPanel orderFront:nil];
353
void TextChecker::updateSpellingUIWithMisspelledWord(int64_t, const String& misspelledWord)
355
[[NSSpellChecker sharedSpellChecker] updateSpellingPanelWithMisspelledWord:misspelledWord];
358
void TextChecker::updateSpellingUIWithGrammarString(int64_t, const String& badGrammarPhrase, const GrammarDetail& grammarDetail)
360
RetainPtr<NSMutableArray> corrections(AdoptNS, [[NSMutableArray alloc] init]);
361
for (size_t i = 0; i < grammarDetail.guesses.size(); ++i) {
362
NSString *guess = grammarDetail.guesses[i];
363
[corrections.get() addObject:guess];
366
NSRange grammarRange = NSMakeRange(grammarDetail.location, grammarDetail.length);
367
NSString *grammarUserDescription = grammarDetail.userDescription;
368
RetainPtr<NSDictionary> grammarDetailDict(AdoptNS, [[NSDictionary alloc] initWithObjectsAndKeys:[NSValue valueWithRange:grammarRange], NSGrammarRange, grammarUserDescription, NSGrammarUserDescription, corrections.get(), NSGrammarCorrections, nil]);
370
[[NSSpellChecker sharedSpellChecker] updateSpellingPanelWithGrammarString:badGrammarPhrase detail:grammarDetailDict.get()];
373
void TextChecker::getGuessesForWord(int64_t spellDocumentTag, const String& word, const String& context, Vector<String>& guesses)
375
#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070
376
NSString* language = nil;
377
NSOrthography* orthography = nil;
378
NSSpellChecker *checker = [NSSpellChecker sharedSpellChecker];
379
if (context.length()) {
380
[checker checkString:context range:NSMakeRange(0, context.length()) types:NSTextCheckingTypeOrthography options:0 inSpellDocumentWithTag:spellDocumentTag orthography:&orthography wordCount:0];
381
language = [checker languageForWordRange:NSMakeRange(0, context.length()) inString:context orthography:orthography];
383
NSArray* stringsArray = [checker guessesForWordRange:NSMakeRange(0, word.length()) inString:word language:language inSpellDocumentWithTag:spellDocumentTag];
385
NSArray* stringsArray = [[NSSpellChecker sharedSpellChecker] guessesForWord:word];
388
for (NSString *guess in stringsArray)
389
guesses.append(guess);
392
void TextChecker::learnWord(int64_t, const String& word)
394
[[NSSpellChecker sharedSpellChecker] learnWord:word];
397
void TextChecker::ignoreWord(int64_t spellDocumentTag, const String& word)
399
[[NSSpellChecker sharedSpellChecker] ignoreWord:word inSpellDocumentWithTag:spellDocumentTag];
402
} // namespace WebKit