File indexing completed on 2025-02-16 04:48:49
0001 /* 0002 * fileresourcedatamodel.cpp - model containing file system resources and their events 0003 * Program: kalarm 0004 * SPDX-FileCopyrightText: 2007-2022 David Jarvie <djarvie@kde.org> 0005 * 0006 * SPDX-License-Identifier: GPL-2.0-or-later 0007 */ 0008 0009 #include "fileresourcedatamodel.h" 0010 0011 #include "fileresourcecalendarupdater.h" 0012 #include "fileresourcecreator.h" 0013 #include "migration/fileresourcemigrator.h" 0014 #include "eventmodel.h" 0015 #include "resourcemodel.h" 0016 #include "resources.h" 0017 0018 #include "preferences.h" 0019 #include "lib/synchtimer.h" 0020 #include "kalarm_debug.h" 0021 0022 // Represents a resource or event within the data model. 0023 struct FileResourceDataModel::Node 0024 { 0025 private: 0026 KAEvent* eitem; // if type Event, the KAEvent, which is owned by this instance 0027 Resource ritem; // if type Resource, the resource 0028 Resource owner; // resource containing this KAEvent, or null 0029 public: 0030 Type type; 0031 0032 explicit Node(Resource& r) : ritem(r), type(Type::Resource) {} 0033 Node(KAEvent* e, Resource& r) : eitem(e), owner(r), type(Type::Event) {} 0034 ~Node() 0035 { 0036 if (type == Type::Event) 0037 delete eitem; 0038 } 0039 Resource resource() const { return (type == Type::Resource) ? ritem : Resource(); } 0040 KAEvent* event() const { return (type == Type::Event) ? eitem : nullptr; } 0041 Resource parent() const { return (type == Type::Event) ? owner : Resource(); } 0042 }; 0043 0044 0045 bool FileResourceDataModel::mInstanceIsOurs = false; 0046 0047 /****************************************************************************** 0048 * Returns the unique instance, creating it first if necessary. 0049 */ 0050 FileResourceDataModel* FileResourceDataModel::instance(QObject* parent) 0051 { 0052 if (!mInstance) 0053 { 0054 auto inst = new FileResourceDataModel(parent); 0055 mInstance = inst; 0056 mInstanceIsOurs = true; 0057 inst->initialise(); 0058 } 0059 return mInstanceIsOurs ? (FileResourceDataModel*)mInstance : nullptr; 0060 } 0061 0062 /****************************************************************************** 0063 */ 0064 FileResourceDataModel::FileResourceDataModel(QObject* parent) 0065 : QAbstractItemModel(parent) 0066 , ResourceDataModelBase() 0067 , mHaveEvents(false) 0068 { 0069 qCDebug(KALARM_LOG) << "FileResourceDataModel::FileResourceDataModel"; 0070 0071 // Create the vector of resource nodes for the model root. 0072 mResourceNodes[Resource()]; 0073 0074 // Get a list of all resources, and their alarms, if they have already been 0075 // created before this, by a previous call to FileResourceConfigManager::createResources(). 0076 const QList<ResourceId> resourceIds = FileResourceConfigManager::resourceIds(); 0077 for (ResourceId id : resourceIds) 0078 { 0079 Resource resource = Resources::resource(id); 0080 if (!mResourceNodes.contains(resource)) 0081 addResource(resource); 0082 } 0083 0084 Resources* resources = Resources::instance(); 0085 connect(resources, &Resources::resourceAdded, 0086 this, &FileResourceDataModel::addResource); 0087 connect(resources, &Resources::resourcePopulated, 0088 this, &FileResourceDataModel::slotResourceLoaded); 0089 connect(resources, &Resources::resourceToBeRemoved, 0090 this, &FileResourceDataModel::removeResource); 0091 connect(resources, &Resources::eventsAdded, 0092 this, &FileResourceDataModel::slotEventsAdded); 0093 connect(resources, &Resources::eventUpdated, 0094 this, &FileResourceDataModel::slotEventUpdated); 0095 connect(resources, &Resources::eventsToBeRemoved, 0096 this, &FileResourceDataModel::deleteEvents); 0097 0098 connect(resources, &Resources::settingsChanged, 0099 this, &FileResourceDataModel::slotResourceSettingsChanged); 0100 connect(resources, &Resources::resourceMessage, 0101 this, &FileResourceDataModel::slotResourceMessage, Qt::QueuedConnection); 0102 0103 // Exit now, so that ResourceDataModelBase::mInstance will be set before 0104 // setCalendarsCreated() and setMigrationCompleted() are called in initialise(). 0105 } 0106 0107 /****************************************************************************** 0108 * Initialise the instance. To be called immediately after construction. 0109 */ 0110 void FileResourceDataModel::initialise() 0111 { 0112 FileResourceConfigManager::createResources(this); 0113 setCalendarsCreated(); 0114 0115 FileResourceMigrator* migrator = FileResourceMigrator::instance(); 0116 if (!migrator) 0117 setMigrationComplete(); 0118 else 0119 { 0120 connect(migrator, &QObject::destroyed, this, &FileResourceDataModel::slotMigrationCompleted); 0121 setMigrationInitiated(); 0122 migrator->start(); 0123 } 0124 0125 MinuteTimer::connect(this, SLOT(slotUpdateTimeTo())); 0126 Preferences::connect(&Preferences::archivedColourChanged, this, &FileResourceDataModel::slotUpdateArchivedColour); 0127 Preferences::connect(&Preferences::disabledColourChanged, this, &FileResourceDataModel::slotUpdateDisabledColour); 0128 Preferences::connect(&Preferences::holidaysChanged, this, &FileResourceDataModel::slotUpdateHolidays); 0129 Preferences::connect(&Preferences::workTimeChanged, this, &FileResourceDataModel::slotUpdateWorkingHours); 0130 } 0131 0132 /****************************************************************************** 0133 */ 0134 FileResourceDataModel::~FileResourceDataModel() 0135 { 0136 qCDebug(KALARM_LOG) << "FileResourceDataModel::~FileResourceDataModel"; 0137 ResourceFilterCheckListModel::disable(); // prevent resources being disabled when they are removed 0138 while (!mResources.isEmpty()) 0139 removeResource(mResources.first()); 0140 if (mInstance == this) 0141 { 0142 mInstance = nullptr; 0143 mInstanceIsOurs = false; 0144 } 0145 delete Resources::instance(); 0146 } 0147 0148 /****************************************************************************** 0149 * Return whether a model index refers to a resource or an event. 0150 */ 0151 FileResourceDataModel::Type FileResourceDataModel::type(const QModelIndex& ix) const 0152 { 0153 if (ix.isValid()) 0154 { 0155 const Node* node = reinterpret_cast<Node*>(ix.internalPointer()); 0156 if (node) 0157 return node->type; 0158 } 0159 return Type::Error; 0160 } 0161 0162 /****************************************************************************** 0163 * Return the resource with the given ID. 0164 */ 0165 Resource FileResourceDataModel::resource(ResourceId id) const 0166 { 0167 return Resource(Resources::resource(id)); 0168 } 0169 0170 /****************************************************************************** 0171 * Return the resource referred to by an index, or invalid resource if the index 0172 * is not for a resource. 0173 */ 0174 Resource FileResourceDataModel::resource(const QModelIndex& ix) const 0175 { 0176 if (ix.isValid()) 0177 { 0178 const Node* node = reinterpret_cast<Node*>(ix.internalPointer()); 0179 if (node) 0180 { 0181 Resource res = node->resource(); 0182 if (!res.isNull()) 0183 return res; 0184 } 0185 } 0186 return {}; 0187 } 0188 0189 /****************************************************************************** 0190 * Find the QModelIndex of a resource. 0191 */ 0192 QModelIndex FileResourceDataModel::resourceIndex(const Resource& resource) const 0193 { 0194 if (resource.isValid()) 0195 { 0196 int row = mResources.indexOf(const_cast<Resource&>(resource)); 0197 if (row >= 0) 0198 return createIndex(row, 0, mResourceNodes.value(Resource()).at(row)); 0199 } 0200 return {}; 0201 } 0202 0203 /****************************************************************************** 0204 * Find the QModelIndex of a resource. 0205 */ 0206 QModelIndex FileResourceDataModel::resourceIndex(ResourceId id) const 0207 { 0208 return resourceIndex(Resources::resource(id)); 0209 } 0210 0211 /****************************************************************************** 0212 * Return the event with the given ID. 0213 */ 0214 KAEvent FileResourceDataModel::event(const QString& eventId) const 0215 { 0216 const Node* node = mEventNodes.value(eventId, nullptr); 0217 if (node) 0218 { 0219 KAEvent* event = node->event(); 0220 if (event) 0221 return *event; 0222 } 0223 return {}; 0224 } 0225 0226 /****************************************************************************** 0227 * Return the event referred to by an index, or invalid event if the index is 0228 * not for an event. 0229 */ 0230 KAEvent FileResourceDataModel::event(const QModelIndex& ix) const 0231 { 0232 if (ix.isValid()) 0233 { 0234 const Node* node = reinterpret_cast<Node*>(ix.internalPointer()); 0235 if (node) 0236 { 0237 const KAEvent* event = node->event(); 0238 if (event) 0239 return *event; 0240 } 0241 } 0242 return {}; 0243 } 0244 0245 /****************************************************************************** 0246 * Return the index to a specified event. 0247 */ 0248 QModelIndex FileResourceDataModel::eventIndex(const QString& eventId) const 0249 { 0250 Node* node = mEventNodes.value(eventId, nullptr); 0251 if (node) 0252 { 0253 Resource resource = node->parent(); 0254 if (resource.isValid()) 0255 { 0256 const QList<Node*> nodes = mResourceNodes.value(resource); 0257 int row = nodes.indexOf(node); 0258 if (row >= 0) 0259 return createIndex(row, 0, node); 0260 } 0261 } 0262 return {}; 0263 } 0264 0265 /****************************************************************************** 0266 * Return the index to a specified event. 0267 */ 0268 QModelIndex FileResourceDataModel::eventIndex(const KAEvent& event) const 0269 { 0270 return eventIndex(event.id()); 0271 } 0272 0273 /****************************************************************************** 0274 * Add an event to a specified resource. 0275 * The event's 'updated' flag is cleared. 0276 * Reply = true if item creation has been scheduled. 0277 */ 0278 bool FileResourceDataModel::addEvent(KAEvent& event, Resource& resource) 0279 { 0280 qCDebug(KALARM_LOG) << "FileResourceDataModel::addEvent: ID:" << event.id(); 0281 return resource.addEvent(event); 0282 } 0283 0284 /****************************************************************************** 0285 * Recursive function to Q_EMIT the dataChanged() signal for all events in a 0286 * specified column range. 0287 */ 0288 void FileResourceDataModel::signalDataChanged(bool (*checkFunc)(const KAEvent*), int startColumn, int endColumn, const QModelIndex& parent) 0289 { 0290 int start = -1; 0291 int end = -1; 0292 for (int row = 0, count = rowCount(parent); row < count; ++row) 0293 { 0294 KAEvent* event = nullptr; 0295 const QModelIndex ix = index(row, 0, parent); 0296 const Node* node = reinterpret_cast<Node*>(ix.internalPointer()); 0297 if (node) 0298 { 0299 event = node->event(); 0300 if (event) 0301 { 0302 if ((*checkFunc)(event)) 0303 { 0304 // For efficiency, emit a single signal for each group of 0305 // consecutive events, rather than a separate signal for each event. 0306 if (start < 0) 0307 start = row; 0308 end = row; 0309 continue; 0310 } 0311 } 0312 } 0313 if (start >= 0) 0314 Q_EMIT dataChanged(index(start, startColumn, parent), index(end, endColumn, parent)); 0315 start = -1; 0316 if (!event) 0317 signalDataChanged(checkFunc, startColumn, endColumn, ix); 0318 } 0319 0320 if (start >= 0) 0321 Q_EMIT dataChanged(index(start, startColumn, parent), index(end, endColumn, parent)); 0322 } 0323 0324 void FileResourceDataModel::slotMigrationCompleted() 0325 { 0326 qCDebug(KALARM_LOG) << "FileResourceDataModel: Migration completed"; 0327 setMigrationComplete(); 0328 } 0329 0330 /****************************************************************************** 0331 * Signal every minute that the time-to-alarm values have changed. 0332 */ 0333 static bool checkEvent_isActive(const KAEvent* event) 0334 { return event->category() == CalEvent::ACTIVE; } 0335 0336 void FileResourceDataModel::slotUpdateTimeTo() 0337 { 0338 signalDataChanged(&checkEvent_isActive, TimeToColumn, TimeToColumn, QModelIndex()); 0339 } 0340 0341 /****************************************************************************** 0342 * Called when the colour used to display archived alarms has changed. 0343 */ 0344 static bool checkEvent_isArchived(const KAEvent* event) 0345 { return event->category() == CalEvent::ARCHIVED; } 0346 0347 void FileResourceDataModel::slotUpdateArchivedColour(const QColor&) 0348 { 0349 qCDebug(KALARM_LOG) << "FileResourceDataModel::slotUpdateArchivedColour"; 0350 signalDataChanged(&checkEvent_isArchived, 0, ColumnCount - 1, QModelIndex()); 0351 } 0352 0353 /****************************************************************************** 0354 * Called when the colour used to display disabled alarms has changed. 0355 */ 0356 static bool checkEvent_isDisabled(const KAEvent* event) 0357 { 0358 return !event->enabled(); 0359 } 0360 0361 void FileResourceDataModel::slotUpdateDisabledColour(const QColor&) 0362 { 0363 qCDebug(KALARM_LOG) << "FileResourceDataModel::slotUpdateDisabledColour"; 0364 signalDataChanged(&checkEvent_isDisabled, 0, ColumnCount - 1, QModelIndex()); 0365 } 0366 0367 /****************************************************************************** 0368 * Called when the definition of holidays has changed. 0369 * Update the next trigger time for all alarms which are set to recur only on 0370 * non-holidays. 0371 */ 0372 static bool checkEvent_excludesHolidays(const KAEvent* event) 0373 { 0374 return event->holidaysExcluded(); 0375 } 0376 0377 void FileResourceDataModel::slotUpdateHolidays() 0378 { 0379 qCDebug(KALARM_LOG) << "FileResourceDataModel::slotUpdateHolidays"; 0380 Q_ASSERT(TimeToColumn == TimeColumn + 1); // signal should be emitted only for TimeTo and Time columns 0381 signalDataChanged(&checkEvent_excludesHolidays, TimeColumn, TimeToColumn, QModelIndex()); 0382 } 0383 0384 /****************************************************************************** 0385 * Called when the definition of working hours has changed. 0386 * Update the next trigger time for all alarms which are set to recur only 0387 * during working hours. 0388 */ 0389 static bool checkEvent_workTimeOnly(const KAEvent* event) 0390 { 0391 return event->workTimeOnly(); 0392 } 0393 0394 void FileResourceDataModel::slotUpdateWorkingHours() 0395 { 0396 qCDebug(KALARM_LOG) << "FileResourceDataModel::slotUpdateWorkingHours"; 0397 Q_ASSERT(TimeToColumn == TimeColumn + 1); // signal should be emitted only for TimeTo and Time columns 0398 signalDataChanged(&checkEvent_workTimeOnly, TimeColumn, TimeToColumn, QModelIndex()); 0399 } 0400 0401 /****************************************************************************** 0402 * Called when loading of a resource is complete. 0403 */ 0404 void FileResourceDataModel::slotResourceLoaded(Resource& resource) 0405 { 0406 qCDebug(KALARM_LOG) << "FileResourceDataModel::slotResourceLoaded:" << resource.displayName(); 0407 addResource(resource); 0408 } 0409 0410 /****************************************************************************** 0411 * Called when a resource setting has changed. 0412 */ 0413 void FileResourceDataModel::slotResourceSettingsChanged(Resource& res, ResourceType::Changes changes) 0414 { 0415 if (changes & ResourceType::Enabled) 0416 { 0417 if (res.enabledTypes()) 0418 { 0419 // If the resource has just been loaded, addResource() will already 0420 // have been called, so don't call it again. 0421 if (!(changes & ResourceType::Loaded)) 0422 { 0423 qCDebug(KALARM_LOG) << "FileResourceDataModel::slotResourceSettingsChanged: Enabled" << res.displayName(); 0424 addResource(res); 0425 } 0426 } 0427 else 0428 { 0429 qCDebug(KALARM_LOG) << "FileResourceDataModel::slotResourceSettingsChanged: Disabled" << res.displayName(); 0430 removeResourceEvents(res); 0431 } 0432 } 0433 if (changes & (ResourceType::Name | ResourceType::Standard | ResourceType::ReadOnly)) 0434 { 0435 qCDebug(KALARM_LOG) << "FileResourceDataModel::slotResourceSettingsChanged:" << res.displayName(); 0436 const QModelIndex resourceIx = resourceIndex(res); 0437 if (resourceIx.isValid()) 0438 Q_EMIT dataChanged(resourceIx, resourceIx); 0439 } 0440 if (changes & ResourceType::BackgroundColour) 0441 { 0442 qCDebug(KALARM_LOG) << "FileResourceDataModel::slotResourceSettingsChanged: Colour" << res.displayName(); 0443 const QList<Node*>& eventNodes = mResourceNodes.value(res); 0444 const int lastRow = eventNodes.count() - 1; 0445 if (lastRow >= 0) 0446 Q_EMIT dataChanged(createIndex(0, 0, eventNodes[0]), createIndex(lastRow, ColumnCount - 1, eventNodes[lastRow])); 0447 } 0448 0449 // if (changes & (ResourceType::AlarmTypes | ResourceType::KeepFormat | ResourceType::UpdateFormat)) 0450 // qCDebug(KALARM_LOG) << "FileResourceDataModel::slotResourceSettingsChanged: UNHANDLED" << res.displayName(); 0451 } 0452 0453 /****************************************************************************** 0454 * Called when events have been added to a resource. Add events to the list. 0455 */ 0456 void FileResourceDataModel::slotEventsAdded(Resource& resource, const QList<KAEvent>& events) 0457 { 0458 if (events.isEmpty()) 0459 return; 0460 qCDebug(KALARM_LOG) << "FileResourceDataModel::slotEventsAdded:" << resource.displayId() << "Count:" << events.count(); 0461 if (mResourceNodes.contains(resource)) 0462 { 0463 // If events with the same ID already exist, remove them first. 0464 QList<KAEvent> eventsToAdd = events; 0465 { 0466 QList<KAEvent> eventsToDelete; 0467 for (int i = eventsToAdd.count(); --i >= 0; ) 0468 { 0469 const KAEvent& event = eventsToAdd.at(i); 0470 const Node* dnode = mEventNodes.value(event.id(), nullptr); 0471 if (dnode) 0472 { 0473 if (dnode->parent() != resource) 0474 { 0475 qCWarning(KALARM_LOG) << "FileResourceDataModel::slotEventsAdded: Event ID already exists in another resource"; 0476 eventsToAdd.removeAt(i); 0477 } 0478 else 0479 eventsToDelete << *dnode->event(); 0480 } 0481 } 0482 if (!eventsToDelete.isEmpty()) 0483 deleteEvents(resource, eventsToDelete); 0484 } 0485 0486 if (!eventsToAdd.isEmpty()) 0487 { 0488 QList<Node*>& resourceEventNodes = mResourceNodes[resource]; 0489 int row = resourceEventNodes.count(); 0490 resourceEventNodes.reserve(row + eventsToAdd.count()); 0491 const QModelIndex resourceIx = resourceIndex(resource); 0492 beginInsertRows(resourceIx, row, row + eventsToAdd.count() - 1); 0493 for (const KAEvent& event : std::as_const(eventsToAdd)) 0494 { 0495 auto ev = new KAEvent(event); 0496 ev->setResourceId(resource.id()); 0497 Node* node = new Node(ev, resource); 0498 resourceEventNodes += node; 0499 mEventNodes[ev->id()] = node; 0500 } 0501 endInsertRows(); 0502 if (!mHaveEvents) 0503 updateHaveEvents(true); 0504 } 0505 } 0506 } 0507 0508 /****************************************************************************** 0509 * Update an event which already exists (and with the same UID) in the model. 0510 */ 0511 void FileResourceDataModel::slotEventUpdated(Resource& resource, const KAEvent& event) 0512 { 0513 auto it = mEventNodes.constFind(event.id()); 0514 if (it != mEventNodes.constEnd()) 0515 { 0516 Node* node = it.value(); 0517 if (node && node->parent() == resource) 0518 { 0519 KAEvent* oldEvent = node->event(); 0520 if (oldEvent) 0521 { 0522 *oldEvent = event; 0523 0524 const QList<Node *> eventNodes = mResourceNodes.value(resource); 0525 int row = eventNodes.indexOf(node); 0526 if (row >= 0) 0527 { 0528 const QModelIndex resourceIx = resourceIndex(resource); 0529 Q_EMIT dataChanged(index(row, 0, resourceIx), index(row, ColumnCount - 1, resourceIx)); 0530 } 0531 } 0532 } 0533 } 0534 } 0535 0536 /****************************************************************************** 0537 * Delete events from their resource. 0538 */ 0539 bool FileResourceDataModel::deleteEvents(Resource& resource, const QList<KAEvent>& events) 0540 { 0541 qCDebug(KALARM_LOG) << "FileResourceDataModel::deleteEvents:" << resource.displayName() << "Count:" << events.count(); 0542 QModelIndex resourceIx = resourceIndex(resource); 0543 if (!resourceIx.isValid()) 0544 return false; 0545 auto it = mResourceNodes.find(resource); 0546 if (it == mResourceNodes.end()) 0547 return false; 0548 QList<Node*>& eventNodes = it.value(); 0549 0550 // Find the row numbers of the events to delete. 0551 QList<int> rowsToDelete; 0552 rowsToDelete.reserve(events.count()); 0553 for (const KAEvent& event : events) 0554 { 0555 Node* node = mEventNodes.value(event.id(), nullptr); 0556 if (node && node->parent() == resource) 0557 { 0558 int row = eventNodes.indexOf(node); 0559 if (row >= 0) 0560 rowsToDelete << row; 0561 } 0562 } 0563 0564 // Delete the events in groups of consecutive rows (if any). 0565 std::sort(rowsToDelete.begin(), rowsToDelete.end()); 0566 for (int i = 0, count = rowsToDelete.count(); i < count; ) 0567 { 0568 int row = rowsToDelete.at(i); 0569 int lastRow = row; 0570 while (++i < count && rowsToDelete.at(i) == lastRow + 1) 0571 ++lastRow; 0572 0573 beginRemoveRows(resourceIx, row, lastRow); 0574 do 0575 { 0576 Node* node = eventNodes.at(row); 0577 eventNodes.removeAt(row); 0578 mEventNodes.remove(node->event()->id()); 0579 delete node; 0580 } while (++row <= lastRow); 0581 endRemoveRows(); 0582 } 0583 0584 if (mHaveEvents && mEventNodes.isEmpty()) 0585 updateHaveEvents(false); 0586 return true; 0587 } 0588 0589 /****************************************************************************** 0590 * Add a resource and all its events into the model. 0591 */ 0592 void FileResourceDataModel::addResource(Resource& resource) 0593 { 0594 // Get the events to add to the model 0595 const QList<KAEvent> events = resource.events(); 0596 qCDebug(KALARM_LOG) << "FileResourceDataModel::addResource" << resource.displayName() << ", event count:" << events.count(); 0597 0598 const QModelIndex resourceIx = resourceIndex(resource); 0599 if (resourceIx.isValid()) 0600 { 0601 // The resource already exists: remove its existing events from the model. 0602 bool noEvents = events.isEmpty(); 0603 removeResourceEvents(resource, noEvents); 0604 if (noEvents) 0605 return; 0606 beginInsertRows(resourceIx, 0, events.count() - 1); 0607 } 0608 else 0609 { 0610 // Add the new resource to the model 0611 QList<Node*>& resourceNodes = mResourceNodes[Resource()]; 0612 int row = resourceNodes.count(); 0613 beginInsertRows(QModelIndex(), row, row); 0614 mResources += resource; 0615 resourceNodes += new Node(resource); 0616 mResourceNodes.insert(resource, QList<Node*>()); 0617 } 0618 0619 if (!events.isEmpty()) 0620 { 0621 QList<Node*>& resourceEventNodes = mResourceNodes[resource]; 0622 resourceEventNodes.reserve(resourceEventNodes.count() + events.count()); 0623 for (const KAEvent& event : events) 0624 { 0625 Node* node = new Node(new KAEvent(event), resource); 0626 resourceEventNodes += node; 0627 mEventNodes[event.id()] = node; 0628 } 0629 } 0630 endInsertRows(); 0631 if (!mHaveEvents && !mEventNodes.isEmpty()) 0632 updateHaveEvents(true); 0633 else if (mHaveEvents && mEventNodes.isEmpty()) 0634 updateHaveEvents(false); 0635 } 0636 0637 /****************************************************************************** 0638 * Remove a resource and its events from the list. 0639 * This has to be called before the resource is actually deleted or reloaded. If 0640 * not, timer based updates can occur between the resource being deleted and 0641 * slotResourceSettingsChanged(Deleted) being triggered, leading to crashes when 0642 * data from the resource's events is fetched. 0643 */ 0644 void FileResourceDataModel::removeResource(Resource& resource) 0645 { 0646 qCDebug(KALARM_LOG) << "FileResourceDataModel::removeResource" << resource.displayName(); 0647 int row = mResources.indexOf(resource); 0648 if (row < 0) 0649 return; 0650 0651 Resource r(resource); // in case 'resource' is a reference to the copy in mResources 0652 int count = 0; 0653 beginRemoveRows(QModelIndex(), row, row); 0654 mResources.removeAt(row); 0655 QList<Node*>& resourceNodes = mResourceNodes[Resource()]; 0656 delete resourceNodes.at(row); 0657 resourceNodes.removeAt(row); 0658 auto it = mResourceNodes.find(r); 0659 if (it != mResourceNodes.end()) 0660 { 0661 count = removeResourceEvents(it.value()); 0662 mResourceNodes.erase(it); 0663 } 0664 endRemoveRows(); 0665 0666 if (count) 0667 { 0668 if (mHaveEvents && mEventNodes.isEmpty()) 0669 updateHaveEvents(false); 0670 } 0671 } 0672 0673 /****************************************************************************** 0674 * Remove a resource's events from the list. 0675 */ 0676 void FileResourceDataModel::removeResourceEvents(Resource& resource, bool setHaveEvents) 0677 { 0678 qCDebug(KALARM_LOG) << "FileResourceDataModel::removeResourceEvents" << resource.displayName(); 0679 const QModelIndex resourceIx = resourceIndex(resource); 0680 if (resourceIx.isValid()) 0681 { 0682 // The resource already exists: remove its existing events from the model. 0683 QList<Node*>& eventNodes = mResourceNodes[resource]; 0684 if (!eventNodes.isEmpty()) 0685 { 0686 beginRemoveRows(resourceIx, 0, eventNodes.count() - 1); 0687 int count = removeResourceEvents(eventNodes); 0688 endRemoveRows(); 0689 if (count && setHaveEvents) 0690 { 0691 if (mHaveEvents && mEventNodes.isEmpty()) 0692 updateHaveEvents(false); 0693 } 0694 } 0695 } 0696 } 0697 0698 /****************************************************************************** 0699 * Remove a resource's events from the list. 0700 * beginRemoveRows() must be called before this method, and endRemoveRows() 0701 * afterwards. Then, removeConfigEvents() must be called with the return value 0702 * from this method as a parameter. 0703 * Return - number of events which have been removed. 0704 */ 0705 int FileResourceDataModel::removeResourceEvents(QList<Node*>& eventNodes) 0706 { 0707 qCDebug(KALARM_LOG) << "FileResourceDataModel::removeResourceEvents"; 0708 int count = 0; 0709 for (Node* node : eventNodes) 0710 { 0711 KAEvent* event = node->event(); 0712 if (event) 0713 { 0714 const QString eventId = event->id(); 0715 mEventNodes.remove(eventId); 0716 ++count; 0717 } 0718 delete node; 0719 } 0720 eventNodes.clear(); 0721 return count; 0722 } 0723 0724 /****************************************************************************** 0725 * Terminate access to the data model, and tidy up. 0726 */ 0727 void FileResourceDataModel::terminate() 0728 { 0729 delete mInstance; 0730 } 0731 0732 /****************************************************************************** 0733 * Reload all resources' data from storage. 0734 */ 0735 void FileResourceDataModel::reload() 0736 { 0737 for (int i = 0, iMax = mResources.count(); i < iMax; ++i) 0738 mResources[i].reload(); 0739 } 0740 0741 /****************************************************************************** 0742 * Reload a resource's data from storage. 0743 */ 0744 bool FileResourceDataModel::reload(Resource& resource) 0745 { 0746 if (!resource.isValid()) 0747 return false; 0748 qCDebug(KALARM_LOG) << "FileResourceDataModel::reload:" << resource.displayId(); 0749 return resource.reload(); 0750 } 0751 0752 /****************************************************************************** 0753 * Create a FileResourceCreator instance. 0754 */ 0755 ResourceCreator* FileResourceDataModel::createResourceCreator(KAlarmCal::CalEvent::Type defaultType, QWidget* parent) 0756 { 0757 return new FileResourceCreator(defaultType, parent); 0758 } 0759 0760 /****************************************************************************** 0761 * Update a resource's backend calendar file to the current KAlarm format. 0762 */ 0763 void FileResourceDataModel::updateCalendarToCurrentFormat(Resource& resource, bool ignoreKeepFormat, QObject* parent) 0764 { 0765 FileResourceCalendarUpdater::updateToCurrentFormat(resource, ignoreKeepFormat, parent); 0766 } 0767 0768 /****************************************************************************** 0769 * Create model instances which are dependent on the resource data model type. 0770 */ 0771 ResourceListModel* FileResourceDataModel::createResourceListModel(QObject* parent) 0772 { 0773 return ResourceListModel::create<FileResourceDataModel>(parent); 0774 } 0775 0776 ResourceFilterCheckListModel* FileResourceDataModel::createResourceFilterCheckListModel(QObject* parent) 0777 { 0778 return ResourceFilterCheckListModel::create<FileResourceDataModel>(parent); 0779 } 0780 0781 AlarmListModel* FileResourceDataModel::createAlarmListModel(QObject* parent) 0782 { 0783 return AlarmListModel::create<FileResourceDataModel>(parent); 0784 } 0785 0786 AlarmListModel* FileResourceDataModel::allAlarmListModel() 0787 { 0788 return AlarmListModel::all<FileResourceDataModel>(); 0789 } 0790 0791 TemplateListModel* FileResourceDataModel::createTemplateListModel(QObject* parent) 0792 { 0793 return TemplateListModel::create<FileResourceDataModel>(parent); 0794 } 0795 0796 TemplateListModel* FileResourceDataModel::allTemplateListModel() 0797 { 0798 return TemplateListModel::all<FileResourceDataModel>(); 0799 } 0800 0801 /****************************************************************************** 0802 * Return the number of children of a model index. 0803 */ 0804 int FileResourceDataModel::rowCount(const QModelIndex& parent) const 0805 { 0806 if (!parent.isValid()) 0807 return mResourceNodes.count() - 1; 0808 const Node* node = reinterpret_cast<Node*>(parent.internalPointer()); 0809 if (node && node->type == Type::Resource) 0810 return mResourceNodes.value(node->resource()).count(); 0811 return 0; 0812 } 0813 0814 /****************************************************************************** 0815 * Return the number of columns for children of a model index. 0816 */ 0817 int FileResourceDataModel::columnCount(const QModelIndex& parent) const 0818 { 0819 // Although the number of columns differs between resources and events, 0820 // returning different values here doesn't work. So return the maximum 0821 // number of columns. 0822 Q_UNUSED(parent); 0823 return ColumnCount; 0824 } 0825 0826 /****************************************************************************** 0827 * Return the model index for a specified item. 0828 */ 0829 QModelIndex FileResourceDataModel::index(int row, int column, const QModelIndex& parent) const 0830 { 0831 if (row >= 0 && column >= 0) 0832 { 0833 if (!parent.isValid()) 0834 { 0835 if (!column) 0836 { 0837 const QList<Node*>& nodes = mResourceNodes.value(Resource()); 0838 if (row < nodes.count()) 0839 return createIndex(row, column, nodes[row]); 0840 } 0841 } 0842 else 0843 { 0844 if (column < ColumnCount) 0845 { 0846 const Node* node = reinterpret_cast<Node*>(parent.internalPointer()); 0847 if (node) 0848 { 0849 Resource resource = node->resource(); 0850 if (resource.isValid()) 0851 { 0852 const QList<Node*>& nodes = mResourceNodes.value(resource); 0853 if (row < nodes.count()) 0854 return createIndex(row, column, nodes[row]); 0855 } 0856 } 0857 } 0858 } 0859 } 0860 return {}; 0861 } 0862 0863 /****************************************************************************** 0864 * Return the model index for the parent of a specified item. 0865 */ 0866 QModelIndex FileResourceDataModel::parent(const QModelIndex& ix) const 0867 { 0868 const Node* node = reinterpret_cast<Node*>(ix.internalPointer()); 0869 if (node) 0870 { 0871 Resource resource = node->parent(); 0872 if (resource.isValid()) 0873 { 0874 int row = mResources.indexOf(resource); 0875 if (row >= 0) 0876 return createIndex(row, 0, mResourceNodes.value(Resource()).at(row)); 0877 } 0878 } 0879 return {}; 0880 } 0881 0882 /****************************************************************************** 0883 * Return the indexes which match a data value in the 'start' index column. 0884 */ 0885 QModelIndexList FileResourceDataModel::match(const QModelIndex& start, int role, const QVariant& value, int hits, Qt::MatchFlags flags) const 0886 { 0887 switch (role) 0888 { 0889 case ResourceIdRole: 0890 { 0891 QModelIndexList result; //clazy:exclude=inefficient-qlist 0892 const ResourceId id = value.toLongLong(); 0893 if (id >= 0) 0894 { 0895 const QModelIndex ix = resourceIndex(id); 0896 if (ix.isValid()) 0897 result += ix; 0898 } 0899 return result; 0900 } 0901 case EventIdRole: 0902 { 0903 QModelIndexList result; //clazy:exclude=inefficient-qlist 0904 const QModelIndex ix = eventIndex(value.toString()); 0905 if (ix.isValid()) 0906 result += ix; 0907 return result; 0908 } 0909 default: 0910 break; 0911 } 0912 0913 return QAbstractItemModel::match(start, role, value, hits, flags); 0914 } 0915 0916 /****************************************************************************** 0917 * Return the data for a given role, for a specified item. 0918 */ 0919 QVariant FileResourceDataModel::data(const QModelIndex& ix, int role) const 0920 { 0921 const Node* node = reinterpret_cast<Node*>(ix.internalPointer()); 0922 if (node) 0923 { 0924 const Resource res = node->resource(); 0925 if (!res.isNull()) 0926 { 0927 bool handled; 0928 const QVariant value = resourceData(role, res, handled); 0929 if (handled) 0930 return value; 0931 0932 switch (role) 0933 { 0934 case Qt::CheckStateRole: 0935 return res.enabledTypes() ? Qt::Checked : Qt::Unchecked; 0936 default: 0937 break; 0938 } 0939 } 0940 else 0941 { 0942 KAEvent* event = node->event(); 0943 if (event) 0944 { 0945 // This is an Event row 0946 if (role == ParentResourceIdRole) 0947 return node->parent().id(); 0948 const Resource resp = node->parent(); 0949 bool handled; 0950 const QVariant value = eventData(role, ix.column(), *event, resp, handled); 0951 if (handled) 0952 return value; 0953 } 0954 } 0955 0956 // Return invalid value 0957 switch (role) 0958 { 0959 case ItemTypeRole: 0960 return static_cast<int>(Type::Error); 0961 case ResourceIdRole: 0962 case ParentResourceIdRole: 0963 return -1; 0964 case StatusRole: 0965 default: 0966 break; 0967 } 0968 } 0969 return {}; 0970 } 0971 0972 /****************************************************************************** 0973 * Set the data for a given role, for a specified item. 0974 */ 0975 bool FileResourceDataModel::setData(const QModelIndex& ix, const QVariant& value, int role) 0976 { 0977 // NOTE: need to Q_EMIT dataChanged() whenever something is updated (except via a job). 0978 const Node* node = reinterpret_cast<Node*>(ix.internalPointer()); 0979 if (!node) 0980 return false; 0981 Resource resource = node->resource(); 0982 if (resource.isNull()) 0983 { 0984 // This is an Event row 0985 KAEvent* event = node->event(); 0986 if (event && event->isValid()) 0987 { 0988 switch (role) 0989 { 0990 case Qt::EditRole: 0991 { 0992 int row = ix.row(); 0993 Q_EMIT dataChanged(index(row, 0, ix.parent()), index(row, ColumnCount - 1, ix.parent())); 0994 return true; 0995 } 0996 default: 0997 break; 0998 } 0999 } 1000 } 1001 return QAbstractItemModel::setData(ix, value, role); 1002 } 1003 1004 /****************************************************************************** 1005 * Return data for a column heading. 1006 */ 1007 QVariant FileResourceDataModel::headerData(int section, Qt::Orientation orientation, int role) const 1008 { 1009 bool handled; 1010 QVariant value = ResourceDataModelBase::headerData(section, orientation, role, true, handled); 1011 if (handled) 1012 return value; 1013 return {}; 1014 } 1015 1016 Qt::ItemFlags FileResourceDataModel::flags(const QModelIndex& ix) const 1017 { 1018 if (!ix.isValid()) 1019 return Qt::ItemIsEnabled; 1020 return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsDragEnabled; 1021 } 1022 1023 /****************************************************************************** 1024 * Display a message to the user. 1025 */ 1026 void FileResourceDataModel::slotResourceMessage(ResourceType::MessageType type, const QString& message, const QString& details) 1027 { 1028 handleResourceMessage(type, message, details); 1029 } 1030 1031 #include "moc_fileresourcedatamodel.cpp" 1032 1033 // vim: et sw=4: