]> git.lyx.org Git - lyx.git/blob - src/AppleSpellChecker.cpp
Fix #10778 (issue with CJK and language nesting)
[lyx.git] / src / AppleSpellChecker.cpp
1 /**
2  * \file AppleSpellChecker.cpp
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 #include <config.h>
12
13 #include "AppleSpellChecker.h"
14 #include "WordLangTuple.h"
15
16 #include "support/lassert.h"
17 #include "support/debug.h"
18 #include "support/docstring_list.h"
19 #include "support/AppleSpeller.h"
20
21 using namespace std;
22 using namespace lyx::support;
23
24 namespace lyx {
25
26 struct AppleSpellChecker::Private
27 {
28         Private();
29
30         ~Private();
31
32         SpellChecker::Result toResult(SpellCheckResult status);
33         string toString(SpellCheckResult status);
34         int numDictionaries() const;
35         
36         /// the speller
37         AppleSpeller speller;
38         
39         /// language map
40         map<string, string> languageMap;
41         
42 };
43
44
45 AppleSpellChecker::Private::Private()
46 {
47         speller = newAppleSpeller();
48 }
49
50
51 AppleSpellChecker::Private::~Private()
52 {
53         freeAppleSpeller(speller);
54         speller = 0;
55 }
56
57
58 AppleSpellChecker::AppleSpellChecker()
59         : d(new Private)
60 {}
61
62
63 AppleSpellChecker::~AppleSpellChecker()
64 {
65         delete d;
66 }
67
68
69 SpellChecker::Result AppleSpellChecker::Private::toResult(SpellCheckResult status)
70 {
71         return status == SPELL_CHECK_FAILED ? UNKNOWN_WORD :
72                 status == SPELL_CHECK_LEARNED ? LEARNED_WORD : WORD_OK ;
73 }
74
75
76 string AppleSpellChecker::Private::toString(SpellCheckResult status)
77 {
78         return status == SPELL_CHECK_FAILED ? "FAILED" :
79                  status == SPELL_CHECK_LEARNED ? "LEARNED" : "OK";
80 }
81
82
83 SpellChecker::Result AppleSpellChecker::check(WordLangTuple const & word)
84 {
85         if (!hasDictionary(word.lang()))
86                 return NO_DICTIONARY;
87
88         string const word_str = to_utf8(word.word());
89         string const lang = d->languageMap[word.lang()->lang()];
90         SpellCheckResult result =
91                 AppleSpeller_check(d->speller,
92                         word_str.c_str(), lang.c_str());
93         LYXERR(Debug::GUI, "spellCheck: \"" <<
94                    word.word() << "\" = " << d->toString(result) <<
95                    ", lang = " << lang) ;
96         return d->toResult(result);
97 }
98
99
100 void AppleSpellChecker::advanceChangeNumber()
101 {
102         nextChangeNumber();
103 }
104
105
106 // add to personal dictionary
107 void AppleSpellChecker::insert(WordLangTuple const & word)
108 {
109         string const word_str = to_utf8(word.word());
110         AppleSpeller_learn(d->speller, word_str.c_str());
111         LYXERR(Debug::GUI, "learn word: \"" << word.word() << "\"") ;
112         advanceChangeNumber();
113 }
114
115
116 // remove from personal dictionary
117 void AppleSpellChecker::remove(WordLangTuple const & word)
118 {
119         string const word_str = to_utf8(word.word());
120         AppleSpeller_unlearn(d->speller, word_str.c_str());
121         LYXERR(Debug::GUI, "unlearn word: \"" << word.word() << "\"") ;
122         advanceChangeNumber();
123 }
124
125
126 // ignore for session
127 void AppleSpellChecker::accept(WordLangTuple const & word)
128 {
129         string const word_str = to_utf8(word.word());
130         AppleSpeller_ignore(d->speller, word_str.c_str());
131         LYXERR(Debug::GUI, "ignore word: \"" << word.word() << "\"") ;
132         advanceChangeNumber();
133 }
134
135
136 void AppleSpellChecker::suggest(WordLangTuple const & wl,
137         docstring_list & suggestions)
138 {
139         suggestions.clear();
140         string const word_str = to_utf8(wl.word());
141         size_t num = AppleSpeller_makeSuggestion(d->speller, 
142                                         word_str.c_str(), wl.lang()->code().c_str());
143         for (size_t i = 0; i < num; i++) {
144                 char const * next = AppleSpeller_getSuggestion(d->speller, i);
145                 if (!next) break;
146                 suggestions.push_back(from_utf8(next));
147         }
148 }
149
150
151 bool AppleSpellChecker::hasDictionary(Language const * lang) const
152 {
153         string const langmap = d->languageMap[lang->lang()];
154         bool result = !langmap.empty();
155
156         if (result)
157                 return result;
158
159         result = AppleSpeller_hasLanguage(d->speller,lang->code().c_str());
160         if (result) {
161                 d->languageMap[lang->lang()] = lang->code();
162         } else {
163                 result = AppleSpeller_hasLanguage(d->speller,lang->lang().c_str());
164                 if (result)
165                         d->languageMap[lang->lang()] = lang->lang();
166         }
167         LYXERR(Debug::GUI, "has dictionary: " << lang->lang() << " = " << result) ;
168         return result;
169 }
170
171
172 int AppleSpellChecker::numDictionaries() const
173 {
174         int result = 0;
175         map<string, string>::const_iterator it = d->languageMap.begin();
176         map<string, string>::const_iterator et = d->languageMap.end();
177
178         for (; it != et; ++it) {
179                 string const langmap = it->second;
180                 result += langmap.empty() ? 0 : 1;
181         }
182         return result;
183 }
184
185         
186 int AppleSpellChecker::numMisspelledWords() const
187 {
188         return AppleSpeller_numMisspelledWords(d->speller);
189 }
190
191
192 void AppleSpellChecker::misspelledWord(int index, int & start, int & length) const
193 {
194         AppleSpeller_misspelledWord(d->speller, index, &start, &length);
195 }
196
197
198 docstring const AppleSpellChecker::error()
199 {
200         return docstring();
201 }
202
203
204 } // namespace lyx