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