first commit

This commit is contained in:
Albert S. 2018-01-03 09:52:05 +01:00
commit f96452d669
13 changed files with 1130 additions and 0 deletions

90
README.md Normal file
View File

@ -0,0 +1,90 @@
qsRunner
========
qsRunner is a launcher. It contains user defined entries for applications and also searches
system-applications. Using libcalculate, it can also be used as a calculator. It
can also search for files (and their contents) by querying a sqlite database, although it itself does
not index files.
If you run a desktop environment like KDE it is questionable whether you will
find this useful, since they usually bring applications that are more or less
comparable to qsRunner, although much more
powerful (like KRunner). It can be useful for users running a window manager like
fluxbox etc.
Dependencies
------------
It has been only tested for Qt 5.7.
For the calculation engine, libqalculate is needed.
For file search, easyindex is necessary.
Currently no conditional compile flags are supported...
Getting started
----------------
Currently it may not be considered a classical GUI application because the
configuration must be done outside of it.
mkdir $HOME/.config/qsRunner
In this folder user-defined entries should be put (See "Entry format").
Config format
------------
Path: $HOME/.config/qsRunner/qsrunner.config
```
[Search]
dbpath="/path/to/database.sqlite"
[General]
systemApplicationsPath="/usr/share/applications/"
```
systemApplicationsPath will default to "/usr/share/applications/",
therefore specifying it explicitly is not necessary.
Entry format
------------
It rudimentary supports .desktop files, but for user entries, the own format
should be preferred.
It's a simple format: [key] [value].
Example: quasselclient.qsrun:
```
command quasselclient
name Quassel
icon /usr/share/icons/hicolor/128x128/apps/quassel.png
row 1
col 0
key I
```
"key" means a shortcut key, you can launch those by pressing Ctrl + "key", so in
the example above: CTRL + I.
Simply pressing Ctrl will show you the associated shortcuts on each individual
button.
General usage
=============
Starting to type will search user defined entries first, followed by system
entries. Then the PATH variable will be searched, if there is a single match you can also
press TAB for auto completion.
In general it will launch anything once you press enter, however it won't open a
terminal.
Calculator
=========
Start by typing "=", followed by your expression, e. g: "=(2+3)^2"
File searches
=============
It only queries a sqlite database. It does not index files, this is beyond the
scope a launcher. For the file searches functionality, easyindex is necessary.
See: git.quitesimple.org/easyindex.

42
calculationengine.cpp Normal file
View File

@ -0,0 +1,42 @@
#include "calculationengine.h"
CalculationEngine::CalculationEngine()
{
if (!CALCULATOR) {
new Calculator();
CALCULATOR->terminateThreads();
CALCULATOR->setPrecision(16);
}
//stolen from krunner's qalculate engine
/*eo.auto_post_conversion = POST_CONVERSION_BEST;
eo.keep_zero_units = false;
eo.parse_options.angle_unit = ANGLE_UNIT_RADIANS;
eo.structuring = STRUCTURING_SIMPLIFY;
// suggested in https://github.com/Qalculate/libqalculate/issues/16
// to avoid memory overflow for seemingly innocent calculations (Bug 277011)
eo.approximation = APPROXIMATION_APPROXIMATE;
po.number_fraction_format = FRACTION_DECIMAL;
po.indicate_infinite_series = false;
po.use_all_prefixes = false;
po.use_denominator_prefix = true;
po.negative_exponents = false;
po.lower_case_e = true;
po.base_display = BASE_DISPLAY_NORMAL;*/
}
QString CalculationEngine::evaluate(const QString &expression)
{
CALCULATOR->terminateThreads();
QByteArray ba = expression.toLatin1();
const char *ctext = ba.data();
MathStructure result = CALCULATOR->calculate(ctext, this->eo);
result.format(po);
return result.print(po).c_str();
}

22
calculationengine.h Normal file
View File

@ -0,0 +1,22 @@
#ifndef CALCULATIONENGINE_H
#define CALCULATIONENGINE_H
#include <QString>
#include <libqalculate/Calculator.h>
#include <libqalculate/ExpressionItem.h>
#include <libqalculate/Unit.h>
#include <libqalculate/Prefix.h>
#include <libqalculate/Variable.h>
#include <libqalculate/Function.h>
class CalculationEngine
{
private:
EvaluationOptions eo;
PrintOptions po;
public:
CalculationEngine();
QString evaluate(const QString &expression);
};
#endif // CALCULATIONENGINE_H

151
config.cpp Normal file
View File

@ -0,0 +1,151 @@
#include "config.h"
#include <QDirIterator>
#include <QDebug>
#include <QTextStream>
ConfigReader::ConfigReader(QString directory)
{
this->configDirectory = directory;
}
EntryConfig ConfigReader::readFromDesktopFile(const QString &path)
{
EntryConfig result;
QFile file(path);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
{
//TODO: better exception class
throw new std::runtime_error("Failed to open file");
}
QTextStream stream(&file);
QString firstline = stream.readLine();
while(firstline[0] == '#')
{
firstline = stream.readLine();
}
if(firstline != "[Desktop Entry]")
{
throw new ConfigFormatException(".desktop file does not start with [Desktop Entry]");
}
while(!stream.atEnd())
{
QString line = stream.readLine();
QStringList splitted = line.split("=");
if(splitted.length() >= 2)
{
qDebug() << splitted [0] + " " + splitted[1];
QString key = splitted[0].toLower();
if(key == "name")
{
if(result.name.length() == 0)
{
result.name = splitted[1];
}
}
if(key == "icon")
{
result.icon = QIcon::fromTheme(splitted[1]);
}
if(key == "exec")
{
//TODO: the other arguments may also be relevant... except for %f and so
QStringList arguments = splitted[1].split(" ");
result.command = arguments[0];
if(arguments.length() > 1)
{
arguments = arguments.mid(1);
}
}
}
}
return result;
}
/* qsrun own's config file */
EntryConfig ConfigReader::readFromFile(const QString &path)
{
EntryConfig result;
QFile file(path);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
{
//TODO: better exception class
throw new std::runtime_error("Failed to open file");
}
QTextStream stream(&file);
while(!stream.atEnd())
{
QString line = stream.readLine();
QStringList splitted = line.split(" ");
if(splitted.length() < 2)
{
// throw new ConfigFormatException("Format must be [key] [value] for every line");
}
qDebug() << splitted [0] + " " + splitted[1];
QString key = splitted[0];
if(key == "arguments")
{
result.arguments = splitted.mid(1);
}
if(key == "name")
{
result.name = splitted[1];
}
if(key == "icon")
{
result.icon = QIcon(splitted[1]);
}
if(key == "row")
{
result.row = splitted[1].toInt();
}
if(key == "col")
{
result.col = splitted[1].toInt();
}
if(key == "command")
{
result.command = splitted[1];
}
if(key == "key")
{
//QKeySequence sequence(splitted[1]);
//result.keySequence = sequence;
result.key = splitted[1].toLower();
}
}
return result;
}
QVector<EntryConfig> ConfigReader::readConfig()
{
QVector<EntryConfig> result;
QDirIterator it(this->configDirectory);
while(it.hasNext())
{
QString path = it.next();
QFileInfo info(path);
if(info.isFile())
{
QString suffix = info.completeSuffix();
if(suffix == "desktop")
{
result.append(readFromDesktopFile(path));
}
if(suffix == "qsrun")
{
result.append(readFromFile(path));
}
}
}
return result;
}

43
config.h Normal file
View File

@ -0,0 +1,43 @@
#ifndef CONFIG_H
#define CONFIG_H
#include <exception>
#include <QFile>
#include <QDir>
#include <QString>
#include <QStringList>
#include <QIcon>
#include <QKeySequence>
class EntryConfig
{
public:
QString key;
QString name;
QString command;
QStringList arguments;
QIcon icon;
int row=0;
int col=0;
};
class ConfigReader
{
private:
QString configDirectory;
EntryConfig readFromFile(const QString &path);
EntryConfig readFromDesktopFile(const QString &path);
public:
ConfigReader(QString path);
QVector<EntryConfig> readConfig();
};
class ConfigFormatException : public std::runtime_error
{
public:
ConfigFormatException() : std::runtime_error("Error in configuration file, misformated line?") {};
ConfigFormatException(const std::string &str) : std::runtime_error(str) {};
};
#endif

48
entrypushbutton.cpp Normal file
View File

@ -0,0 +1,48 @@
#include "entrypushbutton.h"
EntryPushButton::EntryPushButton(const EntryConfig &config) : QPushButton()
{
this->setText(config.name);
this->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
this->setIcon(config.icon);
this->setIconSize(config.icon.availableSizes().first());
this->config = config;
connect(this, SIGNAL(clicked()), this, SLOT(emitOwnClicked()));
}
void EntryPushButton::emitOwnClicked()
{
emit clicked(this->config);
}
const EntryConfig &EntryPushButton::getEntryConfig()
{
return this->config;
}
void EntryPushButton::setEntryConfig(const EntryConfig &config)
{
this->config = config;
}
void EntryPushButton::showShortcut()
{
this->setText(this->config.key);
}
void EntryPushButton::showName()
{
this->setText(this->config.name);
}
int EntryPushButton::getRow() const { return config.row; }
int EntryPushButton::getCol() const { return config.col; }
QString EntryPushButton::getName() const { return config.name; }
QString EntryPushButton::getShortcutKey() const { return config.key; }
void EntryPushButton::setRow(int row) { this->config.row = row; }
void EntryPushButton::setCol(int col) { this->config.col = col; }
QStringList EntryPushButton::getArguments() const { return this->config.arguments; }
QString EntryPushButton::getCommand() const { return this->config.command; }

35
entrypushbutton.h Normal file
View File

@ -0,0 +1,35 @@
#ifndef ENTRYPUSHBUTTON_H
#define ENTRYPUSHBUTTON_H
#include <QWidget>
#include <QPushButton>
#include "config.h"
class EntryPushButton : public QPushButton
{
Q_OBJECT
private:
EntryConfig config;
private slots:
void emitOwnClicked();
signals:
void clicked(const EntryConfig &config);
public:
EntryPushButton(const EntryConfig &config);
const EntryConfig &getEntryConfig();
void setEntryConfig(const EntryConfig &config);
void showShortcut();
void showName();
int getRow() const;
int getCol() const;
QString getName() const;
QString getShortcutKey() const;
QStringList getArguments() const;
QString getCommand() const;
void setRow(int row);
void setCol(int col);
};
#endif // ENTRYPUSHBUTTON_H

80
main.cpp Normal file
View File

@ -0,0 +1,80 @@
#include <QApplication>
#include <QFuture>
#include <QFutureWatcher>
#include <QtConcurrent/QtConcurrentRun>
#include <QSettings>
#include "window.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QString configDirectoryPath;
if(argc >= 2)
{
configDirectoryPath = QCoreApplication::arguments().at(1);
}
else
{
configDirectoryPath = QDir::homePath() + "/.config/qsRun/";
}
qRegisterMetaType<QVector<QString> >("QVector<QString>");
QDir dir(configDirectoryPath);
if(!dir.exists(configDirectoryPath))
{
QMessageBox::warning(nullptr, "Directory not found", configDirectoryPath + " was not found!");
return 1;
}
QSettings settings(configDirectoryPath + "qsrun.config", QSettings::NativeFormat);
QString dbpath = settings.value("Search/dbpath").toString();
QString systemApplicationsPath = settings.value("General/systemApplicationsPath", "/usr/share/applications/").toString();
QVector<EntryConfig> configs;
try
{
ConfigReader reader(configDirectoryPath);
configs = reader.readConfig();
}
catch(std::exception &e)
{
std::cerr << e.what() << std::endl;
}
Window w(configs, dbpath);
/*
* TODO: Reconsider the need
* QFuture<void> future = QtConcurrent::run([&w] {
ConfigReader systemConfigReader("/usr/share/applications/");
QList<EntryConfig> systemconfigs = systemConfigReader.readConfig();
if(systemconfigs.count() > 0)
{
w.setSystemConfig(systemconfigs);
w.systemConfigReady();
}
});*/
try
{
ConfigReader systemConfigReader(systemApplicationsPath);
QVector<EntryConfig> systemconfigs = systemConfigReader.readConfig();
if(systemconfigs.count() > 0)
{
w.setSystemConfig(systemconfigs);
}
}
catch(std::exception &e)
{
std::cerr << e.what() << std::endl;
}
w.showMaximized();
return app.exec();
}

23
qsRun.pro Normal file
View File

@ -0,0 +1,23 @@
######################################################################
# Automatically generated by qmake (3.0) Mon Dec 25 15:21:45 2017
######################################################################
TEMPLATE = app
TARGET = qsRun
INCLUDEPATH += .
# Input
HEADERS += config.h window.h \
entrypushbutton.h \
calculationengine.h \
searchworker.h
SOURCES += config.cpp main.cpp window.cpp \
entrypushbutton.cpp \
calculationengine.cpp \
searchworker.cpp
QT += widgets sql
QT_CONFIG -= no-pkg-config
LIBS += -lcln
CONFIG += link_pkgconfig
PKGCONFIG += libqalculate

73
searchworker.cpp Normal file
View File

@ -0,0 +1,73 @@
#include "searchworker.h"
#include <QDebug>
#include <QSqlError>
//TODO: we have code duplication in the search functions currently.
SearchWorker::SearchWorker(const QString &dbpath)
{
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
db.setDatabaseName(dbpath);
if(!db.open())
{
qDebug() << "failed to open database";
}
queryContent = new QSqlQuery(db);
queryFile = new QSqlQuery(db);
queryFile->prepare("SELECT path FROM file WHERE path LIKE ? ORDER BY mtime DESC");
queryContent->prepare("SELECT file.path FROM file INNER JOIN file_fts ON file.id = file_fts.ROWID WHERE file_fts.content MATCH ? ORDER By file.mtime DESC");
}
void SearchWorker::searchForFile(const QString &query)
{
this->isPending = true;
this->cancelCurrent = false;
QVector<QString> results;
queryFile->addBindValue("%" + query + "%");
queryFile->exec();
while(queryFile->next())
{
if(cancelCurrent)
{
this->isPending = false;
emit searchCancelled();
return;
}
QString result = queryFile->value(0).toString();
qDebug() << "result" << result;
results.append(queryFile->value(0).toString());
}
this->isPending = false;
emit searchResultsReady(results);
}
void SearchWorker::requestCancellation()
{
this->cancelCurrent = true;
}
void SearchWorker::searchForContent(const QString &query)
{
this->isPending = true;
this->cancelCurrent = false;
QVector<QString> results;
queryContent->addBindValue(query);
queryContent->exec();
while(queryContent->next())
{
if(cancelCurrent)
{
this->isPending = false;
emit searchCancelled();
return;
}
QString result = queryContent->value(0).toString();
qDebug() << "result" << result;
results.append(queryContent->value(0).toString());
}
this->isPending = false;
emit searchResultsReady(results);
}
bool SearchWorker::isOperationPending()
{
return this->isPending;
}

32
searchworker.h Normal file
View File

@ -0,0 +1,32 @@
#ifndef SEARCHWORKER_H
#define SEARCHWORKER_H
#include <QObject>
#include <QSqlDatabase>
#include <QSqlQuery>
#include <QString>
#include <QVector>
class SearchWorker : public QObject
{
Q_OBJECT
private:
QSqlQuery *queryFile;
QSqlQuery *queryContent;
bool isPending = false;
bool cancelCurrent = false;
public:
SearchWorker(const QString &dbpath);
bool isOperationPending();
void requestCancellation();
public slots:
void searchForFile(const QString &query);
void searchForContent(const QString &query);
signals:
void searchResultsReady(const QVector<QString> &results);
void searchCancelled();
};
#endif // SEARCHWORKER_H

423
window.cpp Normal file
View File

@ -0,0 +1,423 @@
#include "window.h"
#include <QDebug>
#include <QProcess>
#include <QProcessEnvironment>
#include <QDirIterator>
#include <QIcon>
#include <QKeySequence>
#include <QLabel>
#include <QDate>
#include <QHeaderView>
#include <QDesktopServices>
Window::Window(const QVector<EntryConfig> &configs, const QString &dbpath)
{
this->userEntryButtons = generateEntryButtons(configs);
createGui();
populateGrid(this->userEntryButtons);
searchWorker = new SearchWorker(dbpath);
searchWorker->moveToThread(&searchThread);
connect(this, &Window::beginFileSearch, searchWorker, &SearchWorker::searchForFile);
connect(this, &Window::beginContentSearch, searchWorker, &SearchWorker::searchForContent);
connect(searchWorker, &SearchWorker::searchResultsReady, this, &Window::handleSearchResults);
searchThread.start();
initTreeWidgets();
this->lineEdit->installEventFilter(this);
}
Window::~Window()
{
searchThread.quit();
searchThread.wait();
}
void Window::initTreeWidgets()
{
QStringList headers;
headers << "Filename";
headers << "Path";
headers << "Modification time";
treeFileSearchResults.setHeaderLabels(headers);
treeFileSearchResults.header()->setSectionResizeMode(QHeaderView::ResizeToContents);
connect(&treeFileSearchResults, &QTreeWidget::itemActivated, this, &Window::treeSearchItemActivated);
}
QVector<EntryPushButton*> Window::generateEntryButtons(const QVector<EntryConfig> &configs)
{
QVector<EntryPushButton*> result;
for(const EntryConfig &config : configs)
{
EntryPushButton *button = createEntryButton(config);
result.append(button);
}
return result;
}
void Window::createGui()
{
QVBoxLayout *vbox = new QVBoxLayout(this);
grid = new QGridLayout();
lineEdit = new QLineEdit();
vbox->setAlignment(Qt::AlignTop);
vbox->addWidget(lineEdit);
vbox->addLayout(grid);
connect(lineEdit, &QLineEdit::textChanged, this, [this](QString newtext) { this->lineEditTextChanged(newtext); });
connect(lineEdit, &QLineEdit::returnPressed, this, &Window::lineEditReturnPressed);
}
void Window::populateGrid(const QVector<EntryPushButton *> &list)
{
clearGrid();
for(EntryPushButton *button : list)
{
button->setVisible(true);
grid->addWidget(button, button->getRow(), button->getCol());
buttonsInGrid.append(button);
}
}
void Window::buttonClick(const EntryPushButton &config)
{
QProcess::startDetached(config.getCommand(), config.getArguments());
qApp->quit();
}
QStringList Window::generatePATHSuggestions(const QString &text)
{
QStringList results;
QString pathVar = QProcessEnvironment::systemEnvironment().value("PATH", "/usr/bin/:/bin/:");
QStringList paths = pathVar.split(":");
for(const QString &path : paths)
{
QDirIterator it(path);
while(it.hasNext())
{
QFileInfo info(it.next());
if(info.isFile() && info.isExecutable())
{
QString entry = info.baseName();
if(entry.startsWith(text))
{
results.append(entry);
}
}
}
}
return results;
}
void Window::addPATHSuggestion(const QString &text)
{
QStringList suggestions = generatePATHSuggestions(text);
if(suggestions.length() == 1)
{
EntryConfig e;
e.name = suggestions[0];
e.col=0;
e.row=0;
e.command = suggestions[0];
e.icon = QIcon::fromTheme(suggestions[0]);
EntryPushButton *button = createEntryButton(e);
clearGrid();
grid->addWidget(button, 0, 0);
buttonsInGrid.append(button);
}
}
void Window::clearGrid()
{
int count = grid->count();
for(int i = 0; i < count; i++)
{
auto item = grid->itemAt(0)->widget();
grid->removeWidget(item);
item->setVisible(false);
}
buttonsInGrid.clear();
}
void Window::addCalcResult(const QString &expression)
{
clearGrid();
QString calculationresult = calcEngine.evaluate(expression);
QLabel *lbl = new QLabel();
lbl->setText(expression + ": " + calculationresult);
lbl->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
lbl->setAlignment(Qt::AlignCenter);
lbl->setWordWrap(true);
QFont font;
font.setPointSize(48);
font.setBold(true);
lbl->setFont(font);
grid->addWidget(lbl, 0, 0);
}
//main problem here there is no easy event compression (clearing emit queue and only processing the last one)
void Window::lineEditTextChanged(QString text)
{
if(text.length() >= 2)
{
QString input = text.mid(1);
if(text[0] == '=')
{
addCalcResult(input);
return;
}
if(text[0] == '/')
{
if(searchWorker->isOperationPending())
{
this->queuedFileSearch = input;
searchWorker->requestCancellation();
}
else
{
this->queuedFileSearch = "";
emit beginFileSearch(input);
}
return;
}
if(text[0] == '|')
{
if(searchWorker->isOperationPending())
{
this->queuedContentSearch = input;
searchWorker->requestCancellation();
}
else
{
this->queuedContentSearch = "";
emit beginContentSearch(input);
}
return;
}
}
filterGridFor(text);
if(this->grid->count() == 0)
{
addPATHSuggestion(text);
if(this->grid->count() == 0)
{
QStringList arguments = text.split(" ");
EntryConfig e;
e.name = "Execute: " + text;
if(arguments.length() > 1)
{
e.arguments = arguments.mid(1);
}
e.command = arguments[0];
e.icon = QIcon::fromTheme("utilities-terminal");
EntryPushButton *button = createEntryButton(e);
clearGrid();
grid->addWidget(button, 0, 0);
buttonsInGrid.append(button);
}
}
}
void Window::keyReleaseEvent(QKeyEvent *event)
{
if(event->key() == Qt::Key_Control)
{
for(EntryPushButton *button : buttonsInGrid)
{
button->showName();
}
}
QWidget::keyReleaseEvent(event);
}
void Window::keyPressEvent(QKeyEvent *event)
{
bool quit = ((event->modifiers() & Qt::ControlModifier && event->key() == Qt::Key_Q) || event->key() == Qt::Key_Escape);
if(quit)
{
qApp->quit();
}
if(event->modifiers() & Qt::ControlModifier && buttonsInGrid.count() > 0)
{
if(event->key() == Qt::Key_L)
{
this->lineEdit->setFocus();
this->lineEdit->selectAll();
}
for(EntryPushButton *button : buttonsInGrid)
{
button->showShortcut();
}
QKeySequence seq(event->key());
QString key = seq.toString().toLower();
auto it = std::find_if(userEntryButtons.begin(), userEntryButtons.end(), [&key](const EntryPushButton *y) { return y->getShortcutKey() == key; });
if(it != userEntryButtons.end())
{
buttonClick(**it);
}
}
QWidget::keyPressEvent(event);
}
void Window::filterGridFor(QString filter)
{
if(filter.length() > 0)
{
clearGrid();
bool userEntryMatch = false;
for(EntryPushButton *button : this->userEntryButtons)
{
if(button->getName().contains(filter, Qt::CaseInsensitive))
{
button->setVisible(true);
grid->addWidget(button, button->getRow(), button->getCol());
this->buttonsInGrid.append(button);
userEntryMatch = true;
}
}
if(!userEntryMatch)
{
int currow = 0;
int curcol = 0;
int i = 1;
for(EntryPushButton *button : this->systemEntryButtons)
{
if(button->getName().contains(filter, Qt::CaseInsensitive))
{
button->setVisible(true);
button->setShortcut(QString::number(i++));
grid->addWidget(button, currow, curcol++);
this->buttonsInGrid.append(button);
if(curcol == 3)
{
curcol = 0;
++currow;
}
}
}
}
}
else
{
populateGrid(this->userEntryButtons);
}
}
EntryPushButton * Window::createEntryButton(const EntryConfig &entry)
{
EntryPushButton *button = new EntryPushButton(entry);
connect(button, &EntryPushButton::clicked, this, &Window::buttonClick);
return button;
}
void Window::lineEditReturnPressed()
{
if(buttonsInGrid.length() > 0 && this->lineEdit->text().length() > 0 )
{
buttonClick(*buttonsInGrid[0]);
return;
}
if(this->isSearchMode())
{
treeSearchItemActivated(treeFileSearchResults.topLevelItem(0), 0);
}
}
void Window::setSystemConfig(const QVector<EntryConfig> &config)
{
this->systemEntryButtons = generateEntryButtons(config);
}
void Window::handleSearchResults(const QVector<QString> &results)
{
clearGrid();
treeFileSearchResults.clear();
for(const QString &path : results)
{
QFileInfo pathInfo(path);
QString fileName =pathInfo.fileName();
QString absPath = path;
QString datestr = pathInfo.lastModified().toString(Qt::ISODate);
QTreeWidgetItem *item = new QTreeWidgetItem(&treeFileSearchResults);
item->setText(0, fileName);
item->setText(1, absPath);
item->setText(2, datestr);
}
treeFileSearchResults.resizeColumnToContents(0);
treeFileSearchResults.resizeColumnToContents(2);
treeFileSearchResults.setVisible(true);
this->grid->addWidget(&treeFileSearchResults, 0, 0);
}
void Window::handleCancelledSearch()
{
if(this->queuedFileSearch != "")
{
QString searchFor = this->queuedFileSearch;
this->queuedFileSearch = "";
emit beginFileSearch(searchFor);
}
if(this->queuedContentSearch != "")
{
QString searchFor = this->queuedContentSearch;
this->queuedContentSearch = "";
emit beginContentSearch(searchFor);
}
}
void Window::treeSearchItemActivated(QTreeWidgetItem *item, int i)
{
qDebug() << item->text(1);
QDesktopServices::openUrl(QUrl::fromLocalFile(item->text(1)));
}
bool Window::eventFilter(QObject *obj, QEvent *event)
{
if(obj == this->lineEdit)
{
if (event->type() == QEvent::KeyPress)
{
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
if(keyEvent->key() == Qt::Key_Tab)
{
QStringList suggestions = generatePATHSuggestions(this->lineEdit->text());
if(suggestions.length() == 1)
{
this->lineEdit->setText(suggestions[0] + " ");
this->lineEdit->setCursorPosition(this->lineEdit->text().length()+1);
}
return true;
}
}
}
return QObject::eventFilter(obj, event);
}
bool Window::isSearchMode()
{
QChar c = this->lineEdit->text()[0];
return c == '/' || c == '|';
}

