WIP/exiled_previews -> dev #36
| @@ -4,6 +4,8 @@ search terms have been found, as shown in the screenshots below. | ||||
|  | ||||
|  | ||||
| ## Screenshots | ||||
| The screenshots in this section may occasionally be slightly outdated, but they are usually recent enough to get an overall impression of the current state. | ||||
|  | ||||
| ### List | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -61,7 +61,8 @@ void IPCPreviewClient::start(RenderConfig config, const QVector<RenderTarget> &t | ||||
|  | ||||
| 	if(!connect() || !socket->isOpen()) | ||||
| 	{ | ||||
| 		// TODO: ERROR | ||||
| 		emit error("Could not connect to IPC worker"); | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	if(socket->isOpen() && socket->isWritable()) | ||||
| @@ -88,7 +89,8 @@ void IPCPreviewClient::start(RenderConfig config, const QVector<RenderTarget> &t | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			// TODO: ERROR | ||||
| 			emit error("Error while trying to process previews: " + socket->errorString()); | ||||
| 			return; | ||||
| 		} | ||||
|  | ||||
| 		int processed = 0; | ||||
| @@ -107,7 +109,7 @@ void IPCPreviewClient::start(RenderConfig config, const QVector<RenderTarget> &t | ||||
| 		} | ||||
| 		if(processed != targets.count()) | ||||
| 		{ | ||||
| 			// TODO: ERROR | ||||
| 			emit error("IPC worker didn't send enough previews. This is a bug, please report"); | ||||
| 		} | ||||
| 	} | ||||
| 	socket->disconnectFromServer(); | ||||
| @@ -118,7 +120,8 @@ void IPCPreviewClient::stopGeneration() | ||||
| { | ||||
| 	if(!connect() || !socket->isOpen()) | ||||
| 	{ | ||||
| 		// TODO: ERROR | ||||
| 		emit error("Could not connect to IPC worker"); | ||||
| 		return; | ||||
| 	} | ||||
| 	QDataStream stream(socket); | ||||
| 	stream << StopGeneratePreviews; | ||||
|   | ||||
| @@ -31,6 +31,7 @@ class IPCPreviewClient : public QObject | ||||
|   signals: | ||||
| 	void previewReceived(QSharedPointer<PreviewResult> previewResult, unsigned int currentPreviewGeneration); | ||||
| 	void finished(); | ||||
| 	void error(QString); | ||||
| }; | ||||
|  | ||||
| #endif // IPCPREVIEWCLIENT_H | ||||
|   | ||||
| @@ -3,29 +3,22 @@ | ||||
| #include "previewgeneratormapfunctor.h" | ||||
| IPCPreviewWorker::IPCPreviewWorker() | ||||
| { | ||||
| 	this->connect(&previewWorkerWatcher, &QFutureWatcher<QByteArray>::resultReadyAt, this, | ||||
| 				  [this](int index) { emit previewGenerated(previewWorkerWatcher.resultAt(index)); }); | ||||
| 	connect(&previewWorkerWatcher, &QFutureWatcher<QByteArray>::finished, this, [this] { emit finished(); }); | ||||
| } | ||||
|  | ||||
| void IPCPreviewWorker::start(RenderConfig config, const QVector<RenderTarget> &targets, QLocalSocket *peer) | ||||
| { | ||||
| 	connect(&previewWorkerWatcher, &QFutureWatcher<QByteArray>::resultReadyAt, this, | ||||
| 			[peer, this](int index) | ||||
| 			{ | ||||
| 				QDataStream stream{peer}; | ||||
| 				stream << previewWorkerWatcher.resultAt(index); | ||||
| 				peer->flush(); | ||||
| 			}); | ||||
| 	connect(&previewWorkerWatcher, &QFutureWatcher<QByteArray>::finished, this, | ||||
| 			[peer] | ||||
| 			{ | ||||
| 				/* TODO / | ||||
| 	stop(); | ||||
| 	auto mapFunctor = PreviewGeneratorMapFunctor(); | ||||
| 	mapFunctor.setRenderConfig(config); | ||||
|  | ||||
| 				/*peer->waitForBytesWritten(); | ||||
| 				peer->disconnectFromServer(); | ||||
| 				peer->deleteLater();*/ | ||||
| 			}); | ||||
|  | ||||
| 	auto mapFunctor = new PreviewGeneratorMapFunctor(); | ||||
| 	mapFunctor->setRenderConfig(config); | ||||
|  | ||||
| 	previewWorkerWatcher.setFuture(QtConcurrent::mapped(targets, *mapFunctor)); | ||||
| 	previewWorkerWatcher.setFuture(QtConcurrent::mapped(targets, mapFunctor)); | ||||
| } | ||||
|  | ||||
| void IPCPreviewWorker::stop() | ||||
| { | ||||
| 	previewWorkerWatcher.cancel(); | ||||
| 	previewWorkerWatcher.waitForFinished(); | ||||
| } | ||||
|   | ||||
| @@ -15,6 +15,10 @@ class IPCPreviewWorker : public QObject | ||||
|   public: | ||||
| 	IPCPreviewWorker(); | ||||
| 	void start(RenderConfig config, const QVector<RenderTarget> &targets, QLocalSocket *peer); | ||||
| 	void stop(); | ||||
|   signals: | ||||
| 	void previewGenerated(QByteArray); | ||||
| 	void finished(); | ||||
| }; | ||||
|  | ||||
| #endif // IPCPREVIEWWORKER_H | ||||
|   | ||||
| @@ -15,7 +15,11 @@ | ||||
|  | ||||
| IpcServer::IpcServer() | ||||
| { | ||||
| 	/* Only 1, we are doing work for the GUI, not a service for general availability */ | ||||
| 	this->spawningServer.setMaxPendingConnections(1); | ||||
| 	connect(&this->spawningServer, &QLocalServer::newConnection, this, &IpcServer::spawnerNewConnection); | ||||
| 	connect(&this->previewWorker, &IPCPreviewWorker::previewGenerated, this, &IpcServer::handlePreviewGenerated); | ||||
| 	connect(&this->previewWorker, &IPCPreviewWorker::finished, this, [this] { this->currentSocket->flush(); }); | ||||
| } | ||||
|  | ||||
| bool IpcServer::startSpawner(QString socketPath) | ||||
| @@ -28,6 +32,7 @@ void IpcServer::spawnerNewConnection() | ||||
| { | ||||
| 	QLocalSocket *socket = this->spawningServer.nextPendingConnection(); | ||||
| 	connect(socket, &QLocalSocket::disconnected, socket, &QLocalSocket::deleteLater); | ||||
| 	this->currentSocket = socket; | ||||
| 	if(socket != nullptr) | ||||
| 	{ | ||||
| 		if(!socket->waitForReadyRead()) | ||||
| @@ -39,10 +44,8 @@ void IpcServer::spawnerNewConnection() | ||||
| 		stream >> command; | ||||
| 		if(command == GeneratePreviews) | ||||
| 		{ | ||||
| 			IPCPreviewWorker *worker = new IPCPreviewWorker(); | ||||
| 			RenderConfig renderConfig; | ||||
| 			QVector<RenderTarget> targets; | ||||
|  | ||||
| 			do | ||||
| 			{ | ||||
| 				/* TODO: this is not entirely robust */ | ||||
| @@ -53,7 +56,18 @@ void IpcServer::spawnerNewConnection() | ||||
|  | ||||
| 			stream << targets.count(); | ||||
| 			socket->flush(); | ||||
| 			worker->start(renderConfig, targets, socket); | ||||
| 			previewWorker.start(renderConfig, targets, socket); | ||||
| 		} | ||||
| 		if(command == StopGeneratePreviews) | ||||
| 		{ | ||||
| 			previewWorker.stop(); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void IpcServer::handlePreviewGenerated(QByteArray ba) | ||||
| { | ||||
| 	QDataStream stream{this->currentSocket}; | ||||
| 	stream << ba; | ||||
| 	this->currentSocket->flush(); | ||||
| } | ||||
|   | ||||
| @@ -4,14 +4,19 @@ | ||||
| #include <QLocalServer> | ||||
| #include "ipc.h" | ||||
| #include "filesaver.h" | ||||
| #include "ipcpreviewworker.h" | ||||
|  | ||||
| class IpcServer : public QObject | ||||
| { | ||||
| 	Q_OBJECT | ||||
|   private: | ||||
| 	IPCPreviewWorker previewWorker; | ||||
| 	QLocalServer spawningServer; | ||||
| 	QLocalSocket *currentSocket = nullptr; | ||||
| 	SaveFileResult addFile(QString file); | ||||
|   private slots: | ||||
| 	void spawnerNewConnection(); | ||||
| 	void handlePreviewGenerated(QByteArray ba); | ||||
|  | ||||
|   public: | ||||
| 	IpcServer(); | ||||
|   | ||||
							
								
								
									
										59
									
								
								gui/main.cpp
									
									
									
									
									
								
							
							
						
						
									
										59
									
								
								gui/main.cpp
									
									
									
									
									
								
							| @@ -5,6 +5,7 @@ | ||||
| #include <QProcess> | ||||
| #include <QDir> | ||||
| #include <QCommandLineParser> | ||||
| #include <QFileInfo> | ||||
|  | ||||
| #include "mainwindow.h" | ||||
| #include "searchresult.h" | ||||
| @@ -32,15 +33,46 @@ void enableSandbox() | ||||
| 	} | ||||
| 	exile_free_policy(policy); | ||||
| } | ||||
|  | ||||
| void enableIpcSandbox() | ||||
| { | ||||
| 	struct exile_policy *policy = exile_create_policy(); | ||||
| 	if(policy == NULL) | ||||
| 	{ | ||||
| 		qCritical() << "Failed to init policy for sandbox"; | ||||
| 		exit(EXIT_FAILURE); | ||||
| 	} | ||||
| 	policy->namespace_options = EXILE_UNSHARE_NETWORK | EXILE_UNSHARE_USER; | ||||
| 	policy->no_new_privs = 1; | ||||
| 	policy->drop_caps = 1; | ||||
| 	policy->vow_promises = exile_vows_from_str("thread cpath wpath rpath unix stdio prot_exec proc shm fsnotify ioctl"); | ||||
|  | ||||
| 	QString ipcSocketPath = Common::ipcSocketPath(); | ||||
| 	QFileInfo info{ipcSocketPath}; | ||||
| 	QString ipcSocketPathDir = info.absolutePath(); | ||||
| 	std::string stdIpcSocketPath = ipcSocketPathDir.toStdString(); | ||||
|  | ||||
| 	exile_append_path_policies(policy, EXILE_FS_ALLOW_ALL_READ, "/"); | ||||
| 	exile_append_path_policies(policy, EXILE_FS_ALLOW_ALL_READ | EXILE_FS_ALLOW_ALL_WRITE, stdIpcSocketPath.c_str()); | ||||
| 	int ret = exile_enable_policy(policy); | ||||
| 	if(ret != 0) | ||||
| 	{ | ||||
| 		qDebug() << "Failed to establish sandbox"; | ||||
| 		exit(EXIT_FAILURE); | ||||
| 	} | ||||
| 	exile_free_policy(policy); | ||||
| } | ||||
|  | ||||
| int main(int argc, char *argv[]) | ||||
| { | ||||
| 	QString socketPath = "/tmp/looqs-spawner"; | ||||
| 	QString socketPath = Common::ipcSocketPath(); | ||||
| 	if(argc > 1) | ||||
| 	{ | ||||
| 		QString arg = argv[1]; | ||||
| 		if(arg == "ipc") | ||||
| 		{ | ||||
| 			Common::setupAppInfo(); | ||||
| 			enableIpcSandbox(); | ||||
| 			QApplication a(argc, argv); | ||||
|  | ||||
| 			IpcServer *ipcserver = new IpcServer(); | ||||
| @@ -70,10 +102,24 @@ int main(int argc, char *argv[]) | ||||
| 			return processor.process(); | ||||
| 		} | ||||
| 	} | ||||
| 	QString ipcSocketPath = Common::ipcSocketPath(); | ||||
| 	QFileInfo info{ipcSocketPath}; | ||||
| 	QString ipcSocketPathDir = info.absolutePath(); | ||||
|  | ||||
| 	QDir dir; | ||||
| 	if(!dir.mkpath(ipcSocketPathDir)) | ||||
| 	{ | ||||
| 		qCritical() << "Failed to create dir for ipc socket" << Qt::endl; | ||||
| 		exit(EXIT_FAILURE); | ||||
| 	} | ||||
|  | ||||
| 	QProcess process; | ||||
| 	QStringList args; | ||||
| 	args << "ipc"; | ||||
| 	if(!process.startDetached("/proc/self/exe", args)) | ||||
| 	process.setProcessChannelMode(QProcess::ForwardedChannels); | ||||
|  | ||||
| 	process.start("/proc/self/exe", args); | ||||
| 	if(!process.waitForStarted(5000)) | ||||
| 	{ | ||||
| 		QString errorMsg = "Failed to start IPC server"; | ||||
| 		qDebug() << errorMsg; | ||||
| @@ -108,17 +154,18 @@ int main(int argc, char *argv[]) | ||||
| 		QMessageBox::critical(nullptr, "Error", e.message); | ||||
| 		return 1; | ||||
| 	} | ||||
| 	// Keep this post sandbox, afterwards does not work (suspect due to threads, but unconfirmed) | ||||
| 	QApplication a(argc, argv); | ||||
| 	a.setWindowIcon(QIcon(":/icon.svg")); | ||||
| 	QObject::connect(&a, &QApplication::aboutToQuit, &process, &QProcess::kill); | ||||
|  | ||||
| 	qRegisterMetaType<QVector<SearchResult>>("QVector<SearchResult>"); | ||||
| 	qRegisterMetaType<QVector<PreviewResultPdf>>("QVector<PreviewResultPdf>"); | ||||
| 	qRegisterMetaType<PreviewResultPdf>("PreviewResultPdf"); | ||||
| 	qRegisterMetaType<FileScanResult>("FileScanResult"); | ||||
|  | ||||
| 	IPCClient client{socketPath}; | ||||
| 	MainWindow w{0, client}; | ||||
| 	qRegisterMetaType<RenderConfig>("RenderConfig"); | ||||
| 	qRegisterMetaType<QVector<RenderTarget>>("QVector<RenderTarget>"); | ||||
| 	qRegisterMetaType<QSharedPointer<PreviewResult>>("QSharedPointer<PreviewResult>"); | ||||
| 	MainWindow w{0, socketPath}; | ||||
| 	w.showMaximized(); | ||||
| 	return a.exec(); | ||||
| } | ||||
|   | ||||
| @@ -13,18 +13,43 @@ | ||||
| #include <QtConcurrent/QtConcurrent> | ||||
| #include <QMessageBox> | ||||
| #include <QFileDialog> | ||||
| #include <QScreen> | ||||
| #include "mainwindow.h" | ||||
| #include "ui_mainwindow.h" | ||||
| #include "clicklabel.h" | ||||
| #include "../shared/sqlitesearch.h" | ||||
| #include "../shared/looqsgeneralexception.h" | ||||
| #include "../shared/common.h" | ||||
| #include "ipcpreviewclient.h" | ||||
| #include "previewgenerator.h" | ||||
|  | ||||
| MainWindow::MainWindow(QWidget *parent, IPCClient &client) : QMainWindow(parent), ui(new Ui::MainWindow) | ||||
| MainWindow::MainWindow(QWidget *parent, QString socketPath) : QMainWindow(parent), ui(new Ui::MainWindow) | ||||
| { | ||||
| 	ui->setupUi(this); | ||||
| 	setWindowTitle(QCoreApplication::applicationName()); | ||||
| 	this->ipcClient = &client; | ||||
| 	this->ipcPreviewClient.moveToThread(&this->ipcClientThread); | ||||
| 	this->ipcPreviewClient.setSocketPath(socketPath); | ||||
|  | ||||
| 	connect(&ipcPreviewClient, &IPCPreviewClient::previewReceived, this, &MainWindow::previewReceived, | ||||
| 			Qt::QueuedConnection); | ||||
| 	connect(&ipcPreviewClient, &IPCPreviewClient::finished, this, | ||||
| 			[&] | ||||
| 			{ | ||||
| 				this->ui->previewProcessBar->setValue(this->ui->previewProcessBar->maximum()); | ||||
| 				this->ui->spinPreviewPage->setEnabled(true); | ||||
| 			}); | ||||
| 	connect(&ipcPreviewClient, &IPCPreviewClient::error, this, | ||||
| 			[this](QString msg) | ||||
| 			{ | ||||
| 				qCritical() << msg << Qt::endl; | ||||
| 				QMessageBox::critical(this, "IPC error", msg); | ||||
| 			}); | ||||
|  | ||||
| 	connect(this, &MainWindow::startIpcPreviews, &ipcPreviewClient, &IPCPreviewClient::startGeneration, | ||||
| 			Qt::QueuedConnection); | ||||
| 	connect(this, &MainWindow::stopIpcPreviews, &ipcPreviewClient, &IPCPreviewClient::stopGeneration, | ||||
| 			Qt::QueuedConnection); | ||||
| 	this->ipcClientThread.start(); | ||||
| 	QSettings settings; | ||||
|  | ||||
| 	this->dbFactory = new DatabaseFactory(Common::databasePath()); | ||||
| @@ -46,6 +71,9 @@ MainWindow::MainWindow(QWidget *parent, IPCClient &client) : QMainWindow(parent) | ||||
|  | ||||
| 	QStringList indexPaths = settings.value("indexPaths").toStringList(); | ||||
| 	ui->lstPaths->addItems(indexPaths); | ||||
|  | ||||
| 	ui->spinPreviewPage->setValue(1); | ||||
| 	ui->spinPreviewPage->setMinimum(1); | ||||
| } | ||||
|  | ||||
| void MainWindow::addPathToIndex() | ||||
| @@ -82,16 +110,18 @@ void MainWindow::connectSignals() | ||||
| 					handleSearchError(e.message); | ||||
| 				} | ||||
| 			}); | ||||
| 	connect(&previewWorkerWatcher, &QFutureWatcher<QSharedPointer<PreviewResult>>::resultReadyAt, this, | ||||
| 			[&](int index) { previewReceived(previewWorkerWatcher.resultAt(index)); }); | ||||
| 	connect(&previewWorkerWatcher, &QFutureWatcher<QSharedPointer<PreviewResult>>::progressValueChanged, | ||||
| 			ui->previewProcessBar, &QProgressBar::setValue); | ||||
| 	connect(&previewWorkerWatcher, &QFutureWatcher<QSharedPointer<PreviewResult>>::started, this, | ||||
| 			[&] { ui->indexerTab->setEnabled(false); }); | ||||
|  | ||||
| 	connect(&previewWorkerWatcher, &QFutureWatcher<QSharedPointer<PreviewResult>>::finished, this, | ||||
| 	/* connect(&previewWorkerWatcher, &QFutureWatcher<QByteArray>::resultReadyAt, this, | ||||
| 			[&](int index) { | ||||
| 		previewReceived(previewWorkerWatcher.resultAt(index)); }); | ||||
| 			connect(&previewWorkerWatcher, &QFutureWatcher<QSharedPointer<PreviewResult>>::progressValueChanged, | ||||
| 					ui->previewProcessBar, &QProgressBar::setValue); | ||||
| 			connect(&previewWorkerWatcher, &QFutureWatcher<QSharedPointer<PreviewResult>>::started, this, | ||||
| 					[&] { ui->indexerTab->setEnabled(false); });*/ | ||||
|  | ||||
| 	/*connect(&previewWorkerWatcher, &QFutureWatcher<QSharedPointer<PreviewResult>>::finished, this, | ||||
| 			[&] { ui->indexerTab->setEnabled(true); }); | ||||
|  | ||||
| 	*/ | ||||
| 	connect(ui->treeResultsList, &QTreeWidget::itemActivated, this, &MainWindow::treeSearchItemActivated); | ||||
| 	connect(ui->treeResultsList, &QTreeWidget::customContextMenuRequested, this, | ||||
| 			&MainWindow::showSearchResultsContextMenu); | ||||
| @@ -261,16 +291,21 @@ void MainWindow::tabChanged() | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void MainWindow::previewReceived(QSharedPointer<PreviewResult> preview) | ||||
| void MainWindow::previewReceived(QSharedPointer<PreviewResult> preview, unsigned int previewGeneration) | ||||
| { | ||||
| 	if(preview->hasPreview()) | ||||
| 	if(previewGeneration < this->currentPreviewGeneration) | ||||
| 	{ | ||||
| 		return; | ||||
| 	} | ||||
| 	this->ui->previewProcessBar->setValue(this->ui->previewProcessBar->value() + 1); | ||||
| 	if(!preview.isNull() && preview->hasPreview()) | ||||
| 	{ | ||||
| 		QString docPath = preview->getDocumentPath(); | ||||
| 		auto previewPage = preview->getPage(); | ||||
|  | ||||
| 		ClickLabel *label = dynamic_cast<ClickLabel *>(preview->createPreviewWidget()); | ||||
| 		ui->scrollAreaWidgetContents->layout()->addWidget(label); | ||||
| 		connect(label, &ClickLabel::leftClick, [this, docPath, previewPage]() { ipcDocOpen(docPath, previewPage); }); | ||||
| 		connect(label, &ClickLabel::leftClick, [this, docPath, previewPage]() { openDocument(docPath, previewPage); }); | ||||
| 		connect(label, &ClickLabel::rightClick, | ||||
| 				[this, docPath, previewPage]() | ||||
| 				{ | ||||
| @@ -357,10 +392,8 @@ void MainWindow::handleSearchResults(const QVector<SearchResult> &results) | ||||
| 	ui->treeResultsList->resizeColumnToContents(1); | ||||
| 	previewDirty = !this->previewableSearchResults.empty(); | ||||
|  | ||||
| 	int numpages = ceil(static_cast<double>(this->previewableSearchResults.size()) / previewsPerPage); | ||||
| 	ui->spinPreviewPage->setMinimum(1); | ||||
| 	ui->spinPreviewPage->setMaximum(numpages); | ||||
| 	ui->spinPreviewPage->setValue(1); | ||||
|  | ||||
| 	if(previewTabActive() && previewDirty) | ||||
| 	{ | ||||
| 		makePreviews(1); | ||||
| @@ -376,12 +409,10 @@ void MainWindow::handleSearchResults(const QVector<SearchResult> &results) | ||||
|  | ||||
| void MainWindow::makePreviews(int page) | ||||
| { | ||||
|  | ||||
| 	this->previewWorkerWatcher.cancel(); | ||||
| 	this->previewWorkerWatcher.waitForFinished(); | ||||
|  | ||||
| 	QCoreApplication::processEvents(); // Maybe not necessary anymore, depends on whether it's possible that a slot is | ||||
| 									   // still to be fired. | ||||
| 	if(this->previewableSearchResults.empty()) | ||||
| 	{ | ||||
| 		return; | ||||
| 	} | ||||
| 	qDeleteAll(ui->scrollAreaWidgetContents->children()); | ||||
|  | ||||
| 	ui->scrollAreaWidgetContents->setLayout(new QHBoxLayout()); | ||||
| @@ -409,14 +440,43 @@ void MainWindow::makePreviews(int page) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	PreviewWorker worker; | ||||
| 	int end = previewsPerPage; | ||||
| 	int begin = page * previewsPerPage - previewsPerPage; | ||||
| 	this->previewWorkerWatcher.setFuture(worker.generatePreviews(this->previewableSearchResults.mid(begin, end), | ||||
| 																 wordsToHighlight, scaleText.toInt() / 100.)); | ||||
| 	ui->previewProcessBar->setMaximum(this->previewWorkerWatcher.progressMaximum()); | ||||
| 	ui->previewProcessBar->setMinimum(this->previewWorkerWatcher.progressMinimum()); | ||||
| 	if(begin < 0) | ||||
| 	{ | ||||
| 		// Should not happen actually | ||||
| 		begin = 0; | ||||
| 	} | ||||
|  | ||||
| 	RenderConfig renderConfig; | ||||
| 	renderConfig.scaleX = QGuiApplication::primaryScreen()->physicalDotsPerInchX() * (scaleText.toInt() / 100.); | ||||
| 	renderConfig.scaleY = QGuiApplication::primaryScreen()->physicalDotsPerInchY() * (scaleText.toInt() / 100.); | ||||
| 	renderConfig.wordsToHighlight = wordsToHighlight; | ||||
|  | ||||
| 	QVector<RenderTarget> targets; | ||||
| 	for(SearchResult &sr : this->previewableSearchResults) | ||||
| 	{ | ||||
| 		RenderTarget renderTarget; | ||||
| 		renderTarget.path = sr.fileData.absPath; | ||||
|  | ||||
| 		for(unsigned int pagenum : sr.pages) | ||||
| 		{ | ||||
| 			renderTarget.page = (int)pagenum; | ||||
| 			targets.append(renderTarget); | ||||
| 		} | ||||
| 	} | ||||
| 	int numpages = ceil(static_cast<double>(targets.size()) / previewsPerPage); | ||||
| 	ui->spinPreviewPage->setMaximum(numpages); | ||||
| 	targets = targets.mid(begin, end); | ||||
|  | ||||
| 	ui->lblTotalPreviewPagesCount->setText(QString::number(numpages)); | ||||
| 	ui->previewProcessBar->setMaximum(targets.count()); | ||||
| 	ui->previewProcessBar->setMinimum(0); | ||||
| 	ui->previewProcessBar->setValue(0); | ||||
| 	ui->previewProcessBar->setVisible(this->previewableSearchResults.size() > 0); | ||||
| 	++this->currentPreviewGeneration; | ||||
| 	this->ui->spinPreviewPage->setEnabled(false); | ||||
| 	emit startIpcPreviews(renderConfig, targets); | ||||
| } | ||||
|  | ||||
| void MainWindow::handleSearchError(QString error) | ||||
| @@ -430,27 +490,39 @@ void MainWindow::createSearchResutlMenu(QMenu &menu, const QFileInfo &fileInfo) | ||||
| 				   [&fileInfo] { QGuiApplication::clipboard()->setText(fileInfo.fileName()); }); | ||||
| 	menu.addAction("Copy full path to clipboard", | ||||
| 				   [&fileInfo] { QGuiApplication::clipboard()->setText(fileInfo.absoluteFilePath()); }); | ||||
| 	menu.addAction("Open containing folder", [this, &fileInfo] { this->ipcFileOpen(fileInfo.absolutePath()); }); | ||||
| 	menu.addAction("Open containing folder", [this, &fileInfo] { this->openFile(fileInfo.absolutePath()); }); | ||||
| } | ||||
|  | ||||
| void MainWindow::ipcDocOpen(QString path, int num) | ||||
| void MainWindow::openDocument(QString path, int num) | ||||
| { | ||||
| 	QStringList args; | ||||
| 	args << path; | ||||
| 	args << QString::number(num); | ||||
| 	this->ipcClient->sendCommand(DocOpen, args); | ||||
| 	QSettings settings; | ||||
| 	QString command = settings.value("pdfviewer").toString(); | ||||
| 	if(path.endsWith(".pdf") && command != "" && command.contains("%p") && command.contains("%f")) | ||||
| 	{ | ||||
| 		QStringList splitted = command.split(" "); | ||||
| 		if(splitted.size() > 1) | ||||
| 		{ | ||||
| 			QString cmd = splitted[0]; | ||||
| 			QStringList args = splitted.mid(1); | ||||
| 			args.replaceInStrings("%f", path); | ||||
| 			args.replaceInStrings("%p", QString::number(num)); | ||||
| 			QProcess::startDetached(cmd, args); | ||||
| 		} | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		QDesktopServices::openUrl(QUrl::fromLocalFile(path)); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void MainWindow::ipcFileOpen(QString path) | ||||
| void MainWindow::openFile(QString path) | ||||
| { | ||||
| 	QStringList args; | ||||
| 	args << path; | ||||
| 	this->ipcClient->sendCommand(FileOpen, args); | ||||
| 	QDesktopServices::openUrl(QUrl::fromLocalFile(path)); | ||||
| } | ||||
|  | ||||
| void MainWindow::treeSearchItemActivated(QTreeWidgetItem *item, int i) | ||||
| { | ||||
| 	ipcFileOpen(item->text(1)); | ||||
| 	openFile(item->text(1)); | ||||
| } | ||||
|  | ||||
| void MainWindow::showSearchResultsContextMenu(const QPoint &point) | ||||
|   | ||||
| @@ -9,9 +9,8 @@ | ||||
| #include <QFutureWatcher> | ||||
| #include <QSqlDatabase> | ||||
| #include <QLocalSocket> | ||||
| #include "previewworker.h" | ||||
| #include "../shared/looqsquery.h" | ||||
| #include "ipcclient.h" | ||||
| #include "ipcpreviewclient.h" | ||||
| #include "indexer.h" | ||||
| namespace Ui | ||||
| { | ||||
| @@ -23,7 +22,7 @@ class MainWindow : public QMainWindow | ||||
| 	Q_OBJECT | ||||
|  | ||||
|   public: | ||||
| 	explicit MainWindow(QWidget *parent, IPCClient &client); | ||||
| 	explicit MainWindow(QWidget *parent, QString socketPath); | ||||
| 	~MainWindow(); | ||||
|   signals: | ||||
| 	void beginSearch(const QString &query); | ||||
| @@ -33,13 +32,14 @@ class MainWindow : public QMainWindow | ||||
| 	DatabaseFactory *dbFactory; | ||||
| 	SqliteDbService *dbService; | ||||
| 	Ui::MainWindow *ui; | ||||
| 	IPCClient *ipcClient; | ||||
| 	IPCPreviewClient ipcPreviewClient; | ||||
| 	QThread ipcClientThread; | ||||
|  | ||||
| 	Indexer *indexer; | ||||
| 	QFileIconProvider iconProvider; | ||||
| 	bool previewDirty; | ||||
| 	QSqlDatabase db; | ||||
| 	QFutureWatcher<QVector<SearchResult>> searchWatcher; | ||||
| 	QFutureWatcher<QSharedPointer<PreviewResult>> previewWorkerWatcher; | ||||
| 	void add(QString path, unsigned int page); | ||||
| 	QVector<SearchResult> previewableSearchResults; | ||||
| 	void connectSignals(); | ||||
| @@ -53,20 +53,24 @@ class MainWindow : public QMainWindow | ||||
| 	LooqsQuery contentSearchQuery; | ||||
| 	int previewsPerPage; | ||||
| 	void createSearchResutlMenu(QMenu &menu, const QFileInfo &fileInfo); | ||||
| 	void ipcDocOpen(QString path, int num); | ||||
| 	void ipcFileOpen(QString path); | ||||
|  | ||||
| 	void openDocument(QString path, int num); | ||||
| 	void openFile(QString path); | ||||
| 	unsigned int currentPreviewGeneration = 1; | ||||
|   private slots: | ||||
| 	void lineEditReturnPressed(); | ||||
| 	void treeSearchItemActivated(QTreeWidgetItem *item, int i); | ||||
| 	void showSearchResultsContextMenu(const QPoint &point); | ||||
| 	void tabChanged(); | ||||
| 	void previewReceived(QSharedPointer<PreviewResult> preview); | ||||
| 	void previewReceived(QSharedPointer<PreviewResult> preview, unsigned int previewGeneration); | ||||
| 	void comboScaleChanged(int i); | ||||
| 	void spinPreviewPageValueChanged(int val); | ||||
| 	void startIndexing(); | ||||
| 	void finishIndexing(); | ||||
| 	void addPathToIndex(); | ||||
|  | ||||
|   signals: | ||||
| 	void startIpcPreviews(RenderConfig config, const QVector<RenderTarget> &targets); | ||||
| 	void stopIpcPreviews(); | ||||
| }; | ||||
|  | ||||
| #endif // MAINWINDOW_H | ||||
|   | ||||
| @@ -27,7 +27,7 @@ | ||||
|        <enum>QTabWidget::South</enum> | ||||
|       </property> | ||||
|       <property name="currentIndex"> | ||||
|        <number>2</number> | ||||
|        <number>1</number> | ||||
|       </property> | ||||
|       <widget class="QWidget" name="resultsTab"> | ||||
|        <attribute name="title"> | ||||
| @@ -155,6 +155,13 @@ | ||||
|             </property> | ||||
|            </widget> | ||||
|           </item> | ||||
|           <item> | ||||
|            <widget class="QLabel" name="lblTotalPreviewPagesCount"> | ||||
|             <property name="text"> | ||||
|              <string/> | ||||
|             </property> | ||||
|            </widget> | ||||
|           </item> | ||||
|           <item> | ||||
|            <spacer name="horizontalSpacer"> | ||||
|             <property name="orientation"> | ||||
| @@ -349,7 +356,7 @@ | ||||
|     <item> | ||||
|      <widget class="QProgressBar" name="previewProcessBar"> | ||||
|       <property name="value"> | ||||
|        <number>24</number> | ||||
|        <number>0</number> | ||||
|       </property> | ||||
|      </widget> | ||||
|     </item> | ||||
|   | ||||
| @@ -156,6 +156,9 @@ QString Common::databasePath() | ||||
|  | ||||
| QString Common::ipcSocketPath() | ||||
| { | ||||
| 	QSettings settings; | ||||
| 	return settings.value(SETTINGS_KEY_IPCSOCKETPATH, "/tmp/looqs-spawner").toString(); | ||||
| 	return "/tmp/.looqs/looqs-ipc-socket"; | ||||
|  | ||||
| 	/* May not a good idea to set it in the settings and probably nobody would ever bother to change it anyway */ | ||||
| 	// QSettings settings; | ||||
| 	// return settings.value(SETTINGS_KEY_IPCSOCKETPATH, "/tmp/.looqs/looqs-ipc-socket").toString(); | ||||
| } | ||||
|   | ||||
		Atsaukties uz šo jaunā problēmā
	
	Block a user