16 次代码提交

作者 SHA1 备注 提交日期
2df37212dd EntryPushButton: Don't allow moving dynamic buttons 2020-10-19 21:45:33 +02:00
b13815a924 executeConfig: Open terminal if Shift pressed 2020-10-19 21:45:33 +02:00
bb74d156c9 Introduce new button type 'DYNAMIC' for PATH suggestions
Fixes #23
2020-10-19 21:45:33 +02:00
f9038f3098 EntryProvider: Don't use 'new' when throwing exceptions 2020-10-19 21:45:18 +02:00
042b53a0be Window: addToFavourites(): Discard shortcut from system entries 2020-10-19 21:36:59 +02:00
64d3223fb2 EntryProvider: saveUserEntry: Only save key field if it's not empty 2020-10-19 21:30:11 +02:00
adfb065358 Window: keyPressEvent: don't show empty shortcuts 2020-10-10 22:25:40 +02:00
98cff50c41 main: don't ignore config value for single instance mode. allow override using cli param 2020-10-10 21:58:37 +02:00
5b5333585c SettingsProvider/main: Read single instance socket path from config 2020-10-10 21:58:37 +02:00
8eb52fbfb3 main: allow overriding config dir from commandline 2020-10-10 21:58:37 +02:00
241bec54ad Parse argv using QCommandLineParser
Fixes: #17
2020-10-10 21:58:37 +02:00
ac5498990b EntryProvider: Introduce isSavable() to avoid code duplicates 2020-10-08 22:50:38 +02:00
38cc87fbcd add 'terminal=true' support for .qsrun entries 2020-10-08 22:27:48 +02:00
2d14b01c5c EntryPushButton: Set term icon for terminal cmds w/o icon 2020-10-08 22:27:48 +02:00
59ff382856 Implement basic 'Terminal=' support for .desktop files 2020-10-04 22:19:30 +02:00
bf8f09ec66 Delete TODO
Issue and TODO Tracker resides here:
https://gitea.quitesimple.org/crtxcr/qsrun/issues
2020-10-04 21:57:15 +02:00
共有 8 个文件被更改,包括 113 次插入47 次删除

4
TODO
查看文件

@ -1,4 +0,0 @@
- Investigate memory leaks (relevant now that we have a single user mode,
so qsrun may stay open after launch)
- Provide --help
- ESC/CTRL+Q close the app. May not be expected behaviour

查看文件

