]> git.lyx.org Git - lyx.git/blob - src/HunspellChecker.cpp
Routines for calculating numerical labels for BibTeX citations.
[lyx.git] / src / HunspellChecker.cpp
1 /**
2  * \file HunspellChecker.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Abdelrazak Younes
7  *
8  * Full author contact details are available in file CREDITS.
9  */
10
11 #include <config.h>
12
13 #include "HunspellChecker.h"
14
15 #include "LyXRC.h"
16 #include "WordLangTuple.h"
17
18 #include "frontends/alert.h"
19
20 #include "support/debug.h"
21 #include "support/docstring_list.h"
22 #include "support/filetools.h"
23 #include "support/FileName.h"
24 #include "support/gettext.h"
25 #include "support/lassert.h"
26 #include "support/lstrings.h"
27 #include "support/os.h"
28
29 #include <hunspell/hunspell.hxx>
30
31 #include <map>
32 #include <string>
33
34 using namespace std;
35 using namespace lyx::support;
36 using namespace lyx::support::os;
37
38 namespace lyx {
39
40 namespace {
41
42 typedef map<std::string, Hunspell *> Spellers;
43
44 } // anon namespace
45
46 struct HunspellChecker::Private
47 {
48         Private() {}
49
50         ~Private();
51
52         Hunspell * addSpeller(string const & lang);
53         Hunspell * speller(string const & lang);
54
55         /// the spellers
56         Spellers spellers_;
57 };
58
59
60 HunspellChecker::Private::~Private()
61 {
62         Spellers::iterator it = spellers_.begin();
63         Spellers::iterator end = spellers_.end();
64
65         for (; it != end; ++it) {
66                 delete it->second;
67         }
68 }
69
70
71 namespace {
72 bool haveLanguageFiles(string const & hpath)
73 {
74         FileName const affix(hpath + ".aff");
75         FileName const dict(hpath + ".dic");
76         if (!affix.isReadableFile()) {
77                 // FIXME: We should indicate somehow that this language is not
78                 // supported.
79                 LYXERR(Debug::FILES, "Hunspell affix file " << affix << " does not exist");
80                 return false;
81         }
82         if (!dict.isReadableFile()) {
83                 LYXERR(Debug::FILES, "Hunspell dictionary file " << dict << " does not exist");
84                 return false;
85         }
86         return true;
87 }
88 }
89
90
91 Hunspell * HunspellChecker::Private::addSpeller(string const & lang)
92 {
93         string hunspell_path = lyxrc.hunspelldir_path;
94         LYXERR(Debug::FILES, "hunspell path: " << external_path(hunspell_path));
95         if (hunspell_path.empty()) {
96                 // FIXME We'd like to issue a better error message here, but there seems
97                 // to be a problem about thread safety, or something of the sort. If
98                 // we issue the message using frontend::Alert, then the code comes
99                 // back through here while the box is waiting, and causes some kind
100                 // of crash. 
101                 static bool warned = false;
102                 if (!warned) {
103                         warned = true;
104                         LYXERR0("Hunspell path not set.");
105                         //frontend::Alert::error(_("Hunspell Path Not Found"), 
106                         //              _("You must set the Hunspell dictionary path in Tools>Preferences>Paths."));
107                 }
108                 return 0;
109         }
110
111         hunspell_path = external_path(addName(hunspell_path, lang));
112         if (!haveLanguageFiles(hunspell_path)) {
113                 // try with '_' replaced by '-'
114                 hunspell_path = subst(hunspell_path, '_', '-');
115                 if (!haveLanguageFiles(hunspell_path)) {
116                         // FIXME: We should indicate somehow that this language is not
117                         // supported, probably by popping a warning. But we'll need to
118                         // remember which warnings we've issued.
119                         return 0;
120                 }
121         }
122         FileName const affix(hunspell_path + ".aff");
123         FileName const dict(hunspell_path + ".dic");
124         Hunspell * h = new Hunspell(affix.absFilename().c_str(), dict.absFilename().c_str());
125         spellers_[lang] = h;
126         return h;
127 }
128
129
130 Hunspell * HunspellChecker::Private::speller(string const & lang)
131 {
132         Spellers::iterator it = spellers_.find(lang);
133         if (it != spellers_.end())
134                 return it->second;
135         
136         return addSpeller(lang);
137 }
138
139
140 HunspellChecker::HunspellChecker(): d(new Private)
141 {
142 }
143
144
145 HunspellChecker::~HunspellChecker()
146 {
147         delete d;
148 }
149
150
151 SpellChecker::Result HunspellChecker::check(WordLangTuple const & wl)
152 {
153         string const word_to_check = to_utf8(wl.word());
154         Hunspell * h = d->speller(wl.lang_code());
155         if (!h)
156                 return OK;
157         int info;
158         if (h->spell(word_to_check.c_str(), &info))
159                 return OK;
160
161         if (info & SPELL_COMPOUND) {
162                 // FIXME: What to do with that?
163                 LYXERR(Debug::FILES, "Hunspell compound word found " << word_to_check);
164         }
165         if (info & SPELL_FORBIDDEN) {
166                 // FIXME: What to do with that?
167                 LYXERR(Debug::FILES, "Hunspell explicit forbidden word found " << word_to_check);
168         }
169
170         return UNKNOWN_WORD;
171 }
172
173
174 void HunspellChecker::insert(WordLangTuple const & wl)
175 {
176         string const word_to_check = to_utf8(wl.word());
177         Hunspell * h = d->speller(wl.lang_code());
178         if (!h)
179                 return;
180         h->add(word_to_check.c_str());
181 }
182
183
184 void HunspellChecker::accept(WordLangTuple const &)
185 {
186         // FIXME: not implemented!
187 }
188
189
190 void HunspellChecker::suggest(WordLangTuple const & wl,
191         docstring_list & suggestions)
192 {
193         suggestions.clear();
194         string const word_to_check = to_utf8(wl.word());
195         Hunspell * h = d->speller(wl.lang_code());
196         if (!h)
197                 return;
198         char ** suggestion_list;
199         int const suggestion_number = h->suggest(&suggestion_list, word_to_check.c_str());
200         if (suggestion_number <= 0)
201                 return;
202         for (int i = 0; i != suggestion_number; ++i)
203                 suggestions.push_back(from_utf8(suggestion_list[i]));
204         h->free_list(&suggestion_list, suggestion_number);
205 }
206
207
208 docstring const HunspellChecker::error()
209 {
210         return docstring();
211 }
212
213
214 } // namespace lyx