diff options
author | Simon Rettberg | 2018-09-06 11:32:54 +0200 |
---|---|---|
committer | Simon Rettberg | 2018-09-06 11:32:54 +0200 |
commit | cb93788148785758be72fcb2afe2039e7a882c53 (patch) | |
tree | c2095f4ac5995d53e62bd9e247345a1b60c3bf5a /src/client | |
parent | [client] Fix memory leak (diff) | |
download | pvs2-cb93788148785758be72fcb2afe2039e7a882c53.tar.gz pvs2-cb93788148785758be72fcb2afe2039e7a882c53.tar.xz pvs2-cb93788148785758be72fcb2afe2039e7a882c53.zip |
[client] New addon system for buttons and menu
Instead of integrating workspace switching and screen locking
directly into PVS2, introduce an addon system to insert new
functionality into the toolbar, as buttons or menu entries.
Diffstat (limited to 'src/client')
-rw-r--r-- | src/client/addons/addons.cpp | 235 | ||||
-rw-r--r-- | src/client/addons/addons.h | 25 | ||||
-rw-r--r-- | src/client/toolbar/toolbar.cpp | 110 | ||||
-rw-r--r-- | src/client/toolbar/toolbar.h | 11 |
4 files changed, 291 insertions, 90 deletions
diff --git a/src/client/addons/addons.cpp b/src/client/addons/addons.cpp new file mode 100644 index 0000000..9f4a99f --- /dev/null +++ b/src/client/addons/addons.cpp @@ -0,0 +1,235 @@ +#include "addons.h" + +#include <QDir> +#include <QSettings> +#include <QPushButton> +#include <QAction> +#include <QProcess> +#include <QtDebug> +#include <QRegularExpression> + +#define MKSTR(x) static const QString s_ ## x(#x) + +MKSTR(EVENT); +MKSTR(ADDRESS); +MKSTR(ISLOCAL); +MKSTR(CHECKED); +MKSTR(ENABLED); +MKSTR(VISIBLE); +MKSTR(connected); +MKSTR(disconnected); +MKSTR(init); +MKSTR(clicked); +MKSTR(true); +MKSTR(false); + +static const QSize ICON_SIZE(20, 20); + +QList<Addon*> AddonManager::_addons; + +class Addon +{ +public: + Addon() : button(nullptr), menu(nullptr), wantConnectInfo(false) {} + QPushButton *button; + QAction *menu; + QProcess process; + bool wantConnectInfo; + bool wantInit; + bool runAsync; + QList<QPair<QString, QString>> envir; +}; + +static inline bool toBool(const QString &string) +{ + return string.toLower() == s_true; +} + +static void executeAddon(Addon *addon); + +static void setAddonVisible(Addon *addon, bool visible); + +static void handleAddonOutput(Addon *addon, QRegularExpressionMatchIterator &matches); + +void AddonManager::loadFromPath(const QString &path, QList<QPushButton*> &buttons, QList<QAction*> &menuEntries) +{ + QDir configDir(path); + QFileInfoList fileInfoList = configDir.entryInfoList(QDir::Files, QDir::Name); + QRegularExpression paramRegex("^([A-Z]+)=(.*)$", QRegularExpression::MultilineOption); + + for (QFileInfo fileInfo : fileInfoList) { + QString filePath = fileInfo.absoluteFilePath(); + QSettings setting(filePath, QSettings::IniFormat); + QString caption = setting.value("caption").toString(); // TODO: i18n + QString exec = setting.value("exec").toString(); + QString type = setting.value("type").toString(); + QString tooltip = setting.value("tooltip").toString(); + QIcon icon(setting.value("icon").toString()); + bool checkable = setting.value("checkable").toBool(); + if (exec.isEmpty() || (caption.isEmpty() && icon.isNull())) { + qDebug() << "Ignoring" << filePath << "caption+icon or exec empty"; + continue; + } + if (!QFileInfo(exec).isExecutable() || !QFileInfo(exec).isFile()) { + qDebug() << "Ignoring" << filePath << "since target" << exec << "doesn't exist or isn't an executable file"; + continue; + } + + // Alloc addon + Addon *addon = new Addon(); + // Toggle/click callback + auto toggleFun = [=](bool value) { + addon->envir.append(qMakePair(s_EVENT, s_clicked)); + addon->envir.append(qMakePair(s_CHECKED, (value ? s_true : s_false))); + executeAddon(addon); + }; + if (type == "menu") { + addon->menu = new QAction(caption); + if (!icon.isNull()) { + addon->menu->setIcon(icon); + } + addon->menu->setCheckable(checkable); + addon->menu->setToolTip(tooltip); + menuEntries.append(addon->menu); + addon->menu->connect(addon->menu, &QAction::triggered, toggleFun); + } else if (type == "button") { + addon->button = new QPushButton(caption); + if (!icon.isNull()) { + addon->button->setIcon(icon); + addon->button->setIconSize(ICON_SIZE); + } + addon->button->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::MinimumExpanding); + addon->button->setCheckable(checkable); + addon->button->setToolTip(tooltip); + buttons.append(addon->button); + addon->button->connect(addon->button, &QPushButton::clicked, toggleFun); + } else { + qDebug() << "Ignoring unknown addon type" << type; + delete addon; + continue; + } + _addons.append(addon); + + addon->wantConnectInfo = setting.value("connection-events").toBool(); + addon->wantInit = setting.value("init").toBool(); + addon->runAsync = setting.value("async").toBool(); + // Setup process + addon->process.setProgram(exec); + // Stdin for status updates + addon->process.connect(&addon->process, &QProcess::readyReadStandardOutput, [=]() { + auto lines = addon->process.readAllStandardOutput(); + auto matches = paramRegex.globalMatch(lines); + handleAddonOutput(addon, matches); + }); + // Stderr just for debugging + addon->process.connect(&addon->process, &QProcess::readyReadStandardError, [=]() { + qDebug() << exec << "stderr:" << QString::fromLocal8Bit(addon->process.readAllStandardError()); + }); + } +} + +void AddonManager::initControls() +{ + // Call all init functions + for (auto addon : _addons) { + if (addon->wantInit) { + addon->envir.append(qMakePair(s_EVENT, s_init)); + executeAddon(addon); + } else { + setAddonVisible(addon, true); + } + } +} + +void AddonManager::connectEvent(bool isLocal, const QString &address) +{ + for (auto addon : _addons) { + if (!addon->wantConnectInfo) + continue; + addon->envir.append(qMakePair(s_EVENT, s_connected)); + addon->envir.append(qMakePair(s_ADDRESS, address)); + addon->envir.append(qMakePair(s_ISLOCAL, isLocal ? s_true : s_false)); + executeAddon(addon); + } +} + +void AddonManager::disconnectEvent() +{ + for (auto addon : _addons) { + if (!addon->wantConnectInfo) + continue; + addon->envir.append(qMakePair(s_EVENT, s_disconnected)); + executeAddon(addon); + } +} + +static void executeAddon(Addon *addon) +{ + // Set up environment + auto env = QProcessEnvironment::systemEnvironment(); + for (auto e : addon->envir) { + env.insert(e.first, e.second); + } + addon->envir.clear(); + if (!addon->runAsync) { + // Kill remains + if (addon->process.state() != QProcess::NotRunning) { + addon->process.waitForFinished(500); + } + if (addon->process.state() != QProcess::NotRunning) { + addon->process.close(); + } + } + addon->process.setProcessEnvironment(env); + // Run + if (addon->runAsync) { + addon->process.startDetached(addon->process.program(), QStringList()); + } else { + addon->process.start(); + addon->process.closeWriteChannel(); + } +} + +static void setAddonVisible(Addon *addon, bool newValue) +{ + if (addon->button != nullptr) { + QWidget *p = addon->button->parentWidget(); + bool wasVisible = p != nullptr && addon->button->isVisibleTo(p); + qDebug() << "Visibility" << wasVisible << "->" << newValue; + addon->button->setVisible(newValue); + if (p != nullptr && wasVisible != newValue) { + // Visibility changed -- adjust size of toolbar + int size = (addon->button->width() + 2) * (newValue ? 1 : -1); + qDebug() << "Adding" << size; + p->setFixedWidth(p->width() + size); + } + } else { + addon->menu->setVisible(newValue); + } +} + +static void handleAddonOutput(Addon* addon, QRegularExpressionMatchIterator &matches) +{ + while (matches.hasNext()) { + auto m = matches.next(); + qDebug() << "Match:" << m.captured(0); + auto key = m.captured(1); + auto val = m.captured(2); + bool newValue = toBool(val); + if (key == s_VISIBLE) { + setAddonVisible(addon, newValue); + } else if (key == s_CHECKED) { + if (addon->button != nullptr) { + addon->button->setChecked(newValue); + } else { + addon->menu->setChecked(newValue); + } + } else if (key == s_ENABLED) { + if (addon->button != nullptr) { + addon->button->setEnabled(newValue); + } else { + addon->menu->setEnabled(newValue); + } + } + } +} diff --git a/src/client/addons/addons.h b/src/client/addons/addons.h new file mode 100644 index 0000000..185c399 --- /dev/null +++ b/src/client/addons/addons.h @@ -0,0 +1,25 @@ +#ifndef _ADDONS_H_ +#define _ADDONS_H_ + +#include <QString> +#include <QList> + +class Toolbar; +class QPushButton; +class QAction; + +class Addon; + +class AddonManager +{ +public: + static void loadFromPath(const QString &path, QList<QPushButton*> &buttons, QList<QAction*> &menuEntries); + static void initControls(); + static void connectEvent(bool isLocal, const QString &address); + static void disconnectEvent(); + +private: + static QList<Addon*> _addons; +}; + +#endif diff --git a/src/client/toolbar/toolbar.cpp b/src/client/toolbar/toolbar.cpp index 1640c01..bec8586 100644 --- a/src/client/toolbar/toolbar.cpp +++ b/src/client/toolbar/toolbar.cpp @@ -1,12 +1,3 @@ -/* - * toolbar.cpp - * - * Created on: 21.01.2013 - * Author: sr - */ -#include <QtAlgorithms> -#include <QNetworkInterface> -#include <QProcess> #include "../../shared/settings.h" #include "../net/serverconnection.h" #include "../vnc/vncwindow.h" @@ -14,10 +5,15 @@ #include "../util/util.h" #include "../informationdialog/informationdialog.h" #include "../clientapp/clientapp.h" +#include "../addons/addons.h" #include "toolbar.h" #include "ui_toolbar.h" +#include <QtAlgorithms> +#include <QNetworkInterface> +#include <QProcess> + /** * @brief * @@ -107,23 +103,9 @@ void Toolbar::init() connect(clientApp->connectWindow(), SIGNAL(disconnect()), this, SLOT(onDoDisconnect())); connect(clientApp->connectWindow(), SIGNAL(connected(ServerConnection*)), this, SLOT(onConnected(ServerConnection*))); connect(_ui->btnAttention, SIGNAL(toggled(bool)), this, SLOT(onBtnAttention())); - connect(_ui->btnManager, &QPushButton::toggled, [=](bool pressed) { - this->toggleDesktop(pressed ? Desktop::Manager : Desktop::Primary); - }); /* Setup menu */ - initMenu(); - - updateButtonVisibility(); - - /* setup lock desktop button*/ - bool showLock = clientApp->getSettings()->value("showLockDesktopButton").toBool(); - if (showLock) { - connect(_ui->btnLockDesktop, SIGNAL(clicked()), this, SLOT(onBtnLockDesktop())); - } else { - setFixedWidth(width() - _ui->btnLockDesktop->width()); - _ui->btnLockDesktop->setVisible(false); - } + initButtonsAndMenus(); /* hide attention button while disconnected */ _ui->btnAttention->setVisible(false); @@ -160,8 +142,18 @@ void Toolbar::init() * This function should be called once from the main init() function which in * turn should only be called by the constructor. **/ -void Toolbar::initMenu() +void Toolbar::initButtonsAndMenus() { + QList<QPushButton*> buttons; + QList<QAction*> menus; + auto settings = clientApp->getSettings(); + AddonManager::loadFromPath(settings->value("addonConfigDir", "/opt/openslx/pvs2/addons").toString(), buttons, menus); + // Buttons + for (auto i : buttons) { + _ui->buttonContainer->addWidget(i); + i->setVisible(false); + } + // Menu _menu = new QMenu(this); _acnConnect = new QAction(tr("&Connect..."), this); _acnDisconnect = new QAction(tr("&Disconnect"), this); @@ -173,6 +165,10 @@ void Toolbar::initMenu() _menu->addAction(_acnConnect); _menu->addAction(_acnDisconnect); _menu->addSeparator(); + for (auto i : menus) { + _menu->addAction(i); + i->setVisible(false); + } _menu->addAction(_acnInformation); _menu->addAction(_acnAbout); _menu->addSeparator(); @@ -181,7 +177,7 @@ void Toolbar::initMenu() /* only add a "quit"-button when the configuration allows it. */ - bool allow = clientApp->getSettings()->value("allowClientQuit").toBool(); + bool allow = settings->value("allowClientQuit").toBool(); _acnQuit->setVisible(allow); // Connect the signals @@ -191,26 +187,11 @@ void Toolbar::initMenu() connect(_acnInformation, SIGNAL(triggered()), this, SLOT(showInformationDialog())); connect(_acnAbout, SIGNAL(triggered()), this, SLOT(showAboutDialog())); connect(_acnQuit, SIGNAL(triggered()), this, SLOT(exit())); -} -void Toolbar::updateButtonVisibility() -{ - // "Toggle to manager view" (switch between desktop 1 and 2) - bool showManagerToggle = clientApp->isConfiguredAsManager() || clientApp->isConnectedToLocalManager(); - if (showManagerToggle == _ui->btnManager->isHidden()) { - // Visibility should change - if (showManagerToggle) { - _ui->btnManager->blockSignals(false); - setFixedWidth(width() + _ui->btnManager->width()); - } else { - // Hide "toggle manager" -- assume manager was closed, switch to first desktop where the VM should be running - _ui->btnManager->blockSignals(true); - _ui->btnManager->setChecked(false); - setFixedWidth(width() - _ui->btnManager->width()); - toggleDesktop(Desktop::Primary); - } - _ui->btnManager->setVisible(showManagerToggle); - } + // Delay until bar is visible + QTimer::singleShot(10, [=]() { + AddonManager::initControls(); + }); } /** @@ -382,7 +363,7 @@ void Toolbar::onDisconnected(ServerConnection* connection) _ui->btnAttention->setVisible(false); onBtnAttention(); delayedHideBar(); - QTimer::singleShot(1, this, SLOT(updateButtonVisibility())); + AddonManager::disconnectEvent(); } /** @@ -401,7 +382,7 @@ void Toolbar::onConnected(ServerConnection* connection) _ui->lblStatus->setText(tr("Online")); /* connected, show button */ _ui->btnAttention->setVisible(true); - QTimer::singleShot(1, this, SLOT(updateButtonVisibility())); + AddonManager::connectEvent(connection->isLocalConnection(), connection->getPeerAdress()); // connect(connection, SIGNAL(disconnected(ServerConnection*)), this, SLOT(onDisconnected(ServerConnection*))); connect(connection, SIGNAL(openVnc(const QString&, int, const QString&, bool, bool, const QString&, const int, const QByteArray&)), @@ -501,6 +482,8 @@ void Toolbar::showInformationDialog() InformationDialog* d = new InformationDialog(); d->exec(); d->deleteLater(); + auto b = new QPushButton("sdfadsfgadfgadfgadg"); + this->layout()->addWidget(b); } void Toolbar::onBtnAttention() @@ -515,39 +498,6 @@ void Toolbar::onBtnAttention() } } -/** call script to switch to workspace of the manager */ -void Toolbar::toggleDesktop(Desktop d) -{ - QProcess switchP; - if (d == Desktop::Manager) { - switchP.start("/bin/sh", QStringList() << "/opt/openslx/pvs2/switchToManager.sh"); - } else if (d == Desktop::Primary) { - switchP.start("/bin/sh", QStringList() << "/opt/openslx/pvs2/switchBack.sh"); - } else { - qDebug() << "Invalid toggleDesktop call"; - return; - } - switchP.waitForFinished(); -} - -void Toolbar::onBtnLockDesktop() -{ - if (this->lockDesktopP.state() == QProcess::NotRunning) { - _ui->btnLockDesktop->setEnabled(false); - this->lockDesktopP.start("/bin/sh", QStringList() << "/opt/openslx/pvs2/lockDesktop.sh"); - - QTimer::singleShot(2 * 1000, this, SLOT(enableLockBtn())); - - } else { - qDebug() << "trying to use lockDesktop-process while it is still running"; - } -} - -void Toolbar::enableLockBtn() -{ - _ui->btnLockDesktop->setEnabled(true); -} - void Toolbar::setToolbarPosition(const QRect &availableGeometry) { const QRect primaryScreen = QGuiApplication::primaryScreen()->geometry(); diff --git a/src/client/toolbar/toolbar.h b/src/client/toolbar/toolbar.h index 67e9347..48fbf03 100644 --- a/src/client/toolbar/toolbar.h +++ b/src/client/toolbar/toolbar.h @@ -27,11 +27,6 @@ namespace Ui class Toolbar; } -enum class Desktop { - Primary, - Manager, -}; - class Toolbar : public QWidget { Q_OBJECT @@ -69,8 +64,7 @@ private: bool isManagerPc(); QString identifyMgrIP(); void init(); - void initMenu(); - void toggleDesktop(Desktop d); + void initButtonsAndMenus(); QProcess lockDesktopP; @@ -80,7 +74,6 @@ private slots: void onConnected(ServerConnection* connection); void onServerAttentionChanged(const bool on); void onDoDisconnect(); - void onBtnLockDesktop(); void onBtnAttention(); void exit(); void cameraBlink(); @@ -90,8 +83,6 @@ private slots: void delayedHideBar(); void showAboutDialog(); void showInformationDialog(); - void enableLockBtn(); - void updateButtonVisibility(); void setToolbarPosition(const QRect &geometry); }; |