Compare commits

...

28 Commits

Author SHA1 Message Date
5f19328679 Release v0.4 2022-06-29 19:31:40 +02:00
5ffd7ae97e shared: common: setPdfViewer(): Fix missing spaces between viewer and args 2022-06-29 19:31:40 +02:00
9ed28df09c README.me: Minor changes 2022-06-29 18:00:05 +02:00
69e028daf5 gui: mainwindow: Open new AboutDialog 2022-06-29 18:00:05 +02:00
d4b0c1feae gui: Add new AboutDialog to show our licenses "paperwork" 2022-06-29 18:00:05 +02:00
990888efc1 shared: Include license texts as resource 2022-06-29 18:00:05 +02:00
3f8c147182 Add LICENSE-3RD-PARTY 2022-06-29 18:00:05 +02:00
ba030169aa gui: previews: Allow filtering file for which to show preview 2022-06-29 18:00:05 +02:00
1636e39c5b shared: ParallelDirScanner: Fix missing progress report from top-level scan 2022-06-27 23:11:26 +02:00
bda23670e3 shared: ParallelDirScanner: Correct filter to 'NoDotAndDotDot' from 'NoDotDot' 2022-06-27 23:10:53 +02:00
d0fe51c666 README: Mention prebuilt tarball, add sig verification, minor improvements 2022-06-24 19:17:50 +02:00
a561032668 shared: Indexer: Adjust for OK_WASEMPTY 2022-06-24 19:17:50 +02:00
b8006bde7b shared: FileSaver: Don't launch SandboxedProcessor for empty files 2022-06-24 19:17:50 +02:00
614238edda shared: FileSaver: Print message for OK_WASEMPTY 2022-06-24 19:17:50 +02:00
83ed935790 shared: FileSaver: Return OK_WASEMPTY
We get OK_WASEMPTY from the processor when there
usually should be something. Rarely, this is not the case.

Let the callers know about this state at least
2022-06-24 19:17:50 +02:00
69837ed60f shared: SaveFileResult: Add OK_WASEMPTY when processor could not extract anything 2022-06-24 19:17:50 +02:00
2ad99699bf shared: FileSaver: Adjust to SandboxedProcessor new SaveFileResult value 2022-06-24 19:17:50 +02:00
b6b3e2f3c0 shared SandBoxedProcessor: Return SaveFileResult, remove defines
Not entirely ideal as SandboxedProcessor does not save anything,
but an improvement nevertheless over the current mess
2022-06-24 19:17:50 +02:00
cf0c940b15 shared: Move SaveFileResult enum to own header 2022-06-24 19:17:50 +02:00
9c41d531b0 gui: mainwindow: Add button to save log of failed paths 2022-06-24 19:17:50 +02:00
780318db6d shared: Indexer: Consider new NOACCESS too 2022-06-24 19:17:50 +02:00
9fd48d4664 shared: FileSaver: Return NOACCESS when no read access 2022-06-24 19:17:50 +02:00
e99b2ba091 shared: Indexer: IndexResult: Introduce failedResults() 2022-06-24 19:17:50 +02:00
0867ab294d shared: SandboxedProcessor: Return NO_ACCESS when file not readable 2022-06-24 19:17:50 +02:00
01a6a499c5 shared: SqliteDBService: Add 'NOACCESS'. Add function to convert enum to str 2022-06-24 19:17:50 +02:00
b3c6ce3899 cli: main: Improve usage text, print it when unknown command given 2022-06-24 19:17:50 +02:00
72dae1f7be cli: CommandAdd: Mention we read from stdin when no path is given 2022-06-24 19:17:50 +02:00
e01f5d6490 shared: ParallelDirScanner: Perform first pass to collect paths
Scan the top directory to collect paths for the threads. This
way we don't launch threads for paths without subdirs. Secondly,
large trees like usually $HOME will be scanned by multiple threads
at first.

Nevertheless, ParallelDirScanner can be improved still as threads may
run quickly out of work, so we may end up with only one anyway again
2022-06-24 19:17:50 +02:00
25 changed files with 1612 additions and 102 deletions

View File

@ -1,5 +1,19 @@
# looqs: Release notes # looqs: Release notes
## 2022-06-29 - v0.4
This release makes several minor improvements and begins prebuilt binaries of looqs that (should) run
on any recent Linux distribution.
- General: Begin new, experimental distro-agnostic tarball containing prebuilt binaries and libs. See the README for more information.
- GUI: In the "Previews" tab, allow filtering by file, to only show previews for a specific file.
- GUI: Add "Show previews" context menu option to files in result (if available)
- General: Fix build with libquazip 1.X
- General: Properly report access errors as a failure during indexing
- GUI: Add button to export a list of all paths that failed
- General: Improve dir scan threading
- CLI: Improve helptext
- General: Add voidlinux build instructions
## 2022-06-14 - v0.3 ## 2022-06-14 - v0.3
CHANGES: CHANGES:
- GUI: Add settings tab to configure various settings - GUI: Add settings tab to configure various settings

1119
LICENSE-3RD-PARTY Normal file

File diff suppressed because it is too large Load Diff

View File

@ -3,8 +3,6 @@ looqs is a tool that creates a full-text search index for your files. It allows
search terms have been found, as shown in the screenshots below. search terms have been found, as shown in the screenshots below.
## Screenshots ## 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 of the GUI.
### Preview ### Preview
looqs allow you to look inside files. It marks what you have searched for. looqs allow you to look inside files. It marks what you have searched for.
@ -28,9 +26,10 @@ There is no need to write the long form of filters. There are also booleans avai
![Screenshot looqs results](https://garage.quitesimple.org/assets/looqs/looqs_beatles_marley.png) ![Screenshot looqs results](https://garage.quitesimple.org/assets/looqs/looqs_beatles_marley.png)
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 of the GUI.
## Current status ## Current status
Last version: 2022-06-14, v0.3 Latest version: 2022-06-29, v0.4
Please see [Changelog](CHANGELOG.md) for a human readable list of changes. Please see [Changelog](CHANGELOG.md) for a human readable list of changes.
@ -58,6 +57,8 @@ Linux (on amd64) is currently the main focus. Currently, I don't plan on support
### Licence ### Licence
GPLv3. GPLv3.
For the dependencies/third-party libraries, see: LICENSE-3RD-PARTY
### Contributing ### Contributing
Please see the [Contribution guidelines](CONTRIBUTING.md) file. Please see the [Contribution guidelines](CONTRIBUTING.md) file.
@ -106,7 +107,35 @@ sudo apt-get update
sudo apt-get install looqs sudo apt-get install looqs
``` ```
### Prebuilt tarball (distro-agnostic) (EXPERIMENTAL)
looqs is also distributed as a tarball containing prebuilt binaries and its library dependencies. The tarball is
built using the Gentoo Hardened toolchain and the Qt version is smaller than what distributions usually
include. It does not include libraries that should mess with fontrendering or the graphics stack. The binaries should run on any recent Linux distribution (requires glibc 2.34 or newer at least) and expects
dependencies such as libGL to be provided by your distribution already (should be the case).
It's considered experimental for two reasons. Firstly, looqs has no updater (yet). You will have to manually check for updates. Secondly, I can't guarantee that I'll be quick with updates of the tarball specifically if the library versions become outdated between looqs updates.
You are therefore encouraged to use distro-native packages or to build it yourself if possible.
The tarball can be obtained here: https://repo.quitesimple.org/tarball/looqs
Quick start:
```
# Verify sig, see the end of this document: gpg --verify looqs-v0.4.tar.xz.sig
tar xf looqs-v0.4.tar.xz # Replace with the actual version you have obtained
cd looqs-v0.4
./looqs-gui # or ./looqs for the CLI
```
An AppImage may accompany the tarball in the future.
### Other distros ### Other distros
I'll probably add a package for voidlinux at some point and maybe will provide a Gentoo ebuild. However, I would appreciate help for others distros. If you create a package, let me know! I'll probably add a package for voidlinux at some point and maybe will provide a Gentoo ebuild. However, I would appreciate help for others distros. If you create a package, let me know!
As for distro-agnostic packages, I will also take a look into appimage / flatpak etc. and/or maybe just provide a self-contained archive.
### Signature verification
Release tags can be verified with [my PGP public key](https://quitesimple.org/share/pubkey). For what little it's worth, its fingerprint is: `C342 CA02 D2EC 2E14 F3C3 D5FF 7F7B 4C08 02CD 02F2`
Packages can be verified with the [repo-specific public key](https://repo.quitesimple.org/repo.quitesimple.org.asc). For what little it's worth, its fingerprint is: `1B49 45B3 16B2 468A 3DAC C1E0 75EF 3FE8 D753 C8F9`

View File

@ -47,7 +47,8 @@ int CommandAdd::handle(QStringList arguments)
{{"t", "threads"}, "Number of threads to use.", "threads"}}); {{"t", "threads"}, "Number of threads to use.", "threads"}});
parser.addHelpOption(); parser.addHelpOption();
parser.addPositionalArgument("add", "Add paths to the index", "add [paths...]"); parser.addPositionalArgument("add", "Add paths to the index",
"add [paths...]. If no path is given, read from stdin, one path per line.");
parser.process(arguments); parser.process(arguments);
this->keepGoing = parser.isSet("continue"); this->keepGoing = parser.isSet("continue");

View File

@ -30,6 +30,7 @@
void printUsage(QString argv0) void printUsage(QString argv0)
{ {
qInfo() << "Usage:" << argv0 << "command"; qInfo() << "Usage:" << argv0 << "command";
qInfo() << "Valid commands: add, update, delete, search, list. Each command has a --help option.";
} }
Command *commandFromName(QString name, SqliteDbService &dbService) Command *commandFromName(QString name, SqliteDbService &dbService)
@ -97,7 +98,7 @@ int main(int argc, char *argv[])
QString file = args.at(1); QString file = args.at(1);
SandboxedProcessor processor(file); SandboxedProcessor processor(file);
return processor.process(); return static_cast<int>(processor.process());
} }
Command *cmd = commandFromName(commandName, dbService); Command *cmd = commandFromName(commandName, dbService);
if(cmd != nullptr) if(cmd != nullptr)
@ -116,7 +117,7 @@ int main(int argc, char *argv[])
} }
else else
{ {
Logger::error() << "Unknown command:" << commandName << Qt::endl; printUsage(argv0);
return 1; return 1;
} }

74
gui/aboutdialog.cpp Normal file
View File

@ -0,0 +1,74 @@
#include <QTabWidget>
#include <QVBoxLayout>
#include <QLabel>
#include <QTextBrowser>
#include <QPushButton>
#include "aboutdialog.h"
#include "common.h"
AboutDialog::AboutDialog(QWidget *parent) : QDialog(parent)
{
QVBoxLayout *layout = new QVBoxLayout(this);
QTabWidget *tabWidget = new QTabWidget(this);
this->setWindowTitle("About looqs");
QHBoxLayout *closeLayout = new QHBoxLayout();
QPushButton *closeButton = new QPushButton(this);
closeButton->setText("Close");
closeButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
connect(closeButton, &QPushButton::clicked, this, &QDialog::close);
closeLayout->setMargin(10);
closeLayout->addStretch(10);
closeLayout->addWidget(closeButton);
layout->addWidget(tabWidget);
layout->addLayout(closeLayout);
QFrame *frame = new QFrame(this);
QVBoxLayout *aboutLayout = new QVBoxLayout(frame);
frame->setLayout(aboutLayout);
QLabel *aboutLooqs = new QLabel(this);
QString html = "<h2>looqs</h2>";
html += "Full-text search with previews for your files<br><br>";
html += "Copyright (c) 2018-2022: Albert Schwarzkopf<br><br>";
html += QString("Version: %1<br><br>").arg(Common::versionText());
html += "Contact: looqs at quitesimple dot org<br><br>";
html += "Website: <a href=\"https://quitesimple.org\">https://quitesimple.org</a><br><br>";
html += "Source code: <a "
"href=\"https://github.com/quitesimpleorg/looqs\">https://github.com/quitesimpleorg/looqs</a><br><br>";
html += "License: GPLv3<br><br>";
html += "looqs is open source and free of charge in the hope it will be useful. The author(s) do not "
"give any warranty. In the unlikely event of any damage, the author(s) cannot be held responsible. "
"You are using looqs at your own risk<br><br>";
html += "looqs uses third-party libraries, please see the corresponding tab.";
aboutLooqs->setText(html);
aboutLooqs->setTextFormat(Qt::RichText);
QLabel *logo = new QLabel(this);
QImage image(QString(":/looqs.svg"));
logo->setPixmap(QPixmap::fromImage(image));
aboutLayout->addWidget(logo);
aboutLayout->addWidget(aboutLooqs);
tabWidget->addTab(frame, "About");
QFile license(QString(":/LICENSE"));
license.open(QIODevice::ReadOnly);
QString licenseText = license.readAll();
QFile thirdPartyLicense(QString(":./LICENSE-3RD-PARTY"));
thirdPartyLicense.open(QIODevice::ReadOnly);
QString thirdPartyLicenseInfo = thirdPartyLicense.readAll();
QTextBrowser *licenseTextBrowser = new QTextBrowser(this);
licenseTextBrowser->setText(licenseText);
QTextBrowser *thirdPartyLicenceTextBrowser = new QTextBrowser(this);
thirdPartyLicenceTextBrowser->setText(thirdPartyLicenseInfo);
tabWidget->addTab(licenseTextBrowser, "License");
tabWidget->addTab(thirdPartyLicenceTextBrowser, "Third-party libraries");
}

12
gui/aboutdialog.h Normal file
View File

@ -0,0 +1,12 @@
#ifndef ABOUTDIALOG_H
#define ABOUTDIALOG_H
#include <QDialog>
class AboutDialog : public QDialog
{
Q_OBJECT
public:
AboutDialog(QWidget *parent = nullptr);
};
#endif // ABOUTDIALOG_H

View File

@ -27,6 +27,7 @@ DEFINES += QT_DEPRECATED_WARNINGS
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
SOURCES += \ SOURCES += \
aboutdialog.cpp \
ipcpreviewclient.cpp \ ipcpreviewclient.cpp \
ipcpreviewworker.cpp \ ipcpreviewworker.cpp \
ipcserver.cpp \ ipcserver.cpp \
@ -44,6 +45,7 @@ SOURCES += \
rendertarget.cpp rendertarget.cpp
HEADERS += \ HEADERS += \
aboutdialog.h \
ipc.h \ ipc.h \
ipcpreviewclient.h \ ipcpreviewclient.h \
ipcpreviewworker.h \ ipcpreviewworker.h \

View File

@ -114,7 +114,7 @@ int main(int argc, char *argv[])
QString file = args.at(2); QString file = args.at(2);
SandboxedProcessor processor(file); SandboxedProcessor processor(file);
return processor.process(); return static_cast<int>(processor.process());
} }
} }
QString ipcSocketPath = Common::ipcSocketPath(); QString ipcSocketPath = Common::ipcSocketPath();

View File

@ -23,6 +23,7 @@
#include "../shared/common.h" #include "../shared/common.h"
#include "ipcpreviewclient.h" #include "ipcpreviewclient.h"
#include "previewgenerator.h" #include "previewgenerator.h"
#include "aboutdialog.h"
MainWindow::MainWindow(QWidget *parent, QString socketPath) MainWindow::MainWindow(QWidget *parent, QString socketPath)
: QMainWindow(parent), ui(new Ui::MainWindow), progressDialog(this) : QMainWindow(parent), ui(new Ui::MainWindow), progressDialog(this)
@ -40,6 +41,7 @@ MainWindow::MainWindow(QWidget *parent, QString socketPath)
{ {
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);
}); });
connect(&ipcPreviewClient, &IPCPreviewClient::error, this, connect(&ipcPreviewClient, &IPCPreviewClient::error, this,
[this](QString msg) [this](QString msg)
@ -82,6 +84,13 @@ MainWindow::MainWindow(QWidget *parent, QString socketPath)
ui->spinPreviewPage->setValue(1); ui->spinPreviewPage->setValue(1);
ui->spinPreviewPage->setMinimum(1); ui->spinPreviewPage->setMinimum(1);
ui->btnOpenFailed->setVisible(false);
ui->comboPreviewFiles->setVisible(false);
auto policy = ui->btnOpenFailed->sizePolicy();
policy.setRetainSizeWhenHidden(true);
ui->btnOpenFailed->setSizePolicy(policy);
} }
void MainWindow::addPathToIndex() void MainWindow::addPathToIndex()
@ -172,25 +181,12 @@ void MainWindow::connectSignals()
} }
} }
}); });
connect( connect(ui->menuAboutAction, &QAction::triggered, this,
ui->menuAboutAction, &QAction::triggered, this,
[this](bool checked) [this](bool checked)
{ {
QString html = "<h2>looqs</h2>"; AboutDialog aboutDialog(this);
html += "Full-text search with previews for your files<br><br>";
html += "Copyright (c) 2018-2022: Albert Schwarzkopf<br><br>";
html += QString("Version: %1<br><br>").arg(Common::versionText());
html += "Contact: looqs at quitesimple dot org<br><br>";
html += "Website: <a href=\"https://quitesimple.org\">https://quitesimple.org</a><br><br>";
html +=
"Source code: <a "
"href=\"https://github.com/quitesimpleorg/looqs\">https://github.com/quitesimpleorg/looqs</a><br><br>";
html += "License: GPLv3<br><br><br>";
html += "looqs is open source and free of charge in the hope it will be useful. The author(s) do not "
"give any warranty. In the unlikely event of any damage, the author(s) cannot be held responsible. "
"You are using looqs at your own risk";
QMessageBox::about(this, "About looqs", html); aboutDialog.exec();
}); });
connect(ui->menuAboutQtAction, &QAction::triggered, this, connect(ui->menuAboutQtAction, &QAction::triggered, this,
[this](bool checked) { QMessageBox::aboutQt(this, "About Qt"); }); [this](bool checked) { QMessageBox::aboutQt(this, "About Qt"); });
@ -210,6 +206,35 @@ void MainWindow::connectSignals()
connect(this, &MainWindow::beginIndexSync, indexSyncer, &IndexSyncer::sync); connect(this, &MainWindow::beginIndexSync, indexSyncer, &IndexSyncer::sync);
connect(&this->progressDialog, &QProgressDialog::canceled, indexSyncer, &IndexSyncer::cancel); connect(&this->progressDialog, &QProgressDialog::canceled, indexSyncer, &IndexSyncer::cancel);
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->comboPreviewFiles, qOverload<int>(&QComboBox::currentIndexChanged), this, [&]() { makePreviews(1); },
Qt::QueuedConnection);
}
void MainWindow::exportFailedPaths()
{
QString filename =
QString("/tmp/looqs_indexresult_failed_%1").arg(QDateTime::currentDateTime().toString("yyyy-MM-dd_hhmmss"));
QFile outFile(filename);
if(!outFile.open(QIODevice::WriteOnly | QIODevice::Text))
{
QMessageBox::critical(this, "Failed to write log file", "An error occured while trying to create log file");
return;
}
QTextStream stream(&outFile);
IndexResult result = this->indexer->getResult();
stream << "Failed to index the following paths:\n";
for(FileScanResult &fsr : result.failedResults())
{
stream << fsr.first << " " << SaveFileResultToString(fsr.second) << '\n';
}
QDesktopServices::openUrl(filename);
} }
void MainWindow::startIndexSync() void MainWindow::startIndexSync()
@ -254,6 +279,7 @@ void MainWindow::startIndexing()
ui->txtSearch->setEnabled(false); ui->txtSearch->setEnabled(false);
ui->previewProcessBar->setValue(0); ui->previewProcessBar->setValue(0);
ui->previewProcessBar->setVisible(true); ui->previewProcessBar->setVisible(true);
ui->btnOpenFailed->setVisible(false);
QVector<QString> paths; QVector<QString> paths;
QStringList pathSettingsValue; QStringList pathSettingsValue;
@ -289,6 +315,10 @@ void MainWindow::finishIndexing()
ui->settingsTab->setEnabled(true); ui->settingsTab->setEnabled(true);
ui->txtPathScanAdd->setEnabled(true); ui->txtPathScanAdd->setEnabled(true);
ui->txtSearch->setEnabled(true); ui->txtSearch->setEnabled(true);
if(result.erroredPaths > 0)
{
ui->btnOpenFailed->setVisible(true);
}
} }
void MainWindow::comboScaleChanged(int i) void MainWindow::comboScaleChanged(int i)
@ -477,6 +507,9 @@ void MainWindow::handleSearchResults(const QVector<SearchResult> &results)
{ {
this->previewableSearchResults.clear(); this->previewableSearchResults.clear();
ui->treeResultsList->clear(); ui->treeResultsList->clear();
ui->comboPreviewFiles->clear();
ui->comboPreviewFiles->addItem("All previews");
ui->comboPreviewFiles->setVisible(true);
bool hasDeleted = false; bool hasDeleted = false;
for(const SearchResult &result : results) for(const SearchResult &result : results)
@ -505,6 +538,7 @@ void MainWindow::handleSearchResults(const QVector<SearchResult> &results)
if(PreviewGenerator::get(pathInfo) != nullptr) if(PreviewGenerator::get(pathInfo) != nullptr)
{ {
this->previewableSearchResults.append(result); this->previewableSearchResults.append(result);
ui->comboPreviewFiles->addItem(result.fileData.absPath);
} }
} }
} }
@ -582,6 +616,13 @@ void MainWindow::makePreviews(int page)
QVector<RenderTarget> targets; QVector<RenderTarget> targets;
for(SearchResult &sr : this->previewableSearchResults) for(SearchResult &sr : this->previewableSearchResults)
{ {
if(ui->comboPreviewFiles->currentIndex() != 0)
{
if(sr.fileData.absPath != ui->comboPreviewFiles->currentText())
{
continue;
}
}
RenderTarget renderTarget; RenderTarget renderTarget;
renderTarget.path = sr.fileData.absPath; renderTarget.path = sr.fileData.absPath;
@ -602,6 +643,7 @@ void MainWindow::makePreviews(int page)
ui->previewProcessBar->setVisible(this->previewableSearchResults.size() > 0); ui->previewProcessBar->setVisible(this->previewableSearchResults.size() > 0);
++this->currentPreviewGeneration; ++this->currentPreviewGeneration;
this->ui->spinPreviewPage->setEnabled(false); this->ui->spinPreviewPage->setEnabled(false);
this->ui->comboPreviewFiles->setEnabled(false);
emit startIpcPreviews(renderConfig, targets); emit startIpcPreviews(renderConfig, targets);
} }
@ -617,6 +659,20 @@ void MainWindow::createSearchResutlMenu(QMenu &menu, const QFileInfo &fileInfo)
menu.addAction("Copy full path to clipboard", menu.addAction("Copy full path to clipboard",
[&fileInfo] { QGuiApplication::clipboard()->setText(fileInfo.absoluteFilePath()); }); [&fileInfo] { QGuiApplication::clipboard()->setText(fileInfo.absoluteFilePath()); });
menu.addAction("Open containing folder", [this, &fileInfo] { this->openFile(fileInfo.absolutePath()); }); menu.addAction("Open containing folder", [this, &fileInfo] { this->openFile(fileInfo.absolutePath()); });
auto result =
std::find_if(this->previewableSearchResults.begin(), this->previewableSearchResults.end(),
[this, &fileInfo](SearchResult &a) { return fileInfo.absoluteFilePath() == a.fileData.absPath; });
if(result != this->previewableSearchResults.end())
{
menu.addAction("Show previews for this file",
[this, &fileInfo]
{
ui->tabWidget->setCurrentIndex(1);
this->ui->comboPreviewFiles->setCurrentText(fileInfo.absoluteFilePath());
});
}
} }
void MainWindow::openDocument(QString path, int num) void MainWindow::openDocument(QString path, int num)

