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