@ -13,6 +13,11 @@ EntryProvider::EntryProvider(QStringList userEntriesDirsPaths, QStringList syste
<< "%u"; << "%u";
} }
bool EntryProvider::isSavable(const EntryConfig &config) const
{
return ! config.entryPath.isEmpty() && (config.type == EntryType::USER || config.type == EntryType::INHERIT);
}
EntryConfig EntryProvider::readFromDesktopFile(const QString &path) EntryConfig EntryProvider::readFromDesktopFile(const QString &path)
{ {
EntryConfig result; EntryConfig result;
@ -20,7 +25,7 @@ EntryConfig EntryProvider::readFromDesktopFile(const QString &path)
if(!file.open(QIODevice::ReadOnly | QIODevice::Text)) if(!file.open(QIODevice::ReadOnly | QIODevice::Text))
{ {
// TODO: better exception class // TODO: better exception class
throw new std::runtime_error("Failed to open file"); throw std::runtime_error("Failed to open file");
} }
QTextStream stream(&file); QTextStream stream(&file);
// There should be nothing preceding this group in the desktop entry file but possibly one or more comments. // There should be nothing preceding this group in the desktop entry file but possibly one or more comments.
@ -79,6 +84,10 @@ EntryConfig EntryProvider::readFromDesktopFile(const QString &path)
{ {
result.hidden = args == "true"; result.hidden = args == "true";
} }
if(key == "terminal")
{
result.isTerminalCommand = args == "true";
}
} }
result.type = EntryType::SYSTEM; result.type = EntryType::SYSTEM;
return result; return result;
@ -111,7 +120,7 @@ EntryConfig EntryProvider::readqsrunFile(const QString &path)
if(!file.open(QIODevice::ReadOnly | QIODevice::Text)) if(!file.open(QIODevice::ReadOnly | QIODevice::Text))
{ {
// TODO: better exception class // TODO: better exception class
throw new std::runtime_error("Failed to open file"); throw std::runtime_error("Failed to open file");
} }
QHash<QString, QString> map; QHash<QString, QString> map;
QTextStream stream(&file); QTextStream stream(&file);
@ -122,7 +131,7 @@ EntryConfig EntryProvider::readqsrunFile(const QString &path)
int spacePos = line.indexOf(' '); int spacePos = line.indexOf(' ');
if(spacePos == -1) if(spacePos == -1)
{ {
throw new ConfigFormatException("misformated line in .qsrun config file " + path.toStdString()); throw ConfigFormatException("misformated line in .qsrun config file " + path.toStdString());
} }
QString key = line.mid(0, spacePos); QString key = line.mid(0, spacePos);
@ -130,7 +139,7 @@ EntryConfig EntryProvider::readqsrunFile(const QString &path)
if(key == "" || value == "") if(key == "" || value == "")
{ {
throw new ConfigFormatException("empty key or value in .qsrun config file " + path.toStdString()); throw ConfigFormatException("empty key or value in .qsrun config file " + path.toStdString());
} }
map[key] = value; map[key] = value;
} }
@ -145,7 +154,7 @@ EntryConfig EntryProvider::readqsrunFile(const QString &path)
} }
else else
{ {
throw new ConfigFormatException("Error attempting to read inherited entry"); throw ConfigFormatException("Error attempting to read inherited entry");
} }
} }
QString type = map["type"]; QString type = map["type"];
@ -153,7 +162,7 @@ EntryConfig EntryProvider::readqsrunFile(const QString &path)
{ {
if(type == "system") if(type == "system")
{ {
throw new ConfigFormatException(".qsrun files cannot be designated as system entries " + throw ConfigFormatException(".qsrun files cannot be designated as system entries " +
path.toStdString()); path.toStdString());
} }
else if(type == "inherit") else if(type == "inherit")
@ -166,7 +175,7 @@ EntryConfig EntryProvider::readqsrunFile(const QString &path)
} }
else else
{ {
throw new ConfigFormatException("Invalid value for type provided in file: " + path.toStdString()); throw ConfigFormatException("Invalid value for type provided in file: " + path.toStdString());
} }
} }
else else
@ -219,6 +228,7 @@ EntryConfig EntryProvider::readqsrunFile(const QString &path)
} }
result.col = map["col"].toInt(); result.col = map["col"].toInt();
result.row = map["row"].toInt(); result.row = map["row"].toInt();
result.isTerminalCommand = map["terminal"] == "true";
return result; return result;
} }
@ -280,7 +290,7 @@ QVector<EntryConfig> EntryProvider::getSystemEntries()
void EntryProvider::saveUserEntry(const EntryConfig &config) void EntryProvider::saveUserEntry(const EntryConfig &config)
{ {
if(config.type == EntryType::SYSTEM || config.entryPath.isEmpty()) if(!isSavable(config))
{ {
throw std::runtime_error("Only user/inherited entries can be saved"); throw std::runtime_error("Only user/inherited entries can be saved");
} }
@ -299,7 +309,10 @@ void EntryProvider::saveUserEntry(const EntryConfig &config)
outStream << "row" << " " << config.row << endl; outStream << "row" << " " << config.row << endl;
outStream << "col" << " " << config.col << endl; outStream << "col" << " " << config.col << endl;
outStream << "hidden" << " " << config.hidden << endl; outStream << "hidden" << " " << config.hidden << endl;
if(!config.key.isEmpty())
{
outStream << "key" << " " << config.key << endl; outStream << "key" << " " << config.key << endl;
}
if(config.type == EntryType::USER) if(config.type == EntryType::USER)
{ {
if(!config.name.isEmpty()) if(!config.name.isEmpty())
@ -334,7 +347,7 @@ void EntryProvider::saveUserEntry(const EntryConfig &config)
bool EntryProvider::deleteUserEntry(const EntryConfig &config) bool EntryProvider::deleteUserEntry(const EntryConfig &config)
{ {
if(config.type == EntryType::SYSTEM || config.entryPath.isEmpty()) if(!isSavable(config))
{ {
throw std::runtime_error("Only user/inherited entries can be deleted"); throw std::runtime_error("Only user/inherited entries can be deleted");
} }

查看文件

@ -19,7 +19,8 @@ enum EntryType
{ {
USER, USER,
INHERIT, INHERIT,
SYSTEM SYSTEM,
DYNAMIC
}; };
class EntryConfig class EntryConfig
@ -27,6 +28,7 @@ class EntryConfig
public: public:
EntryType type = SYSTEM; EntryType type = SYSTEM;
bool hidden = false; bool hidden = false;
bool isTerminalCommand = false;
QString entryPath; QString entryPath;
QString key; QString key;
QString name; QString name;
@ -54,6 +56,7 @@ class EntryProvider
public: public:
EntryProvider(QStringList userEntriesDirsPaths, QStringList systemEntriesDirsPaths); EntryProvider(QStringList userEntriesDirsPaths, QStringList systemEntriesDirsPaths);
bool isSavable(const EntryConfig &config) const;
QVector<EntryConfig> getUserEntries(); QVector<EntryConfig> getUserEntries();
QVector<EntryConfig> getSystemEntries(); QVector<EntryConfig> getSystemEntries();
void saveUserEntry(const EntryConfig &config); void saveUserEntry(const EntryConfig &config);

查看文件

@ -23,7 +23,15 @@ EntryPushButton::EntryPushButton(const EntryConfig &config) : QPushButton()
{ {
this->setText(config.name); this->setText(config.name);
this->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); this->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
QIcon icon = resolveIcon(config.iconPath); QIcon icon;
if(config.isTerminalCommand && config.iconPath.isEmpty())
{
icon = resolveIcon("utilities-terminal");
}
else
{
icon = resolveIcon(config.iconPath);
}
this->setIcon(icon); this->setIcon(icon);
if(!icon.availableSizes().isEmpty()) if(!icon.availableSizes().isEmpty())
{ {
@ -98,7 +106,7 @@ void EntryPushButton::mousePressEvent(QMouseEvent *event)
{ {
this->userEntryMenu.exec(QCursor::pos()); this->userEntryMenu.exec(QCursor::pos());
} }
else else if(this->config.type == EntryType::SYSTEM)
{ {
this->systemEntryMenu.exec(QCursor::pos()); this->systemEntryMenu.exec(QCursor::pos());
} }
@ -108,7 +116,7 @@ void EntryPushButton::mousePressEvent(QMouseEvent *event)
void EntryPushButton::mouseMoveEvent(QMouseEvent *event) void EntryPushButton::mouseMoveEvent(QMouseEvent *event)
{ {
if(this->config.type == EntryType::SYSTEM) if(this->config.type == EntryType::SYSTEM || this->config.type == EntryType::DYNAMIC)
{ {
return; return;
} }

查看文件

@ -14,6 +14,7 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/ */
#include <QApplication> #include <QApplication>
#include <QCommandLineParser>
#include <QFuture> #include <QFuture>
#include <QFutureWatcher> #include <QFutureWatcher>
#include <QtConcurrent/QtConcurrentRun> #include <QtConcurrent/QtConcurrentRun>
@ -30,56 +31,76 @@ int main(int argc, char *argv[])
QApplication app(argc, argv); QApplication app(argc, argv);
QString configDirectoryPath; QString configDirectoryPath;
QDir dir; QDir dir;
bool newInstanceRequested = false;
if(argc >= 2) if(argc >= 2)
{ {
configDirectoryPath = QCoreApplication::arguments().at(1); QCommandLineParser parser;
if(!dir.exists(configDirectoryPath)) parser.addOptions({
{"new-instance", "Launch a new instance, ignoring any running ones"},
{"config", "Use supplied config dir instead of default"},
});
parser.addHelpOption();
parser.process(app.arguments());
configDirectoryPath = parser.value("config");
newInstanceRequested = parser.isSet("new-instance");
if(!configDirectoryPath.isEmpty() && !dir.exists(configDirectoryPath))
{ {
QMessageBox::warning(nullptr, "Directory not found", configDirectoryPath + " was not found"); QMessageBox::warning(nullptr, "Directory not found", configDirectoryPath + " was not found");
return 1; return 1;
} }
} }
else if(configDirectoryPath.isEmpty())
{ {
configDirectoryPath = QDir::homePath() + "/.config/qsrun/"; configDirectoryPath = QDir::homePath() + "/.config/qsrun/";
} }
qRegisterMetaType<QVector<QString> >("QVector<QString>");
qRegisterMetaType<QVector<QString>>("QVector<QString>");
if(!dir.exists(configDirectoryPath)) if(!dir.exists(configDirectoryPath))
{ {
if(!dir.mkdir(configDirectoryPath)) if(!dir.mkdir(configDirectoryPath))
{ {
QMessageBox::warning(nullptr, "Failed to create dir", configDirectoryPath + " was not found and could not be created!"); QMessageBox::warning(nullptr, "Failed to create dir",
configDirectoryPath + " was not found and could not be created!");
return 1; return 1;
} }
} }
QSettings settings(configDirectoryPath + "qsrun.config", QSettings::NativeFormat); QSettings settings(configDirectoryPath + "qsrun.config", QSettings::NativeFormat);
SettingsProvider settingsProvider { settings }; SettingsProvider settingsProvider{settings};
EntryProvider entryProvider(settingsProvider.userEntriesPaths(), settingsProvider.systemApplicationsEntriesPaths()); EntryProvider entryProvider(settingsProvider.userEntriesPaths(), settingsProvider.systemApplicationsEntriesPaths());
//TODO if setting single instance mode
SingleInstanceServer *server = nullptr;
bool singleInstanceMode = !newInstanceRequested && settingsProvider.singleInstanceMode();
if(singleInstanceMode)
{
QLocalSocket localSocket; QLocalSocket localSocket;
localSocket.connectToServer("/tmp/qsrun.socket"); localSocket.connectToServer(settingsProvider.socketPath());
SingleInstanceServer server;
if(localSocket.isOpen() && localSocket.isWritable()) if(localSocket.isOpen() && localSocket.isWritable())
{ {
QDataStream stream(&localSocket); QDataStream stream(&localSocket);
stream << (int)0x01; //maximize stream << (int)0x01; // maximize
localSocket.flush(); localSocket.flush();
localSocket.waitForBytesWritten(); localSocket.waitForBytesWritten();
localSocket.disconnectFromServer(); localSocket.disconnectFromServer();
return 0; return 0;
} }
else
{ server = new SingleInstanceServer();
if(!server.listen("/tmp/qsrun.socket")) if(!server->listen(settingsProvider.socketPath()))
{ {
qDebug() << "Failed to listen on socket!"; qDebug() << "Failed to listen on socket!";
return 1;
} }
Window *w = new Window { entryProvider, settingsProvider }; }
QObject::connect(&server, &SingleInstanceServer::receivedMaximizationRequest, [&w]{
Window *w = new Window{entryProvider, settingsProvider};
if(singleInstanceMode && server != nullptr)
{
QObject::connect(server, &SingleInstanceServer::receivedMaximizationRequest, [&w] {
if(w != nullptr) if(w != nullptr)
{ {
qInfo() << "maximizing as requested by other instance"; qInfo() << "maximizing as requested by other instance";
@ -89,12 +110,10 @@ int main(int argc, char *argv[])
w->focusInput(); w->focusInput();
} }
}); });
}
w->showMaximized(); w->showMaximized();
w->focusInput(); w->focusInput();
}
return app.exec(); return app.exec();
} }

查看文件

@ -28,3 +28,13 @@ bool SettingsProvider::singleInstanceMode() const
{ {
return settings->value("singleInstance", true).toBool(); return settings->value("singleInstance", true).toBool();
} }
QString SettingsProvider::getTerminalCommand() const
{
return settings->value("terminal", "/usr/bin/x-terminal-emulator -e %c").toString();
}
QString SettingsProvider::socketPath() const
{
return settings->value("singleInstanceSocket", "/tmp/qsrun").toString();
}

查看文件

@ -15,6 +15,8 @@ class SettingsProvider
virtual QStringList systemApplicationsEntriesPaths() const; virtual QStringList systemApplicationsEntriesPaths() const;
virtual int getMaxCols() const; virtual int getMaxCols() const;
virtual bool singleInstanceMode() const; virtual bool singleInstanceMode() const;
QString getTerminalCommand() const;
QString socketPath() const;
}; };
#endif // SETTINGSPROVIDER_H #endif // SETTINGSPROVIDER_H

查看文件

@ -120,7 +120,16 @@ void Window::populateGrid(const QVector<EntryPushButton *> &list)
void Window::executeConfig(const EntryConfig &config) void Window::executeConfig(const EntryConfig &config)
{ {
if(config.isTerminalCommand || QApplication::keyboardModifiers().testFlag(Qt::ShiftModifier))
{
QString cmd = settingsProvider->getTerminalCommand();
cmd.replace("%c", config.command);
QProcess::startDetached(cmd);
}
else
{
QProcess::startDetached(config.command, config.arguments); QProcess::startDetached(config.command, config.arguments);
}
this->closeWindow(); this->closeWindow();
} }
@ -157,6 +166,7 @@ void Window::addToFavourites(const EntryConfig &config)
when we add it to the favourites. the alternative would be to reload the whole config, when we add it to the favourites. the alternative would be to reload the whole config,
but that's probably overkill. */ but that's probably overkill. */
userConfig.update(config); userConfig.update(config);
userConfig.key = "";
userEntryButtons.append(createEntryButton(userConfig)); userEntryButtons.append(createEntryButton(userConfig));
} }
@ -256,6 +266,7 @@ void Window::addPATHSuggestion(const QString &text)
e.row = 0; e.row = 0;
e.command = suggestions[0]; e.command = suggestions[0];
e.iconPath = suggestions[0]; e.iconPath = suggestions[0];
e.type = EntryType::DYNAMIC;
EntryPushButton *button = createEntryButton(e); EntryPushButton *button = createEntryButton(e);
clearGrid(); clearGrid();
grid->addWidget(button, 0, 0); grid->addWidget(button, 0, 0);
@ -333,6 +344,7 @@ void Window::lineEditTextChanged(QString text)
} }
e.command = arguments[0]; e.command = arguments[0];
e.iconPath = "utilities-terminal"; e.iconPath = "utilities-terminal";
e.type = EntryType::DYNAMIC;
EntryPushButton *button = createEntryButton(e); EntryPushButton *button = createEntryButton(e);
clearGrid(); clearGrid();
@ -371,9 +383,12 @@ void Window::keyPressEvent(QKeyEvent *event)
} }
for(EntryPushButton *button : buttonsInGrid) for(EntryPushButton *button : buttonsInGrid)
{
if(!button->getEntryConfig().key.isEmpty())
{ {
button->showShortcut(); button->showShortcut();
} }
}
QKeySequence seq(event->key()); QKeySequence seq(event->key());
QString key = seq.toString().toLower(); QString key = seq.toString().toLower();