File indexing completed on 2024-04-28 15:29:08

0001 /*
0002     This file is part of the KDE libraries
0003     SPDX-FileCopyrightText: 2005-2006 Olivier Goffart <ogoffart at kde.org>
0004     SPDX-FileCopyrightText: 2013-2014 Martin Klapetek <mklapetek@kde.org>
0005 
0006     code from KNotify/KNotifyClient
0007     SPDX-FileCopyrightText: 1997 Christian Esken <esken@kde.org>
0008     SPDX-FileCopyrightText: 2000 Charles Samuels <charles@kde.org>
0009     SPDX-FileCopyrightText: 2000 Stefan Schimanski <1Stein@gmx.de>
0010     SPDX-FileCopyrightText: 2000 Matthias Ettrich <ettrich@kde.org>
0011     SPDX-FileCopyrightText: 2000 Waldo Bastian <bastian@kde.org>
0012     SPDX-FileCopyrightText: 2000-2003 Carsten Pfeiffer <pfeiffer@kde.org>
0013     SPDX-FileCopyrightText: 2005 Allan Sandfeld Jensen <kde@carewolf.com>
0014 
0015     SPDX-License-Identifier: LGPL-2.0-only
0016 */
0017 
0018 #include "knotification.h"
0019 #include "knotification_p.h"
0020 #include "knotificationmanager_p.h"
0021 #include "knotificationreplyaction.h"
0022 
0023 #include <config-knotifications.h>
0024 
0025 #if HAVE_KWINDOWSYSTEM
0026 #include <KWindowSystem>
0027 #endif
0028 #include <QGuiApplication>
0029 
0030 #include <QStringList>
0031 #ifdef QT_WIDGETS_LIB
0032 #include <QTabWidget>
0033 #endif
0034 #include <QUrl>
0035 
0036 // incremental notification ID
0037 static int notificationIdCounter = 0;
0038 
0039 
0040 #if KNOTIFICATIONS_BUILD_DEPRECATED_SINCE(5, 75)
0041 KNotification::KNotification(const QString &eventId, QWidget *parent, const NotificationFlags &flags)
0042     : QObject(parent)
0043     , d(new Private)
0044 {
0045     d->eventId = eventId;
0046     d->flags = flags;
0047     setWidget(parent);
0048     connect(&d->updateTimer, &QTimer::timeout, this, &KNotification::update);
0049     d->updateTimer.setSingleShot(true);
0050     d->updateTimer.setInterval(100);
0051     d->id = ++notificationIdCounter;
0052 
0053 #if HAVE_KWINDOWSYSTEM
0054     if (KWindowSystem::isPlatformWayland()) {
0055         setHint(QStringLiteral("x-kde-xdgTokenAppId"), QGuiApplication::desktopFileName());
0056     }
0057 #endif
0058 }
0059 #endif
0060 
0061 KNotification::KNotification(const QString &eventId, const NotificationFlags &flags, QObject *parent)
0062     : QObject(parent)
0063     , d(new Private)
0064 {
0065     d->eventId = eventId;
0066     d->flags = flags;
0067     connect(&d->updateTimer, &QTimer::timeout, this, &KNotification::update);
0068     d->updateTimer.setSingleShot(true);
0069     d->updateTimer.setInterval(100);
0070     d->id = ++notificationIdCounter;
0071 
0072 #if HAVE_KWINDOWSYSTEM
0073     if (KWindowSystem::isPlatformWayland()) {
0074         setHint(QStringLiteral("x-kde-xdgTokenAppId"), QGuiApplication::desktopFileName());
0075     }
0076 #endif
0077 }
0078 
0079 KNotification::~KNotification()
0080 {
0081     if (d->id >= 0) {
0082         KNotificationManager::self()->close(d->id);
0083     }
0084 }
0085 
0086 QString KNotification::eventId() const
0087 {
0088     return d->eventId;
0089 }
0090 
0091 void KNotification::setEventId(const QString &eventId)
0092 {
0093     if (d->eventId != eventId) {
0094         d->eventId = eventId;
0095         Q_EMIT eventIdChanged();
0096     }
0097 }
0098 
0099 QString KNotification::title() const
0100 {
0101     return d->title;
0102 }
0103 
0104 QString KNotification::text() const
0105 {
0106     return d->text;
0107 }
0108 
0109 QWidget *KNotification::widget() const
0110 {
0111     return d->widget;
0112 }
0113 
0114 void KNotification::setWidget(QWidget *wid)
0115 {
0116 #ifdef QT_WIDGETS_LIB
0117     d->widget = wid;
0118     //     setParent(wid);
0119     if (wid && d->flags & CloseWhenWidgetActivated) {
0120         wid->installEventFilter(this);
0121     }
0122 #endif
0123 }
0124 
0125 void KNotification::setTitle(const QString &title)
0126 {
0127     if (title == d->title) {
0128         return;
0129     }
0130 
0131     d->needUpdate = true;
0132     d->title = title;
0133     Q_EMIT titleChanged();
0134     if (d->id >= 0) {
0135         d->updateTimer.start();
0136     }
0137 }
0138 
0139 void KNotification::setText(const QString &text)
0140 {
0141     if (text == d->text) {
0142         return;
0143     }
0144 
0145     d->needUpdate = true;
0146     d->text = text;
0147     Q_EMIT textChanged();
0148     if (d->id >= 0) {
0149         d->updateTimer.start();
0150     }
0151 }
0152 
0153 void KNotification::setIconName(const QString &icon)
0154 {
0155     if (icon == d->iconName) {
0156         return;
0157     }
0158 
0159     d->needUpdate = true;
0160     d->iconName = icon;
0161     Q_EMIT iconNameChanged();
0162     if (d->id >= 0) {
0163         d->updateTimer.start();
0164     }
0165 }
0166 
0167 QString KNotification::iconName() const
0168 {
0169     return d->iconName;
0170 }
0171 
0172 QPixmap KNotification::pixmap() const
0173 {
0174     return d->pixmap;
0175 }
0176 
0177 void KNotification::setPixmap(const QPixmap &pix)
0178 {
0179     d->needUpdate = true;
0180     d->pixmap = pix;
0181     if (d->id >= 0) {
0182         d->updateTimer.start();
0183     }
0184 }
0185 
0186 QStringList KNotification::actions() const
0187 {
0188     return d->actions;
0189 }
0190 
0191 void KNotification::setActions(const QStringList &as)
0192 {
0193     if (as == d->actions) {
0194         return;
0195     }
0196 
0197     d->needUpdate = true;
0198     d->actions = as;
0199     Q_EMIT actionsChanged();
0200     if (d->id >= 0) {
0201         d->updateTimer.start();
0202     }
0203 }
0204 
0205 KNotificationReplyAction *KNotification::replyAction() const
0206 {
0207     return d->replyAction.get();
0208 }
0209 
0210 void KNotification::setReplyAction(std::unique_ptr<KNotificationReplyAction> replyAction)
0211 {
0212     if (replyAction == d->replyAction) {
0213         return;
0214     }
0215 
0216     d->needUpdate = true;
0217     d->replyAction = std::move(replyAction);
0218     if (d->id >= 0) {
0219         d->updateTimer.start();
0220     }
0221 }
0222 
0223 void KNotification::setDefaultAction(const QString &defaultAction)
0224 {
0225     if (defaultAction == d->defaultAction) {
0226         return;
0227     }
0228 
0229     d->needUpdate = true;
0230     d->defaultAction = defaultAction;
0231     Q_EMIT defaultActionChanged();
0232     if (d->id >= 0) {
0233         d->updateTimer.start();
0234     }
0235 }
0236 
0237 QString KNotification::defaultAction() const
0238 {
0239     return d->defaultAction;
0240 }
0241 
0242 KNotification::ContextList KNotification::contexts() const
0243 {
0244     return d->contexts;
0245 }
0246 
0247 void KNotification::setContexts(const KNotification::ContextList &contexts)
0248 {
0249     d->contexts = contexts;
0250 }
0251 
0252 void KNotification::addContext(const KNotification::Context &context)
0253 {
0254     d->contexts << context;
0255 }
0256 
0257 void KNotification::addContext(const QString &context_key, const QString &context_value)
0258 {
0259     d->contexts << qMakePair(context_key, context_value);
0260 }
0261 
0262 KNotification::NotificationFlags KNotification::flags() const
0263 {
0264     return d->flags;
0265 }
0266 
0267 void KNotification::setFlags(const NotificationFlags &flags)
0268 {
0269     if (d->flags == flags) {
0270         return;
0271     }
0272 
0273     d->needUpdate = true;
0274     d->flags = flags;
0275     Q_EMIT flagsChanged();
0276     if (d->id >= 0) {
0277         d->updateTimer.start();
0278     }
0279 }
0280 
0281 QString KNotification::componentName() const
0282 {
0283     return d->componentName;
0284 }
0285 
0286 void KNotification::setComponentName(const QString &c)
0287 {
0288     if (d->componentName != c) {
0289         d->componentName = c;
0290         Q_EMIT componentNameChanged();
0291     }
0292 }
0293 
0294 QList<QUrl> KNotification::urls() const
0295 {
0296     return QUrl::fromStringList(d->hints[QStringLiteral("x-kde-urls")].toStringList());
0297 }
0298 
0299 void KNotification::setUrls(const QList<QUrl> &urls)
0300 {
0301     setHint(QStringLiteral("x-kde-urls"), QUrl::toStringList(urls));
0302     Q_EMIT urlsChanged();
0303 }
0304 
0305 KNotification::Urgency KNotification::urgency() const
0306 {
0307     return d->urgency;
0308 }
0309 
0310 void KNotification::setUrgency(Urgency urgency)
0311 {
0312     if (d->urgency == urgency) {
0313         return;
0314     }
0315 
0316     d->needUpdate = true;
0317     d->urgency = urgency;
0318     Q_EMIT urgencyChanged();
0319     if (d->id >= 0) {
0320         d->updateTimer.start();
0321     }
0322 }
0323 
0324 void KNotification::activate(unsigned int action)
0325 {
0326     switch (action) {
0327     case 0:
0328 #if KNOTIFICATIONS_BUILD_DEPRECATED_SINCE(5, 76)
0329         Q_EMIT activated();
0330 #endif
0331         Q_EMIT defaultActivated();
0332         break;
0333     case 1:
0334         Q_EMIT action1Activated();
0335         break;
0336     case 2:
0337         Q_EMIT action2Activated();
0338         break;
0339     case 3:
0340         Q_EMIT action3Activated();
0341         break;
0342     }
0343 
0344     // emitting activated() makes the Manager close all the active plugins
0345     // which will deref() the KNotification object, which will result
0346     // in closing the notification
0347     Q_EMIT activated(action);
0348 }
0349 
0350 void KNotification::close()
0351 {
0352     if (d->id >= 0) {
0353         KNotificationManager::self()->close(d->id);
0354     }
0355 
0356     if (d->id == -1) {
0357         d->id = -2;
0358         Q_EMIT closed();
0359         if (d->autoDelete) {
0360             deleteLater();
0361         } else {
0362             // reset for being reused
0363             d->isNew = true;
0364             d->id = ++notificationIdCounter;
0365         }
0366     }
0367 }
0368 
0369 #if KNOTIFICATIONS_BUILD_DEPRECATED_SINCE(5, 67)
0370 void KNotification::raiseWidget()
0371 {
0372     if (!d->widget) {
0373         return;
0374     }
0375 
0376     d->Private::raiseWidget(d->widget);
0377 }
0378 #endif
0379 
0380 #if KNOTIFICATIONS_BUILD_DEPRECATED_SINCE(5, 67)
0381 void KNotification::Private::raiseWidget(QWidget *w)
0382 {
0383     // TODO  this function is far from finished.
0384     if (w->isTopLevel()) {
0385         w->raise();
0386 #if HAVE_KWINDOWSYSTEM
0387         if (!xdgActivationToken.isEmpty()) {
0388             KWindowSystem::setCurrentXdgActivationToken(xdgActivationToken);
0389         }
0390         KWindowSystem::activateWindow(w->winId());
0391 #endif
0392     } else {
0393         QWidget *pw = w->parentWidget();
0394         raiseWidget(pw);
0395 
0396         if (QTabWidget *tab_widget = qobject_cast<QTabWidget *>(pw)) {
0397             tab_widget->setCurrentIndex(tab_widget->indexOf(w));
0398         }
0399     }
0400 }
0401 #endif
0402 
0403 static QString defaultComponentName()
0404 {
0405 #if defined(Q_OS_ANDROID)
0406     return QStringLiteral("android_defaults");
0407 #else
0408     return QStringLiteral("plasma_workspace");
0409 #endif
0410 }
0411 
0412 KNotification *KNotification::event(const QString &eventid,
0413                                     const QString &title,
0414                                     const QString &text,
0415                                     const QPixmap &pixmap,
0416                                     QWidget *widget,
0417                                     const NotificationFlags &flags,
0418                                     const QString &componentName)
0419 {
0420     KNotification *notify = new KNotification(eventid, flags);
0421     notify->setWidget(widget);
0422     notify->setTitle(title);
0423     notify->setText(text);
0424     notify->setPixmap(pixmap);
0425     notify->setComponentName((flags & DefaultEvent) ? defaultComponentName() : componentName);
0426 
0427     QTimer::singleShot(0, notify, &KNotification::sendEvent);
0428 
0429     return notify;
0430 }
0431 
0432 KNotification *KNotification::event(const QString &eventid,
0433                                     const QString &text,
0434                                     const QPixmap &pixmap,
0435                                     QWidget *widget,
0436                                     const NotificationFlags &flags,
0437                                     const QString &componentName)
0438 {
0439     return event(eventid, QString(), text, pixmap, widget, flags, componentName);
0440 }
0441 
0442 KNotification *
0443 KNotification::event(StandardEvent eventid, const QString &title, const QString &text, const QPixmap &pixmap, QWidget *widget, const NotificationFlags &flags)
0444 {
0445     return event(standardEventToEventId(eventid), title, text, pixmap, widget, flags | DefaultEvent);
0446 }
0447 
0448 KNotification *KNotification::event(StandardEvent eventid, const QString &text, const QPixmap &pixmap, QWidget *widget, const NotificationFlags &flags)
0449 {
0450     return event(eventid, QString(), text, pixmap, widget, flags);
0451 }
0452 
0453 KNotification *KNotification::event(const QString &eventid,
0454                                     const QString &title,
0455                                     const QString &text,
0456                                     const QString &iconName,
0457                                     QWidget *widget,
0458                                     const NotificationFlags &flags,
0459                                     const QString &componentName)
0460 {
0461     KNotification *notify = new KNotification(eventid, flags);
0462     notify->setWidget(widget);
0463     notify->setTitle(title);
0464     notify->setText(text);
0465     notify->setIconName(iconName);
0466     notify->setComponentName((flags & DefaultEvent) ? defaultComponentName() : componentName);
0467 
0468     QTimer::singleShot(0, notify, &KNotification::sendEvent);
0469 
0470     return notify;
0471 }
0472 
0473 KNotification *
0474 KNotification::event(StandardEvent eventid, const QString &title, const QString &text, const QString &iconName, QWidget *widget, const NotificationFlags &flags)
0475 {
0476     return event(standardEventToEventId(eventid), title, text, iconName, widget, flags | DefaultEvent);
0477 }
0478 
0479 KNotification *KNotification::event(StandardEvent eventid, const QString &title, const QString &text, QWidget *widget, const NotificationFlags &flags)
0480 {
0481     return event(standardEventToEventId(eventid), title, text, standardEventToIconName(eventid), widget, flags | DefaultEvent);
0482 }
0483 
0484 void KNotification::ref()
0485 {
0486     d->ref++;
0487 }
0488 void KNotification::deref()
0489 {
0490     Q_ASSERT(d->ref > 0);
0491     d->ref--;
0492     if (d->ref == 0) {
0493         d->id = -1;
0494         close();
0495     }
0496 }
0497 
0498 void KNotification::beep(const QString &reason, QWidget *widget)
0499 {
0500     event(QStringLiteral("beep"), reason, QPixmap(), widget, CloseOnTimeout | DefaultEvent);
0501 }
0502 
0503 void KNotification::sendEvent()
0504 {
0505     d->needUpdate = false;
0506     if (d->isNew) {
0507         d->isNew = false;
0508         KNotificationManager::self()->notify(this);
0509     } else {
0510         KNotificationManager::self()->reemit(this);
0511     }
0512 }
0513 
0514 int KNotification::id()
0515 {
0516     if (!d) {
0517         return -1;
0518     }
0519     return d->id;
0520 }
0521 
0522 QString KNotification::appName() const
0523 {
0524     QString appname;
0525 
0526     if (d->flags & DefaultEvent) {
0527         appname = defaultComponentName();
0528     } else if (!d->componentName.isEmpty()) {
0529         appname = d->componentName;
0530     } else {
0531         appname = QCoreApplication::applicationName();
0532     }
0533 
0534     return appname;
0535 }
0536 
0537 bool KNotification::isAutoDelete() const
0538 {
0539     return d->autoDelete;
0540 }
0541 
0542 void KNotification::setAutoDelete(bool autoDelete)
0543 {
0544     if (d->autoDelete != autoDelete) {
0545         d->autoDelete = autoDelete;
0546         Q_EMIT autoDeleteChanged();
0547     }
0548 }
0549 
0550 void KNotification::update()
0551 {
0552     if (d->needUpdate) {
0553         KNotificationManager::self()->update(this);
0554     }
0555 }
0556 
0557 bool KNotification::eventFilter(QObject *watched, QEvent *event)
0558 {
0559 #ifdef QT_WIDGETS_LIB
0560     if (watched == d->widget) {
0561         if (event->type() == QEvent::WindowActivate) {
0562             if (d->flags & CloseWhenWidgetActivated) {
0563                 QTimer::singleShot(500, this, &KNotification::close);
0564             }
0565         }
0566     }
0567 #endif
0568 
0569     return false;
0570 }
0571 
0572 QString KNotification::standardEventToEventId(KNotification::StandardEvent event)
0573 {
0574     QString eventId;
0575     switch (event) {
0576     case Warning:
0577         eventId = QStringLiteral("warning");
0578         break;
0579     case Error:
0580         eventId = QStringLiteral("fatalerror");
0581         break;
0582     case Catastrophe:
0583         eventId = QStringLiteral("catastrophe");
0584         break;
0585     case Notification: // fall through
0586     default:
0587         eventId = QStringLiteral("notification");
0588         break;
0589     }
0590     return eventId;
0591 }
0592 
0593 QString KNotification::standardEventToIconName(KNotification::StandardEvent event)
0594 {
0595     QString iconName;
0596     switch (event) {
0597     case Warning:
0598         iconName = QStringLiteral("dialog-warning");
0599         break;
0600     case Error:
0601         iconName = QStringLiteral("dialog-error");
0602         break;
0603     case Catastrophe:
0604         iconName = QStringLiteral("dialog-error");
0605         break;
0606     case Notification: // fall through
0607     default:
0608         iconName = QStringLiteral("dialog-information");
0609         break;
0610     }
0611     return iconName;
0612 }
0613 
0614 void KNotification::setHint(const QString &hint, const QVariant &value)
0615 {
0616     if (value == d->hints.value(hint)) {
0617         return;
0618     }
0619 
0620     d->needUpdate = true;
0621     d->hints[hint] = value;
0622     if (d->id >= 0) {
0623         d->updateTimer.start();
0624     }
0625     Q_EMIT hintsChanged();
0626 }
0627 
0628 QVariantMap KNotification::hints() const
0629 {
0630     return d->hints;
0631 }
0632 
0633 void KNotification::setHints(const QVariantMap &hints)
0634 {
0635     if (hints == d->hints) {
0636         return;
0637     }
0638 
0639     d->needUpdate = true;
0640     d->hints = hints;
0641     if (d->id >= 0) {
0642         d->updateTimer.start();
0643     }
0644     Q_EMIT hintsChanged();
0645 }
0646 
0647 QString KNotification::xdgActivationToken() const
0648 {
0649     return d->xdgActivationToken;
0650 }
0651 
0652 #include "moc_knotification.cpp"