diff --git a/gui/mainwindow.cpp b/gui/mainwindow.cpp index 5a589ec..7980ed4 100644 --- a/gui/mainwindow.cpp +++ b/gui/mainwindow.cpp @@ -31,10 +31,8 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi void MainWindow::connectSignals() { - connect(ui->txtSearch, &QLineEdit::textChanged, this, &MainWindow::lineEditTextChanged); connect(ui->txtSearch, &QLineEdit::returnPressed, this, &MainWindow::lineEditReturnPressed); - connect(this, &MainWindow::beginFileSearch, searchWorker, &SearchWorker::searchForFile); - connect(this, &MainWindow::beginContentSearch, searchWorker, &SearchWorker::searchForContent); + connect(this, &MainWindow::beginSearch, searchWorker, &SearchWorker::search); connect(searchWorker, &SearchWorker::searchResultsReady, this, &MainWindow::handleSearchResults); connect(searchWorker, &SearchWorker::searchCancelled, this, &MainWindow::handleCancelledSearch); connect(ui->treeResultsList, &QTreeWidget::itemActivated, this, &MainWindow::treeSearchItemActivated); @@ -99,25 +97,10 @@ void MainWindow::pdfPreviewReceived(PdfPreview preview) } void MainWindow::lineEditReturnPressed() -{ - if(pdfTabActive() && pdfDirty) - { - makePdfPreview(); - } -} - -void MainWindow::lineEditTextChanged() { QString q = ui->txtSearch->text(); - if(q.startsWith("|")) - { - q = q.mid(1); - emit beginContentSearch(q); - } - else - { - emit beginFileSearch(q); - } + // TODO: validate q; + emit beginSearch(q); } void MainWindow::handleSearchResults(const QVector &results) @@ -147,6 +130,10 @@ void MainWindow::handleSearchResults(const QVector &results) ui->treeResultsList->resizeColumnToContents(0); ui->treeResultsList->resizeColumnToContents(1); pdfDirty = !this->pdfSearchResults.empty(); + if(pdfTabActive() && pdfDirty) + { + makePdfPreview(); + } } void MainWindow::makePdfPreview() diff --git a/gui/mainwindow.h b/gui/mainwindow.h index 99417ad..030b682 100644 --- a/gui/mainwindow.h +++ b/gui/mainwindow.h @@ -21,8 +21,7 @@ class MainWindow : public QMainWindow explicit MainWindow(QWidget *parent = 0); ~MainWindow(); signals: - void beginFileSearch(const QString &query); - void beginContentSearch(const QString &query); + void beginSearch(const QString &query); void startPdfPreviewGeneration(QVector paths, double scalefactor); private: @@ -41,7 +40,6 @@ class MainWindow : public QMainWindow unsigned int processedPdfPreviews; private slots: void lineEditReturnPressed(); - void lineEditTextChanged(); void handleSearchResults(const QVector &results); void handleCancelledSearch(); void treeSearchItemActivated(QTreeWidgetItem *item, int i); diff --git a/gui/searchworker.cpp b/gui/searchworker.cpp index bace4aa..3bfb37d 100644 --- a/gui/searchworker.cpp +++ b/gui/searchworker.cpp @@ -1,12 +1,14 @@ #include "searchworker.h" +#include #include +#include SearchWorker::SearchWorker() { } SearchWorker::SearchWorker(const QString &dbpath) { - QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE"); + db = QSqlDatabase::addDatabase("QSQLITE"); db.setDatabaseName(dbpath); if(!db.open()) { @@ -21,11 +23,114 @@ SearchWorker::SearchWorker(const QString &dbpath) "content_fts.content MATCH ? ORDER By file.mtime DESC, content.page ASC"); } -QString normalize(QString str) +QVector SearchWorker::tokenize(QString expression) { - str = str.replace(" ", " AND "); - str = str.replace("|", " OR "); - return str; + // TODO: merge lonewords + QVector result; + QRegularExpression rx("((?(\\.|\\w)+):(?\\((?[^\\)]+)\\)|(\\w)+)|(?AND|OR|!)|" + "(?\\(|\\))|(?\\w+))"); + QRegularExpressionMatchIterator i = rx.globalMatch(expression); + bool wasbool = true; + while(i.hasNext()) + { + QRegularExpressionMatch m = i.next(); + QString boolean = m.captured("boolean"); + QString filtername = m.captured("filtername"); + QString bracket = m.captured("bracket"); + QString loneword = m.captured("loneword"); + if(boolean != "") + { + /* if(wasbool) + { + throw new std::runtime_error("Bool after Bool is invalid"); + }*/ + wasbool = true; + result.append(Command(boolean)); + } + + if(bracket != "") + { + if(!wasbool) + { + if(bracket == "(") + { + result.append(Command("AND")); + } + } + result.append(Command(bracket)); + } + + if(loneword != "") + { + if(!wasbool) + { + result.append(Command("AND")); + } + wasbool = false; + result.append(Command("contains", loneword)); + } + if(filtername != "") + { + if(!wasbool) + { + result.append(Command("AND")); + } + wasbool = false; + QString value = m.captured("innerargs"); + if(value == "") + value = m.captured("args"); + result.append(Command(filtername, value)); + } + } + return result; +} + +QString SearchWorker::createSql(const SearchWorker::Command &cmd) +{ + QString key = cmd.key; + QString value = cmd.value; + if(key == "AND" || key == "OR" || key == "(" || key == ")") + { + return " " + key + " "; + } + if(key == "!") + { + return " NOT "; + } + if(key == "path.starts") + { + return " file.path LIKE '" + value + "%' "; + } + if(key == "path.ends") + { + return " file.path LIKE '%" + value + "' "; + } + if(key == "path.contains" || key == "inpath") + { + return " file.path LIKE '%" + value + "%' "; + } + if(key == "page") + { + return " content.page = " + value; + } + if(key == "contains") + { + return " ( COALESCE( (SELECT 1 FROM content_fts WHERE content_fts.content MATCH '" + value + + "' AND content_fts.ROWID= content.id), 0 ) )"; + } + qDebug() << "NOHIT" << key; + // TODO: exception? + return "NOTHING"; +} + +QString SearchWorker::makeSql(const QVector &tokens) +{ + QString result; + for(const Command &c : tokens) + { + result += createSql(c); + } + return result; } void SearchWorker::searchForFile(const QString &query) { @@ -59,3 +164,28 @@ void SearchWorker::searchForContent(const QString &query) } emit searchResultsReady(results); } + +void SearchWorker::search(const QString &query) +{ + QSqlQuery dbquery(db); + QVector results; + QString whereSql = makeSql(tokenize(query)); + QString prep = + "SELECT file.path AS path, content.page AS page, file.mtime AS mtime FROM file INNER JOIN content ON file.id = " + "content.fileid INNER JOIN content_fts ON content.id = content_fts.ROWID WHERE 1=1 AND " + + whereSql + " ORDER By file.mtime DESC, content.page ASC"; + dbquery.prepare(prep); + dbquery.exec(); + qDebug() << "prepped: " << prep; + qDebug() << dbquery.lastError(); + while(dbquery.next()) + { + SearchResult result; + + result.path = dbquery.value("path").toString(); + result.page = dbquery.value("page").toUInt(); + result.mtime = dbquery.value("mtime").toUInt(); + results.append(result); + } + emit searchResultsReady(results); +} diff --git a/gui/searchworker.h b/gui/searchworker.h index 7252a84..94bed0d 100644 --- a/gui/searchworker.h +++ b/gui/searchworker.h @@ -9,10 +9,27 @@ class SearchWorker : public QObject { + class Command + { + public: + QString key; + QString value; + + Command(QString key = "", QString value = "") + { + this->key = key; + this->value = value; + } + }; + Q_OBJECT private: QSqlQuery *queryFile; QSqlQuery *queryContent; + QVector tokenize(QString expression); + QString createSql(const Command &cmd); + QString makeSql(const QVector &tokens); + QSqlDatabase db; public: SearchWorker(); @@ -20,6 +37,7 @@ class SearchWorker : public QObject public slots: void searchForFile(const QString &query); void searchForContent(const QString &query); + void search(const QString &query); signals: void searchResultsReady(const QVector &results); void searchCancelled();