Warning, file /plasma/plasma-workspace/kcms/notifications/kcm.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /*
0002     SPDX-FileCopyrightText: 2019 Kai Uwe Broulik <kde@privat.broulik.de>
0003 
0004     SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0005 */
0006 
0007 #include "kcm.h"
0008 
0009 #include <QAction>
0010 #include <QCommandLineParser>
0011 #include <QDialog>
0012 #include <QDialogButtonBox>
0013 #include <QPushButton>
0014 #include <QQuickItem>
0015 #include <QQuickRenderControl>
0016 #include <QQuickWindow>
0017 #include <QStandardPaths>
0018 #include <QVBoxLayout>
0019 #include <QWindow>
0020 
0021 #include <KConfigGroup>
0022 #include <KGlobalAccel>
0023 #include <KLocalizedString>
0024 #include <KNotifyConfigWidget>
0025 #include <KPluginFactory>
0026 
0027 #include <algorithm>
0028 
0029 #include "notificationsdata.h"
0030 
0031 #include <libnotificationmanager/badgesettings.h>
0032 #include <libnotificationmanager/behaviorsettings.h>
0033 #include <libnotificationmanager/donotdisturbsettings.h>
0034 #include <libnotificationmanager/jobsettings.h>
0035 #include <libnotificationmanager/notificationsettings.h>
0036 
0037 K_PLUGIN_FACTORY_WITH_JSON(KCMNotificationsFactory, "kcm_notifications.json", registerPlugin<KCMNotifications>(); registerPlugin<NotificationsData>();)
0038 
0039 KCMNotifications::KCMNotifications(QObject *parent, const KPluginMetaData &data, const QVariantList &args)
0040     : KQuickAddons::ManagedConfigModule(parent, data, args)
0041     , m_sourcesModel(new SourcesModel(this))
0042     , m_filteredModel(new FilterProxyModel(this))
0043     , m_data(new NotificationsData(this))
0044     , m_toggleDoNotDisturbAction(new QAction(this))
0045 {
0046     const char uri[] = "org.kde.private.kcms.notifications";
0047     qmlRegisterUncreatableType<SourcesModel>(uri, 1, 0, "SourcesModel", QStringLiteral("Cannot create instances of SourcesModel"));
0048 
0049     qmlRegisterAnonymousType<FilterProxyModel>("FilterProxyModel", 1);
0050     qmlRegisterAnonymousType<QKeySequence>("QKeySequence", 1);
0051     qmlRegisterAnonymousType<NotificationManager::DoNotDisturbSettings>("DoNotDisturbSettings", 1);
0052     qmlRegisterAnonymousType<NotificationManager::NotificationSettings>("NotificationSettings", 1);
0053     qmlRegisterAnonymousType<NotificationManager::JobSettings>("JobSettings", 1);
0054     qmlRegisterAnonymousType<NotificationManager::BadgeSettings>("BadgeSettings", 1);
0055     qmlRegisterAnonymousType<NotificationManager::BehaviorSettings>("BehaviorSettings", 1);
0056     qmlProtectModule(uri, 1);
0057 
0058     m_filteredModel->setSourceModel(m_sourcesModel);
0059 
0060     // for KGlobalAccel...
0061     // keep in sync with globalshortcuts.cpp in notification plasmoid!
0062     m_toggleDoNotDisturbAction->setObjectName(QStringLiteral("toggle do not disturb"));
0063     m_toggleDoNotDisturbAction->setProperty("componentName", QStringLiteral("plasmashell"));
0064     m_toggleDoNotDisturbAction->setText(i18n("Toggle do not disturb"));
0065     m_toggleDoNotDisturbAction->setIcon(QIcon::fromTheme(QStringLiteral("notifications-disabled")));
0066 
0067     QStringList stringArgs;
0068     stringArgs.reserve(args.count() + 1);
0069     // need to add a fake argv[0] for QCommandLineParser
0070     stringArgs.append(QStringLiteral("kcm_notifications"));
0071     for (const QVariant &arg : args) {
0072         stringArgs.append(arg.toString());
0073     }
0074 
0075     QCommandLineParser parser;
0076 
0077     QCommandLineOption desktopEntryOption(QStringLiteral("desktop-entry"), QString(), QStringLiteral("desktop-entry"));
0078     parser.addOption(desktopEntryOption);
0079     QCommandLineOption notifyRcNameOption(QStringLiteral("notifyrc"), QString(), QStringLiteral("notifyrcname"));
0080     parser.addOption(notifyRcNameOption);
0081     QCommandLineOption eventIdOption(QStringLiteral("event-id"), QString(), QStringLiteral("event-id"));
0082     parser.addOption(eventIdOption);
0083 
0084     parser.parse(stringArgs);
0085 
0086     setInitialDesktopEntry(parser.value(desktopEntryOption));
0087     setInitialNotifyRcName(parser.value(notifyRcNameOption));
0088     setInitialEventId(parser.value(eventIdOption));
0089 
0090     connect(this, &KCMNotifications::toggleDoNotDisturbShortcutChanged, this, &KCMNotifications::settingsChanged);
0091     connect(this, &KCMNotifications::defaultsIndicatorsVisibleChanged, this, &KCMNotifications::onDefaultsIndicatorsVisibleChanged);
0092 }
0093 
0094 KCMNotifications::~KCMNotifications()
0095 {
0096 }
0097 
0098 SourcesModel *KCMNotifications::sourcesModel() const
0099 {
0100     return m_sourcesModel;
0101 }
0102 
0103 FilterProxyModel *KCMNotifications::filteredModel() const
0104 {
0105     return m_filteredModel;
0106 }
0107 
0108 NotificationManager::DoNotDisturbSettings *KCMNotifications::dndSettings() const
0109 {
0110     return m_data->dndSettings();
0111 }
0112 
0113 NotificationManager::NotificationSettings *KCMNotifications::notificationSettings() const
0114 {
0115     return m_data->notificationSettings();
0116 }
0117 
0118 NotificationManager::JobSettings *KCMNotifications::jobSettings() const
0119 {
0120     return m_data->jobSettings();
0121 }
0122 
0123 NotificationManager::BadgeSettings *KCMNotifications::badgeSettings() const
0124 {
0125     return m_data->badgeSettings();
0126 }
0127 
0128 QKeySequence KCMNotifications::toggleDoNotDisturbShortcut() const
0129 {
0130     return m_toggleDoNotDisturbShortcut;
0131 }
0132 
0133 void KCMNotifications::setToggleDoNotDisturbShortcut(const QKeySequence &shortcut)
0134 {
0135     if (m_toggleDoNotDisturbShortcut == shortcut) {
0136         return;
0137     }
0138 
0139     m_toggleDoNotDisturbShortcut = shortcut;
0140     m_toggleDoNotDisturbShortcutDirty = true;
0141     Q_EMIT toggleDoNotDisturbShortcutChanged();
0142 }
0143 
0144 QString KCMNotifications::initialDesktopEntry() const
0145 {
0146     return m_initialDesktopEntry;
0147 }
0148 
0149 void KCMNotifications::setInitialDesktopEntry(const QString &desktopEntry)
0150 {
0151     if (m_initialDesktopEntry != desktopEntry) {
0152         m_initialDesktopEntry = desktopEntry;
0153         Q_EMIT initialDesktopEntryChanged();
0154     }
0155 }
0156 
0157 QString KCMNotifications::initialNotifyRcName() const
0158 {
0159     return m_initialNotifyRcName;
0160 }
0161 
0162 void KCMNotifications::setInitialNotifyRcName(const QString &notifyRcName)
0163 {
0164     if (m_initialNotifyRcName != notifyRcName) {
0165         m_initialNotifyRcName = notifyRcName;
0166         Q_EMIT initialNotifyRcNameChanged();
0167     }
0168 }
0169 
0170 QString KCMNotifications::initialEventId() const
0171 {
0172     return m_initialEventId;
0173 }
0174 
0175 void KCMNotifications::setInitialEventId(const QString &eventId)
0176 {
0177     if (m_initialEventId != eventId) {
0178         m_initialEventId = eventId;
0179         Q_EMIT initialEventIdChanged();
0180     }
0181 }
0182 
0183 void KCMNotifications::configureEvents(const QString &notifyRcName, const QString &eventId, QQuickItem *ctx)
0184 {
0185     // We're not using KNotifyConfigWidget::configure here as we want to handle the
0186     // saving ourself (so we Apply with all other KCM settings) but there's no way
0187     // to access the config object :(
0188     // We also need access to the QDialog so we can set the KCM as transient parent.
0189 
0190     QDialog *dialog = new QDialog(nullptr);
0191     dialog->setAttribute(Qt::WA_DeleteOnClose);
0192     dialog->setWindowTitle(i18n("Configure Notifications"));
0193 
0194     if (ctx && ctx->window()) {
0195         dialog->winId(); // so it creates windowHandle
0196         dialog->windowHandle()->setTransientParent(QQuickRenderControl::renderWindowFor(ctx->window()));
0197         dialog->setModal(true);
0198     }
0199 
0200     KNotifyConfigWidget *w = new KNotifyConfigWidget(dialog);
0201 
0202     QDialogButtonBox *buttonBox = new QDialogButtonBox(dialog);
0203     buttonBox->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Apply | QDialogButtonBox::Cancel);
0204     buttonBox->button(QDialogButtonBox::Apply)->setEnabled(false);
0205 
0206     QVBoxLayout *layout = new QVBoxLayout;
0207     layout->addWidget(w);
0208     layout->addWidget(buttonBox);
0209     dialog->setLayout(layout);
0210 
0211     // TODO we should only save settings when clicking Apply in the main UI
0212     connect(buttonBox->button(QDialogButtonBox::Apply), &QPushButton::clicked, w, &KNotifyConfigWidget::save);
0213     connect(buttonBox->button(QDialogButtonBox::Ok), &QPushButton::clicked, w, &KNotifyConfigWidget::save);
0214     connect(w, &KNotifyConfigWidget::changed, buttonBox->button(QDialogButtonBox::Apply), &QPushButton::setEnabled);
0215 
0216     connect(buttonBox, &QDialogButtonBox::accepted, dialog, &QDialog::accept);
0217     connect(buttonBox, &QDialogButtonBox::rejected, dialog, &QDialog::reject);
0218 
0219     w->setApplication(notifyRcName);
0220     w->selectEvent(eventId);
0221 
0222     dialog->show();
0223 }
0224 
0225 NotificationManager::BehaviorSettings *KCMNotifications::behaviorSettings(const QModelIndex &index)
0226 {
0227     if (!index.isValid()) {
0228         return nullptr;
0229     }
0230     return m_data->behaviorSettings(index.row());
0231 }
0232 
0233 bool KCMNotifications::isDefaultsBehaviorSettings() const
0234 {
0235     return m_data->isDefaultsBehaviorSettings();
0236 }
0237 
0238 void KCMNotifications::load()
0239 {
0240     ManagedConfigModule::load();
0241 
0242     bool firstLoad = m_firstLoad;
0243     if (m_firstLoad) {
0244         m_firstLoad = false;
0245         m_sourcesModel->load();
0246 
0247         for (int i = 0; i < m_sourcesModel->rowCount(); ++i) {
0248             const QModelIndex index = m_sourcesModel->index(i, 0);
0249             if (!index.isValid()) {
0250                 continue;
0251             }
0252 
0253             QString typeName;
0254             QString groupName;
0255             if (m_sourcesModel->data(index, SourcesModel::SourceTypeRole) == SourcesModel::ApplicationType) {
0256                 typeName = QStringLiteral("Applications");
0257                 groupName = m_sourcesModel->data(index, SourcesModel::DesktopEntryRole).toString();
0258             } else {
0259                 typeName = QStringLiteral("Services");
0260                 groupName = m_sourcesModel->data(index, SourcesModel::NotifyRcNameRole).toString();
0261             }
0262             auto *toAdd = new NotificationManager::BehaviorSettings(typeName, groupName, this);
0263             m_data->insertBehaviorSettings(index.row(), toAdd);
0264             createConnections(toAdd, index);
0265         }
0266     }
0267 
0268     m_data->loadBehaviorSettings();
0269 
0270     const QKeySequence toggleDoNotDisturbShortcut =
0271         KGlobalAccel::self()
0272             ->globalShortcut(m_toggleDoNotDisturbAction->property("componentName").toString(), m_toggleDoNotDisturbAction->objectName())
0273             .value(0);
0274 
0275     if (m_toggleDoNotDisturbShortcut != toggleDoNotDisturbShortcut) {
0276         m_toggleDoNotDisturbShortcut = toggleDoNotDisturbShortcut;
0277         Q_EMIT toggleDoNotDisturbShortcutChanged();
0278     }
0279 
0280     m_toggleDoNotDisturbShortcutDirty = false;
0281     if (firstLoad) {
0282         Q_EMIT firstLoadDone();
0283     }
0284 }
0285 
0286 void KCMNotifications::save()
0287 {
0288     ManagedConfigModule::save();
0289     m_data->saveBehaviorSettings();
0290 
0291     if (m_toggleDoNotDisturbShortcutDirty) {
0292         // KeySequenceItem will already have checked whether the shortcut is available
0293         KGlobalAccel::self()->setShortcut(m_toggleDoNotDisturbAction, {m_toggleDoNotDisturbShortcut}, KGlobalAccel::NoAutoloading);
0294     }
0295 }
0296 
0297 void KCMNotifications::defaults()
0298 {
0299     ManagedConfigModule::defaults();
0300     m_data->defaultsBehaviorSettings();
0301 
0302     setToggleDoNotDisturbShortcut(QKeySequence());
0303 }
0304 
0305 void KCMNotifications::onDefaultsIndicatorsVisibleChanged()
0306 {
0307     for (int i = 0; i < m_sourcesModel->rowCount(); ++i) {
0308         const QModelIndex index = m_sourcesModel->index(i, 0);
0309         updateModelIsDefaultStatus(index);
0310     }
0311 }
0312 
0313 void KCMNotifications::updateModelIsDefaultStatus(const QModelIndex &index)
0314 {
0315     if (index.isValid()) {
0316         m_sourcesModel->setData(index, behaviorSettings(index)->isDefaults(), SourcesModel::IsDefaultRole);
0317         Q_EMIT isDefaultsBehaviorSettingsChanged();
0318     }
0319 }
0320 
0321 bool KCMNotifications::isSaveNeeded() const
0322 {
0323     return m_toggleDoNotDisturbShortcutDirty || m_data->isSaveNeededBehaviorSettings();
0324 }
0325 
0326 bool KCMNotifications::isDefaults() const
0327 {
0328     return m_data->isDefaultsBehaviorSettings();
0329 }
0330 
0331 void KCMNotifications::createConnections(NotificationManager::BehaviorSettings *settings, const QModelIndex &index)
0332 {
0333     connect(settings, &NotificationManager::BehaviorSettings::ShowPopupsChanged, this, &KCMNotifications::settingsChanged);
0334     connect(settings, &NotificationManager::BehaviorSettings::ShowPopupsInDndModeChanged, this, &KCMNotifications::settingsChanged);
0335     connect(settings, &NotificationManager::BehaviorSettings::ShowInHistoryChanged, this, &KCMNotifications::settingsChanged);
0336     connect(settings, &NotificationManager::BehaviorSettings::ShowBadgesChanged, this, &KCMNotifications::settingsChanged);
0337 
0338     connect(settings, &NotificationManager::BehaviorSettings::ShowPopupsChanged, this, [this, index] {
0339         updateModelIsDefaultStatus(index);
0340     });
0341     connect(settings, &NotificationManager::BehaviorSettings::ShowPopupsInDndModeChanged, this, [this, index] {
0342         updateModelIsDefaultStatus(index);
0343     });
0344     connect(settings, &NotificationManager::BehaviorSettings::ShowInHistoryChanged, this, [this, index] {
0345         updateModelIsDefaultStatus(index);
0346     });
0347     connect(settings, &NotificationManager::BehaviorSettings::ShowBadgesChanged, this, [this, index] {
0348         updateModelIsDefaultStatus(index);
0349     });
0350 }
0351 
0352 #include "kcm.moc"