Compare commits

...

5 Commits

Author SHA1 Message Date
Albert S. dbe4061c58 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.
2022-12-06 11:59:26 +01:00
Albert S. 87e3cc3380 gui: PreviewGeneratorPdf: Enable Text hinting 2022-12-06 11:58:10 +01:00
Albert S. ffdaa4c0c6 gui: mainwindow: Refactor to use new PreviewCoordinator 2022-12-06 11:57:34 +01:00
Albert S. fe8da94951 gui: Introduce PreviewCoordinator
Move some preview generation logic to PreviewCoordinator
2022-12-06 11:55:16 +01:00
Albert S. 57f0afaf91 gui: mainwindow: Fix typo in method name 2022-11-22 20:29:32 +01:00
7 changed files with 203 additions and 85 deletions

View File

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

View File

@ -28,7 +28,7 @@ void enableIpcSandbox()
policy->namespace_options = EXILE_UNSHARE_USER | EXILE_UNSHARE_MOUNT | EXILE_UNSHARE_NETWORK;
policy->no_new_privs = 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;
QString ipcSocketPath = Common::ipcSocketPath();

View File

@ -22,7 +22,6 @@
#include "../shared/sqlitesearch.h"
#include "../shared/looqsgeneralexception.h"
#include "../shared/common.h"
#include "ipcpreviewclient.h"
#include "previewgenerator.h"
#include "aboutdialog.h"
@ -32,8 +31,7 @@ MainWindow::MainWindow(QWidget *parent, QString socketPath)
this->progressDialog.cancel(); // because constructing it shows it, quite weird
ui->setupUi(this);
setWindowTitle(QCoreApplication::applicationName());
this->ipcPreviewClient.moveToThread(&this->ipcClientThread);
this->ipcPreviewClient.setSocketPath(socketPath);
QSettings settings;
this->dbFactory = new DatabaseFactory(Common::databasePath());
@ -78,7 +76,7 @@ MainWindow::MainWindow(QWidget *parent, QString socketPath)
ui->txtSearch->installEventFilter(this);
ui->scrollArea->viewport()->installEventFilter(this);
this->ipcClientThread.start();
this->previewCoordinator.setSocketPath(socketPath);
}
void MainWindow::addPathToIndex()
@ -208,9 +206,9 @@ void MainWindow::connectSignals()
}
},
Qt::QueuedConnection);
connect(&ipcPreviewClient, &IPCPreviewClient::previewReceived, this, &MainWindow::previewReceived,
connect(&previewCoordinator, &PreviewCoordinator::previewReady, this, &MainWindow::previewReceived,
Qt::QueuedConnection);
connect(&ipcPreviewClient, &IPCPreviewClient::finished, this,
connect(&previewCoordinator, &PreviewCoordinator::completedGeneration, this,
[&]
{
this->ui->previewProcessBar->setValue(this->ui->previewProcessBar->maximum());
@ -218,17 +216,17 @@ void MainWindow::connectSignals()
this->ui->comboPreviewFiles->setEnabled(true);
ui->txtSearch->setEnabled(true);
});
connect(&ipcPreviewClient, &IPCPreviewClient::error, this,
connect(&previewCoordinator, &PreviewCoordinator::error, this,
[this](QString msg)
{
qCritical() << msg << Qt::endl;
QMessageBox::critical(this, "IPC error", msg);
});
connect(this, &MainWindow::startIpcPreviews, &ipcPreviewClient, &IPCPreviewClient::startGeneration,
/*connect(this, &MainWindow::startIpcPreviews, &previewCoordinator, &IPCPreviewClient::startGeneration,
Qt::QueuedConnection);
connect(this, &MainWindow::stopIpcPreviews, &ipcPreviewClient, &IPCPreviewClient::stopGeneration,
Qt::QueuedConnection);
Qt::QueuedConnection); */
}
void MainWindow::exportFailedPaths()
@ -632,13 +630,17 @@ void MainWindow::saveSettings()
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);
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())
{
QString docPath = preview->getDocumentPath();
@ -661,7 +663,7 @@ void MainWindow::previewReceived(QSharedPointer<PreviewResult> preview, unsigned
{
QFileInfo fileInfo{docPath};
QMenu menu("labeRightClick", this);
createSearchResutlMenu(menu, fileInfo);
createSearchResultMenu(menu, fileInfo);
menu.addAction("Copy page number",
[previewPage] { QGuiApplication::clipboard()->setText(QString::number(previewPage)); });
menu.exec(QCursor::pos());
@ -684,24 +686,7 @@ void MainWindow::previewReceived(QSharedPointer<PreviewResult> preview, unsigned
previewWidget->setLayout(previewLayout);
QBoxLayout *layout = static_cast<QBoxLayout *>(ui->scrollAreaWidgetContents->layout());
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;
}
layout->insertWidget(index, previewWidget);
}
}
@ -818,7 +803,6 @@ void MainWindow::lineEditReturnPressed()
void MainWindow::handleSearchResults(const QVector<SearchResult> &results)
{
this->previewableSearchResults.clear();
qDeleteAll(ui->scrollAreaWidgetContents->children());
ui->treeResultsList->clear();
@ -827,6 +811,8 @@ void MainWindow::handleSearchResults(const QVector<SearchResult> &results)
ui->comboPreviewFiles->setVisible(true);
ui->lblTotalPreviewPagesCount->setText("");
this->previewCoordinator.init(results);
bool hasDeleted = false;
QHash<QString, bool> seenMap;
for(const SearchResult &result : results)
@ -847,34 +833,29 @@ void MainWindow::handleSearchResults(const QVector<SearchResult> &results)
item->setText(3, this->locale().formattedDataSize(result.fileData.size));
}
bool exists = pathInfo.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
if(!exists)
{
hasDeleted = 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(1);
ui->treeResultsList->resizeColumnToContents(2);
previewDirty = !this->previewableSearchResults.empty();
previewDirty = this->previewCoordinator.previewableCount() > 0;
ui->spinPreviewPage->setValue(1);
@ -884,7 +865,7 @@ void MainWindow::handleSearchResults(const QVector<SearchResult> &results)
}
QString statusText = "Results: " + QString::number(results.size()) + " files";
statusText += ", previewable: " + QString::number(this->previewableSearchResults.count());
statusText += ", previewable: " + QString::number(this->previewCoordinator.previewableCount());
if(hasDeleted)
{
statusText += " WARNING: Some files are inaccessible. No preview available for those. Index may be out of sync";
@ -901,7 +882,7 @@ int MainWindow::currentSelectedScale()
void MainWindow::makePreviews(int page)
{
if(this->previewableSearchResults.empty())
if(this->previewCoordinator.previewableCount() == 0)
{
return;
}
@ -918,8 +899,7 @@ void MainWindow::makePreviews(int page)
ui->scrollAreaWidgetContents->setLayout(new QVBoxLayout());
ui->scrollAreaWidgetContents->layout()->setAlignment(Qt::AlignCenter);
}
ui->previewProcessBar->setMaximum(this->previewableSearchResults.size());
processedPdfPreviews = 0;
ui->previewProcessBar->setMaximum(this->previewCoordinator.previewableCount());
QVector<QString> wordsToHighlight;
QRegularExpression extractor(R"#("([^"]*)"|([^\s]+))#");
@ -954,12 +934,9 @@ void MainWindow::makePreviews(int page)
renderConfig.scaleY = QGuiApplication::primaryScreen()->physicalDotsPerInchY() * (currentScale / 100.);
renderConfig.wordsToHighlight = wordsToHighlight;
this->previewOrder.clear();
this->previewWidgetOrderCache.clear();
int previewPos = 0;
QVector<RenderTarget> targets;
for(SearchResult &sr : this->previewableSearchResults)
for(const SearchResult &sr : this->previewCoordinator.getPreviewableSearchResults())
{
if(ui->comboPreviewFiles->currentIndex() != 0)
{
@ -971,11 +948,8 @@ void MainWindow::makePreviews(int page)
RenderTarget renderTarget;
renderTarget.path = sr.fileData.absPath;
renderTarget.page = (int)sr.page;
targets.append(renderTarget);
int pos = previewPos - beginOffset;
this->previewOrder[renderTarget.path + QString::number(renderTarget.page)] = pos;
++previewPos;
targets.append(renderTarget);
}
int numpages = ceil(static_cast<double>(targets.size()) / previewsPerPage);
ui->spinPreviewPage->setMaximum(numpages);
@ -985,12 +959,12 @@ void MainWindow::makePreviews(int page)
ui->previewProcessBar->setMaximum(targets.count());
ui->previewProcessBar->setMinimum(0);
ui->previewProcessBar->setValue(0);
ui->previewProcessBar->setVisible(this->previewableSearchResults.size() > 0);
++this->currentPreviewGeneration;
ui->previewProcessBar->setVisible(this->previewCoordinator.previewableCount() > 0);
this->ui->spinPreviewPage->setEnabled(false);
this->ui->comboPreviewFiles->setEnabled(false);
this->ui->txtSearch->setEnabled(false);
emit startIpcPreviews(renderConfig, targets);
this->previewCoordinator.startGeneration(renderConfig, targets);
}
void MainWindow::handleSearchError(QString error)
@ -998,7 +972,7 @@ void MainWindow::handleSearchError(QString error)
ui->lblSearchResults->setText("Error:" + error);
}
void MainWindow::createSearchResutlMenu(QMenu &menu, const QFileInfo &fileInfo)
void MainWindow::createSearchResultMenu(QMenu &menu, const QFileInfo &fileInfo)
{
menu.addAction("Copy filename to clipboard",
[&fileInfo] { QGuiApplication::clipboard()->setText(fileInfo.fileName()); });
@ -1006,11 +980,12 @@ void MainWindow::createSearchResutlMenu(QMenu &menu, const QFileInfo &fileInfo)
[&fileInfo] { QGuiApplication::clipboard()->setText(fileInfo.absoluteFilePath()); });
menu.addAction("Open containing folder", [this, &fileInfo] { this->openFile(fileInfo.absolutePath()); });
auto previewables = this->previewCoordinator.getPreviewableSearchResults();
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; });
if(result != this->previewableSearchResults.end())
if(result != previewables.end())
{
menu.addAction("Show previews for this file",
[this, &fileInfo]
@ -1062,14 +1037,13 @@ void MainWindow::showSearchResultsContextMenu(const QPoint &point)
}
QFileInfo pathinfo(item->text(1));
QMenu menu("SearchResults", this);
createSearchResutlMenu(menu, pathinfo);
createSearchResultMenu(menu, pathinfo);
menu.exec(QCursor::pos());
}
MainWindow::~MainWindow()
{
syncerThread.terminate();
ipcClientThread.terminate();
delete this->indexSyncer;
delete this->dbService;
delete this->dbFactory;

View File

@ -12,7 +12,7 @@
#include <QProgressDialog>
#include "../shared/looqsquery.h"
#include "../shared/indexsyncer.h"
#include "ipcpreviewclient.h"
#include "previewcoordinator.h"
#include "indexer.h"
namespace Ui
{
@ -27,8 +27,9 @@ class MainWindow : public QMainWindow
DatabaseFactory *dbFactory;
SqliteDbService *dbService;
Ui::MainWindow *ui;
IPCPreviewClient ipcPreviewClient;
QThread ipcClientThread;
PreviewCoordinator previewCoordinator;
QThread syncerThread;
Indexer *indexer;
IndexSyncer *indexSyncer;
@ -36,18 +37,12 @@ class MainWindow : public QMainWindow
QFileIconProvider iconProvider;
QSqlDatabase db;
QFutureWatcher<QVector<SearchResult>> searchWatcher;
QVector<SearchResult> previewableSearchResults;
LooqsQuery contentSearchQuery;
QVector<QString> searchHistory;
int currentSearchHistoryIndex = 0;
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;
int previewsPerPage = 20;
unsigned int processedPdfPreviews = 0;
unsigned int currentPreviewGeneration = 1;
void connectSignals();
void makePreviews(int page);
@ -56,7 +51,7 @@ class MainWindow : public QMainWindow
void keyPressEvent(QKeyEvent *event) override;
void handleSearchResults(const QVector<SearchResult> &results);
void handleSearchError(QString error);
void createSearchResutlMenu(QMenu &menu, const QFileInfo &fileInfo);
void createSearchResultMenu(QMenu &menu, const QFileInfo &fileInfo);
void openDocument(QString path, int num);
void openFile(QString path);
void initSettingsTabs();
@ -69,7 +64,7 @@ class MainWindow : public QMainWindow
void treeSearchItemActivated(QTreeWidgetItem *item, int i);
void showSearchResultsContextMenu(const QPoint &point);
void tabChanged();
void previewReceived(QSharedPointer<PreviewResult> preview, unsigned int previewGeneration);
void previewReceived();
void comboScaleChanged(int i);
void spinPreviewPageValueChanged(int val);
void startIndexing();

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;
}
result->setRenderHint(Poppler::Document::TextAntialiasing);
result->setRenderHint(Poppler::Document::TextHinting);
result->setRenderHint(Poppler::Document::TextSlightHinting);
locker.relock();
documentcache.insert(path, result);