9 Υποβολές

Συγγραφέας SHA1 Μήνυμα Ημερομηνία
10d61acbd0 shared,gui: SearchResult: remove page vector
Since the previous commit we don't group the results
anymore, making the vector redundant
2022-08-24 00:00:11 +02:00
eef0fae137 shared,gui,cli: Fix intra-file ordering for content search results
group_concat() does not preserve order of the ORDRE BY rank,
making the ordering quite meaningless for pages inside a file.

The recently introduced combobox to filter on a per file basis
should anyway be prefered than any kind of grouping in queries.

So we just remove the groupings here.

"All files" in the previews tab thus should show the best results
first now, from any files part of the result set.

A GUI option to sort by page instead of rank can be considered.
2022-08-23 23:44:47 +02:00
d8205a0da4 gui: Disable search box as long as previews are being generated
Otherwise "spamming" queries can cause high load and
many outstanding may arrive only to be discarded anyway
for not being part of the recent query.
2022-08-23 17:37:05 +02:00
877224b6e1 gui: mainwindow: Only trigger preview generation when in tab
Indirectly generation would fire when we set combobox index
programitcally. So it's slot should only trigger the generation
when the user is viewing the preview tab
2022-08-23 17:35:52 +02:00
14730ed208 gui: mainwindow: Add vertical scroll option, default to it
Seems horizontal mode is too unusual according to multiple
feedback.

Allow choosing this the mode in the settings
2022-08-21 22:57:48 +02:00
fe610d3068 mainwindow: Allow CTRL + mouse wheel to zoom on previews 2022-08-21 18:42:32 +02:00
0c1b57d911 mainwindow: Save/Restore history 2022-08-21 17:48:43 +02:00
2885e40a3a mainwindow: Add search history. Allow going up/down with arrow keys 2022-08-21 17:47:11 +02:00
c0f4087937 sqlitesearch: escapeFtsArgument: Fix handling of '*' prefix search
The * must not be in quotes
2022-08-21 07:55:49 +02:00
7 αρχεία άλλαξαν με 232 προσθήκες και 53 διαγραφές

@ -33,11 +33,18 @@ int CommandSearch::handle(QStringList arguments)
try try
{ {
QHash<QString, bool> seenMap;
auto results = dbService->search(query); auto results = dbService->search(query);
for(SearchResult &result : results) for(const SearchResult &result : results)
{ {
Logger::info() << result.fileData.absPath << Qt::endl; const QString &absPath = result.fileData.absPath;
if(!seenMap.contains(absPath))
{
seenMap[absPath] = true;
Logger::info() << absPath << Qt::endl;
}
} }
} }
catch(LooqsGeneralException &e) catch(LooqsGeneralException &e)