68
window.h Normal file
View File

@ -0,0 +1,68 @@
#ifndef WINDOW_H
#define WINDOW_H
#include <QWidget>
#include <QList>
#include <QLineEdit>
#include <QVBoxLayout>
#include <QGridLayout>
#include <QPushButton>
#include <QStyle>
#include <QKeyEvent>
#include <QMessageBox>
#include <QApplication>
#include <QHash>
#include <QVector>
#include <QThread>
#include <QTreeWidget>
#include "config.h"
#include "entrypushbutton.h"
#include "calculationengine.h"
#include "searchworker.h"
class Window : public QWidget
{
Q_OBJECT
private:
QThread searchThread;
SearchWorker *searchWorker;
CalculationEngine calcEngine;
QVector<EntryPushButton*> userEntryButtons;
QVector<EntryPushButton*> systemEntryButtons;
QVector<EntryPushButton *> buttonsInGrid;
QTreeWidget treeFileSearchResults;
QString queuedFileSearch;
QString queuedContentSearch;
void createGui();
void filterGridFor(QString filter);
void populateGrid(const QVector<EntryPushButton *> &list);
void keyReleaseEvent(QKeyEvent *event);
QVector<EntryPushButton *> generateEntryButtons(const QVector<EntryConfig> &userEntryButtons);
void keyPressEvent(QKeyEvent *event);
void buttonClick(const EntryPushButton &config);
QLineEdit *lineEdit;
QGridLayout *grid;
EntryPushButton *createEntryButton(const EntryConfig &config);
void lineEditTextChanged(QString text);
void addPATHSuggestion(const QString &text);
void clearGrid();
void addCalcResult(const QString & expression);
void initTreeWidgets();
QStringList generatePATHSuggestions(const QString &text);
bool isSearchMode();
private slots:
void lineEditReturnPressed();
void handleSearchResults(const QVector<QString> &results);
void handleCancelledSearch();
void treeSearchItemActivated(QTreeWidgetItem *item, int i);
signals:
void beginFileSearch(const QString &query);
void beginContentSearch(const QString &query);
public:
Window(const QVector<EntryConfig> &userEntryButtons, const QString &dbpath);
void setSystemConfig(const QVector<EntryConfig> &config);
bool eventFilter(QObject *obj, QEvent *event);
~Window();
};
#endif