X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2Ffrontends%2Fqt4%2FFindAndReplace.cpp;h=50f3641b5700fa6ad0442a08c84dd0dea7f3bd49;hb=425d092204118ea6c24c28e85fdf03fcf2bb51a4;hp=8a77a6f66fbe82e0ec4773d4faf928122577f7af;hpb=d5b329502bdd133d2f11834b152d3fd953bf285a;p=lyx.git diff --git a/src/frontends/qt4/FindAndReplace.cpp b/src/frontends/qt4/FindAndReplace.cpp index 8a77a6f66f..50f3641b57 100644 --- a/src/frontends/qt4/FindAndReplace.cpp +++ b/src/frontends/qt4/FindAndReplace.cpp @@ -13,26 +13,32 @@ #include "FindAndReplace.h" #include "GuiApplication.h" -#include "qt_helpers.h" #include "GuiView.h" #include "GuiWorkArea.h" +#include "qt_helpers.h" #include "buffer_funcs.h" #include "BufferParams.h" +#include "BufferList.h" #include "Cursor.h" #include "FuncRequest.h" #include "lyxfind.h" -#include "OutputParams.h" #include "output_latex.h" +#include "OutputParams.h" #include "TexRow.h" +#include "frontends/alert.h" + #include "support/debug.h" +#include "support/filetools.h" #include "support/FileName.h" #include "support/gettext.h" #include "support/lassert.h" +#include "support/lstrings.h" #include #include +#include #include @@ -57,56 +63,83 @@ FindAndReplaceWidget::FindAndReplaceWidget(GuiView & view) replace_work_area_->init(); // We don't want two cursors blinking. replace_work_area_->stopBlinkingCursor(); + + QMenu * menu = new QMenu(); + QAction * regAny = menu->addAction(qt_("&Anything")); + regAny->setData(".*"); + QAction * regAnyNonEmpty = menu->addAction(qt_("Any non-&empty")); + regAnyNonEmpty->setData(".+"); + QAction * regAnyWord = menu->addAction(qt_("Any &word")); + regAnyWord->setData("[a-z]+"); + QAction * regAnyNumber = menu->addAction(qt_("Any &number")); + regAnyNumber->setData("[0-9]+"); + QAction * regCustom = menu->addAction(qt_("&User-defined")); + regCustom->setData(""); + regexpInsertPB->setMenu(menu); + + connect(menu, SIGNAL(triggered(QAction *)), + this, SLOT(insertRegexp(QAction *))); } -bool FindAndReplaceWidget::eventFilter(QObject *obj, QEvent *event) +bool FindAndReplaceWidget::eventFilter(QObject * obj, QEvent * event) { - LYXERR(Debug::FIND, "FindAndReplace::eventFilter(): obj=" << obj - << ", fwa=" << find_work_area_ << ", rwa=" << replace_work_area_ - << "fsa=" << find_scroll_area_ << ", rsa=" << replace_scroll_area_); - if (obj == find_work_area_ && event->type() == QEvent::KeyPress) { - QKeyEvent *e = static_cast (event); - if (e->key() == Qt::Key_Escape && e->modifiers() == Qt::NoModifier) { - on_closePB_clicked(); + if (event->type() != QEvent::KeyPress + || (obj != find_work_area_ && obj != replace_work_area_)) + return QWidget::eventFilter(obj, event); + + QKeyEvent * e = static_cast (event); + switch (e->key()) { + case Qt::Key_Escape: + if (e->modifiers() == Qt::NoModifier) { + hideDialog(); return true; - } else if (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return) { - if (e->modifiers() == Qt::ShiftModifier) { + } + break; + + case Qt::Key_Enter: + case Qt::Key_Return: + if (e->modifiers() == Qt::ShiftModifier) { + if (obj == find_work_area_) on_findPrevPB_clicked(); - return true; - } else if (e->modifiers() == Qt::NoModifier) { + else + on_replacePrevPB_clicked(); + return true; + } else if (e->modifiers() == Qt::NoModifier) { + if (obj == find_work_area_) on_findNextPB_clicked(); - return true; - } - } else if (e->key() == Qt::Key_Tab && e->modifiers() == Qt::NoModifier) { - LYXERR(Debug::FIND, "Focusing replace WA"); - replace_work_area_->setFocus(); + else + on_replaceNextPB_clicked(); return true; } - } - if (obj == replace_work_area_ && event->type() == QEvent::KeyPress) { - QKeyEvent *e = static_cast (event); - if (e->key() == Qt::Key_Escape && e->modifiers() == Qt::NoModifier) { - on_closePB_clicked(); - return true; - } else if (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return) { - if (e->modifiers() == Qt::ShiftModifier) { - on_replacePrevPB_clicked(); - return true; - } else if (e->modifiers() == Qt::NoModifier) { - on_replaceNextPB_clicked(); + break; + + case Qt::Key_Tab: + if (e->modifiers() == Qt::NoModifier) { + if (obj == find_work_area_){ + LYXERR(Debug::FIND, "Focusing replace WA"); + replace_work_area_->setFocus(); return true; } - } else if (e->key() == Qt::Key_Backtab) { + } + break; + + case Qt::Key_Backtab: + if (obj == replace_work_area_) { LYXERR(Debug::FIND, "Focusing find WA"); find_work_area_->setFocus(); return true; } + break; + + default: + break; } // standard event processing return QWidget::eventFilter(obj, event); } + static docstring buffer_to_latex(Buffer & buffer) { OutputParams runparams(&buffer.params().encoding()); odocstringstream os; @@ -119,12 +152,273 @@ static docstring buffer_to_latex(Buffer & buffer) { ParagraphList::const_iterator pit = buffer.paragraphs().begin(); ParagraphList::const_iterator const end = buffer.paragraphs().end(); for (; pit != end; ++pit) { - TeXOnePar(buffer, buffer.text(), pit, os, buffer.texrow(), runparams); - LYXERR(Debug::FIND, "searchString up to here: " << os.str()); + TeXOnePar(buffer, buffer.text(), + pit, os, buffer.texrow(), runparams); + LYXERR(Debug::FIND, "searchString up to here: " + << os.str()); } return os.str(); } + +static vector const & allManualsFiles() { + static vector v; + static const char * files[] = { + "Intro", "UserGuide", "Tutorial", "Additional", + "EmbeddedObjects", "Math", "Customization", "Shortcuts", + "LFUNs", "LaTeXConfig" + }; + if (v.empty()) { + FileName fname; + for (size_t i = 0; i < sizeof(files) / sizeof(files[0]); ++i) { + fname = i18nLibFileSearch("doc", files[i], "lyx"); + v.push_back(fname.absFilename()); + } + } + return v; +} + + +/** Switch p_buf to point to next document buffer. + ** + ** Return true if restarted from master-document buffer. + ** + ** @note + ** Not using p_buf->allRelatives() here, because I'm not sure + ** whether or not the returned order is independent of p_buf. + **/ +static bool next_document_buffer(Buffer * & p_buf) { + Buffer *p_master = p_buf; + Buffer *p_old; + do { + p_old = p_master; + p_master = const_cast(p_master->masterBuffer()); + LYXERR(Debug::FIND, "p_old=" + << p_old + << ", p_master=" + << p_master); + } while (p_master != p_old); + LASSERT(p_master != NULL, /**/); + vector v_children; + /* Root master added as first buffer in the vector */ + v_children.push_back(p_master); + p_master->getChildren(v_children, true); + LYXERR(Debug::FIND, "v_children.size()=" << v_children.size()); + vector::const_iterator it = + find(v_children.begin(), v_children.end(), p_buf); + LASSERT(it != v_children.end(), /**/) + ++it; + if (it == v_children.end()) { + p_buf = *v_children.begin(); + return true; + } + p_buf = *it; + return false; +} + + +/** Switch p_buf to point to previous document buffer. + ** + ** Return true if restarted from last child buffer. + ** + ** @note + ** Not using p_buf->allRelatives() here, because I'm not sure + ** whether or not the returned order is independent of p_buf. + **/ +static bool prev_document_buffer(Buffer * & p_buf) { + Buffer *p_master = p_buf; + Buffer *p_old; + do { + p_old = p_master; + p_master = const_cast(p_master->masterBuffer()); + LYXERR(Debug::FIND, + "p_old=" << p_old + << ", p_master=" << p_master); + } while (p_master != p_old); + LASSERT(p_master != NULL, /**/); + vector v_children; + /* Root master added as first buffer in the vector */ + v_children.push_back(p_master); + p_master->getChildren(v_children, true); + LYXERR(Debug::FIND, "v_children.size()=" << v_children.size()); + vector::const_iterator it = + find(v_children.begin(), v_children.end(), p_buf); + LASSERT(it != v_children.end(), /**/) + if (it == v_children.begin()) { + it = v_children.end(); + --it; + p_buf = *it; + return true; + } + --it; + p_buf = *it; + return false; +} + + +/** Switch buf to point to next or previous buffer in search scope. + ** + ** Return true if restarted from scratch. + **/ +static bool next_prev_buffer(Buffer * & buf, + FindAndReplaceOptions const & opt) +{ + bool restarted = false; + switch (opt.scope) { + case FindAndReplaceOptions::S_BUFFER: + restarted = true; + break; + case FindAndReplaceOptions::S_DOCUMENT: + if (opt.forward) + restarted = next_document_buffer(buf); + else + restarted = prev_document_buffer(buf); + break; + case FindAndReplaceOptions::S_OPEN_BUFFERS: + if (opt.forward) { + buf = theBufferList().next(buf); + restarted = buf == *theBufferList().begin(); + } else { + buf = theBufferList().previous(buf); + restarted = buf == *(theBufferList().end() - 1); + } + break; + case FindAndReplaceOptions::S_ALL_MANUALS: + vector const & v = allManualsFiles(); + vector::const_iterator it = + find(v.begin(), v.end(), buf->absFileName()); + if (it == v.end()) { + it = v.begin(); + } else if (opt.forward) { + ++it; + if (it == v.end()) { + it = v.begin(); + restarted = true; + } + } else { + if (it == v.begin()) { + it = v.end(); + restarted = true; + } + --it; + } + FileName const & fname = FileName(*it); + if (!theBufferList().exists(fname)) { + guiApp->currentView()->setBusy(false); + guiApp->currentView()->loadDocument(fname, false); + guiApp->currentView()->setBusy(true); + } + buf = theBufferList().getBuffer(fname); + break; + } + return restarted; +} + + +/** Find the finest question message to post to the user */ +docstring question_string(FindAndReplaceOptions const & opt) +{ + docstring scope; + switch (opt.scope) { + case FindAndReplaceOptions::S_BUFFER: + scope = _("file[[scope]]"); + break; + case FindAndReplaceOptions::S_DOCUMENT: + scope = _("master document[[scope]]"); + break; + case FindAndReplaceOptions::S_OPEN_BUFFERS: + scope = _("open files[[scope]]"); + break; + case FindAndReplaceOptions::S_ALL_MANUALS: + scope = _("manuals[[scope]]"); + break; + } + docstring message = opt.forward ? + bformat(_("End of %1$s reached while searching forward.\n" + "Continue searching from the beginning?"), + scope) : + bformat(_("Beginning of %1$s reached while searching backward.\n" + "Continue searching from the end?"), + scope); + + return message; +} + + +void FindAndReplaceWidget::findAndReplaceScope(FindAndReplaceOptions & opt) +{ + int wrap_answer = -1; + ostringstream oss; + oss << opt; + FuncRequest cmd(LFUN_WORD_FINDADV, from_utf8(oss.str())); + BufferView * bv = view_.documentBufferView(); + Buffer * buf = &bv->buffer(); + + Buffer * buf_orig = &bv->buffer(); + Cursor cur_orig(bv->cursor()); + + if (opt.scope == FindAndReplaceOptions::S_ALL_MANUALS) { + vector const & v = allManualsFiles(); + if (std::find(v.begin(), v.end(), buf->absFileName()) == v.end()) { + FileName const & fname = FileName(*v.begin()); + if (!theBufferList().exists(fname)) { + guiApp->currentView()->setBusy(false); + guiApp->currentView()->loadDocument(fname, false); + guiApp->currentView()->setBusy(true); + } + buf = theBufferList().getBuffer(fname); + lyx::dispatch(FuncRequest(LFUN_BUFFER_SWITCH, + buf->absFileName())); + bv = view_.documentBufferView(); + bv->cursor().clear(); + bv->cursor().push_back(CursorSlice(buf->inset())); + } + } + + do { + LYXERR(Debug::FIND, "Dispatching LFUN_WORD_FINDADV"); + dispatch(cmd); + if (bv->cursor().result().dispatched()) { + // Match found, selected and replaced if needed + return; + } + + // No match found in current buffer: + // select next buffer in scope, if any + bool prompt = next_prev_buffer(buf, opt); + if (prompt) { + if (wrap_answer != -1) + break; + docstring q = question_string(opt); + wrap_answer = frontend::Alert::prompt( + _("Wrap search?"), q, + 0, 1, _("&Yes"), _("&No")); + if (wrap_answer == 1) + break; + } + lyx::dispatch(FuncRequest(LFUN_BUFFER_SWITCH, + buf->absFileName())); + bv = view_.documentBufferView(); + if (opt.forward) { + bv->cursor().clear(); + bv->cursor().push_back(CursorSlice(buf->inset())); + } else { + lyx::dispatch(FuncRequest(LFUN_BUFFER_END)); + bv->cursor().setCursor(doc_iterator_end(buf)); + bv->cursor().backwardPos(); + LYXERR(Debug::FIND, "findBackAdv5: cur: " + << bv->cursor()); + } + bv->clearSelection(); + } while (wrap_answer != 1); + if (buf != buf_orig) + lyx::dispatch(FuncRequest(LFUN_BUFFER_SWITCH, + buf_orig->absFileName())); + bv = view_.documentBufferView(); + bv->cursor() = cur_orig; +} + + void FindAndReplaceWidget::findAndReplace( bool casesensitive, bool matchword, bool backwards, bool expandmacros, bool ignoreformat, bool replace, @@ -144,24 +438,43 @@ void FindAndReplaceWidget::findAndReplace( runparams.linelen = 100000; //lyxrc.plaintext_linelen; runparams.dryrun = true; for (; it != end; ++it) { - LYXERR(Debug::FIND, "Adding to search string: '" << it->asString(false) << "'"); - searchString += it->stringify(pos_type(0), it->size(), AS_STR_INSETS, runparams); + LYXERR(Debug::FIND, "Adding to search string: '" + << it->asString(false) + << "'"); + searchString += + it->stringify(pos_type(0), it->size(), + AS_STR_INSETS, runparams); } } if (to_utf8(searchString).empty()) { buffer.message(_("Nothing to search")); return; } - bool const regexp = to_utf8(searchString).find("\\regexp") != std::string::npos; + bool const regexp = + to_utf8(searchString).find("\\regexp") != std::string::npos; docstring replaceString; if (replace) { - Buffer & repl_buffer = replace_work_area_->bufferView().buffer(); + Buffer & repl_buffer = + replace_work_area_->bufferView().buffer(); ostringstream oss; repl_buffer.write(oss); - replaceString = from_utf8(oss.str()); //buffer_to_latex(replace_buffer); + //buffer_to_latex(replace_buffer); + replaceString = from_utf8(oss.str()); } else { replaceString = from_utf8(LYX_FR_NULL_STRING); } + FindAndReplaceOptions::SearchScope scope = + FindAndReplaceOptions::S_BUFFER; + if (CurrentDocument->isChecked()) + scope = FindAndReplaceOptions::S_BUFFER; + else if (MasterDocument->isChecked()) + scope = FindAndReplaceOptions::S_DOCUMENT; + else if (OpenDocuments->isChecked()) + scope = FindAndReplaceOptions::S_OPEN_BUFFERS; + else if (AllManualsRB->isChecked()) + scope = FindAndReplaceOptions::S_ALL_MANUALS; + else + LASSERT(false, /**/); LYXERR(Debug::FIND, "FindAndReplaceOptions: " << "searchstring=" << searchString << ", casesensitiv=" << casesensitive @@ -171,14 +484,14 @@ void FindAndReplaceWidget::findAndReplace( << ", ignoreformat=" << ignoreformat << ", regexp=" << regexp << ", replaceString" << replaceString - << ", keep_case=" << keep_case); - FindAndReplaceOptions opt(searchString, casesensitive, matchword, ! backwards, - expandmacros, ignoreformat, regexp, replaceString, keep_case); - LYXERR(Debug::FIND, "Dispatching LFUN_WORD_FINDADV"); - std::ostringstream oss; - oss << opt; - LYXERR(Debug::FIND, "Dispatching LFUN_WORD_FINDADV"); - dispatch(FuncRequest(LFUN_WORD_FINDADV, from_utf8(oss.str()))); + << ", keep_case=" << keep_case + << ", scope=" << scope); + FindAndReplaceOptions opt(searchString, casesensitive, matchword, + !backwards, expandmacros, ignoreformat, + regexp, replaceString, keep_case, scope); + view_.setBusy(true); + findAndReplaceScope(opt); + view_.setBusy(false); } @@ -188,7 +501,8 @@ void FindAndReplaceWidget::findAndReplace(bool backwards, bool replace) view_.message(_("No open document(s) in which to search")); return; } - // FIXME: create a Dialog::returnFocus() or something instead of this: + // FIXME: create a Dialog::returnFocus() + // or something instead of this: view_.setCurrentWorkArea(view_.currentMainWorkArea()); findAndReplace(caseCB->isChecked(), wordsCB->isChecked(), @@ -197,28 +511,22 @@ void FindAndReplaceWidget::findAndReplace(bool backwards, bool replace) ignoreFormatCB->isChecked(), replace, keepCaseCB->isChecked()); - view_.currentMainWorkArea()->redraw(); } -void FindAndReplaceWidget::on_regexpInsertCombo_currentIndexChanged(int index) +void FindAndReplaceWidget::insertRegexp(QAction * action) { - static char const * regexps[] = { - ".*", ".+", "[a-z]+", "[0-9]+", "" - }; - LYXERR(Debug::FIND, "Index: " << index); - if (index >= 1 && index < 1 + int(sizeof(regexps)/sizeof(regexps[0]))) { - find_work_area_->setFocus(); - Cursor & cur = find_work_area_->bufferView().cursor(); - if (! cur.inRegexped()) - dispatch(FuncRequest(LFUN_REGEXP_MODE)); - dispatch(FuncRequest(LFUN_SELF_INSERT, regexps[index - 1])); - regexpInsertCombo->setCurrentIndex(0); - } + string const regexp = fromqstr(action->data().toString()); + LYXERR(Debug::FIND, "Regexp: " << regexp); + find_work_area_->setFocus(); + Cursor & cur = find_work_area_->bufferView().cursor(); + if (!cur.inRegexped()) + dispatch(FuncRequest(LFUN_REGEXP_MODE)); + dispatch(FuncRequest(LFUN_SELF_INSERT, regexp)); } -void FindAndReplaceWidget::on_closePB_clicked() +void FindAndReplaceWidget::hideDialog() { dispatch(FuncRequest(LFUN_DIALOG_TOGGLE, "findreplaceadv")); } @@ -258,13 +566,10 @@ void FindAndReplaceWidget::on_replaceallPB_clicked() void FindAndReplaceWidget::showEvent(QShowEvent * /* ev */) { - replace_work_area_->redraw(); - find_work_area_->setFocus(); view_.setCurrentWorkArea(find_work_area_); LYXERR(Debug::FIND, "Selecting entire find buffer"); dispatch(FuncRequest(LFUN_BUFFER_BEGIN)); dispatch(FuncRequest(LFUN_BUFFER_END_SELECT)); - find_work_area_->redraw(); find_work_area_->installEventFilter(this); replace_work_area_->installEventFilter(this); } @@ -278,20 +583,16 @@ void FindAndReplaceWidget::hideEvent(QHideEvent *ev) } -bool FindAndReplaceWidget::initialiseParams(std::string const & /* params */) +bool FindAndReplaceWidget::initialiseParams(std::string const & /*params*/) { -// find_work_area_->redraw(); -// replace_work_area_->redraw(); -// find_work_area_->setFocus(); -// dispatch(FuncRequest(LFUN_BUFFER_BEGIN)); -// dispatch(FuncRequest(LFUN_BUFFER_END_SELECT)); return true; } FindAndReplace::FindAndReplace(GuiView & parent, Qt::DockWidgetArea area, Qt::WindowFlags flags) - : DockView(parent, "Find LyX", qt_("Find LyX Dialog"), area, flags) + : DockView(parent, "Find LyX", qt_("Advanced Find and Replace"), + area, flags) { widget_ = new FindAndReplaceWidget(parent); setWidget(widget_);