@ -60,6 +60,10 @@ MainWindow::MainWindow(QWidget *parent, QString socketPath)
QString ignorePatterns = settings.value("ignorePatterns").toString(); QString ignorePatterns = settings.value("ignorePatterns").toString();
ui->txtIgnorePatterns->setText(ignorePatterns); ui->txtIgnorePatterns->setText(ignorePatterns);
QStringList searchHistoryList = settings.value(SETTINGS_KEY_SEARCHHISTORY).toStringList();
this->searchHistory = searchHistoryList.toVector();
this->currentSearchHistoryIndex = this->searchHistory.size();
ui->spinPreviewPage->setValue(1); ui->spinPreviewPage->setValue(1);
ui->spinPreviewPage->setMinimum(1); ui->spinPreviewPage->setMinimum(1);
@ -70,6 +74,9 @@ MainWindow::MainWindow(QWidget *parent, QString socketPath)
policy.setRetainSizeWhenHidden(true); policy.setRetainSizeWhenHidden(true);
ui->btnOpenFailed->setSizePolicy(policy); ui->btnOpenFailed->setSizePolicy(policy);
ui->txtSearch->installEventFilter(this);
ui->scrollArea->viewport()->installEventFilter(this);
this->ipcClientThread.start(); this->ipcClientThread.start();
} }
@ -191,7 +198,14 @@ void MainWindow::connectSignals()
connect(ui->btnSaveSettings, &QPushButton::clicked, this, &MainWindow::saveSettings); connect(ui->btnSaveSettings, &QPushButton::clicked, this, &MainWindow::saveSettings);
connect(ui->btnOpenFailed, &QPushButton::clicked, this, &MainWindow::exportFailedPaths); connect(ui->btnOpenFailed, &QPushButton::clicked, this, &MainWindow::exportFailedPaths);
connect( connect(
ui->comboPreviewFiles, qOverload<int>(&QComboBox::currentIndexChanged), this, [&]() { makePreviews(1); }, ui->comboPreviewFiles, qOverload<int>(&QComboBox::currentIndexChanged), this,
[&]()
{
if(this->previewTabActive())
{
makePreviews(1);
}
},
Qt::QueuedConnection); Qt::QueuedConnection);
connect(&ipcPreviewClient, &IPCPreviewClient::previewReceived, this, &MainWindow::previewReceived, connect(&ipcPreviewClient, &IPCPreviewClient::previewReceived, this, &MainWindow::previewReceived,
Qt::QueuedConnection); Qt::QueuedConnection);
@ -201,6 +215,7 @@ void MainWindow::connectSignals()
this->ui->previewProcessBar->setValue(this->ui->previewProcessBar->maximum()); this->ui->previewProcessBar->setValue(this->ui->previewProcessBar->maximum());
this->ui->spinPreviewPage->setEnabled(true); this->ui->spinPreviewPage->setEnabled(true);
this->ui->comboPreviewFiles->setEnabled(true); this->ui->comboPreviewFiles->setEnabled(true);
ui->txtSearch->setEnabled(true);
}); });
connect(&ipcPreviewClient, &IPCPreviewClient::error, this, connect(&ipcPreviewClient, &IPCPreviewClient::error, this,
[this](QString msg) [this](QString msg)
@ -437,6 +452,86 @@ void MainWindow::processShortcut(int key)
} }
} }
bool MainWindow::eventFilter(QObject *object, QEvent *event)
{
if(object == ui->txtSearch && !searchHistory.empty())
{
if(event->type() == QEvent::KeyPress)
{
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
if(keyEvent->key() == Qt::Key_Up)
{
if(this->currentSavedSearchText.isEmpty())
{
this->currentSavedSearchText = ui->txtSearch->text();
}
if(this->currentSearchHistoryIndex <= 0)
{
return true;
}
--this->currentSearchHistoryIndex;
QString text = this->searchHistory.at(this->currentSearchHistoryIndex);
ui->txtSearch->setText(text);
return true;
}
else if(keyEvent->key() == Qt::Key_Down)
{
if(this->currentSearchHistoryIndex == searchHistory.size() - 1)
{
if(!this->currentSavedSearchText.isEmpty())
{
ui->txtSearch->setText(this->currentSavedSearchText);
this->currentSavedSearchText.clear();
++this->currentSearchHistoryIndex;
}
return true;
}
if(this->currentSearchHistoryIndex < searchHistory.size() - 1)
{
++this->currentSearchHistoryIndex;
QString text = this->searchHistory.at(this->currentSearchHistoryIndex);
ui->txtSearch->setText(text);
}
return true;
}
else
{
this->currentSavedSearchText.clear();
/* Off by one on purpose so Key_Up decrements it again and lands at
* the last entry */
this->currentSearchHistoryIndex = this->searchHistory.size();
}
}
}
if(object == ui->scrollArea->viewport())
{
if(event->type() == QEvent::Wheel)
{
QWheelEvent *wheelEvent = static_cast<QWheelEvent *>(event);
if(wheelEvent->modifiers() & Qt::ControlModifier)
{
if(wheelEvent->angleDelta().y() > 0)
{
if(ui->comboScale->currentIndex() < ui->comboScale->count() - 1)
{
ui->comboScale->setCurrentIndex(ui->comboScale->currentIndex() + 1);
}
}
else
{
if(ui->comboScale->currentIndex() > 0)
{
ui->comboScale->setCurrentIndex(ui->comboScale->currentIndex() - 1);
}
}
return true;
}
}
}
return QMainWindow::eventFilter(object, event);
}
void MainWindow::keyPressEvent(QKeyEvent *event) void MainWindow::keyPressEvent(QKeyEvent *event)
{ {
bool quit = bool quit =
@ -498,6 +593,9 @@ void MainWindow::initSettingsTabs()
ui->txtSettingMountPaths->setText(mountPaths); ui->txtSettingMountPaths->setText(mountPaths);
ui->spinSettingNumerPerPages->setValue(numPagesPerPreview); ui->spinSettingNumerPerPages->setValue(numPagesPerPreview);
ui->txtSettingDatabasePath->setText(databasePath); ui->txtSettingDatabasePath->setText(databasePath);
bool horizontalScroll = settings.value(SETTINGS_KEY_PREVIEWS_SCROLL_HORIZONTALLY).toBool();
ui->radioScrollHorizontally->setChecked(horizontalScroll);
ui->radioScrollVertically->setChecked(!horizontalScroll);
} }
void MainWindow::saveSettings() void MainWindow::saveSettings()
@ -525,6 +623,7 @@ void MainWindow::saveSettings()
settings.setValue(SETTINGS_KEY_MOUNTPATHS, mountPaths); settings.setValue(SETTINGS_KEY_MOUNTPATHS, mountPaths);
settings.setValue(SETTINGS_KEY_PREVIEWSPERPAGE, ui->spinSettingNumerPerPages->value()); settings.setValue(SETTINGS_KEY_PREVIEWSPERPAGE, ui->spinSettingNumerPerPages->value());
settings.setValue(SETTINGS_KEY_DBPATH, databasePath); settings.setValue(SETTINGS_KEY_DBPATH, databasePath);
settings.setValue(SETTINGS_KEY_PREVIEWS_SCROLL_HORIZONTALLY, ui->radioScrollHorizontally->isChecked());
settings.sync(); settings.sync();
@ -600,6 +699,14 @@ void MainWindow::lineEditReturnPressed()
ui->tabWidget->setCurrentIndex(0); ui->tabWidget->setCurrentIndex(0);
} }
// TODO: validate q; // TODO: validate q;
while(this->searchHistory.size() > 30)
{
this->searchHistory.removeFirst();
}
this->searchHistory.append(q);
this->currentSearchHistoryIndex = this->searchHistory.size();
this->currentSavedSearchText.clear();
ui->treeResultsList->clear(); ui->treeResultsList->clear();
ui->lblSearchResults->setText("Searching..."); ui->lblSearchResults->setText("Searching...");
this->ui->txtSearch->setEnabled(false); this->ui->txtSearch->setEnabled(false);
@ -701,19 +808,25 @@ void MainWindow::handleSearchResults(const QVector<SearchResult> &results)
ui->comboPreviewFiles->setVisible(true); ui->comboPreviewFiles->setVisible(true);
bool hasDeleted = false; bool hasDeleted = false;
QHash<QString, bool> seenMap;
for(const SearchResult &result : results) for(const SearchResult &result : results)
{ {
QFileInfo pathInfo(result.fileData.absPath); const QString &absPath = result.fileData.absPath;
QFileInfo pathInfo(absPath);
if(!seenMap.contains(absPath))
{
seenMap[absPath] = true;
QString fileName = pathInfo.fileName(); QString fileName = pathInfo.fileName();
QTreeWidgetItem *item = new QTreeWidgetItem(ui->treeResultsList); QTreeWidgetItem *item = new QTreeWidgetItem(ui->treeResultsList);
QDateTime dt = QDateTime::fromSecsSinceEpoch(result.fileData.mtime); QDateTime dt = QDateTime::fromSecsSinceEpoch(result.fileData.mtime);
item->setIcon(0, iconProvider.icon(pathInfo)); item->setIcon(0, iconProvider.icon(pathInfo));
item->setText(0, fileName); item->setText(0, fileName);
item->setText(1, result.fileData.absPath); item->setText(1, absPath);
item->setText(2, dt.toString(Qt::ISODate)); item->setText(2, dt.toString(Qt::ISODate));
item->setText(3, this->locale().formattedDataSize(result.fileData.size)); item->setText(3, this->locale().formattedDataSize(result.fileData.size));
}
bool exists = pathInfo.exists(); bool exists = pathInfo.exists();
if(exists) if(exists)
{ {
@ -736,6 +849,7 @@ void MainWindow::handleSearchResults(const QVector<SearchResult> &results)
hasDeleted = true; hasDeleted = true;
} }
} }
ui->treeResultsList->resizeColumnToContents(0); ui->treeResultsList->resizeColumnToContents(0);
ui->treeResultsList->resizeColumnToContents(1); ui->treeResultsList->resizeColumnToContents(1);
ui->treeResultsList->resizeColumnToContents(2); ui->treeResultsList->resizeColumnToContents(2);
@ -771,7 +885,17 @@ void MainWindow::makePreviews(int page)
} }
qDeleteAll(ui->scrollAreaWidgetContents->children()); qDeleteAll(ui->scrollAreaWidgetContents->children());
QSettings settings;
bool horizontalScroll = settings.value(SETTINGS_KEY_PREVIEWS_SCROLL_HORIZONTALLY, false).toBool();
if(horizontalScroll)
{
ui->scrollAreaWidgetContents->setLayout(new QHBoxLayout()); ui->scrollAreaWidgetContents->setLayout(new QHBoxLayout());
}
else
{
ui->scrollAreaWidgetContents->setLayout(new QVBoxLayout());
ui->scrollAreaWidgetContents->layout()->setAlignment(Qt::AlignCenter);
}
ui->previewProcessBar->setMaximum(this->previewableSearchResults.size()); ui->previewProcessBar->setMaximum(this->previewableSearchResults.size());
processedPdfPreviews = 0; processedPdfPreviews = 0;
@ -820,13 +944,9 @@ void MainWindow::makePreviews(int page)
} }
RenderTarget renderTarget; RenderTarget renderTarget;
renderTarget.path = sr.fileData.absPath; renderTarget.path = sr.fileData.absPath;
renderTarget.page = (int)sr.page;
for(unsigned int pagenum : sr.pages)
{
renderTarget.page = (int)pagenum;
targets.append(renderTarget); targets.append(renderTarget);
} }
}
int numpages = ceil(static_cast<double>(targets.size()) / previewsPerPage); int numpages = ceil(static_cast<double>(targets.size()) / previewsPerPage);
ui->spinPreviewPage->setMaximum(numpages); ui->spinPreviewPage->setMaximum(numpages);
targets = targets.mid(begin, end); targets = targets.mid(begin, end);
@ -839,6 +959,7 @@ void MainWindow::makePreviews(int page)
++this->currentPreviewGeneration; ++this->currentPreviewGeneration;
this->ui->spinPreviewPage->setEnabled(false); this->ui->spinPreviewPage->setEnabled(false);
this->ui->comboPreviewFiles->setEnabled(false); this->ui->comboPreviewFiles->setEnabled(false);
this->ui->txtSearch->setEnabled(false);
emit startIpcPreviews(renderConfig, targets); emit startIpcPreviews(renderConfig, targets);
} }
@ -925,3 +1046,11 @@ MainWindow::~MainWindow()
delete this->indexer; delete this->indexer;
delete ui; delete ui;
} }
void MainWindow::closeEvent(QCloseEvent *event)
{
QStringList list = this->searchHistory.toList();
QSettings settings;
settings.setValue(SETTINGS_KEY_SEARCHHISTORY, list);
settings.sync();
}

@ -30,6 +30,9 @@ class MainWindow : public QMainWindow
void beginSearch(const QString &query); void beginSearch(const QString &query);
void startPdfPreviewGeneration(QVector<SearchResult> paths, double scalefactor); void startPdfPreviewGeneration(QVector<SearchResult> paths, double scalefactor);
protected:
void closeEvent(QCloseEvent *event) override;
private: private:
DatabaseFactory *dbFactory; DatabaseFactory *dbFactory;
SqliteDbService *dbService; SqliteDbService *dbService;
@ -64,7 +67,11 @@ class MainWindow : public QMainWindow
void initSettingsTabs(); void initSettingsTabs();
int currentSelectedScale(); int currentSelectedScale();
void processShortcut(int key); void processShortcut(int key);
private slots: bool eventFilter(QObject *object, QEvent *event);
QVector<QString> searchHistory;
int currentSearchHistoryIndex = 0;
QString currentSavedSearchText;
private slots:
void lineEditReturnPressed(); void lineEditReturnPressed();
void treeSearchItemActivated(QTreeWidgetItem *item, int i); void treeSearchItemActivated(QTreeWidgetItem *item, int i);
void showSearchResultsContextMenu(const QPoint &point); void showSearchResultsContextMenu(const QPoint &point);

@ -7,7 +7,7 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>1280</width> <width>1280</width>
<height>855</height> <height>888</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
@ -27,7 +27,7 @@
<enum>QTabWidget::South</enum> <enum>QTabWidget::South</enum>
</property> </property>
<property name="currentIndex"> <property name="currentIndex">
<number>1</number> <number>3</number>
</property> </property>
<widget class="QWidget" name="resultsTab"> <widget class="QWidget" name="resultsTab">
<attribute name="title"> <attribute name="title">
@ -82,7 +82,7 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>1244</width> <width>1244</width>
<height>565</height> <height>598</height>
</rect> </rect>
</property> </property>
<layout class="QHBoxLayout" name="horizontalLayout"/> <layout class="QHBoxLayout" name="horizontalLayout"/>
@ -542,6 +542,19 @@
</layout> </layout>
</widget> </widget>
</item> </item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item> <item>
<widget class="QGroupBox" name="Misc"> <widget class="QGroupBox" name="Misc">
<property name="title"> <property name="title">
@ -551,7 +564,7 @@
<item> <item>
<layout class="QHBoxLayout" name="horizontalLayout_9"> <layout class="QHBoxLayout" name="horizontalLayout_9">
<item> <item>
<widget class="QLabel" name="label_4"> <widget class="QLabel" name="lblMaxNumbersPreviewPages">
<property name="text"> <property name="text">
<string>Max number of previews per 'page' in 'Previews' tab: </string> <string>Max number of previews per 'page' in 'Previews' tab: </string>
</property> </property>
@ -575,22 +588,47 @@
</item> </item>
</layout> </layout>
</item> </item>
</layout> <item>
<layout class="QHBoxLayout" name="horizontalLayout_8">
<item>
<widget class="QLabel" name="lblScrollModeForPreviews">
<property name="text">
<string>Scroll mode for previews:</string>
</property>
</widget> </widget>
</item> </item>
<item> <item>
<spacer name="verticalSpacer"> <widget class="QRadioButton" name="radioScrollVertically">
<property name="text">
<string>Vertically</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="radioScrollHorizontally">
<property name="text">
<string>Horizontally</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_6">
<property name="orientation"> <property name="orientation">
<enum>Qt::Vertical</enum> <enum>Qt::Horizontal</enum>
</property> </property>
<property name="sizeHint" stdset="0"> <property name="sizeHint" stdset="0">
<size> <size>
<width>20</width> <width>40</width>
<height>40</height> <height>20</height>
</size> </size>
</property> </property>
</spacer> </spacer>
</item> </item>
</layout>
</item>
</layout>
</widget>
</item>
<item> <item>
<widget class="QPushButton" name="btnSaveSettings"> <widget class="QPushButton" name="btnSaveSettings">
<property name="text"> <property name="text">

