1 /* 2 SPDX-FileCopyrightText: 2019 Kai Uwe Broulik <kde@privat.broulik.de> 3 4 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL 5 */ 6 7 #pragma once 8 9 #include <QQmlParserStatus> 10 #include <QSortFilterProxyModel> 11 12 #include <QScopedPointer> 13 14 #include "notificationmanager_export.h" 15 16 namespace NotificationManager 17 { 18 /** 19 * @brief A model with notifications and jobs 20 * 21 * This model contains application notifications as well as jobs 22 * and lets you apply fine-grained filter, sorting, and grouping rules. 23 * 24 * @author Kai Uwe Broulik <kde@privat.broulik.de> 25 **/ 26 class NOTIFICATIONMANAGER_EXPORT Notifications : public QSortFilterProxyModel, public QQmlParserStatus 27 { 28 Q_OBJECT 29 Q_INTERFACES(QQmlParserStatus) 30 31 /** 32 * The number of notifications the model should at most contain. 33 * 34 * Default is 0, which is no limit. 35 */ 36 Q_PROPERTY(int limit READ limit WRITE setLimit NOTIFY limitChanged) 37 38 /** 39 * Whether to show expired notifications. 40 * 41 * Expired notifications are those that timed out, i.e. ones that were not explicitly 42 * closed or acted upon by the user, nor revoked by the issuing application. 43 * 44 * An expired notification has its actions removed. 45 * 46 * Default is false. 47 */ 48 Q_PROPERTY(bool showExpired READ showExpired WRITE setShowExpired NOTIFY showExpiredChanged) 49 50 /** 51 * Whether to show dismissed notifications. 52 * 53 * Dismissed notifications are those that are temporarily hidden by the user. 54 * This can e.g. be a copy job that has its popup closed but still continues in the background. 55 * 56 * Default is false. 57 */ 58 Q_PROPERTY(bool showDismissed READ showDismissed WRITE setShowDismissed NOTIFY showDismissedChanged) 59 60 /** 61 * A list of desktop entries for which no notifications should be shown. 62 * 63 * If the same desktop entry is present in both blacklist and whitelist, 64 * the blacklist takes precedence, i.e. the notification is not shown. 65 */ 66 Q_PROPERTY(QStringList blacklistedDesktopEntries READ blacklistedDesktopEntries WRITE setBlacklistedDesktopEntries NOTIFY blacklistedDesktopEntriesChanged) 67 68 /** 69 * A list of notifyrc names for which no notifications should be shown. 70 * 71 * If the same notifyrc name is present in both blacklist and whitelist, 72 * the blacklist takes precedence, i.e. the notification is not shown. 73 */ 74 Q_PROPERTY(QStringList blacklistedNotifyRcNames READ blacklistedNotifyRcNames WRITE setBlacklistedNotifyRcNames NOTIFY blacklistedNotifyRcNamesChanged) 75 76 /** 77 * A list of desktop entries for which notifications should be shown. 78 * 79 * This bypasses any filtering for urgency. 80 * 81 * If the same desktop entry is present in both whitelist and blacklist, 82 * the blacklist takes precedence, i.e. the notification is not shown. 83 * 84 * Default is empty list, which means normal filtering is applied. 85 */ 86 Q_PROPERTY(QStringList whitelistedDesktopEntries READ whitelistedDesktopEntries WRITE setWhitelistedDesktopEntries NOTIFY whitelistedDesktopEntriesChanged) 87 88 /** 89 * A list of notifyrc names for which notifications should be shown. 90 * 91 * This bypasses any filtering for urgency. 92 * 93 * If the same notifyrc name is present in both whitelist and blacklist, 94 * the blacklist takes precedence, i.e. the notification is not shown. 95 * 96 * Default is empty list, which means normal filtering is applied. 97 */ 98 Q_PROPERTY(QStringList whitelistedNotifyRcNames READ whitelistedNotifyRcNames WRITE setWhitelistedNotifyRcNames NOTIFY whitelistedNotifyRcNamesChanged) 99 100 /** 101 * Whether to show notifications. 102 * 103 * Default is true. 104 */ 105 Q_PROPERTY(bool showNotifications READ showNotifications WRITE setShowNotifications NOTIFY showNotificationsChanged) 106 107 /** 108 * Whether to show application jobs. 109 * 110 * Default is false. 111 */ 112 Q_PROPERTY(bool showJobs READ showJobs WRITE setShowJobs NOTIFY showJobsChanged) 113 114 /** 115 * The notification urgency types the model should contain. 116 * 117 * Default is all urgencies: low, normal, critical. 118 */ 119 Q_PROPERTY(Urgencies urgencies READ urgencies WRITE setUrgencies NOTIFY urgenciesChanged) 120 121 /** 122 * The sort mode for notifications. 123 * 124 * Default is strictly by date created/updated. 125 */ 126 Q_PROPERTY(SortMode sortMode READ sortMode WRITE setSortMode NOTIFY sortModeChanged) 127 128 /** 129 * The sort order for notifications. 130 * 131 * This only affects the sort order by date. When @c sortMode is set to SortByTypeAndUrgency 132 * the order of notification groups (e.g. high - jobs - normal - low) is unaffected, and only 133 * notifications within the same group are either sorted ascending or descending by their 134 * creation/update date. 135 * 136 * Default is DescendingOrder, i.e. newest notifications come first. 137 * 138 * @since 5.19 139 */ 140 Q_PROPERTY(Qt::SortOrder sortOrder READ sortOrder WRITE setSortOrder NOTIFY sortOrderChanged) 141 142 /** 143 * The group mode for notifications. 144 * 145 * Default is ungrouped. 146 */ 147 Q_PROPERTY(GroupMode groupMode READ groupMode WRITE setGroupMode NOTIFY groupModeChanged) 148 149 /** 150 * How many notifications are shown in each group. 151 * 152 * You can expand a group by setting the IsGroupExpandedRole to true. 153 * 154 * Default is 0, which means no limit. 155 */ 156 Q_PROPERTY(int groupLimit READ groupLimit WRITE setGroupLimit NOTIFY groupLimitChanged) 157 158 /** 159 * Whether to automatically show notifications that are unread. 160 * 161 * This is any notification that was created or updated after the value of @c lastRead. 162 */ 163 Q_PROPERTY(bool expandUnread READ expandUnread WRITE setExpandUnread NOTIFY expandUnreadChanged) 164 165 /** 166 * The number of notifications in the model 167 */ 168 Q_PROPERTY(int count READ count NOTIFY countChanged) 169 170 /** 171 * The number of active, i.e. non-expired notifications 172 */ 173 Q_PROPERTY(int activeNotificationsCount READ activeNotificationsCount NOTIFY activeNotificationsCountChanged) 174 175 /** 176 * The number of inactive, i.e. non-expired notifications 177 */ 178 Q_PROPERTY(int expiredNotificationsCount READ expiredNotificationsCount NOTIFY expiredNotificationsCountChanged) 179 180 /** 181 * The time when the user last could read the notifications. 182 * This is typically reset whenever the list of notifications is opened and is used to determine 183 * the @c unreadNotificationsCount 184 */ 185 Q_PROPERTY(QDateTime lastRead READ lastRead WRITE setLastRead RESET resetLastRead NOTIFY lastReadChanged) 186 187 /** 188 * The number of notifications added since lastRead 189 * 190 * This can be used to show a "n unread notifications" label 191 */ 192 Q_PROPERTY(int unreadNotificationsCount READ unreadNotificationsCount NOTIFY unreadNotificationsCountChanged) 193 194 /** 195 * The number of active jobs 196 */ 197 Q_PROPERTY(int activeJobsCount READ activeJobsCount NOTIFY activeJobsCountChanged) 198 /** 199 * The combined percentage of all jobs. 200 * 201 * This is the average of all percentages and could can be used to show 202 * a global progress bar. 203 */ 204 Q_PROPERTY(int jobsPercentage READ jobsPercentage NOTIFY jobsPercentageChanged) 205 206 public: 207 explicit Notifications(QObject *parent = nullptr); 208 ~Notifications() override; 209 210 enum Roles { 211 IdRole = Qt::UserRole + 1, ///< A notification identifier. This can be uint notification ID or string application job source. 212 SummaryRole = Qt::DisplayRole, ///< The notification summary. 213 ImageRole = Qt::DecorationRole, ///< The notification main image, which is not the application icon. Only valid for pixmap icons. 214 215 IsGroupRole = Qt::UserRole + 2, ///< Whether the item is a group 216 GroupChildrenCountRole, ///< The number of children in a group. 217 ExpandedGroupChildrenCountRole, ///< The number of children in a group that are expanded. 218 IsGroupExpandedRole, ///< Whether the group is expanded, this role is writable. 219 220 IsInGroupRole, ///< Whether the notification is currently inside a group. 221 TypeRole, ///< The type of model entry, either NotificationType or JobType. 222 CreatedRole, ///< When the notification was first created. 223 UpdatedRole, ///< When the notification was last updated, invalid when it hasn't been updated. 224 225 BodyRole, ///< The notification body text. 226 IconNameRole, ///< The notification main icon name, which is not the application icon. Only valid for icon names, if a URL supplied, it is loaded and 227 ///< exposed as ImageRole instead. 228 229 DesktopEntryRole, ///< The desktop entry (without .desktop suffix, e.g. org.kde.spectacle) of the application that sent the notification. 230 NotifyRcNameRole, ///< The notifyrc name (e.g. spectaclerc) of the application that sent the notification. 231 232 ApplicationNameRole, ///< The user-visible name of the application (e.g. Spectacle) 233 ApplicationIconNameRole, ///< The icon name of the application 234 OriginNameRole, ///< The name of the device or account the notification originally came from, e.g. "My Phone" (in case of device sync) or 235 ///< "foo@example.com" (in case of an email notification) 236 237 // Jobs 238 JobStateRole, ///< The state of the job, either JobStateJopped, JobStateSuspended, or JobStateRunning. 239 PercentageRole, ///< The percentage of the job. Use @c jobsPercentage to get a global percentage for all jobs. 240 JobErrorRole, ///< The error id of the job, zero in case of no error. 241 SuspendableRole, ///< Whether the job can be suspended @sa suspendJob 242 KillableRole, ///< Whether the job can be killed/canceled @sa killJob 243 JobDetailsRole, ///< A pointer to a Job item itself containing more detailed information about the job 244 245 ActionNamesRole, ///< The IDs of the actions, excluding the default and settings action, e.g. [action1, action2] 246 ActionLabelsRole, ///< The user-visible labels of the actions, excluding the default and settings action, e.g. ["Accept", "Reject"] 247 HasDefaultActionRole, ///< Whether the notification has a default action, which is one that is invoked when the popup itself is clicked 248 DefaultActionLabelRole, ///< The user-visible label of the default action, typically not shown as the popup itself becomes clickable 249 250 UrlsRole, ///< A list of URLs associated with the notification, e.g. a path to a screenshot that was just taken or image received 251 252 UrgencyRole, ///< The notification urgency, either LowUrgency, NormalUrgency, or CriticalUrgency. Jobs do not have an urgency. 253 TimeoutRole, ///< The timeout for the notification in milliseconds. 0 means the notification should not timeout, -1 means a sensible default should be 254 ///< applied. 255 256 ConfigurableRole, ///< Whether the notification can be configured because a desktopEntry or notifyRcName is known, or the notification has a setting 257 ///< action. @sa configure 258 ConfigureActionLabelRole, ///< The user-visible label for the settings action 259 ClosableRole, ///< Whether the item can be closed. Notifications are always closable, jobs are only when in JobStateStopped. 260 261 ExpiredRole, ///< The notification timed out and closed. Actions on it cannot be invoked anymore. 262 DismissedRole, ///< The notification got temporarily hidden by the user but could still be interacted with. 263 ReadRole, ///< Whether the notification got read by the user. If true, the notification isn't considered unread even if created after lastRead. 264 ///< @since 5.17 265 266 UserActionFeedbackRole, ///< Whether this notification is a response/confirmation to an explicit user action. @since 5.18 267 268 HasReplyActionRole, ///< Whether the notification has a reply action. @since 5.18 269 ReplyActionLabelRole, ///< The user-visible label for the reply action. @since 5.18 270 ReplyPlaceholderTextRole, ///< A custom placeholder text for the reply action, e.g. "Reply to Max...". @since 5.18 271 ReplySubmitButtonTextRole, ///< A custom text for the reply submit button, e.g. "Submit Comment". @since 5.18 272 ReplySubmitButtonIconNameRole, ///< A custom icon name for the reply submit button. @since 5.18 273 CategoryRole, ///< The (optional) category of the notification. Notifications can optionally have a type indicator. Although neither client or nor 274 ///< server must support this, some may choose to. Those servers implementing categories may use them to intelligently display the 275 ///< notification in a certain way, or group notifications of similar types. @since 5.21 276 ResidentRole, ///< Whether the notification should keep its actions even when they were invoked. @since 5.22 277 TransientRole, ///< Whether the notification is transient and should not be kept in history. @since 5.22 278 }; 279 Q_ENUM(Roles) 280 281 /** 282 * The type of model item. 283 */ 284 enum Type { 285 NoType, 286 NotificationType, ///< This item represents a notification. 287 JobType, ///< This item represents an application job. 288 }; 289 Q_ENUM(Type) 290 291 /** 292 * The notification urgency. 293 * 294 * @note jobs do not have an urgency, yet still might be above normal urgency notifications. 295 */ 296 enum Urgency { 297 // these don't match the spec's value 298 LowUrgency = 1 << 0, ///< The notification has low urgency, it is not important and may not be shown or added to a history. 299 NormalUrgency = 1 << 1, ///< The notification has normal urgency. This is also the default if no urgecny is supplied. 300 CriticalUrgency = 1 << 2, 301 }; 302 Q_ENUM(Urgency) 303 Q_DECLARE_FLAGS(Urgencies, Urgency) 304 Q_FLAG(Urgencies) 305 306 /** 307 * Which items should be cleared in a call to @c clear 308 */ 309 enum ClearFlag { 310 ClearExpired = 1 << 1, 311 // TODO more 312 }; 313 Q_ENUM(ClearFlag) 314 Q_DECLARE_FLAGS(ClearFlags, ClearFlag) 315 Q_FLAG(ClearFlags) 316 317 /** 318 * The state an application job is in. 319 */ 320 enum JobState { 321 JobStateStopped, ///< The job is stopped. It has either finished (error is 0) or failed (error is not 0) 322 JobStateRunning, ///< The job is currently running. 323 JobStateSuspended, ///< The job is currentl paused 324 }; 325 Q_ENUM(JobState) 326 327 /** 328 * The sort mode for the model. 329 */ 330 enum SortMode { 331 SortByDate = 0, ///< Sort notifications strictly by the date they were updated or created. 332 // should this be flags? SortJobsFirst | SortByUrgency | ...? 333 SortByTypeAndUrgency, ///< Sort notifications taking into account their type and urgency. The order is (descending): Critical, jobs, Normal, Low. 334 }; 335 Q_ENUM(SortMode) 336 337 /** 338 * The group mode for the model. 339 */ 340 enum GroupMode { 341 GroupDisabled = 0, 342 // GroupApplicationsTree, // TODO make actual tree 343 GroupApplicationsFlat, 344 }; 345 Q_ENUM(GroupMode) 346 347 int limit() const; 348 void setLimit(int limit); 349 350 bool showExpired() const; 351 void setShowExpired(bool show); 352 353 bool showDismissed() const; 354 void setShowDismissed(bool show); 355 356 QStringList blacklistedDesktopEntries() const; 357 void setBlacklistedDesktopEntries(const QStringList &blacklist); 358 359 QStringList blacklistedNotifyRcNames() const; 360 void setBlacklistedNotifyRcNames(const QStringList &blacklist); 361 362 QStringList whitelistedDesktopEntries() const; 363 void setWhitelistedDesktopEntries(const QStringList &whitelist); 364 365 QStringList whitelistedNotifyRcNames() const; 366 void setWhitelistedNotifyRcNames(const QStringList &whitelist); 367 368 bool showNotifications() const; 369 void setShowNotifications(bool showNotifications); 370 371 bool showJobs() const; 372 void setShowJobs(bool showJobs); 373 374 Urgencies urgencies() const; 375 void setUrgencies(Urgencies urgencies); 376 377 SortMode sortMode() const; 378 void setSortMode(SortMode sortMode); 379 380 Qt::SortOrder sortOrder() const; 381 void setSortOrder(Qt::SortOrder sortOrder); 382 383 GroupMode groupMode() const; 384 void setGroupMode(GroupMode groupMode); 385 386 int groupLimit() const; 387 void setGroupLimit(int limit); 388 389 bool expandUnread() const; 390 void setExpandUnread(bool expand); 391 392 int count() const; 393 394 int activeNotificationsCount() const; 395 int expiredNotificationsCount() const; 396 397 QDateTime lastRead() const; 398 void setLastRead(const QDateTime &lastRead); 399 void resetLastRead(); 400 401 int unreadNotificationsCount() const; 402 403 int activeJobsCount() const; 404 int jobsPercentage() const; 405 406 /** 407 * Convert the given QModelIndex into a QPersistentModelIndex 408 */ 409 Q_INVOKABLE QPersistentModelIndex makePersistentModelIndex(const QModelIndex &idx) const; 410 411 /** 412 * @brief Expire a notification 413 * 414 * Closes the notification in response to its timeout running out. 415 * 416 * Call this if you have an implementation that handles the timeout itself 417 * by having called @c stopTimeout 418 * 419 * @sa stopTimeout 420 */ 421 Q_INVOKABLE void expire(const QModelIndex &idx); 422 /** 423 * @brief Close a notification 424 * 425 * Closes the notification in response to the user explicitly closing it. 426 * 427 * When the model index belongs to a group, the entire group is closed. 428 */ 429 Q_INVOKABLE void close(const QModelIndex &idx); 430 /** 431 * @brief Configure a notification 432 * 433 * This will invoke the settings action, if available, otherwise open the 434 * kcm_notifications KCM for configuring the respective application and event. 435 */ 436 Q_INVOKABLE void configure(const QModelIndex &idx); // TODO pass ctx for transient handling 437 /** 438 * @brief Invoke the default notification action 439 * 440 * Invokes the action that should be triggered when clicking 441 * the notification bubble itself. 442 */ 443 Q_INVOKABLE void invokeDefaultAction(const QModelIndex &idx); 444 /** 445 * @brief Invoke a notification action 446 * 447 * Invokes the action with the given actionId on the notification. 448 * For invoking the default action, i.e. the one that is triggered 449 * when clicking the notification bubble, use invokeDefaultAction 450 */ 451 Q_INVOKABLE void invokeAction(const QModelIndex &idx, const QString &actionId); 452 453 /** 454 * @brief Reply to a notification 455 * 456 * Replies to the given notification with the given text. 457 * @since 5.18 458 */ 459 Q_INVOKABLE void reply(const QModelIndex &idx, const QString &text); 460 461 /** 462 * @brief Start automatic timeout of notifications 463 * 464 * Call this if you no longer handle the timeout yourself. 465 * 466 * @sa stopTimeout 467 */ 468 Q_INVOKABLE void startTimeout(const QModelIndex &idx); 469 470 Q_INVOKABLE void startTimeout(uint notificationId); 471 /** 472 * @brief Stop the automatic timeout of notifications 473 * 474 * Call this if you have an implementation that handles the timeout itself 475 * taking into account e.g. whether the user is currently interacting with 476 * the notification to not close it under their mouse. Call @c expire 477 * once your custom timer has run out. 478 * 479 * @sa expire 480 */ 481 Q_INVOKABLE void stopTimeout(const QModelIndex &idx); 482 483 /** 484 * @brief Suspend a job 485 */ 486 Q_INVOKABLE void suspendJob(const QModelIndex &idx); 487 /** 488 * @brief Resume a job 489 */ 490 Q_INVOKABLE void resumeJob(const QModelIndex &idx); 491 /** 492 * @brief Kill a job 493 */ 494 Q_INVOKABLE void killJob(const QModelIndex &idx); 495 496 /** 497 * @brief Clear notifications 498 * 499 * Removes the notifications matching th ClearFlags from the model. 500 * This can be used for e.g. a "Clear History" action. 501 */ 502 Q_INVOKABLE void clear(ClearFlags flags); 503 504 /** 505 * Returns a model index pointing to the group of a notification. 506 */ 507 Q_INVOKABLE QModelIndex groupIndex(const QModelIndex &idx) const; 508 509 Q_INVOKABLE void collapseAllGroups(); 510 511 QVariant data(const QModelIndex &index, int role) const override; 512 bool setData(const QModelIndex &index, const QVariant &value, int role) override; 513 int rowCount(const QModelIndex &parent = QModelIndex()) const override; 514 QHash<int, QByteArray> roleNames() const override; 515 516 bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override; 517 bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const override; 518 519 Q_SIGNALS: 520 void limitChanged(); 521 void showExpiredChanged(); 522 void showDismissedChanged(); 523 void blacklistedDesktopEntriesChanged(); 524 void blacklistedNotifyRcNamesChanged(); 525 void whitelistedDesktopEntriesChanged(); 526 void whitelistedNotifyRcNamesChanged(); 527 void showNotificationsChanged(); 528 void showJobsChanged(); 529 void urgenciesChanged(); 530 void sortModeChanged(); 531 void sortOrderChanged(); 532 void groupModeChanged(); 533 void groupLimitChanged(); 534 void expandUnreadChanged(); 535 void countChanged(); 536 void activeNotificationsCountChanged(); 537 void expiredNotificationsCountChanged(); 538 void lastReadChanged(); 539 void unreadNotificationsCountChanged(); 540 void activeJobsCountChanged(); 541 void jobsPercentageChanged(); 542 543 protected: 544 void classBegin() override; 545 void componentComplete() override; 546 547 private: 548 class Private; 549 QScopedPointer<Private> d; 550 }; 551 552 } // namespace NotificationManager 553 554 Q_DECLARE_OPERATORS_FOR_FLAGS(NotificationManager::Notifications::Urgencies) 555