X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2Ffrontends%2Fqt4%2FGuiSpellchecker.cpp;h=537d9d79b50184b8e652d75aac0ab3369db54e2e;hb=b7f6b979d0f889f08e735f35378bb20ba3788b4b;hp=f89dd750fc8a16a0b22a09faffb12033dff23ee9;hpb=d29f1f6930099d3dab3af973d6329f4ad6505251;p=lyx.git diff --git a/src/frontends/qt4/GuiSpellchecker.cpp b/src/frontends/qt4/GuiSpellchecker.cpp index f89dd750fc..537d9d79b5 100644 --- a/src/frontends/qt4/GuiSpellchecker.cpp +++ b/src/frontends/qt4/GuiSpellchecker.cpp @@ -59,21 +59,22 @@ namespace frontend { struct SpellcheckerWidget::Private { - Private(SpellcheckerWidget * parent, DockView * dv) - : p(parent), dv_(dv), incheck_(false), wrap_around_(false) {} + 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() { @@ -90,25 +91,37 @@ struct SpellcheckerWidget::Private end_ = start_; } } - bool isWrapAround(DocIterator cursor) const; + /// 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 cursor) const; + 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; /// - GuiView * gv_; - /// DockView * dv_; + /// + GuiView * gv_; /// current word being checked and lang code WordLangTuple word_; - /// cursor position when spell checking starts + /// cursor position where spell checking starts DocIterator start_; /// range to spell check /// for selection both are non-empty - /// after wrap around the start becomes the end + /// after wrap around the start position becomes the end DocIterator begin_; DocIterator end_; /// @@ -119,10 +132,9 @@ struct SpellcheckerWidget::Private SpellcheckerWidget::SpellcheckerWidget(GuiView * gv, DockView * dv, QWidget * parent) - : QTabWidget(parent), d(new Private(this, dv)) + : QTabWidget(parent), d(new Private(this, dv, gv)) { d->ui.setupUi(this); - d->gv_ = gv; connect(d->ui.suggestionsLW, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(on_replacePB_clicked())); @@ -201,7 +213,7 @@ void SpellcheckerWidget::updateView() setEnabled(enabled); if (enabled && hasFocus()) { Cursor const & cursor = bv->cursor(); - if (d->start_.empty() || d->start_.buffer() != cursor.buffer()) { + if (d->start_.empty() || !d->isCurrentBuffer(cursor)) { if (cursor.selection()) { d->begin_ = cursor.selectionBegin(); d->end_ = cursor.selectionEnd(); @@ -218,14 +230,19 @@ void SpellcheckerWidget::updateView() } } +DocIterator const SpellcheckerWidget::Private::cursor() const +{ + BufferView * bv = gv_->documentBufferView(); + return bv ? bv->cursor() : DocIterator(); +} bool SpellcheckerWidget::Private::continueFromBeginning() { - BufferView * bv = gv_->documentBufferView(); - if (!begin_.empty()) { + DocIterator const current_ = cursor(); + if (isCurrentBuffer(current_) && !begin_.empty()) { // selection was checked // start over from beginning makes no sense - DocIterator current_ = bv->cursor(); + fixPositionsIfBroken(); hide(); if (current_ == start_) { // no errors found... tell the user the good news @@ -243,6 +260,7 @@ bool SpellcheckerWidget::Private::continueFromBeginning() "continue from the beginning?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::No); if (answer == QMessageBox::No) { + fixPositionsIfBroken(); hide(); return false; } @@ -252,32 +270,59 @@ bool SpellcheckerWidget::Private::continueFromBeginning() return true; } -bool SpellcheckerWidget::Private::atLastPos(DocIterator cursor) const +bool SpellcheckerWidget::Private::isCurrentBuffer(DocIterator const & cursor) const +{ + return start_.buffer() == cursor.buffer(); +} + +bool SpellcheckerWidget::Private::atLastPos(DocIterator const & cursor) const { bool const valid_end = !end_.empty(); return cursor.depth() <= 1 && ( cursor.atEnd() || - (valid_end && cursor >= end_)); + (valid_end && isCurrentBuffer(cursor) && cursor >= end_)); } -bool SpellcheckerWidget::Private::isWrapAround(DocIterator cursor) const +bool SpellcheckerWidget::Private::isWrapAround(DocIterator const & cursor) const { - return wrap_around_ && start_ < cursor; + return wrap_around_ && isCurrentBuffer(cursor) && start_ < cursor; +} + +void SpellcheckerWidget::Private::fixPositionsIfBroken() +{ + 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 SpellcheckerWidget::Private::hide() const { + BufferView * bv = gv_->documentBufferView(); + Cursor & bvcur = bv->cursor(); dv_->hide(); - if (!begin_.empty() && !end_.empty()) { - // restore previous selection - setSelection(begin_, end_); - } else { - // restore cursor position - BufferView * bv = gv_->documentBufferView(); - Cursor & bvcur = bv->cursor(); - bvcur.setCursor(start_); - bvcur.clearSelection(); - bv->processUpdateFlags(Update::Force | Update::FitCursor); + 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); + } } } @@ -287,43 +332,40 @@ void SpellcheckerWidget::Private::setSelection( BufferView * bv = gv_->documentBufferView(); DocIterator end = to; - // spell checker corrections may have invalidated the end - end.fixIfBroken(); - if (from.pit() != end.pit()) { - // there are multiple paragraphs in selection + // there are multiple paragraphs in selection Cursor & bvcur = bv->cursor(); bvcur.setCursor(from); bvcur.clearSelection(); - bvcur.setSelection(true); + bvcur.selection(true); bvcur.setCursor(end); - bvcur.setSelection(true); + 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); + bv->processUpdateFlags(Update::Force | Update::FitCursor); } void SpellcheckerWidget::Private::forward() { - BufferView * bv = gv_->documentBufferView(); - DocIterator from = bv->cursor(); + DocIterator const from = cursor(); dispatch(FuncRequest(LFUN_ESCAPE)); - if (!atLastPos(bv->cursor())) { + fixPositionsIfBroken(); + if (!atLastPos(cursor())) { dispatch(FuncRequest(LFUN_CHAR_FORWARD)); } - if (atLastPos(bv->cursor())) { + if (atLastPos(cursor())) { return; } - if (from == bv->cursor()) { + if (from == cursor()) { //FIXME we must be at the end of a cell dispatch(FuncRequest(LFUN_CHAR_FORWARD)); } - if (isWrapAround(bv->cursor())) { + if (isWrapAround(cursor())) { hide(); } } @@ -346,7 +388,7 @@ bool SpellcheckerWidget::initialiseParams(std::string const &) BufferView * bv = d->gv_->documentBufferView(); if (bv == 0) return false; - std::set languages = + std::set languages = bv->buffer().masterBuffer()->getLanguages(); if (!languages.empty()) d->setLanguage(*languages.begin()); @@ -360,7 +402,7 @@ bool SpellcheckerWidget::initialiseParams(std::string const &) void SpellcheckerWidget::on_ignoreAllPB_clicked() { /// ignore all occurrences of word - if (d->inCheck()) + if (d->disabled()) return; LYXERR(Debug::GUI, "Spellchecker: ignore all button"); if (d->word_.lang() && !d->word_.word().empty()) @@ -374,7 +416,7 @@ void SpellcheckerWidget::on_ignoreAllPB_clicked() void SpellcheckerWidget::on_addPB_clicked() { /// insert word in personal dictionary - if (d->inCheck()) + if (d->disabled()) return; LYXERR(Debug::GUI, "Spellchecker: add word button"); theSpellChecker()->insert(d->word_); @@ -387,7 +429,7 @@ void SpellcheckerWidget::on_addPB_clicked() void SpellcheckerWidget::on_ignorePB_clicked() { /// ignore this occurrence of word - if (d->inCheck()) + if (d->disabled()) return; LYXERR(Debug::GUI, "Spellchecker: ignore button"); d->forward(); @@ -398,7 +440,7 @@ void SpellcheckerWidget::on_ignorePB_clicked() void SpellcheckerWidget::on_findNextPB_clicked() { - if (d->inCheck()) + if (d->disabled()) return; docstring const textfield = qstring_to_ucs4(d->ui.wordED->text()); docstring const datastring = find2string(textfield, @@ -411,12 +453,17 @@ void SpellcheckerWidget::on_findNextPB_clicked() void SpellcheckerWidget::on_replacePB_clicked() { - if (d->inCheck()) + 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, true, false, false); + 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)); @@ -428,16 +475,23 @@ void SpellcheckerWidget::on_replacePB_clicked() void SpellcheckerWidget::on_replaceAllPB_clicked() { - if (d->inCheck()) + 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, true, true, true); + 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(); } @@ -476,8 +530,22 @@ void SpellcheckerWidget::Private::check() if (!bv || bv->buffer().text().empty()) return; + fixPositionsIfBroken(); + + 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 = end_; + DocIterator to = isCurrentBuffer(from) ? end_ : doc_iterator_end(&bv->buffer()); WordLangTuple word_lang; docstring_list suggestions; @@ -545,10 +613,10 @@ void GuiSpellchecker::updateView() } -Dialog * createGuiSpellchecker(GuiView & lv) -{ +Dialog * createGuiSpellchecker(GuiView & lv) +{ GuiSpellchecker * gui = new GuiSpellchecker(lv, Qt::RightDockWidgetArea); -#ifdef Q_WS_MACX +#ifdef Q_OS_MAC gui->setFloating(true); #endif return gui;