@ -9,6 +9,8 @@
#define SETTINGS_KEY_EXCLUDEDPATHS "excludedpaths" #define SETTINGS_KEY_EXCLUDEDPATHS "excludedpaths"
#define SETTINGS_KEY_MOUNTPATHS "mountpaths" #define SETTINGS_KEY_MOUNTPATHS "mountpaths"
#define SETTINGS_KEY_PREVIEWSPERPAGE "previewsPerPage" #define SETTINGS_KEY_PREVIEWSPERPAGE "previewsPerPage"
#define SETTINGS_KEY_SEARCHHISTORY "searchhistory"
#define SETTINGS_KEY_PREVIEWS_SCROLL_HORIZONTALLY "horizontalscroll"
namespace Common namespace Common
{ {

@ -6,7 +6,7 @@ class SearchResult
{ {
public: public:
FileData fileData; FileData fileData;
QVector<unsigned int> pages; unsigned int page;
bool wasContentSearch = false; bool wasContentSearch = false;
}; };

@ -78,13 +78,18 @@ QString SqliteSearch::escapeFtsArgument(QString ftsArg)
if(value.isEmpty()) if(value.isEmpty())
{ {
value = m.captured(2); value = m.captured(2);
if(value.endsWith('*'))
{
value = value.mid(0, value.size() - 1);
}
result += "\"" + value + "\"*";
} }
else else
{ {
value = "\"\"" + value + "\"\""; value = "\"\"" + value + "\"\"";
}
result += "\"" + value + "\" "; result += "\"" + value + "\" ";
} }
}
return result; return result;
} }
@ -189,10 +194,9 @@ QSqlQuery SqliteSearch::makeSqlQuery(const LooqsQuery &query)
sortSql = "ORDER BY rank"; sortSql = "ORDER BY rank";
} }
} }
prepSql = prepSql = "SELECT file.path AS path, content.page AS page, file.mtime AS mtime, file.size AS size, "
"SELECT file.path AS path, group_concat(content.page) AS pages, file.mtime AS mtime, file.size AS size, "
"file.filetype AS filetype FROM file INNER JOIN content ON file.id = content.fileid " + "file.filetype AS filetype FROM file INNER JOIN content ON file.id = content.fileid " +
joinSql + " WHERE 1=1 AND " + whereSql + " GROUP BY file.path " + sortSql; joinSql + " WHERE 1=1 AND " + whereSql + " " + sortSql;
} }
else else
{ {
@ -200,7 +204,7 @@ QSqlQuery SqliteSearch::makeSqlQuery(const LooqsQuery &query)
{ {
sortSql = "ORDER BY file.mtime DESC"; sortSql = "ORDER BY file.mtime DESC";
} }
prepSql = "SELECT file.path AS path, '0' as pages, file.mtime AS mtime, file.size AS size, file.filetype AS " prepSql = "SELECT file.path AS path, '0' as page, file.mtime AS mtime, file.size AS size, file.filetype AS "
"filetype FROM file WHERE 1=1 AND " + "filetype FROM file WHERE 1=1 AND " +
whereSql + " " + sortSql; whereSql + " " + sortSql;
} }
@ -243,15 +247,7 @@ QVector<SearchResult> SqliteSearch::search(const LooqsQuery &query)
result.fileData.mtime = dbQuery.value("mtime").toUInt(); result.fileData.mtime = dbQuery.value("mtime").toUInt();
result.fileData.size = dbQuery.value("size").toUInt(); result.fileData.size = dbQuery.value("size").toUInt();
result.fileData.filetype = dbQuery.value("filetype").toChar(); result.fileData.filetype = dbQuery.value("filetype").toChar();
QString pages = dbQuery.value("pages").toString(); result.page = dbQuery.value("page").toUInt();
QStringList pagesList = pages.split(",");
for(QString &page : pagesList)
{
if(page != "")
{
result.pages.append(page.toUInt());
}
}
result.wasContentSearch = contentSearch; result.wasContentSearch = contentSearch;
results.append(result); results.append(result);
} }