Commitok összehasonlítása
	
		
			19 Commit-ok
		
	
	
		
			v0.6
			...
			2ab6e40d44
		
	
	| Szerző | SHA1 | Dátum | |
|---|---|---|---|
| 2ab6e40d44 | |||
| 31f0568a87 | |||
| 238f9add49 | |||
| 7c63ee9178 | |||
| 1edfcc8f23 | |||
| 2df273dee3 | |||
| 5a47f5949f | |||
| e6a0c0daee | |||
| 11b070ed42 | |||
| 47874b3706 | |||
| 10d61acbd0 | |||
| eef0fae137 | |||
| d8205a0da4 | |||
| 877224b6e1 | |||
| 14730ed208 | |||
| fe610d3068 | |||
| 0c1b57d911 | |||
| 2885e40a3a | |||
| c0f4087937 | 
| @@ -33,11 +33,18 @@ int CommandSearch::handle(QStringList arguments) | ||||
|  | ||||
| 	try | ||||
| 	{ | ||||
|  | ||||
| 		QHash<QString, bool> seenMap; | ||||
| 		auto results = dbService->search(query); | ||||
|  | ||||
| 		for(SearchResult &result : results) | ||||
| 		for(const SearchResult &result : results) | ||||
| 		{ | ||||
| 			Logger::info() << result.fileData.absPath << Qt::endl; | ||||
| 			const QString &absPath = result.fileData.absPath; | ||||
| 			if(!seenMap.contains(absPath)) | ||||
| 			{ | ||||
| 				seenMap[absPath] = true; | ||||
| 				Logger::info() << absPath << Qt::endl; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	catch(LooqsGeneralException &e) | ||||
|   | ||||
| @@ -1,16 +1,50 @@ | ||||
| #include <QtConcurrent> | ||||
| #include "ipcpreviewworker.h" | ||||
| #include "previewgeneratormapfunctor.h" | ||||
| IPCPreviewWorker::IPCPreviewWorker() | ||||
| IPCPreviewWorker::IPCPreviewWorker(QLocalSocket *peer) | ||||
| { | ||||
| 	this->peer = peer; | ||||
| 	this->connect(&previewWorkerWatcher, &QFutureWatcher<QByteArray>::resultReadyAt, this, | ||||
| 				  [this](int index) { emit previewGenerated(previewWorkerWatcher.resultAt(index)); }); | ||||
| 	connect(&previewWorkerWatcher, &QFutureWatcher<QByteArray>::finished, this, [this] { emit finished(); }); | ||||
| 				  [this](int index) | ||||
| 				  { | ||||
| 					  if(this->peer != nullptr) | ||||
| 					  { | ||||
| 						  QDataStream stream{this->peer}; | ||||
| 						  stream << previewWorkerWatcher.resultAt(index); | ||||
| 						  this->peer->flush(); | ||||
| 					  } | ||||
| 				  }); | ||||
| 	connect(&previewWorkerWatcher, &QFutureWatcher<QByteArray>::finished, this, &IPCPreviewWorker::shutdownSocket); | ||||
| 	connect(this->peer, &QLocalSocket::disconnected, this, &IPCPreviewWorker::shutdownSocket); | ||||
| } | ||||
|  | ||||
| void IPCPreviewWorker::start(RenderConfig config, const QVector<RenderTarget> &targets, QLocalSocket *peer) | ||||
| void IPCPreviewWorker::shutdownSocket() | ||||
| { | ||||
| 	if(cleaned) | ||||
| 	{ | ||||
| 		return; | ||||
| 	} | ||||
| 	cleaned = true; | ||||
| 	if(this->peer != nullptr) | ||||
| 	{ | ||||
| 		if(this->peer->state() == QLocalSocket::ConnectedState) | ||||
| 		{ | ||||
| 			this->peer->flush(); | ||||
| 			this->peer->waitForBytesWritten(); | ||||
| 			this->peer->disconnectFromServer(); | ||||
| 			if(this->peer->state() != QLocalSocket::UnconnectedState) | ||||
| 			{ | ||||
| 				this->peer->waitForDisconnected(); | ||||
| 			} | ||||
| 		} | ||||
| 		delete this->peer; | ||||
| 		this->peer = nullptr; | ||||
| 	} | ||||
| 	emit finished(); | ||||
| } | ||||
|  | ||||
| void IPCPreviewWorker::start(RenderConfig config, const QVector<RenderTarget> &targets) | ||||
| { | ||||
| 	stop(); | ||||
| 	auto mapFunctor = PreviewGeneratorMapFunctor(); | ||||
| 	mapFunctor.setRenderConfig(config); | ||||
|  | ||||
|   | ||||
| @@ -11,13 +11,21 @@ class IPCPreviewWorker : public QObject | ||||
| 	Q_OBJECT | ||||
|   private: | ||||
| 	QFutureWatcher<QByteArray> previewWorkerWatcher; | ||||
| 	QLocalSocket *peer; | ||||
| 	bool cleaned = false; | ||||
|  | ||||
|   public: | ||||
| 	IPCPreviewWorker(); | ||||
| 	void start(RenderConfig config, const QVector<RenderTarget> &targets, QLocalSocket *peer); | ||||
| 	IPCPreviewWorker(QLocalSocket *peer); | ||||
| 	void start(RenderConfig config, const QVector<RenderTarget> &targets); | ||||
| 	void stop(); | ||||
| 	~IPCPreviewWorker() | ||||
| 	{ | ||||
| 		delete this->peer; | ||||
| 	} | ||||
|   private slots: | ||||
| 	void shutdownSocket(); | ||||
|  | ||||
|   signals: | ||||
| 	void previewGenerated(QByteArray); | ||||
| 	void finished(); | ||||
| }; | ||||
|  | ||||
|   | ||||
| @@ -18,8 +18,6 @@ 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) | ||||
| @@ -31,8 +29,6 @@ bool IpcServer::startSpawner(QString socketPath) | ||||
| void IpcServer::spawnerNewConnection() | ||||
| { | ||||
| 	QLocalSocket *socket = this->spawningServer.nextPendingConnection(); | ||||
| 	connect(socket, &QLocalSocket::disconnected, socket, &QLocalSocket::deleteLater); | ||||
| 	this->currentSocket = socket; | ||||
| 	if(socket != nullptr) | ||||
| 	{ | ||||
| 		if(!socket->waitForReadyRead()) | ||||
| @@ -53,21 +49,22 @@ void IpcServer::spawnerNewConnection() | ||||
| 				stream.startTransaction(); | ||||
| 				stream >> renderConfig >> targets; | ||||
| 			} while(!stream.commitTransaction() && socket->state() == QLocalSocket::ConnectedState); | ||||
|  | ||||
| 			if(socket->state() == QLocalSocket::ConnectedState) | ||||
| 			{ | ||||
| 				stream << targets.count(); | ||||
| 				socket->flush(); | ||||
| 			previewWorker.start(renderConfig, targets, socket); | ||||
| 				IPCPreviewWorker *previewWorker = new IPCPreviewWorker(socket); | ||||
| 				connect(previewWorker, &IPCPreviewWorker::finished, this, [previewWorker] { delete previewWorker; }); | ||||
| 				previewWorker->start(renderConfig, targets); | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				delete socket; | ||||
| 			} | ||||
| 		} | ||||
| 		if(command == StopGeneratePreviews) | ||||
| 		{ | ||||
| 			previewWorker.stop(); | ||||
| 			/* TODO: implement */ | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void IpcServer::handlePreviewGenerated(QByteArray ba) | ||||
| { | ||||
| 	QDataStream stream{this->currentSocket}; | ||||
| 	stream << ba; | ||||
| 	this->currentSocket->flush(); | ||||
| } | ||||
|   | ||||
| @@ -10,13 +10,10 @@ 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(); | ||||
|   | ||||
| @@ -15,6 +15,7 @@ | ||||
| #include <QFileDialog> | ||||
| #include <QScreen> | ||||
| #include <QProgressDialog> | ||||
| #include <QDesktopWidget> | ||||
| #include "mainwindow.h" | ||||
| #include "ui_mainwindow.h" | ||||
| #include "clicklabel.h" | ||||
| @@ -60,6 +61,10 @@ MainWindow::MainWindow(QWidget *parent, QString socketPath) | ||||
| 	QString ignorePatterns = settings.value("ignorePatterns").toString(); | ||||
| 	ui->txtIgnorePatterns->setText(ignorePatterns); | ||||
|  | ||||
| 	QStringList searchHistoryList = settings.value(SETTINGS_KEY_SEARCHHISTORY).toStringList(); | ||||
| 	this->searchHistory = searchHistoryList.toVector(); | ||||
| 	this->currentSearchHistoryIndex = this->searchHistory.size(); | ||||
|  | ||||
| 	ui->spinPreviewPage->setValue(1); | ||||
| 	ui->spinPreviewPage->setMinimum(1); | ||||
|  | ||||
| @@ -70,6 +75,9 @@ MainWindow::MainWindow(QWidget *parent, QString socketPath) | ||||
| 	policy.setRetainSizeWhenHidden(true); | ||||
| 	ui->btnOpenFailed->setSizePolicy(policy); | ||||
|  | ||||
| 	ui->txtSearch->installEventFilter(this); | ||||
| 	ui->scrollArea->viewport()->installEventFilter(this); | ||||
|  | ||||
| 	this->ipcClientThread.start(); | ||||
| } | ||||
|  | ||||
| @@ -191,7 +199,14 @@ void MainWindow::connectSignals() | ||||
| 	connect(ui->btnSaveSettings, &QPushButton::clicked, this, &MainWindow::saveSettings); | ||||
| 	connect(ui->btnOpenFailed, &QPushButton::clicked, this, &MainWindow::exportFailedPaths); | ||||
| 	connect( | ||||
| 		ui->comboPreviewFiles, qOverload<int>(&QComboBox::currentIndexChanged), this, [&]() { makePreviews(1); }, | ||||
| 		ui->comboPreviewFiles, qOverload<int>(&QComboBox::currentIndexChanged), this, | ||||
| 		[&]() | ||||
| 		{ | ||||
| 			if(this->previewTabActive()) | ||||
| 			{ | ||||
| 				makePreviews(1); | ||||
| 			} | ||||
| 		}, | ||||
| 		Qt::QueuedConnection); | ||||
| 	connect(&ipcPreviewClient, &IPCPreviewClient::previewReceived, this, &MainWindow::previewReceived, | ||||
| 			Qt::QueuedConnection); | ||||
| @@ -201,6 +216,7 @@ void MainWindow::connectSignals() | ||||
| 				this->ui->previewProcessBar->setValue(this->ui->previewProcessBar->maximum()); | ||||
| 				this->ui->spinPreviewPage->setEnabled(true); | ||||
| 				this->ui->comboPreviewFiles->setEnabled(true); | ||||
| 				ui->txtSearch->setEnabled(true); | ||||
| 			}); | ||||
| 	connect(&ipcPreviewClient, &IPCPreviewClient::error, this, | ||||
| 			[this](QString msg) | ||||
| @@ -437,6 +453,86 @@ void MainWindow::processShortcut(int key) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| bool MainWindow::eventFilter(QObject *object, QEvent *event) | ||||
| { | ||||
| 	if(object == ui->txtSearch && !searchHistory.empty()) | ||||
| 	{ | ||||
| 		if(event->type() == QEvent::KeyPress) | ||||
| 		{ | ||||
| 			QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event); | ||||
| 			if(keyEvent->key() == Qt::Key_Up) | ||||
| 			{ | ||||
| 				if(this->currentSavedSearchText.isEmpty()) | ||||
| 				{ | ||||
| 					this->currentSavedSearchText = ui->txtSearch->text(); | ||||
| 				} | ||||
| 				if(this->currentSearchHistoryIndex <= 0) | ||||
| 				{ | ||||
| 					return true; | ||||
| 				} | ||||
| 				--this->currentSearchHistoryIndex; | ||||
| 				QString text = this->searchHistory.at(this->currentSearchHistoryIndex); | ||||
| 				ui->txtSearch->setText(text); | ||||
| 				return true; | ||||
| 			} | ||||
| 			else if(keyEvent->key() == Qt::Key_Down) | ||||
| 			{ | ||||
| 				if(this->currentSearchHistoryIndex == searchHistory.size() - 1) | ||||
| 				{ | ||||
| 					if(!this->currentSavedSearchText.isEmpty()) | ||||
| 					{ | ||||
| 						ui->txtSearch->setText(this->currentSavedSearchText); | ||||
| 						this->currentSavedSearchText.clear(); | ||||
| 						++this->currentSearchHistoryIndex; | ||||
| 					} | ||||
| 					return true; | ||||
| 				} | ||||
| 				if(this->currentSearchHistoryIndex < searchHistory.size() - 1) | ||||
| 				{ | ||||
| 					++this->currentSearchHistoryIndex; | ||||
| 					QString text = this->searchHistory.at(this->currentSearchHistoryIndex); | ||||
| 					ui->txtSearch->setText(text); | ||||
| 				} | ||||
| 				return true; | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				this->currentSavedSearchText.clear(); | ||||
| 				/* Off by one on purpose so Key_Up decrements it again and lands at | ||||
| 				 * the last entry */ | ||||
| 				this->currentSearchHistoryIndex = this->searchHistory.size(); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	if(object == ui->scrollArea->viewport()) | ||||
| 	{ | ||||
| 		if(event->type() == QEvent::Wheel) | ||||
| 		{ | ||||
| 			QWheelEvent *wheelEvent = static_cast<QWheelEvent *>(event); | ||||
| 			if(wheelEvent->modifiers() & Qt::ControlModifier) | ||||
| 			{ | ||||
| 				if(wheelEvent->angleDelta().y() > 0) | ||||
| 				{ | ||||
| 					if(ui->comboScale->currentIndex() < ui->comboScale->count() - 1) | ||||
| 					{ | ||||
| 						ui->comboScale->setCurrentIndex(ui->comboScale->currentIndex() + 1); | ||||
| 					} | ||||
| 				} | ||||
|  | ||||
| 				else | ||||
| 				{ | ||||
| 					if(ui->comboScale->currentIndex() > 0) | ||||
| 					{ | ||||
| 						ui->comboScale->setCurrentIndex(ui->comboScale->currentIndex() - 1); | ||||
| 					} | ||||
| 				} | ||||
| 				return true; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	return QMainWindow::eventFilter(object, event); | ||||
| } | ||||
|  | ||||
| void MainWindow::keyPressEvent(QKeyEvent *event) | ||||
| { | ||||
| 	bool quit = | ||||
| @@ -498,6 +594,9 @@ void MainWindow::initSettingsTabs() | ||||
| 	ui->txtSettingMountPaths->setText(mountPaths); | ||||
| 	ui->spinSettingNumerPerPages->setValue(numPagesPerPreview); | ||||
| 	ui->txtSettingDatabasePath->setText(databasePath); | ||||
| 	bool horizontalScroll = settings.value(SETTINGS_KEY_PREVIEWS_SCROLL_HORIZONTALLY).toBool(); | ||||
| 	ui->radioScrollHorizontally->setChecked(horizontalScroll); | ||||
| 	ui->radioScrollVertically->setChecked(!horizontalScroll); | ||||
| } | ||||
|  | ||||
| void MainWindow::saveSettings() | ||||
| @@ -525,6 +624,7 @@ void MainWindow::saveSettings() | ||||
| 	settings.setValue(SETTINGS_KEY_MOUNTPATHS, mountPaths); | ||||
| 	settings.setValue(SETTINGS_KEY_PREVIEWSPERPAGE, ui->spinSettingNumerPerPages->value()); | ||||
| 	settings.setValue(SETTINGS_KEY_DBPATH, databasePath); | ||||
| 	settings.setValue(SETTINGS_KEY_PREVIEWS_SCROLL_HORIZONTALLY, ui->radioScrollHorizontally->isChecked()); | ||||
|  | ||||
| 	settings.sync(); | ||||
|  | ||||
| @@ -548,6 +648,7 @@ void MainWindow::previewReceived(QSharedPointer<PreviewResult> preview, unsigned | ||||
| 		headerLabel->setText(QString("Path: ") + preview->getDocumentPath()); | ||||
|  | ||||
| 		ClickLabel *label = dynamic_cast<ClickLabel *>(preview->createPreviewWidget()); | ||||
| 		label->setMaximumWidth(QApplication::desktop()->availableGeometry().width() - 200); | ||||
|  | ||||
| 		QVBoxLayout *previewLayout = new QVBoxLayout(); | ||||
|  | ||||
| @@ -579,6 +680,7 @@ void MainWindow::previewReceived(QSharedPointer<PreviewResult> preview, unsigned | ||||
| 		previewLayout->setMargin(0); | ||||
| 		previewLayout->insertStretch(0, 1); | ||||
| 		previewLayout->insertStretch(-1, 1); | ||||
| 		previewLayout->setAlignment(Qt::AlignCenter); | ||||
| 		QWidget *previewWidget = new QWidget(); | ||||
|  | ||||
| 		previewWidget->setLayout(previewLayout); | ||||
| @@ -600,6 +702,14 @@ void MainWindow::lineEditReturnPressed() | ||||
| 		ui->tabWidget->setCurrentIndex(0); | ||||
| 	} | ||||
| 	// TODO: validate q; | ||||
| 	while(this->searchHistory.size() > 30) | ||||
| 	{ | ||||
| 		this->searchHistory.removeFirst(); | ||||
| 	} | ||||
| 	this->searchHistory.append(q); | ||||
| 	this->currentSearchHistoryIndex = this->searchHistory.size(); | ||||
| 	this->currentSavedSearchText.clear(); | ||||
|  | ||||
| 	ui->treeResultsList->clear(); | ||||
| 	ui->lblSearchResults->setText("Searching..."); | ||||
| 	this->ui->txtSearch->setEnabled(false); | ||||
| @@ -699,43 +809,52 @@ void MainWindow::handleSearchResults(const QVector<SearchResult> &results) | ||||
| 	ui->comboPreviewFiles->clear(); | ||||
| 	ui->comboPreviewFiles->addItem("All previews"); | ||||
| 	ui->comboPreviewFiles->setVisible(true); | ||||
| 	ui->lblTotalPreviewPagesCount->setText(""); | ||||
|  | ||||
| 	bool hasDeleted = false; | ||||
| 	QHash<QString, bool> seenMap; | ||||
| 	for(const SearchResult &result : results) | ||||
| 	{ | ||||
| 		QFileInfo pathInfo(result.fileData.absPath); | ||||
| 		const QString &absPath = result.fileData.absPath; | ||||
| 		QFileInfo pathInfo(absPath); | ||||
|  | ||||
| 		if(!seenMap.contains(absPath)) | ||||
| 		{ | ||||
| 			QString fileName = pathInfo.fileName(); | ||||
| 			QTreeWidgetItem *item = new QTreeWidgetItem(ui->treeResultsList); | ||||
|  | ||||
| 			QDateTime dt = QDateTime::fromSecsSinceEpoch(result.fileData.mtime); | ||||
| 			item->setIcon(0, iconProvider.icon(pathInfo)); | ||||
| 			item->setText(0, fileName); | ||||
| 		item->setText(1, result.fileData.absPath); | ||||
| 			item->setText(1, absPath); | ||||
| 			item->setText(2, dt.toString(Qt::ISODate)); | ||||
| 			item->setText(3, this->locale().formattedDataSize(result.fileData.size)); | ||||
| 		} | ||||
| 		bool exists = pathInfo.exists(); | ||||
| 		if(exists) | ||||
| 		{ | ||||
| 			if(!result.wasContentSearch) | ||||
| 			if(result.wasContentSearch) | ||||
| 			{ | ||||
| 				continue; | ||||
| 			} | ||||
|  | ||||
| 				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; | ||||
| 		} | ||||
| 		seenMap[absPath] = true; | ||||
| 	} | ||||
|  | ||||
| 	ui->treeResultsList->resizeColumnToContents(0); | ||||
| 	ui->treeResultsList->resizeColumnToContents(1); | ||||
| 	ui->treeResultsList->resizeColumnToContents(2); | ||||
| @@ -749,6 +868,7 @@ void MainWindow::handleSearchResults(const QVector<SearchResult> &results) | ||||
| 	} | ||||
|  | ||||
| 	QString statusText = "Results: " + QString::number(results.size()) + " files"; | ||||
| 	statusText += ", previewable: " + QString::number(this->previewableSearchResults.count()); | ||||
| 	if(hasDeleted) | ||||
| 	{ | ||||
| 		statusText += " WARNING: Some files are inaccessible. No preview available for those. Index may be out of sync"; | ||||
| @@ -771,7 +891,17 @@ void MainWindow::makePreviews(int page) | ||||
| 	} | ||||
| 	qDeleteAll(ui->scrollAreaWidgetContents->children()); | ||||
|  | ||||
| 	QSettings settings; | ||||
| 	bool horizontalScroll = settings.value(SETTINGS_KEY_PREVIEWS_SCROLL_HORIZONTALLY, false).toBool(); | ||||
| 	if(horizontalScroll) | ||||
| 	{ | ||||
| 		ui->scrollAreaWidgetContents->setLayout(new QHBoxLayout()); | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		ui->scrollAreaWidgetContents->setLayout(new QVBoxLayout()); | ||||
| 		ui->scrollAreaWidgetContents->layout()->setAlignment(Qt::AlignCenter); | ||||
| 	} | ||||
| 	ui->previewProcessBar->setMaximum(this->previewableSearchResults.size()); | ||||
| 	processedPdfPreviews = 0; | ||||
|  | ||||
| @@ -820,13 +950,9 @@ void MainWindow::makePreviews(int page) | ||||
| 		} | ||||
| 		RenderTarget renderTarget; | ||||
| 		renderTarget.path = sr.fileData.absPath; | ||||
|  | ||||
| 		for(unsigned int pagenum : sr.pages) | ||||
| 		{ | ||||
| 			renderTarget.page = (int)pagenum; | ||||
| 		renderTarget.page = (int)sr.page; | ||||
| 		targets.append(renderTarget); | ||||
| 	} | ||||
| 	} | ||||
| 	int numpages = ceil(static_cast<double>(targets.size()) / previewsPerPage); | ||||
| 	ui->spinPreviewPage->setMaximum(numpages); | ||||
| 	targets = targets.mid(begin, end); | ||||
| @@ -839,6 +965,7 @@ void MainWindow::makePreviews(int page) | ||||
| 	++this->currentPreviewGeneration; | ||||
| 	this->ui->spinPreviewPage->setEnabled(false); | ||||
| 	this->ui->comboPreviewFiles->setEnabled(false); | ||||
| 	this->ui->txtSearch->setEnabled(false); | ||||
| 	emit startIpcPreviews(renderConfig, targets); | ||||
| } | ||||
|  | ||||
| @@ -925,3 +1052,11 @@ MainWindow::~MainWindow() | ||||
| 	delete this->indexer; | ||||
| 	delete ui; | ||||
| } | ||||
|  | ||||
| void MainWindow::closeEvent(QCloseEvent *event) | ||||
| { | ||||
| 	QStringList list = this->searchHistory.toList(); | ||||
| 	QSettings settings; | ||||
| 	settings.setValue(SETTINGS_KEY_SEARCHHISTORY, list); | ||||
| 	settings.sync(); | ||||
| } | ||||
|   | ||||
| @@ -30,6 +30,9 @@ class MainWindow : public QMainWindow | ||||
| 	void beginSearch(const QString &query); | ||||
| 	void startPdfPreviewGeneration(QVector<SearchResult> paths, double scalefactor); | ||||
|  | ||||
|   protected: | ||||
| 	void closeEvent(QCloseEvent *event) override; | ||||
|  | ||||
|   private: | ||||
| 	DatabaseFactory *dbFactory; | ||||
| 	SqliteDbService *dbService; | ||||
| @@ -64,7 +67,11 @@ class MainWindow : public QMainWindow | ||||
| 	void initSettingsTabs(); | ||||
| 	int currentSelectedScale(); | ||||
| 	void processShortcut(int key); | ||||
| private slots: | ||||
| 	bool eventFilter(QObject *object, QEvent *event); | ||||
| 	QVector<QString> searchHistory; | ||||
| 	int currentSearchHistoryIndex = 0; | ||||
| 	QString currentSavedSearchText; | ||||
|   private slots: | ||||
| 	void lineEditReturnPressed(); | ||||
| 	void treeSearchItemActivated(QTreeWidgetItem *item, int i); | ||||
| 	void showSearchResultsContextMenu(const QPoint &point); | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
|     <x>0</x> | ||||
|     <y>0</y> | ||||
|     <width>1280</width> | ||||
|     <height>855</height> | ||||
|     <height>923</height> | ||||
|    </rect> | ||||
|   </property> | ||||
|   <property name="windowTitle"> | ||||
| @@ -82,7 +82,7 @@ | ||||
|              <x>0</x> | ||||
|              <y>0</y> | ||||
|              <width>1244</width> | ||||
|              <height>565</height> | ||||
|              <height>633</height> | ||||
|             </rect> | ||||
|            </property> | ||||
|            <layout class="QHBoxLayout" name="horizontalLayout"/> | ||||
| @@ -165,7 +165,7 @@ | ||||
|           <item> | ||||
|            <widget class="QComboBox" name="comboPreviewFiles"> | ||||
|             <property name="sizePolicy"> | ||||
|              <sizepolicy hsizetype="Expanding" vsizetype="Fixed"> | ||||
|              <sizepolicy hsizetype="Maximum" vsizetype="Fixed"> | ||||
|               <horstretch>0</horstretch> | ||||
|               <verstretch>0</verstretch> | ||||
|              </sizepolicy> | ||||
| @@ -542,6 +542,19 @@ | ||||
|           </layout> | ||||
|          </widget> | ||||
|         </item> | ||||
|         <item> | ||||
|          <spacer name="verticalSpacer"> | ||||
|           <property name="orientation"> | ||||
|            <enum>Qt::Vertical</enum> | ||||
|           </property> | ||||
|           <property name="sizeHint" stdset="0"> | ||||
|            <size> | ||||
|             <width>20</width> | ||||
|             <height>40</height> | ||||
|            </size> | ||||
|           </property> | ||||
|          </spacer> | ||||
|         </item> | ||||
|         <item> | ||||
|          <widget class="QGroupBox" name="Misc"> | ||||
|           <property name="title"> | ||||
| @@ -551,7 +564,7 @@ | ||||
|            <item> | ||||
|             <layout class="QHBoxLayout" name="horizontalLayout_9"> | ||||
|              <item> | ||||
|               <widget class="QLabel" name="label_4"> | ||||
|               <widget class="QLabel" name="lblMaxNumbersPreviewPages"> | ||||
|                <property name="text"> | ||||
|                 <string>Max number of previews per 'page' in 'Previews' tab: </string> | ||||
|                </property> | ||||
| @@ -575,22 +588,47 @@ | ||||
|              </item> | ||||
|             </layout> | ||||
|            </item> | ||||
|           </layout> | ||||
|            <item> | ||||
|             <layout class="QHBoxLayout" name="horizontalLayout_8"> | ||||
|              <item> | ||||
|               <widget class="QLabel" name="lblScrollModeForPreviews"> | ||||
|                <property name="text"> | ||||
|                 <string>Scroll mode for previews:</string> | ||||
|                </property> | ||||
|               </widget> | ||||
|              </item> | ||||
|              <item> | ||||
|          <spacer name="verticalSpacer"> | ||||
|               <widget class="QRadioButton" name="radioScrollVertically"> | ||||
|                <property name="text"> | ||||
|                 <string>Vertically</string> | ||||
|                </property> | ||||
|               </widget> | ||||
|              </item> | ||||
|              <item> | ||||
|               <widget class="QRadioButton" name="radioScrollHorizontally"> | ||||
|                <property name="text"> | ||||
|                 <string>Horizontally</string> | ||||
|                </property> | ||||
|               </widget> | ||||
|              </item> | ||||
|              <item> | ||||
|               <spacer name="horizontalSpacer_6"> | ||||
|                <property name="orientation"> | ||||
|            <enum>Qt::Vertical</enum> | ||||
|                 <enum>Qt::Horizontal</enum> | ||||
|                </property> | ||||
|                <property name="sizeHint" stdset="0"> | ||||
|                 <size> | ||||
|             <width>20</width> | ||||
|             <height>40</height> | ||||
|                  <width>40</width> | ||||
|                  <height>20</height> | ||||
|                 </size> | ||||
|                </property> | ||||
|               </spacer> | ||||
|              </item> | ||||
|             </layout> | ||||
|            </item> | ||||
|           </layout> | ||||
|          </widget> | ||||
|         </item> | ||||
|         <item> | ||||
|          <widget class="QPushButton" name="btnSaveSettings"> | ||||
|           <property name="text"> | ||||
|   | ||||
| @@ -1,5 +1,6 @@ | ||||
| #include <QMutexLocker> | ||||
| #include <QPainter> | ||||
| #include <QRegularExpression> | ||||
| #include "previewgeneratorpdf.h" | ||||
|  | ||||
| static QMutex cacheMutex; | ||||
| @@ -17,6 +18,7 @@ Poppler::Document *PreviewGeneratorPdf::document(QString path) | ||||
| 		return nullptr; | ||||
| 	} | ||||
| 	result->setRenderHint(Poppler::Document::TextAntialiasing); | ||||
|  | ||||
| 	QMutexLocker locker(&cacheMutex); | ||||
| 	documentcache.insert(path, result); | ||||
| 	locker.unlock(); | ||||
| @@ -45,7 +47,12 @@ QSharedPointer<PreviewResult> PreviewGeneratorPdf::generate(RenderConfig config, | ||||
| 	QImage img = pdfPage->renderToImage(config.scaleX, config.scaleY); | ||||
| 	for(QString &word : config.wordsToHighlight) | ||||
| 	{ | ||||
| 		QList<QRectF> rects = pdfPage->search(word, Poppler::Page::SearchFlag::IgnoreCase); | ||||
| 		QList<QRectF> rects = | ||||
| 			pdfPage->search(word, Poppler::Page::SearchFlag::IgnoreCase | Poppler::Page::SearchFlag::WholeWords); | ||||
| 		if(rects.empty()) | ||||
| 		{ | ||||
| 			rects = pdfPage->search(word, Poppler::Page::SearchFlag::IgnoreCase); | ||||
| 		} | ||||
| 		for(QRectF &rect : rects) | ||||
| 		{ | ||||
| 			QPainter painter(&img); | ||||
|   | ||||
| @@ -1,4 +1,5 @@ | ||||
| #include <QTextStream> | ||||
| #include <QRegularExpression> | ||||
|  | ||||
| #include "previewgeneratorplaintext.h" | ||||
| #include "previewresultplaintext.h" | ||||
| @@ -57,6 +58,7 @@ QString PreviewGeneratorPlainText::generatePreviewText(QString content, RenderCo | ||||
| 		++i; | ||||
| 	} | ||||
|  | ||||
| 	resulText = resulText.toHtmlEscaped(); | ||||
| 	QString header = "<b>" + fileName + "</b> "; | ||||
| 	for(QString &word : config.wordsToHighlight) | ||||
| 	{ | ||||
| @@ -74,10 +76,19 @@ QString PreviewGeneratorPlainText::generatePreviewText(QString content, RenderCo | ||||
| 	return header + resulText.replace("\n", "<br>").mid(0, 1000); | ||||
| } | ||||
|  | ||||
| struct Snippet | ||||
| { | ||||
| 	/* Contains each line number and line of the snippet*/ | ||||
| 	QString snippetText; | ||||
|  | ||||
| 	/* How many times a word occurs in the snippetText */ | ||||
| 	QHash<QString, int> wordCountMap; | ||||
| }; | ||||
|  | ||||
| QString PreviewGeneratorPlainText::generateLineBasedPreviewText(QTextStream &in, RenderConfig config, QString fileName) | ||||
| { | ||||
| 	QString resultText; | ||||
| 	const unsigned int contextLinesCount = 2; | ||||
| 	QVector<Snippet> snippets; | ||||
| 	const int contextLinesCount = 2; | ||||
| 	LimitQueue<QString> queue(contextLinesCount); | ||||
| 	QString currentLine; | ||||
| 	currentLine.reserve(512); | ||||
| @@ -85,38 +96,73 @@ QString PreviewGeneratorPlainText::generateLineBasedPreviewText(QTextStream &in, | ||||
| 	/* How many lines to read after a line with a match (like grep -A ) */ | ||||
| 	int justReadLinesCount = -1; | ||||
|  | ||||
| 	auto appendLine = [&resultText](int lineNumber, QString &line) | ||||
| 	{ resultText.append(QString("<b>%1</b>%2<br>").arg(lineNumber).arg(line)); }; | ||||
| 	struct Snippet currentSnippet; | ||||
|  | ||||
| 	QHash<QString, int> countmap; | ||||
| 	QString header = "<b>" + fileName + "</b> "; | ||||
|  | ||||
| 	unsigned int snippetsCount = 0; | ||||
| 	unsigned int lineCount = 0; | ||||
| 	while(in.readLineInto(¤tLine) && snippetsCount < MAX_SNIPPETS) | ||||
| 	auto appendLine = [¤tSnippet, &config](int lineNumber, QString &line) | ||||
| 	{ | ||||
| 		int foundWordsCount = 0; | ||||
| 		for(QString &word : config.wordsToHighlight) | ||||
| 		{ | ||||
| 			QRegularExpression searchRegex("\\b" + word + "\\b"); | ||||
| 			bool containsRegex = line.contains(searchRegex); | ||||
| 			bool contains = false; | ||||
| 			if(!containsRegex) | ||||
| 			{ | ||||
| 				contains = line.contains(word, Qt::CaseInsensitive); | ||||
| 			} | ||||
| 			if(containsRegex || contains) | ||||
| 			{ | ||||
| 				currentSnippet.wordCountMap[word] = currentSnippet.wordCountMap.value(word, 0) + 1; | ||||
| 				QString replacementString = "<span style=\"background-color: yellow;\">" + word + "</span>"; | ||||
| 				if(containsRegex) | ||||
| 				{ | ||||
| 					line.replace(searchRegex, replacementString); | ||||
| 				} | ||||
| 				else | ||||
| 				{ | ||||
| 					line.replace(word, replacementString, Qt::CaseInsensitive); | ||||
| 				} | ||||
| 				++foundWordsCount; | ||||
| 			} | ||||
| 		} | ||||
| 		currentSnippet.snippetText.append(QString("<b>%1</b>%2<br>").arg(lineNumber).arg(line)); | ||||
| 		return foundWordsCount; | ||||
| 	}; | ||||
|  | ||||
| 	unsigned int lineCount = 0; | ||||
| 	while(in.readLineInto(¤tLine)) | ||||
| 	{ | ||||
| 		currentLine = currentLine.toHtmlEscaped(); | ||||
| 		++lineCount; | ||||
| 		bool matched = false; | ||||
| 		if(justReadLinesCount > 0) | ||||
| 		{ | ||||
| 			appendLine(lineCount, currentLine); | ||||
|  | ||||
| 			int result = appendLine(lineCount, currentLine); | ||||
| 			if(justReadLinesCount == 1 && result > 0) | ||||
| 			{ | ||||
| 				justReadLinesCount = contextLinesCount; | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				--justReadLinesCount; | ||||
| 			} | ||||
|  | ||||
| 			continue; | ||||
| 		} | ||||
| 		if(justReadLinesCount == 0) | ||||
| 		{ | ||||
| 			resultText += "---<br>"; | ||||
| 			currentSnippet.snippetText += "---<br>"; | ||||
| 			justReadLinesCount = -1; | ||||
| 			++snippetsCount; | ||||
| 			snippets.append(currentSnippet); | ||||
| 			currentSnippet = {}; | ||||
| 		} | ||||
| 		for(QString &word : config.wordsToHighlight) | ||||
| 		{ | ||||
| 			if(currentLine.contains(word, Qt::CaseInsensitive)) | ||||
| 			{ | ||||
| 				countmap[word] = countmap.value(word, 0) + 1; | ||||
| 				matched = true; | ||||
| 				currentLine.replace(word, "<span style=\"background-color: yellow;\">" + word + "</span>", | ||||
| 									Qt::CaseInsensitive); | ||||
| 				break; | ||||
| 			} | ||||
| 		} | ||||
| 		if(matched) | ||||
| @@ -125,7 +171,6 @@ QString PreviewGeneratorPlainText::generateLineBasedPreviewText(QTextStream &in, | ||||
| 			{ | ||||
| 				int queuedLineCount = lineCount - queue.size(); | ||||
| 				QString queuedLine = queue.dequeue(); | ||||
|  | ||||
| 				appendLine(queuedLineCount, queuedLine); | ||||
| 			} | ||||
| 			appendLine(lineCount, currentLine); | ||||
| @@ -137,13 +182,77 @@ QString PreviewGeneratorPlainText::generateLineBasedPreviewText(QTextStream &in, | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if(!currentSnippet.snippetText.isEmpty()) | ||||
| 	{ | ||||
| 		currentSnippet.snippetText += "---<br>"; | ||||
| 		snippets.append(currentSnippet); | ||||
| 	} | ||||
|  | ||||
| 	std::sort(snippets.begin(), snippets.end(), | ||||
| 			  [](Snippet &a, Snippet &b) | ||||
| 			  { | ||||
| 				  int differentWordsA = 0; | ||||
| 				  int totalWordsA = 0; | ||||
| 				  int differentWordsB = 0; | ||||
| 				  int totalWordsB = 0; | ||||
| 				  for(int count : a.wordCountMap.values()) | ||||
| 				  { | ||||
| 					  if(count > 0) | ||||
| 					  { | ||||
| 						  ++differentWordsA; | ||||
| 					  } | ||||
| 					  totalWordsA += count; | ||||
| 				  } | ||||
| 				  for(int count : b.wordCountMap.values()) | ||||
| 				  { | ||||
| 					  if(count > 0) | ||||
| 					  { | ||||
| 						  ++differentWordsB; | ||||
| 					  } | ||||
| 					  totalWordsB += count; | ||||
| 				  } | ||||
|  | ||||
| 				  if(differentWordsA > differentWordsB) | ||||
| 				  { | ||||
| 					  return true; | ||||
| 				  } | ||||
| 				  if(differentWordsA == differentWordsB) | ||||
| 				  { | ||||
| 					  return totalWordsA > totalWordsB; | ||||
| 				  } | ||||
| 				  return false; | ||||
| 			  }); | ||||
|  | ||||
| 	QString resultText = ""; | ||||
|  | ||||
| 	unsigned int snippetsCount = 0; | ||||
|  | ||||
| 	QString header = "<b>" + fileName + "</b> "; | ||||
|  | ||||
| 	QHash<QString, int> totalWordCountMap; | ||||
| 	bool isTruncated = false; | ||||
| 	for(Snippet &snippet : snippets) | ||||
| 	{ | ||||
| 		if(snippetsCount++ < MAX_SNIPPETS) | ||||
| 		{ | ||||
| 			resultText += snippet.snippetText; | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			isTruncated = true; | ||||
| 		} | ||||
| 		for(auto it = snippet.wordCountMap.keyValueBegin(); it != snippet.wordCountMap.keyValueEnd(); it++) | ||||
| 		{ | ||||
| 			totalWordCountMap[it->first] = totalWordCountMap.value(it->first, 0) + it->second; | ||||
| 		} | ||||
| 	} | ||||
| 	if(isTruncated) | ||||
| 	{ | ||||
| 		header += "(truncated) "; | ||||
| 	} | ||||
| 	for(QString &word : config.wordsToHighlight) | ||||
| 	{ | ||||
| 		header += word + ": " + QString::number(countmap[word]) + " "; | ||||
| 	} | ||||
| 	if(snippetsCount == MAX_SNIPPETS) | ||||
| 	{ | ||||
| 		header += "(truncated)"; | ||||
| 		header += word + ": " + QString::number(totalWordCountMap[word]) + " "; | ||||
| 	} | ||||
| 	header += "<hr>"; | ||||
|  | ||||
|   | ||||
| @@ -9,6 +9,8 @@ | ||||
| #define SETTINGS_KEY_EXCLUDEDPATHS "excludedpaths" | ||||
| #define SETTINGS_KEY_MOUNTPATHS "mountpaths" | ||||
| #define SETTINGS_KEY_PREVIEWSPERPAGE "previewsPerPage" | ||||
| #define SETTINGS_KEY_SEARCHHISTORY "searchhistory" | ||||
| #define SETTINGS_KEY_PREVIEWS_SCROLL_HORIZONTALLY "horizontalscroll" | ||||
|  | ||||
| namespace Common | ||||
| { | ||||
|   | ||||
| @@ -6,11 +6,11 @@ template <class T> class LimitQueue | ||||
| { | ||||
|   protected: | ||||
| 	QQueue<T> queue; | ||||
| 	unsigned int limit = 0; | ||||
| 	int limit = 0; | ||||
|  | ||||
|   public: | ||||
| 	LimitQueue(); | ||||
| 	LimitQueue(unsigned int limit) | ||||
| 	LimitQueue(int limit) | ||||
| 	{ | ||||
| 		this->limit = limit; | ||||
| 	} | ||||
| @@ -34,7 +34,7 @@ template <class T> class LimitQueue | ||||
| 		return queue.dequeue(); | ||||
| 	} | ||||
|  | ||||
| 	void setLimit(unsigned int limit) | ||||
| 	void setLimit(int limit) | ||||
| 	{ | ||||
| 		this->limit = limit; | ||||
| 	} | ||||
|   | ||||
| @@ -6,7 +6,7 @@ class SearchResult | ||||
| { | ||||
|   public: | ||||
| 	FileData fileData; | ||||
| 	QVector<unsigned int> pages; | ||||
| 	unsigned int page; | ||||
| 	bool wasContentSearch = false; | ||||
| }; | ||||
|  | ||||
|   | ||||
| @@ -78,13 +78,18 @@ QString SqliteSearch::escapeFtsArgument(QString ftsArg) | ||||
| 		if(value.isEmpty()) | ||||
| 		{ | ||||
| 			value = m.captured(2); | ||||
| 			if(value.endsWith('*')) | ||||
| 			{ | ||||
| 				value = value.mid(0, value.size() - 1); | ||||
| 			} | ||||
| 			result += "\"" + value + "\"*"; | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			value = "\"\"" + value + "\"\""; | ||||
| 		} | ||||
| 			result += "\"" + value + "\" "; | ||||
| 		} | ||||
| 	} | ||||
| 	return result; | ||||
| } | ||||
|  | ||||
| @@ -189,10 +194,9 @@ QSqlQuery SqliteSearch::makeSqlQuery(const LooqsQuery &query) | ||||
| 				sortSql = "ORDER BY rank"; | ||||
| 			} | ||||
| 		} | ||||
| 		prepSql = | ||||
| 			"SELECT file.path AS path, group_concat(content.page) AS pages, file.mtime AS mtime, file.size AS size, " | ||||
| 		prepSql = "SELECT file.path AS path,  content.page AS page, file.mtime AS mtime, file.size AS size, " | ||||
| 				  "file.filetype AS filetype FROM file INNER JOIN content ON file.id = content.fileid " + | ||||
| 			joinSql + " WHERE 1=1 AND " + whereSql + " GROUP BY file.path  " + sortSql; | ||||
| 				  joinSql + " WHERE 1=1 AND " + whereSql + " " + sortSql; | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| @@ -200,7 +204,7 @@ QSqlQuery SqliteSearch::makeSqlQuery(const LooqsQuery &query) | ||||
| 		{ | ||||
| 			sortSql = "ORDER BY file.mtime DESC"; | ||||
| 		} | ||||
| 		prepSql = "SELECT file.path AS path, '0' as pages,  file.mtime AS mtime, file.size AS size, file.filetype AS " | ||||
| 		prepSql = "SELECT file.path AS path, '0' as page,  file.mtime AS mtime, file.size AS size, file.filetype AS " | ||||
| 				  "filetype FROM file WHERE  1=1 AND " + | ||||
| 				  whereSql + " " + sortSql; | ||||
| 	} | ||||
| @@ -243,15 +247,7 @@ QVector<SearchResult> SqliteSearch::search(const LooqsQuery &query) | ||||
| 		result.fileData.mtime = dbQuery.value("mtime").toUInt(); | ||||
| 		result.fileData.size = dbQuery.value("size").toUInt(); | ||||
| 		result.fileData.filetype = dbQuery.value("filetype").toChar(); | ||||
| 		QString pages = dbQuery.value("pages").toString(); | ||||
| 		QStringList pagesList = pages.split(","); | ||||
| 		for(QString &page : pagesList) | ||||
| 		{ | ||||
| 			if(page != "") | ||||
| 			{ | ||||
| 				result.pages.append(page.toUInt()); | ||||
| 			} | ||||
| 		} | ||||
| 		result.page = dbQuery.value("page").toUInt(); | ||||
| 		result.wasContentSearch = contentSearch; | ||||
| 		results.append(result); | ||||
| 	} | ||||
|   | ||||
		Reference in New Issue
	
	Block a user