File indexing completed on 2024-05-12 09:40:44
0001 /* 0002 SPDX-FileCopyrightText: 2008 Dmitry Suzdalev <dimsuz@gmail.com> 0003 SPDX-FileCopyrightText: 2017 David Edmundson <davidedmundson@kde.org> 0004 SPDX-FileCopyrightText: 2018-2019 Kai Uwe Broulik <kde@privat.broulik.de> 0005 0006 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL 0007 */ 0008 0009 #include "notification.h" 0010 #include "notification_p.h" 0011 0012 #include <QDBusArgument> 0013 #include <QDebug> 0014 #include <QImageReader> 0015 #include <QRegularExpression> 0016 #include <QXmlStreamReader> 0017 0018 #include <KApplicationTrader> 0019 #include <KConfig> 0020 #include <KConfigGroup> 0021 #include <KService> 0022 0023 #include "debug.h" 0024 0025 using namespace NotificationManager; 0026 using namespace Qt::StringLiterals; 0027 0028 Notification::Private::Private() 0029 { 0030 } 0031 0032 Notification::Private::~Private() = default; 0033 0034 QString Notification::Private::sanitize(const QString &text) 0035 { 0036 // replace all \ns with <br/> 0037 QString t = text; 0038 0039 t.replace(QLatin1String("\n"), QStringLiteral("<br/>")); 0040 // Now remove all inner whitespace (\ns are already <br/>s) 0041 t = std::move(t).simplified(); 0042 // Finally, check if we don't have multiple <br/>s following, 0043 // can happen for example when "\n \n" is sent, this replaces 0044 // all <br/>s in succession with just one 0045 static const QRegularExpression brExpr(QStringLiteral("<br/>\\s*<br/>(\\s|<br/>)*")); 0046 t.replace(brExpr, QLatin1String("<br/>")); 0047 // This fancy RegExp escapes every occurrence of & since QtQuick Text will blatantly cut off 0048 // text where it finds a stray ampersand. 0049 // Only &{apos, quot, gt, lt, amp}; as well as { character references will be allowed 0050 static const QRegularExpression escapeExpr(QStringLiteral("&(?!(?:apos|quot|[gl]t|amp);|#)")); 0051 t.replace(escapeExpr, QLatin1String("&")); 0052 0053 // Don't bother adding some HTML structure if the body is now empty 0054 if (t.isEmpty()) { 0055 return t; 0056 } 0057 0058 QXmlStreamReader r(QStringLiteral("<html>") + t + QStringLiteral("</html>")); 0059 QString result; 0060 QXmlStreamWriter out(&result); 0061 0062 const QList<QString> allowedTags = {"b", "i", "u", "img", "a", "html", "br", "table", "tr", "td"}; 0063 0064 out.writeStartDocument(); 0065 while (!r.atEnd()) { 0066 r.readNext(); 0067 0068 if (r.tokenType() == QXmlStreamReader::StartElement) { 0069 const QString name = r.name().toString(); 0070 if (!allowedTags.contains(name)) { 0071 continue; 0072 } 0073 out.writeStartElement(name); 0074 if (name == QLatin1String("img")) { 0075 auto src = r.attributes().value("src").toString(); 0076 auto alt = r.attributes().value("alt").toString(); 0077 0078 const QUrl url(src); 0079 if (url.isLocalFile()) { 0080 out.writeAttribute(QStringLiteral("src"), src); 0081 } else { 0082 // image denied for security reasons! Do not copy the image src here! 0083 } 0084 0085 out.writeAttribute(QStringLiteral("alt"), alt); 0086 } 0087 if (name == QLatin1Char('a')) { 0088 out.writeAttribute(QStringLiteral("href"), r.attributes().value("href").toString()); 0089 } 0090 } 0091 0092 if (r.tokenType() == QXmlStreamReader::EndElement) { 0093 const QString name = r.name().toString(); 0094 if (!allowedTags.contains(name)) { 0095 continue; 0096 } 0097 out.writeEndElement(); 0098 } 0099 0100 if (r.tokenType() == QXmlStreamReader::Characters) { 0101 const auto text = r.text().toString(); 0102 out.writeCharacters(text); // this auto escapes chars -> HTML entities 0103 } 0104 } 0105 out.writeEndDocument(); 0106 0107 if (r.hasError()) { 0108 qCWarning(NOTIFICATIONMANAGER) << "Notification to send to backend contains invalid XML: " << r.errorString() << "line" << r.lineNumber() << "col" 0109 << r.columnNumber(); 0110 } 0111 0112 // The Text.StyledText format handles only html3.2 stuff and ' is html4 stuff 0113 // so we need to replace it here otherwise it will not render at all. 0114 result.replace(QLatin1String("'"), QChar('\'')); 0115 0116 return result; 0117 } 0118 0119 QImage Notification::Private::decodeNotificationSpecImageHint(const QDBusArgument &arg) 0120 { 0121 int width, height, rowStride, hasAlpha, bitsPerSample, channels; 0122 QByteArray pixels; 0123 char *ptr; 0124 char *end; 0125 0126 if (arg.currentType() != QDBusArgument::StructureType) { 0127 return QImage(); 0128 } 0129 arg.beginStructure(); 0130 arg >> width >> height >> rowStride >> hasAlpha >> bitsPerSample >> channels >> pixels; 0131 arg.endStructure(); 0132 0133 #define SANITY_CHECK(condition) \ 0134 if (!(condition)) { \ 0135 qCWarning(NOTIFICATIONMANAGER) << "Image decoding sanity check failed on" << #condition; \ 0136 return QImage(); \ 0137 } 0138 0139 SANITY_CHECK(width > 0); 0140 SANITY_CHECK(width < 2048); 0141 SANITY_CHECK(height > 0); 0142 SANITY_CHECK(height < 2048); 0143 SANITY_CHECK(rowStride > 0); 0144 0145 #undef SANITY_CHECK 0146 0147 auto copyLineRGB32 = [](QRgb *dst, const char *src, int width) { 0148 const char *end = src + width * 3; 0149 for (; src != end; ++dst, src += 3) { 0150 *dst = qRgb(src[0], src[1], src[2]); 0151 } 0152 }; 0153 0154 auto copyLineARGB32 = [](QRgb *dst, const char *src, int width) { 0155 const char *end = src + width * 4; 0156 for (; src != end; ++dst, src += 4) { 0157 *dst = qRgba(src[0], src[1], src[2], src[3]); 0158 } 0159 }; 0160 0161 QImage::Format format = QImage::Format_Invalid; 0162 void (*fcn)(QRgb *, const char *, int) = nullptr; 0163 if (bitsPerSample == 8) { 0164 if (channels == 4) { 0165 format = QImage::Format_ARGB32; 0166 fcn = copyLineARGB32; 0167 } else if (channels == 3) { 0168 format = QImage::Format_RGB32; 0169 fcn = copyLineRGB32; 0170 } 0171 } 0172 if (format == QImage::Format_Invalid) { 0173 qCWarning(NOTIFICATIONMANAGER) << "Unsupported image format (hasAlpha:" << hasAlpha << "bitsPerSample:" << bitsPerSample << "channels:" << channels 0174 << ")"; 0175 return QImage(); 0176 } 0177 0178 QImage image(width, height, format); 0179 ptr = pixels.data(); 0180 end = ptr + pixels.length(); 0181 for (int y = 0; y < height; ++y, ptr += rowStride) { 0182 if (ptr + channels * width > end) { 0183 qCWarning(NOTIFICATIONMANAGER) << "Image data is incomplete. y:" << y << "height:" << height; 0184 break; 0185 } 0186 fcn((QRgb *)image.scanLine(y), ptr, width); 0187 } 0188 0189 return image; 0190 } 0191 0192 void Notification::Private::sanitizeImage(QImage &image) 0193 { 0194 if (image.isNull()) { 0195 return; 0196 } 0197 0198 const QSize max = maximumImageSize(); 0199 if (image.size().width() > max.width() || image.size().height() > max.height()) { 0200 image = image.scaled(max, Qt::KeepAspectRatio, Qt::SmoothTransformation); 0201 } 0202 } 0203 0204 void Notification::Private::loadImagePath(const QString &path) 0205 { 0206 // image_path and appIcon should either be a URL with file scheme or the name of a themed icon. 0207 // We're lenient and also allow local paths. 0208 0209 image = QImage(); // clear 0210 icon.clear(); 0211 0212 QUrl imageUrl; 0213 if (path.startsWith(QLatin1Char('/'))) { 0214 imageUrl = QUrl::fromLocalFile(path); 0215 } else if (path.contains(QLatin1Char('/'))) { // bad heuristic to detect a URL 0216 imageUrl = QUrl(path); 0217 0218 if (!imageUrl.isLocalFile()) { 0219 qCDebug(NOTIFICATIONMANAGER) << "Refused to load image from" << path << "which isn't a valid local location."; 0220 return; 0221 } 0222 } 0223 0224 if (!imageUrl.isValid()) { 0225 // try icon path instead; 0226 icon = path; 0227 return; 0228 } 0229 0230 QImageReader reader(imageUrl.toLocalFile()); 0231 reader.setAutoTransform(true); 0232 0233 const QSize imageSize = reader.size(); 0234 if (imageSize.isValid() && (imageSize.width() > maximumImageSize().width() || imageSize.height() > maximumImageSize().height())) { 0235 const QSize thumbnailSize = imageSize.scaled(maximumImageSize(), Qt::KeepAspectRatio); 0236 reader.setScaledSize(thumbnailSize); 0237 } 0238 0239 image = reader.read(); 0240 } 0241 0242 QString Notification::Private::defaultComponentName() 0243 { 0244 // NOTE Keep in sync with KNotification 0245 return QStringLiteral("plasma_workspace"); 0246 } 0247 0248 QSize Notification::Private::maximumImageSize() 0249 { 0250 return QSize(256, 256); 0251 } 0252 0253 KService::Ptr Notification::Private::serviceForDesktopEntry(const QString &desktopEntry) 0254 { 0255 if (desktopEntry.isEmpty()) { 0256 return {}; 0257 } 0258 0259 KService::Ptr service; 0260 0261 if (desktopEntry.startsWith(QLatin1Char('/'))) { 0262 service = KService::serviceByDesktopPath(desktopEntry); 0263 } else { 0264 service = KService::serviceByDesktopName(desktopEntry); 0265 } 0266 0267 if (!service) { 0268 const QString lowerDesktopEntry = desktopEntry.toLower(); 0269 service = KService::serviceByDesktopName(lowerDesktopEntry); 0270 } 0271 0272 // Try if it's a renamed flatpak 0273 if (!service) { 0274 const QString desktopId = desktopEntry + QLatin1String(".desktop"); 0275 0276 const auto services = KApplicationTrader::query([&desktopId](const KService::Ptr &app) -> bool { 0277 const QStringList renamedFrom = app->property<QStringList>(QStringLiteral("X-Flatpak-RenamedFrom")); 0278 return renamedFrom.contains(desktopId); 0279 }); 0280 0281 if (!services.isEmpty()) { 0282 service = services.first(); 0283 } 0284 } 0285 0286 // Try snap instance name. 0287 if (!service) { 0288 const auto services = KApplicationTrader::query([&desktopEntry](const KService::Ptr &app) -> bool { 0289 const QString snapInstanceName = app->property<QString>(QStringLiteral("X-SnapInstanceName")); 0290 return desktopEntry.compare(snapInstanceName, Qt::CaseInsensitive) == 0; 0291 }); 0292 0293 if (!services.isEmpty()) { 0294 service = services.first(); 0295 } 0296 } 0297 0298 return service; 0299 } 0300 0301 void Notification::Private::setDesktopEntry(const QString &desktopEntry) 0302 { 0303 QString serviceName; 0304 0305 configurableService = false; 0306 0307 KService::Ptr service = serviceForDesktopEntry(desktopEntry); 0308 if (service) { 0309 this->desktopEntry = service->desktopEntryName(); 0310 serviceName = service->name(); 0311 applicationIconName = service->icon(); 0312 configurableService = !service->noDisplay(); 0313 } 0314 0315 const bool isDefaultEvent = (notifyRcName == defaultComponentName()); 0316 configurableNotifyRc = false; 0317 if (!notifyRcName.isEmpty()) { 0318 // Check whether the application actually has notifications we can configure 0319 KConfig config(notifyRcName + QStringLiteral(".notifyrc"), KConfig::NoGlobals); 0320 0321 QStringList configSources = 0322 QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("knotifications6/%1.notifyrc").arg(notifyRcName)); 0323 // Keep compatibility with KF5 applications 0324 if (configSources.isEmpty()) { 0325 configSources = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("knotifications5/%1.notifyrc").arg(notifyRcName)); 0326 } 0327 config.addConfigSources(configSources); 0328 0329 KConfigGroup globalGroup(&config, u"Global"_s); 0330 0331 const QString iconName = globalGroup.readEntry("IconName"); 0332 0333 // also only overwrite application icon name for non-default events (or if we don't have a service icon) 0334 if (!iconName.isEmpty() && (!isDefaultEvent || applicationIconName.isEmpty())) { 0335 applicationIconName = iconName; 0336 } 0337 0338 const QRegularExpression regexp(QStringLiteral("^Event/([^/]*)$")); 0339 configurableNotifyRc = !config.groupList().filter(regexp).isEmpty(); 0340 } 0341 0342 // For default events we try to show the application name from the desktop entry if possible 0343 // This will have us show e.g. "Dr Konqi" instead of generic "Plasma Desktop" 0344 // The application may not send an applicationName. Use the name from the desktop entry then 0345 if ((isDefaultEvent || applicationName.isEmpty()) && !serviceName.isEmpty()) { 0346 applicationName = serviceName; 0347 } 0348 } 0349 0350 void Notification::Private::processHints(const QVariantMap &hints) 0351 { 0352 auto end = hints.end(); 0353 0354 notifyRcName = hints.value(QStringLiteral("x-kde-appname")).toString(); 0355 0356 setDesktopEntry(hints.value(QStringLiteral("desktop-entry")).toString()); 0357 0358 // Special override for KDE Connect since the notification is sent by kdeconnectd 0359 // but actually comes from a different app on the phone 0360 const QString applicationDisplayName = hints.value(QStringLiteral("x-kde-display-appname")).toString(); 0361 if (!applicationDisplayName.isEmpty()) { 0362 applicationName = applicationDisplayName; 0363 } 0364 0365 originName = hints.value(QStringLiteral("x-kde-origin-name")).toString(); 0366 0367 eventId = hints.value(QStringLiteral("x-kde-eventId")).toString(); 0368 xdgTokenAppId = hints.value(QStringLiteral("x-kde-xdgTokenAppId")).toString(); 0369 0370 bool ok; 0371 const int urgency = hints.value(QStringLiteral("urgency")).toInt(&ok); // DBus type is actually "byte" 0372 if (ok) { 0373 // FIXME use separate enum again 0374 switch (urgency) { 0375 case 0: 0376 setUrgency(Notifications::LowUrgency); 0377 break; 0378 case 1: 0379 setUrgency(Notifications::NormalUrgency); 0380 break; 0381 case 2: 0382 setUrgency(Notifications::CriticalUrgency); 0383 break; 0384 } 0385 } 0386 0387 resident = hints.value(QStringLiteral("resident")).toBool(); 0388 transient = hints.value(QStringLiteral("transient")).toBool(); 0389 0390 userActionFeedback = hints.value(QStringLiteral("x-kde-user-action-feedback")).toBool(); 0391 if (userActionFeedback) { 0392 // A confirmation of an explicit user interaction is assumed to have been seen by the user. 0393 read = true; 0394 } 0395 0396 urls = QUrl::fromStringList(hints.value(QStringLiteral("x-kde-urls")).toStringList()); 0397 0398 replyPlaceholderText = hints.value(QStringLiteral("x-kde-reply-placeholder-text")).toString(); 0399 replySubmitButtonText = hints.value(QStringLiteral("x-kde-reply-submit-button-text")).toString(); 0400 replySubmitButtonIconName = hints.value(QStringLiteral("x-kde-reply-submit-button-icon-name")).toString(); 0401 0402 category = hints.value(QStringLiteral("category")).toString(); 0403 0404 // Underscored hints was in use in version 1.1 of the spec but has been 0405 // replaced by dashed hints in version 1.2. We need to support it for 0406 // users of the 1.2 version of the spec. 0407 auto it = hints.find(QStringLiteral("image-data")); 0408 if (it == end) { 0409 it = hints.find(QStringLiteral("image_data")); 0410 } 0411 if (it == end) { 0412 // This hint was in use in version 1.0 of the spec but has been 0413 // replaced by "image_data" in version 1.1. We need to support it for 0414 // users of the 1.0 version of the spec. 0415 it = hints.find(QStringLiteral("icon_data")); 0416 } 0417 0418 if (it != end) { 0419 image = decodeNotificationSpecImageHint(it->value<QDBusArgument>()); 0420 } 0421 0422 if (image.isNull()) { 0423 it = hints.find(QStringLiteral("image-path")); 0424 if (it == end) { 0425 it = hints.find(QStringLiteral("image_path")); 0426 } 0427 0428 if (it != end) { 0429 loadImagePath(it->toString()); 0430 } 0431 } 0432 0433 sanitizeImage(image); 0434 } 0435 0436 void Notification::Private::setUrgency(Notifications::Urgency urgency) 0437 { 0438 this->urgency = urgency; 0439 0440 // Critical notifications must not time out 0441 // TODO should we really imply this here and not on the view side? 0442 // are there usecases for critical but can expire? 0443 // "critical updates available"? 0444 if (urgency == Notifications::CriticalUrgency) { 0445 timeout = 0; 0446 } 0447 } 0448 0449 Notification::Notification(uint id) 0450 : d(new Private()) 0451 { 0452 d->id = id; 0453 d->created = QDateTime::currentDateTimeUtc(); 0454 } 0455 0456 Notification::Notification(const Notification &other) 0457 : d(new Private(*other.d)) 0458 { 0459 } 0460 0461 Notification::Notification(Notification &&other) noexcept 0462 : d(other.d) 0463 { 0464 other.d = nullptr; 0465 } 0466 0467 Notification &Notification::operator=(const Notification &other) 0468 { 0469 *d = *other.d; 0470 return *this; 0471 } 0472 0473 Notification &Notification::operator=(Notification &&other) noexcept 0474 { 0475 d = other.d; 0476 other.d = nullptr; 0477 return *this; 0478 } 0479 0480 Notification::~Notification() 0481 { 0482 delete d; 0483 } 0484 0485 uint Notification::id() const 0486 { 0487 return d->id; 0488 } 0489 0490 QString Notification::dBusService() const 0491 { 0492 return d->dBusService; 0493 } 0494 0495 void Notification::setDBusService(const QString &dBusService) 0496 { 0497 d->dBusService = dBusService; 0498 } 0499 0500 QDateTime Notification::created() const 0501 { 0502 return d->created; 0503 } 0504 0505 void Notification::setCreated(const QDateTime &created) 0506 { 0507 d->created = created; 0508 } 0509 0510 QDateTime Notification::updated() const 0511 { 0512 return d->updated; 0513 } 0514 0515 void Notification::resetUpdated() 0516 { 0517 d->updated = QDateTime::currentDateTimeUtc(); 0518 } 0519 0520 bool Notification::read() const 0521 { 0522 return d->read; 0523 } 0524 0525 void Notification::setRead(bool read) 0526 { 0527 d->read = read; 0528 } 0529 0530 QString Notification::summary() const 0531 { 0532 return d->summary; 0533 } 0534 0535 void Notification::setSummary(const QString &summary) 0536 { 0537 d->summary = summary; 0538 } 0539 0540 QString Notification::body() const 0541 { 0542 return d->body; 0543 } 0544 0545 void Notification::setBody(const QString &body) 0546 { 0547 d->rawBody = body; 0548 d->body = Private::sanitize(body.trimmed()); 0549 } 0550 0551 QString Notification::rawBody() const 0552 { 0553 return d->rawBody; 0554 } 0555 0556 QString Notification::icon() const 0557 { 0558 return d->icon; 0559 } 0560 0561 void Notification::setIcon(const QString &icon) 0562 { 0563 d->loadImagePath(icon); 0564 Private::sanitizeImage(d->image); 0565 } 0566 0567 QImage Notification::image() const 0568 { 0569 return d->image; 0570 } 0571 0572 void Notification::setImage(const QImage &image) 0573 { 0574 d->image = image; 0575 } 0576 0577 QString Notification::desktopEntry() const 0578 { 0579 return d->desktopEntry; 0580 } 0581 0582 void Notification::setDesktopEntry(const QString &desktopEntry) 0583 { 0584 d->setDesktopEntry(desktopEntry); 0585 } 0586 0587 QString Notification::notifyRcName() const 0588 { 0589 return d->notifyRcName; 0590 } 0591 0592 QString Notification::eventId() const 0593 { 0594 return d->eventId; 0595 } 0596 0597 QString Notification::applicationName() const 0598 { 0599 return d->applicationName; 0600 } 0601 0602 void Notification::setApplicationName(const QString &applicationName) 0603 { 0604 d->applicationName = applicationName; 0605 } 0606 0607 QString Notification::applicationIconName() const 0608 { 0609 return d->applicationIconName; 0610 } 0611 0612 void Notification::setApplicationIconName(const QString &applicationIconName) 0613 { 0614 d->applicationIconName = applicationIconName; 0615 } 0616 0617 QString Notification::originName() const 0618 { 0619 return d->originName; 0620 } 0621 0622 QStringList Notification::actionNames() const 0623 { 0624 return d->actionNames; 0625 } 0626 0627 QStringList Notification::actionLabels() const 0628 { 0629 return d->actionLabels; 0630 } 0631 0632 bool Notification::hasDefaultAction() const 0633 { 0634 return d->hasDefaultAction; 0635 } 0636 0637 QString Notification::defaultActionLabel() const 0638 { 0639 return d->defaultActionLabel; 0640 } 0641 0642 void Notification::setActions(const QStringList &actions) 0643 { 0644 if (actions.count() % 2 != 0) { 0645 qCWarning(NOTIFICATIONMANAGER) << "List of actions must contain an even number of items, tried to set actions to" << actions; 0646 return; 0647 } 0648 0649 d->hasDefaultAction = false; 0650 d->hasConfigureAction = false; 0651 d->hasReplyAction = false; 0652 0653 QStringList names; 0654 QStringList labels; 0655 0656 for (int i = 0; i < actions.count(); i += 2) { 0657 const QString &name = actions.at(i); 0658 const QString &label = actions.at(i + 1); 0659 0660 if (!d->hasDefaultAction && name == QLatin1String("default")) { 0661 d->hasDefaultAction = true; 0662 d->defaultActionLabel = label; 0663 continue; 0664 } 0665 0666 if (!d->hasConfigureAction && name == QLatin1String("settings")) { 0667 d->hasConfigureAction = true; 0668 d->configureActionLabel = label; 0669 continue; 0670 } 0671 0672 if (!d->hasReplyAction && name == QLatin1String("inline-reply")) { 0673 d->hasReplyAction = true; 0674 d->replyActionLabel = label; 0675 continue; 0676 } 0677 0678 names << name; 0679 labels << label; 0680 } 0681 0682 d->actionNames = names; 0683 d->actionLabels = labels; 0684 } 0685 0686 QList<QUrl> Notification::urls() const 0687 { 0688 return d->urls; 0689 } 0690 0691 void Notification::setUrls(const QList<QUrl> &urls) 0692 { 0693 d->urls = urls; 0694 } 0695 0696 Notifications::Urgency Notification::urgency() const 0697 { 0698 return d->urgency; 0699 } 0700 0701 bool Notification::userActionFeedback() const 0702 { 0703 return d->userActionFeedback; 0704 } 0705 0706 int Notification::timeout() const 0707 { 0708 return d->timeout; 0709 } 0710 0711 void Notification::setTimeout(int timeout) 0712 { 0713 d->timeout = timeout; 0714 } 0715 0716 bool Notification::configurable() const 0717 { 0718 return d->hasConfigureAction || d->configurableNotifyRc || d->configurableService; 0719 } 0720 0721 QString Notification::configureActionLabel() const 0722 { 0723 return d->configureActionLabel; 0724 } 0725 0726 bool Notification::hasReplyAction() const 0727 { 0728 return d->hasReplyAction; 0729 } 0730 0731 QString Notification::replyActionLabel() const 0732 { 0733 return d->replyActionLabel; 0734 } 0735 0736 QString Notification::replyPlaceholderText() const 0737 { 0738 return d->replyPlaceholderText; 0739 } 0740 0741 QString Notification::replySubmitButtonText() const 0742 { 0743 return d->replySubmitButtonText; 0744 } 0745 0746 QString Notification::replySubmitButtonIconName() const 0747 { 0748 return d->replySubmitButtonIconName; 0749 } 0750 0751 QString Notification::category() const 0752 { 0753 return d->category; 0754 } 0755 0756 bool Notification::expired() const 0757 { 0758 return d->expired; 0759 } 0760 0761 void Notification::setExpired(bool expired) 0762 { 0763 d->expired = expired; 0764 } 0765 0766 bool Notification::dismissed() const 0767 { 0768 return d->dismissed; 0769 } 0770 0771 void Notification::setDismissed(bool dismissed) 0772 { 0773 d->dismissed = dismissed; 0774 } 0775 0776 bool Notification::resident() const 0777 { 0778 return d->resident; 0779 } 0780 0781 void Notification::setResident(bool resident) 0782 { 0783 d->resident = resident; 0784 } 0785 0786 bool Notification::transient() const 0787 { 0788 return d->transient; 0789 } 0790 0791 void Notification::setTransient(bool transient) 0792 { 0793 d->transient = transient; 0794 } 0795 0796 QVariantMap Notification::hints() const 0797 { 0798 return d->hints; 0799 } 0800 0801 void Notification::setHints(const QVariantMap &hints) 0802 { 0803 d->hints = hints; 0804 } 0805 0806 void Notification::processHints(const QVariantMap &hints) 0807 { 0808 d->processHints(hints); 0809 }