Git commit 9e8e3f63c0243e04009414701efb541ab196fc2b by David Jarvie. Committed on 21/10/2020 at 23:45. Pushed by djarvie into branch 'master'.
Don't execute display alarms while desktop notifications are inhibited M +2 -1 Changelog M +6 -1 doc/index.docbook M +3 -0 src/CMakeLists.txt A +28 -0 src/data/org.freedesktop.DBus.Properties.xml A +60 -0 src/data/org.freedesktop.Notifications.xml M +1 -1 src/editdlg.cpp M +94 -21 src/kalarmapp.cpp M +7 -2 src/kalarmapp.h M +62 -17 src/resourcescalendar.cpp M +5 -3 src/resourcescalendar.h https://invent.kde.org/pim/kalarm/commit/9e8e3f63c0243e04009414701efb541ab196fc2b diff --git a/Changelog b/Changelog index cead132b..42d5e2db 100644 --- a/Changelog +++ b/Changelog @@ -1,7 +1,8 @@ KAlarm Change Log -=== Version 3.1.0 (KDE Applications 20.12) --- 24 September 2020 === +=== Version 3.1.0 (KDE Applications 20.12) --- 22 October 2020 === + Add option to show alarm message as a notification instead of in a window [KDE Bug 345922] ++ Don't execute display alarms while desktop notifications are inhibited. === Version 3.0.3 (KDE Applications 20.08.3) --- 15 October 2020 === + Prevent resources being disabled at logout [KDE Bug 427722] diff --git a/doc/index.docbook b/doc/index.docbook index ed86cc44..663e673b 100644 --- a/doc/index.docbook +++ b/doc/index.docbook @@ -39,7 +39,7 @@ <!-- Don't change format of date and version of the documentation --> -<date>2020-09-24</date> +<date>2020-10-22</date> <releaseinfo>3.1.0 (Applications 20.12)</releaseinfo> <abstract> @@ -1926,6 +1926,11 @@ depending on the option chosen in the <link linkend="alarm-edit-dlg">Alarm Edit dialog</link>. These two display options are described below.</para> +<para>If you inhibit notifications for the desktop, the execution of +display alarms will be suspended until notifications are re-enabled. +Note that this applies to all display alarms, regardless of whether +they are displayed in windows or as notifications.</para> + <sect2 id="message-window"> <title>Alarm Message Window</title> diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 96e028b5..821b5058 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -145,6 +145,9 @@ qt5_add_dbus_adaptor(kalarm_bin_SRCS data/org.kde.kalarm.kalarm.xml dbushandler. qt5_add_dbus_interfaces(kalarm_bin_SRCS data/org.kde.kmail.kmail.xml) +qt5_add_dbus_interface(kalarm_bin_SRCS data/org.freedesktop.Notifications.xml notifications_interface) +qt5_add_dbus_interface(kalarm_bin_SRCS data/org.freedesktop.DBus.Properties.xml dbusproperties) + kcfg_generate_dbus_interface(${CMAKE_CURRENT_SOURCE_DIR}/data/kalarmresource.kcfg org.kde.Akonadi.KAlarm.Settings) qt5_add_dbus_interface(kalarm_bin_SRCS ${CMAKE_CURRENT_BINARY_DIR}/org.kde.Akonadi.KAlarm.Settings.xml kalarmsettings KAlarmSettings) diff --git a/src/data/org.freedesktop.DBus.Properties.xml b/src/data/org.freedesktop.DBus.Properties.xml new file mode 100644 index 00000000..688ed108 --- /dev/null +++ b/src/data/org.freedesktop.DBus.Properties.xml @@ -0,0 +1,28 @@ +<?xml version="1.0"?> +<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> +<!-- GDBus 2.32.4 --> +<node> + <interface name="org.freedesktop.DBus.Properties"> + <method name="Get"> + <arg type="s" name="interface_name" direction="in"/> + <arg type="s" name="property_name" direction="in"/> + <arg type="v" name="value" direction="out"/> + </method> + <method name="GetAll"> + <arg type="s" name="interface_name" direction="in"/> + <arg type="a{sv}" name="properties" direction="out"/> + <annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QVariantMap" /> + </method> + <method name="Set"> + <arg type="s" name="interface_name" direction="in"/> + <arg type="s" name="property_name" direction="in"/> + <arg type="v" name="value" direction="in"/> + </method> + <signal name="PropertiesChanged"> + <arg type="s" name="interface_name"/> + <arg type="a{sv}" name="changed_properties"/> + <arg type="as" name="invalidated_properties"/> + <annotation name="org.qtproject.QtDBus.QtTypeName.Out1" value="QVariantMap" /> + </signal> + </interface> +</node> diff --git a/src/data/org.freedesktop.Notifications.xml b/src/data/org.freedesktop.Notifications.xml new file mode 100644 index 00000000..fc0825df --- /dev/null +++ b/src/data/org.freedesktop.Notifications.xml @@ -0,0 +1,60 @@ +<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> +<node> + <interface name="org.freedesktop.Notifications"> + <signal name="NotificationClosed"> + <arg name="id" type="u" direction="out"/> + <arg name="reason" type="u" direction="out"/> + </signal> + <signal name="ActionInvoked"> + <arg name="id" type="u" direction="out"/> + <arg name="action_key" type="s" direction="out"/> + </signal> + <method name="Notify"> + <annotation name="org.qtproject.QtDBus.QtTypeName.In6" value="QVariantMap"/> + <arg type="u" direction="out"/> + <arg name="app_name" type="s" direction="in"/> + <arg name="replaces_id" type="u" direction="in"/> + <arg name="app_icon" type="s" direction="in"/> + <arg name="summary" type="s" direction="in"/> + <arg name="body" type="s" direction="in"/> + <arg name="actions" type="as" direction="in"/> + <arg name="hints" type="a{sv}" direction="in"/> + <arg name="timeout" type="i" direction="in"/> + </method> + <method name="CloseNotification"> + <arg name="id" type="u" direction="in"/> + </method> + <method name="GetCapabilities"> + <arg type="as" name="caps" direction="out"/> + </method> + <method name="GetServerInformation"> + <arg type="s" name="name" direction="out"/> + <arg type="s" name="vendor" direction="out"/> + <arg type="s" name="version" direction="out"/> + <arg type="s" name="spec_version" direction="out"/> + </method> + + <!-- Inhibitions --> + <method name="Inhibit"> + <annotation name="org.qtproject.QtDBus.QtTypeName.In2" value="QVariantMap"/> + <arg type="u" direction="out"/> + <arg name="desktop_entry" type="s" direction="in"/> + <arg name="reason" type="s" direction="in"/> + <arg name="hints" type="a{sv}" direction="in"/> + </method> + + <method name="UnInhibit"> + <arg type="u" direction="in"/> + </method> + + <property name="Inhibited" type="b" access="read"> + <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="true"/> + </property> + + <!--<method name="ListInhibitors"> + <annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QList<Inhibition>"/> + <arg name="inhibitors" type="a(ssa{sv})" direction="out"/> + </method>--> + + </interface> +</node> diff --git a/src/editdlg.cpp b/src/editdlg.cpp index cb73bde0..48fdf076 100644 --- a/src/editdlg.cpp +++ b/src/editdlg.cpp @@ -1166,7 +1166,7 @@ void EditAlarmDlg::slotTry() event.setEventId(mEventId); } type_aboutToTry(); - void* result = theApp()->execAlarm(event, event.firstAlarm(), KAlarmApp::NoRecordCmdError); + void* result = theApp()->execAlarm(event, event.firstAlarm(), KAlarmApp::NoRecordCmdError | KAlarmApp::NoNotifyInhibit); type_executedTry(text, result); } } diff --git a/src/kalarmapp.cpp b/src/kalarmapp.cpp index ac03d923..407e6f81 100644 --- a/src/kalarmapp.cpp +++ b/src/kalarmapp.cpp @@ -30,6 +30,8 @@ #include "lib/desktop.h" #include "lib/messagebox.h" #include "lib/shellprocess.h" +#include "notifications_interface.h" // DBUS-generated +#include "dbusproperties.h" // DBUS-generated #include "kalarm_debug.h" #include <KAlarmCal/DateTime> @@ -62,6 +64,9 @@ namespace { const int RESOURCES_TIMEOUT = 30; // timeout (seconds) for resources to be populated +const char FDO_NOTIFICATIONS_SERVICE[] = "org.freedesktop.Notifications"; +const char FDO_NOTIFICATIONS_PATH[] = "/org/freedesktop/Notifications"; + /****************************************************************************** * Find the maximum number of seconds late which a late-cancel alarm is allowed * to be. This is calculated as the late cancel interval, plus a few seconds @@ -194,6 +199,23 @@ void KAlarmApp::initialise() DateTime::setStartOfDay(Preferences::startOfDay()); mPrefsArchivedColour = Preferences::archivedColour(); } + + // Get notified when the Freedesktop notifications properties have changed. + QDBusConnection conn = QDBusConnection::sessionBus(); + if (conn.interface()->isServiceRegistered(QString::fromLatin1(FDO_NOTIFICATIONS_SERVICE))) + { + OrgFreedesktopDBusPropertiesInterface* piface = new OrgFreedesktopDBusPropertiesInterface( + QString::fromLatin1(FDO_NOTIFICATIONS_SERVICE), + QString::fromLatin1(FDO_NOTIFICATIONS_PATH), + conn, this); + connect(piface, &OrgFreedesktopDBusPropertiesInterface::PropertiesChanged, + this, &KAlarmApp::slotFDOPropertiesChanged); + OrgFreedesktopNotificationsInterface niface( + QString::fromLatin1(FDO_NOTIFICATIONS_SERVICE), + QString::fromLatin1(FDO_NOTIFICATIONS_PATH), + conn); + mNotificationsInhibited = niface.inhibited(); + } } /****************************************************************************** @@ -845,7 +867,7 @@ void KAlarmApp::checkNextDueAlarm() return; // Find the first alarm due KADateTime nextDt; - const KAEvent nextEvent = ResourcesCalendar::earliestAlarm(nextDt); + const KAEvent nextEvent = ResourcesCalendar::earliestAlarm(nextDt, mNotificationsInhibited); if (!nextEvent.isValid()) return; // there are no alarms pending const KADateTime now = KADateTime::currentDateTime(Preferences::timeSpec()); @@ -981,13 +1003,15 @@ void KAlarmApp::processQueue() const QueuedAction action = static_cast<QueuedAction>(int(entry.action) & int(QueuedAction::ActionMask)); bool ok = true; + bool inhibit = false; if (entry.eventId.isEmpty()) { // It's a new alarm switch (action) { case QueuedAction::Trigger: - execAlarm(entry.event, entry.event.firstAlarm()); + if (execAlarm(entry.event, entry.event.firstAlarm()) == (void*)-2) + inhibit = true; break; case QueuedAction::Handle: { @@ -1034,13 +1058,22 @@ void KAlarmApp::processQueue() } else { - ok = handleEvent(entry.eventId, action, findUniqueId); - if (!ok && exitAfter) + // Trigger the event if it's due. + const int result = handleEvent(entry.eventId, action, findUniqueId); + if (!result) + inhibit = true; + else if (result < 0 && exitAfter) CommandOptions::printError(xi18nc("@info:shell", "%1: Event <resource>%2</resource> not found, or not unique", mCommandOption, entry.eventId.eventId())); } } - if (exitAfter) + if (inhibit) + { + // It's a display event which can't be executed because notifications + // are inhibited. Move it to the inhibited queue until the inhibition + // is removed. + } + else if (exitAfter) { mActionQueue.clear(); // ensure that quitIf() actually exits the program quitIf(ok ? 0 : 1); @@ -1506,6 +1539,29 @@ void KAlarmApp::purge(int daysToKeep) processQueue(); } +/****************************************************************************** +* Called when the Freedesktop notifications properties have changed. +* Check whether the inhibited property has changed. +*/ +void KAlarmApp::slotFDOPropertiesChanged(const QString& interface, + const QVariantMap& changedProperties, + const QStringList& invalidatedProperties) +{ + Q_UNUSED(interface); // always "org.freedesktop.Notifications" + Q_UNUSED(invalidatedProperties); + const auto it = changedProperties.find(QStringLiteral("Inhibited")); + if (it != changedProperties.end()) + { + const bool inhibited = it.value().toBool(); + if (inhibited != mNotificationsInhibited) + { + qCDebug(KALARM_LOG) << "KAlarmApp::slotFDOPropertiesChanged: Notifications inhibited ->" << inhibited; + mNotificationsInhibited = inhibited; + if (!mNotificationsInhibited) + QTimer::singleShot(0, this, &KAlarmApp::processQueue); + } + } +} /****************************************************************************** * Output a list of pending alarms, with their next scheduled occurrence. @@ -1626,13 +1682,12 @@ bool KAlarmApp::scheduleEvent(KAEvent::SubAction action, const QString& text, co event.endChanges(); if (alarmTime <= now) { - // Alarm is due for display already. + // Alarm is due for execution already. // First execute it once without adding it to the calendar file. qCDebug(KALARM_LOG) << "KAlarmApp::scheduleEvent: executing" << text; - if (!mInitialised) + if (!mInitialised + || execAlarm(event, event.firstAlarm()) == (void*)-2) mActionQueue.enqueue(ActionQEntry(event, QueuedAction::Trigger)); - else - execAlarm(event, event.firstAlarm()); // If it's a recurring alarm, reschedule it for its next occurrence if (!event.recurs() || event.setNextOccurrence(now) == KAEvent::NO_OCCURRENCE) @@ -1673,17 +1728,20 @@ QString KAlarmApp::dbusList() /****************************************************************************** * Either: -* a) Display the event and then delete it if it has no outstanding repetitions. +* a) Execute the event if it's due, and then delete it if it has no outstanding +* repetitions. * b) Delete the event. * c) Reschedule the event for its next repetition. If none remain, delete it. * If the event is deleted, it is removed from the calendar file and from every * main window instance. * If 'findUniqueId' is true and 'id' does not specify a resource, all resources * will be searched for the event's unique ID. -* Reply = false if event ID not found, or if more than one event with the same -* ID is found. +* Reply = -1 if event ID not found, or if more than one event with the same ID +* is found. +* = 0 if can't trigger display event because notifications are inhibited. +* = 1 if success. */ -bool KAlarmApp::handleEvent(const EventId& id, QueuedAction action, bool findUniqueId) +int KAlarmApp::handleEvent(const EventId& id, QueuedAction action, bool findUniqueId) { Q_ASSERT(!(int(action) & ~int(QueuedAction::ActionMask))); @@ -1700,7 +1758,7 @@ bool KAlarmApp::handleEvent(const EventId& id, QueuedAction action, bool findUni qCWarning(KALARM_LOG) << "KAlarmApp::handleEvent: Event ID not found, or duplicated:" << eventID; else qCCritical(KALARM_LOG) << "KAlarmApp::handleEvent: No resource ID specified for event:" << eventID; - return false; + return -1; } switch (action) { @@ -1850,7 +1908,7 @@ bool KAlarmApp::handleEvent(const EventId& id, QueuedAction action, bool findUni // All recurrences are finished, so cancel the event event.setArchive(); if (cancelAlarm(event, alarm.type(), false)) - return true; // event has been deleted + return 1; // event has been deleted updateCalAndDisplay = true; continue; } @@ -1868,7 +1926,7 @@ bool KAlarmApp::handleEvent(const EventId& id, QueuedAction action, bool findUni restart = true; break; case -1: - return true; // event has been deleted + return 1; // event has been deleted default: break; } @@ -1888,7 +1946,10 @@ bool KAlarmApp::handleEvent(const EventId& id, QueuedAction action, bool findUni // If there is an alarm to execute, do this last after rescheduling/cancelling // any others. This ensures that the updated event is only saved once to the calendar. if (alarmToExecute.isValid()) - execAlarm(event, alarmToExecute, Reschedule | (alarmToExecute.repeatAtLogin() ? NoExecFlag : AllowDefer)); + { + if (execAlarm(event, alarmToExecute, Reschedule | (alarmToExecute.repeatAtLogin() ? NoExecFlag : AllowDefer)) == (void*)-2) + return 0; // display alarm, but notifications are inhibited + } else { if (action == QueuedAction::Trigger) @@ -1898,7 +1959,10 @@ bool KAlarmApp::handleEvent(const EventId& id, QueuedAction action, bool findUni // identical messages, for example. const KAAlarm alarm = event.firstAlarm(); if (alarm.isValid()) - execAlarm(event, alarm); + { + if (execAlarm(event, alarm) == (void*)-2) + return 0; // display alarm, but notifications are inhibited + } } if (updateCalAndDisplay) KAlarm::updateEvent(event); // update the window lists and calendar file @@ -1909,7 +1973,7 @@ bool KAlarmApp::handleEvent(const EventId& id, QueuedAction action, bool findUni default: break; } - return true; + return 1; } /****************************************************************************** @@ -2098,9 +2162,10 @@ bool KAlarmApp::cancelReminderAndDeferral(KAEvent& event) * Execute an alarm by displaying its message or file, or executing its command. * Reply = ShellProcess instance if a command alarm * = MessageWindow if an audio alarm -* != 0 if successful +* != null if successful * = -1 if execution has not completed -* = 0 if the alarm is disabled, or if an error message was output. +* = -2 if can't execute display event because notifications are inhibited. +* = null if the alarm is disabled, or if an error message was output. */ void* KAlarmApp::execAlarm(KAEvent& event, const KAAlarm& alarm, ExecAlarmFlags flags) { @@ -2113,6 +2178,14 @@ void* KAlarmApp::execAlarm(KAEvent& event, const KAAlarm& alarm, ExecAlarmFlags return nullptr; } + if (mNotificationsInhibited && !(flags & NoNotifyInhibit) + && (event.actionTypes() & KAEvent::ACT_DISPLAY)) + { + // It's a display event and notifications are inhibited. + qCDebug(KALARM_LOG) << "KAlarmApp::execAlarm:" << event.id() << ": notifications inhibited"; + return (void*)-2; + } + void* result = (void*)1; event.setArchive(); diff --git a/src/kalarmapp.h b/src/kalarmapp.h index c7df5f46..76b32efa 100644 --- a/src/kalarmapp.h +++ b/src/kalarmapp.h @@ -43,7 +43,8 @@ public: Reschedule = 0x01, // reschedule the alarm after executing it AllowDefer = 0x02, // allow the alarm to be deferred NoRecordCmdError = 0x04, // don't record command errors - NoPreAction = 0x08 + NoPreAction = 0x08, // it isn't a pre-alarm action + NoNotifyInhibit = 0x10 // ignore notification inhibit }; Q_DECLARE_FLAGS(ExecAlarmFlags, ExecAlarmFlag) @@ -144,6 +145,9 @@ private Q_SLOTS: void slotResourcePopulated(const Resource&); void slotPurge() { purge(mArchivedPurgeDays); } void slotCommandExited(ShellProcess*); + void slotFDOPropertiesChanged(const QString& interface, + const QVariantMap& changedProperties, + const QStringList& invalidatedProperties); private: // Actions to execute in processQueue(). May be OR'ed together. @@ -217,7 +221,7 @@ private: void checkArchivedCalendar(); void queueAlarmId(const KAEvent&); bool dbusHandleEvent(const EventId&, QueuedAction); - bool handleEvent(const EventId&, QueuedAction, bool findUniqueId = false); + int handleEvent(const EventId&, QueuedAction, bool findUniqueId = false); int rescheduleAlarm(KAEvent&, const KAAlarm&, bool updateCalAndDisplay, const KADateTime& nextDt = KADateTime()); bool cancelAlarm(KAEvent&, KAAlarm::Type, bool updateCalAndDisplay); @@ -266,6 +270,7 @@ private: bool mKOrganizerEnabled; // KOrganizer options are enabled (korganizer exists) bool mWindowFocusBroken; // keyboard focus transfer between windows doesn't work bool mResourcesTimedOut {false}; // timeout has expired for populating resources + bool mNotificationsInhibited {false}; // Freedesktop notifications are inhibited }; inline KAlarmApp* theApp() { return KAlarmApp::instance(); } diff --git a/src/resourcescalendar.cpp b/src/resourcescalendar.cpp index 3c6d5714..a0509818 100644 --- a/src/resourcescalendar.cpp +++ b/src/resourcescalendar.cpp @@ -20,6 +20,7 @@ using namespace KAlarmCal; ResourcesCalendar* ResourcesCalendar::mInstance {nullptr}; ResourcesCalendar::ResourceMap ResourcesCalendar::mResourceMap; ResourcesCalendar::EarliestMap ResourcesCalendar::mEarliestAlarm; +ResourcesCalendar::EarliestMap ResourcesCalendar::mEarliestNonDispAlarm; QSet<QString> ResourcesCalendar::mPendingAlarms; bool ResourcesCalendar::mIgnoreAtLogin {false}; bool ResourcesCalendar::mHaveDisabledAlarms {false}; @@ -111,6 +112,7 @@ void ResourcesCalendar::removeKAEvents(ResourceId key, bool closing, CalEvent::T if (removed) { mEarliestAlarm.remove(key); + mEarliestNonDispAlarm.remove(key); // Emit signal only if we're not in the process of closing the calendar if (!closing) { @@ -193,17 +195,38 @@ void ResourcesCalendar::slotEventUpdated(Resource& resource, const KAEvent& even && event.category() == CalEvent::ACTIVE) { // Update the earliest alarm to trigger - const QString earliestId = mEarliestAlarm.value(key); - if (earliestId == event.id()) + const QString earliestId = mEarliestAlarm.value(key); + const QString earliestNonDispId = mEarliestNonDispAlarm.value(key); + if (earliestId == event.id() || earliestNonDispId == event.id()) findEarliestAlarm(resource); else { const KADateTime dt = event.nextTrigger(KAEvent::ALL_TRIGGER).effectiveKDateTime(); - if (dt.isValid() - && (earliestId.isEmpty() || dt < resource.event(earliestId).nextTrigger(KAEvent::ALL_TRIGGER))) + if (dt.isValid()) { - mEarliestAlarm[key] = event.id(); - Q_EMIT earliestAlarmChanged(); + bool changed = false; + DateTime next; + if (!earliestId.isEmpty()) + next = resource.event(earliestId).nextTrigger(KAEvent::ALL_TRIGGER); + if (earliestId.isEmpty() || dt < next) + { + mEarliestAlarm[key] = event.id(); + changed = true; + } + if (!(event.actionTypes() & KAEvent::ACT_DISPLAY)) + { + // It is not a display event. + DateTime nextNonDisp; + if (!earliestNonDispId.isEmpty()) + nextNonDisp = (earliestId == earliestNonDispId) ? next : resource.event(earliestNonDispId).nextTrigger(KAEvent::ALL_TRIGGER); + if (earliestNonDispId.isEmpty() || dt < nextNonDisp) + { + mEarliestNonDispAlarm[key] = event.id(); + changed = true; + } + } + if (changed) + Q_EMIT earliestAlarmChanged(); } } } @@ -435,7 +458,8 @@ CalEvent::Type ResourcesCalendar::deleteEventInternal(const QString& eventID, co { const ResourceId key = resource.id(); mResourceMap[key].remove(eventID); - if (mEarliestAlarm.value(key) == eventID) + if (mEarliestAlarm.value(key) == eventID + || mEarliestNonDispAlarm.value(key) == eventID) mInstance->findEarliestAlarm(resource); CalEvent::Type status = CalEvent::EMPTY; @@ -609,7 +633,8 @@ void ResourcesCalendar::checkForDisabledAlarms() } /****************************************************************************** -* Find and note the active alarm with the earliest trigger time for a calendar. +* Find and note the active alarm with the earliest trigger time for a calendar, +* and the non-display active alarm with the earliest trigger time. */ void ResourcesCalendar::findEarliestAlarm(const Resource& resource) { @@ -618,28 +643,46 @@ void ResourcesCalendar::findEarliestAlarm(const Resource& resource) return; if (!(resource.alarmTypes() & CalEvent::ACTIVE)) return; + + // Invalidate any existing earliest alarms for the resource EarliestMap::Iterator eit = mEarliestAlarm.find(key); if (eit != mEarliestAlarm.end()) eit.value() = QString(); + eit = mEarliestNonDispAlarm.find(key); + if (eit != mEarliestNonDispAlarm.end()) + eit.value() = QString(); + ResourceMap::ConstIterator rit = mResourceMap.constFind(key); if (rit == mResourceMap.constEnd()) return; const QVector<KAEvent> events = eventsForResource(resource, rit.value()); - KAEvent earliest; - KADateTime earliestTime; + KAEvent earliest, earliestNonDisp; + KADateTime earliestTime, earliestNonDispTime; for (const KAEvent& event : events) { if (event.category() != CalEvent::ACTIVE || mPendingAlarms.contains(event.id())) continue; const KADateTime dt = event.nextTrigger(KAEvent::ALL_TRIGGER).effectiveKDateTime(); - if (dt.isValid() && (!earliest.isValid() || dt < earliestTime)) + if (dt.isValid()) { - earliestTime = dt; - earliest = event; + if (!earliest.isValid() || dt < earliestTime) + { + earliestTime = dt; + earliest = event; + } + if (!(event.actionTypes() & KAEvent::ACT_DISPLAY)) + { + if (!earliestNonDisp.isValid() || dt < earliestNonDispTime) + { + earliestNonDispTime = dt; + earliestNonDisp = event; + } + } } } - mEarliestAlarm[key] = earliest.id(); + mEarliestAlarm[key] = earliest.id(); + mEarliestNonDispAlarm[key] = earliestNonDisp.id(); Q_EMIT earliestAlarmChanged(); } @@ -647,11 +690,12 @@ void ResourcesCalendar::findEarliestAlarm(const Resource& resource) * Return the active alarm with the earliest trigger time. * Reply = invalid if none. */ -KAEvent ResourcesCalendar::earliestAlarm(KADateTime& nextTriggerTime) +KAEvent ResourcesCalendar::earliestAlarm(KADateTime& nextTriggerTime, bool excludeDisplayAlarms) { KAEvent earliest; KADateTime earliestTime; - for (EarliestMap::ConstIterator eit = mEarliestAlarm.constBegin(); eit != mEarliestAlarm.constEnd(); ++eit) + const EarliestMap& earliestAlarms(excludeDisplayAlarms ? mEarliestNonDispAlarm : mEarliestAlarm); + for (EarliestMap::ConstIterator eit = earliestAlarms.constBegin(); eit != earliestAlarms.constEnd(); ++eit) { const QString id = eit.value(); if (id.isEmpty()) @@ -663,8 +707,9 @@ KAEvent ResourcesCalendar::earliestAlarm(KADateTime& nextTriggerTime) // Something went wrong: mEarliestAlarm wasn't updated when it should have been!! qCCritical(KALARM_LOG) << "ResourcesCalendar::earliestAlarm: resource" << eit.key() << "does not contain" << id; mInstance->findEarliestAlarm(res); - return earliestAlarm(nextTriggerTime); + return earliestAlarm(nextTriggerTime, excludeDisplayAlarms); } +//TODO: use next trigger calculated in findEarliestAlarm() (allowing for it being out of date)? const KADateTime dt = event.nextTrigger(KAEvent::ALL_TRIGGER).effectiveKDateTime(); if (dt.isValid() && (!earliest.isValid() || dt < earliestTime)) { diff --git a/src/resourcescalendar.h b/src/resourcescalendar.h index 04c65282..f679e7f6 100644 --- a/src/resourcescalendar.h +++ b/src/resourcescalendar.h @@ -38,10 +38,11 @@ public: static void terminate(); /** Return the active alarm with the earliest trigger time. - * @param nextTriggerTime The next trigger time of the earliest alarm. + * @param nextTriggerTime The next trigger time of the earliest alarm. + * @param excludeDisplayAlarms Ignore display alarms. * @return The earliest alarm. */ - static KAEvent earliestAlarm(KADateTime& nextTriggerTime); + static KAEvent earliestAlarm(KADateTime& nextTriggerTime, bool excludeDisplayAlarms = false); static void setAlarmPending(const KAEvent&, bool pending = true); static bool haveDisabledAlarms() { return mHaveDisabledAlarms; } @@ -90,7 +91,8 @@ private: typedef QHash<ResourceId, QString> EarliestMap; // event ID of earliest alarm, for each resource static ResourceMap mResourceMap; - static EarliestMap mEarliestAlarm; // alarm with earliest trigger time, by resource + static EarliestMap mEarliestAlarm; // alarm with earliest trigger time, by resource + static EarliestMap mEarliestNonDispAlarm; // non-display alarm with earliest trigger time, by resource static QSet<QString> mPendingAlarms; // IDs of alarms which are currently being processed after triggering static bool mIgnoreAtLogin; // ignore new/updated repeat-at-login alarms static bool mHaveDisabledAlarms; // there is at least one individually disabled alarm
