From: hama Date: Sat, 19 Dec 2015 21:07:01 +0000 (+0100) Subject: ReFind: file search in background X-Git-Url: https://gitweb.hamatoma.de/?a=commitdiff_plain;h=b343cef70393238a685eec22788dde2773a7387c;p=reqt ReFind: file search in background * own thread for file search * "clear" button * "append" checkbox * full path in column path (not relative) * removed: base dir to clipboard --- diff --git a/appl/refind/filefinder.cpp b/appl/refind/filefinder.cpp index 5f66614..34c3172 100644 --- a/appl/refind/filefinder.cpp +++ b/appl/refind/filefinder.cpp @@ -34,28 +34,16 @@ FileFinder::FileFinder() : m_baseDir(""), m_checkDates(false), m_excludedDirs(), - m_textFinder(NULL){ + m_textFinder(NULL), + m_table(NULL), + m_statistics(), + m_announcer(NULL), + m_observer(NULL){ m_youngerThan.setMSecsSinceEpoch(0); m_olderThan.setMSecsSinceEpoch(0); } -FileFinder::~FileFinder(){ -} -/** - * Resets the data. - */ -void FileFinder::clear(){ - m_lines = 0; - m_patterns.clear(); - m_minSize = 0; - m_maxSize = -1; - m_youngerThan.setMSecsSinceEpoch(0); - m_olderThan.setMSecsSinceEpoch(0); - m_fileTypes = 0; - m_minDepth = 0; - m_maxDepth = 512; - // m_baseDir; - m_checkDates = false; +FileFinder::~FileFinder(){ } /** @@ -101,23 +89,21 @@ void FileFinder::setTextFinder(TextFinder* textFinder){ /** * Fills the table with the data of the filtered files of a given directory. * + * This method is recursive over all directories. + * * @param path the directory to inspect * @param depth the depth of the directory (relative to base directory) - * @param table OUT: the table to fill - * @param statistics OUT: statistic about the found objects */ -void FileFinder::fillTable(const QString& path, int depth, QTableWidget* table, - Statistics& statistics){ +void FileFinder::fillTable(const QString& path, int depth){ QFileInfoList entries; QDir dir(path); - table->setSortingEnabled(false); QDir::Filters filters = m_fileTypes | QDir::NoDotAndDotDot; if (m_patterns.count() == 0) entries = dir.entryInfoList(filters, QDir::NoSort); else entries = dir.entryInfoList(m_patterns, filters, QDir::NoSort); - QString relativePath = ReQStringUtils::nativePath( - path.mid(1 + m_baseDir.length())); + //QString relativePath = ReQStringUtils::nativePath( + //path.mid(1 + m_baseDir.length())); QString node, ext; QList antiPatterns; QStringList::const_iterator it3; @@ -127,6 +113,7 @@ void FileFinder::fillTable(const QString& path, int depth, QTableWidget* table, } QList::const_iterator it2; QList ::const_iterator it; + clock_t nextUpdate = clock() + CLOCKS_PER_SEC; for (it = entries.begin(); it != entries.end(); ++it){ node = it->fileName(); if (node == "." || node == "..") @@ -141,30 +128,35 @@ void FileFinder::fillTable(const QString& path, int depth, QTableWidget* table, if (ignore) continue; if (depth >= m_minDepth && isValid(*it)){ - if (m_lines >= table->rowCount()){ - table->setRowCount(m_lines + 500); + if (m_lines >= m_table->rowCount()){ + m_table->setRowCount(m_lines + 500); } bool isDir = it->isDir(); if (isDir) - statistics.m_dirs++; + m_statistics.m_dirs++; else - statistics.m_files++; - table->setItem(m_lines, TC_NODE, new QTableWidgetItem(node)); + m_statistics.m_files++; + m_table->setItem(m_lines, TC_NODE, new QTableWidgetItem(node)); int ix = node.lastIndexOf('.'); ext = ix <= 0 ? "" : node.mid(ix + 1).toLower(); - table->setItem(m_lines, TC_EXT, new QTableWidgetItem(ext)); - table->setItem(m_lines, TC_TYPE, new QTableWidgetItem(typeOf(*it))); - table->setItem(m_lines, TC_PATH, new QTableWidgetItem(relativePath)); + m_table->setItem(m_lines, TC_EXT, new QTableWidgetItem(ext)); + m_table->setItem(m_lines, TC_TYPE, new QTableWidgetItem(typeOf(*it))); + m_table->setItem(m_lines, TC_PATH, new QTableWidgetItem(path)); QTableWidgetItem* item = new QTableWidgetItem( isDir ? "" : fileSize(it->size())); if (!isDir) - statistics.m_bytes += it->size(); + m_statistics.m_bytes += it->size(); item->setTextAlignment(Qt::AlignRight); - table->setItem(m_lines, TC_SIZE, item); - table->setItem(m_lines, TC_MODIFIED, + m_table->setItem(m_lines, TC_SIZE, item); + m_table->setItem(m_lines, TC_MODIFIED, new QTableWidgetItem( it->lastModified().toString("yyyy.MM.dd/hh:mm:ss"))); m_lines++; + clock_t now = clock(); + if (now > nextUpdate){ + m_table->setRowCount(m_lines); + nextUpdate = now + CLOCKS_PER_SEC; + } } } for (it2 = antiPatterns.begin(); it2 != antiPatterns.end(); ++it2){ @@ -177,11 +169,44 @@ void FileFinder::fillTable(const QString& path, int depth, QTableWidget* table, for (it = entries.begin(); it != entries.end(); ++it){ QString node = it->fileName(); if (!filtered || !isExcludedDir(node)) - fillTable(path + QDir::separator() + node, depth + 1, table, - statistics); + fillTable(path + QDir::separator() + node, depth + 1); } } - table->setRowCount(m_lines); + m_table->setRowCount(m_lines); +} +/** + * Runs a file search in a second thread. + */ +void FileFinder::run() +{ + search(); +} + +/** + * Fills the table with the data of the filtered files of a given directory. + */ +void FileFinder::search(){ + if (! m_append){ + m_statistics.clear(); + m_lines = 0; + } + clock_t start = clock(); + m_table->setSortingEnabled(false); + fillTable(ReQStringUtils::chomp(m_baseDir, OS_SEPARATOR), 0); + m_statistics.m_runtimeSeconds = (double) (clock() - start) + / CLOCKS_PER_SEC; + if (m_announcer != NULL){ + QString msg; + msg.sprintf( + I18N::s2b(QObject::tr( + "Found: %d dir(s) and %d file(s) with %.6f MByte. Duration of the search: %.3f sec")), + m_statistics.m_dirs, m_statistics.m_files, + m_statistics.m_bytes / 1000000.0, m_statistics.m_runtimeSeconds); + m_announcer->say(LOG_INFO, msg); + } + m_table->setSortingEnabled(true); + if (m_observer != NULL) + m_observer->notify("ready"); } /** @@ -229,13 +254,27 @@ bool FileFinder::isValid(const QFileInfo& file){ return rc; } +void FileFinder::setAppend(bool append) +{ + m_append = append; +} + +/** + * Sets the announcer for the summary message at the end. + * + * @param announcer the announcer + */ +void FileFinder::setAnnouncer(ReAnnouncer* announcer) +{ + m_announcer = announcer; +} /** * Sets the base directory. * * @param baseDir the directory where the search starts */ void FileFinder::setBaseDir(const QString& baseDir){ - m_baseDir = baseDir; + m_baseDir = baseDir; } /** @@ -293,6 +332,15 @@ void FileFinder::setMinSize(const int64_t& minSize){ m_minSize = minSize; } +/** + * Sets the observer object. Will be notified about the search exit. + * @param observer + */ +void FileFinder::setObserver(ReObserver* observer) +{ + m_observer = observer; +} + /** * Sets the date time which is the lower bound. * @@ -325,6 +373,15 @@ void FileFinder::setPatterns(const QStringList& patterns){ } } +/** + * Sets the table to fill. + * @param table the table containing the found files + */ +void FileFinder::setTable(QTableWidget* table) +{ + m_table = table; +} + /** * Sets the date time which is the upper bound. * @@ -336,3 +393,12 @@ void FileFinder::setYoungerThan(const QDateTime& youngerThan){ m_checkDates = true; } + +/** + * Returns the statistics about the search. + * @return the statistics + */ +const Statistics& FileFinder::statistics() const +{ + return m_statistics; +} diff --git a/appl/refind/filefinder.hpp b/appl/refind/filefinder.hpp index 48998be..8292633 100644 --- a/appl/refind/filefinder.hpp +++ b/appl/refind/filefinder.hpp @@ -17,14 +17,16 @@ #include class TextFinder; -class FileFinder { +class FileFinder : public QThread{ public: FileFinder(); ~FileFinder(); public: - void clear(); - void fillTable(const QString& path, int depth, QTableWidget* table, - Statistics& statistics); + void fillTable(const QString& path, int depth); + void run(); + void search(); + void setAppend(bool append); + void setAnnouncer(ReAnnouncer* announcer); void setBaseDir(const QString& baseDir); void setFiletypes(const QDir::Filters& filetypes); void setExcludedDirs(const QStringList& excludedDirs); @@ -32,11 +34,13 @@ public: void setMaxSize(const int64_t& maxSize); void setMinDepth(int minDepth); void setMinSize(const int64_t& minSize); + void setObserver(ReObserver* observer); void setOlderThan(const QDateTime& olderThan); void setPatterns(const QStringList& patterns); + void setTable(QTableWidget* table); void setTextFinder(TextFinder* textFinder); void setYoungerThan(const QDateTime& youngerThan); - + const Statistics& statistics() const; private: bool isExcludedDir(const QString& node); bool isValid(const QFileInfo& file); @@ -56,6 +60,11 @@ private: QStringList m_excludedDirs; // Only used to hold the search parameters: TextFinder* m_textFinder; + QTableWidget* m_table; + Statistics m_statistics; + ReAnnouncer* m_announcer; + ReObserver* m_observer; + bool m_append; }; #endif // FILEFINDER_HPP diff --git a/appl/refind/mainwindow.cpp b/appl/refind/mainwindow.cpp index f21234a..a035843 100644 --- a/appl/refind/mainwindow.cpp +++ b/appl/refind/mainwindow.cpp @@ -31,7 +31,8 @@ #include #include -const QString VERSION("2015.06.01"); +const QString VERSION("2015.12.15"); + /** * @brief Constructor. * @@ -52,7 +53,8 @@ MainWindow::MainWindow(const QString& startDir, const QString& homeDir, m_homeDir(homeDir), m_storageFile(), m_contextHandlers(), - m_logger(new ReMemoryLogger()){ + m_logger(new ReMemoryLogger()), + m_finder(NULL){ ui->setupUi(this); initializeHome(); m_statusMessage = new QLabel(tr("Welcome at refind")); @@ -63,8 +65,10 @@ MainWindow::MainWindow(const QString& startDir, const QString& homeDir, ui->tableWidget->setMainWindow(this); statusBar()->addWidget(m_statusMessage); connect(ui->actionSearch, SIGNAL(triggered()), this, SLOT(search())); + connect(ui->actionClear, SIGNAL(triggered()), this, SLOT(clear())); connect(ui->pushButtonSearch, SIGNAL(clicked()), this, SLOT(search())); connect(ui->pushButtonSearch2, SIGNAL(clicked()), this, SLOT(search())); + connect(ui->pushButtonClear, SIGNAL(clicked()), this, SLOT(clear())); connect(ui->actionUp, SIGNAL(triggered()), this, SLOT(up())); connect(ui->pushButtonUp, SIGNAL(clicked()), this, SLOT(up())); connect(ui->actionSelectDirectory, SIGNAL(triggered()), this, @@ -180,12 +184,7 @@ void MainWindow::baseDirToClipboard(){ * @return the absolute path of the file given by the row */ QString MainWindow::buildAbsPath(int row, bool withNode, bool uriFormat){ - QString rc(m_lastBaseDir.absolutePath()); - QString value = cellAsText(row, TC_PATH); - if (!value.isEmpty()){ - ReQStringUtils::ensureLastChar(rc, '/'); - rc += value; - } + QString rc = cellAsText(row, TC_PATH); if (withNode){ ReQStringUtils::ensureLastChar(rc, '/'); rc += cellAsText(row, TC_NODE); @@ -268,7 +267,15 @@ QString replaceEscSequences(const QString& text){ } void MainWindow::closing(){ - saveState(); + saveState(); +} + +/** + * Clears the table. + */ +void MainWindow::clear() +{ + ui->tableWidget->setRowCount(0); } /** @@ -419,6 +426,7 @@ void MainWindow::fullNameToClipboard(){ if (row >= 0){ QClipboard* clipboard = QApplication::clipboard(); QString path = buildAbsPath(row); + ReQStringUtils::ensureLastChar(path, OS_SEPARATOR); path += cellAsText(row, TC_NODE); clipboard->setText(path); } @@ -620,6 +628,21 @@ void MainWindow::headerPlaceholder(){ handlePlaceholder(ui->comboBoxHeader); } +/** + * Informs the instance about some state changes. + * + * @param message description of the state change + * @return SUCCESS + */ +ReObserver::ReturnCode MainWindow::notify(const char* message) +{ + if (strcmp(message, "ready") == 0){ + ui->pushButtonSearch->setEnabled(true); + ui->pushButtonSearch2->setEnabled(true); + } + return ReObserver::SUCCESS; +} + /** * Prepares the context menu of the result table. * @@ -763,62 +786,66 @@ m_contextHandlers.save(storage); storage.close(); } +/** + * Sets the properties of the file finder. + * + * @param finder OUT: the finder to populate + */ +void MainWindow::populateFinder(FileFinder& finder){ + finder.setObserver(this); + finder.setBaseDir(comboText(ui->comboBoxDirectory)); + finder.setTable(ui->tableWidget); + m_lastBaseDir.cd(comboText(ui->comboBoxDirectory)); + finder.setMaxSize(comboSize(ui->comboBoxMaxSize)); + finder.setMinSize(comboSize(ui->comboBoxMinSize)); + finder.setOlderThan(comboDate(ui->comboBoxOlder)); + finder.setYoungerThan(comboDate(ui->comboBoxYounger)); + finder.setMinDepth(comboInt(ui->comboBoxMinDepth, 0)); + finder.setMaxDepth(comboInt(ui->comboBoxMaxDepth, -1)); + finder.setFiletypes(buildFileTypes()); + finder.setAppend(ui->checkBoxAppend->isChecked()); + QStringList patterns; + QString value = comboText(ui->comboBoxFilePatterns); + if (!value.isEmpty()) + patterns = value.split(","); + finder.setPatterns(patterns); + value = comboText(ui->comboBoxExcludedDirs); + if (value.indexOf('/') >= 0 || value.indexOf('\\') >= 0) + guiError(ui->comboBoxExcludedDirs, tr("no path delimiter allowed")); + else if (value.indexOf('*') >= 0) + guiError(ui->comboBoxExcludedDirs, + tr("no patterns allowed. Do not use '*")); + else if (!value.isEmpty()) + patterns = value.split(","); + finder.setExcludedDirs(patterns); + if (m_errors == 0){ + prepareTextFind(); + if (!comboText(ui->comboBoxTextPattern).isEmpty()) + finder.setTextFinder(&m_textFinder); + } +} + /** * Handles the "search" button. */ void MainWindow::search(){ -m_errors = 0; -QApplication::setOverrideCursor (QCursor(Qt::WaitCursor));QString -path = comboText(ui->comboBoxDirectory); -QFileInfo dir(path); -if (!dir.exists()) - guiError(ui->comboBoxDirectory, tr("directory not found: ") + path); -else if (!dir.isDir()) - guiError(ui->comboBoxDirectory, tr("not a directory: ") + path); -else{ - m_lastBaseDir.cd(path); - FileFinder finder; - finder.setBaseDir(path); - finder.setMaxSize(comboSize(ui->comboBoxMaxSize)); - finder.setMinSize(comboSize(ui->comboBoxMinSize)); - finder.setOlderThan(comboDate(ui->comboBoxOlder)); - finder.setYoungerThan(comboDate(ui->comboBoxYounger)); - finder.setMinDepth(comboInt(ui->comboBoxMinDepth, 0)); - finder.setMaxDepth(comboInt(ui->comboBoxMaxDepth, -1)); - finder.setFiletypes(buildFileTypes()); - QStringList patterns; - QString value = comboText(ui->comboBoxFilePatterns); - if (!value.isEmpty()) - patterns = value.split(","); - finder.setPatterns(patterns); - value = comboText(ui->comboBoxExcludedDirs); - if (value.indexOf('/') >= 0 || value.indexOf('\\') >= 0) - guiError(ui->comboBoxExcludedDirs, tr("no path delimiter allowed")); - else if (value.indexOf('*') >= 0) - guiError(ui->comboBoxExcludedDirs, - tr("no patterns allowed. Do not use '*")); - else if (!value.isEmpty()) - patterns = value.split(","); - finder.setExcludedDirs(patterns); - prepareTextFind(); - if (m_errors == 0){ - if (!comboText(ui->comboBoxTextPattern).isEmpty()) - finder.setTextFinder(&m_textFinder); - m_statistics.clear(); - clock_t start = clock(); - finder.fillTable(path, 0, ui->tableWidget, m_statistics); - m_statistics.m_runtimeSeconds = (double) (clock() - start) - / CLOCKS_PER_SEC; - QString msg; - msg.sprintf( - I18N::s2b(QObject::tr( - "Found: %d dir(s) and %d file(s) with %.6f MByte. Duration of the search: %.3f sec")), - m_statistics.m_dirs, m_statistics.m_files, - m_statistics.m_bytes / 1000000.0, m_statistics.m_runtimeSeconds); - say(LOG_INFO, msg); - } -} -QApplication::restoreOverrideCursor(); + m_errors = 0; + QString path = comboText(ui->comboBoxDirectory); + QFileInfo dir(path); + if (!dir.exists()) + guiError(ui->comboBoxDirectory, tr("directory not found: ") + path); + else if (!dir.isDir()) + guiError(ui->comboBoxDirectory, tr("not a directory: ") + path); + else{ + QApplication::setOverrideCursor (QCursor(Qt::WaitCursor)); + if (m_finder == NULL) + m_finder = new FileFinder; + populateFinder(*m_finder); + if (! ui->checkBoxAppend->isChecked()) + clear(); + m_finder->start(); + QApplication::restoreOverrideCursor(); + } } /** * Handles the push of the button "select directory". @@ -876,3 +903,4 @@ if (dir.exists()){ } } } + diff --git a/appl/refind/mainwindow.hpp b/appl/refind/mainwindow.hpp index d38e53a..d7bb211 100644 --- a/appl/refind/mainwindow.hpp +++ b/appl/refind/mainwindow.hpp @@ -34,8 +34,9 @@ class MainWindow; enum TableColumns { TC_NODE, TC_EXT, TC_SIZE, TC_MODIFIED, TC_TYPE, TC_PATH }; +class FileFinder; -class MainWindow: public QMainWindow, public ReGuiValidator { +class MainWindow: public QMainWindow, public ReGuiValidator, public ReObserver { Q_OBJECT @@ -47,11 +48,14 @@ public: public: void fileDragging(); protected: + void findFiles(); + void populateFinder(FileFinder& finder); private slots: void about(); void absPathToClipboard(); void baseDirToClipboard(); void closing(); + void clear(); void exportFiles(); void filePlaceholder(); void footerPlaceholder(); @@ -59,9 +63,11 @@ private slots: void handleTableContextMenu(const QPoint& position); void headerClicked(int col); void headerPlaceholder(); + virtual ReturnCode notify(const char* message); void options(); void preview(); void resetParameters(); + virtual bool say(ReLoggerLevel level, const QString& message); void saveState(); void search(); void selectDirectory(); @@ -84,7 +90,6 @@ private: QString replaceGlobalPlaceholders(QComboBox* combo, QMap & placeholders); void restoreState(); - virtual bool say(ReLoggerLevel level, const QString& message); private: Ui::MainWindow *ui; QLabel* m_statusMessage; @@ -101,6 +106,7 @@ private: QAction* m_actionStartShell; ContextHandlerList m_contextHandlers; ReLogger* m_logger; + FileFinder* m_finder; }; #endif // MAINWINDOW_HPP diff --git a/appl/refind/mainwindow.ui b/appl/refind/mainwindow.ui index 3636517..e666097 100644 --- a/appl/refind/mainwindow.ui +++ b/appl/refind/mainwindow.ui @@ -116,6 +116,32 @@ + + + + Clears the file table + + + C&lear + + + + + + false + + + + + + + Adds the result of the next search to the table + + + Append + + + @@ -579,7 +605,7 @@ 10 11 981 - 103 + 116 @@ -849,7 +875,7 @@ Size (MByte) - AlignRight|AlignBottom + AlignTrailing|AlignBottom @@ -882,7 +908,7 @@ 0 0 1030 - 23 + 26 @@ -920,6 +946,8 @@ + + @@ -965,7 +993,6 @@ - @@ -1169,6 +1196,17 @@ Ctrl+Shift+V + + + C&lear + + + Clears the file table + + + Ctrl+L + + diff --git a/appl/refind/utils.cpp b/appl/refind/utils.cpp index b04e151..da9e65a 100644 --- a/appl/refind/utils.cpp +++ b/appl/refind/utils.cpp @@ -184,7 +184,7 @@ void ContextHandlerList::restore(ReStateStorage& storage){ long int lValue = atol(cols.at(3).toLatin1().constData()); handler->m_fileType = ContextHandler::FileType(lValue); lValue = atol(cols.at(4).toLatin1().constData()); - handler->m_directoryMode = ContextHandler::DirMode(atol(lValue)); + handler->m_directoryMode = ContextHandler::DirMode(lValue); m_list.append(handler); } if (m_list.size() == 0){ diff --git a/base/rebase.hpp b/base/rebase.hpp index 5b61dd6..0b03d5e 100644 --- a/base/rebase.hpp +++ b/base/rebase.hpp @@ -149,6 +149,21 @@ inline int hexToInt(char hex, int defaultValue = -1){ inline int roundInt(double value) { return (int) round(value); } +/** An observer can be informed about state changes. + * Pure abstract class. + */ +class ReObserver{ +public: + enum ReturnCode { SUCCESS, STOP, EXIT }; + /** Informs the observer about a state change. + * @param message NULL or an info about the state change + * @return SUCCESS: success
+ * STOP: the current action should be stopped
+ * EXIT: the process should be stopped + */ + virtual ReturnCode notify(const char* message) = 0; +}; + #define ReUseParameter(var) (void) var #include "remodules.hpp"