]> git.lyx.org Git - lyx.git/blob - src/support/AppleSpeller.m
Workaround for #6865: smarter FontList::setMisspelled implementation
[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 checkAppleSpeller(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
98         [speller->misspelled release];
99         speller->misspelled = nil;
100
101         while (result == SPELL_CHECK_FAILED) {
102                 NSRange match = [speller->checker
103                         checkSpellingOfString:word_
104                         startingAt:start
105                         language:lang_
106                         wrap:NO
107                         inSpellDocumentWithTag:speller->doctag
108                         wordCount:NULL];
109
110                 result = match.length == 0 ? SPELL_CHECK_OK : SPELL_CHECK_FAILED;
111                 if (result == SPELL_CHECK_OK) {
112                         if ([NSSpellChecker instancesRespondToSelector:@selector(hasLearnedWord:)]) {
113                                 if ([speller->checker hasLearnedWord:word_])
114                                         result = SPELL_CHECK_LEARNED;
115                         }
116                 } else {
117                         int capacity = [speller->misspelled count] + 1;
118                         NSMutableArray * misspelled = [NSMutableArray arrayWithCapacity:capacity];
119                         [misspelled addObjectsFromArray:speller->misspelled];
120                         [misspelled addObject:[NSValue valueWithRange:match]];
121                         [speller->misspelled release];
122                         speller->misspelled = [[NSArray arrayWithArray:misspelled] retain];
123                         start = match.location + match.length + 1;
124                 }
125         }
126
127         [word_ release];
128         [lang_ release];
129         [pool release];
130
131         return [speller->misspelled count] ? SPELL_CHECK_FAILED : result;
132 }
133
134
135 void ignoreAppleSpeller(AppleSpeller speller, const char * word)
136 {
137         NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
138         NSString * word_ = toString(word);
139
140         [speller->checker ignoreWord:word_ inSpellDocumentWithTag:(speller->doctag)];
141
142         [word_ release];
143         [pool release];
144 }
145
146
147 size_t makeSuggestionAppleSpeller(AppleSpeller speller, const char * word, const char * lang)
148 {
149         if (!speller->checker || !word || !lang)
150                 return 0;
151
152         NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
153         NSString * word_ = toString(word);
154         NSString * lang_ = toString(lang);
155         NSArray * result ;
156
157 #if defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && (__MAC_OS_X_VERSION_MAX_ALLOWED >= 1060)
158         // Mac OS X 10.6 only
159         NSInteger slen = [word_ length];
160         NSRange range = { 0, slen };
161         
162         if ([NSSpellChecker instancesRespondToSelector:@selector(guessesForWordRange:)]) {
163                 result = [speller->checker guessesForWordRange:range
164                         inString:word_
165                         language:lang_
166                         inSpellDocumentWithTag:speller->doctag];
167         } else {
168                 [speller->checker setLanguage:lang_];
169                 result = [speller->checker guessesForWord:word_];
170         }
171 #else
172         [speller->checker setLanguage:lang_];
173         result = [speller->checker guessesForWord:word_];
174 #endif
175
176         [word_ release];
177         [lang_ release];
178
179         [speller->suggestions release];
180         speller->suggestions = [[NSArray arrayWithArray:result] retain];
181
182         [pool release];
183         return [speller->suggestions count];
184 }
185
186
187 const char * getSuggestionAppleSpeller(AppleSpeller speller, size_t pos)
188 {
189         const char * result = 0;
190         if (pos < [speller->suggestions count]) {
191                 result = [[speller->suggestions objectAtIndex:pos] UTF8String] ;
192         }
193         return result;
194 }
195
196
197 void learnAppleSpeller(AppleSpeller speller, const char * word)
198 {
199 #if defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && (__MAC_OS_X_VERSION_MAX_ALLOWED >= 1050)
200         NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
201         NSString * word_ = toString(word);
202
203         if ([NSSpellChecker instancesRespondToSelector:@selector(learnWord:)])
204                 [speller->checker learnWord:word_];
205         
206         [word_ release];
207         [pool release];
208 #endif
209 }
210
211
212 void unlearnAppleSpeller(AppleSpeller speller, const char * word)
213 {
214 #if defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && (__MAC_OS_X_VERSION_MAX_ALLOWED >= 1050)
215         NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
216         NSString * word_ = toString(word);
217
218         if ([NSSpellChecker instancesRespondToSelector:@selector(unlearnWord:)])
219                 [speller->checker unlearnWord:word_];
220         
221         [word_ release];
222         [pool release];
223 #endif
224 }
225
226
227 int numMisspelledWordsAppleSpeller(AppleSpeller speller)
228 {
229         return [speller->misspelled count];
230 }
231
232
233 void misspelledWordAppleSpeller(AppleSpeller speller, int const position, int * start, int * length)
234 {
235         NSRange range = [[speller->misspelled objectAtIndex:position] rangeValue];
236         *start = range.location;
237         *length = range.length;
238 }
239
240
241 int hasLanguageAppleSpeller(AppleSpeller speller, const char * lang)
242 {
243         return toLanguage(speller, lang) != nil;
244 }