#include "notification.hpp" #include #include #include #include #include #include #include #include #include #include #include "../../core/desktopentry.hpp" #include "../../core/iconimageprovider.hpp" #include "dbusimage.hpp" #include "server.hpp" namespace qs::service::notifications { // NOLINTNEXTLINE(misc-use-internal-linkage) Q_DECLARE_LOGGING_CATEGORY(logNotifications); // server.cpp QString NotificationUrgency::toString(NotificationUrgency::Enum value) { switch (value) { case NotificationUrgency::Low: return "Low"; case NotificationUrgency::Normal: return "Normal"; case NotificationUrgency::Critical: return "Critical"; default: return "Invalid notification urgency"; } } QString NotificationCloseReason::toString(NotificationCloseReason::Enum value) { switch (value) { case NotificationCloseReason::Expired: return "Expired"; case NotificationCloseReason::Dismissed: return "Dismissed"; case NotificationCloseReason::CloseRequested: return "CloseRequested"; default: return "Invalid notification close reason"; } } QString NotificationAction::identifier() const { return this->mIdentifier; } QString NotificationAction::text() const { return this->mText; } void NotificationAction::invoke() { if (this->notification->isRetained()) { qCritical() << "Cannot invoke destroyed notification" << this; return; } NotificationServer::instance()->ActionInvoked(this->notification->id(), this->mIdentifier); if (!this->notification->resident()) { this->notification->close(NotificationCloseReason::Dismissed); } } void NotificationAction::setText(const QString& text) { if (text != this->mText) return; this->mText = text; emit this->textChanged(); } void Notification::expire() { this->close(NotificationCloseReason::Expired); } void Notification::dismiss() { this->close(NotificationCloseReason::Dismissed); } void Notification::close(NotificationCloseReason::Enum reason) { if (this->isRetained()) { qCritical() << "Cannot close destroyed notification" << this; return; } this->mCloseReason = reason; if (reason != 0) { NotificationServer::instance()->deleteNotification(this, reason); } } void Notification::updateProperties( const QString& appName, QString appIcon, const QString& summary, const QString& body, const QStringList& actions, QVariantMap hints, qint32 expireTimeout ) { Qt::beginPropertyUpdateGroup(); this->bExpireTimeout = expireTimeout; this->bAppName = appName; this->bSummary = summary; this->bBody = body; this->bHasActionIcons = hints.value("action-icons").toBool(); this->bResident = hints.value("resident").toBool(); this->bTransient = hints.value("transient").toBool(); this->bDesktopEntry = hints.value("desktop-entry").toString(); this->bUrgency = hints.contains("urgency") ? hints.value("urgency").value() : NotificationUrgency::Normal; if (appIcon.isEmpty() && !this->bDesktopEntry.value().isEmpty()) { if (auto* entry = DesktopEntryManager::instance()->byId(this->bDesktopEntry.value())) { appIcon = entry->mIcon; } } this->bAppIcon = appIcon; QString imageDataName; if (hints.contains("image-data")) imageDataName = "image-data"; else if (hints.contains("image_data")) imageDataName = "image_data"; else if (hints.contains("icon_data")) imageDataName = "icon_data"; QString imagePath; if (!imageDataName.isEmpty()) { auto value = hints.value(imageDataName).value(); DBusNotificationImage image; value >> image; if (this->mImagePixmap) this->mImagePixmap->deleteLater(); this->mImagePixmap = new NotificationImage(std::move(image), this); imagePath = this->mImagePixmap->url(); } // don't store giant byte arrays longer than necessary hints.remove("image-data"); hints.remove("image_data"); hints.remove("icon_data"); if (!this->mImagePixmap) { QString imagePathName; if (hints.contains("image-path")) imagePathName = "image-path"; else if (hints.contains("image_path")) imagePathName = "image_path"; if (!imagePathName.isEmpty()) { imagePath = hints.value(imagePathName).value(); if (!imagePath.startsWith("file:")) { imagePath = IconImageProvider::requestString(imagePath, ""); } } } this->bImage = imagePath; this->bHints = hints; Qt::endPropertyUpdateGroup(); bool actionsChanged = false; auto deletedActions = QVector(); if (actions.length() % 2 == 0) { int ai = 0; for (auto i = 0; i != actions.length(); i += 2) { ai = i / 2; const auto& identifier = actions.at(i); const auto& text = actions.at(i + 1); auto* action = ai < this->mActions.length() ? this->mActions.at(ai) : nullptr; if (action && identifier == action->identifier()) { action->setText(text); } else { auto* newAction = new NotificationAction(identifier, text, this); if (action) { deletedActions.push_back(action); this->mActions.replace(ai, newAction); } else { this->mActions.push_back(newAction); } actionsChanged = true; } ai++; } for (auto i = this->mActions.length(); i > ai; i--) { deletedActions.push_back(this->mActions.at(i - 1)); this->mActions.remove(i - 1); actionsChanged = true; } } else { qCWarning(logNotifications) << this << '(' << appName << ')' << "sent an action set of an invalid length."; } if (actionsChanged) emit this->actionsChanged(); for (auto* action: deletedActions) { delete action; } } quint32 Notification::id() const { return this->mId; } bool Notification::isTracked() const { return this->mCloseReason == 0; } QList Notification::actions() const { return this->mActions; } NotificationCloseReason::Enum Notification::closeReason() const { return this->mCloseReason; } void Notification::setTracked(bool tracked) { this->close( tracked ? static_cast(0) : NotificationCloseReason::Dismissed ); } bool Notification::isLastGeneration() const { return this->mLastGeneration; } void Notification::setLastGeneration() { this->mLastGeneration = true; } } // namespace qs::service::notifications