]> git.lyx.org Git - lyx.git/blobdiff - src/frontends/controllers/ControlSpellchecker.C
fix crash due to invalidated iterator
[lyx.git] / src / frontends / controllers / ControlSpellchecker.C
index 9aa770f043e724f4063735607e6182c86c2d4b5a..7e0c88d281d247a926c71ca8c48e638e65ac2387 100644 (file)
 #include <config.h>
 
 #include "ControlSpellchecker.h"
-#include "ViewBase.h"
 
 #include "buffer.h"
 #include "bufferparams.h"
 #include "BufferView.h"
-#include "bufferview_funcs.h"
+#include "cursor.h"
+#include "CutAndPaste.h"
 #include "debug.h"
 #include "gettext.h"
 #include "language.h"
 #include "lyxrc.h"
-
-#include "PosIterator.h"
 #include "paragraph.h"
 
-#include "ispell.h"
-#ifdef USE_PSPELL
-# include "pspell.h"
-#else
-#ifdef USE_ASPELL
+#if defined(USE_ASPELL)
 # include "aspell_local.h"
+#elif defined(USE_PSPELL)
+# include "pspell.h"
 #endif
+
+#if defined(USE_ISPELL)
+# include "ispell.h"
+#else
+# include "SpellBase.h"
 #endif
 
-#include "support/tostr.h"
+#include "support/textutils.h"
+#include "support/convert.h"
 
 #include "frontends/Alert.h"
 
-
-using lyx::support::bformat;
-
 using std::advance;
 using std::distance;
 using std::endl;
 using std::string;
 
