X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2Ffrontends%2Fqt4%2FGuiSpellchecker.cpp;h=537d9d79b50184b8e652d75aac0ab3369db54e2e;hb=b7f6b979d0f889f08e735f35378bb20ba3788b4b;hp=2718e06cccf9dd4cbbfd9bbbccadefdac6677d0c;hpb=8e2f971ada2769718dc68972debbf7ae4bdc91a4;p=lyx.git diff --git a/src/frontends/qt4/GuiSpellchecker.cpp b/src/frontends/qt4/GuiSpellchecker.cpp index 2718e06ccc..537d9d79b5 100644 --- a/src/frontends/qt4/GuiSpellchecker.cpp +++ b/src/frontends/qt4/GuiSpellchecker.cpp @@ -5,6 +5,7 @@ * * \author John Levon * \author Edwin Leuven + * \author Abdelrazak Younes * * Full author contact details are available in file CREDITS. */ @@ -12,31 +13,40 @@ #include #include "GuiSpellchecker.h" +#include "GuiApplication.h" #include "qt_helpers.h" +#include "ui_SpellcheckerUi.h" + #include "Buffer.h" #include "BufferParams.h" #include "BufferView.h" +#include "buffer_funcs.h" #include "Cursor.h" +#include "Text.h" #include "CutAndPaste.h" +#include "FuncRequest.h" #include "Language.h" +#include "LyX.h" #include "LyXRC.h" +#include "lyxfind.h" #include "Paragraph.h" +#include "WordLangTuple.h" #include "support/debug.h" #include "support/docstring.h" +#include "support/docstring_list.h" +#include "support/ExceptionMessage.h" #include "support/gettext.h" #include "support/lstrings.h" #include "support/textutils.h" +#include #include +#include -#if defined(USE_ASPELL) -# include "ASpell_local.h" -#endif - -#include "SpellBase.h" +#include "SpellChecker.h" #include "frontends/alert.h" @@ -47,416 +57,572 @@ namespace lyx { namespace frontend { -GuiSpellchecker::GuiSpellchecker(GuiView & lv) - : GuiDialog(lv, "spellchecker", qt_("Spellchecker")), exitEarly_(false), - oldval_(0), newvalue_(0), count_(0), speller_(0) +struct SpellcheckerWidget::Private { - setupUi(this); + Private(SpellcheckerWidget * parent, DockView * dv, GuiView * gv) + : p(parent), dv_(dv), gv_(gv), incheck_(false), wrap_around_(false) {} + /// update from controller + void updateSuggestions(docstring_list & words); + /// move to next position after current word + void forward(); + /// check text until next misspelled/unknown word + void check(); + /// close the spell checker dialog + void hide() const; + /// make/restore a selection between from and to + void setSelection(DocIterator const & from, DocIterator const & to) const; + /// if no selection was checked: + /// ask the user if the check should start over + bool continueFromBeginning(); + /// set the given language in language chooser + void setLanguage(Language const * lang); + /// test and set guard flag + bool inCheck() { + if (incheck_) + return true; + incheck_ = true; + return false; + } + void canCheck() { incheck_ = false; } + /// check for wrap around + void wrapAround(bool flag) { + wrap_around_ = flag; + if (flag) { + end_ = start_; + } + } + /// test for existing association with a document buffer + /// and test for already active check + bool disabled() { + return gv_->documentBufferView() == 0 || inCheck(); + } + /// the cursor position of the buffer view + DocIterator const cursor() const; + /// status checks + bool isCurrentBuffer(DocIterator const & cursor) const; + bool isWrapAround(DocIterator const & cursor) const; + bool isWrapAround() const { return wrap_around_; } + bool atLastPos(DocIterator const & cursor) const; + /// validate the cached doc iterators + /// The spell checker dialog is not modal. + /// The user may change the buffer being checked and break the iterators. + void fixPositionsIfBroken(); + /// + Ui::SpellcheckerUi ui; + /// + SpellcheckerWidget * p; + /// + DockView * dv_; + /// + GuiView * gv_; + /// current word being checked and lang code + WordLangTuple word_; + /// cursor position where spell checking starts + DocIterator start_; + /// range to spell check + /// for selection both are non-empty + /// after wrap around the start position becomes the end + DocIterator begin_; + DocIterator end_; + /// + bool incheck_; + /// + bool wrap_around_; +}; + + +SpellcheckerWidget::SpellcheckerWidget(GuiView * gv, DockView * dv, QWidget * parent) + : QTabWidget(parent), d(new Private(this, dv, gv)) +{ + d->ui.setupUi(this); - connect(closePB, SIGNAL(clicked()), this, SLOT(slotClose())); - connect(replacePB, SIGNAL(clicked()), this, SLOT(replace())); - connect(ignorePB, SIGNAL(clicked()), this, SLOT(ignore())); - connect(replacePB_3, SIGNAL(clicked()), this, SLOT(accept())); - connect(addPB, SIGNAL(clicked()), this, SLOT(add())); + connect(d->ui.suggestionsLW, SIGNAL(itemDoubleClicked(QListWidgetItem*)), + this, SLOT(on_replacePB_clicked())); - connect(replaceCO, SIGNAL(highlighted(QString)), - this, SLOT(replaceChanged(QString))); - connect(suggestionsLW, SIGNAL(itemDoubleClicked(QListWidgetItem*)), - this, SLOT(replace())); - connect(suggestionsLW, SIGNAL(itemClicked(QListWidgetItem*)), - this, SLOT(suggestionChanged(QListWidgetItem*))); + // language + QAbstractItemModel * language_model = guiApp->languageModel(); + // FIXME: it would be nice if sorting was enabled/disabled via a checkbox. + language_model->sort(0); + d->ui.languageCO->setModel(language_model); + d->ui.languageCO->setModelColumn(1); - wordED->setReadOnly(true); + d->ui.wordED->setReadOnly(true); - bc().setPolicy(ButtonPolicy::NoRepeatedApplyReadOnlyPolicy); - bc().setCancel(closePB); + d->ui.suggestionsLW->installEventFilter(this); } -GuiSpellchecker::~GuiSpellchecker() +SpellcheckerWidget::~SpellcheckerWidget() { - delete speller_; + delete d; +} + + +bool SpellcheckerWidget::eventFilter(QObject *obj, QEvent *event) +{ + if (obj == d->ui.suggestionsLW && event->type() == QEvent::KeyPress) { + QKeyEvent *e = static_cast (event); + if (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return) { + if (d->ui.suggestionsLW->currentItem()) { + on_suggestionsLW_itemClicked(d->ui.suggestionsLW->currentItem()); + on_replacePB_clicked(); + } + return true; + } else if (e->key() == Qt::Key_Right) { + if (d->ui.suggestionsLW->currentItem()) + on_suggestionsLW_itemClicked(d->ui.suggestionsLW->currentItem()); + return true; + } + } + // standard event processing + return QWidget::eventFilter(obj, event); } -void GuiSpellchecker::suggestionChanged(QListWidgetItem * item) +void SpellcheckerWidget::on_suggestionsLW_itemClicked(QListWidgetItem * item) { - if (replaceCO->count() != 0) - replaceCO->setItemText(0, item->text()); + if (d->ui.replaceCO->count() != 0) + d->ui.replaceCO->setItemText(0, item->text()); else - replaceCO->addItem(item->text()); + d->ui.replaceCO->addItem(item->text()); - replaceCO->setCurrentIndex(0); + d->ui.replaceCO->setCurrentIndex(0); } -void GuiSpellchecker::replaceChanged(const QString & str) +void SpellcheckerWidget::on_replaceCO_highlighted(const QString & str) { - if (suggestionsLW->currentItem()->text() == str) + QListWidget * lw = d->ui.suggestionsLW; + if (lw->currentItem() && lw->currentItem()->text() == str) return; - for (int i = 0; i != suggestionsLW->count(); ++i) { - if (suggestionsLW->item(i)->text() == str) { - suggestionsLW->setCurrentRow(i); + for (int i = 0; i != lw->count(); ++i) { + if (lw->item(i)->text() == str) { + lw->setCurrentRow(i); break; } } } -void GuiSpellchecker::reject() +void SpellcheckerWidget::updateView() { - slotClose(); - QDialog::reject(); + BufferView * bv = d->gv_->documentBufferView(); + // we need a buffer view and the buffer has to be writable + bool const enabled = bv != 0 && !bv->buffer().isReadonly(); + setEnabled(enabled); + if (enabled && hasFocus()) { + Cursor const & cursor = bv->cursor(); + if (d->start_.empty() || !d->isCurrentBuffer(cursor)) { + if (cursor.selection()) { + d->begin_ = cursor.selectionBegin(); + d->end_ = cursor.selectionEnd(); + d->start_ = d->begin_; + bv->cursor().setCursor(d->start_); + } else { + d->begin_ = DocIterator(); + d->end_ = DocIterator(); + d->start_ = cursor; + } + d->wrapAround(false); + d->check(); + } + } } +DocIterator const SpellcheckerWidget::Private::cursor() const +{ + BufferView * bv = gv_->documentBufferView(); + return bv ? bv->cursor() : DocIterator(); +} -void GuiSpellchecker::updateContents() +bool SpellcheckerWidget::Private::continueFromBeginning() { - // The clauses below are needed because the spellchecker - // has many flaws (see bugs 1950, 2218). - // Basically, we have to distinguish the case where a - // spellcheck has already been performed for the whole - // document (exitEarly() == true, isVisible() == false) - // from the rest (exitEarly() == false, isVisible() == true). - // FIXME: rewrite the whole beast! - static bool check_after_early_exit; - if (exitEarly()) { - // a spellcheck has already been performed, - check(); - check_after_early_exit = true; + DocIterator const current_ = cursor(); + if (isCurrentBuffer(current_) && !begin_.empty()) { + // selection was checked + // start over from beginning makes no sense + fixPositionsIfBroken(); + hide(); + if (current_ == start_) { + // no errors found... tell the user the good news + // so there is some feedback + QMessageBox::information(p, + qt_("Spell Checker"), + qt_("Spell check of the selection done, " + "did not find any errors.")); + } + return false; } - else if (isVisible()) { - // the above check triggers a second update, - // and isVisible() is true then. Prevent a - // second check that skips the first word - if (check_after_early_exit) - // don't check, but reset the bool. - // business as usual after this. - check_after_early_exit = false; - else - // perform spellcheck (default case) - check(); + QMessageBox::StandardButton const answer = QMessageBox::question(p, + qt_("Spell Checker"), + qt_("We reached the end of the document, would you like to " + "continue from the beginning?"), + QMessageBox::Yes | QMessageBox::No, QMessageBox::No); + if (answer == QMessageBox::No) { + fixPositionsIfBroken(); + hide(); + return false; } + // there is no selection, start over from the beginning now + wrapAround(true); + dispatch(FuncRequest(LFUN_BUFFER_BEGIN)); + return true; } - -void GuiSpellchecker::accept() +bool SpellcheckerWidget::Private::isCurrentBuffer(DocIterator const & cursor) const { - ignoreAll(); + return start_.buffer() == cursor.buffer(); } - -void GuiSpellchecker::add() +bool SpellcheckerWidget::Private::atLastPos(DocIterator const & cursor) const { - insert(); + bool const valid_end = !end_.empty(); + return cursor.depth() <= 1 && ( + cursor.atEnd() || + (valid_end && isCurrentBuffer(cursor) && cursor >= end_)); } - -void GuiSpellchecker::ignore() +bool SpellcheckerWidget::Private::isWrapAround(DocIterator const & cursor) const { - check(); + return wrap_around_ && isCurrentBuffer(cursor) && start_ < cursor; } - -void GuiSpellchecker::replace() +void SpellcheckerWidget::Private::fixPositionsIfBroken() { - replace(qstring_to_ucs4(replaceCO->currentText())); + DocIterator const current_ = cursor(); + if (!isCurrentBuffer(current_)) { + LYXERR(Debug::GUI, "wrong document of current cursor position " << start_); + start_ = current_; + begin_ = DocIterator(); + end_ = DocIterator(); + } + if (start_.fixIfBroken()) + LYXERR(Debug::GUI, "broken start position fixed " << start_); + if (begin_.fixIfBroken()) { + LYXERR(Debug::GUI, "broken selection begin position fixed " << begin_); + begin_ = DocIterator(); + end_ = DocIterator(); + } + if (end_.fixIfBroken()) + LYXERR(Debug::GUI, "broken selection end position fixed " << end_); } - -void GuiSpellchecker::partialUpdate(int state) +void SpellcheckerWidget::Private::hide() const { - switch (state) { - case SPELL_PROGRESSED: - spellcheckPR->setValue(getProgress()); - break; - - case SPELL_FOUND_WORD: { - wordED->setText(toqstr(getWord())); - suggestionsLW->clear(); - - docstring w; - while (!(w = getSuggestion()).empty()) - suggestionsLW->addItem(toqstr(w)); - - if (suggestionsLW->count() == 0) - suggestionChanged(new QListWidgetItem(wordED->text())); - else - suggestionChanged(suggestionsLW->item(0)); - - suggestionsLW->setCurrentRow(0); - break; + BufferView * bv = gv_->documentBufferView(); + Cursor & bvcur = bv->cursor(); + dv_->hide(); + if (isCurrentBuffer(bvcur)) { + if (!begin_.empty() && !end_.empty()) { + // restore previous selection + setSelection(begin_, end_); + } else { + // restore cursor position + bvcur.setCursor(start_); + bvcur.clearSelection(); + bv->processUpdateFlags(Update::Force | Update::FitCursor); } } } - -static SpellBase * createSpeller(BufferParams const & bp) +void SpellcheckerWidget::Private::setSelection( + DocIterator const & from, DocIterator const & to) const { - string lang = lyxrc.spellchecker_use_alt_lang - ? lyxrc.spellchecker_alt_lang - : bp.language->code(); - -#if defined(USE_ASPELL) - if (lyxrc.use_spell_lib) - return new ASpell(bp, lang); -#endif - return new SpellBase; + BufferView * bv = gv_->documentBufferView(); + DocIterator end = to; + + if (from.pit() != end.pit()) { + // there are multiple paragraphs in selection + Cursor & bvcur = bv->cursor(); + bvcur.setCursor(from); + bvcur.clearSelection(); + bvcur.selection(true); + bvcur.setCursor(end); + bvcur.selection(true); + } else { + // FIXME LFUN + // If we used a LFUN, dispatch would do all of this for us + int const size = end.pos() - from.pos(); + bv->putSelectionAt(from, size, false); + } + bv->processUpdateFlags(Update::Force | Update::FitCursor); } - -bool GuiSpellchecker::initialiseParams(string const &) +void SpellcheckerWidget::Private::forward() { - LYXERR(Debug::GUI, "Spellchecker::initialiseParams"); + DocIterator const from = cursor(); - speller_ = createSpeller(buffer().params()); - if (!speller_) - return false; - - // reset values to initial - oldval_ = 0; - newvalue_ = 0; - count_ = 0; - - bool const success = speller_->error().empty(); - - if (!success) { - Alert::error(_("Spellchecker error"), - _("The spellchecker could not be started\n") - + speller_->error()); - delete speller_; - speller_ = 0; + dispatch(FuncRequest(LFUN_ESCAPE)); + fixPositionsIfBroken(); + if (!atLastPos(cursor())) { + dispatch(FuncRequest(LFUN_CHAR_FORWARD)); + } + if (atLastPos(cursor())) { + return; + } + if (from == cursor()) { + //FIXME we must be at the end of a cell + dispatch(FuncRequest(LFUN_CHAR_FORWARD)); + } + if (isWrapAround(cursor())) { + hide(); } - - return success; } -void GuiSpellchecker::clearParams() +void SpellcheckerWidget::on_languageCO_activated(int index) { - LYXERR(Debug::GUI, "Spellchecker::clearParams"); - delete speller_; - speller_ = 0; + string const lang = + fromqstr(d->ui.languageCO->itemData(index).toString()); + if (!d->word_.lang() || d->word_.lang()->lang() == lang) + // nothing changed + return; + dispatch(FuncRequest(LFUN_LANGUAGE, lang)); + d->check(); } -static bool isLetter(DocIterator const & dit) +bool SpellcheckerWidget::initialiseParams(std::string const &) { - return dit.inTexted() - && dit.inset().allowSpellCheck() - && dit.pos() != dit.lastpos() - && (dit.paragraph().isLetter(dit.pos()) - // We want to pass the ' and escape chars to ispell - || contains(from_utf8(lyxrc.spellchecker_esc_chars + '\''), - dit.paragraph().getChar(dit.pos()))) - && !dit.paragraph().isDeleted(dit.pos()); + BufferView * bv = d->gv_->documentBufferView(); + if (bv == 0) + return false; + std::set languages = + bv->buffer().masterBuffer()->getLanguages(); + if (!languages.empty()) + d->setLanguage(*languages.begin()); + d->start_ = DocIterator(); + d->wrapAround(false); + d->canCheck(); + return true; } -static WordLangTuple nextWord(Cursor & cur, ptrdiff_t & progress) +void SpellcheckerWidget::on_ignoreAllPB_clicked() { - BufferParams const & bp = cur.bv().buffer().params(); - bool inword = false; - bool ignoreword = false; - cur.resetAnchor(); - docstring word; - string lang_code; - - while (cur.depth()) { - if (isLetter(cur)) { - if (!inword) { - inword = true; - ignoreword = false; - cur.resetAnchor(); - 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())) { - char_type const c = cur.paragraph().getChar(cur.pos()); - word += c; - if (isDigit(c)) - ignoreword = true; - } - } else { // !isLetter(cur) - if (inword) - if (!word.empty() && !ignoreword) { - cur.setSelection(); - return WordLangTuple(word, lang_code); - } - inword = false; - } - - cur.forwardPos(); - ++progress; - } - - return WordLangTuple(docstring(), string()); + /// ignore all occurrences of word + if (d->disabled()) + return; + LYXERR(Debug::GUI, "Spellchecker: ignore all button"); + if (d->word_.lang() && !d->word_.word().empty()) + theSpellChecker()->accept(d->word_); + d->forward(); + d->check(); + d->canCheck(); } -void GuiSpellchecker::check() +void SpellcheckerWidget::on_addPB_clicked() { - LYXERR(Debug::GUI, "Check the spelling of a word"); - - SpellBase::Result res = SpellBase::OK; + /// insert word in personal dictionary + if (d->disabled()) + return; + LYXERR(Debug::GUI, "Spellchecker: add word button"); + theSpellChecker()->insert(d->word_); + d->forward(); + d->check(); + d->canCheck(); +} - Cursor cur = bufferview()->cursor(); - while (cur && cur.pos() && isLetter(cur)) - cur.backwardPos(); - ptrdiff_t start = 0; - ptrdiff_t total = 0; - DocIterator it = doc_iterator_begin(buffer().inset()); - for (start = 1; it != cur; it.forwardPos()) - ++start; +void SpellcheckerWidget::on_ignorePB_clicked() +{ + /// ignore this occurrence of word + if (d->disabled()) + return; + LYXERR(Debug::GUI, "Spellchecker: ignore button"); + d->forward(); + d->check(); + d->canCheck(); +} - for (total = start; it; it.forwardPos()) - ++total; - exitEarly_ = false; +void SpellcheckerWidget::on_findNextPB_clicked() +{ + if (d->disabled()) + return; + docstring const textfield = qstring_to_ucs4(d->ui.wordED->text()); + docstring const datastring = find2string(textfield, + true, true, true); + LYXERR(Debug::GUI, "Spellchecker: find next (" << textfield << ")"); + dispatch(FuncRequest(LFUN_WORD_FIND, datastring)); + d->canCheck(); +} - while (res == SpellBase::OK || res == SpellBase::IGNORED_WORD) { - word_ = nextWord(cur, start); - // end of document - if (getWord().empty()) { - showSummary(); - exitEarly_ = true; - return; - } +void SpellcheckerWidget::on_replacePB_clicked() +{ + if (d->disabled()) + return; + docstring const textfield = qstring_to_ucs4(d->ui.wordED->text()); + docstring const replacement = qstring_to_ucs4(d->ui.replaceCO->currentText()); + docstring const datastring = + replace2string(replacement, textfield, + true, // case sensitive + true, // match word + false, // all words + true, // forward + false); // find next + + LYXERR(Debug::GUI, "Replace (" << replacement << ")"); + dispatch(FuncRequest(LFUN_WORD_REPLACE, datastring)); + d->forward(); + d->check(); + d->canCheck(); +} - ++count_; - // Update slider if and only if value has changed - float progress = total ? float(start)/total : 1; - newvalue_ = int(100.0 * progress); - if (newvalue_!= oldval_) { - LYXERR(Debug::GUI, "Updating spell progress."); - oldval_ = newvalue_; - // set progress bar - partialUpdate(SPELL_PROGRESSED); - } +void SpellcheckerWidget::on_replaceAllPB_clicked() +{ + if (d->disabled()) + return; + docstring const textfield = qstring_to_ucs4(d->ui.wordED->text()); + docstring const replacement = qstring_to_ucs4(d->ui.replaceCO->currentText()); + docstring const datastring = + replace2string(replacement, textfield, + true, // case sensitive + true, // match word + true, // all words + true, // forward + false); // find next + + LYXERR(Debug::GUI, "Replace all (" << replacement << ")"); + dispatch(FuncRequest(LFUN_WORD_REPLACE, datastring)); + d->forward(); + // replace all wraps around + d->wrapAround(true); + d->check(); // continue spellchecking + d->canCheck(); +} - // speller might be dead ... - if (!checkAlive()) - return; - res = speller_->check(word_); +void SpellcheckerWidget::Private::updateSuggestions(docstring_list & words) +{ + QString const suggestion = toqstr(word_.word()); + ui.wordED->setText(suggestion); + QListWidget * lw = ui.suggestionsLW; + lw->clear(); - // ... or it might just be reporting an error - if (!checkAlive()) - return; + if (words.empty()) { + p->on_suggestionsLW_itemClicked(new QListWidgetItem(suggestion)); + return; } + for (size_t i = 0; i != words.size(); ++i) + lw->addItem(toqstr(words[i])); - LYXERR(Debug::GUI, "Found word \"" << to_utf8(getWord()) << "\""); - - int const size = cur.selEnd().pos() - cur.selBegin().pos(); - cur.pos() -= size; - BufferView * bv = const_cast(bufferview()); - bv->putSelectionAt(cur, size, false); - // FIXME: if we used a lfun like in find/replace, dispatch would do - // that for us - // FIXME: this Controller is very badly designed... - bv->processUpdateFlags(Update::Force | Update::FitCursor); - - // set suggestions - if (res != SpellBase::OK && res != SpellBase::IGNORED_WORD) { - LYXERR(Debug::GUI, "Found a word needing checking."); - partialUpdate(SPELL_FOUND_WORD); - } + p->on_suggestionsLW_itemClicked(lw->item(0)); + lw->setCurrentRow(0); } -bool GuiSpellchecker::checkAlive() +void SpellcheckerWidget::Private::setLanguage(Language const * lang) { - if (speller_->alive() && speller_->error().empty()) - return true; - - docstring 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(); - - slotClose(); - - Alert::error(_("The spellchecker has failed"), message); - return false; + int const pos = ui.languageCO->findData(toqstr(lang->lang())); + if (pos != -1) + ui.languageCO->setCurrentIndex(pos); } -void GuiSpellchecker::showSummary() +void SpellcheckerWidget::Private::check() { - if (!checkAlive() || count_ == 0) { - slotClose(); + BufferView * bv = gv_->documentBufferView(); + if (!bv || bv->buffer().text().empty()) return; - } - docstring message; - if (count_ != 1) - message = bformat(_("%1$d words checked."), count_); - else - message = _("One word checked."); + fixPositionsIfBroken(); - slotClose(); - Alert::information(_("Spelling check completed"), message); -} + SpellChecker * speller = theSpellChecker(); + if (speller && !speller->hasDictionary(bv->buffer().language())) { + int dsize = speller->numDictionaries(); + if (0 == dsize) { + hide(); + QMessageBox::information(p, + qt_("Spell Checker"), + qt_("Spell checker has no dictionaries.")); + return; + } + } + DocIterator from = bv->cursor(); + DocIterator to = isCurrentBuffer(from) ? end_ : doc_iterator_end(&bv->buffer()); + WordLangTuple word_lang; + docstring_list suggestions; + + LYXERR(Debug::GUI, "Spellchecker: start check at " << from); + try { + bv->buffer().spellCheck(from, to, word_lang, suggestions); + } catch (ExceptionMessage const & message) { + if (message.type_ == WarningException) { + Alert::warning(message.title_, message.details_); + return; + } + throw message; + } -void GuiSpellchecker::replace(docstring const & replacement) -{ - LYXERR(Debug::GUI, "GuiSpellchecker::replace(" - << to_utf8(replacement) << ")"); - BufferView * bv = const_cast(bufferview()); - cap::replaceSelectionWithString(bv->cursor(), replacement, true); - bv->buffer().markDirty(); - // If we used an LFUN, we would not need that - bv->processUpdateFlags(Update::Force | Update::FitCursor); - // fix up the count - --count_; - check(); -} + // end of document or selection? + if (atLastPos(from)) { + if (isWrapAround()) { + hide(); + return; + } + if (continueFromBeginning()) + check(); + return; + } + + if (isWrapAround(from)) { + hide(); + return; + } + word_ = word_lang; -void GuiSpellchecker::replaceAll(docstring const & replacement) -{ - // TODO: add to list - replace(replacement); + // set suggestions + updateSuggestions(suggestions); + // set language + if (!word_lang.lang()) + return; + setLanguage(word_lang.lang()); + // mark misspelled word + setSelection(from, to); } -void GuiSpellchecker::insert() +GuiSpellchecker::GuiSpellchecker(GuiView & parent, + Qt::DockWidgetArea area, Qt::WindowFlags flags) + : DockView(parent, "spellchecker", qt_("Spellchecker"), + area, flags) { - speller_->insert(word_); - check(); + widget_ = new SpellcheckerWidget(&parent, this); + setWidget(widget_); + setFocusProxy(widget_); } -docstring GuiSpellchecker::getSuggestion() const +GuiSpellchecker::~GuiSpellchecker() { - return speller_->nextMiss(); + setFocusProxy(0); + delete widget_; } -docstring GuiSpellchecker::getWord() const +void GuiSpellchecker::updateView() { - return word_.word(); + widget_->updateView(); } -void GuiSpellchecker::ignoreAll() +Dialog * createGuiSpellchecker(GuiView & lv) { - speller_->accept(word_); - check(); + GuiSpellchecker * gui = new GuiSpellchecker(lv, Qt::RightDockWidgetArea); +#ifdef Q_OS_MAC + gui->setFloating(true); +#endif + return gui; } -Dialog * createGuiSpellchecker(GuiView & lv) { return new GuiSpellchecker(lv); } - } // namespace frontend } // namespace lyx