Compare commits

...

10 Commits

Author SHA1 Message Date
Albert S. 590a8888fc gui: Add index options group in index tab 2023-01-08 17:37:28 +01:00
Albert S. ccc4d09b36 shared: FilesSverOptions: Rename members 2023-01-08 17:37:28 +01:00
Albert S. 8298b675aa cli: CommandAdd: Implement --no-content and --fill-content 2023-01-08 17:37:28 +01:00
Albert S. 71789b5b56 shared: SqliteDbService: Add queryFileType() 2023-01-08 17:37:28 +01:00
Albert S. 363d207ccc LICENSE: Update copyright year 2023-01-08 17:37:28 +01:00
Albert S. 4b1522b82a Introduce FileSaverOptions to consolidate common parameters 2023-01-08 17:37:28 +01:00
Albert S. efca45b88a gui sandbox: Allow wpath to improve poppler text rendering
Apparently poppler or something needs open() with write
flags to render pdfs with proper fonts.

Landlock guards file system write access, so this is fine.
2023-01-08 17:37:28 +01:00
Albert S. 0cd19b53e4 gui: PreviewGeneratorPdf: Enable Text hinting 2023-01-08 17:37:28 +01:00
Albert S. 889725033a gui: mainwindow: Refactor to use new PreviewCoordinator 2023-01-08 17:37:28 +01:00
Albert S. 8485a25b21 gui: Introduce PreviewCoordinator
Move some preview generation logic to PreviewCoordinator
2023-01-08 17:37:28 +01:00
23 changed files with 490 additions and 218 deletions

View File

@ -1,4 +1,4 @@
Copyright (c) 2018-2022: Albert Schwarzkopf <looqs at quitesimple period org> Copyright (c) 2018-2023: Albert Schwarzkopf <looqs at quitesimple period org>
looqs is made available under the following license: looqs is made available under the following license:

View File

@ -44,20 +44,30 @@ int CommandAdd::handle(QStringList arguments)
"Continue adding files, don't exit on first error. If this option is not given, looqs will " "Continue adding files, don't exit on first error. If this option is not given, looqs will "
"exit asap, but it's possible that a few files will still be processed. " "exit asap, but it's possible that a few files will still be processed. "
"Set -t 1 to avoid this behavior, but processing will be slower. "}, "Set -t 1 to avoid this behavior, but processing will be slower. "},
{{"n", "no-content"}, "Only add paths to database. Do not index content"},
{{"f", "fill-content"}, "Index content for files previously indexed with -n"},
{"tags", "Comma-separated list of tags to assign"},
{{"t", "threads"}, "Number of threads to use.", "threads"}}); {{"t", "threads"}, "Number of threads to use.", "threads"}});
parser.addHelpOption(); parser.addHelpOption();
parser.addPositionalArgument("add", "Add paths to the index", parser.addPositionalArgument("add", "Add paths to the index",
"add [paths...]. If no path is given, read from stdin, one path per line."); "add [paths...]. If no path is given, read from stdin, one path per line.");
parser.process(arguments); parser.process(arguments);
this->keepGoing = parser.isSet("continue"); this->keepGoing = parser.isSet("continue");
bool pathsOnly = parser.isSet("no-content");
bool fillContent = parser.isSet("fill-content");
if(parser.isSet("threads")) if(parser.isSet("threads"))
{ {
QString threadsCount = parser.value("threads"); QString threadsCount = parser.value("threads");
QThreadPool::globalInstance()->setMaxThreadCount(threadsCount.toInt()); QThreadPool::globalInstance()->setMaxThreadCount(threadsCount.toInt());
} }
if(pathsOnly && fillContent)
{
Logger::error() << "Invalid options: -n and -f cannot both be set";
return EXIT_FAILURE;
}
QStringList files = parser.positionalArguments(); QStringList files = parser.positionalArguments();
if(files.length() == 0) if(files.length() == 0)
@ -71,9 +81,16 @@ int CommandAdd::handle(QStringList arguments)
} }
} }
FileSaverOptions fileSaverOptions;
fileSaverOptions.keepGoing = keepGoing;
fileSaverOptions.fillExistingContentless = fillContent;
fileSaverOptions.metadataOnly = pathsOnly;
fileSaverOptions.verbose = false;
indexer = new Indexer(*this->dbService); indexer = new Indexer(*this->dbService);
indexer->setFileSaverOptions(fileSaverOptions);
indexer->setTargetPaths(files.toVector()); indexer->setTargetPaths(files.toVector());
indexer->setKeepGoing(keepGoing);
connect(indexer, &Indexer::pathsCountChanged, this, connect(indexer, &Indexer::pathsCountChanged, this,
[](int pathsCount) { Logger::info() << "Found paths: " << pathsCount << Qt::endl; }); [](int pathsCount) { Logger::info() << "Found paths: " << pathsCount << Qt::endl; });

View File

@ -40,8 +40,12 @@ int CommandUpdate::handle(QStringList arguments)
bool hasErrors = false; bool hasErrors = false;
IndexSyncer *syncer = new IndexSyncer(*this->dbService); IndexSyncer *syncer = new IndexSyncer(*this->dbService);
syncer->setKeepGoing(keepGoing);
syncer->setVerbose(verbose); FileSaverOptions fileOptions;
fileOptions.keepGoing = keepGoing;
fileOptions.verbose = verbose;
syncer->setFileSaverOptions(fileOptions);
syncer->setPattern(pattern); syncer->setPattern(pattern);
syncer->setDryRun(dryRun); syncer->setDryRun(dryRun);
syncer->setRemoveDeletedFromIndex(deleteMissing); syncer->setRemoveDeletedFromIndex(deleteMissing);

View File

@ -34,6 +34,7 @@ SOURCES += \
main.cpp \ main.cpp \
mainwindow.cpp \ mainwindow.cpp \
clicklabel.cpp \ clicklabel.cpp \
previewcoordinator.cpp \
previewgenerator.cpp \ previewgenerator.cpp \
previewgeneratormapfunctor.cpp \ previewgeneratormapfunctor.cpp \
previewgeneratorodt.cpp \ previewgeneratorodt.cpp \
@ -54,6 +55,7 @@ HEADERS += \
ipcserver.h \ ipcserver.h \
mainwindow.h \ mainwindow.h \
clicklabel.h \ clicklabel.h \
previewcoordinator.h \
previewgenerator.h \ previewgenerator.h \
previewgeneratormapfunctor.h \ previewgeneratormapfunctor.h \
previewgeneratorodt.h \ previewgeneratorodt.h \

View File

@ -28,7 +28,7 @@ void enableIpcSandbox()
policy->namespace_options = EXILE_UNSHARE_USER | EXILE_UNSHARE_MOUNT | EXILE_UNSHARE_NETWORK; policy->namespace_options = EXILE_UNSHARE_USER | EXILE_UNSHARE_MOUNT | EXILE_UNSHARE_NETWORK;
policy->no_new_privs = 1; policy->no_new_privs = 1;
policy->drop_caps = 1; policy->drop_caps = 1;
policy->vow_promises = exile_vows_from_str("thread cpath rpath unix stdio proc error"); policy->vow_promises = exile_vows_from_str("thread cpath rpath wpath unix stdio proc error");
policy->mount_path_policies_to_chroot = 1; policy->mount_path_policies_to_chroot = 1;
QString ipcSocketPath = Common::ipcSocketPath(); QString ipcSocketPath = Common::ipcSocketPath();

