From d8f75c39943f81e32a45c7aa8e66ea71f6e78bf4 Mon Sep 17 00:00:00 2001 From: Julien Wadel Date: Wed, 2 Oct 2024 12:40:45 +0200 Subject: [PATCH] Options : 'auto_start' + 'exit_on_close' --- Linphone/core/App.cpp | 93 +++++++++++++++++-- Linphone/core/App.hpp | 5 + Linphone/core/address-books/LdapCore.hpp | 38 ++++---- Linphone/core/setting/SettingsCore.cpp | 15 +++ Linphone/core/setting/SettingsCore.hpp | 41 ++++---- Linphone/model/setting/SettingsModel.cpp | 8 ++ Linphone/model/setting/SettingsModel.hpp | 1 + Linphone/tool/AbstractObject.hpp | 16 +++- Linphone/tool/Utils.cpp | 3 +- Linphone/view/Page/Window/Main/MainWindow.qml | 1 - 10 files changed, 161 insertions(+), 60 deletions(-) diff --git a/Linphone/core/App.cpp b/Linphone/core/App.cpp index 11e0064c5..1908fe8f3 100644 --- a/Linphone/core/App.cpp +++ b/Linphone/core/App.cpp @@ -22,17 +22,20 @@ #include "App.hpp" +#include #include #include #include #include #include #include +#include #include #include #include #include #include +#include #include #include "core/account/AccountCore.hpp" @@ -122,7 +125,7 @@ bool App::autoStartEnabled() { // check if the Exec part of the autostart ini file not corresponding to our executable (old desktop entry with // wrong version in filename) if (autoStartConf.indexOf(QString("Exec=" + exec + " ")) < - 0) { // On autostart, there is the option --iconified so there is one space. + 0) { // On autostart, there is the option --minimized so there is one space. // replace file setAutoStart(true); } @@ -231,10 +234,10 @@ void App::setAutoStart(bool enabled) { #else void App::setAutoStart(bool enabled) { - if (enabled == mAutoStart) return; - QSettings settings(AutoStartSettingsFilePath, QSettings::NativeFormat); - if (enabled) settings.setValue(EXECUTABLE_NAME, QDir::toNativeSeparators(applicationFilePath())); + QString parameters; + if (!mSettings->getExitOnClose()) parameters = " --minimized"; + if (enabled) settings.setValue(EXECUTABLE_NAME, QDir::toNativeSeparators(applicationFilePath()) + parameters); else settings.remove(EXECUTABLE_NAME); mAutoStart = enabled; @@ -402,7 +405,6 @@ void App::init() { lDebug() << log().arg("Starting Thread"); mLinphoneThread->start(); } - setQuitOnLastWindowClosed(true); // TODO: use settings to set it lInfo() << log().arg("Display server : %1").arg(platformName()); } @@ -468,6 +470,9 @@ void App::initCore() { mAccountList = AccountList::create(); mCallList = CallList::create(); setAutoStart(mSettings->getAutoStart()); + setQuitOnLastWindowClosed(mSettings->getExitOnClose()); + connect(mSettings.get(), &SettingsCore::exitOnCloseChanged, this, &App::onExitOnCloseChanged, + Qt::UniqueConnection); const QUrl url(u"qrc:/Linphone/view/Page/Window/Main/MainWindow.qml"_qs); QObject::connect( @@ -478,9 +483,20 @@ void App::initCore() { lCritical() << log().arg("MainWindow.qml couldn't be load. The app will exit"); exit(-1); } - setMainWindow(qobject_cast(obj)); + auto window = qobject_cast(obj); + setMainWindow(window); QMetaObject::invokeMethod(obj, "initStackViewItem"); - Q_ASSERT(mMainWindow); +#ifndef __APPLE__ + // Enable TrayIconSystem. + if (!QSystemTrayIcon::isSystemTrayAvailable()) + qWarning("System tray not found on this system."); + else setSysTrayIcon(); +#endif // ifndef __APPLE__ + static bool firstOpen = true; + if (!firstOpen || !mParser->isSet("minimized")) { + window->show(); + } + firstOpen = false; } }, Qt::QueuedConnection); @@ -634,7 +650,7 @@ void App::createCommandParser() { tr("commandLineOptionFetchConfigArg")}, {{"c", "call"}, tr("commandLineOptionCall").replace("%1", EXECUTABLE_NAME), tr("commandLineOptionCallArg")}, #ifndef Q_OS_MACOS - {"iconified", tr("commandLineOptionIconified")}, + {"minimized", tr("commandLineOptionMinimized")}, #endif // ifndef Q_OS_MACOS {{"V", "verbose"}, tr("commandLineOptionVerbose")}, {"qt-logs-only", tr("commandLineOptionQtLogsOnly")}, @@ -750,6 +766,12 @@ QSharedPointer App::getSettings() const { return mSettings; } +void App::onExitOnCloseChanged() { + setSysTrayIcon(); // Restore button depends from this option + setQuitOnLastWindowClosed(mSettings->getExitOnClose()); + setAutoStart(mSettings->getAutoStart()); +} + #ifdef Q_OS_LINUX QString App::getApplicationPath() const { const QString binPath(QCoreApplication::applicationFilePath()); @@ -827,7 +849,7 @@ bool App::generateDesktopFile(const QString &confPath, bool remove, bool openInB "GenericName=SIP Phone\n" "Comment=" APPLICATION_DESCRIPTION "\n" "Type=Application\n") - << (openInBackground ? "Exec=" + exec + " --iconified %u\n" : "Exec=" + exec + " %u\n") + << (openInBackground ? "Exec=" + exec + " --minimized %u\n" : "Exec=" + exec + " %u\n") << (haveIcon ? "Icon=" + iconPath + "\n" : "Icon=" EXECUTABLE_NAME "\n") << "Terminal=false\n" "Categories=Network;Telephony;\n" @@ -860,5 +882,56 @@ bool App::event(QEvent *event) { #endif //----------------------------------------------------------- -// AutoStart +// System tray //----------------------------------------------------------- + +void App::setSysTrayIcon() { + QQuickWindow *root = getMainWindow(); + QSystemTrayIcon *systemTrayIcon = + (mSystemTrayIcon + ? mSystemTrayIcon + : new QSystemTrayIcon( + nullptr)); // Workaround : QSystemTrayIcon cannot be deleted because of setContextMenu (indirectly) + + // trayIcon: Right click actions. + QAction *restoreAction = nullptr; + if (!mSettings->getExitOnClose()) { + restoreAction = new QAction(root); + auto setRestoreActionText = [restoreAction](bool visible) { + restoreAction->setText(visible ? tr("Cacher") : tr("Afficher")); + }; + setRestoreActionText(root->isVisible()); + connect(root, &QWindow::visibleChanged, restoreAction, setRestoreActionText); + + root->connect(restoreAction, &QAction::triggered, this, [this, restoreAction](bool checked) { + auto mainWindow = getMainWindow(); + if (mainWindow->isVisible()) { + mainWindow->close(); + } else { + mainWindow->show(); + } + }); + } + + QAction *quitAction = new QAction(tr("Quit"), root); + root->connect(quitAction, &QAction::triggered, this, &App::quit); + + // trayIcon: Left click actions. + QMenu *menu = mSystemTrayIcon ? mSystemTrayIcon->contextMenu() : new QMenu(); + menu->clear(); + menu->setTitle(APPLICATION_NAME); + // Build trayIcon menu. + if (restoreAction) { + menu->addAction(restoreAction); + menu->addSeparator(); + } + menu->addAction(quitAction); + if (!mSystemTrayIcon) + systemTrayIcon->setContextMenu(menu); // This is a Qt bug. We cannot call setContextMenu more than once. So we + // have to keep an instance of the menu. + systemTrayIcon->setIcon(QIcon(Constants::WindowIconPath)); + systemTrayIcon->setToolTip(APPLICATION_NAME); + systemTrayIcon->show(); + if (!mSystemTrayIcon) mSystemTrayIcon = systemTrayIcon; + if (!QSystemTrayIcon::isSystemTrayAvailable()) qInfo() << "System tray is not available"; +} diff --git a/Linphone/core/App.hpp b/Linphone/core/App.hpp index c05ea101f..39ac39240 100644 --- a/Linphone/core/App.hpp +++ b/Linphone/core/App.hpp @@ -34,6 +34,7 @@ class CallGui; class Thread; class Notifier; class QQuickWindow; +class QSystemTrayIcon; class App : public SingleApplication, public AbstractObject { Q_OBJECT @@ -106,6 +107,7 @@ public: void initCppInterfaces(); void restart(); bool autoStartEnabled(); + void setSysTrayIcon(); void onLoggerInitialized(); void sendCommand(); @@ -120,6 +122,8 @@ public: QSharedPointer getCallList() const; QSharedPointer getSettings() const; + void onExitOnCloseChanged(); // Can be used for UniqueConnection + #ifdef Q_OS_LINUX Q_INVOKABLE void exportDesktopFile(); @@ -145,6 +149,7 @@ private: QCommandLineParser *mParser = nullptr; Thread *mLinphoneThread = nullptr; Notifier *mNotifier = nullptr; + QSystemTrayIcon *mSystemTrayIcon = nullptr; QQuickWindow *mMainWindow = nullptr; QQuickWindow *mCallsWindow = nullptr; QSharedPointer mSettings; diff --git a/Linphone/core/address-books/LdapCore.hpp b/Linphone/core/address-books/LdapCore.hpp index 1b6705506..25f414243 100644 --- a/Linphone/core/address-books/LdapCore.hpp +++ b/Linphone/core/address-books/LdapCore.hpp @@ -42,25 +42,25 @@ public: Q_INVOKABLE void save(); Q_INVOKABLE bool isValid(); - DECLARE_CORE_GETSET(bool, enabled, Enabled) - DECLARE_CORE_GETSET(QString, server, Server) - DECLARE_CORE_GETSET(QString, bindDn, BindDn) - DECLARE_CORE_GETSET(QString, password, Password) - DECLARE_CORE_GETSET(linphone::Ldap::AuthMethod, authMethod, AuthMethod) - DECLARE_CORE_GETSET(bool, tls, Tls) - DECLARE_CORE_GETSET(linphone::Ldap::CertVerificationMode, - serverCertificatesVerificationMode, - ServerCertificatesVerificationMode) - DECLARE_CORE_GETSET(QString, baseObject, BaseObject) - DECLARE_CORE_GETSET(QString, filter, Filter) - DECLARE_CORE_GETSET(int, maxResults, MaxResults) - DECLARE_CORE_GETSET(int, timeout, Timeout) - DECLARE_CORE_GETSET(int, delay, Delay) - DECLARE_CORE_GETSET(int, minChars, MinChars) - DECLARE_CORE_GETSET(QString, nameAttribute, NameAttribute) - DECLARE_CORE_GETSET(QString, sipAttribute, SipAttribute) - DECLARE_CORE_GETSET(QString, sipDomain, SipDomain) - DECLARE_CORE_GETSET(linphone::Ldap::DebugLevel, debugLevel, DebugLevel) + DECLARE_CORE_GETSET_MEMBER(bool, enabled, Enabled) + DECLARE_CORE_GETSET_MEMBER(QString, server, Server) + DECLARE_CORE_GETSET_MEMBER(QString, bindDn, BindDn) + DECLARE_CORE_GETSET_MEMBER(QString, password, Password) + DECLARE_CORE_GETSET_MEMBER(linphone::Ldap::AuthMethod, authMethod, AuthMethod) + DECLARE_CORE_GETSET_MEMBER(bool, tls, Tls) + DECLARE_CORE_GETSET_MEMBER(linphone::Ldap::CertVerificationMode, + serverCertificatesVerificationMode, + ServerCertificatesVerificationMode) + DECLARE_CORE_GETSET_MEMBER(QString, baseObject, BaseObject) + DECLARE_CORE_GETSET_MEMBER(QString, filter, Filter) + DECLARE_CORE_GETSET_MEMBER(int, maxResults, MaxResults) + DECLARE_CORE_GETSET_MEMBER(int, timeout, Timeout) + DECLARE_CORE_GETSET_MEMBER(int, delay, Delay) + DECLARE_CORE_GETSET_MEMBER(int, minChars, MinChars) + DECLARE_CORE_GETSET_MEMBER(QString, nameAttribute, NameAttribute) + DECLARE_CORE_GETSET_MEMBER(QString, sipAttribute, SipAttribute) + DECLARE_CORE_GETSET_MEMBER(QString, sipDomain, SipDomain) + DECLARE_CORE_GETSET_MEMBER(linphone::Ldap::DebugLevel, debugLevel, DebugLevel) private: std::shared_ptr mLdapModel; diff --git a/Linphone/core/setting/SettingsCore.cpp b/Linphone/core/setting/SettingsCore.cpp index 9f4a3e210..3bd90579c 100644 --- a/Linphone/core/setting/SettingsCore.cpp +++ b/Linphone/core/setting/SettingsCore.cpp @@ -92,6 +92,7 @@ SettingsCore::SettingsCore(QObject *parent) : QObject(parent) { INIT_CORE_MEMBER(AssistantThirdPartySipAccountDomain, mSettingsModel) INIT_CORE_MEMBER(AssistantThirdPartySipAccountTransport, mSettingsModel) INIT_CORE_MEMBER(AutoStart, mSettingsModel) + INIT_CORE_MEMBER(ExitOnClose, mSettingsModel) INIT_CORE_MEMBER(SyncLdapContacts, mSettingsModel) } @@ -326,6 +327,8 @@ void SettingsCore::setSelf(QSharedPointer me) { assistantThirdPartySipAccountTransport, AssistantThirdPartySipAccountTransport) DEFINE_CORE_GETSET_CONNECT(mSettingsModelConnection, SettingsCore, SettingsModel, mSettingsModel, bool, autoStart, AutoStart) + DEFINE_CORE_GETSET_CONNECT(mSettingsModelConnection, SettingsCore, SettingsModel, mSettingsModel, bool, exitOnClose, + ExitOnClose) DEFINE_CORE_GETSET_CONNECT(mSettingsModelConnection, SettingsCore, SettingsModel, mSettingsModel, bool, syncLdapContacts, SyncLdapContacts) @@ -486,3 +489,15 @@ QString SettingsCore::getLogsFolder() const { bool SettingsCore::dndEnabled() const { return mDndEnabled; } + +bool SettingsCore::getAutoStart() const { + return mAutoStart; +} + +bool SettingsCore::getExitOnClose() const { + return mExitOnClose; +} + +bool SettingsCore::getSyncLdapContacts() const { + return mSyncLdapContacts; +} diff --git a/Linphone/core/setting/SettingsCore.hpp b/Linphone/core/setting/SettingsCore.hpp index e3417e7c9..2bca1f4c0 100644 --- a/Linphone/core/setting/SettingsCore.hpp +++ b/Linphone/core/setting/SettingsCore.hpp @@ -143,31 +143,26 @@ public: bool dndEnabled() const; - DECLARE_CORE_GETSET(bool, disableChatFeature, DisableChatFeature) - DECLARE_CORE_GETSET(bool, disableMeetingsFeature, DisableMeetingsFeature) - DECLARE_CORE_GETSET(bool, disableBroadcastFeature, DisableBroadcastFeature) - DECLARE_CORE_GETSET(bool, hideSettings, HideSettings) - DECLARE_CORE_GETSET(bool, hideAccountSettings, HideAccountSettings) - DECLARE_CORE_GETSET(bool, disableCallRecordings, DisableCallRecordings) - DECLARE_CORE_GETSET(bool, assistantHideCreateAccount, AssistantHideCreateAccount) - DECLARE_CORE_GETSET(bool, assistantDisableQrCode, AssistantDisableQrCode) - DECLARE_CORE_GETSET(bool, assistantHideThirdPartyAccount, AssistantHideThirdPartyAccount) - DECLARE_CORE_GETSET(bool, onlyDisplaySipUriUsername, OnlyDisplaySipUriUsername) - DECLARE_CORE_GETSET(bool, darkModeAllowed, DarkModeAllowed) - DECLARE_CORE_GETSET(int, maxAccount, MaxAccount) - DECLARE_CORE_GETSET(bool, - assistantGoDirectlyToThirdPartySipAccountLogin, - AssistantGoDirectlyToThirdPartySipAccountLogin) - DECLARE_CORE_GETSET(QString, assistantThirdPartySipAccountDomain, AssistantThirdPartySipAccountDomain) - DECLARE_CORE_GETSET(QString, assistantThirdPartySipAccountTransport, AssistantThirdPartySipAccountTransport) + DECLARE_CORE_GETSET_MEMBER(bool, disableChatFeature, DisableChatFeature) + DECLARE_CORE_GETSET_MEMBER(bool, disableMeetingsFeature, DisableMeetingsFeature) + DECLARE_CORE_GETSET_MEMBER(bool, disableBroadcastFeature, DisableBroadcastFeature) + DECLARE_CORE_GETSET_MEMBER(bool, hideSettings, HideSettings) + DECLARE_CORE_GETSET_MEMBER(bool, hideAccountSettings, HideAccountSettings) + DECLARE_CORE_GETSET_MEMBER(bool, disableCallRecordings, DisableCallRecordings) + DECLARE_CORE_GETSET_MEMBER(bool, assistantHideCreateAccount, AssistantHideCreateAccount) + DECLARE_CORE_GETSET_MEMBER(bool, assistantDisableQrCode, AssistantDisableQrCode) + DECLARE_CORE_GETSET_MEMBER(bool, assistantHideThirdPartyAccount, AssistantHideThirdPartyAccount) + DECLARE_CORE_GETSET_MEMBER(bool, onlyDisplaySipUriUsername, OnlyDisplaySipUriUsername) + DECLARE_CORE_GETSET_MEMBER(bool, darkModeAllowed, DarkModeAllowed) + DECLARE_CORE_GETSET_MEMBER(int, maxAccount, MaxAccount) + DECLARE_CORE_GETSET_MEMBER(bool, + assistantGoDirectlyToThirdPartySipAccountLogin, + AssistantGoDirectlyToThirdPartySipAccountLogin) + DECLARE_CORE_GETSET_MEMBER(QString, assistantThirdPartySipAccountDomain, AssistantThirdPartySipAccountDomain) + DECLARE_CORE_GETSET_MEMBER(QString, assistantThirdPartySipAccountTransport, AssistantThirdPartySipAccountTransport) DECLARE_CORE_GETSET(bool, autoStart, AutoStart) - bool getAutoStart() { - return mAutoStart; - }; + DECLARE_CORE_GETSET(bool, exitOnClose, ExitOnClose) DECLARE_CORE_GETSET(bool, syncLdapContacts, SyncLdapContacts) - bool getSyncLdapContacts() { - return mSyncLdapContacts; - }; signals: diff --git a/Linphone/model/setting/SettingsModel.cpp b/Linphone/model/setting/SettingsModel.cpp index abe28714e..1984af4f0 100644 --- a/Linphone/model/setting/SettingsModel.cpp +++ b/Linphone/model/setting/SettingsModel.cpp @@ -527,6 +527,7 @@ void SettingsModel::notifyConfigReady(){ DEFINE_NOTIFY_CONFIG_READY(assistantThirdPartySipAccountDomain, AssistantThirdPartySipAccountDomain) DEFINE_NOTIFY_CONFIG_READY(assistantThirdPartySipAccountTransport, AssistantThirdPartySipAccountTransport) DEFINE_NOTIFY_CONFIG_READY(autoStart, AutoStart) + DEFINE_NOTIFY_CONFIG_READY(exitOnClose, ExitOnClose) DEFINE_NOTIFY_CONFIG_READY(syncLdapContacts, SyncLdapContacts) } @@ -610,6 +611,13 @@ DEFINE_GETSET_CONFIG(SettingsModel, AutoStart, "auto_start", false) +DEFINE_GETSET_CONFIG(SettingsModel, + bool, + Bool, + exitOnClose, + ExitOnClose, + "exit_on_close", + false) DEFINE_GETSET_CONFIG(SettingsModel, bool, Bool, diff --git a/Linphone/model/setting/SettingsModel.hpp b/Linphone/model/setting/SettingsModel.hpp index 88c78816c..860e915d9 100644 --- a/Linphone/model/setting/SettingsModel.hpp +++ b/Linphone/model/setting/SettingsModel.hpp @@ -144,6 +144,7 @@ public: DECLARE_GETSET(QString, assistantThirdPartySipAccountDomain, AssistantThirdPartySipAccountDomain) DECLARE_GETSET(QString, assistantThirdPartySipAccountTransport, AssistantThirdPartySipAccountTransport) DECLARE_GETSET(bool, autoStart, AutoStart) + DECLARE_GETSET(bool, exitOnClose, ExitOnClose) DECLARE_GETSET(bool, syncLdapContacts, SyncLdapContacts) signals: diff --git a/Linphone/tool/AbstractObject.hpp b/Linphone/tool/AbstractObject.hpp index 24476b3e8..ae49f2ac0 100644 --- a/Linphone/tool/AbstractObject.hpp +++ b/Linphone/tool/AbstractObject.hpp @@ -61,12 +61,19 @@ public: } \ } -#define DECLARE_CORE_GETSET(type, x, X) \ +#define DECLARE_CORE_GETSET_MEMBER(type, x, X) \ Q_PROPERTY(type x MEMBER m##X WRITE set##X NOTIFY x##Changed) \ Q_SIGNAL void set##X(type data); \ Q_SIGNAL void x##Changed(); \ type m##X; +#define DECLARE_CORE_GETSET(type, x, X) \ + Q_PROPERTY(type x READ get##X WRITE set##X NOTIFY x##Changed) \ + Q_SIGNAL void set##X(type data); \ + type get##X() const; \ + Q_SIGNAL void x##Changed(); \ + type m##X; + #define DECLARE_CORE_GET(type, x, X) \ Q_PROPERTY(type x MEMBER m##X NOTIFY x##Changed) \ Q_SIGNAL void x##Changed(); \ @@ -123,8 +130,7 @@ public: } \ } -#define DEFINE_NOTIFY_CONFIG_READY(x, X) \ - emit x##Changed(get##X()); +#define DEFINE_NOTIFY_CONFIG_READY(x, X) emit x##Changed(get##X()); #define DECLARE_GETSET_API(type, x, X) \ type get##X() const; \ @@ -145,7 +151,7 @@ public: } #define DEFINE_GETSET_MODEL_STRING(Class, x, X, ownerNotNull) \ -QString Class::get##X() const { \ + QString Class::get##X() const { \ mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); \ return Utils::coreStringToAppString(ownerNotNull->get##X()); \ } \ @@ -158,7 +164,7 @@ QString Class::get##X() const { } #define DEFINE_GETSET_ENABLED(Class, x, X, ownerNotNull) \ -bool Class::get##X() const { \ + bool Class::get##X() const { \ mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); \ return ownerNotNull->x##Enabled(); \ } \ diff --git a/Linphone/tool/Utils.cpp b/Linphone/tool/Utils.cpp index ff3275f37..b2795b9f2 100644 --- a/Linphone/tool/Utils.cpp +++ b/Linphone/tool/Utils.cpp @@ -159,7 +159,6 @@ void Utils::closeCallsWindow() { QQuickWindow *Utils::getMainWindow() { auto win = App::getInstance()->getMainWindow(); - smartShowWindow(win); return win; } @@ -1378,4 +1377,4 @@ QString Utils::boldTextPart(const QString &text, const QString ®ex) { } if (splittedText.size() > 0) result.append(splittedText[splittedText.size() - 1]); return result; -} \ No newline at end of file +} diff --git a/Linphone/view/Page/Window/Main/MainWindow.qml b/Linphone/view/Page/Window/Main/MainWindow.qml index 53df69f90..bc50e4b9f 100644 --- a/Linphone/view/Page/Window/Main/MainWindow.qml +++ b/Linphone/view/Page/Window/Main/MainWindow.qml @@ -10,7 +10,6 @@ import LinphoneAccountsCpp AbstractWindow { id: mainWindow // height: 982 * DefaultStyle.dp - visible: true title: qsTr("Linphone") // TODO : handle this bool when security mode is implemented property bool firstConnection: true