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: