2 * \file HunspellChecker.cpp
3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Abdelrazak Younes
8 * Full author contact details are available in file CREDITS.
13 #include "HunspellChecker.h"
16 #include "WordLangTuple.h"
18 #include "frontends/alert.h"
20 #include "support/debug.h"
21 #include "support/docstring_list.h"
22 #include "support/FileName.h"
23 #include "support/gettext.h"
24 #include "support/lassert.h"
25 #include "support/lstrings.h"
26 #include "support/os.h"
28 #include <hunspell/hunspell.hxx>
34 using namespace lyx::support;
35 using namespace lyx::support::os;
41 typedef map<std::string, Hunspell *> Spellers;
45 struct HunspellChecker::Private
51 Hunspell * addSpeller(string const & lang);
52 Hunspell * speller(string const & lang);
59 HunspellChecker::Private::~Private()
61 Spellers::iterator it = spellers_.begin();
62 Spellers::iterator end = spellers_.end();
64 for (; it != end; ++it) {
71 bool haveLanguageFiles(string const & hpath)
73 FileName const affix(hpath + ".aff");
74 FileName const dict(hpath + ".dic");
75 if (!affix.isReadableFile()) {
76 // FIXME: We should indicate somehow that this language is not
78 LYXERR(Debug::FILES, "Hunspell affix file " << affix << " does not exist");
81 if (!dict.isReadableFile()) {
82 LYXERR(Debug::FILES, "Hunspell dictionary file " << dict << " does not exist");
90 Hunspell * HunspellChecker::Private::addSpeller(string const & lang)
92 string hunspell_path = external_path(lyxrc.hunspelldir_path);
93 LYXERR(Debug::FILES, "hunspell path: " << hunspell_path);
94 if (hunspell_path.empty()) {
95 static bool warned = false;
97 frontend::Alert::error(_("Hunspell Path Not Found"),
98 _("You must set the Hunspell dictionary path in Tools>Preferences>Paths."));
104 hunspell_path += "/" + lang;
105 if (!haveLanguageFiles(hunspell_path)) {
106 // try with '_' replaced by '-'
107 hunspell_path = subst(hunspell_path, '_', '-');
108 if (!haveLanguageFiles(hunspell_path)) {
109 // FIXME: We should indicate somehow that this language is not
110 // supported, probably by popping a warning. But we'll need to
111 // remember which warnings we've issued.
115 FileName const affix(hunspell_path + ".aff");
116 FileName const dict(hunspell_path + ".dic");
117 Hunspell * h = new Hunspell(affix.absFilename().c_str(), dict.absFilename().c_str());
123 Hunspell * HunspellChecker::Private::speller(string const & lang)
125 Spellers::iterator it = spellers_.find(lang);
126 if (it != spellers_.end())
129 return addSpeller(lang);
133 HunspellChecker::HunspellChecker(): d(new Private)
138 HunspellChecker::~HunspellChecker()
144 SpellChecker::Result HunspellChecker::check(WordLangTuple const & wl)
146 string const word_to_check = to_utf8(wl.word());
147 Hunspell * h = d->speller(wl.lang_code());
151 if (h->spell(word_to_check.c_str(), &info))
154 if (info & SPELL_COMPOUND) {
155 // FIXME: What to do with that?
156 LYXERR(Debug::FILES, "Hunspell compound word found " << word_to_check);
158 if (info & SPELL_FORBIDDEN) {
159 // FIXME: What to do with that?
160 LYXERR(Debug::FILES, "Hunspell explicit forbidden word found " << word_to_check);
167 void HunspellChecker::insert(WordLangTuple const & wl)
169 string const word_to_check = to_utf8(wl.word());
170 Hunspell * h = d->speller(wl.lang_code());
173 h->add(word_to_check.c_str());
177 void HunspellChecker::accept(WordLangTuple const &)
179 // FIXME: not implemented!
183 void HunspellChecker::suggest(WordLangTuple const & wl,
184 docstring_list & suggestions)
187 string const word_to_check = to_utf8(wl.word());
188 Hunspell * h = d->speller(wl.lang_code());
191 char ** suggestion_list;
192 int const suggestion_number = h->suggest(&suggestion_list, word_to_check.c_str());
193 if (suggestion_number <= 0)
195 for (int i = 0; i != suggestion_number; ++i)
196 suggestions.push_back(from_utf8(suggestion_list[i]));
197 h->free_list(&suggestion_list, suggestion_number);
201 docstring const HunspellChecker::error()