diff --git a/src/HunspellChecker.cpp b/src/HunspellChecker.cpp index c51c0001a6..f024f2ec02 100644 --- a/src/HunspellChecker.cpp +++ b/src/HunspellChecker.cpp @@ -15,20 +15,21 @@ #include "LyXRC.h" #include "WordLangTuple.h" -#include "support/lassert.h" #include "support/debug.h" #include "support/docstring_list.h" +#include "support/FileName.h" +#include "support/gettext.h" +#include "support/lassert.h" +#include "support/os.h" #include #include #include -// FIXME (Abdel): I still got linking problems but if anybody wants -// to try, defines this to 1. -#define TRY_HUNSPELL 0 - using namespace std; +using namespace lyx::support; +using namespace lyx::support::os; namespace lyx { @@ -54,24 +55,40 @@ struct HunspellChecker::Private HunspellChecker::Private::~Private() { -#if TRY_HUNSPELL Spellers::iterator it = spellers_.begin(); Spellers::iterator end = spellers_.end(); for (; it != end; ++it) { delete it->second; } -#endif } Hunspell * HunspellChecker::Private::addSpeller(string const & lang) { - // FIXME: not implemented! + string hunspell_path = external_path(lyxrc.hunspelldir_path); + LYXERR(Debug::FILES, "hunspell path: " << hunspell_path); + if (hunspell_path.empty()) + return false; - // FIXME: We should we indicate somehow that this language is not - // supported. - return 0; + hunspell_path += "/" + lang; + // replace '_' with '-' as this is the convention used by hunspell. + hunspell_path[hunspell_path.size() - 3] = '-'; + FileName const affix(hunspell_path + ".aff"); + FileName const dict(hunspell_path + ".dic"); + if (!affix.isReadableFile()) { + // FIXME: We should indicate somehow that this language is not + // supported. + LYXERR(Debug::FILES, "Hunspell affix file " << affix << " does not exist"); + return 0; + } + if (!dict.isReadableFile()) { + LYXERR(Debug::FILES, "Hunspell dictionary file " << dict << " does not exist"); + return 0; + } + Hunspell * h = new Hunspell(affix.absFilename().c_str(), dict.absFilename().c_str()); + spellers_[lang] = h; + return h; } @@ -99,31 +116,33 @@ HunspellChecker::~HunspellChecker() SpellChecker::Result HunspellChecker::check(WordLangTuple const & wl) { string const word_to_check = to_utf8(wl.word()); -#if TRY_HUNSPELL Hunspell * h = d->speller(wl.lang_code()); + if (!h) + return OK; int info; if (h->spell(word_to_check.c_str(), &info)) return OK; - // FIXME: What to do with that? - switch (info) { - case SPELL_COMPOUND: - case SPELL_FORBIDDEN: - default: - return UNKNOWN_WORD; + + if (info & SPELL_COMPOUND) { + // FIXME: What to do with that? + LYXERR(Debug::FILES, "Hunspell compound word found " << word_to_check); } + if (info & SPELL_FORBIDDEN) { + // FIXME: What to do with that? + LYXERR(Debug::FILES, "Hunspell explicit forbidden word found " << word_to_check); + } + return UNKNOWN_WORD; -#endif - return OK; } void HunspellChecker::insert(WordLangTuple const & wl) { string const word_to_check = to_utf8(wl.word()); -#if TRY_HUNSPELL Hunspell * h = d->speller(wl.lang_code()); + if (!h) + return; h->add(word_to_check.c_str()); -#endif } @@ -138,14 +157,18 @@ void HunspellChecker::suggest(WordLangTuple const & wl, { suggestions.clear(); string const word_to_check = to_utf8(wl.word()); -#if TRY_HUNSPELL Hunspell * h = d->speller(wl.lang_code()); + if (!h) + return; char *** suggestion_list = 0; + + // FIXME: Hunspell::suggest() crashes on Win/MSVC9 + return; + int const suggestion_number = h->suggest(suggestion_list, word_to_check.c_str()); if (suggestion_number == 0) return; h->free_list(suggestion_list, suggestion_number); -#endif } diff --git a/src/LyX.cpp b/src/LyX.cpp index 55cd35ceb5..f09edcdda2 100644 --- a/src/LyX.cpp +++ b/src/LyX.cpp @@ -29,6 +29,7 @@ #include "ErrorList.h" #include "Format.h" #include "FuncStatus.h" +#include "HunspellChecker.h" #include "KeyMap.h" #include "Language.h" #include "LayoutFile.h" @@ -118,22 +119,15 @@ void reconfigureUserLyXDir() } // namespace anon - /// The main application class private implementation. struct LyX::Impl { - Impl() + Impl() : spell_checker_(0), aspell_checker_(0), hunspell_checker_(0) { // Set the default User Interface language as soon as possible. // The language used will be derived from the environment // variables. messages_["GUI"] = Messages(); - -#if defined(USE_ASPELL) - spell_checker_ = new AspellChecker(); -#else - spell_checker_ = 0; -#endif } ~Impl() @@ -184,6 +178,10 @@ struct LyX::Impl graphics::Previews preview_; /// SpellChecker * spell_checker_; + /// + SpellChecker * aspell_checker_; + /// + SpellChecker * hunspell_checker_; }; /// @@ -228,6 +226,7 @@ LyX::LyX() { singleton_ = this; pimpl_ = new Impl; + setSpellChecker(); } @@ -1278,4 +1277,26 @@ SpellChecker * theSpellChecker() return singleton_->pimpl_->spell_checker_; } + +void setSpellChecker() +{ +#if defined(USE_ASPELL) + if (lyxrc.spellchecker == "aspell") { + if (!singleton_->pimpl_->aspell_checker_) + singleton_->pimpl_->aspell_checker_ = new AspellChecker(); + singleton_->pimpl_->spell_checker_ = singleton_->pimpl_->aspell_checker_; + return; + } +#endif +#if defined(USE_HUNSPELL) + if (lyxrc.spellchecker == "hunspell") { + if (!singleton_->pimpl_->hunspell_checker_) + singleton_->pimpl_->hunspell_checker_ = new HunspellChecker(); + singleton_->pimpl_->spell_checker_ = singleton_->pimpl_->hunspell_checker_; + return; + } +#endif + singleton_->pimpl_->spell_checker_ = 0; +} + } // namespace lyx diff --git a/src/LyX.h b/src/LyX.h index 100b593047..6c6db63c0e 100644 --- a/src/LyX.h +++ b/src/LyX.h @@ -139,6 +139,7 @@ private: friend Session & theSession(); friend CmdDef & theTopLevelCmdDef(); friend SpellChecker * theSpellChecker(); + friend void setSpellChecker(); friend void setRcGuiLanguage(); friend void emergencyCleanup(); friend void execBatchCommands(); diff --git a/src/LyXFunc.cpp b/src/LyXFunc.cpp index 8dac8f676b..52ef0102fc 100644 --- a/src/LyXFunc.cpp +++ b/src/LyXFunc.cpp @@ -1590,6 +1590,8 @@ void LyXFunc::dispatch(FuncRequest const & cmd) actOnUpdatedPrefs(lyxrc_orig, lyxrc); + setSpellChecker(); + theApp()->resetGui(); /// We force the redraw in any case because there might be @@ -1969,6 +1971,7 @@ void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new) case LyXRC::RC_FONT_ENCODING: case LyXRC::RC_FORMAT: case LyXRC::RC_GROUP_LAYOUTS: + case LyXRC::RC_HUNSPELLDIR_PATH: case LyXRC::RC_INDEX_ALTERNATIVES: case LyXRC::RC_INDEX_COMMAND: case LyXRC::RC_JBIBTEX_COMMAND: @@ -2036,6 +2039,7 @@ void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new) case LyXRC::RC_SHOW_BANNER: case LyXRC::RC_OPEN_BUFFERS_IN_TABS: case LyXRC::RC_SPELL_COMMAND: + case LyXRC::RC_SPELLCHECKER: case LyXRC::RC_SPELLCHECK_CONTINUOUSLY: case LyXRC::RC_SPLITINDEX_COMMAND: case LyXRC::RC_TEMPDIRPATH: diff --git a/src/LyXRC.cpp b/src/LyXRC.cpp index 00ac217963..0de230eccb 100644 --- a/src/LyXRC.cpp +++ b/src/LyXRC.cpp @@ -99,6 +99,7 @@ LexerKeyword lyxrcTags[] = { { "\\fullscreen_width", LyXRC::RC_FULL_SCREEN_WIDTH }, { "\\group_layouts", LyXRC::RC_GROUP_LAYOUTS }, { "\\gui_language", LyXRC::RC_GUI_LANGUAGE }, + { "\\hunspelldir_path", LyXRC::RC_HUNSPELLDIR_PATH }, { "\\index_alternatives", LyXRC::RC_INDEX_ALTERNATIVES }, { "\\index_command", LyXRC::RC_INDEX_COMMAND }, { "\\input", LyXRC::RC_INPUT }, @@ -168,6 +169,7 @@ LexerKeyword lyxrcTags[] = { { "\\sort_layouts", LyXRC::RC_SORT_LAYOUTS }, { "\\spell_command", LyXRC::RC_SPELL_COMMAND }, { "\\spellcheck_continuously", LyXRC::RC_SPELLCHECK_CONTINUOUSLY }, + { "\\spellchecker", LyXRC::RC_SPELLCHECKER }, { "\\splitindex_command", LyXRC::RC_SPLITINDEX_COMMAND }, { "\\tempdir_path", LyXRC::RC_TEMPDIRPATH }, { "\\template_path", LyXRC::RC_TEMPLATEPATH }, @@ -269,6 +271,7 @@ void LyXRC::setDefaults() backupdir_path.erase(); display_graphics = true; // Spellchecker settings: + spellchecker = "aspell"; spellchecker_accept_compound = false; spellcheck_continuously = false; use_kbmap = false; @@ -704,6 +707,13 @@ int LyXRC::read(Lexer & lexrc) } break; + case RC_HUNSPELLDIR_PATH: + if (lexrc.next()) { + hunspelldir_path = os::internal_path(lexrc.getString()); + hunspelldir_path = expandPath(hunspelldir_path); + } + break; + case RC_USELASTFILEPOS: lexrc >> use_lastfilepos; break; @@ -875,6 +885,9 @@ int LyXRC::read(Lexer & lexrc) case RC_USE_PIXMAP_CACHE: lexrc >> use_pixmap_cache; break; + case RC_SPELLCHECKER: + lexrc >> spellchecker; + break; case RC_ALT_LANG: lexrc >> spellchecker_alt_lang; break; @@ -2132,6 +2145,14 @@ void LyXRC::write(ostream & os, bool ignore_system_lyxrc, string const & name) c } if (tag != RC_LAST) break; + case RC_HUNSPELLDIR_PATH: + if (ignore_system_lyxrc || + hunspelldir_path != system_lyxrc.hunspelldir_path) { + string const path = os::external_path(hunspelldir_path); + os << "\\hunspelldir_path \"" << path << "\"\n"; + } + if (tag != RC_LAST) + break; case RC_USETEMPDIR: if (tag != RC_LAST) break; @@ -2242,6 +2263,14 @@ void LyXRC::write(ostream & os, bool ignore_system_lyxrc, string const & name) c if (tag != RC_LAST) break; + case RC_SPELLCHECKER: + if (ignore_system_lyxrc || + spellchecker != system_lyxrc.spellchecker) { + os << "\\spellchecker " << spellchecker << '\n'; + } + if (tag != RC_LAST) + break; + case RC_SPELLCHECK_CONTINUOUSLY: if (ignore_system_lyxrc || spellcheck_continuously != system_lyxrc.spellcheck_continuously) { diff --git a/src/LyXRC.h b/src/LyXRC.h index bc54504805..79f4fbcf15 100644 --- a/src/LyXRC.h +++ b/src/LyXRC.h @@ -84,6 +84,7 @@ public: RC_GEOMETRY_SESSION, RC_GROUP_LAYOUTS, RC_GUI_LANGUAGE, + RC_HUNSPELLDIR_PATH, RC_INDEX_ALTERNATIVES, RC_INDEX_COMMAND, RC_INPUT, @@ -153,6 +154,7 @@ public: RC_SORT_LAYOUTS, RC_SPELL_COMMAND, RC_SPELLCHECK_CONTINUOUSLY, + RC_SPELLCHECKER, RC_SPLITINDEX_COMMAND, RC_TEMPDIRPATH, RC_TEMPLATEPATH, @@ -276,6 +278,8 @@ public: /// std::string thesaurusdir_path; /// + std::string hunspelldir_path; + /// bool auto_region_delete; /// flag telling whether lastfiles should be checked for existance bool auto_reset_options; @@ -328,6 +332,8 @@ public: bool use_tooltip; /// Use pixmap cache? bool use_pixmap_cache; + /// Spellchecker engine: aspell, hunspell, etc + std::string spellchecker; /// Alternate language for spellchecker std::string spellchecker_alt_lang; /// Escape characters diff --git a/src/SpellChecker.h b/src/SpellChecker.h index c6e3748c84..6d8c77df00 100644 --- a/src/SpellChecker.h +++ b/src/SpellChecker.h @@ -64,6 +64,10 @@ public: /// Implemented in LyX.cpp SpellChecker * theSpellChecker(); +/// Set the singleton SpellChecker engine. +/// Implemented in LyX.cpp +void setSpellChecker(); + } // namespace lyx #endif // SPELL_BASE_H diff --git a/src/frontends/qt4/GuiPrefs.cpp b/src/frontends/qt4/GuiPrefs.cpp index 0789a964d1..aaeaa73042 100644 --- a/src/frontends/qt4/GuiPrefs.cpp +++ b/src/frontends/qt4/GuiPrefs.cpp @@ -34,6 +34,7 @@ #include "PanelStack.h" #include "paper.h" #include "Session.h" +#include "SpellChecker.h" #include "support/debug.h" #include "support/FileName.h" @@ -1129,6 +1130,7 @@ PrefPaths::PrefPaths(GuiPreferences * form) connect(workingDirPB, SIGNAL(clicked()), this, SLOT(selectWorkingdir())); connect(lyxserverDirPB, SIGNAL(clicked()), this, SLOT(selectLyxPipe())); connect(thesaurusDirPB, SIGNAL(clicked()), this, SLOT(selectThesaurusdir())); + connect(hunspellDirPB, SIGNAL(clicked()), this, SLOT(selectHunspelldir())); connect(workingDirED, SIGNAL(textChanged(QString)), this, SIGNAL(changed())); connect(exampleDirED, SIGNAL(textChanged(QString)), @@ -1156,6 +1158,7 @@ void PrefPaths::apply(LyXRC & rc) const rc.backupdir_path = internal_path(fromqstr(backupDirED->text())); rc.tempdir_path = internal_path(fromqstr(tempDirED->text())); rc.thesaurusdir_path = internal_path(fromqstr(thesaurusDirED->text())); + rc.hunspelldir_path = internal_path(fromqstr(hunspellDirED->text())); rc.path_prefix = internal_path_list(fromqstr(pathPrefixED->text())); // FIXME: should be a checkbox only rc.lyxpipes = internal_path(fromqstr(lyxserverDirED->text())); @@ -1170,6 +1173,7 @@ void PrefPaths::update(LyXRC const & rc) backupDirED->setText(toqstr(external_path(rc.backupdir_path))); tempDirED->setText(toqstr(external_path(rc.tempdir_path))); thesaurusDirED->setText(toqstr(external_path(rc.thesaurusdir_path))); + hunspellDirED->setText(toqstr(external_path(rc.hunspelldir_path))); pathPrefixED->setText(toqstr(external_path_list(rc.path_prefix))); // FIXME: should be a checkbox only lyxserverDirED->setText(toqstr(external_path(rc.lyxpipes))); @@ -1230,6 +1234,15 @@ void PrefPaths::selectThesaurusdir() } +void PrefPaths::selectHunspelldir() +{ + QString file = browseDir(internalPath(hunspellDirED->text()), + qt_("Set the path to the Hunspell dictionaries")); + if (!file.isEmpty()) + hunspellDirED->setText(file); +} + + void PrefPaths::selectLyxPipe() { QString file = form_->browse(internalPath(lyxserverDirED->text()), @@ -1250,6 +1263,11 @@ PrefSpellchecker::PrefSpellchecker(GuiPreferences * form) { setupUi(this); + spellcheckerCB->addItem("aspell"); + spellcheckerCB->addItem("hunspell"); + + connect(spellcheckerCB, SIGNAL(currentIndexChanged(int)), + this, SIGNAL(changed())); connect(altLanguageED, SIGNAL(textChanged(QString)), this, SIGNAL(changed())); connect(escapeCharactersED, SIGNAL(textChanged(QString)), @@ -1263,6 +1281,7 @@ PrefSpellchecker::PrefSpellchecker(GuiPreferences * form) void PrefSpellchecker::apply(LyXRC & rc) const { + rc.spellchecker = fromqstr(spellcheckerCB->currentText()); rc.spellchecker_alt_lang = fromqstr(altLanguageED->text()); rc.spellchecker_esc_chars = fromqstr(escapeCharactersED->text()); rc.spellchecker_accept_compound = compoundWordCB->isChecked(); @@ -1272,6 +1291,8 @@ void PrefSpellchecker::apply(LyXRC & rc) const void PrefSpellchecker::update(LyXRC const & rc) { + spellcheckerCB->setCurrentIndex(spellcheckerCB->findText( + toqstr(rc.spellchecker))); altLanguageED->setText(toqstr(rc.spellchecker_alt_lang)); escapeCharactersED->setText(toqstr(rc.spellchecker_esc_chars)); compoundWordCB->setChecked(rc.spellchecker_accept_compound); diff --git a/src/frontends/qt4/GuiPrefs.h b/src/frontends/qt4/GuiPrefs.h index 737d37812f..5e5d03a9c7 100644 --- a/src/frontends/qt4/GuiPrefs.h +++ b/src/frontends/qt4/GuiPrefs.h @@ -300,6 +300,7 @@ private Q_SLOTS: void selectBackupdir(); void selectWorkingdir(); void selectThesaurusdir(); + void selectHunspelldir(); void selectLyxPipe(); }; diff --git a/src/frontends/qt4/ui/PrefPathsUi.ui b/src/frontends/qt4/ui/PrefPathsUi.ui index d288117afc..cfbeb3fece 100644 --- a/src/frontends/qt4/ui/PrefPathsUi.ui +++ b/src/frontends/qt4/ui/PrefPathsUi.ui @@ -19,7 +19,7 @@ 6 - + Qt::Vertical @@ -27,7 +27,7 @@ QSizePolicy::Expanding - + 329 16 @@ -35,7 +35,7 @@ - + &PATH prefix: @@ -45,7 +45,7 @@ - + @@ -209,6 +209,29 @@ + + + + Hunspell dictionaries: + + + thesaurusDirED + + + + + + + + + + Browse... + + + false + + + diff --git a/src/frontends/qt4/ui/PrefSpellcheckerUi.ui b/src/frontends/qt4/ui/PrefSpellcheckerUi.ui index ed24fcb6e5..6c77e7270a 100644 --- a/src/frontends/qt4/ui/PrefSpellcheckerUi.ui +++ b/src/frontends/qt4/ui/PrefSpellcheckerUi.ui @@ -19,7 +19,7 @@ 6 - + Al&ternative language: @@ -29,14 +29,14 @@ - + Override the language used for the spellchecker - + &Escape characters: @@ -46,14 +46,14 @@ - + The characters inserted here are ignored by the spellchecker. - + Qt::Horizontal @@ -61,7 +61,7 @@ QSizePolicy::Expanding - + 41 22 @@ -69,7 +69,7 @@ - + Mark misspelled words with a wavy underline. @@ -79,7 +79,7 @@ - + Qt::Vertical @@ -87,7 +87,7 @@ QSizePolicy::Expanding - + 20 20 @@ -95,7 +95,7 @@ - + Accept words such as "diskdrive" @@ -105,6 +105,19 @@ + + + + + + + &Spellchecker engine: + + + altLanguageED + + +