File indexing completed on 2023-05-30 09:17:23

0001 /*
0002  * SPDX-FileCopyrightText: 2016 Aleix Pol Gonzalez <aleixpol@kde.org>
0003  *
0004  * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0005  */
0006 
0007 #include <QApplication>
0008 #include <QPointer>
0009 #include <QProcess>
0010 #include <QThread>
0011 
0012 #ifdef QSYSTRAY
0013 #include <QSystemTrayIcon>
0014 #else
0015 #include <KStatusNotifierItem>
0016 #endif
0017 
0018 #include <KAboutData>
0019 #include <KCMultiDialog>
0020 #include <KColorSchemeManager>
0021 #include <KDBusService>
0022 #include <KLocalizedString>
0023 
0024 #include "deviceindicator.h"
0025 #include "interfaces/dbusinterfaces.h"
0026 #include "interfaces/devicesmodel.h"
0027 #include "kdeconnect-version.h"
0028 
0029 #include <dbushelper.h>
0030 
0031 #include "indicatorhelper.h"
0032 
0033 int main(int argc, char **argv)
0034 {
0035     QIcon::setFallbackThemeName(QStringLiteral("breeze"));
0036     QGuiApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
0037     QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
0038 
0039     QApplication app(argc, argv);
0040     KAboutData about(QStringLiteral("kdeconnect-indicator"),
0041                      i18n("KDE Connect Indicator"),
0042                      QStringLiteral(KDECONNECT_VERSION_STRING),
0043                      i18n("KDE Connect Indicator tool"),
0044                      KAboutLicense::GPL,
0045                      i18n("(C) 2016 Aleix Pol Gonzalez"));
0046     KAboutData::setApplicationData(about);
0047 
0048 #ifdef Q_OS_WIN
0049     KColorSchemeManager manager;
0050     QApplication::setStyle(QStringLiteral("breeze"));
0051     IndicatorHelper helper(QUrl::fromLocalFile(qApp->applicationDirPath()));
0052 #else
0053     IndicatorHelper helper;
0054 #endif
0055 
0056     helper.preInit();
0057 
0058     // Run Daemon initialization step
0059     // When run from macOS app bundle, D-Bus call should be later than kdeconnectd and D-Bus daemon
0060     QProcess kdeconnectd;
0061     if (helper.daemonHook(kdeconnectd)) {
0062         return -1;
0063     }
0064 
0065     KDBusService dbusService(KDBusService::Unique);
0066 
0067     // Trigger loading the KIconLoader plugin
0068     about.setProgramLogo(QIcon(QStringLiteral(":/icons/kdeconnect/kdeconnect.svg")));
0069 
0070     DevicesModel model;
0071     model.setDisplayFilter(DevicesModel::Reachable | DevicesModel::Paired);
0072     QMenu *menu = new QMenu;
0073 
0074     QPointer<KCMultiDialog> dialog;
0075 
0076     DaemonDbusInterface iface;
0077 
0078     auto refreshMenu = [&iface, &model, &menu, &helper, &dialog]() {
0079         menu->clear();
0080         auto configure = menu->addAction(QIcon::fromTheme(QStringLiteral("configure")), i18n("Configure..."));
0081         QObject::connect(configure, &QAction::triggered, configure, [&dialog]() {
0082             if (dialog == nullptr) {
0083                 dialog = new KCMultiDialog;
0084                 dialog->addModule(KPluginMetaData(QStringLiteral("plasma/kcms/systemsettings_qwidgets/kcm_kdeconnect")));
0085                 dialog->setAttribute(Qt::WA_DeleteOnClose);
0086                 dialog->show();
0087                 dialog->raise();
0088             } else {
0089                 dialog->raise();
0090                 dialog->activateWindow();
0091             }
0092         });
0093         for (int i = 0, count = model.rowCount(); i < count; ++i) {
0094             DeviceDbusInterface *device = model.getDevice(i);
0095             auto indicator = new DeviceIndicator(device);
0096             QObject::connect(device, &DeviceDbusInterface::destroyed, indicator, &QObject::deleteLater);
0097 
0098             menu->addMenu(indicator);
0099         }
0100         const QStringList requests = iface.pairingRequests();
0101         if (!requests.isEmpty()) {
0102             menu->addSection(i18n("Pairing requests"));
0103 
0104             for (const auto &req : requests) {
0105                 DeviceDbusInterface *dev = new DeviceDbusInterface(req, menu);
0106                 auto pairMenu = menu->addMenu(dev->name());
0107                 pairMenu->addAction(i18n("Pair"), dev, &DeviceDbusInterface::acceptPairing);
0108                 pairMenu->addAction(i18n("Reject"), dev, &DeviceDbusInterface::rejectPairing);
0109             }
0110         }
0111         // Add quit menu
0112 #if defined Q_OS_MAC
0113 
0114         menu->addAction(i18n("Quit"), []() {
0115             auto message = QDBusMessage::createMethodCall(QStringLiteral("org.kde.kdeconnect.daemon"),
0116                                                           QStringLiteral("/MainApplication"),
0117                                                           QStringLiteral("org.qtproject.Qt.QCoreApplication"),
0118                                                           QStringLiteral("quit"));
0119             QDBusConnection::sessionBus().call(message, QDBus::NoBlock);
0120             qApp->quit();
0121         });
0122 #elif defined Q_OS_WIN
0123 
0124         menu->addAction(QIcon::fromTheme(QStringLiteral("application-exit")), i18n("Quit"), []() {
0125             qApp->quit();
0126         });
0127 #endif
0128     };
0129 
0130     QObject::connect(&iface, &DaemonDbusInterface::pairingRequestsChangedProxy, &model, refreshMenu);
0131     QObject::connect(&model, &DevicesModel::rowsInserted, &model, refreshMenu);
0132     QObject::connect(&model, &DevicesModel::rowsRemoved, &model, refreshMenu);
0133 
0134     // Run icon to add icon path (if necessary)
0135     helper.iconPathHook();
0136 
0137 #ifdef QSYSTRAY
0138     QSystemTrayIcon systray;
0139     helper.systrayIconHook(systray);
0140     systray.setVisible(true);
0141     systray.setToolTip(QStringLiteral("KDE Connect"));
0142     QObject::connect(&model, &DevicesModel::rowsChanged, &model, [&systray, &model]() {
0143         systray.setToolTip(i18np("%1 device connected", "%1 devices connected", model.rowCount()));
0144     });
0145     QObject::connect(&systray, &QSystemTrayIcon::activated, [](QSystemTrayIcon::ActivationReason reason) {
0146         if (reason == QSystemTrayIcon::Trigger) {
0147             const QString kdeconnectAppExecutable = QStandardPaths::findExecutable(QStringLiteral("kdeconnect-app"), {QCoreApplication::applicationDirPath()});
0148             if (!kdeconnectAppExecutable.isEmpty()) {
0149                 QProcess::startDetached(kdeconnectAppExecutable, {});
0150             }
0151         }
0152     });
0153 
0154     systray.setContextMenu(menu);
0155 #else
0156     KStatusNotifierItem systray;
0157     helper.systrayIconHook(systray);
0158     systray.setToolTip(QStringLiteral("kdeconnect"), QStringLiteral("KDE Connect"), QStringLiteral("KDE Connect"));
0159     systray.setCategory(KStatusNotifierItem::Communications);
0160     systray.setStatus(KStatusNotifierItem::Passive);
0161     systray.setStandardActionsEnabled(false);
0162     QObject::connect(&model, &DevicesModel::rowsChanged, &model, [&systray, &model]() {
0163         const auto count = model.rowCount();
0164 #ifndef Q_OS_MACOS // On MacOS, setting status to Active disables color theme syncing of the menu icon
0165         systray.setStatus(count == 0 ? KStatusNotifierItem::Passive : KStatusNotifierItem::Active);
0166 #endif
0167         systray.setToolTip(QStringLiteral("kdeconnect"), QStringLiteral("KDE Connect"), i18np("%1 device connected", "%1 devices connected", count));
0168     });
0169 
0170     systray.setContextMenu(menu);
0171 #endif
0172 
0173     refreshMenu();
0174 
0175     app.setQuitOnLastWindowClosed(false);
0176 
0177     // Finish init
0178     helper.postInit();
0179 
0180     return app.exec();
0181 }