View File

@ -22,7 +22,6 @@
#include "../shared/sqlitesearch.h" #include "../shared/sqlitesearch.h"
#include "../shared/looqsgeneralexception.h" #include "../shared/looqsgeneralexception.h"
#include "../shared/common.h" #include "../shared/common.h"
#include "ipcpreviewclient.h"
#include "previewgenerator.h" #include "previewgenerator.h"
#include "aboutdialog.h" #include "aboutdialog.h"
@ -32,8 +31,7 @@ MainWindow::MainWindow(QWidget *parent, QString socketPath)
this->progressDialog.cancel(); // because constructing it shows it, quite weird this->progressDialog.cancel(); // because constructing it shows it, quite weird
ui->setupUi(this); ui->setupUi(this);
setWindowTitle(QCoreApplication::applicationName()); setWindowTitle(QCoreApplication::applicationName());
this->ipcPreviewClient.moveToThread(&this->ipcClientThread);
this->ipcPreviewClient.setSocketPath(socketPath);
QSettings settings; QSettings settings;
this->dbFactory = new DatabaseFactory(Common::databasePath()); this->dbFactory = new DatabaseFactory(Common::databasePath());
@ -78,7 +76,7 @@ MainWindow::MainWindow(QWidget *parent, QString socketPath)
ui->txtSearch->installEventFilter(this); ui->txtSearch->installEventFilter(this);
ui->scrollArea->viewport()->installEventFilter(this); ui->scrollArea->viewport()->installEventFilter(this);
this->ipcClientThread.start(); this->previewCoordinator.setSocketPath(socketPath);
} }
void MainWindow::addPathToIndex() void MainWindow::addPathToIndex()
@ -208,9 +206,9 @@ void MainWindow::connectSignals()
} }
}, },
Qt::QueuedConnection); Qt::QueuedConnection);
connect(&ipcPreviewClient, &IPCPreviewClient::previewReceived, this, &MainWindow::previewReceived, connect(&previewCoordinator, &PreviewCoordinator::previewReady, this, &MainWindow::previewReceived,
Qt::QueuedConnection); Qt::QueuedConnection);
connect(&ipcPreviewClient, &IPCPreviewClient::finished, this, connect(&previewCoordinator, &PreviewCoordinator::completedGeneration, this,
[&] [&]
{ {
this->ui->previewProcessBar->setValue(this->ui->previewProcessBar->maximum()); this->ui->previewProcessBar->setValue(this->ui->previewProcessBar->maximum());
@ -218,22 +216,24 @@ void MainWindow::connectSignals()
this->ui->comboPreviewFiles->setEnabled(true); this->ui->comboPreviewFiles->setEnabled(true);
ui->txtSearch->setEnabled(true); ui->txtSearch->setEnabled(true);
}); });
connect(&ipcPreviewClient, &IPCPreviewClient::error, this, connect(&previewCoordinator, &PreviewCoordinator::error, this,
[this](QString msg) [this](QString msg)
{ {
qCritical() << msg << Qt::endl; qCritical() << msg << Qt::endl;
QMessageBox::critical(this, "IPC error", msg); QMessageBox::critical(this, "IPC error", msg);
}); });
connect(ui->radioMetadataOnly, &QRadioButton::toggled, this,
connect(this, &MainWindow::startIpcPreviews, &ipcPreviewClient, &IPCPreviewClient::startGeneration, [this](bool toggled)
Qt::QueuedConnection); {
connect(this, &MainWindow::stopIpcPreviews, &ipcPreviewClient, &IPCPreviewClient::stopGeneration, if(toggled)
Qt::QueuedConnection); {
this->ui->chkFillContentForContentless->setChecked(false);
};
});
} }
void MainWindow::exportFailedPaths() void MainWindow::exportFailedPaths()
{ {
QString filename = QString filename =
QString("/tmp/looqs_indexresult_failed_%1").arg(QDateTime::currentDateTime().toString("yyyy-MM-dd_hhmmss")); QString("/tmp/looqs_indexresult_failed_%1").arg(QDateTime::currentDateTime().toString("yyyy-MM-dd_hhmmss"));
QFile outFile(filename); QFile outFile(filename);
@ -266,8 +266,11 @@ void MainWindow::startIndexSync()
progressDialog.setValue(0); progressDialog.setValue(0);
progressDialog.open(); progressDialog.open();
indexSyncer->setKeepGoing(true); FileSaverOptions options;
indexSyncer->setVerbose(false); options.keepGoing = true;
options.verbose = false;
indexSyncer->setFileSaverOptions(options);
indexSyncer->setDryRun(false); indexSyncer->setDryRun(false);
indexSyncer->setRemoveDeletedFromIndex(true); indexSyncer->setRemoveDeletedFromIndex(true);
@ -311,6 +314,15 @@ void MainWindow::startIndexing()
this->indexer->setTargetPaths(paths); this->indexer->setTargetPaths(paths);
QString ignorePatterns = ui->txtIgnorePatterns->text(); QString ignorePatterns = ui->txtIgnorePatterns->text();
this->indexer->setIgnorePattern(ignorePatterns.split(";")); this->indexer->setIgnorePattern(ignorePatterns.split(";"));
FileSaverOptions options;
options.fillExistingContentless =
ui->chkFillContentForContentless->isEnabled() && ui->chkFillContentForContentless->isChecked();
options.metadataOnly = ui->radioMetadataOnly->isChecked();
options.verbose = false;
options.keepGoing = true;
this->indexer->setFileSaverOptions(options);
this->indexer->beginIndexing(); this->indexer->beginIndexing();
QSettings settings; QSettings settings;
settings.setValue("indexPaths", pathSettingsValue); settings.setValue("indexPaths", pathSettingsValue);
@ -632,13 +644,17 @@ void MainWindow::saveSettings()
qApp->quit(); qApp->quit();
} }
void MainWindow::previewReceived(QSharedPointer<PreviewResult> preview, unsigned int previewGeneration) void MainWindow::previewReceived()
{ {
if(previewGeneration < this->currentPreviewGeneration)
{
return;
}
this->ui->previewProcessBar->setValue(this->ui->previewProcessBar->value() + 1); this->ui->previewProcessBar->setValue(this->ui->previewProcessBar->value() + 1);
QBoxLayout *layout = static_cast<QBoxLayout *>(ui->scrollAreaWidgetContents->layout());
int index = layout->count();
if(index > 0)
{
--index;
}
QSharedPointer<PreviewResult> preview = this->previewCoordinator.resultAt(index);
if(!preview.isNull() && preview->hasPreview()) if(!preview.isNull() && preview->hasPreview())
{ {
QString docPath = preview->getDocumentPath(); QString docPath = preview->getDocumentPath();
@ -684,24 +700,7 @@ void MainWindow::previewReceived(QSharedPointer<PreviewResult> preview, unsigned
previewWidget->setLayout(previewLayout); previewWidget->setLayout(previewLayout);
QBoxLayout *layout = static_cast<QBoxLayout *>(ui->scrollAreaWidgetContents->layout()); layout->insertWidget(index, previewWidget);
int pos = previewOrder[docPath + QString::number(previewPage)];
if(pos <= layout->count())
{
layout->insertWidget(pos, previewWidget);
for(auto it = previewWidgetOrderCache.constKeyValueBegin();
it != previewWidgetOrderCache.constKeyValueEnd(); it++)
{
if(it->first <= layout->count())
{
layout->insertWidget(it->first, it->second);
}
}
}
else
{
previewWidgetOrderCache[pos] = previewWidget;
}
} }
} }
@ -818,7 +817,6 @@ void MainWindow::lineEditReturnPressed()
void MainWindow::handleSearchResults(const QVector<SearchResult> &results) void MainWindow::handleSearchResults(const QVector<SearchResult> &results)
{ {
this->previewableSearchResults.clear();
qDeleteAll(ui->scrollAreaWidgetContents->children()); qDeleteAll(ui->scrollAreaWidgetContents->children());
ui->treeResultsList->clear(); ui->treeResultsList->clear();
@ -827,6 +825,8 @@ void MainWindow::handleSearchResults(const QVector<SearchResult> &results)
ui->comboPreviewFiles->setVisible(true); ui->comboPreviewFiles->setVisible(true);
ui->lblTotalPreviewPagesCount->setText(""); ui->lblTotalPreviewPagesCount->setText("");
this->previewCoordinator.init(results);
bool hasDeleted = false; bool hasDeleted = false;
QHash<QString, bool> seenMap; QHash<QString, bool> seenMap;
for(const SearchResult &result : results) for(const SearchResult &result : results)
@ -847,34 +847,29 @@ void MainWindow::handleSearchResults(const QVector<SearchResult> &results)
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)
{
if(result.wasContentSearch)
{
if(!pathInfo.suffix().contains("htm")) // hack until we can preview them properly...
{
if(PreviewGenerator::get(pathInfo) != nullptr)
{
this->previewableSearchResults.append(result);
if(!seenMap.contains(result.fileData.absPath))
{
ui->comboPreviewFiles->addItem(result.fileData.absPath);
}
}
}
}
}
else
{ {
hasDeleted = true; hasDeleted = true;
} }
seenMap[absPath] = true; seenMap[absPath] = true;
} }
seenMap.clear();
for(const SearchResult &result : this->previewCoordinator.getPreviewableSearchResults())
{
const QString &absPath = result.fileData.absPath;
if(!seenMap.contains(absPath))
{
ui->comboPreviewFiles->addItem(absPath);
}
seenMap[absPath] = 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);
previewDirty = !this->previewableSearchResults.empty();
previewDirty = this->previewCoordinator.previewableCount() > 0;
ui->spinPreviewPage->setValue(1); ui->spinPreviewPage->setValue(1);
@ -884,7 +879,7 @@ void MainWindow::handleSearchResults(const QVector<SearchResult> &results)
} }
QString statusText = "Results: " + QString::number(results.size()) + " files"; QString statusText = "Results: " + QString::number(results.size()) + " files";
statusText += ", previewable: " + QString::number(this->previewableSearchResults.count()); statusText += ", previewable: " + QString::number(this->previewCoordinator.previewableCount());
if(hasDeleted) if(hasDeleted)
{ {
statusText += " WARNING: Some files are inaccessible. No preview available for those. Index may be out of sync"; statusText += " WARNING: Some files are inaccessible. No preview available for those. Index may be out of sync";
@ -901,7 +896,7 @@ int MainWindow::currentSelectedScale()
void MainWindow::makePreviews(int page) void MainWindow::makePreviews(int page)
{ {
if(this->previewableSearchResults.empty()) if(this->previewCoordinator.previewableCount() == 0)
{ {
return; return;
} }
@ -918,8 +913,7 @@ void MainWindow::makePreviews(int page)
ui->scrollAreaWidgetContents->setLayout(new QVBoxLayout()); ui->scrollAreaWidgetContents->setLayout(new QVBoxLayout());
ui->scrollAreaWidgetContents->layout()->setAlignment(Qt::AlignCenter); ui->scrollAreaWidgetContents->layout()->setAlignment(Qt::AlignCenter);
} }
ui->previewProcessBar->setMaximum(this->previewableSearchResults.size()); ui->previewProcessBar->setMaximum(this->previewCoordinator.previewableCount());
processedPdfPreviews = 0;
QVector<QString> wordsToHighlight; QVector<QString> wordsToHighlight;
QRegularExpression extractor(R"#("([^"]*)"|([^\s]+))#"); QRegularExpression extractor(R"#("([^"]*)"|([^\s]+))#");
@ -954,12 +948,9 @@ void MainWindow::makePreviews(int page)
renderConfig.scaleY = QGuiApplication::primaryScreen()->physicalDotsPerInchY() * (currentScale / 100.); renderConfig.scaleY = QGuiApplication::primaryScreen()->physicalDotsPerInchY() * (currentScale / 100.);
renderConfig.wordsToHighlight = wordsToHighlight; renderConfig.wordsToHighlight = wordsToHighlight;
this->previewOrder.clear();
this->previewWidgetOrderCache.clear();
int previewPos = 0; int previewPos = 0;
QVector<RenderTarget> targets; QVector<RenderTarget> targets;
for(SearchResult &sr : this->previewableSearchResults) for(const SearchResult &sr : this->previewCoordinator.getPreviewableSearchResults())
{ {
if(ui->comboPreviewFiles->currentIndex() != 0) if(ui->comboPreviewFiles->currentIndex() != 0)
{ {
@ -971,11 +962,8 @@ void MainWindow::makePreviews(int page)
RenderTarget renderTarget; RenderTarget renderTarget;
renderTarget.path = sr.fileData.absPath; renderTarget.path = sr.fileData.absPath;
renderTarget.page = (int)sr.page; renderTarget.page = (int)sr.page;
targets.append(renderTarget);
int pos = previewPos - beginOffset; targets.append(renderTarget);
this->previewOrder[renderTarget.path + QString::number(renderTarget.page)] = pos;
++previewPos;
} }
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);
@ -985,12 +973,12 @@ void MainWindow::makePreviews(int page)
ui->previewProcessBar->setMaximum(targets.count()); ui->previewProcessBar->setMaximum(targets.count());
ui->previewProcessBar->setMinimum(0); ui->previewProcessBar->setMinimum(0);
ui->previewProcessBar->setValue(0); ui->previewProcessBar->setValue(0);
ui->previewProcessBar->setVisible(this->previewableSearchResults.size() > 0); ui->previewProcessBar->setVisible(this->previewCoordinator.previewableCount() > 0);
++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); this->ui->txtSearch->setEnabled(false);
emit startIpcPreviews(renderConfig, targets);
this->previewCoordinator.startGeneration(renderConfig, targets);
} }
void MainWindow::handleSearchError(QString error) void MainWindow::handleSearchError(QString error)
@ -1006,11 +994,12 @@ void MainWindow::createSearchResultMenu(QMenu &menu, const QFileInfo &fileInfo)
[&fileInfo] { QGuiApplication::clipboard()->setText(fileInfo.absoluteFilePath()); }); [&fileInfo] { QGuiApplication::clipboard()->setText(fileInfo.absoluteFilePath()); });
menu.addAction("Open containing folder", [this, &fileInfo] { this->openFile(fileInfo.absolutePath()); }); menu.addAction("Open containing folder", [this, &fileInfo] { this->openFile(fileInfo.absolutePath()); });
auto previewables = this->previewCoordinator.getPreviewableSearchResults();
auto result = auto result =
std::find_if(this->previewableSearchResults.begin(), this->previewableSearchResults.end(), std::find_if(previewables.begin(), previewables.end(),
[this, &fileInfo](SearchResult &a) { return fileInfo.absoluteFilePath() == a.fileData.absPath; }); [this, &fileInfo](SearchResult &a) { return fileInfo.absoluteFilePath() == a.fileData.absPath; });
if(result != this->previewableSearchResults.end()) if(result != previewables.end())
{ {
menu.addAction("Show previews for this file", menu.addAction("Show previews for this file",
[this, &fileInfo] [this, &fileInfo]
@ -1069,7 +1058,6 @@ void MainWindow::showSearchResultsContextMenu(const QPoint &point)
MainWindow::~MainWindow() MainWindow::~MainWindow()
{ {
syncerThread.terminate(); syncerThread.terminate();
ipcClientThread.terminate();
delete this->indexSyncer; delete this->indexSyncer;
delete this->dbService; delete this->dbService;
delete this->dbFactory; delete this->dbFactory;

View File

@ -12,7 +12,7 @@
#include <QProgressDialog> #include <QProgressDialog>
#include "../shared/looqsquery.h" #include "../shared/looqsquery.h"
#include "../shared/indexsyncer.h" #include "../shared/indexsyncer.h"
#include "ipcpreviewclient.h" #include "previewcoordinator.h"
#include "indexer.h" #include "indexer.h"
namespace Ui namespace Ui
{ {
@ -27,8 +27,9 @@ class MainWindow : public QMainWindow
DatabaseFactory *dbFactory; DatabaseFactory *dbFactory;
SqliteDbService *dbService; SqliteDbService *dbService;
Ui::MainWindow *ui; Ui::MainWindow *ui;
IPCPreviewClient ipcPreviewClient;
QThread ipcClientThread; PreviewCoordinator previewCoordinator;
QThread syncerThread; QThread syncerThread;
Indexer *indexer; Indexer *indexer;
IndexSyncer *indexSyncer; IndexSyncer *indexSyncer;
@ -36,18 +37,12 @@ class MainWindow : public QMainWindow
QFileIconProvider iconProvider; QFileIconProvider iconProvider;
QSqlDatabase db; QSqlDatabase db;
QFutureWatcher<QVector<SearchResult>> searchWatcher; QFutureWatcher<QVector<SearchResult>> searchWatcher;
QVector<SearchResult> previewableSearchResults;
LooqsQuery contentSearchQuery; LooqsQuery contentSearchQuery;
QVector<QString> searchHistory; QVector<QString> searchHistory;
int currentSearchHistoryIndex = 0; int currentSearchHistoryIndex = 0;
QString currentSavedSearchText; QString currentSavedSearchText;
QHash<QString, int> previewOrder; /* Quick lookup for the order a preview should have */
QMap<int, QWidget *>
previewWidgetOrderCache /* Saves those that arrived out of order to be inserted later at the correct pos */;
bool previewDirty = false; bool previewDirty = false;
int previewsPerPage = 20; int previewsPerPage = 20;
unsigned int processedPdfPreviews = 0;
unsigned int currentPreviewGeneration = 1;
void connectSignals(); void connectSignals();
void makePreviews(int page); void makePreviews(int page);
@ -69,7 +64,7 @@ class MainWindow : public QMainWindow
void treeSearchItemActivated(QTreeWidgetItem *item, int i); void treeSearchItemActivated(QTreeWidgetItem *item, int i);
void showSearchResultsContextMenu(const QPoint &point); void showSearchResultsContextMenu(const QPoint &point);
void tabChanged(); void tabChanged();
void previewReceived(QSharedPointer<PreviewResult> preview, unsigned int previewGeneration); void previewReceived();
void comboScaleChanged(int i); void comboScaleChanged(int i);
void spinPreviewPageValueChanged(int val); void spinPreviewPageValueChanged(int val);
void startIndexing(); void startIndexing();

View File

@ -18,16 +18,13 @@
<item> <item>
<widget class="QLineEdit" name="txtSearch"/> <widget class="QLineEdit" name="txtSearch"/>
</item> </item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3"/>
</item>
<item> <item>
<widget class="QTabWidget" name="tabWidget"> <widget class="QTabWidget" name="tabWidget">
<property name="tabPosition"> <property name="tabPosition">
<enum>QTabWidget::South</enum> <enum>QTabWidget::South</enum>
</property> </property>
<property name="currentIndex"> <property name="currentIndex">
<number>1</number> <number>2</number>
</property> </property>
<widget class="QWidget" name="resultsTab"> <widget class="QWidget" name="resultsTab">
<attribute name="title"> <attribute name="title">
@ -82,7 +79,7 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>1244</width> <width>1244</width>
<height>633</height> <height>641</height>
</rect> </rect>
</property> </property>
<layout class="QHBoxLayout" name="horizontalLayout"/> <layout class="QHBoxLayout" name="horizontalLayout"/>
@ -195,62 +192,6 @@
</attribute> </attribute>
<layout class="QGridLayout" name="gridLayout"> <layout class="QGridLayout" name="gridLayout">
<item row="6" column="0"> <item row="6" column="0">
<widget class="QLineEdit" name="txtIgnorePatterns"/>
</item>
<item row="11" column="0">
<widget class="QPushButton" name="btnStartIndexing">
<property name="text">
<string>Start indexing</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QGroupBox" name="groupBoxPaths">
<property name="title">
<string>Add paths to scan</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="1" column="0">
<widget class="QLineEdit" name="txtPathScanAdd"/>
</item>
<item row="3" column="0" colspan="5">
<widget class="QListWidget" name="lstPaths"/>
</item>
<item row="1" column="3">
<widget class="QToolButton" name="btnDeletePath">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Delete</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QPushButton" name="btnChoosePath">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QPushButton" name="btnAddPath">
<property name="text">
<string>Add</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Ignore patterns, separated by ';'. Example: *.js;*Downloads*</string>
</property>
</widget>
</item>
<item row="9" column="0">
<widget class="QGroupBox" name="groupBoxIndexProgress"> <widget class="QGroupBox" name="groupBoxIndexProgress">
<property name="contextMenuPolicy"> <property name="contextMenuPolicy">
<enum>Qt::PreventContextMenu</enum> <enum>Qt::PreventContextMenu</enum>
@ -452,6 +393,102 @@
</layout> </layout>
</widget> </widget>
</item> </item>
<item row="2" column="0">
<widget class="QGroupBox" name="groupBoxIndexOptions">
<property name="title">
<string>Index options</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_11">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Ignore patterns, separated by ';'. Example: *.js;*Downloads*:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="txtIgnorePatterns"/>
</item>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="radioIndexEverything">
<property name="text">
<string>Index everything (metadata + file content)</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="chkFillContentForContentless">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Index content for files previously indexed without content</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="radioMetadataOnly">
<property name="text">
<string>Index metadata only, don't process content of files</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="8" column="0">
<widget class="QPushButton" name="btnStartIndexing">
<property name="text">
<string>Start indexing</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QGroupBox" name="groupBoxPaths">
<property name="title">
<string>Add paths to scan</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="1" column="0">
<widget class="QLineEdit" name="txtPathScanAdd"/>
</item>
<item row="3" column="0" colspan="5">
<widget class="QListWidget" name="lstPaths"/>
</item>
<item row="1" column="3">
<widget class="QToolButton" name="btnDeletePath">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Delete</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QPushButton" name="btnChoosePath">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QPushButton" name="btnAddPath">
<property name="text">
<string>Add</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout> </layout>
</widget> </widget>
<widget class="QWidget" name="settingsTab"> <widget class="QWidget" name="settingsTab">
@ -701,5 +738,22 @@
</widget> </widget>
<layoutdefault spacing="6" margin="11"/> <layoutdefault spacing="6" margin="11"/>
<resources/> <resources/>
<connections/> <connections>
<connection>
<sender>radioIndexEverything</sender>
<signal>toggled(bool)</signal>
<receiver>chkFillContentForContentless</receiver>
<slot>setEnabled(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>639</x>
<y>464</y>
</hint>
<hint type="destinationlabel">
<x>639</x>
<y>497</y>
</hint>
</hints>
</connection>
</connections>
</ui> </ui>

View File

@ -0,0 +1,97 @@
#include "previewcoordinator.h"
#include <QFileInfo>
PreviewCoordinator::PreviewCoordinator()
{
this->ipcPreviewClient.moveToThread(&this->ipcClientThread);
connect(&ipcPreviewClient, &IPCPreviewClient::previewReceived, this, &PreviewCoordinator::handleReceivedPreview,
Qt::QueuedConnection);
connect(&ipcPreviewClient, &IPCPreviewClient::finished, this, [&] { emit completedGeneration(); });
connect(this, &PreviewCoordinator::ipcStartGeneration, &ipcPreviewClient, &IPCPreviewClient::startGeneration,
Qt::QueuedConnection);
this->ipcClientThread.start();
}
void PreviewCoordinator::init(const QVector<SearchResult> &searchResults)
{
this->previewableSearchResults.clear();
for(const SearchResult &result : searchResults)
{
if(result.wasContentSearch)
{
QString path = result.fileData.absPath;
// HACK until we can preview them properly
if(path.endsWith(".html") || path.endsWith(".htm"))
{
continue;
}
QFileInfo info{path};
if(info.exists())
{
this->previewableSearchResults.append(result);
}
}
}
}
void PreviewCoordinator::setSocketPath(QString socketPath)
{
this->socketPath = socketPath;
this->ipcPreviewClient.setSocketPath(socketPath);
}
int PreviewCoordinator::previewableCount() const
{
return this->previewableSearchResults.count();
}
QSharedPointer<PreviewResult> PreviewCoordinator::resultAt(int index)
{
if(this->previewResults.size() > index)
{
return {this->previewResults[index]};
}
return {nullptr};
}
const QVector<SearchResult> &PreviewCoordinator::getPreviewableSearchResults() const
{
return this->previewableSearchResults;
}
void PreviewCoordinator::handleReceivedPreview(QSharedPointer<PreviewResult> preview, unsigned int previewGeneration)
{
if(previewGeneration < this->currentPreviewGeneration)
{
return;
}
if(!preview.isNull() && preview->hasPreview())
{
QString docPath = preview->getDocumentPath();
auto previewPage = preview->getPage();
int pos = previewOrder[docPath + QString::number(previewPage)];
this->previewResults[pos] = preview;
emit previewReady();
}
}
void PreviewCoordinator::startGeneration(RenderConfig config, const QVector<RenderTarget> &targets)
{
++this->currentPreviewGeneration;
this->previewOrder.clear();
this->previewResults.clear();
this->previewResults.resize(targets.size());
this->previewResults.fill(nullptr);
int i = 0;
for(const RenderTarget &target : targets)
{
this->previewOrder[target.path + QString::number(target.page)] = i++;
}
emit ipcStartGeneration(config, targets);
}

48
gui/previewcoordinator.h Normal file
View File

@ -0,0 +1,48 @@
#ifndef PREVIEWCOORDINATOR_H
#define PREVIEWCOORDINATOR_H
#include <QVector>
#include <QObject>
#include <QThread>
#include "searchresult.h"
#include "previewresult.h"
#include "ipcpreviewclient.h"
#include "rendertarget.h"
class PreviewCoordinator : public QObject
{
Q_OBJECT
private:
QThread ipcClientThread;
IPCPreviewClient ipcPreviewClient;
QString socketPath;
QVector<QSharedPointer<PreviewResult>> previewResults;
QVector<SearchResult> previewableSearchResults;
unsigned int currentPreviewGeneration = 1;
/* Quick lookup table for the order a preview should have */
QHash<QString, int> previewOrder;
public:
PreviewCoordinator();
void init(const QVector<SearchResult> &searchResults);
int previewableCount() const;
const QVector<SearchResult> &getPreviewableSearchResults() const;
QSharedPointer<PreviewResult> resultAt(int index);
void setSocketPath(QString socketPath);
public slots:
void startGeneration(RenderConfig config, const QVector<RenderTarget> &targets);
void handleReceivedPreview(QSharedPointer<PreviewResult> preview, unsigned int previewGeneration);
signals:
void previewReady();
void completedGeneration();
void error(QString);
void ipcStartGeneration(RenderConfig config, const QVector<RenderTarget> &targets);
};
#endif // PREVIEWCOORDINATOR_H

View File

@ -20,6 +20,8 @@ Poppler::Document *PreviewGeneratorPdf::document(QString path)
return nullptr; return nullptr;
} }
result->setRenderHint(Poppler::Document::TextAntialiasing); result->setRenderHint(Poppler::Document::TextAntialiasing);
result->setRenderHint(Poppler::Document::TextHinting);
result->setRenderHint(Poppler::Document::TextSlightHinting);
locker.relock(); locker.relock();
documentcache.insert(path, result); documentcache.insert(path, result);

View File

@ -38,18 +38,17 @@ SaveFileResult FileSaver::updateFile(QString path)
return saveFile(info); return saveFile(info);
} }
int FileSaver::addFiles(const QVector<QString> paths, bool keepGoing, bool verbose) int FileSaver::addFiles(const QVector<QString> paths)
{ {
return processFiles(paths, std::bind(&FileSaver::addFile, this, std::placeholders::_1), keepGoing, verbose); return processFiles(paths, std::bind(&FileSaver::addFile, this, std::placeholders::_1));
} }
int FileSaver::updateFiles(const QVector<QString> paths, bool keepGoing, bool verbose) int FileSaver::updateFiles(const QVector<QString> paths)
{ {
return processFiles(paths, std::bind(&FileSaver::updateFile, this, std::placeholders::_1), keepGoing, verbose); return processFiles(paths, std::bind(&FileSaver::updateFile, this, std::placeholders::_1));
} }
int FileSaver::processFiles(const QVector<QString> paths, std::function<SaveFileResult(QString path)> saverFunc, int FileSaver::processFiles(const QVector<QString> paths, std::function<SaveFileResult(QString path)> saverFunc)
bool keepGoing, bool verbose)
{ {
std::atomic<bool> terminate{false}; std::atomic<bool> terminate{false};
std::atomic<int> processedCount{0}; std::atomic<int> processedCount{0};
@ -60,7 +59,7 @@ int FileSaver::processFiles(const QVector<QString> paths, std::function<SaveFile
{ {
return; return;
} }
if(verbose) if(this->fileSaverOptions.verbose)
{ {
Logger::info() << "Processing " << path << Qt::endl; Logger::info() << "Processing " << path << Qt::endl;
} }
@ -68,7 +67,7 @@ int FileSaver::processFiles(const QVector<QString> paths, std::function<SaveFile
if(result == DBFAIL || result == PROCESSFAIL) if(result == DBFAIL || result == PROCESSFAIL)
{ {
Logger::error() << "Failed to process " << path << Qt::endl; Logger::error() << "Failed to process " << path << Qt::endl;
if(!keepGoing) if(!this->fileSaverOptions.keepGoing)
{ {
terminate = true; terminate = true;
} }
@ -76,7 +75,7 @@ int FileSaver::processFiles(const QVector<QString> paths, std::function<SaveFile
else else
{ {
++processedCount; ++processedCount;
if(verbose) if(this->fileSaverOptions.verbose)
{ {
if(result == SKIPPED) if(result == SKIPPED)
{ {
@ -120,11 +119,29 @@ SaveFileResult FileSaver::saveFile(const QFileInfo &fileInfo)
{ {
if(canonicalPath.startsWith(excludedPath)) if(canonicalPath.startsWith(excludedPath))
{ {
if(this->fileSaverOptions.verbose)
{
Logger::info() << "Skipped due to excluded path";
}
return SKIPPED; return SKIPPED;
} }
} }
if(fileInfo.size() > 0) bool mustFillContent = this->fileSaverOptions.fillExistingContentless;
if(!mustFillContent)
{
mustFillContent = !this->fileSaverOptions.metadataOnly;
if(mustFillContent)
{
auto filetype = this->dbService->queryFileType(fileInfo.absolutePath());
if(filetype)
{
mustFillContent = filetype.value() == 'c';
}
}
}
if(fileInfo.size() > 0 && mustFillContent)
{ {
QProcess process; QProcess process;
QStringList args; QStringList args;
@ -159,7 +176,7 @@ SaveFileResult FileSaver::saveFile(const QFileInfo &fileInfo)
} }
} }
} }
SaveFileResult result = this->dbService->saveFile(fileInfo, pageData); SaveFileResult result = this->dbService->saveFile(fileInfo, pageData, this->fileSaverOptions.metadataOnly);
if(result == OK && processorReturnCode == OK_WASEMPTY) if(result == OK && processorReturnCode == OK_WASEMPTY)
{ {
return OK_WASEMPTY; return OK_WASEMPTY;

View File

@ -2,6 +2,7 @@
#define FILESAVER_H #define FILESAVER_H
#include <QSqlDatabase> #include <QSqlDatabase>
#include <QFileInfo> #include <QFileInfo>
#include "filesaveroptions.h"
#include "pagedata.h" #include "pagedata.h"
#include "filedata.h" #include "filedata.h"
#include "sqlitedbservice.h" #include "sqlitedbservice.h"
@ -11,16 +12,21 @@ class FileSaver
private: private:
SqliteDbService *dbService; SqliteDbService *dbService;
QStringList excludedPaths = Common::excludedPaths(); QStringList excludedPaths = Common::excludedPaths();
FileSaverOptions fileSaverOptions;
public: public:
FileSaver(SqliteDbService &dbService); FileSaver(SqliteDbService &dbService);
SaveFileResult addFile(QString path); SaveFileResult addFile(QString path);
SaveFileResult updateFile(QString path); SaveFileResult updateFile(QString path);
SaveFileResult saveFile(const QFileInfo &fileInfo); SaveFileResult saveFile(const QFileInfo &fileInfo);
int processFiles(const QVector<QString> paths, std::function<SaveFileResult(QString path)> saverFunc, int processFiles(const QVector<QString> paths, std::function<SaveFileResult(QString path)> saverFunc);
bool keepGoing, bool verbose); int addFiles(const QVector<QString> paths);
int addFiles(const QVector<QString> paths, bool keepGoing, bool verbose); int updateFiles(const QVector<QString> paths);
int updateFiles(const QVector<QString> paths, bool keepGoing, bool verbose);
void setFileSaverOptions(FileSaverOptions options)
{
this->fileSaverOptions = options;
}
}; };
#endif // FILESAVER_H #endif // FILESAVER_H

14
shared/filesaveroptions.h Normal file
View File

@ -0,0 +1,14 @@
#ifndef FILESAVEROPTIONS_H
#define FILESAVEROPTIONS_H
class FileSaverOptions
{
public:
bool verbose = false;
bool keepGoing = false;
bool metadataOnly = false;
/* Whether those previously explicitly without content should be filled */
bool fillExistingContentless = false;
};
#endif // FILESAVEROPTIONS_H

View File

@ -12,6 +12,7 @@ FileScanWorker::FileScanWorker(SqliteDbService &db, ConcurrentQueue<QString> &qu
void FileScanWorker::run() void FileScanWorker::run()
{ {
FileSaver saver{*this->dbService}; FileSaver saver{*this->dbService};
saver.setFileSaverOptions(this->fileSaverOptions);
auto paths = queue->dequeue(batchsize); auto paths = queue->dequeue(batchsize);
for(QString &path : paths) for(QString &path : paths)
{ {
@ -34,3 +35,8 @@ void FileScanWorker::run()
} }
emit finished(); emit finished();
} }
void FileScanWorker::setFileSaverOptions(FileSaverOptions options)
{
this->fileSaverOptions = options;
}

View File

@ -15,12 +15,14 @@ class FileScanWorker : public QObject, public QRunnable
protected: protected:
SqliteDbService *dbService; SqliteDbService *dbService;
ConcurrentQueue<QString> *queue; ConcurrentQueue<QString> *queue;
FileSaverOptions fileSaverOptions;
int batchsize; int batchsize;
std::atomic<bool> *stopToken; std::atomic<bool> *stopToken;
public: public:
FileScanWorker(SqliteDbService &db, ConcurrentQueue<QString> &queue, int batchsize, std::atomic<bool> &stopToken); FileScanWorker(SqliteDbService &db, ConcurrentQueue<QString> &queue, int batchsize, std::atomic<bool> &stopToken);
void run() override; void run() override;
void setFileSaverOptions(FileSaverOptions options);
signals: signals:
void result(FileScanResult); void result(FileScanResult);
void finished(); void finished();

View File

@ -73,16 +73,6 @@ void Indexer::setTargetPaths(QVector<QString> pathsToScan)
this->pathsToScan = pathsToScan; this->pathsToScan = pathsToScan;
} }
void Indexer::setVerbose(bool verbose)
{
this->verbose = verbose;
}
void Indexer::setKeepGoing(bool keepGoing)
{
this->keepGoing = keepGoing;
}
void Indexer::requestCancellation() void Indexer::requestCancellation()
{ {
this->dirScanner->cancel(); this->dirScanner->cancel();
@ -108,6 +98,7 @@ void Indexer::launchWorker(ConcurrentQueue<QString> &queue, int batchsize)
FileScanWorker *runnable = new FileScanWorker(*this->db, queue, batchsize, this->workerCancellationToken); FileScanWorker *runnable = new FileScanWorker(*this->db, queue, batchsize, this->workerCancellationToken);
connect(runnable, &FileScanWorker::result, this, &Indexer::processFileScanResult); connect(runnable, &FileScanWorker::result, this, &Indexer::processFileScanResult);
connect(runnable, &FileScanWorker::finished, this, &Indexer::processFinishedWorker); connect(runnable, &FileScanWorker::finished, this, &Indexer::processFinishedWorker);
runnable->setFileSaverOptions(this->fileSaverOptions);
++this->runningWorkers; ++this->runningWorkers;
QThreadPool::globalInstance()->start(runnable); QThreadPool::globalInstance()->start(runnable);
} }
@ -123,7 +114,7 @@ void Indexer::processFileScanResult(FileScanResult result)
if(isErrorSaveFileResult(result.second)) if(isErrorSaveFileResult(result.second))
{ {
this->currentIndexResult.results.append(result); this->currentIndexResult.results.append(result);
if(!keepGoing) if(!this->fileSaverOptions.keepGoing)
{ {
this->requestCancellation(); this->requestCancellation();
emit finished(); emit finished();
@ -132,7 +123,7 @@ void Indexer::processFileScanResult(FileScanResult result)
} }
else else
{ {
if(verbose) if(this->fileSaverOptions.verbose)
{ {
this->currentIndexResult.results.append(result); this->currentIndexResult.results.append(result);
} }
@ -175,3 +166,8 @@ void Indexer::processFinishedWorker()
emit finished(); emit finished();
} }
} }
void Indexer::setFileSaverOptions(FileSaverOptions options)
{
this->fileSaverOptions = options;
}

View File

@ -52,8 +52,7 @@ class Indexer : public QObject
{ {
Q_OBJECT Q_OBJECT
protected: protected:
bool verbose = false; FileSaverOptions fileSaverOptions;
bool keepGoing = true;
SqliteDbService *db; SqliteDbService *db;
int progressReportThreshold = 50; int progressReportThreshold = 50;
@ -80,8 +79,8 @@ class Indexer : public QObject
void beginIndexing(); void beginIndexing();
void setIgnorePattern(QStringList ignorePattern); void setIgnorePattern(QStringList ignorePattern);
void setTargetPaths(QVector<QString> pathsToScan); void setTargetPaths(QVector<QString> pathsToScan);
void setVerbose(bool verbose);
void setKeepGoing(bool keepGoing); void setFileSaverOptions(FileSaverOptions options);
void requestCancellation(); void requestCancellation();

View File

@ -7,21 +7,16 @@ IndexSyncer::IndexSyncer(SqliteDbService &dbService)
this->dbService = &dbService; this->dbService = &dbService;
} }
void IndexSyncer::setFileSaverOptions(FileSaverOptions options)
{
fileSaverOptions = options;
}
void IndexSyncer::setDryRun(bool dryRun) void IndexSyncer::setDryRun(bool dryRun)
{ {
this->dryRun = dryRun; this->dryRun = dryRun;
} }
void IndexSyncer::setVerbose(bool verbose)
{
this->verbose = verbose;
}
void IndexSyncer::setKeepGoing(bool keepGoing)
{
this->keepGoing = keepGoing;
}
void IndexSyncer::setRemoveDeletedFromIndex(bool removeDeletedFromIndex) void IndexSyncer::setRemoveDeletedFromIndex(bool removeDeletedFromIndex)
{ {
this->removeDeletedFromIndex = removeDeletedFromIndex; this->removeDeletedFromIndex = removeDeletedFromIndex;
@ -35,7 +30,7 @@ void IndexSyncer::setPattern(QString pattern)
void IndexSyncer::sync() void IndexSyncer::sync()
{ {
this->stopToken.store(false, std::memory_order_relaxed); this->stopToken.store(false, std::memory_order_relaxed);
FileSaver saver(*this->dbService);
QVector<FileData> files; QVector<FileData> files;
int offset = 0; int offset = 0;
int limit = 10000; int limit = 10000;
@ -87,7 +82,7 @@ void IndexSyncer::sync()
if(!this->dbService->deleteFile(fileData.absPath)) if(!this->dbService->deleteFile(fileData.absPath))
{ {
emit error("Error: Failed to delete " + fileData.absPath + " from the index"); emit error("Error: Failed to delete " + fileData.absPath + " from the index");
if(!this->keepGoing) if(!this->fileSaverOptions.keepGoing)
{ {
emit finished(totalUpdatesFilesCount, totalDeletedFilesCount, totalErroredFilesCount); emit finished(totalUpdatesFilesCount, totalDeletedFilesCount, totalErroredFilesCount);
return; return;
@ -104,13 +99,15 @@ void IndexSyncer::sync()
} }
} }
unsigned int updatedFilesCount = saver.updateFiles(filePathsToUpdate, keepGoing, verbose); FileSaver saver(*this->dbService);
saver.setFileSaverOptions(this->fileSaverOptions);
unsigned int updatedFilesCount = saver.updateFiles(filePathsToUpdate);
unsigned int shouldHaveUpdatedCount = static_cast<unsigned int>(filePathsToUpdate.size()); unsigned int shouldHaveUpdatedCount = static_cast<unsigned int>(filePathsToUpdate.size());
if(updatedFilesCount != shouldHaveUpdatedCount) if(updatedFilesCount != shouldHaveUpdatedCount)
{ {
totalErroredFilesCount += (shouldHaveUpdatedCount - updatedFilesCount); totalErroredFilesCount += (shouldHaveUpdatedCount - updatedFilesCount);
if(!keepGoing) if(!this->fileSaverOptions.keepGoing)
{ {
QString errorMsg = QString("Failed to update all files selected for updating in this batch. Updated") + QString errorMsg = QString("Failed to update all files selected for updating in this batch. Updated") +
updatedFilesCount + "out of" + shouldHaveUpdatedCount + "selected for updating"; updatedFilesCount + "out of" + shouldHaveUpdatedCount + "selected for updating";

View File

@ -1,16 +1,15 @@
#ifndef INDEXSYNCER_H #ifndef INDEXSYNCER_H
#define INDEXSYNCER_H #define INDEXSYNCER_H
#include "sqlitedbservice.h" #include "sqlitedbservice.h"
#include "filesaveroptions.h"
class IndexSyncer : public QObject class IndexSyncer : public QObject
{ {
Q_OBJECT Q_OBJECT
private: private:
SqliteDbService *dbService = nullptr; SqliteDbService *dbService = nullptr;
bool keepGoing = true; FileSaverOptions fileSaverOptions;
bool removeDeletedFromIndex = true; bool removeDeletedFromIndex = true;
bool dryRun = false; bool dryRun = false;
bool verbose = false;
QString pattern; QString pattern;
std::atomic<bool> stopToken{false}; std::atomic<bool> stopToken{false};
@ -18,12 +17,12 @@ class IndexSyncer : public QObject
public: public:
IndexSyncer(SqliteDbService &dbService); IndexSyncer(SqliteDbService &dbService);
void setFileSaverOptions(FileSaverOptions options);
public slots: public slots:
void sync(); void sync();
void cancel(); void cancel();
void setDryRun(bool dryRun); void setDryRun(bool dryRun);
void setVerbose(bool verbose);
void setKeepGoing(bool keepGoing);
void setRemoveDeletedFromIndex(bool removeDeletedFromIndex); void setRemoveDeletedFromIndex(bool removeDeletedFromIndex);
void setPattern(QString pattern); void setPattern(QString pattern);

View File

@ -74,6 +74,7 @@ HEADERS += sqlitesearch.h \
encodingdetector.h \ encodingdetector.h \
filedata.h \ filedata.h \
filesaver.h \ filesaver.h \
filesaveroptions.h \
filescanworker.h \ filescanworker.h \
indexer.h \ indexer.h \
indexsyncer.h \ indexsyncer.h \

View File

@ -29,6 +29,22 @@ QVector<SearchResult> SqliteDbService::search(const LooqsQuery &query)
return searcher.search(query); return searcher.search(query);
} }
std::optional<QChar> SqliteDbService::queryFileType(QString absPath)
{
auto query = QSqlQuery(dbFactory->forCurrentThread());
query.prepare("SELECT filetype FROM file WHERE path = ?");
query.addBindValue(absPath);
if(!query.exec())
{
throw LooqsGeneralException("Error while trying to query for file type: " + query.lastError().text());
}
if(!query.next())
{
return {};
}
return query.value(0).toChar();
}
bool SqliteDbService::fileExistsInDatabase(QString path) bool SqliteDbService::fileExistsInDatabase(QString path)
{ {
auto query = QSqlQuery(dbFactory->forCurrentThread()); auto query = QSqlQuery(dbFactory->forCurrentThread());
@ -148,11 +164,15 @@ bool SqliteDbService::insertToFTS(bool useTrigrams, QSqlDatabase &db, int fileid
return true; return true;
} }
SaveFileResult SqliteDbService::saveFile(QFileInfo fileInfo, QVector<PageData> &pageData) SaveFileResult SqliteDbService::saveFile(QFileInfo fileInfo, QVector<PageData> &pageData, bool pathsOnly)
{ {
QString absPath = fileInfo.absoluteFilePath(); QString absPath = fileInfo.absoluteFilePath();
auto mtime = fileInfo.lastModified().toSecsSinceEpoch(); auto mtime = fileInfo.lastModified().toSecsSinceEpoch();
QChar fileType = fileInfo.isDir() ? 'd' : 'f'; QChar fileType = fileInfo.isDir() ? 'd' : 'c';
if(pathsOnly)
{
fileType = 'f';
}
QSqlDatabase db = dbFactory->forCurrentThread(); QSqlDatabase db = dbFactory->forCurrentThread();
QSqlQuery delQuery(db); QSqlQuery delQuery(db);
@ -186,6 +206,8 @@ SaveFileResult SqliteDbService::saveFile(QFileInfo fileInfo, QVector<PageData> &
return DBFAIL; return DBFAIL;
} }
if(!pathsOnly)
{
int lastid = inserterQuery.lastInsertId().toInt(); int lastid = inserterQuery.lastInsertId().toInt();
if(!insertToFTS(false, db, lastid, pageData)) if(!insertToFTS(false, db, lastid, pageData))
{ {
@ -199,6 +221,8 @@ SaveFileResult SqliteDbService::saveFile(QFileInfo fileInfo, QVector<PageData> &
Logger::error() << "Failed to insert data to FTS index " << Qt::endl; Logger::error() << "Failed to insert data to FTS index " << Qt::endl;
return DBFAIL; return DBFAIL;
} }
}
if(!db.commit()) if(!db.commit())
{ {
db.rollback(); db.rollback();

View File

@ -1,6 +1,8 @@
#ifndef SQLITEDBSERVICE_H #ifndef SQLITEDBSERVICE_H
#define SQLITEDBSERVICE_H #define SQLITEDBSERVICE_H
#include <QFileInfo> #include <QFileInfo>
#include <optional>
#include "databasefactory.h" #include "databasefactory.h"
#include "utils.h" #include "utils.h"
#include "pagedata.h" #include "pagedata.h"
@ -17,12 +19,14 @@ class SqliteDbService
public: public:
SqliteDbService(DatabaseFactory &dbFactory); SqliteDbService(DatabaseFactory &dbFactory);
SaveFileResult saveFile(QFileInfo fileInfo, QVector<PageData> &pageData); SaveFileResult saveFile(QFileInfo fileInfo, QVector<PageData> &pageData, bool pathsOnly);
unsigned int getFiles(QVector<FileData> &results, QString wildCardPattern, int offset, int limit); unsigned int getFiles(QVector<FileData> &results, QString wildCardPattern, int offset, int limit);
bool deleteFile(QString path); bool deleteFile(QString path);
bool fileExistsInDatabase(QString path); bool fileExistsInDatabase(QString path);
bool fileExistsInDatabase(QString path, qint64 mtime); bool fileExistsInDatabase(QString path, qint64 mtime);
QVector<SearchResult> search(const LooqsQuery &query); QVector<SearchResult> search(const LooqsQuery &query);
std::optional<QChar> queryFileType(QString absPath);
}; };
#endif // SQLITEDBSERVICE_H #endif // SQLITEDBSERVICE_H