diff --git a/src/vexplorer.cpp b/src/vexplorer.cpp index ba21f482..dd680f81 100644 --- a/src/vexplorer.cpp +++ b/src/vexplorer.cpp @@ -783,3 +783,14 @@ void VExplorer::keyPressEvent(QKeyEvent *p_event) QWidget::keyPressEvent(p_event); } + +QString VExplorer::getRootDirectory() const +{ + const_cast(this)->init(); + + if (checkIndex()) { + return m_entries[m_index].m_directory; + } + + return QString(); +} diff --git a/src/vexplorer.h b/src/vexplorer.h index b28c7610..0133a27c 100644 --- a/src/vexplorer.h +++ b/src/vexplorer.h @@ -23,6 +23,8 @@ public: void setRootDirectory(const QString &p_path); + QString getRootDirectory() const; + protected: void showEvent(QShowEvent *p_event) Q_DECL_OVERRIDE; diff --git a/src/vsearch.cpp b/src/vsearch.cpp index 5ca34888..d7b719bc 100644 --- a/src/vsearch.cpp +++ b/src/vsearch.cpp @@ -133,6 +133,39 @@ QSharedPointer VSearch::search(const QVector &p_note return result; } +QSharedPointer VSearch::search(const QString &p_directoryPath) +{ + Q_ASSERT(!askedToStop()); + + QSharedPointer result(new VSearchResult(this)); + + if (p_directoryPath.isEmpty() || m_config->isEmpty()) { + result->m_state = VSearchState::Success; + return result; + } + + if ((!testTarget(VSearchConfig::Note) + && !testTarget(VSearchConfig::Folder)) + || testObject(VSearchConfig::Outline) + || testObject(VSearchConfig::Tag)) { + qDebug() << "search is not applicable for directory"; + result->m_state = VSearchState::Success; + return result; + } + + result->m_state = VSearchState::Busy; + + searchFirstPhase(p_directoryPath, p_directoryPath, result); + + if (result->hasSecondPhaseItems()) { + searchSecondPhase(result); + } else if (result->m_state == VSearchState::Busy) { + result->m_state = VSearchState::Success; + } + + return result; +} + void VSearch::searchFirstPhase(VFile *p_file, const QSharedPointer &p_result, bool p_searchContent) @@ -209,10 +242,7 @@ void VSearch::searchFirstPhase(VFile *p_file, void VSearch::searchFirstPhase(VDirectory *p_directory, const QSharedPointer &p_result) { - if (!testTarget(VSearchConfig::Note) - && !testTarget(VSearchConfig::Folder)) { - return; - } + Q_ASSERT(testTarget(VSearchConfig::Note) || testTarget(VSearchConfig::Folder)); bool opened = p_directory->isOpened(); if (!opened && !p_directory->open()) { @@ -324,6 +354,116 @@ exit: } } +void VSearch::searchFirstPhase(const QString &p_basePath, + const QString &p_directoryPath, + const QSharedPointer &p_result) +{ + Q_ASSERT(testTarget(VSearchConfig::Note) || testTarget(VSearchConfig::Folder)); + Q_ASSERT(!p_directoryPath.isEmpty()); + + QDir dir(p_directoryPath); + if (!dir.exists()) { + p_result->logError(QString("Directory %1 does not exist.").arg(p_directoryPath)); + p_result->m_state = VSearchState::Fail; + return; + } + + Q_ASSERT(dir.isAbsolute()); + + if (testTarget(VSearchConfig::Folder)) { + QString name = dir.dirName(); + if (testObject(VSearchConfig::Name)) { + if (matchNonContent(name)) { + VSearchResultItem *item = new VSearchResultItem(VSearchResultItem::Folder, + VSearchResultItem::LineNumber, + name, + p_directoryPath); + QSharedPointer pitem(item); + emit resultItemAdded(pitem); + } + } + + if (testObject(VSearchConfig::Path)) { + QString normPath(QDir(p_basePath).relativeFilePath(p_directoryPath)); + removeSlashFromPath(normPath); + if (matchNonContent(normPath)) { + VSearchResultItem *item = new VSearchResultItem(VSearchResultItem::Folder, + VSearchResultItem::LineNumber, + name, + p_directoryPath); + QSharedPointer pitem(item); + emit resultItemAdded(pitem); + } + } + } + + if (testTarget(VSearchConfig::Note)) { + QStringList files = dir.entryList(QDir::Files); + for (auto const & file : files) { + if (askedToStop()) { + qDebug() << "asked to cancel the search"; + p_result->m_state = VSearchState::Cancelled; + return; + } + + searchFirstPhaseFile(p_basePath, dir.absoluteFilePath(file), p_result); + } + } + + // Search subfolders. + QStringList subdirs = dir.entryList(QDir::AllDirs | QDir::NoDotAndDotDot); + for (auto const & sub : subdirs) { + if (askedToStop()) { + qDebug() << "asked to cancel the search"; + p_result->m_state = VSearchState::Cancelled; + return; + } + + searchFirstPhase(p_basePath, dir.absoluteFilePath(sub), p_result); + } +} + +void VSearch::searchFirstPhaseFile(const QString &p_basePath, + const QString &p_filePath, + const QSharedPointer &p_result) +{ + Q_ASSERT(testTarget(VSearchConfig::Note)); + + QString name = VUtils::fileNameFromPath(p_filePath); + if (!matchPattern(name)) { + return; + } + + if (testObject(VSearchConfig::Name)) { + if (matchNonContent(name)) { + VSearchResultItem *item = new VSearchResultItem(VSearchResultItem::Note, + VSearchResultItem::LineNumber, + name, + p_filePath); + QSharedPointer pitem(item); + emit resultItemAdded(pitem); + } + } + + if (testObject(VSearchConfig::Path)) { + QString normFilePath(QDir(p_basePath).relativeFilePath(p_filePath)); + removeSlashFromPath(normFilePath); + if (matchNonContent(normFilePath)) { + VSearchResultItem *item = new VSearchResultItem(VSearchResultItem::Note, + VSearchResultItem::LineNumber, + name, + p_filePath); + QSharedPointer pitem(item); + emit resultItemAdded(pitem); + } + } + + if (testObject(VSearchConfig::Content)) { + // Add an item for second phase process. + p_result->addSecondPhaseItem(p_filePath); + } +} + VSearchResultItem *VSearch::searchForOutline(const VFile *p_file) const { VEditTab *tab = g_mainWin->getEditArea()->getTab(p_file); diff --git a/src/vsearch.h b/src/vsearch.h index c71f0e2e..09d9ffd0 100644 --- a/src/vsearch.h +++ b/src/vsearch.h @@ -32,6 +32,9 @@ public: // Search folder for CurrentNotebook and AllNotebooks. QSharedPointer search(const QVector &p_notebooks); + // Search directory path for ExplorerDirectory. + QSharedPointer search(const QString &p_directoryPath); + // Clear resources after a search completed. void clear(); @@ -60,6 +63,14 @@ private: void searchFirstPhase(VNotebook *p_notebook, const QSharedPointer &p_result); + void searchFirstPhase(const QString &p_basePath, + const QString &p_directoryPath, + const QSharedPointer &p_result); + + void searchFirstPhaseFile(const QString &p_basePath, + const QString &p_filePath, + const QSharedPointer &p_result); + bool testTarget(VSearchConfig::Target p_target) const; bool testObject(VSearchConfig::Object p_object) const; diff --git a/src/vsearchconfig.h b/src/vsearchconfig.h index ea2516a3..2afa3780 100644 --- a/src/vsearchconfig.h +++ b/src/vsearchconfig.h @@ -194,7 +194,8 @@ struct VSearchConfig OpenedNotes, CurrentFolder, CurrentNotebook, - AllNotebooks + AllNotebooks, + ExplorerDirectory }; enum Object diff --git a/src/vsearcher.cpp b/src/vsearcher.cpp index ed09121c..7ded6055 100644 --- a/src/vsearcher.cpp +++ b/src/vsearcher.cpp @@ -16,6 +16,7 @@ #include "vnotebook.h" #include "vnote.h" #include "vconfigmanager.h" +#include "vexplorer.h" extern VMainWindow *g_mainWin; @@ -243,12 +244,14 @@ void VSearcher::initUIFields() m_searchScopeCB->addItem(tr("Current Folder"), VSearchConfig::CurrentFolder); m_searchScopeCB->addItem(tr("Current Notebook"), VSearchConfig::CurrentNotebook); m_searchScopeCB->addItem(tr("All Notebooks"), VSearchConfig::AllNotebooks); + m_searchScopeCB->addItem(tr("Explorer Directory"), VSearchConfig::ExplorerDirectory); m_searchScopeCB->setCurrentIndex(m_searchScopeCB->findData(config.m_scope)); // Object. m_searchObjectCB->addItem(tr("Name"), VSearchConfig::Name); m_searchObjectCB->addItem(tr("Content"), VSearchConfig::Content); m_searchObjectCB->addItem(tr("Tag"), VSearchConfig::Tag); + m_searchObjectCB->addItem(tr("Path"), VSearchConfig::Path); m_searchObjectCB->setCurrentIndex(m_searchObjectCB->findData(config.m_object)); // Target. @@ -314,20 +317,32 @@ void VSearcher::handleInputChanged() readyToSearch = !keyword.isEmpty(); if (readyToSearch) { - // Other targets are only available for Name and Path. int obj = m_searchObjectCB->currentData().toInt(); - if (obj != VSearchConfig::Name && obj != VSearchConfig::Path) { - int target = m_searchTargetCB->currentData().toInt(); - if (!(target & VSearchConfig::Note)) { - readyToSearch = false; - } + int target = m_searchTargetCB->currentData().toInt(); + int scope = m_searchScopeCB->currentData().toInt(); + + // Other targets are only available for Name and Path. + if (!(obj & VSearchConfig::Name) + && !(obj & VSearchConfig::Path) + && !(target & VSearchConfig::Note)) { + readyToSearch = false; } - if (readyToSearch && obj == VSearchConfig::Outline) { + if (readyToSearch + && obj == VSearchConfig::Outline + && scope != VSearchConfig::CurrentNotebook + && scope != VSearchConfig::OpenedNotes) { // Outline is available only for CurrentNote and OpenedNotes. - int scope = m_searchScopeCB->currentData().toInt(); - if (scope != VSearchConfig::CurrentNote - && scope != VSearchConfig::OpenedNotes) { + readyToSearch = false; + } + + if (readyToSearch && scope == VSearchConfig::ExplorerDirectory) { + if (!(obj & VSearchConfig::Name) + && !(obj & VSearchConfig::Path) + && !(obj & VSearchConfig::Content)) { + readyToSearch = false; + } else if (!(target & VSearchConfig::Note) + && !(target & VSearchConfig::Folder)) { readyToSearch = false; } } @@ -429,6 +444,19 @@ void VSearcher::startSearch() break; } + case VSearchConfig::ExplorerDirectory: + { + QString rootDirectory = g_mainWin->getExplorer()->getRootDirectory(); + if (!rootDirectory.isEmpty()) { + QString msg(tr("Search Explorer directory %1.").arg(rootDirectory)); + appendLogLine(msg); + showMessage(msg); + } + + result = m_search.search(rootDirectory); + break; + } + default: break; } diff --git a/src/vsearchresulttree.cpp b/src/vsearchresulttree.cpp index f2b97cd0..e56acbe9 100644 --- a/src/vsearchresulttree.cpp +++ b/src/vsearchresulttree.cpp @@ -10,6 +10,7 @@ #include "vnotefile.h" #include "vcart.h" #include "vhistorylist.h" +#include "vexplorer.h" extern VNote *g_vnote; @@ -272,6 +273,10 @@ void VSearchResultTree::activateItem(const QTreeWidgetItem *p_item) const VDirectory *dir = g_vnote->getInternalDirectory(resItem->m_path); if (dir) { g_mainWin->locateDirectory(dir); + } else { + // External directory. + g_mainWin->showExplorerPanel(true); + g_mainWin->getExplorer()->setRootDirectory(resItem->m_path); } break;