+namespace lyx {
+
+using support::bformat;
+using support::contains;
+
+namespace frontend {
+
 
-ControlSpellchecker::ControlSpellchecker(LyXView & lv, Dialogs & d)
-       : ControlDialogBD(lv, d),
+ControlSpellchecker::ControlSpellchecker(Dialog & parent)
+       : Dialog::Controller(parent), exitEarly_(false),
          oldval_(0), newvalue_(0), count_(0)
 {}
 
@@ -57,158 +63,157 @@ ControlSpellchecker::~ControlSpellchecker()
 {}
 
 
-void ControlSpellchecker::setParams()
-{
-       lyxerr[Debug::GUI] << "spell setParams" << endl;
-       startSession();
-}
-
-
-void ControlSpellchecker::clearParams()
-{
-       lyxerr[Debug::GUI] << "spell clearParams" << endl;
-       endSession();
-}
-
-
 namespace {
 
-
 SpellBase * getSpeller(BufferParams const & bp)
 {
        string lang = (lyxrc.isp_use_alt_lang)
                      ? lyxrc.isp_alt_lang
                      : bp.language->code();
 
-#ifdef USE_ASPELL
+#if defined(USE_ASPELL)
        if (lyxrc.use_spell_lib)
                return new ASpell(bp, lang);
-#endif
-#ifdef USE_PSPELL
+#elif defined(USE_PSPELL)
        if (lyxrc.use_spell_lib)
                return new PSpell(bp, lang);
 #endif
 
+#if defined(USE_ISPELL)
        lang = (lyxrc.isp_use_alt_lang) ?
                lyxrc.isp_alt_lang : bp.language->lang();
 
        return new ISpell(bp, lang);
+#else
+       return new SpellBase;
+#endif
 }
 
-}
+} // namespace anon
 
 
-void ControlSpellchecker::startSession()
+bool ControlSpellchecker::initialiseParams(std::string const &)
 {
-       lyxerr[Debug::GUI] << "spell startSession" << endl;
+       lyxerr[Debug::GUI] << "Spellchecker::initialiseParams" << endl;
 
-       if (speller_.get()) {
-               lyxerr[Debug::GUI] << "startSession: speller exists" << endl;
-               speller_.reset(0);
-               return;
-       }
-
-       speller_.reset(getSpeller(buffer()->params()));
+       speller_.reset(getSpeller(kernel().buffer().params()));
+       if (!speller_.get())
+               return false;
 
        // reset values to initial
        oldval_ = 0;
        newvalue_ = 0;
        count_ = 0;
-       emergency_exit_ = false;
 
-       // start off the check
-       if (speller_->error().empty()) {
-               check();
-               return;
-       }
+       bool const success = speller_->error().empty();
 
-       emergency_exit_ = true;
-       string message = speller_->error();
-       if (message.empty())
-               message = _("The spell-checker could not be started.\n"
-                        "Maybe it is mis-configured.");
+       if (!success) {
+               Alert::error(_("Spellchecker error"),
+                            _("The spellchecker could not be started\n")
+                            + speller_->error());
+               speller_.reset(0);
+       }
 
-       Alert::error(_("The spell-checker has failed"), message);
-       speller_.reset(0);
+       return success;
 }
 
 
-void ControlSpellchecker::endSession()
+void ControlSpellchecker::clearParams()
 {
-       lyxerr[Debug::GUI] << "spell endSession" << endl;
-
-       emergency_exit_ = true;
-
-       if (!speller_.get()) {
-               lyxerr[Debug::GUI] << "endSession with no speller" << endl;
-               return;
-       }
-
+       lyxerr[Debug::GUI] << "Spellchecker::clearParams" << endl;
        speller_.reset(0);
 }
 
 
 namespace {
 
-
-bool isLetter(PosIterator & cur)
+bool isLetter(DocIterator const & cur)
 {
-       return !cur.at_end()
-               && cur.pit()->isLetter(cur.pos())
-               && !isDeletedText(*cur.pit(), cur.pos())
-               && (!cur.inset() || cur.inset()->allowSpellCheck());
+       return cur.inTexted()
+               && cur.inset().allowSpellCheck()
+               && cur.pos() != cur.lastpos()
+               && (cur.paragraph().isLetter(cur.pos())
+                   // We want to pass the ' and escape chars to ispell
+                   || contains(lyxrc.isp_esc_chars + '\'',
+                               cur.paragraph().getChar(cur.pos())))
+               && !isDeletedText(cur.paragraph(), cur.pos());
 }
 
 
-WordLangTuple nextWord(PosIterator & cur, PosIterator const & end,
-                      int & progress, BufferParams & bp)
+WordLangTuple nextWord(DocIterator & cur, ptrdiff_t & progress,
+       BufferParams & bp)
 {
-       // skip until we have real text (will jump paragraphs)
-       for (; cur != end && !isLetter(cur); ++cur, ++progress);
-
-       if (cur == end)
-               return WordLangTuple(string(), string());
-
-       string lang_code = cur.pit()->getFontSettings(bp, cur.pos()).language()->code();
-       string str;
-       // and find the end of the word (insets like optional hyphens
-       // and ligature break are part of a word)
-       for (; cur != end && isLetter(cur); ++cur, ++progress) {
-               if (!cur.pit()->isInset(cur.pos()))
-                       str += cur.pit()->getChar(cur.pos());
+       bool inword = false;
+       bool ignoreword = false;
+       string word, lang_code;
+
+       while (cur.depth()) {
+               if (isLetter(cur)) {
+                       if (!inword) {
+                               inword = true;
+                               ignoreword = false;
+                               word.clear();
+                               lang_code = cur.paragraph().getFontSettings(bp, cur.pos()).language()->code();
+                       }
+                       // Insets like optional hyphens and ligature
+                       // break are part of a word.
+                       if (!cur.paragraph().isInset(cur.pos())) {
+                               Paragraph::value_type const c =
+                                       cur.paragraph().getChar(cur.pos());
+                               word += c;
+                               if (IsDigit(c))
+                                       ignoreword = true;
+                       }
+               } else { // !isLetter(cur)
+                       if (inword)
+                               if (!word.empty() && !ignoreword)
+                                       return WordLangTuple(word, lang_code);
+                               else
+                                       inword = false;
+               }
+
+               cur.forwardPos();
+               ++progress;
        }
 
-       return WordLangTuple(str, lang_code);
+       return WordLangTuple(string(), string());
 }
 
-
-} //namespace anon
-
+} // namespace anon
 
 
 
 void ControlSpellchecker::check()
 {
-       lyxerr[Debug::GUI] << "spell check a word" << endl;
+       lyxerr[Debug::GUI] << "Check the spelling of a word" << endl;
 
        SpellBase::Result res = SpellBase::OK;
 
-       PosIterator cur(*bufferview());
-       PosIterator const beg = buffer()->pos_iterator_begin();
-       PosIterator const end = buffer()->pos_iterator_end();
+       DocIterator cur = kernel().bufferview()->cursor();
+       while (cur && cur.pos() && isLetter(cur)) {
+               cur.backwardPos();
+       }
 
-       PosIterator::difference_type start = distance(beg, cur);
-       PosIterator::difference_type const total = start + distance(cur, end);
+       ptrdiff_t start = 0, total = 0;
+       DocIterator it = DocIterator(kernel().buffer().inset());
+       for (start = 0; it != cur; it.forwardPos())
+               ++start;
 
-       if (cur != buffer()->pos_iterator_begin())
-               for (; cur != end && isLetter(cur); ++cur, ++start);
+       for (total = start; it; it.forwardPos())
+               ++total;
 
-       while (res == SpellBase::OK || res == SpellBase::IGNORE) {
-               word_ = nextWord(cur, end, start, buffer()->params());
+       BufferParams & bufferparams = kernel().buffer().params();
+       exitEarly_ = false;
+
+       while (res == SpellBase::OK || res == SpellBase::IGNORED_WORD) {
+               word_ = nextWord(cur, start, bufferparams);
 
                // end of document
-               if (word_.word().empty())
-                       break;
+               if (getWord().empty()) {
+                       showSummary();
+                       exitEarly_ = true;
+                       return;
+               }
 
                ++count_;
 
@@ -219,7 +224,7 @@ void ControlSpellchecker::check()
                        lyxerr[Debug::GUI] << "Updating spell progress." << endl;
                        oldval_ = newvalue_;
                        // set progress bar
-                       view().partialUpdate(SPELL_PROGRESSED);
+                       dialog().view().partialUpdate(SPELL_PROGRESSED);
                }
 
                // speller might be dead ...
@@ -233,23 +238,19 @@ void ControlSpellchecker::check()
                        return;
        }
 
-       lyxerr[Debug::GUI] << "Found word \"" << word_.word() << "\"" << endl;
+       lyxerr[Debug::GUI] << "Found word \"" << getWord() << "\"" << endl;
 
-       if (!word_.word().empty()) {
-               int const size = word_.word().size();
-               advance(cur, -size);
-               bufferview()->putSelectionAt(cur, size, false);
-               advance(cur, size);
-       } else {
-               showSummary();
-               endSession();
-               return;
-       }
+       int const size = getWord().size();
+       cur.pos() -= size;
+       kernel().bufferview()->putSelectionAt(cur, size, false);
+       // if we used a lfun like in find/replace, dispatch would do
+       // that for us
+       kernel().bufferview()->update();
 
        // set suggestions
-       if (res != SpellBase::OK && res != SpellBase::IGNORE) {
+       if (res != SpellBase::OK && res != SpellBase::IGNORED_WORD) {
                lyxerr[Debug::GUI] << "Found a word needing checking." << endl;
-               view().partialUpdate(SPELL_FOUND_WORD);
+               dialog().view().partialUpdate(SPELL_FOUND_WORD);
        }
 }
 
@@ -259,15 +260,17 @@ bool ControlSpellchecker::checkAlive()
        if (speller_->alive() && speller_->error().empty())
                return true;
 
-       string message = speller_->error();
-       if (message.empty())
-               message = _("The spell-checker has died for some reason.\n"
-                        "Maybe it has been killed.");
+       string message;
+       if (speller_->error().empty())
+               message = _("The spellchecker has died for some reason.\n"
+                           "Maybe it has been killed.");
+       else
+               message = _("The spellchecker has failed.\n") 
+                       + speller_->error();
 
-       view().hide();
-       speller_.reset(0);
+       dialog().CancelButton();
 
-       Alert::error(_("The spell-checker has failed"), message);
+       Alert::error(_("The spellchecker has failed"), message);
        return false;
 }
 
@@ -275,24 +278,29 @@ bool ControlSpellchecker::checkAlive()
 void ControlSpellchecker::showSummary()
 {
        if (!checkAlive() || count_ == 0) {
-               view().hide();
+               dialog().CancelButton();
                return;
        }
 
        string message;
        if (count_ != 1)
-               message = bformat(_("%1$s words checked."), tostr(count_));
+               message = bformat(_("%1$d words checked."), count_);
        else
                message = _("One word checked.");
 
-       view().hide();
-       Alert::information(_("Spell-checking is complete"), message);
+       dialog().CancelButton();
+       Alert::information(_("Spelling check completed"), message);
 }
 
 
 void ControlSpellchecker::replace(string const & replacement)
 {
-       bufferview()->replaceWord(replacement);
+       lyxerr << "ControlSpellchecker::replace("
+              << replacement << ")" << std::endl;
+       BufferView & bufferview = *kernel().bufferview();
+       cap::replaceWord(bufferview.cursor(), replacement);
+       kernel().buffer().markDirty();
+       bufferview.update();
        // fix up the count
        --count_;
        check();
@@ -330,3 +338,6 @@ void ControlSpellchecker::ignoreAll()
        speller_->accept(word_);
        check();
 }
+
+} // namespace frontend
+} // namespace lyx