diff --git a/src/ui/fort_images.qrc b/src/ui/fort_images.qrc
index 9a43c405..5d93503b 100644
--- a/src/ui/fort_images.qrc
+++ b/src/ui/fort_images.qrc
@@ -1,5 +1,6 @@
images/cog.png
+ images/shield.png
diff --git a/src/ui/fort_qml.qrc b/src/ui/fort_qml.qrc
index d7ae5175..e356c005 100644
--- a/src/ui/fort_qml.qrc
+++ b/src/ui/fort_qml.qrc
@@ -6,6 +6,7 @@
qml/pages/AddressesPage.qml
qml/pages/ApplicationsPage.qml
qml/pages/BasePage.qml
+ qml/pages/MainPage.qml
qml/pages/OptionsPage.qml
qml/pages/addresses/AddressesColumn.qml
qml/pages/apps/AppsColumn.qml
diff --git a/src/ui/fortmanager.cpp b/src/ui/fortmanager.cpp
index 8d6862d0..fa8459e3 100644
--- a/src/ui/fortmanager.cpp
+++ b/src/ui/fortmanager.cpp
@@ -1,8 +1,11 @@
#include "fortmanager.h"
-#include
+#include
+#include
#include
#include
+#include
+#include
#include "conf/addressgroup.h"
#include "conf/appgroup.h"
@@ -11,13 +14,18 @@
FortManager::FortManager(QObject *parent) :
QObject(parent),
+ m_trayIcon(new QSystemTrayIcon(this)),
+ m_engine(new QQmlApplicationEngine(this)),
m_fortSettings(new FortSettings(qApp->arguments(), this)),
m_firewallConf(new FirewallConf(this)),
- m_firewallConfToEdit(nullptr)
+ m_firewallConfToEdit(nullConf())
{
m_fortSettings->readConf(*m_firewallConf);
registerQmlTypes();
+
+ setupTrayIcon();
+ setupEngine();
}
void FortManager::registerQmlTypes()
@@ -32,27 +40,52 @@ void FortManager::registerQmlTypes()
qmlRegisterType("com.fortfirewall", 1, 0, "FirewallConf");
}
-void FortManager::setupContext()
+void FortManager::setupTrayIcon()
{
- QQmlContext *context = m_engine->rootContext();
+ m_trayIcon->setToolTip(qApp->applicationDisplayName());
+ m_trayIcon->setIcon(QIcon(":/images/shield.png"));
- context->setContextProperty("fortManager", this);
+ connect(m_trayIcon, &QSystemTrayIcon::activated,
+ [this](QSystemTrayIcon::ActivationReason reason) {
+ if (reason == QSystemTrayIcon::Trigger)
+ showWindow();
+ });
+
+ updateTrayMenu();
+}
+
+void FortManager::setupEngine()
+{
+ m_engine->rootContext()->setContextProperty("fortManager", this);
+
+ m_engine->load(QUrl("qrc:/qml/main.qml"));
+
+ m_appWindow = qobject_cast(
+ m_engine->rootObjects().first());
+ Q_ASSERT(m_appWindow);
+}
+
+void FortManager::showTrayIcon()
+{
+ m_trayIcon->show();
}
void FortManager::showWindow()
{
- m_engine = new QQmlApplicationEngine(this);
+ if (m_firewallConfToEdit == nullConf()) {
+ setFirewallConfToEdit(cloneConf(*m_firewallConf));
+ }
- connect(m_engine, &QQmlApplicationEngine::destroyed,
- this, &FortManager::handleClosedWindow);
+ m_appWindow->show();
+ m_appWindow->raise();
+ m_appWindow->requestActivate();
+}
- setupContext();
+void FortManager::closeWindow()
+{
+ m_appWindow->hide();
- // New conf to edit
- Q_ASSERT(!m_firewallConfToEdit);
- m_firewallConfToEdit = cloneConf(*m_firewallConf);
-
- m_engine->load(QUrl("qrc:/qml/main.qml"));
+ setFirewallConfToEdit(nullConf());
}
bool FortManager::saveConf()
@@ -62,10 +95,21 @@ bool FortManager::saveConf()
bool FortManager::applyConf()
{
- Q_ASSERT(m_firewallConfToEdit);
+ Q_ASSERT(m_firewallConfToEdit != nullConf());
return saveSettings(cloneConf(*m_firewallConfToEdit));
}
+void FortManager::setFirewallConfToEdit(FirewallConf *conf)
+{
+ if (m_firewallConfToEdit != nullConf()
+ && m_firewallConfToEdit != m_firewallConf) {
+ m_firewallConfToEdit->deleteLater();
+ }
+
+ m_firewallConfToEdit = conf;
+ emit firewallConfToEditChanged();
+}
+
bool FortManager::saveSettings(FirewallConf *newConf)
{
if (!m_fortSettings->writeConf(*newConf))
@@ -74,20 +118,11 @@ bool FortManager::saveSettings(FirewallConf *newConf)
m_firewallConf->deleteLater();
m_firewallConf = newConf;
+ updateTrayMenu();
+
return true;
}
-void FortManager::handleClosedWindow()
-{
- m_engine->deleteLater();
- m_engine = nullptr;
-
- if (m_firewallConfToEdit && m_firewallConfToEdit != m_firewallConf) {
- m_firewallConfToEdit->deleteLater();
- m_firewallConfToEdit = nullptr;
- }
-}
-
FirewallConf *FortManager::cloneConf(const FirewallConf &conf)
{
FirewallConf *newConf = new FirewallConf(this);
@@ -99,3 +134,50 @@ FirewallConf *FortManager::cloneConf(const FirewallConf &conf)
return newConf;
}
+
+void FortManager::updateTrayMenu()
+{
+ QMenu *menu = m_trayIcon->contextMenu();
+ if (menu) {
+ menu->deleteLater();
+ }
+
+ menu = new QMenu(&m_window);
+
+ addAction(menu, QIcon(), tr("Show"), this, SLOT(showWindow()));
+
+ menu->addSeparator();
+ addAction(menu, QIcon(), tr("Quit"), qApp, SLOT(quit()));
+
+ m_trayIcon->setContextMenu(menu);
+}
+
+QAction *FortManager::addAction(QWidget *widget,
+ const QIcon &icon, const QString &text,
+ const QObject *receiver, const char *member,
+ bool checkable, bool checked)
+{
+ QAction *action = new QAction(icon, text, widget);
+
+ if (receiver) {
+ connect(action, SIGNAL(triggered(bool)), receiver, member);
+ }
+ if (checkable) {
+ setActionCheckable(action, checked);
+ }
+
+ widget->addAction(action);
+
+ return action;
+}
+
+void FortManager::setActionCheckable(QAction *action, bool checked,
+ const QObject *receiver, const char *member)
+{
+ action->setCheckable(true);
+ action->setChecked(checked);
+
+ if (receiver) {
+ connect(action, SIGNAL(toggled(bool)), receiver, member);
+ }
+}
diff --git a/src/ui/fortmanager.h b/src/ui/fortmanager.h
index 59a5f05c..5155d475 100644
--- a/src/ui/fortmanager.h
+++ b/src/ui/fortmanager.h
@@ -2,8 +2,10 @@
#define FORTMANAGER_H
#include
+#include
class QQmlApplicationEngine;
+class QSystemTrayIcon;
class FortSettings;
class FirewallConf;
@@ -12,38 +14,58 @@ class FortManager : public QObject
{
Q_OBJECT
Q_PROPERTY(FortSettings *fortSettings READ fortSettings CONSTANT)
- Q_PROPERTY(FirewallConf *firewallConf READ firewallConf CONSTANT)
- Q_PROPERTY(FirewallConf *firewallConfToEdit READ firewallConfToEdit CONSTANT)
+ Q_PROPERTY(FirewallConf *firewallConfToEdit READ firewallConfToEdit NOTIFY firewallConfToEditChanged)
public:
explicit FortManager(QObject *parent = nullptr);
FortSettings *fortSettings() const { return m_fortSettings; }
- FirewallConf *firewallConf() const { return m_firewallConf; }
- FirewallConf *firewallConfToEdit() const { return m_firewallConfToEdit; }
+
+ FirewallConf *firewallConfToEdit() const {
+ return m_firewallConfToEdit ? m_firewallConfToEdit : m_firewallConf;
+ }
signals:
+ void firewallConfToEditChanged();
public slots:
+ void showTrayIcon();
+
void showWindow();
+ void closeWindow();
bool saveConf();
bool applyConf();
-private slots:
- void handleClosedWindow();
-
private:
+ FirewallConf *nullConf() const { return nullptr; }
+
+ void setFirewallConfToEdit(FirewallConf *conf);
+
static void registerQmlTypes();
- void setupContext();
+ void setupTrayIcon();
+ void setupEngine();
bool saveSettings(FirewallConf *newConf);
FirewallConf *cloneConf(const FirewallConf &conf);
+ void updateTrayMenu();
+
+ static QAction *addAction(QWidget *widget,
+ const QIcon &icon, const QString &text,
+ const QObject *receiver = 0, const char *member = 0,
+ bool checkable = false, bool checked = false);
+ static void setActionCheckable(QAction *action, bool checked = false,
+ const QObject *receiver = 0, const char *member = 0);
+
private:
+ QMainWindow m_window; // dummy window for tray icon
+
+ QSystemTrayIcon *m_trayIcon;
QQmlApplicationEngine *m_engine;
+ QWindow *m_appWindow;
FortSettings *m_fortSettings;
FirewallConf *m_firewallConf;
diff --git a/src/ui/images/shield.png b/src/ui/images/shield.png
new file mode 100644
index 00000000..3cb4e257
Binary files /dev/null and b/src/ui/images/shield.png differ
diff --git a/src/ui/main.cpp b/src/ui/main.cpp
index ac47c659..bfc75e0c 100644
--- a/src/ui/main.cpp
+++ b/src/ui/main.cpp
@@ -1,4 +1,4 @@
-#include
+#include
#include "../common/version.h"
#include "fortmanager.h"
@@ -7,13 +7,13 @@ int main(int argc, char *argv[])
{
QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
- QGuiApplication app(argc, argv);
+ QApplication app(argc, argv);
app.setApplicationName(APP_NAME);
app.setApplicationVersion(APP_VERSION_STR);
app.setApplicationDisplayName(APP_NAME " v" APP_VERSION_STR);
FortManager fortManager;
- fortManager.showWindow();
+ fortManager.showTrayIcon();
return app.exec();
}
diff --git a/src/ui/qml/main.qml b/src/ui/qml/main.qml
index 86664d37..20201e97 100644
--- a/src/ui/qml/main.qml
+++ b/src/ui/qml/main.qml
@@ -1,90 +1,43 @@
import QtQuick 2.9
-import QtQuick.Layouts 1.3
import QtQuick.Dialogs 1.2
import QtQuick.Controls 2.2
import "pages"
import com.fortfirewall 1.0
ApplicationWindow {
- id: mainWindow
+ id: appWindow
width: 800
height: 600
minimumWidth: 700
minimumHeight: 600
- visible: true
font.pixelSize: 16
readonly property FortSettings fortSettings: fortManager.fortSettings
readonly property FirewallConf firewallConf: fortManager.firewallConfToEdit
- Component.onCompleted: {
- tabBar.currentItem.forceActiveFocus();
+ onClosing: {
+ if (visible) {
+ close.accepted = false;
+ closeWindow();
+ }
+ }
+
+ onVisibleChanged: {
+ if (visible) {
+ mainPage.initialize();
+ }
}
function closeWindow() {
- mainWindow.close();
+ fortManager.closeWindow();
}
- Page {
+ MainPage {
+ id: mainPage
anchors.fill: parent
Keys.onEscapePressed: closeWindow()
-
- header: TabBar {
- id: tabBar
- currentIndex: swipeView.currentIndex
-
- TabButton {
- text: QT_TRANSLATE_NOOP("qml", "Options")
- }
- TabButton {
- text: QT_TRANSLATE_NOOP("qml", "IPv4 Addresses")
- }
- TabButton {
- text: QT_TRANSLATE_NOOP("qml", "Applications")
- }
- TabButton {
- text: QT_TRANSLATE_NOOP("qml", "Activity")
- }
- }
-
- SwipeView {
- id: swipeView
- anchors.fill: parent
- currentIndex: tabBar.currentIndex
-
- OptionsPage {}
- AddressesPage {}
- ApplicationsPage {}
- ActivityPage {}
- }
-
- footer: Pane {
- RowLayout {
- anchors.right: parent.right
-
- Button {
- text: QT_TRANSLATE_NOOP("qml", "OK")
- onClicked: {
- if (fortManager.saveConf())
- closeWindow();
- }
- }
- Button {
- text: QT_TRANSLATE_NOOP("qml", "Apply")
- onClicked: fortManager.applyConf()
- }
- Button {
- text: QT_TRANSLATE_NOOP("qml", "Cancel")
- onClicked: closeWindow()
- }
- Button {
- text: QT_TRANSLATE_NOOP("qml", "Quit")
- onClicked: Qt.quit()
- }
- }
- }
}
}
diff --git a/src/ui/qml/pages/MainPage.qml b/src/ui/qml/pages/MainPage.qml
new file mode 100644
index 00000000..6282b8d5
--- /dev/null
+++ b/src/ui/qml/pages/MainPage.qml
@@ -0,0 +1,67 @@
+import QtQuick 2.9
+import QtQuick.Layouts 1.3
+import QtQuick.Controls 2.2
+import com.fortfirewall 1.0
+
+Page {
+ anchors.fill: parent
+
+ function initialize() {
+ tabBar.currentItem.forceActiveFocus();
+ }
+
+ header: TabBar {
+ id: tabBar
+ currentIndex: swipeView.currentIndex
+
+ TabButton {
+ text: QT_TRANSLATE_NOOP("qml", "Options")
+ }
+ TabButton {
+ text: QT_TRANSLATE_NOOP("qml", "IPv4 Addresses")
+ }
+ TabButton {
+ text: QT_TRANSLATE_NOOP("qml", "Applications")
+ }
+ TabButton {
+ text: QT_TRANSLATE_NOOP("qml", "Activity")
+ }
+ }
+
+ SwipeView {
+ id: swipeView
+ anchors.fill: parent
+ currentIndex: tabBar.currentIndex
+
+ OptionsPage {}
+ AddressesPage {}
+ ApplicationsPage {}
+ ActivityPage {}
+ }
+
+ footer: Pane {
+ RowLayout {
+ anchors.right: parent.right
+
+ Button {
+ text: QT_TRANSLATE_NOOP("qml", "OK")
+ onClicked: {
+ if (fortManager.saveConf())
+ closeWindow();
+ }
+ }
+ Button {
+ text: QT_TRANSLATE_NOOP("qml", "Apply")
+ onClicked: fortManager.applyConf()
+ }
+ Button {
+ text: QT_TRANSLATE_NOOP("qml", "Cancel")
+ onClicked: closeWindow()
+ }
+ Button {
+ text: QT_TRANSLATE_NOOP("qml", "Quit")
+ onClicked: Qt.quit()
+ }
+ }
+ }
+}