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