+HunspellChecker::Private::Private()
+{
+ setUserPath(lyxrc.hunspelldir_path);
+}
+
+
+HunspellChecker::Private::~Private()
+{
+ cleanCache();
+}
+
+
+void HunspellChecker::Private::setUserPath(std::string const & path)
+{
+ if (user_path_ != lyxrc.hunspelldir_path) {
+ cleanCache();
+ user_path_ = path;
+ }
+}
+
+
+void HunspellChecker::Private::cleanCache()
+{
+ Spellers::iterator it = spellers_.begin();
+ Spellers::iterator end = spellers_.end();
+
+ for (; it != end; ++it) {
+ delete it->second;
+ it->second = 0;
+ }
+
+ LangPersonalWordList::const_iterator pdit = personal_.begin();
+ LangPersonalWordList::const_iterator pdet = personal_.end();
+
+ for (; pdit != pdet; ++pdit) {
+ if ( 0 == pdit->second)
+ continue;
+ PersonalWordList * pd = pdit->second;
+ pd->save();
+ delete pd;
+ }
+}
+
+
+bool HunspellChecker::Private::haveLanguageFiles(string const & hpath)
+{
+ FileName const affix(hpath + ".aff");
+ FileName const dict(hpath + ".dic");
+ return affix.isReadableFile() && dict.isReadableFile();
+}
+
+
+const string HunspellChecker::Private::dictPath(int selector)
+{
+ switch (selector) {
+ case 4:
+ return hunspellPackageDictDirectory();
+ case 3:
+ return myspellPackageDictDirectory();
+ case 2:
+ return addName(package().system_support().absFileName(),dictDirectory());
+ case 1:
+ return addName(package().user_support().absFileName(),dictDirectory());
+ default:
+ return user_path_;
+ }
+}
+
+
+bool HunspellChecker::Private::haveDictionary(Language const * lang, string & hpath)
+{
+ if (hpath.empty() || !lang)
+ return false;
+
+ if (lookup(lang)) return true;
+
+ string d_name = HunspellDictionaryName(lang);
+
+ LYXERR(Debug::FILES, "check hunspell path: " << hpath
+ << " for language " << lang->lang() << " with name " << d_name);
+
+ string h_path = addName(hpath, d_name);
+ // first we try lang code+variety
+ if (haveLanguageFiles(h_path)) {
+ LYXERR(Debug::FILES, " found " << h_path);
+ hpath = h_path;
+ return true;
+ }
+ // another try with code, '_' replaced by '-'
+ h_path = addName(hpath, subst(lang->code(), '_', '-'));
+ if (!haveLanguageFiles(h_path))
+ return false;
+ LYXERR(Debug::FILES, " found " << h_path);
+ hpath = h_path;
+ return true;
+}
+
+
+bool HunspellChecker::Private::haveDictionary(Language const * lang)
+{
+ bool result = false;
+
+ setUserPath(lyxrc.hunspelldir_path);
+ for (int p = 0; !result && p < maxLookupSelector(); ++p) {
+ string lpath = dictPath(p);
+ result = haveDictionary(lang, lpath);
+ }
+ return result;
+}
+
+
+Hunspell * HunspellChecker::Private::speller(Language const * lang)
+{
+ Hunspell * h = lookup(lang);
+ if (h) return h;
+
+ setUserPath(lyxrc.hunspelldir_path);
+ return addSpeller(lang);
+}
+
+
+Hunspell * HunspellChecker::Private::lookup(Language const * lang)
+{
+ Spellers::iterator it = spellers_.find(lang->lang());
+ return it != spellers_.end() ? it->second : 0;
+}
+
+
+Hunspell * HunspellChecker::Private::addSpeller(Language const * lang,string & path)
+{
+ if (!haveDictionary(lang, path)) {
+ spellers_[lang->lang()] = 0;
+ return 0;
+ }
+
+ FileName const affix(path + ".aff");
+ FileName const dict(path + ".dic");
+ Hunspell * h = new Hunspell(affix.absFileName().c_str(), dict.absFileName().c_str());
+ LYXERR(Debug::FILES, "Hunspell speller for langage " << lang << " at " << dict << " added.");
+ spellers_[lang->lang()] = h;
+ return h;
+}
+
+
+Hunspell * HunspellChecker::Private::addSpeller(Language const * lang)
+{
+ Hunspell * h = 0;
+ for (int p = 0; p < maxLookupSelector() && 0 == h; ++p) {
+ string lpath = dictPath(p);
+ h = addSpeller(lang, lpath);
+ }
+ if (h) {
+ string const encoding = h->get_dic_encoding();
+ PersonalWordList * pd = new PersonalWordList(lang->lang());
+ pd->load();
+ personal_[lang->lang()] = pd;
+ docstring_list::const_iterator it = pd->begin();
+ docstring_list::const_iterator et = pd->end();
+ for (; it != et; ++it) {
+ string const word_to_add = to_iconv_encoding(*it, encoding);
+ h->add(word_to_add.c_str());
+ }
+ }
+ return h;
+}
+
+
+int HunspellChecker::Private::numDictionaries() const
+{
+ int result = 0;
+ Spellers::const_iterator it = spellers_.begin();
+ Spellers::const_iterator et = spellers_.end();
+
+ for (; it != et; ++it)
+ result += it->second != 0;
+ return result;
+}
+
+
+bool HunspellChecker::Private::isIgnored(WordLangTuple const & wl) const
+{
+ IgnoreList::const_iterator it = ignored_.begin();
+ for (; it != ignored_.end(); ++it) {
+ if (it->lang()->code() != wl.lang()->code())
+ continue;
+ if (it->word() == wl.word())
+ return true;
+ }
+ return false;
+}
+
+/// personal word list interface
+void HunspellChecker::Private::remove(WordLangTuple const & wl)
+{
+ Hunspell * h = speller(wl.lang());
+ if (!h)
+ return;
+ string const encoding = h->get_dic_encoding();
+ string const word_to_check = to_iconv_encoding(wl.word(), encoding);
+ h->remove(word_to_check.c_str());
+ PersonalWordList * pd = personal_[wl.lang()->lang()];
+ if (!pd)
+ return;
+ pd->remove(wl.word());
+}
+
+
+void HunspellChecker::Private::insert(WordLangTuple const & wl)
+{
+ Hunspell * h = speller(wl.lang());
+ if (!h)
+ return;
+ string const encoding = h->get_dic_encoding();
+ string const word_to_check = to_iconv_encoding(wl.word(), encoding);
+ h->add(word_to_check.c_str());
+ PersonalWordList * pd = personal_[wl.lang()->lang()];
+ if (!pd)
+ return;
+ pd->insert(wl.word());
+}
+
+
+bool HunspellChecker::Private::learned(WordLangTuple const & wl)