File indexing completed on 2024-05-12 05:10:43
0001 /* 0002 SPDX-FileCopyrightText: 2011-2013 Sérgio Martins <iamsergio@gmail.com> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "etmcalendar.h" 0008 #include "blockalarmsattribute.h" 0009 #include "calendarmodel_p.h" 0010 #include "calendarutils.h" 0011 #include "calfilterpartstatusproxymodel_p.h" 0012 #include "calfilterproxymodel_p.h" 0013 #include "etmcalendar_p.h" 0014 #include "kcolumnfilterproxymodel_p.h" 0015 0016 #include <Akonadi/CollectionFilterProxyModel> 0017 #include <Akonadi/CollectionUtils> 0018 #include <Akonadi/EntityDisplayAttribute> 0019 #include <Akonadi/EntityMimeTypeFilterModel> 0020 #include <Akonadi/Item> 0021 #include <Akonadi/ItemFetchScope> 0022 #include <Akonadi/Monitor> 0023 #include <Akonadi/Session> 0024 #include <KDescendantsProxyModel> 0025 #include <KSelectionProxyModel> 0026 0027 #include <QItemSelectionModel> 0028 #include <QTreeView> 0029 0030 using namespace Akonadi; 0031 using namespace KCalendarCore; 0032 0033 // TODO: implement batchAdding 0034 0035 ETMCalendarPrivate::ETMCalendarPrivate(ETMCalendar *qq) 0036 : CalendarBasePrivate(qq) 0037 , mETM(nullptr) 0038 , q(qq) 0039 { 0040 mListensForNewItems = true; 0041 } 0042 0043 void ETMCalendarPrivate::init() 0044 { 0045 if (!mETM) { 0046 auto session = new Akonadi::Session("ETMCalendar", q); 0047 auto monitor = new Akonadi::Monitor(q); 0048 monitor->setObjectName(QLatin1StringView("ETMCalendarMonitor")); 0049 connect(monitor, 0050 qOverload<const Akonadi::Collection &, const QSet<QByteArray> &>(&Monitor::collectionChanged), 0051 this, 0052 [this](const Akonadi::Collection &cols, const QSet<QByteArray> &set) { 0053 onCollectionChanged(cols, set); 0054 }); 0055 0056 Akonadi::ItemFetchScope scope; 0057 scope.fetchFullPayload(true); 0058 scope.fetchAttribute<Akonadi::EntityDisplayAttribute>(); 0059 0060 monitor->setSession(session); 0061 monitor->setCollectionMonitored(Akonadi::Collection::root()); 0062 monitor->fetchCollection(true); 0063 monitor->setItemFetchScope(scope); 0064 monitor->setAllMonitored(true); 0065 0066 const QStringList allMimeTypes = {KCalendarCore::Event::eventMimeType(), 0067 KCalendarCore::Todo::todoMimeType(), 0068 KCalendarCore::Journal::journalMimeType()}; 0069 0070 for (const QString &mimetype : allMimeTypes) { 0071 monitor->setMimeTypeMonitored(mimetype, mMimeTypes.isEmpty() || mMimeTypes.contains(mimetype)); 0072 } 0073 0074 mETM = CalendarModel::create(monitor); 0075 mETM->setObjectName(QLatin1StringView("ETM")); 0076 mETM->setListFilter(Akonadi::CollectionFetchScope::Display); 0077 } 0078 0079 setupFilteredETM(); 0080 0081 connect(q, &Calendar::filterChanged, this, &ETMCalendarPrivate::onFilterChanged); 0082 0083 connect(mETM.data(), &EntityTreeModel::collectionPopulated, this, &ETMCalendarPrivate::onCollectionPopulated); 0084 connect(mETM.data(), &QAbstractItemModel::rowsInserted, this, &ETMCalendarPrivate::onRowsInserted); 0085 connect(mETM.data(), &QAbstractItemModel::dataChanged, this, &ETMCalendarPrivate::onDataChanged); 0086 connect(mETM.data(), &QAbstractItemModel::rowsMoved, this, &ETMCalendarPrivate::onRowsMoved); 0087 connect(mETM.data(), &QAbstractItemModel::rowsRemoved, this, &ETMCalendarPrivate::onRowsRemoved); 0088 0089 connect(mFilteredETM, &QAbstractItemModel::dataChanged, this, &ETMCalendarPrivate::onDataChangedInFilteredModel); 0090 connect(mFilteredETM, &QAbstractItemModel::layoutChanged, this, &ETMCalendarPrivate::onLayoutChangedInFilteredModel); 0091 connect(mFilteredETM, &QAbstractItemModel::modelReset, this, &ETMCalendarPrivate::onModelResetInFilteredModel); 0092 connect(mFilteredETM, &QAbstractItemModel::rowsInserted, this, &ETMCalendarPrivate::onRowsInsertedInFilteredModel); 0093 connect(mFilteredETM, &QAbstractItemModel::rowsAboutToBeRemoved, this, &ETMCalendarPrivate::onRowsAboutToBeRemovedInFilteredModel); 0094 0095 loadFromETM(); 0096 updateLoadingState(); 0097 } 0098 0099 void ETMCalendarPrivate::onCollectionChanged(const Akonadi::Collection &collection, const QSet<QByteArray> &attributeNames) 0100 { 0101 Q_ASSERT(collection.isValid()); 0102 // Is the collection changed to read-only, we update all Incidences 0103 if (attributeNames.contains("AccessRights")) { 0104 const Akonadi::Item::List items = q->items(); 0105 for (const Akonadi::Item &item : items) { 0106 if (item.storageCollectionId() == collection.id()) { 0107 KCalendarCore::Incidence::Ptr incidence = CalendarUtils::incidence(item); 0108 if (incidence) { 0109 incidence->setReadOnly(!(collection.rights() & Akonadi::Collection::CanChangeItem)); 0110 } 0111 } 0112 } 0113 } 0114 0115 Q_EMIT q->collectionChanged(collection, attributeNames); 0116 } 0117 0118 void ETMCalendarPrivate::setupFilteredETM() 0119 { 0120 // We're only interested in the CollectionTitle column 0121 auto columnFilterProxy = new KColumnFilterProxyModel(this); 0122 columnFilterProxy->setSourceModel(mETM.data()); 0123 columnFilterProxy->setVisibleColumn(CalendarModel::CollectionTitle); 0124 columnFilterProxy->setObjectName(QLatin1StringView("Remove columns")); 0125 0126 mCollectionProxyModel = new Akonadi::CollectionFilterProxyModel(this); 0127 mCollectionProxyModel->setObjectName(QLatin1StringView("Only show collections")); 0128 mCollectionProxyModel->setDynamicSortFilter(true); 0129 mCollectionProxyModel->addMimeTypeFilter(QStringLiteral("text/calendar")); 0130 mCollectionProxyModel->setExcludeVirtualCollections(false); 0131 mCollectionProxyModel->setSortCaseSensitivity(Qt::CaseInsensitive); 0132 mCollectionProxyModel->setSourceModel(columnFilterProxy); 0133 0134 // Keep track of selected items. 0135 auto selectionModel = new QItemSelectionModel(mCollectionProxyModel); 0136 selectionModel->setObjectName(QLatin1StringView("Calendar Selection Model")); 0137 0138 // Make item selection work by means of checkboxes. 0139 mCheckableProxyModel = new CheckableProxyModel(this); 0140 mCheckableProxyModel->setSelectionModel(selectionModel); 0141 mCheckableProxyModel->setSourceModel(mCollectionProxyModel); 0142 mCheckableProxyModel->setObjectName(QLatin1StringView("Add checkboxes")); 0143 0144 mSelectionProxy = new KSelectionProxyModel(selectionModel, /**parent=*/this); 0145 mSelectionProxy->setObjectName(QLatin1StringView("Only show items of selected collection")); 0146 mSelectionProxy->setFilterBehavior(KSelectionProxyModel::ChildrenOfExactSelection); 0147 mSelectionProxy->setSourceModel(mETM.data()); 0148 0149 mCalFilterProxyModel = new CalFilterProxyModel(this); 0150 mCalFilterProxyModel->setFilter(q->filter()); 0151 mCalFilterProxyModel->setSourceModel(mSelectionProxy); 0152 mCalFilterProxyModel->setObjectName(QLatin1StringView("KCalendarCore::CalFilter filtering")); 0153 0154 mCalFilterPartStatusProxyModel = new CalFilterPartStatusProxyModel(this); 0155 mCalFilterPartStatusProxyModel->setFilterVirtual(false); 0156 QList<KCalendarCore::Attendee::PartStat> blockedStatusList; 0157 blockedStatusList << KCalendarCore::Attendee::NeedsAction; 0158 blockedStatusList << KCalendarCore::Attendee::Declined; 0159 mCalFilterPartStatusProxyModel->setDynamicSortFilter(true); 0160 mCalFilterPartStatusProxyModel->setBlockedStatusList(blockedStatusList); 0161 mCalFilterPartStatusProxyModel->setSourceModel(mCalFilterProxyModel); 0162 mCalFilterPartStatusProxyModel->setObjectName(QLatin1StringView("PartStatus filtering")); 0163 0164 mFilteredETM = new Akonadi::EntityMimeTypeFilterModel(this); 0165 mFilteredETM->setSourceModel(mCalFilterPartStatusProxyModel); 0166 mFilteredETM->setHeaderGroup(Akonadi::EntityTreeModel::ItemListHeaders); 0167 mFilteredETM->setSortRole(CalendarModel::SortRole); 0168 mFilteredETM->setObjectName(QLatin1StringView("Show headers")); 0169 0170 #ifdef AKONADI_CALENDAR_DEBUG_MODEL 0171 QTreeView *view = new QTreeView; 0172 view->setModel(mFilteredETM); 0173 view->show(); 0174 #endif 0175 } 0176 0177 ETMCalendarPrivate::~ETMCalendarPrivate() = default; 0178 0179 void ETMCalendarPrivate::loadFromETM() 0180 { 0181 itemsAdded(itemsFromModel(mFilteredETM)); 0182 } 0183 0184 void ETMCalendarPrivate::clear() 0185 { 0186 mCollectionMap.clear(); 0187 mItemsByCollection.clear(); 0188 0189 Akonadi::Item::List removedItems; 0190 removedItems.reserve(mItemById.size()); 0191 for (auto it = mItemById.cbegin(), end = mItemById.cend(); it != end; ++it) { 0192 removedItems.push_back(it.value()); 0193 } 0194 0195 itemsRemoved(removedItems); 0196 0197 if (!mItemById.isEmpty()) { 0198 mItemById.clear(); 0199 // Q_ASSERT(false); // TODO: discover why this happens 0200 } 0201 0202 if (!mItemIdByUid.isEmpty()) { 0203 mItemIdByUid.clear(); 0204 // Q_ASSERT(false); 0205 } 0206 mParentUidToChildrenUid.clear(); 0207 // m_virtualItems.clear(); 0208 } 0209 0210 Akonadi::Item::List ETMCalendarPrivate::itemsFromModel(const QAbstractItemModel *model, const QModelIndex &parentIndex, int start, int end) 0211 { 0212 const int endRow = end >= 0 ? end : model->rowCount(parentIndex) - 1; 0213 Akonadi::Item::List items; 0214 int row = start; 0215 QModelIndex i = model->index(row, 0, parentIndex); 0216 while (row <= endRow) { 0217 const Akonadi::Item item = itemFromIndex(i); 0218 if (item.hasPayload<KCalendarCore::Incidence::Ptr>()) { 0219 items << item; 0220 } else { 0221 const QModelIndex childIndex = model->index(0, 0, i); 0222 if (childIndex.isValid()) { 0223 items << itemsFromModel(model, i); 0224 } 0225 } 0226 ++row; 0227 i = i.sibling(row, 0); 0228 } 0229 return items; 0230 } 0231 0232 Akonadi::Collection::List ETMCalendarPrivate::collectionsFromModel(const QAbstractItemModel *model, const QModelIndex &parentIndex, int start, int end) 0233 { 0234 const int endRow = end >= 0 ? end : model->rowCount(parentIndex) - 1; 0235 Akonadi::Collection::List collections; 0236 int row = start; 0237 QModelIndex i = model->index(row, 0, parentIndex); 0238 while (row <= endRow) { 0239 const Akonadi::Collection collection = CollectionUtils::fromIndex(i); 0240 if (collection.isValid()) { 0241 collections << collection; 0242 QModelIndex childIndex = model->index(0, 0, i); 0243 if (childIndex.isValid()) { 0244 collections << collectionsFromModel(model, i); 0245 } 0246 } 0247 ++row; 0248 i = i.sibling(row, 0); 0249 } 0250 return collections; 0251 } 0252 0253 Akonadi::Item ETMCalendarPrivate::itemFromIndex(const QModelIndex &idx) 0254 { 0255 auto item = idx.data(Akonadi::EntityTreeModel::ItemRole).value<Akonadi::Item>(); 0256 item.setParentCollection(idx.data(Akonadi::EntityTreeModel::ParentCollectionRole).value<Akonadi::Collection>()); 0257 return item; 0258 } 0259 0260 void ETMCalendarPrivate::itemsAdded(const Akonadi::Item::List &items) 0261 { 0262 if (!items.isEmpty()) { 0263 for (const Akonadi::Item &item : items) { 0264 internalInsert(item); 0265 } 0266 0267 Akonadi::Collection::Id id = items.first().storageCollectionId(); 0268 if (mPopulatedCollectionIds.contains(id)) { 0269 // If the collection isn't populated yet, it will be sent later 0270 // Saves some cpu cycles 0271 Q_EMIT q->calendarChanged(); 0272 } 0273 } 0274 } 0275 0276 void ETMCalendarPrivate::itemsRemoved(const Akonadi::Item::List &items) 0277 { 0278 for (const Akonadi::Item &item : items) { 0279 internalRemove(item); 0280 } 0281 Q_EMIT q->calendarChanged(); 0282 } 0283 0284 void ETMCalendarPrivate::onRowsInserted(const QModelIndex &index, int start, int end) 0285 { 0286 const Akonadi::Collection::List collections = collectionsFromModel(mETM.data(), index, start, end); 0287 0288 for (const Akonadi::Collection &collection : collections) { 0289 mCollectionMap[collection.id()] = collection; 0290 } 0291 0292 if (!collections.isEmpty()) { 0293 Q_EMIT q->collectionsAdded(collections); 0294 } 0295 } 0296 0297 void ETMCalendarPrivate::onCollectionPopulated(Akonadi::Collection::Id id) 0298 { 0299 mPopulatedCollectionIds.insert(id); 0300 Q_EMIT q->calendarChanged(); 0301 updateLoadingState(); 0302 } 0303 0304 void ETMCalendarPrivate::onRowsRemoved(const QModelIndex &index, int start, int end) 0305 { 0306 const Akonadi::Collection::List collections = collectionsFromModel(mETM.data(), index, start, end); 0307 for (const Akonadi::Collection &collection : collections) { 0308 mCollectionMap.remove(collection.id()); 0309 } 0310 0311 if (!collections.isEmpty()) { 0312 Q_EMIT q->collectionsRemoved(collections); 0313 } 0314 } 0315 0316 void ETMCalendarPrivate::onDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight) 0317 { 0318 // We only update collections, because items are handled in the filtered model 0319 Q_ASSERT(topLeft.row() <= bottomRight.row()); 0320 const int endRow = bottomRight.row(); 0321 for (int row = topLeft.row(); row <= endRow; ++row) { 0322 const Akonadi::Collection col = CollectionUtils::fromIndex(topLeft.sibling(row, 0)); 0323 if (col.isValid()) { 0324 // Attributes might have changed, store the new collection and discard the old one 0325 mCollectionMap.insert(col.id(), col); 0326 } 0327 } 0328 } 0329 0330 void ETMCalendarPrivate::onRowsMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destinationParent, int destinationRow) 0331 { 0332 // TODO 0333 Q_UNUSED(sourceParent) 0334 Q_UNUSED(sourceStart) 0335 Q_UNUSED(sourceEnd) 0336 Q_UNUSED(destinationParent) 0337 Q_UNUSED(destinationRow) 0338 } 0339 0340 void ETMCalendarPrivate::onLayoutChangedInFilteredModel() 0341 { 0342 clear(); 0343 loadFromETM(); 0344 } 0345 0346 void ETMCalendarPrivate::onModelResetInFilteredModel() 0347 { 0348 clear(); 0349 loadFromETM(); 0350 } 0351 0352 void ETMCalendarPrivate::onDataChangedInFilteredModel(const QModelIndex &topLeft, const QModelIndex &bottomRight) 0353 { 0354 Q_ASSERT(topLeft.row() <= bottomRight.row()); 0355 const int endRow = bottomRight.row(); 0356 QModelIndex i(topLeft); 0357 int row = i.row(); 0358 while (row <= endRow) { 0359 const Akonadi::Item item = itemFromIndex(i); 0360 if (item.isValid() && item.hasPayload<KCalendarCore::Incidence::Ptr>()) { 0361 updateItem(item); 0362 } 0363 0364 ++row; 0365 i = i.sibling(row, topLeft.column()); 0366 } 0367 0368 Q_EMIT q->calendarChanged(); 0369 } 0370 0371 void ETMCalendarPrivate::updateItem(const Akonadi::Item &item) 0372 { 0373 Incidence::Ptr newIncidence = CalendarUtils::incidence(item); 0374 Q_ASSERT(newIncidence); 0375 Q_ASSERT(!newIncidence->uid().isEmpty()); 0376 newIncidence->setCustomProperty("VOLATILE", "AKONADI-ID", QString::number(item.id())); 0377 IncidenceBase::Ptr existingIncidence = q->incidence(newIncidence->uid(), newIncidence->recurrenceId()); 0378 0379 if (!existingIncidence && !mItemById.contains(item.id())) { 0380 // We don't know about this one because it was discarded, for example because of not having DTSTART 0381 return; 0382 } 0383 0384 mItemsByCollection.insert(item.storageCollectionId(), item); 0385 Akonadi::Item oldItem = mItemById.value(item.id()); 0386 0387 if (existingIncidence) { 0388 // We set the payload so that the internal incidence pointer and the one in mItemById stay the same 0389 Akonadi::Item updatedItem = item; 0390 updatedItem.setPayload<KCalendarCore::Incidence::Ptr>(existingIncidence.staticCast<KCalendarCore::Incidence>()); 0391 mItemById.insert(item.id(), updatedItem); // The item needs updating too, revision changed. 0392 0393 // Check if RELATED-TO changed, updating parenting information 0394 handleParentChanged(newIncidence); 0395 *(existingIncidence.data()) = *(newIncidence.data()); 0396 } else { 0397 mItemById.insert(item.id(), item); // The item needs updating too, revision changed. 0398 // The item changed it's UID, update our maps, the Google resource changes the UID when we create incidences. 0399 handleUidChange(oldItem, item, newIncidence->instanceIdentifier()); 0400 } 0401 } 0402 0403 void ETMCalendarPrivate::onRowsInsertedInFilteredModel(const QModelIndex &index, int start, int end) 0404 { 0405 itemsAdded(itemsFromModel(mFilteredETM, index, start, end)); 0406 } 0407 0408 void ETMCalendarPrivate::onRowsAboutToBeRemovedInFilteredModel(const QModelIndex &index, int start, int end) 0409 { 0410 itemsRemoved(itemsFromModel(mFilteredETM, index, start, end)); 0411 } 0412 0413 void ETMCalendarPrivate::onFilterChanged() 0414 { 0415 mCalFilterProxyModel->setFilter(q->filter()); 0416 } 0417 0418 ETMCalendar::ETMCalendar(QObject *parent) 0419 : CalendarBase(new ETMCalendarPrivate(this), parent) 0420 { 0421 Q_D(ETMCalendar); 0422 d->init(); 0423 } 0424 0425 ETMCalendar::ETMCalendar(const QStringList &mimeTypes, QObject *parent) 0426 : CalendarBase(new ETMCalendarPrivate(this), parent) 0427 { 0428 Q_D(ETMCalendar); 0429 d->mMimeTypes = mimeTypes; 0430 d->init(); 0431 } 0432 0433 ETMCalendar::ETMCalendar(ETMCalendar *other, QObject *parent) 0434 : CalendarBase(new ETMCalendarPrivate(this), parent) 0435 { 0436 Q_D(ETMCalendar); 0437 0438 auto model = qobject_cast<Akonadi::CalendarModel *>(other->entityTreeModel()); 0439 if (model) { 0440 d->mETM = model->weakPointer().toStrongRef(); 0441 } 0442 0443 d->init(); 0444 } 0445 0446 ETMCalendar::ETMCalendar(Monitor *monitor, QObject *parent) 0447 : CalendarBase(new ETMCalendarPrivate(this), parent) 0448 { 0449 Q_D(ETMCalendar); 0450 0451 if (monitor) { 0452 QObject::connect(monitor, 0453 qOverload<const Akonadi::Collection &, const QSet<QByteArray> &>(&Akonadi::Monitor::collectionChanged), 0454 d, 0455 &ETMCalendarPrivate::onCollectionChanged); 0456 d->mETM = CalendarModel::create(monitor); 0457 d->mETM->setObjectName(QLatin1StringView("ETM")); 0458 d->mETM->setListFilter(Akonadi::CollectionFetchScope::Display); 0459 } 0460 0461 d->init(); 0462 } 0463 0464 ETMCalendar::~ETMCalendar() = default; 0465 0466 // TODO: move this up? 0467 Akonadi::Collection ETMCalendar::collection(Akonadi::Collection::Id id) const 0468 { 0469 Q_D(const ETMCalendar); 0470 return d->mCollectionMap.value(id); 0471 } 0472 0473 bool ETMCalendar::hasRight(const QString &uid, Akonadi::Collection::Right right) const 0474 { 0475 return hasRight(item(uid), right); 0476 } 0477 0478 bool ETMCalendar::hasRight(const Akonadi::Item &item, Akonadi::Collection::Right right) const 0479 { 0480 // if the users changes the rights, item.parentCollection() 0481 // can still have the old rights, so we use call collection() 0482 // which returns the updated one 0483 const Akonadi::Collection col = collection(item.storageCollectionId()); 0484 return col.rights() & right; 0485 } 0486 0487 QAbstractItemModel *ETMCalendar::model() const 0488 { 0489 Q_D(const ETMCalendar); 0490 return d->mFilteredETM; 0491 } 0492 0493 KCheckableProxyModel *ETMCalendar::checkableProxyModel() const 0494 { 0495 Q_D(const ETMCalendar); 0496 return d->mCheckableProxyModel; 0497 } 0498 0499 KCalendarCore::Alarm::List ETMCalendar::alarms(const QDateTime &from, const QDateTime &to, bool excludeBlockedAlarms) const 0500 { 0501 Q_D(const ETMCalendar); 0502 KCalendarCore::Alarm::List alarmList; 0503 QHashIterator<Akonadi::Item::Id, Akonadi::Item> i(d->mItemById); 0504 while (i.hasNext()) { 0505 const Akonadi::Item item = i.next().value(); 0506 0507 Akonadi::Collection parentCollection; // must have same lifetime as blockedAttr 0508 BlockAlarmsAttribute *blockedAttr = nullptr; 0509 0510 if (excludeBlockedAlarms) { 0511 // take the collection from m_collectionMap, because we need the up-to-date collection attrs 0512 parentCollection = d->mCollectionMap.value(item.storageCollectionId()); 0513 if (parentCollection.isValid() && parentCollection.hasAttribute<BlockAlarmsAttribute>()) { 0514 blockedAttr = parentCollection.attribute<BlockAlarmsAttribute>(); 0515 if (blockedAttr->isEverythingBlocked()) { 0516 continue; 0517 } 0518 } 0519 } 0520 0521 KCalendarCore::Incidence::Ptr incidence; 0522 if (item.isValid() && item.hasPayload<KCalendarCore::Incidence::Ptr>()) { 0523 incidence = item.payload<KCalendarCore::Incidence::Ptr>(); 0524 } 0525 if (!incidence || incidence->alarms().isEmpty()) { 0526 continue; 0527 } 0528 0529 Alarm::List tmpList; 0530 if (incidence->recurs()) { 0531 appendRecurringAlarms(tmpList, incidence, from, to); 0532 } else { 0533 appendAlarms(tmpList, incidence, from, to); 0534 } 0535 0536 if (blockedAttr) { 0537 tmpList.erase(std::remove_if(tmpList.begin(), 0538 tmpList.end(), 0539 [blockedAttr](const auto &alarm) { 0540 return blockedAttr->isAlarmTypeBlocked(alarm->type()); 0541 }), 0542 tmpList.end()); 0543 } 0544 0545 alarmList += tmpList; 0546 } 0547 return alarmList; 0548 } 0549 0550 Akonadi::EntityTreeModel *ETMCalendar::entityTreeModel() const 0551 { 0552 Q_D(const ETMCalendar); 0553 return d->mETM.data(); 0554 } 0555 0556 void ETMCalendar::setCollectionFilteringEnabled(bool enable) 0557 { 0558 Q_D(ETMCalendar); 0559 if (d->mCollectionFilteringEnabled != enable) { 0560 d->mCollectionFilteringEnabled = enable; 0561 if (enable) { 0562 d->mSelectionProxy->setSourceModel(d->mETM.data()); 0563 QAbstractItemModel *oldModel = d->mCalFilterProxyModel->sourceModel(); 0564 d->mCalFilterProxyModel->setSourceModel(d->mSelectionProxy); 0565 delete qobject_cast<KDescendantsProxyModel *>(oldModel); 0566 } else { 0567 auto flatner = new KDescendantsProxyModel(this); 0568 flatner->setSourceModel(d->mETM.data()); 0569 d->mCalFilterProxyModel->setSourceModel(flatner); 0570 } 0571 } 0572 } 0573 0574 bool ETMCalendar::collectionFilteringEnabled() const 0575 { 0576 Q_D(const ETMCalendar); 0577 return d->mCollectionFilteringEnabled; 0578 } 0579 0580 void ETMCalendarPrivate::updateLoadingState() 0581 { 0582 if (!q->entityTreeModel()->isCollectionTreeFetched()) { 0583 return q->setIsLoading(true); 0584 } 0585 0586 for (const Akonadi::Collection &collection : std::as_const(mCollectionMap)) { 0587 if (!q->entityTreeModel()->isCollectionPopulated(collection.id())) { 0588 return q->setIsLoading(true); 0589 } 0590 } 0591 0592 q->setIsLoading(false); 0593 } 0594 0595 #include "moc_etmcalendar.cpp" 0596 #include "moc_etmcalendar_p.cpp"