File indexing completed on 2024-05-12 05:14:57
0001 /* 0002 * resourcescalendar.cpp - KAlarm calendar resources access 0003 * Program: kalarm 0004 * SPDX-FileCopyrightText: 2001-2023 David Jarvie <djarvie@kde.org> 0005 * 0006 * SPDX-License-Identifier: GPL-2.0-or-later 0007 */ 0008 0009 #include "resourcescalendar.h" 0010 0011 #include "eventid.h" 0012 #include "kalarmapp.h" 0013 #include "resources/resources.h" 0014 #include "kalarm_debug.h" 0015 0016 #include <KCalendarCore/CalFormat> 0017 0018 using namespace KAlarmCal; 0019 0020 0021 ResourcesCalendar* ResourcesCalendar::mInstance {nullptr}; 0022 ResourcesCalendar::ResourceMap ResourcesCalendar::mResourceMap; 0023 ResourcesCalendar::EarliestMap ResourcesCalendar::mEarliestAlarm; 0024 ResourcesCalendar::EarliestMap ResourcesCalendar::mEarliestNonDispAlarm; 0025 QSet<QString> ResourcesCalendar::mPendingAlarms; 0026 bool ResourcesCalendar::mIgnoreAtLogin {false}; 0027 bool ResourcesCalendar::mHaveDisabledAlarms {false}; 0028 QHash<ResourceId, QHash<QString, KernelWakeAlarm>> ResourcesCalendar::mWakeSuspendTimers; 0029 0030 0031 /****************************************************************************** 0032 * Initialise the resource alarm calendars, and ensure that their file names are 0033 * different. The resources calendar contains the active alarms, archived alarms 0034 * and alarm templates; 0035 */ 0036 void ResourcesCalendar::initialise(const QByteArray& appName, const QByteArray& appVersion) 0037 { 0038 if (!mInstance) 0039 { 0040 KACalendar::setProductId(appName, appVersion); 0041 mInstance = new ResourcesCalendar(); 0042 } 0043 } 0044 0045 /****************************************************************************** 0046 * Terminate access to the resource calendars. 0047 */ 0048 void ResourcesCalendar::terminate() 0049 { 0050 delete mInstance; 0051 mInstance = nullptr; 0052 } 0053 0054 /****************************************************************************** 0055 * Constructor for the resources calendar. 0056 */ 0057 ResourcesCalendar::ResourcesCalendar() 0058 { 0059 Resources* resources = Resources::instance(); 0060 connect(resources, &Resources::resourceAdded, this, &ResourcesCalendar::slotResourceAdded); 0061 connect(resources, &Resources::eventsAdded, this, &ResourcesCalendar::slotEventsAdded); 0062 connect(resources, &Resources::eventsToBeRemoved, this, &ResourcesCalendar::slotEventsToBeRemoved); 0063 connect(resources, &Resources::eventUpdated, this, &ResourcesCalendar::slotEventUpdated); 0064 connect(resources, &Resources::resourcesPopulated, this, &ResourcesCalendar::slotResourcesPopulated); 0065 connect(resources, &Resources::settingsChanged, this, &ResourcesCalendar::slotResourceSettingsChanged); 0066 connect(theApp(), &KAlarmApp::alarmEnabledToggled, this, &ResourcesCalendar::slotAlarmsEnabledToggled); 0067 Preferences::connect(&Preferences::wakeFromSuspendAdvanceChanged, this, &ResourcesCalendar::slotWakeFromSuspendAdvanceChanged); 0068 0069 // Fetch events from all resources which already exist. 0070 QList<Resource> allResources = Resources::enabledResources(); 0071 for (Resource& resource : allResources) 0072 slotResourceAdded(resource); 0073 } 0074 0075 ResourcesCalendar::~ResourcesCalendar() 0076 { 0077 // Resource map should be empty, but just in case... 0078 while (!mResourceMap.isEmpty()) 0079 removeKAEvents(mResourceMap.constBegin().key(), true, CalEvent::ACTIVE | CalEvent::ARCHIVED | CalEvent::TEMPLATE | CalEvent::DISPLAYING); 0080 } 0081 0082 /****************************************************************************** 0083 * Delete a calendar and all its KAEvent instances of specified alarm types from 0084 * the lists. 0085 * Called after the calendar is deleted or alarm types have been disabled, or 0086 * the ResourcesCalendar is closed. 0087 */ 0088 void ResourcesCalendar::removeKAEvents(ResourceId key, bool closing, CalEvent::Types types) 0089 { 0090 bool removed = false; 0091 ResourceMap::Iterator rit = mResourceMap.find(key); 0092 if (rit != mResourceMap.end()) 0093 { 0094 Resource resource = Resources::resource(key); 0095 QSet<QString> retained; 0096 QSet<QString>& eventIds = rit.value(); 0097 for (auto it = eventIds.constBegin(); it != eventIds.constEnd(); ++it) 0098 { 0099 const KAEvent event = resource.event(*it, true); 0100 bool remove = (event.resourceId() != key); 0101 if (remove) 0102 qCCritical(KALARM_LOG) << "ResourcesCalendar::removeKAEvents: Event" << event.id() << ", resource" << event.resourceId() << "Indexed under resource" << key; 0103 else 0104 remove = event.category() & types; 0105 if (remove) 0106 removed = true; 0107 else 0108 retained.insert(*it); 0109 } 0110 if (retained.isEmpty()) 0111 mResourceMap.erase(rit); 0112 else 0113 eventIds.swap(retained); 0114 } 0115 if (removed) 0116 { 0117 mEarliestAlarm.remove(key); 0118 mEarliestNonDispAlarm.remove(key); 0119 // Emit signal only if we're not in the process of closing the calendar 0120 if (!closing) 0121 { 0122 Q_EMIT earliestAlarmChanged(); 0123 if (mHaveDisabledAlarms) 0124 checkForDisabledAlarms(); 0125 } 0126 } 0127 } 0128 0129 /****************************************************************************** 0130 * Called when the enabled or read-only status of a resource has changed. 0131 * If the resource is now disabled, remove its events from the calendar. 0132 */ 0133 void ResourcesCalendar::slotResourceSettingsChanged(Resource& resource, ResourceType::Changes change) 0134 { 0135 if (change & ResourceType::Enabled) 0136 { 0137 if (resource.isValid()) 0138 { 0139 // For each alarm type which has been disabled, remove the 0140 // resource's events from the map, but not from the resource. 0141 const CalEvent::Types enabled = resource.enabledTypes(); 0142 const CalEvent::Types disabled = ~enabled & (CalEvent::ACTIVE | CalEvent::ARCHIVED | CalEvent::TEMPLATE); 0143 removeKAEvents(resource.id(), false, disabled); 0144 0145 // For each alarm type which has been enabled, add the resource's 0146 // events to the map. 0147 if (enabled != CalEvent::EMPTY) 0148 slotEventsAdded(resource, resource.events()); 0149 } 0150 } 0151 } 0152 0153 /****************************************************************************** 0154 * Called when all resources have been populated for the first time. 0155 */ 0156 void ResourcesCalendar::slotResourcesPopulated() 0157 { 0158 // Now that all calendars have been processed, all repeat-at-login alarms 0159 // will have been triggered. Prevent any new or updated repeat-at-login 0160 // alarms (e.g. when they are edited by the user) triggering from now on. 0161 mIgnoreAtLogin = true; 0162 } 0163 0164 /****************************************************************************** 0165 * Called when a resource has been added. 0166 * Add its KAEvent instances to those held by ResourcesCalendar. 0167 * All events must have their resource ID set. 0168 */ 0169 void ResourcesCalendar::slotResourceAdded(Resource& resource) 0170 { 0171 slotEventsAdded(resource, resource.events()); 0172 } 0173 0174 /****************************************************************************** 0175 * Called when events have been added to a resource. 0176 * Record that the event is now usable by the ResourcesCalendar. 0177 * Update the earliest alarm for the resource. 0178 */ 0179 void ResourcesCalendar::slotEventsAdded(Resource& resource, const QList<KAEvent>& events) 0180 { 0181 for (const KAEvent& event : events) 0182 slotEventUpdated(resource, event); 0183 } 0184 0185 /****************************************************************************** 0186 * Called when an event has been changed in a resource. 0187 * Record that the event is now usable by the ResourcesCalendar. 0188 * Update the earliest alarm for the resource. 0189 */ 0190 void ResourcesCalendar::slotEventUpdated(Resource& resource, const KAEvent& event) 0191 { 0192 const ResourceId key = resource.id(); 0193 const bool added = !mResourceMap[key].contains(event.id()); 0194 qCDebug(KALARM_LOG) << "ResourcesCalendar::slotEventUpdated: resource" << resource.displayId() << (added ? "added" : "updated") << event.id(); 0195 mResourceMap[key].insert(event.id()); 0196 0197 if ((resource.alarmTypes() & CalEvent::ACTIVE) 0198 && event.category() == CalEvent::ACTIVE) 0199 { 0200 // Set/clear wake from suspend timer if needed 0201 checkKernelWakeSuspend(key, event); 0202 0203 // Update the earliest alarm to trigger 0204 const QString earliestId = mEarliestAlarm.value(key); 0205 const QString earliestNonDispId = mEarliestNonDispAlarm.value(key); 0206 if (earliestId == event.id() || earliestNonDispId == event.id()) 0207 findEarliestAlarm(resource); 0208 else 0209 { 0210 const KADateTime dt = event.nextTrigger(KAEvent::Trigger::All).effectiveKDateTime(); 0211 if (dt.isValid()) 0212 { 0213 bool changed = false; 0214 DateTime next; 0215 if (!earliestId.isEmpty()) 0216 next = resource.event(earliestId).nextTrigger(KAEvent::Trigger::All); 0217 if (earliestId.isEmpty() || dt < next) 0218 { 0219 mEarliestAlarm[key] = event.id(); 0220 changed = true; 0221 } 0222 if (!(event.actionTypes() & KAEvent::Action::Display)) 0223 { 0224 // It is not a display event. 0225 DateTime nextNonDisp; 0226 if (!earliestNonDispId.isEmpty()) 0227 nextNonDisp = (earliestId == earliestNonDispId) ? next : resource.event(earliestNonDispId).nextTrigger(KAEvent::Trigger::All); 0228 if (earliestNonDispId.isEmpty() || dt < nextNonDisp) 0229 { 0230 mEarliestNonDispAlarm[key] = event.id(); 0231 changed = true; 0232 } 0233 } 0234 if (changed) 0235 Q_EMIT earliestAlarmChanged(); 0236 } 0237 } 0238 } 0239 0240 if (event.category() == CalEvent::ACTIVE) 0241 { 0242 bool enabled = event.enabled(); 0243 checkForDisabledAlarms(!enabled, enabled); 0244 if (!mIgnoreAtLogin && added && enabled && event.repeatAtLogin()) 0245 Q_EMIT atLoginEventAdded(event); 0246 } 0247 } 0248 0249 /****************************************************************************** 0250 * Called when events are about to be removed from a resource. 0251 * Remove the corresponding KAEvent instances held by ResourcesCalendar. 0252 */ 0253 void ResourcesCalendar::slotEventsToBeRemoved(Resource& resource, const QList<KAEvent>& events) 0254 { 0255 const ResourceId key = resource.id(); 0256 for (const KAEvent& event : events) 0257 { 0258 if (mResourceMap.value(key).contains(event.id())) 0259 deleteEventInternal(event, resource, false); 0260 } 0261 } 0262 0263 /****************************************************************************** 0264 * Called when alarm monitoring has been enabled or disabled (for all alarms). 0265 * Arm or disarm kernel wake alarms for all events which use them. 0266 */ 0267 void ResourcesCalendar::slotAlarmsEnabledToggled(bool enabled) 0268 { 0269 if (!KernelWakeAlarm::isAvailable()) 0270 return; 0271 0272 if (enabled) 0273 { 0274 // Set kernel wake timers for all events which require them. 0275 setKernelWakeSuspend(); 0276 } 0277 else 0278 { 0279 // Disarm all kernel wake timers (but don't delete them). 0280 for (auto itr = mWakeSuspendTimers.begin(), endr = mWakeSuspendTimers.end(); itr != endr; ++itr) 0281 { 0282 auto& resourceHash = itr.value(); 0283 for (auto it = resourceHash.begin(), end = resourceHash.end(); it != end; ++it) 0284 it.value().disarm(); 0285 } 0286 } 0287 } 0288 0289 /****************************************************************************** 0290 * Called when the wake-from-suspend wakeup advance interval has changed. 0291 * Revise all kernel wake alarm times. 0292 * Note that the user only has the option to change the wakeup advance if kernel 0293 * wake alarms are used, and not if the RTC wake timer is used. 0294 */ 0295 void ResourcesCalendar::slotWakeFromSuspendAdvanceChanged(unsigned advance) 0296 { 0297 if (!KernelWakeAlarm::isAvailable() || !theApp()->alarmsEnabled()) 0298 return; 0299 0300 qCDebug(KALARM_LOG) << "ResourcesCalendar::slotWakeFromSuspendAdvanceChanged:" << advance; 0301 setKernelWakeSuspend(); 0302 } 0303 0304 /****************************************************************************** 0305 * This method must only be called from the main KAlarm queue processing loop, 0306 * to prevent asynchronous calendar operations interfering with one another. 0307 * 0308 * Purge a list of archived events from the calendar. 0309 */ 0310 void ResourcesCalendar::purgeEvents(const QList<KAEvent>& events) 0311 { 0312 for (const KAEvent& event : events) 0313 { 0314 Resource resource = Resources::resource(event.resourceId()); 0315 if (resource.isValid()) 0316 deleteEventInternal(event.id(), event, resource, true); 0317 } 0318 if (mHaveDisabledAlarms) 0319 mInstance->checkForDisabledAlarms(); 0320 } 0321 0322 /****************************************************************************** 0323 * Add the specified event to the calendar. 0324 * If it is an active event and 'useEventID' is false, a new event ID is 0325 * created. In all other cases, the event ID is taken from 'evnt' (if non-null). 0326 * 'evnt' is updated with the actual event ID. 0327 * The event is added to 'resource' if specified; otherwise the default resource 0328 * is used or the user is prompted, depending on policy, and 'resource' is 0329 * updated with the actual resource used. If 'noPrompt' is true, the user will 0330 * not be prompted so that if no default resource is defined, the function will 0331 * fail. 0332 * Reply = true if 'evnt' was written to the calendar. 'evnt' is updated. 0333 * = false if an error occurred, in which case 'evnt' is unchanged. 0334 */ 0335 bool ResourcesCalendar::addEvent(KAEvent& evnt, Resource& resource, QWidget* promptParent, AddEventOptions options, bool* cancelled) 0336 { 0337 bool useEventID = options & UseEventId; 0338 if (cancelled) 0339 *cancelled = false; 0340 qCDebug(KALARM_LOG) << "ResourcesCalendar::addEvent:" << evnt.id() << ", resource" << resource.displayId(); 0341 0342 // Check that the event type is valid for the calendar 0343 const CalEvent::Type type = evnt.category(); 0344 switch (type) 0345 { 0346 case CalEvent::ACTIVE: 0347 case CalEvent::ARCHIVED: 0348 case CalEvent::TEMPLATE: 0349 break; 0350 default: 0351 return false; 0352 } 0353 0354 KAEvent event = evnt; 0355 QString id = event.id(); 0356 if (type == CalEvent::ACTIVE) 0357 { 0358 if (id.isEmpty()) 0359 useEventID = false; 0360 else if (!useEventID) 0361 id.clear(); 0362 } 0363 else 0364 useEventID = true; 0365 if (id.isEmpty()) 0366 id = KCalendarCore::CalFormat::createUniqueId(); 0367 if (useEventID) 0368 id = CalEvent::uid(id, type); // include the alarm type tag in the ID 0369 event.setEventId(id); 0370 0371 bool ok = false; 0372 if (!resource.isEnabled(type)) 0373 { 0374 Resources::DestOptions destOptions {}; 0375 if (options & NoResourcePrompt) 0376 destOptions |= Resources::NoResourcePrompt; 0377 resource = Resources::destination(type, promptParent, destOptions, cancelled); 0378 if (!resource.isValid()) 0379 qCWarning(KALARM_LOG) << "ResourcesCalendar::addEvent: Error! Cannot create" << type << "(No default calendar is defined)"; 0380 } 0381 if (resource.isValid()) 0382 { 0383 // Don't add event to mResourceMap yet - its ID is not yet known. 0384 // It will be added after it is inserted into the data model, when 0385 // the resource signals eventsAdded(). 0386 ok = resource.addEvent(event); 0387 if (ok && type == CalEvent::ACTIVE && !event.enabled()) 0388 mInstance->checkForDisabledAlarms(true, false); 0389 event.setResourceId(resource.id()); 0390 } 0391 if (ok) 0392 evnt = event; 0393 return ok; 0394 } 0395 0396 /****************************************************************************** 0397 * Modify the specified event in the calendar with its new contents. 0398 * The new event must have a different event ID from the old one; if it does not 0399 * have an ID, it will be updated with a new ID. 0400 * It is assumed to be of the same event type as the old one (active, etc.) 0401 * Reply = true if 'newEvent' was written to the calendar. 'newEvent' is updated. 0402 * = false if an error occurred, in which case 'newEvent' is unchanged. 0403 */ 0404 bool ResourcesCalendar::modifyEvent(const EventId& oldEventId, KAEvent& newEvent) 0405 { 0406 const EventId newId(oldEventId.resourceId(), newEvent.id()); 0407 bool noNewId = newId.isEmpty(); 0408 if (!noNewId && oldEventId == newId) 0409 { 0410 qCCritical(KALARM_LOG) << "ResourcesCalendar::modifyEvent: Same IDs" << oldEventId; 0411 return false; 0412 } 0413 0414 // Set the event's ID, and update the old event in the resources calendar. 0415 if (!mResourceMap.value(oldEventId.resourceId()).contains(oldEventId.eventId())) 0416 { 0417 qCCritical(KALARM_LOG) << "ResourcesCalendar::modifyEvent: Old event not found" << oldEventId; 0418 return false; 0419 } 0420 Resource resource = Resources::resource(oldEventId.resourceId()); 0421 if (!resource.isValid()) 0422 { 0423 qCCritical(KALARM_LOG) << "ResourcesCalendar::modifyEvent: Old event's resource not found" << oldEventId; 0424 return false; 0425 } 0426 const KAEvent oldEvent = resource.event(oldEventId.eventId()); 0427 if (noNewId) 0428 newEvent.setEventId(KCalendarCore::CalFormat::createUniqueId()); 0429 qCDebug(KALARM_LOG) << "ResourcesCalendar::modifyEvent:" << oldEventId << "->" << newEvent.id(); 0430 0431 // Don't add new event to mResourceMap yet - it will be added when the resource 0432 // signals eventsAdded(). 0433 if (!resource.addEvent(newEvent)) 0434 return false; 0435 deleteEventInternal(oldEvent, resource); 0436 if (mHaveDisabledAlarms) 0437 mInstance->checkForDisabledAlarms(); 0438 return true; 0439 } 0440 0441 /****************************************************************************** 0442 * Update the specified event in the calendar with its new contents. 0443 * The event retains the same ID. The event must be in the resource calendar, 0444 * and must have its resourceId() set correctly. 0445 * Reply = event which has been updated 0446 * = invalid if error. 0447 */ 0448 KAEvent ResourcesCalendar::updateEvent(const KAEvent& evnt, bool saveIfReadOnly) 0449 { 0450 if (mResourceMap.value(evnt.resourceId()).contains(evnt.id())) 0451 { 0452 Resource resource = Resources::resource(evnt.resourceId()); 0453 if (resource.updateEvent(evnt, saveIfReadOnly)) 0454 { 0455 // Set/clear wake from suspend timer if needed 0456 checkKernelWakeSuspend(resource.id(), evnt); 0457 return evnt; 0458 } 0459 } 0460 qCDebug(KALARM_LOG) << "ResourcesCalendar::updateEvent: error" << evnt.id(); 0461 return {}; 0462 } 0463 0464 0465 /****************************************************************************** 0466 * Delete the specified event from the resource calendar, if it exists. 0467 * The calendar is then optionally saved. 0468 */ 0469 bool ResourcesCalendar::deleteEvent(const KAEvent& event, Resource& resource, bool saveit) 0470 { 0471 Q_UNUSED(saveit); 0472 0473 if (!resource.isValid()) 0474 { 0475 resource = Resources::resource(event.resourceId()); 0476 if (!resource.isValid()) 0477 { 0478 qCDebug(KALARM_LOG) << "ResourcesCalendar::deleteEvent: Resource not found for" << event.id(); 0479 return false; 0480 } 0481 } 0482 else if (!resource.containsEvent(event.id())) 0483 { 0484 qCDebug(KALARM_LOG) << "ResourcesCalendar::deleteEvent: Event" << event.id() << "not in resource" << resource.displayId(); 0485 return false; 0486 } 0487 qCDebug(KALARM_LOG) << "ResourcesCalendar::deleteEvent:" << event.id(); 0488 const CalEvent::Type status = deleteEventInternal(event.id(), event, resource, true); 0489 if (mHaveDisabledAlarms) 0490 mInstance->checkForDisabledAlarms(); 0491 return status != CalEvent::EMPTY; 0492 } 0493 0494 /****************************************************************************** 0495 * Internal method to delete the specified event from the calendar and lists. 0496 * Reply = event status, if it was found in the resource calendar/calendar 0497 * resource or local calendar 0498 * = CalEvent::EMPTY otherwise. 0499 */ 0500 CalEvent::Type ResourcesCalendar::deleteEventInternal(const KAEvent& event, Resource& resource, bool deleteFromResource) 0501 { 0502 if (!resource.isValid()) 0503 return CalEvent::EMPTY; 0504 if (event.resourceId() != resource.id()) 0505 { 0506 qCCritical(KALARM_LOG) << "ResourcesCalendar::deleteEventInternal: Event" << event.id() << ": resource" << event.resourceId() << "differs from 'resource'" << resource.id(); 0507 return CalEvent::EMPTY; 0508 } 0509 return deleteEventInternal(event.id(), event, resource, deleteFromResource); 0510 } 0511 0512 CalEvent::Type ResourcesCalendar::deleteEventInternal(const QString& eventID, const KAEvent& event, Resource& resource, bool deleteFromResource) 0513 { 0514 const ResourceId key = resource.id(); 0515 0516 if (mWakeSuspendTimers.contains(key)) 0517 mWakeSuspendTimers[key].remove(eventID); // this cancels the timer 0518 0519 mResourceMap[key].remove(eventID); 0520 if (mEarliestAlarm.value(key) == eventID 0521 || mEarliestNonDispAlarm.value(key) == eventID) 0522 mInstance->findEarliestAlarm(resource); 0523 0524 CalEvent::Type status = CalEvent::EMPTY; 0525 if (deleteFromResource) 0526 { 0527 // Delete from the resource. 0528 CalEvent::Type s = event.category(); 0529 if (resource.deleteEvent(event)) 0530 status = s; 0531 } 0532 return status; 0533 } 0534 0535 /****************************************************************************** 0536 * Return the event with the specified ID. 0537 * If 'findUniqueId' is true, and the resource ID is invalid, if there is a 0538 * unique event with the given ID, it will be returned. 0539 */ 0540 KAEvent ResourcesCalendar::event(const EventId& uniqueID, bool findUniqueId) 0541 { 0542 const QString eventId = uniqueID.eventId(); 0543 const ResourceId resourceId = uniqueID.resourceId(); 0544 if (resourceId == -1 && findUniqueId) 0545 { 0546 // The resource isn't known, but use the event ID if it is unique among 0547 // all resources. 0548 const QList<KAEvent> list = events(eventId); 0549 if (list.count() > 1) 0550 { 0551 qCWarning(KALARM_LOG) << "ResourcesCalendar::event: Multiple events found with ID" << eventId; 0552 return {}; 0553 } 0554 if (list.isEmpty()) 0555 return {}; 0556 return list[0]; 0557 } 0558 0559 // The resource is specified. 0560 if (!mResourceMap.value(resourceId).contains(eventId)) 0561 return {}; 0562 return Resources::resource(resourceId).event(eventId); 0563 } 0564 0565 /****************************************************************************** 0566 * Find the alarm template with the specified name. 0567 * Reply = 0 if not found. 0568 */ 0569 KAEvent ResourcesCalendar::templateEvent(const QString& templateName) 0570 { 0571 if (templateName.isEmpty()) 0572 return {}; 0573 const QList<KAEvent> eventlist = events(CalEvent::TEMPLATE); 0574 for (const KAEvent& event : eventlist) 0575 { 0576 if (event.name() == templateName) 0577 return event; 0578 } 0579 return {}; 0580 } 0581 0582 /****************************************************************************** 0583 * Return all events with the specified ID, from all calendars. 0584 */ 0585 QList<KAEvent> ResourcesCalendar::events(const QString& uniqueId) 0586 { 0587 QList<KAEvent> list; 0588 for (ResourceMap::ConstIterator rit = mResourceMap.constBegin(); rit != mResourceMap.constEnd(); ++rit) 0589 { 0590 if (rit.value().contains(uniqueId)) 0591 list += Resources::resource(rit.key()).event(uniqueId); 0592 } 0593 return list; 0594 } 0595 0596 QList<KAEvent> ResourcesCalendar::events(const Resource& resource, CalEvent::Types type) 0597 { 0598 return events(type, resource); 0599 } 0600 0601 QList<KAEvent> ResourcesCalendar::events(CalEvent::Types type) 0602 { 0603 Resource resource; 0604 return events(type, resource); 0605 } 0606 0607 QList<KAEvent> ResourcesCalendar::events(CalEvent::Types type, const Resource& resource) 0608 { 0609 QList<KAEvent> list; 0610 if (resource.isValid()) 0611 { 0612 const ResourceId key = resource.id(); 0613 ResourceMap::ConstIterator rit = mResourceMap.constFind(key); 0614 if (rit == mResourceMap.constEnd()) 0615 return list; 0616 const QList<KAEvent> events = eventsForResource(resource, rit.value()); 0617 if (type == CalEvent::EMPTY) 0618 return events; 0619 for (const KAEvent& event : events) 0620 if (type & event.category()) 0621 list += event; 0622 } 0623 else 0624 { 0625 for (ResourceMap::ConstIterator rit = mResourceMap.constBegin(); rit != mResourceMap.constEnd(); ++rit) 0626 { 0627 const Resource res = Resources::resource(rit.key()); 0628 const QList<KAEvent> events = eventsForResource(res, rit.value()); 0629 if (type == CalEvent::EMPTY) 0630 list += events; 0631 else 0632 { 0633 for (const KAEvent& event : events) 0634 if (type & event.category()) 0635 list += event; 0636 } 0637 } 0638 } 0639 return list; 0640 } 0641 0642 /****************************************************************************** 0643 * Called when an alarm's enabled status has changed. 0644 */ 0645 void ResourcesCalendar::disabledChanged(const KAEvent& event) 0646 { 0647 if (event.category() == CalEvent::ACTIVE) 0648 { 0649 bool status = event.enabled(); 0650 mInstance->checkForDisabledAlarms(!status, status); 0651 } 0652 } 0653 0654 /****************************************************************************** 0655 * Check whether there are any individual disabled alarms, following an alarm 0656 * creation or modification. Must only be called for an ACTIVE alarm. 0657 */ 0658 void ResourcesCalendar::checkForDisabledAlarms(bool oldEnabled, bool newEnabled) 0659 { 0660 if (newEnabled != oldEnabled) 0661 { 0662 if (newEnabled && mHaveDisabledAlarms) 0663 checkForDisabledAlarms(); 0664 else if (!newEnabled && !mHaveDisabledAlarms) 0665 { 0666 mHaveDisabledAlarms = true; 0667 Q_EMIT haveDisabledAlarmsChanged(true); 0668 } 0669 } 0670 } 0671 0672 /****************************************************************************** 0673 * Check whether there are any individual disabled alarms. 0674 */ 0675 void ResourcesCalendar::checkForDisabledAlarms() 0676 { 0677 bool disabled = false; 0678 const QList<KAEvent> eventlist = events(CalEvent::ACTIVE); 0679 for (const KAEvent& event : eventlist) 0680 { 0681 if (!event.enabled()) 0682 { 0683 disabled = true; 0684 break; 0685 } 0686 } 0687 if (disabled != mHaveDisabledAlarms) 0688 { 0689 mHaveDisabledAlarms = disabled; 0690 Q_EMIT haveDisabledAlarmsChanged(disabled); 0691 } 0692 } 0693 0694 /****************************************************************************** 0695 * Set kernel wake alarm timers for all events which require them. 0696 */ 0697 void ResourcesCalendar::setKernelWakeSuspend() 0698 { 0699 for (auto itr = mWakeSuspendTimers.begin(), endr = mWakeSuspendTimers.end(); itr != endr; ++itr) 0700 { 0701 const ResourceId resourceId = itr.key(); 0702 Resource resource = Resources::resource(resourceId); 0703 auto& resourceHash = itr.value(); 0704 for (auto it = resourceHash.begin(), end = resourceHash.end(); it != end; ++it) 0705 { 0706 const KAEvent event = resource.event(it.key()); 0707 checkKernelWakeSuspend(resourceId, event); 0708 } 0709 } 0710 } 0711 0712 /****************************************************************************** 0713 * Set or clear any kernel wake alarm associated with an event. 0714 */ 0715 void ResourcesCalendar::checkKernelWakeSuspend(ResourceId key, const KAEvent& event) 0716 { 0717 if (KernelWakeAlarm::isAvailable() && event.enabled() && event.wakeFromSuspend()) 0718 { 0719 const KADateTime dt = event.nextDateTime(KAEvent::NextWorkHoliday).kDateTime(); 0720 if (!dt.isDateOnly()) // can't determine a wakeup time for date-only events 0721 { 0722 KernelWakeAlarm& kernelAlarm = mWakeSuspendTimers[key][event.id()]; 0723 if (theApp()->alarmsEnabled()) 0724 kernelAlarm.arm(dt.addSecs(static_cast<int>(Preferences::wakeFromSuspendAdvance()) * -60)); 0725 else 0726 kernelAlarm.disarm(); 0727 } 0728 } 0729 else 0730 { 0731 if (mWakeSuspendTimers.contains(key)) 0732 mWakeSuspendTimers[key].remove(event.id()); // this cancels the timer 0733 } 0734 } 0735 0736 /****************************************************************************** 0737 * Find and note the active alarm with the earliest trigger time for a calendar, 0738 * and the non-display active alarm with the earliest trigger time. 0739 */ 0740 void ResourcesCalendar::findEarliestAlarm(const Resource& resource) 0741 { 0742 ResourceId key = resource.id(); 0743 if (key < 0) 0744 return; 0745 if (!(resource.alarmTypes() & CalEvent::ACTIVE)) 0746 return; 0747 0748 // Invalidate any existing earliest alarms for the resource 0749 EarliestMap::Iterator eit = mEarliestAlarm.find(key); 0750 if (eit != mEarliestAlarm.end()) 0751 eit.value() = QString(); 0752 eit = mEarliestNonDispAlarm.find(key); 0753 if (eit != mEarliestNonDispAlarm.end()) 0754 eit.value() = QString(); 0755 0756 ResourceMap::ConstIterator rit = mResourceMap.constFind(key); 0757 if (rit == mResourceMap.constEnd()) 0758 return; 0759 const QList<KAEvent> events = eventsForResource(resource, rit.value()); 0760 KAEvent earliest, earliestNonDisp; 0761 KADateTime earliestTime, earliestNonDispTime; 0762 for (const KAEvent& event : events) 0763 { 0764 if (event.category() != CalEvent::ACTIVE 0765 || mPendingAlarms.contains(event.id())) 0766 continue; 0767 const KADateTime dt = event.nextTrigger(KAEvent::Trigger::All).effectiveKDateTime(); 0768 if (dt.isValid()) 0769 { 0770 if (!earliest.isValid() || dt < earliestTime) 0771 { 0772 earliestTime = dt; 0773 earliest = event; 0774 } 0775 if (!(event.actionTypes() & KAEvent::Action::Display)) 0776 { 0777 if (!earliestNonDisp.isValid() || dt < earliestNonDispTime) 0778 { 0779 earliestNonDispTime = dt; 0780 earliestNonDisp = event; 0781 } 0782 } 0783 } 0784 } 0785 mEarliestAlarm[key] = earliest.id(); 0786 mEarliestNonDispAlarm[key] = earliestNonDisp.id(); 0787 Q_EMIT earliestAlarmChanged(); 0788 } 0789 0790 /****************************************************************************** 0791 * Return the active alarm with the earliest trigger time. 0792 * Reply = invalid if none. 0793 */ 0794 KAEvent ResourcesCalendar::earliestAlarm(KADateTime& nextTriggerTime, bool excludeDisplayAlarms) 0795 { 0796 KAEvent earliest; 0797 KADateTime earliestTime; 0798 const EarliestMap& earliestAlarms(excludeDisplayAlarms ? mEarliestNonDispAlarm : mEarliestAlarm); 0799 for (EarliestMap::ConstIterator eit = earliestAlarms.constBegin(); eit != earliestAlarms.constEnd(); ++eit) 0800 { 0801 const QString id = eit.value(); 0802 if (id.isEmpty()) 0803 continue; 0804 Resource res = Resources::resource(eit.key()); 0805 const KAEvent event = res.event(id); 0806 if (!event.isValid()) 0807 { 0808 // Something went wrong: mEarliestAlarm wasn't updated when it should have been!! 0809 qCCritical(KALARM_LOG) << "ResourcesCalendar::earliestAlarm: resource" << eit.key() << "does not contain" << id; 0810 mInstance->findEarliestAlarm(res); 0811 return earliestAlarm(nextTriggerTime, excludeDisplayAlarms); 0812 } 0813 //TODO: use next trigger calculated in findEarliestAlarm() (allowing for it being out of date)? 0814 const KADateTime dt = event.nextTrigger(KAEvent::Trigger::All).effectiveKDateTime(); 0815 if (dt.isValid() && (!earliest.isValid() || dt < earliestTime)) 0816 { 0817 earliestTime = dt; 0818 earliest = event; 0819 } 0820 } 0821 nextTriggerTime = earliestTime; 0822 return earliest; 0823 } 0824 0825 /****************************************************************************** 0826 * Note that an alarm which has triggered is now being processed. While pending, 0827 * it will be ignored for the purposes of finding the earliest trigger time. 0828 */ 0829 void ResourcesCalendar::setAlarmPending(const KAEvent& event, bool pending) 0830 { 0831 const QString id = event.id(); 0832 bool wasPending = mPendingAlarms.contains(id); 0833 qCDebug(KALARM_LOG) << "ResourcesCalendar::setAlarmPending:" << id << "," << pending << "(was" << wasPending << ")"; 0834 if (pending) 0835 { 0836 if (wasPending) 0837 return; 0838 mPendingAlarms += id; 0839 } 0840 else 0841 { 0842 if (!wasPending) 0843 return; 0844 mPendingAlarms.remove(id); 0845 } 0846 // Now update the earliest alarm to trigger for its calendar 0847 mInstance->findEarliestAlarm(Resources::resourceForEvent(event.id())); 0848 } 0849 0850 /****************************************************************************** 0851 * Get the events for a list of event IDs. 0852 */ 0853 QList<KAEvent> ResourcesCalendar::eventsForResource(const Resource& resource, const QSet<QString>& eventIds) 0854 { 0855 QList<KAEvent> events; 0856 events.reserve(eventIds.count()); 0857 for (const QString& eventId : eventIds) 0858 events += resource.event(eventId); 0859 return events; 0860 } 0861 0862 #include "moc_resourcescalendar.cpp" 0863 0864 // vim: et sw=4: