From 7746747506617b952e04e2e805c3688f4fef5256 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20Spitzm=C3=BCller?= Date: Fri, 22 Jan 2010 15:26:38 +0000 Subject: [PATCH] * add support for the enchant spell checker (bug 6226). SCons might be broken. git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@33157 a592a061-630c-0410-9148-cb99ea01b6c8 --- config/spell.m4 | 25 +++++ development/scons/SConstruct | 16 ++- development/scons/scons_manifest.py | 2 + src/EnchantChecker.cpp | 163 ++++++++++++++++++++++++++++ src/EnchantChecker.h | 49 +++++++++ src/LyX.cpp | 14 ++- src/Makefile.am | 9 +- src/frontends/qt4/GuiPrefs.cpp | 14 ++- 8 files changed, 282 insertions(+), 10 deletions(-) create mode 100644 src/EnchantChecker.cpp create mode 100644 src/EnchantChecker.h diff --git a/config/spell.m4 b/config/spell.m4 index adce739c94..a729949071 100644 --- a/config/spell.m4 +++ b/config/spell.m4 @@ -23,6 +23,26 @@ AC_DEFUN([CHECK_WITH_ASPELL], fi ]) +# Macro to add for using enchant spellchecker libraries! -*- sh -*- +AC_DEFUN([CHECK_WITH_ENCHANT], +[ + lyx_use_enchant=true + AC_ARG_WITH(enchant, AC_HELP_STRING([--without-enchant],[do not check for Enchant library])) + test "$with_enchant" = "no" && lyx_use_enchant=false + + if $lyx_use_enchant; then + PKG_CHECK_MODULES([ENCHANT], [enchant], [], [lyx_use_enchant=false]) + AC_MSG_CHECKING([whether to use enchant]) + if $lyx_use_enchant ; then + AC_MSG_RESULT(yes) + AC_DEFINE(USE_ENCHANT, 1, [Define as 1 to use the enchant library]) + lyx_flags="$lyx_flags use-enchant" + else + AC_MSG_RESULT(no) + fi + fi + ]) + # Macro to add for using hunspell spellchecker libraries! -*- sh -*- AC_DEFUN([CHECK_WITH_HUNSPELL], [ @@ -55,6 +75,11 @@ AC_DEFUN([LYX_CHECK_SPELL_ENGINES], AM_CONDITIONAL(USE_ASPELL, $lyx_use_aspell) + lyx_use_enchant=false + CHECK_WITH_ENCHANT + + AM_CONDITIONAL(USE_ENCHANT, $lyx_use_enchant) + lyx_use_hunspell=false CHECK_WITH_HUNSPELL diff --git a/development/scons/SConstruct b/development/scons/SConstruct index 176b6e05d8..e28e6f1de3 100644 --- a/development/scons/SConstruct +++ b/development/scons/SConstruct @@ -136,7 +136,7 @@ opts.AddVariables( ) ), # EnumVariable('spell', 'Choose spell checker to use.', 'auto', - allowed_values = ('aspell', 'hunspell', 'auto', 'no') ), + allowed_values = ('aspell', 'enchant', 'hunspell', 'auto', 'no') ), # packaging method EnumVariable('packaging', 'Packaging method to use.', default_packaging_method, allowed_values = ('windows', 'posix', 'macosx')), @@ -810,8 +810,14 @@ else: # determine headers to use spell_opt = ARGUMENTS.get('spell', 'auto') env['USE_ASPELL'] = False +env['USE_ENCHANT'] = False +env['USE_HUNSPELL'] = False if spell_opt in ['auto', 'aspell'] and conf.CheckLib(aspell_lib): spell_engine = 'USE_ASPELL' +elif spell_opt in ['auto', 'enchant'] and conf.CheckLib('enchant'): + spell_engine = 'USE_ENCHANT' +elif spell_opt in ['auto', 'hunspell'] and conf.CheckLib('enchant'): + spell_engine = 'USE_HUNSPELL' else: spell_engine = None @@ -1069,7 +1075,7 @@ char * strerror(int n); ) # these keys are needed in env -for key in ['USE_ASPELL', 'HAVE_FCNTL',\ +for key in ['USE_ASPELL', 'USE_ENCHANT', 'USE_HUNSPELL', 'HAVE_FCNTL',\ 'HAVE_LIBGDI32', 'HAVE_LIBAIKSAURUS', 'AIKSAURUS_LIB']: # USE_ASPELL etc does not go through result if result.has_key(key): @@ -1227,6 +1233,8 @@ libs = [ ('HAVE_LIBGDI32', 'gdi32'), ('HAVE_LIBAIKSAURUS', env['AIKSAURUS_LIB']), ('USE_ASPELL', aspell_lib), + ('USE_ENCHANT', 'enchant'), + ('USE_HUNSPELL', 'hunspell') ] for lib in libs: @@ -1547,6 +1555,10 @@ Alias('tex2lyx', tex2lyx) # if env.has_key('USE_ASPELL') and env['USE_ASPELL']: src_post_files.append('AspellChecker.cpp') +elif env.has_key('USE_ENCHANT') and env['USE_ENCHANT']: + src_post_files.append('EnchantChecker.cpp') +elif env.has_key('USE_HUNSPELL') and env['USE_HUNSPELL']: + src_post_files.append('HunspellChecker.cpp') # tells scons how to get these moced files, although not all moced files are needed # (or are actually generated). diff --git a/development/scons/scons_manifest.py b/development/scons/scons_manifest.py index 962e284016..166c16e485 100644 --- a/development/scons/scons_manifest.py +++ b/development/scons/scons_manifest.py @@ -60,6 +60,7 @@ src_header_files = Split(''' Dimension.h DispatchResult.h DocIterator.h + EnchantChecker.h Encoding.h ErrorList.h Exporter.h @@ -251,6 +252,7 @@ src_post_files = Split(''' src_extra_src_files = Split(''' AspellChecker.cpp + EnchantChecker.cpp HunspellChecker.cpp main.cpp Section.cpp diff --git a/src/EnchantChecker.cpp b/src/EnchantChecker.cpp new file mode 100644 index 0000000000..23cbee923e --- /dev/null +++ b/src/EnchantChecker.cpp @@ -0,0 +1,163 @@ +/** + * \file EnchantChecker.cpp + * This file is part of LyX, the document processor. + * Licence details can be found in the file COPYING. + * + * \author Caolán McNamara + * \author Jürgen Spitzmüller + * + * Full author contact details are available in file CREDITS. + */ + +#include + +#include + +#include "EnchantChecker.h" +#include "LyXRC.h" +#include "WordLangTuple.h" + +#include "support/lassert.h" +#include "support/debug.h" +#include "support/docstring_list.h" + +#include +#include + +using namespace std; + +namespace lyx { + +namespace { + +struct Speller { + enchant::Dict * speller; +}; + +typedef std::map Spellers; + +} // anon namespace + +struct EnchantChecker::Private +{ + Private() {} + + ~Private(); + + /// add a speller of the given language + enchant::Dict * addSpeller(string const & lang); + + /// + enchant::Dict * speller(string const & lang); + + /// the spellers + Spellers spellers_; +}; + + +EnchantChecker::Private::~Private() +{ + Spellers::iterator it = spellers_.begin(); + Spellers::iterator end = spellers_.end(); + + for (; it != end; ++it) { + delete it->second.speller; + } +} + + +enchant::Dict * EnchantChecker::Private::addSpeller(string const & lang) +{ + enchant::Broker * instance = enchant::Broker::instance(); + enchant::Dict * dict = instance->request_dict(lang); + + if (dict) { + Speller m; + m.speller = dict; + spellers_[lang] = m; + return m.speller; + } + // FIXME error handling? + return 0; +} + + +enchant::Dict * EnchantChecker::Private::speller(string const & lang) +{ + Spellers::iterator it = spellers_.find(lang); + if (it != spellers_.end()) + return it->second.speller; + + return addSpeller(lang); +} + + +EnchantChecker::EnchantChecker(): d(new Private) +{ +} + + +EnchantChecker::~EnchantChecker() +{ + delete d; +} + + +SpellChecker::Result EnchantChecker::check(WordLangTuple const & word) +{ + enchant::Dict * m = d->speller(word.lang_code()); + + if (!m) + return OK; + + std::string utf8word(to_utf8(word.word())); + + if (m->check(utf8word)) + return OK; + + return UNKNOWN_WORD; +} + + +void EnchantChecker::insert(WordLangTuple const & word) +{ + Spellers::iterator it = d->spellers_.find(word.lang_code()); + if (it != d->spellers_.end()) + it->second.speller->add(to_utf8(word.word())); +} + + +void EnchantChecker::accept(WordLangTuple const & word) +{ + Spellers::iterator it = d->spellers_.find(word.lang_code()); + if (it != d->spellers_.end()) + it->second.speller->add_to_session(to_utf8(word.word())); +} + + +void EnchantChecker::suggest(WordLangTuple const & wl, + docstring_list & suggestions) +{ + suggestions.clear(); + enchant::Dict * m = d->speller(wl.lang_code()); + + if (!m) + return; + + string utf8word = to_utf8(wl.word()); + + vector suggs = m->suggest(utf8word); + vector::const_iterator it = suggs.begin(); + + for (; it != suggs.end(); ++it) + suggestions.push_back(from_utf8(*it)); +} + + +docstring const EnchantChecker::error() +{ + return docstring(); +} + + +} // namespace lyx diff --git a/src/EnchantChecker.h b/src/EnchantChecker.h new file mode 100644 index 0000000000..e0954bc9e2 --- /dev/null +++ b/src/EnchantChecker.h @@ -0,0 +1,49 @@ +// -*- C++ -*- +/** + * \file EnchantChecker.h + * This file is part of LyX, the document processor. + * Licence details can be found in the file COPYING. + * + * \author Caolán McNamara + * \author Jürgen Spitzmüller + * + * Full author contact details are available in file CREDITS. + */ + +#ifndef LYX_ENCHANT_H +#define LYX_ENCHANT_H + +#include "SpellChecker.h" + +namespace enchant { + class Dict; +} + +namespace lyx { + +class BufferParams; + + +class EnchantChecker : public SpellChecker { +public: + EnchantChecker(); + ~EnchantChecker(); + + /// SpellChecker inherited methods. + ///@{ + enum Result check(WordLangTuple const &); + void suggest(WordLangTuple const &, docstring_list &); + void insert(WordLangTuple const &); + void accept(WordLangTuple const &); + docstring const error(); + ///@} + +private: + struct Private; + Private * d; +}; + + +} // namespace lyx + +#endif // LYX_ENCHANT_H diff --git a/src/LyX.cpp b/src/LyX.cpp index 7b5c07636f..02759cad54 100644 --- a/src/LyX.cpp +++ b/src/LyX.cpp @@ -25,6 +25,7 @@ #include "ConverterCache.h" #include "Converter.h" #include "CutAndPaste.h" +#include "EnchantChecker.h" #include "Encoding.h" #include "ErrorList.h" #include "Format.h" @@ -122,7 +123,7 @@ void reconfigureUserLyXDir() /// The main application class private implementation. struct LyX::Impl { - Impl() : spell_checker_(0), aspell_checker_(0), hunspell_checker_(0) + Impl() : spell_checker_(0), aspell_checker_(0), enchant_checker_(0), hunspell_checker_(0) { // Set the default User Interface language as soon as possible. // The language used will be derived from the environment @@ -133,6 +134,7 @@ struct LyX::Impl ~Impl() { delete aspell_checker_; + delete enchant_checker_; delete hunspell_checker_; } @@ -182,6 +184,8 @@ struct LyX::Impl /// SpellChecker * aspell_checker_; /// + SpellChecker * enchant_checker_; + /// SpellChecker * hunspell_checker_; }; @@ -1288,6 +1292,14 @@ void setSpellChecker() return; } #endif +#if defined(USE_ENCHANT) + if (lyxrc.spellchecker == "enchant") { + if (!singleton_->pimpl_->enchant_checker_) + singleton_->pimpl_->enchant_checker_ = new EnchantChecker(); + singleton_->pimpl_->spell_checker_ = singleton_->pimpl_->enchant_checker_; + return; + } +#endif #if defined(USE_HUNSPELL) if (lyxrc.spellchecker == "hunspell") { if (!singleton_->pimpl_->hunspell_checker_) diff --git a/src/Makefile.am b/src/Makefile.am index 989438df0d..54b8d65308 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -4,7 +4,7 @@ include $(top_srcdir)/config/common.am DISTCLEANFILES += config.h libintl.h -AM_CPPFLAGS += $(PCH_FLAGS) -I$(top_srcdir)/src $(BOOST_INCLUDES) +AM_CPPFLAGS += $(PCH_FLAGS) -I$(top_srcdir)/src $(BOOST_INCLUDES) $(ENCHANT_CFLAGS) AM_CPPFLAGS += $(QT4_CPPFLAGS) $(QT4_CORE_INCLUDES) if BUILD_CLIENT_SUBDIR @@ -21,7 +21,7 @@ EXTRA_DIST = Section.h \ pch.h OTHERLIBS = $(BOOST_LIBS) $(INTLLIBS) $(MYTHES_LIBS) $(AIKSAURUS_LIBS) \ - @LIBS@ $(SOCKET_LIBS) $(LIBSHLWAPI) $(LIBPSAPI) + $(ENCHANT_LIBS) @LIBS@ $(SOCKET_LIBS) $(LIBSHLWAPI) $(LIBPSAPI) noinst_LIBRARIES = liblyxcore.a bin_PROGRAMS = lyx @@ -52,6 +52,10 @@ if USE_ASPELL ASPELL = AspellChecker.cpp AspellChecker.h endif +if USE_ENCHANT +ENCHANT = EnchantChecker.cpp EnchantChecker.h +endif + if USE_HUNSPELL HUNSPELL = HunspellChecker.cpp HunspellChecker.h endif @@ -71,6 +75,7 @@ lyx_SOURCES = \ Compare.h \ Dimension.cpp \ Dimension.h \ + $(ENCHANT) \ $(HUNSPELL) \ PrinterParams.cpp \ PrinterParams.h \ diff --git a/src/frontends/qt4/GuiPrefs.cpp b/src/frontends/qt4/GuiPrefs.cpp index ea71bfdfce..4518838456 100644 --- a/src/frontends/qt4/GuiPrefs.cpp +++ b/src/frontends/qt4/GuiPrefs.cpp @@ -1312,10 +1312,13 @@ PrefSpellchecker::PrefSpellchecker(GuiPreferences * form) setupUi(this); #if defined(USE_ASPELL) - spellcheckerCB->addItem("aspell"); + spellcheckerCB->addItem(qt_("aspell"), QString("aspell")); +#endif +#if defined(USE_ENCHANT) + spellcheckerCB->addItem(qt_("enchant"), QString("enchant")); #endif #if defined(USE_HUNSPELL) - spellcheckerCB->addItem("hunspell"); + spellcheckerCB->addItem(qt_("hunspell"), QString("hunspell")); #endif if (theSpellChecker()) { @@ -1341,7 +1344,8 @@ PrefSpellchecker::PrefSpellchecker(GuiPreferences * form) void PrefSpellchecker::apply(LyXRC & rc) const { - rc.spellchecker = fromqstr(spellcheckerCB->currentText()); + rc.spellchecker = fromqstr(spellcheckerCB->itemData( + spellcheckerCB->currentIndex()).toString()); rc.spellchecker_alt_lang = fromqstr(altLanguageED->text()); rc.spellchecker_esc_chars = fromqstr(escapeCharactersED->text()); rc.spellchecker_accept_compound = compoundWordCB->isChecked(); @@ -1351,8 +1355,8 @@ void PrefSpellchecker::apply(LyXRC & rc) const void PrefSpellchecker::update(LyXRC const & rc) { - spellcheckerCB->setCurrentIndex(spellcheckerCB->findText( - toqstr(rc.spellchecker))); + spellcheckerCB->setCurrentIndex( + spellcheckerCB->findData(toqstr(rc.spellchecker))); altLanguageED->setText(toqstr(rc.spellchecker_alt_lang)); escapeCharactersED->setText(toqstr(rc.spellchecker_esc_chars)); compoundWordCB->setChecked(rc.spellchecker_accept_compound);