View File

@ -75,6 +75,7 @@ class MainWindow : public QMainWindow
void addPathToIndex(); void addPathToIndex();
void startIndexSync(); void startIndexSync();
void saveSettings(); void saveSettings();
void exportFailedPaths();
signals: signals:
void startIpcPreviews(RenderConfig config, const QVector<RenderTarget> &targets); void startIpcPreviews(RenderConfig config, const QVector<RenderTarget> &targets);

View File

@ -7,7 +7,7 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>1280</width> <width>1280</width>
<height>736</height> <height>855</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
@ -27,7 +27,7 @@
<enum>QTabWidget::South</enum> <enum>QTabWidget::South</enum>
</property> </property>
<property name="currentIndex"> <property name="currentIndex">
<number>3</number> <number>1</number>
</property> </property>
<widget class="QWidget" name="resultsTab"> <widget class="QWidget" name="resultsTab">
<attribute name="title"> <attribute name="title">
@ -82,7 +82,7 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>1244</width> <width>1244</width>
<height>446</height> <height>565</height>
</rect> </rect>
</property> </property>
<layout class="QHBoxLayout" name="horizontalLayout"/> <layout class="QHBoxLayout" name="horizontalLayout"/>
@ -162,6 +162,16 @@
</property> </property>
</widget> </widget>
</item> </item>
<item>
<widget class="QComboBox" name="comboPreviewFiles">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item> <item>
<spacer name="horizontalSpacer"> <spacer name="horizontalSpacer">
<property name="orientation"> <property name="orientation">
@ -184,6 +194,16 @@
<string>Index</string> <string>Index</string>
</attribute> </attribute>
<layout class="QGridLayout" name="gridLayout"> <layout class="QGridLayout" name="gridLayout">
<item row="6" column="0">
<widget class="QLineEdit" name="txtIgnorePatterns"/>
</item>
<item row="11" column="0">
<widget class="QPushButton" name="btnStartIndexing">
<property name="text">
<string>Start indexing</string>
</property>
</widget>
</item>
<item row="1" column="0"> <item row="1" column="0">
<widget class="QGroupBox" name="groupBoxPaths"> <widget class="QGroupBox" name="groupBoxPaths">
<property name="title"> <property name="title">
@ -243,6 +263,12 @@
<layout class="QHBoxLayout" name="horizontalLayout_7"> <layout class="QHBoxLayout" name="horizontalLayout_7">
<item> <item>
<widget class="QLabel" name="lblPathsFound"> <widget class="QLabel" name="lblPathsFound">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text"> <property name="text">
<string>Paths found:</string> <string>Paths found:</string>
</property> </property>
@ -250,17 +276,42 @@
</item> </item>
<item> <item>
<widget class="QLabel" name="lblPathsFoundValue"> <widget class="QLabel" name="lblPathsFoundValue">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text"> <property name="text">
<string/> <string/>
</property> </property>
</widget> </widget>
</item> </item>
<item>
<spacer name="horizontalSpacer_5">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout> </layout>
</item> </item>
<item> <item>
<layout class="QHBoxLayout" name="horizontalLayout_4"> <layout class="QHBoxLayout" name="horizontalLayout_4">
<item> <item>
<widget class="QLabel" name="lblAdded"> <widget class="QLabel" name="lblAdded">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font"> <property name="font">
<font> <font>
<weight>50</weight> <weight>50</weight>
@ -274,17 +325,42 @@
</item> </item>
<item> <item>
<widget class="QLabel" name="lblAddedValue"> <widget class="QLabel" name="lblAddedValue">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text"> <property name="text">
<string/> <string/>
</property> </property>
</widget> </widget>
</item> </item>
<item>
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout> </layout>
</item> </item>
<item> <item>
<layout class="QHBoxLayout" name="horizontalLayout_5"> <layout class="QHBoxLayout" name="horizontalLayout_5">
<item> <item>
<widget class="QLabel" name="lblSkipped"> <widget class="QLabel" name="lblSkipped">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font"> <property name="font">
<font> <font>
<weight>50</weight> <weight>50</weight>
@ -298,17 +374,42 @@
</item> </item>
<item> <item>
<widget class="QLabel" name="lblSkippedValue"> <widget class="QLabel" name="lblSkippedValue">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text"> <property name="text">
<string/> <string/>
</property> </property>
</widget> </widget>
</item> </item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout> </layout>
</item> </item>
<item> <item>
<layout class="QHBoxLayout" name="horizontalLayout_6"> <layout class="QHBoxLayout" name="horizontalLayout_6">
<item> <item>
<widget class="QLabel" name="lblFailed"> <widget class="QLabel" name="lblFailed">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font"> <property name="font">
<font> <font>
<weight>50</weight> <weight>50</weight>
@ -322,24 +423,33 @@
</item> </item>
<item> <item>
<widget class="QLabel" name="lblFailedValue"> <widget class="QLabel" name="lblFailedValue">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text"> <property name="text">
<string/> <string/>
</property> </property>
</widget> </widget>
</item> </item>
</layout> <item>
</item> <widget class="QPushButton" name="btnOpenFailed">
</layout> <property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Open log of failed paths</string>
</property>
</widget> </widget>
</item> </item>
<item row="6" column="0"> </layout>
<widget class="QLineEdit" name="txtIgnorePatterns"/>
</item> </item>
<item row="10" column="0"> </layout>
<widget class="QPushButton" name="btnStartIndexing">
<property name="text">
<string>Start indexing</string>
</property>
</widget> </widget>
</item> </item>
</layout> </layout>

View File

@ -17,6 +17,7 @@
inline void initResources() inline void initResources()
{ {
Q_INIT_RESOURCE(migrations); Q_INIT_RESOURCE(migrations);
Q_INIT_RESOURCE(plaintexts);
} }
bool Common::initSqliteDatabase(QString path) bool Common::initSqliteDatabase(QString path)
@ -68,11 +69,11 @@ void Common::setPdfViewer()
} }
else if(evincePath != "") else if(evincePath != "")
{ {
value = evincePath + "-i %p %f"; value = evincePath + " -i %p %f";
} }
else if(qpdfviewPath != "") else if(qpdfviewPath != "")
{ {
value = qpdfviewPath + "%f#%p"; value = qpdfviewPath + " %f#%p";
} }
QSettings settings; QSettings settings;

View File

@ -19,9 +19,12 @@ void DirScanWorker::run()
QString path; QString path;
/* TODO: if we have e. g. only one path, then only one thread will scan this path. /* TODO: if we have e. g. only one path, then only one thread will scan this path.
* *
* Thus, we must resubmit to the queue directories so other threads can help * Thus, we must submit new directory paths to the queue so other threads can help
the current one (requires a new logic for threads in ParallelDirScanner). Alterantively, the current one.
start new DirScanWorkers ourselves here... */
It also may not be guaranteed enqueuing new paths is faster than just scanning them,
at least not for dirs with a small number of files
*/
while(queue->dequeue(path)) while(queue->dequeue(path))
{ {
if(wildcardMatcher.match(path)) if(wildcardMatcher.match(path))

View File

@ -87,6 +87,10 @@ int FileSaver::processFiles(const QVector<QString> paths, std::function<SaveFile
{ {
Logger::info() << "Saved" << path << Qt::endl; Logger::info() << "Saved" << path << Qt::endl;
} }
else if(result == OK_WASEMPTY)
{
Logger::info() << "Saved (but content was empty)" << path << Qt::endl;
}
} }
} }
}); });
@ -98,7 +102,12 @@ SaveFileResult FileSaver::saveFile(const QFileInfo &fileInfo)
QVector<PageData> pageData; QVector<PageData> pageData;
QString canonicalPath = fileInfo.canonicalFilePath(); QString canonicalPath = fileInfo.canonicalFilePath();
int status = -1; int processorReturnCode = -1;
if(!fileInfo.isReadable())
{
return NOACCESS;
}
if(!fileInfo.exists()) if(!fileInfo.exists())
{ {
@ -115,6 +124,8 @@ SaveFileResult FileSaver::saveFile(const QFileInfo &fileInfo)
} }
} }
if(fileInfo.size() > 0)
{
QProcess process; QProcess process;
QStringList args; QStringList args;
args << "process" << canonicalPath; args << "process" << canonicalPath;
@ -138,21 +149,20 @@ SaveFileResult FileSaver::saveFile(const QFileInfo &fileInfo)
in >> pd; in >> pd;
pageData.append(pd); pageData.append(pd);
} }
status = process.exitCode(); processorReturnCode = process.exitCode();
if(status != 0 && status != NOTHING_PROCESSED) if(processorReturnCode != OK && processorReturnCode != OK_WASEMPTY)
{ {
Logger::error() << "FileSaver::saveFile(): Error while processing" << canonicalPath << ":" Logger::error() << "FileSaver::saveFile(): Error while processing" << canonicalPath << ":"
<< "Exit code " << status << Qt::endl; << "Exit code " << processorReturnCode << Qt::endl;
return PROCESSFAIL; return static_cast<SaveFileResult>(processorReturnCode);
} }
} }
}
// Could happen if a file corrupted for example SaveFileResult result = this->dbService->saveFile(fileInfo, pageData);
if(pageData.isEmpty() && status != NOTHING_PROCESSED) if(result == OK && processorReturnCode == OK_WASEMPTY)
{ {
Logger::error() << "Could not get any content for " << canonicalPath << Qt::endl; return OK_WASEMPTY;
} }
return result;
return this->dbService->saveFile(fileInfo, pageData);
} }

View File

@ -120,7 +120,7 @@ void Indexer::dirScanProgress(int current, int total)
void Indexer::processFileScanResult(FileScanResult result) void Indexer::processFileScanResult(FileScanResult result)
{ {
if(result.second == DBFAIL || result.second == PROCESSFAIL || result.second == NOTFOUND) if(result.second != OK || result.second != OK_WASEMPTY || result.second != SKIPPED)
{ {
this->currentIndexResult.results.append(result); this->currentIndexResult.results.append(result);
if(!keepGoing) if(!keepGoing)
@ -138,7 +138,8 @@ void Indexer::processFileScanResult(FileScanResult result)
} }
} }
if(result.second == OK) /* TODO: OK_WASEMPTY might need a special list */
if(result.second == OK || result.second == OK_WASEMPTY)
{ {
++this->currentIndexResult.addedPaths; ++this->currentIndexResult.addedPaths;
} }

View File

@ -22,19 +22,30 @@ class IndexResult
return addedPaths + skippedPaths + erroredPaths; return addedPaths + skippedPaths + erroredPaths;
} }
QVector<QString> failedPaths() const QVector<FileScanResult> failedResults() const
{ {
QVector<QString> result; QVector<FileScanResult> result;
std::for_each(results.begin(), results.end(), std::for_each(results.begin(), results.end(),
[&result](FileScanResult res) [&result](FileScanResult res)
{ {
if(res.second == DBFAIL || res.second == PROCESSFAIL || res.second == NOTFOUND) if(res.second == DBFAIL || res.second == PROCESSFAIL || res.second == NOTFOUND ||
res.second == NOACCESS)
{ {
result.append(res.first); result.append(res);
} }
}); });
return result; return result;
} }
QVector<QString> failedPaths() const
{
QVector<QString> result;
QVector<FileScanResult> results = failedResults();
std::for_each(results.begin(), results.end(), [&result](FileScanResult res) { result.append(res.first); });
return result;
}
}; };
class Indexer : public QObject class Indexer : public QObject

View File

@ -71,7 +71,35 @@ void ParallelDirScanner::scan()
this->targetPathsQueue.clear(); this->targetPathsQueue.clear();
this->resultPathsQueue.clear(); this->resultPathsQueue.clear();
this->targetPathsQueue.enqueue(this->paths); /* First scan without subdirs. This way we collect paths for the threads */
WildcardMatcher matcher;
matcher.setPatterns(this->ignorePatterns);
for(QString &path : this->paths)
{
QDirIterator iterator(path, QStringList{}, QDir::Dirs | QDir::QDir::Files | QDir::NoDotAndDotDot);
while(iterator.hasNext())
{
QString path = iterator.next();
if(matcher.match(path))
{
continue;
}
QFileInfo info = iterator.fileInfo();
if(!info.isSymLink())
{
if(info.isDir())
{
this->targetPathsQueue.enqueue(info.absoluteFilePath());
}
else
{
this->resultPathsQueue.enqueue(info.absoluteFilePath());
this->processedPaths += 1;
}
}
}
}
emit this->progress(this->processedPaths, this->processedPaths);
int threadsNum = getThreadsNum(); int threadsNum = getThreadsNum();
if(threadsNum == 0) if(threadsNum == 0)
{ {

6
shared/plaintexts.qrc Normal file
View File

@ -0,0 +1,6 @@
<RCC>
<qresource>
<file>../LICENSE</file>
<file>../LICENSE-3RD-PARTY</file>
</qresource>
</RCC>

View File

@ -10,8 +10,6 @@ enum DataSource
ARRAY ARRAY
}; };
#define NOTHING_PROCESSED 4
class Processor class Processor
{ {
public: public:

View File

@ -80,7 +80,7 @@ void SandboxedProcessor::printResults(const QVector<PageData> &pageData)
fsstdout.close(); fsstdout.close();
} }
int SandboxedProcessor::process() SaveFileResult SandboxedProcessor::process()
{ {
QFileInfo fileInfo(this->filePath); QFileInfo fileInfo(this->filePath);
Processor *processor = processors.value(fileInfo.suffix(), nullptr); Processor *processor = processors.value(fileInfo.suffix(), nullptr);
@ -92,10 +92,14 @@ int SandboxedProcessor::process()
processor = defaultTextProcessor; processor = defaultTextProcessor;
} }
} }
if(!fileInfo.isReadable())
{
return NOACCESS;
}
if(processor == nullptr || processor == nothingProcessor) if(processor == nullptr || processor == nothingProcessor)
{ {
/* Nothing to do */ /* Nothing to do */
return NOTHING_PROCESSED; return OK;
} }
QVector<PageData> pageData; QVector<PageData> pageData;
@ -119,9 +123,9 @@ int SandboxedProcessor::process()
catch(LooqsGeneralException &e) catch(LooqsGeneralException &e)
{ {
Logger::error() << "SandboxedProcessor: Error while processing" << absPath << ":" << e.message << Qt::endl; Logger::error() << "SandboxedProcessor: Error while processing" << absPath << ":" << e.message << Qt::endl;
return 3 /* PROCESSFAIL */; return PROCESSFAIL;
} }
printResults(pageData); printResults(pageData);
return 0; return pageData.isEmpty() ? OK_WASEMPTY : OK;
} }

View File

@ -3,6 +3,7 @@
#include <QString> #include <QString>
#include <QMimeDatabase> #include <QMimeDatabase>
#include "pagedata.h" #include "pagedata.h"
#include "savefileresult.h"
class SandboxedProcessor class SandboxedProcessor
{ {
@ -19,7 +20,7 @@ class SandboxedProcessor
this->filePath = filepath; this->filePath = filepath;
} }
int process(); SaveFileResult process();
}; };
#endif // SANDBOXEDPROCESSOR_H #endif // SANDBOXEDPROCESSOR_H

33
shared/savefileresult.h Normal file
View File

@ -0,0 +1,33 @@
#ifndef SAVEFILERESULT_H
#define SAVEFILERESULT_H
#include <QStringList>
enum SaveFileResult
{
OK,
OK_WASEMPTY, /* The file was successfully read but no content could be extracted. Might be just an empty document or
so*/
SKIPPED, /* The file was not processed/saved, because it has not changed */
DBFAIL, /* A database error occured */
PROCESSFAIL, /* General processor failure */
NOTFOUND, /* The file was not found */
NOACCESS /* The process can't read the file */
};
static inline QString SaveFileResultToString(SaveFileResult sfr)
{
QStringList SaveFileResultStr;
SaveFileResultStr << "OK"
<< "OK_WASEMPTY"
<< "SKIPPED"
<< "DBFAIL"
<< "PROCESSFAIL"
<< "NOTFOUND"
<< "NOACCESS";
return SaveFileResultStr[(int)sfr];
}
#endif // SAVEFILERESULT_H

View File

@ -88,6 +88,7 @@ HEADERS += sqlitesearch.h \
pdfprocessor.h \ pdfprocessor.h \
processor.h \ processor.h \
sandboxedprocessor.h \ sandboxedprocessor.h \
savefileresult.h \
searchresult.h \ searchresult.h \
sqlitedbservice.h \ sqlitedbservice.h \
tagstripperprocessor.h \ tagstripperprocessor.h \
@ -100,3 +101,4 @@ unix {
INSTALLS += target INSTALLS += target
} }
RESOURCES = migrations/migrations.qrc RESOURCES = migrations/migrations.qrc
RESOURCES += plaintexts.qrc

View File

@ -7,14 +7,7 @@
#include "filedata.h" #include "filedata.h"
#include "../shared/sqlitesearch.h" #include "../shared/sqlitesearch.h"
#include "../shared/token.h" #include "../shared/token.h"
enum SaveFileResult #include "savefileresult.h"
{
OK,
SKIPPED,
DBFAIL,
PROCESSFAIL,
NOTFOUND
};
class SqliteDbService class SqliteDbService
{ {