File indexing completed on 2025-03-09 04:46:06
0001 /* 0002 * resourcetype.cpp - base class for an alarm calendar resource type 0003 * Program: kalarm 0004 * SPDX-FileCopyrightText: 2019-2023 David Jarvie <djarvie@kde.org> 0005 * 0006 * SPDX-License-Identifier: GPL-2.0-or-later 0007 */ 0008 0009 #include "resourcetype.h" 0010 0011 #include "resources.h" 0012 #include "preferences.h" 0013 #include "kalarm_debug.h" 0014 0015 #include <KColorScheme> 0016 #include <KColorUtils> 0017 #include <KLocalizedString> 0018 0019 ResourceType::ResourceType(ResourceId id) 0020 : mId(id) 0021 { 0022 } 0023 0024 ResourceType::~ResourceType() 0025 { 0026 } 0027 0028 bool ResourceType::failed() const 0029 { 0030 return mFailed || !isValid(); 0031 } 0032 0033 bool ResourceType::inError() const 0034 { 0035 return mInError || failed(); 0036 } 0037 0038 ResourceId ResourceType::displayId() const 0039 { 0040 return id(); 0041 } 0042 0043 bool ResourceType::isEnabled(CalEvent::Type type) const 0044 { 0045 return (type == CalEvent::EMPTY) ? enabledTypes() : enabledTypes() & type; 0046 } 0047 0048 bool ResourceType::isWritable(CalEvent::Type type) const 0049 { 0050 return writableStatus(type) == 1; 0051 } 0052 0053 /****************************************************************************** 0054 * Return the foreground colour for displaying a resource, based on the alarm 0055 * types which it contains, and on whether it is fully writable. 0056 */ 0057 QColor ResourceType::foregroundColour(CalEvent::Types types) const 0058 { 0059 if (types == CalEvent::EMPTY) 0060 types = alarmTypes(); 0061 else 0062 types &= alarmTypes(); 0063 0064 // Find the highest priority alarm type. 0065 // Note that resources currently only contain a single alarm type. 0066 CalEvent::Type type; 0067 QColor colour; // default to invalid colour 0068 if (types & CalEvent::ACTIVE) 0069 { 0070 type = CalEvent::ACTIVE; 0071 colour = KColorScheme(QPalette::Active).foreground(KColorScheme::NormalText).color(); 0072 } 0073 else if (types & CalEvent::ARCHIVED) 0074 { 0075 type = CalEvent::ARCHIVED; 0076 colour = Preferences::archivedColour(); 0077 } 0078 else if (types & CalEvent::TEMPLATE) 0079 { 0080 type = CalEvent::TEMPLATE; 0081 colour = KColorScheme(QPalette::Active).foreground(KColorScheme::LinkText).color(); 0082 } 0083 else 0084 type = CalEvent::EMPTY; 0085 0086 if (colour.isValid() && !isWritable(type)) 0087 return KColorUtils::lighten(colour, 0.2); 0088 return colour; 0089 } 0090 0091 bool ResourceType::isCompatible() const 0092 { 0093 return compatibility() == KACalendar::Current; 0094 } 0095 0096 KACalendar::Compat ResourceType::compatibility() const 0097 { 0098 QString versionString; 0099 return compatibilityVersion(versionString); 0100 } 0101 0102 /****************************************************************************** 0103 * Return all events belonging to this resource, for enabled alarm types. 0104 */ 0105 QList<KAEvent> ResourceType::events() const 0106 { 0107 // Remove any events with disabled alarm types. 0108 const CalEvent::Types types = enabledTypes(); 0109 QList<KAEvent> events; 0110 for (auto it = mEvents.begin(); it != mEvents.end(); ++it) 0111 { 0112 if (it.value().category() & types) 0113 events += it.value(); 0114 } 0115 return events; 0116 } 0117 0118 /****************************************************************************** 0119 * Return the event with the given ID, provided its alarm type is enabled for 0120 * the resource. 0121 */ 0122 KAEvent ResourceType::event(const QString& eventId, bool allowDisabled) const 0123 { 0124 auto it = mEvents.constFind(eventId); 0125 if (it != mEvents.constEnd() 0126 && (allowDisabled || (it.value().category() & enabledTypes()))) 0127 return it.value(); 0128 return {}; 0129 } 0130 0131 /****************************************************************************** 0132 * Return whether the resource contains the event whose ID is given, and if the 0133 * event's alarm type is enabled for the resource. 0134 */ 0135 bool ResourceType::containsEvent(const QString& eventId) const 0136 { 0137 auto it = mEvents.constFind(eventId); 0138 return it != mEvents.constEnd() 0139 && (it.value().category() & enabledTypes()); 0140 } 0141 0142 /****************************************************************************** 0143 * Called when the user changes the start-of-day time. 0144 * Adjust the start times of all date-only alarms' recurrences. 0145 */ 0146 void ResourceType::adjustStartOfDay() 0147 { 0148 KAEvent::List events; 0149 for (auto it = mEvents.begin(); it != mEvents.end(); ++it) 0150 events += &it.value(); 0151 KAEvent::adjustStartOfDay(events); 0152 } 0153 0154 void ResourceType::notifyDeletion() 0155 { 0156 mBeingDeleted = true; 0157 } 0158 0159 bool ResourceType::isBeingDeleted() const 0160 { 0161 return mBeingDeleted; 0162 } 0163 0164 bool ResourceType::addResource(ResourceType* type, Resource& resource) 0165 { 0166 return Resources::addResource(type, resource); 0167 } 0168 0169 void ResourceType::removeResource(ResourceId id) 0170 { 0171 // Set the resource instance invalid, to ensure that any other references 0172 // to it now see an invalid resource. 0173 Resource res = Resources::resource(id); 0174 ResourceType* tres = res.resource<ResourceType>(); 0175 if (tres) 0176 tres->mId = -1; // set the resource instance invalid 0177 Resources::removeResource(id); 0178 } 0179 0180 /****************************************************************************** 0181 * To be called when the resource has loaded, to update the list of loaded 0182 * events for the resource. 0183 * Added, updated and deleted events are notified, only for enabled alarm types. 0184 */ 0185 void ResourceType::setLoadedEvents(QHash<QString, KAEvent>& newEvents) 0186 { 0187 qCDebug(KALARM_LOG) << "ResourceType::setLoadedEvents: count" << newEvents.count(); 0188 const CalEvent::Types types = enabledTypes(); 0189 0190 // Replace existing events with the new ones, and find events which no 0191 // longer exist. 0192 QStringList eventsToDelete; 0193 QList<KAEvent> eventsToNotifyDelete; 0194 QList<KAEvent> eventsToNotifyNewlyEnabled; // only if mNewlyEnabled is true 0195 for (auto it = mEvents.begin(); it != mEvents.end(); ++it) 0196 { 0197 const QString& id = it.key(); 0198 auto newit = newEvents.find(id); 0199 if (newit == newEvents.end()) 0200 { 0201 eventsToDelete << id; 0202 if (it.value().category() & types) 0203 eventsToNotifyDelete << it.value(); // this event no longer exists 0204 } 0205 else 0206 { 0207 KAEvent& event = it.value(); 0208 bool changed = !event.compare(newit.value(), KAEvent::Compare::Id | KAEvent::Compare::CurrentState); 0209 event = newit.value(); // update existing event 0210 event.setResourceId(mId); 0211 newEvents.erase(newit); 0212 if (mNewlyEnabled) 0213 eventsToNotifyNewlyEnabled << event; 0214 if (changed && (event.category() & types)) 0215 Resources::notifyEventUpdated(this, event); 0216 } 0217 } 0218 0219 // Delete events which no longer exist. 0220 if (!eventsToNotifyDelete.isEmpty()) 0221 Resources::notifyEventsToBeRemoved(this, eventsToNotifyDelete); 0222 for (const QString& id : std::as_const(eventsToDelete)) 0223 mEvents.remove(id); 0224 if (!eventsToNotifyDelete.isEmpty()) 0225 Resources::notifyEventsRemoved(this, eventsToNotifyDelete); 0226 0227 // Add new events. 0228 for (auto newit = newEvents.begin(); newit != newEvents.end(); ) 0229 { 0230 newit.value().setResourceId(mId); 0231 mEvents[newit.key()] = newit.value(); 0232 if (newit.value().category() & types) 0233 ++newit; 0234 else 0235 newit = newEvents.erase(newit); // remove disabled event from notification list 0236 } 0237 if (!newEvents.isEmpty() || !eventsToNotifyNewlyEnabled.isEmpty()) 0238 Resources::notifyEventsAdded(this, newEvents.values() + eventsToNotifyNewlyEnabled); 0239 0240 newEvents.clear(); 0241 mNewlyEnabled = false; 0242 setLoaded(true); 0243 } 0244 0245 /****************************************************************************** 0246 * To be called when events have been created or updated, to amend them in the 0247 * resource's list. 0248 */ 0249 void ResourceType::setUpdatedEvents(const QList<KAEvent>& events, bool notify) 0250 { 0251 const CalEvent::Types types = enabledTypes(); 0252 mEventsAdded.clear(); 0253 mEventsUpdated.clear(); 0254 for (const KAEvent& event : events) 0255 { 0256 auto it = mEvents.find(event.id()); 0257 if (it == mEvents.end()) 0258 { 0259 KAEvent& ev = mEvents[event.id()]; 0260 ev = event; 0261 ev.setResourceId(mId); 0262 if (event.category() & types) 0263 mEventsAdded += ev; 0264 } 0265 else 0266 { 0267 KAEvent& ev = it.value(); 0268 bool changed = !ev.compare(event, KAEvent::Compare::Id | KAEvent::Compare::CurrentState); 0269 ev = event; // update existing event 0270 ev.setResourceId(mId); 0271 if (changed && (event.category() & types)) 0272 { 0273 if (notify) 0274 Resources::notifyEventUpdated(this, event); 0275 else 0276 mEventsUpdated += event; 0277 } 0278 } 0279 } 0280 if (notify && !mEventsAdded.isEmpty()) 0281 Resources::notifyEventsAdded(this, mEventsAdded); 0282 } 0283 0284 /****************************************************************************** 0285 * Notifies added and updated events, after setUpdatedEvents() was called with 0286 * notify = false. 0287 */ 0288 void ResourceType::notifyUpdatedEvents() 0289 { 0290 for (const KAEvent& event : std::as_const(mEventsUpdated)) 0291 Resources::notifyEventUpdated(this, event); 0292 mEventsUpdated.clear(); 0293 0294 if (!mEventsAdded.isEmpty()) 0295 Resources::notifyEventsAdded(this, mEventsAdded); 0296 mEventsAdded.clear(); 0297 } 0298 0299 /****************************************************************************** 0300 * To be called when events have been deleted, to delete them from the 0301 * resource's list. 0302 */ 0303 void ResourceType::setDeletedEvents(const QList<KAEvent>& events) 0304 { 0305 const CalEvent::Types types = enabledTypes(); 0306 QStringList eventsToDelete; 0307 QList<KAEvent> eventsToNotify; 0308 for (const KAEvent& event : events) 0309 { 0310 if (mEvents.constFind(event.id()) != mEvents.constEnd()) 0311 { 0312 eventsToDelete += event.id(); 0313 if (event.category() & types) 0314 eventsToNotify += event; 0315 } 0316 } 0317 if (!eventsToNotify.isEmpty()) 0318 Resources::notifyEventsToBeRemoved(this, eventsToNotify); 0319 for (const QString& id : std::as_const(eventsToDelete)) 0320 mEvents.remove(id); 0321 if (!eventsToNotify.isEmpty()) 0322 Resources::notifyEventsRemoved(this, eventsToNotify); 0323 } 0324 0325 void ResourceType::setLoaded(bool loaded) const 0326 { 0327 if (loaded != mLoaded) 0328 { 0329 mLoaded = loaded; 0330 Resources::notifyResourcePopulated(this); 0331 } 0332 } 0333 0334 void ResourceType::setFailed() 0335 { 0336 mFailed = true; 0337 } 0338 0339 void ResourceType::setError(bool error) 0340 { 0341 mInError = error; 0342 } 0343 0344 QString ResourceType::storageTypeString(Storage type) 0345 { 0346 switch (type) 0347 { 0348 case Storage::File: 0349 case Storage::Directory: 0350 return storageTypeStr(true, (type == Storage::File), true); 0351 default: 0352 return {}; 0353 } 0354 } 0355 0356 QString ResourceType::storageTypeStr(bool description, bool file, bool local) 0357 { 0358 if (description) 0359 return file ? i18nc("@item:inlistbox", "KAlarm Calendar File") : i18nc("@item:inlistbox", "KAlarm Calendar Directory"); 0360 return (file && local) ? i18nc("@item:intext What a resource is stored in", "File") 0361 : (file && !local) ? i18nc("@item:intext What a resource is stored in", "URL") 0362 : (!file && local) ? i18nc("@item:intext What a resource is stored in (directory in filesystem)", "Directory") 0363 : QString(); 0364 } 0365 0366 ResourceType* ResourceType::data(Resource& resource) 0367 { 0368 return resource.mResource.data(); 0369 } 0370 0371 const ResourceType* ResourceType::data(const Resource& resource) 0372 { 0373 return resource.mResource.data(); 0374 } 0375 0376 #include "moc_resourcetype.cpp" 0377 0378 // vim: et sw=4: