]> git.lyx.org Git - lyx.git/blob - src/support/AppleSpeller.m
avoid spell check with out of range start value
[lyx.git] / src / support / AppleSpeller.m
1 /**
2  * \file AppleSpeller.m
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Stephan Witt
7  *
8  * Full author contact details are available in file CREDITS.
9  */
10
11 #import <Carbon/Carbon.h>
12 #import <Cocoa/Cocoa.h>
13
14 #import <AvailabilityMacros.h>
15
16 #include "support/AppleSpeller.h"
17
18 typedef struct AppleSpellerRec {
19         NSSpellChecker * checker;
20 #if defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && (__MAC_OS_X_VERSION_MAX_ALLOWED >= 1050)
21         NSInteger doctag;
22 #else
23         int doctag;
24 #endif
25         NSArray * suggestions;
26         NSArray * misspelled;
27 } AppleSpellerRec ;
28
29
30 AppleSpeller newAppleSpeller(void)
31 {
32         AppleSpeller speller = calloc(1, sizeof(AppleSpellerRec));
33         speller->checker = [NSSpellChecker sharedSpellChecker];
34         speller->doctag = [NSSpellChecker uniqueSpellDocumentTag];
35         speller->suggestions = nil;
36         speller->misspelled = nil;
37         return speller;
38 }
39
40
41 void freeAppleSpeller(AppleSpeller speller)
42 {
43         NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
44
45         [speller->checker closeSpellDocumentWithTag:speller->doctag];
46
47         [speller->suggestions release];
48         [speller->misspelled release];
49
50         [pool release];
51
52         free(speller);
53 }
54
55
56 static NSString * toString(const char * word)
57 {
58         return [[NSString alloc] initWithBytes:word length:strlen(word) encoding:NSUTF8StringEncoding];
59 }
60
61
62 static NSString * toLanguage(AppleSpeller speller, const char * lang)
63 {
64         NSString * result = nil;
65 #if defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && (__MAC_OS_X_VERSION_MAX_ALLOWED >= 1050)
66         NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
67         NSString * lang_ = toString(lang);
68         if ([NSSpellChecker instancesRespondToSelector:@selector(availableLanguages)]) {
69                 NSArray * languages = [speller->checker availableLanguages];
70                 
71                 for (NSString *element in languages) {
72                         if ([element isEqualToString:lang_]) {
73                                 result = element;
74                                 break;
75                         } else if ([lang_ hasPrefix:element]) {
76                                 result = element;
77                         }
78                 }
79         }
80         [lang_ release];
81         [pool release];
82 #endif
83         return result;
84 }
85
86
87 SpellCheckResult AppleSpeller_check(AppleSpeller speller, const char * word, const char * lang)
88 {
89         if (!speller->checker || !lang || !word)
90                 return SPELL_CHECK_FAILED;
91
92         NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
93         NSString * word_ = toString(word);
94         NSString * lang_ = toString(lang);
95         SpellCheckResult result = SPELL_CHECK_FAILED;
96         int start = 0;
97         int length = [word_ length];
98         
99         [speller->misspelled release];
100         speller->misspelled = nil;
101
102         while (result == SPELL_CHECK_FAILED && start < length) {
103                 NSRange match = [speller->checker
104                         checkSpellingOfString:word_
105                         startingAt:start
106                         language:lang_
107                         wrap:NO
108                         inSpellDocumentWithTag:speller->doctag
109                         wordCount:NULL];
110
111                 result = match.length == 0 ? SPELL_CHECK_OK : SPELL_CHECK_FAILED;
112                 if (result == SPELL_CHECK_OK) {
113                         if ([NSSpellChecker instancesRespondToSelector:@selector(hasLearnedWord:)]) {
114                                 if ([speller->checker hasLearnedWord:word_])
115                                         result = SPELL_CHECK_LEARNED;
116                         }
117                 } else {
118                         int capacity = [speller->misspelled count] + 1;
119                         NSMutableArray * misspelled = [NSMutableArray arrayWithCapacity:capacity];
120                         [misspelled addObjectsFromArray:speller->misspelled];
121                         [misspelled addObject:[NSValue valueWithRange:match]];
122                         [speller->misspelled release];
123                         speller->misspelled = [[NSArray arrayWithArray:misspelled] retain];
124                         start = match.location + match.length + 1;
125                 }
126         }
127
128         [word_ release];
129         [lang_ release];
130         [pool release];
131
132         return [speller->misspelled count] ? SPELL_CHECK_FAILED : result;
133 }
134
135
136 void AppleSpeller_ignore(AppleSpeller speller, const char * word)
137 {
138         NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
139         NSString * word_ = toString(word);
140
141         [speller->checker ignoreWord:word_ inSpellDocumentWithTag:(speller->doctag)];
142
143         [word_ release];
144         [pool release];
145 }
146
147
148 size_t AppleSpeller_makeSuggestion(AppleSpeller speller, const char * word, const char * lang)
149 {
150         if (!speller->checker || !word || !lang)
151                 return 0;
152
153         NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
154         NSString * word_ = toString(word);
155         NSString * lang_ = toString(lang);
156         NSArray * result ;
157
158 #if defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && (__MAC_OS_X_VERSION_MAX_ALLOWED >= 1060)
159         // Mac OS X 10.6 only
160         NSInteger slen = [word_ length];
161         NSRange range = { 0, slen };
162         
163         if ([NSSpellChecker instancesRespondToSelector:@selector(guessesForWordRange:)]) {
164                 result = [speller->checker guessesForWordRange:range
165                         inString:word_
166                         language:lang_
167                         inSpellDocumentWithTag:speller->doctag];
168         } else {
169                 [speller->checker setLanguage:lang_];
170                 result = [speller->checker guessesForWord:word_];
171         }
172 #else
173         [speller->checker setLanguage:lang_];
174         result = [speller->checker guessesForWord:word_];
175 #endif
176
177         [word_ release];
178         [lang_ release];
179
180         [speller->suggestions release];
181         speller->suggestions = [[NSArray arrayWithArray:result] retain];
182
183         [pool release];
184         return [speller->suggestions count];
185 }
186
187
188 const char * AppleSpeller_getSuggestion(AppleSpeller speller, size_t pos)
189 {
190         const char * result = 0;
191         if (pos < [speller->suggestions count]) {
192                 result = [[speller->suggestions objectAtIndex:pos] UTF8String] ;
193         }
194         return result;
195 }
196
197
198 void AppleSpeller_learn(AppleSpeller speller, const char * word)
199 {
200 #if defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && (__MAC_OS_X_VERSION_MAX_ALLOWED >= 1050)
201         NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
202         NSString * word_ = toString(word);
203
204         if ([NSSpellChecker instancesRespondToSelector:@selector(learnWord:)])
205                 [speller->checker learnWord:word_];
206         
207         [word_ release];
208         [pool release];
209 #endif
210 }
211
212
213 void AppleSpeller_unlearn(AppleSpeller speller, const char * word)
214 {
215 #if defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && (__MAC_OS_X_VERSION_MAX_ALLOWED >= 1050)
216         NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
217         NSString * word_ = toString(word);
218
219         if ([NSSpellChecker instancesRespondToSelector:@selector(unlearnWord:)])
220                 [speller->checker unlearnWord:word_];
221         
222         [word_ release];
223         [pool release];
224 #endif
225 }
226
227
228 int AppleSpeller_numMisspelledWords(AppleSpeller speller)
229 {
230         return [speller->misspelled count];
231 }
232
233
234 void AppleSpeller_misspelledWord(AppleSpeller speller, int index, int * start, int * length)
235 {
236         NSRange range = [[speller->misspelled objectAtIndex:index] rangeValue];
237         *start = range.location;
238         *length = range.length;
239 }
240
241
242 int AppleSpeller_hasLanguage(AppleSpeller speller, const char * lang)
243 {
244         return toLanguage(speller, lang) != nil;
245 }