File indexing completed on 2025-01-05 04:49:48
0001 /* 0002 * SPDX-FileCopyrightText: 2016 Daniel Vrátil <dvratil@kde.org> 0003 * 0004 * SPDX-License-Identifier: GPL-2.0-or-later 0005 * 0006 */ 0007 0008 #include "eventmodel.h" 0009 #include "pimeventsplugin_debug.h" 0010 0011 #include <Akonadi/IncidenceChanger> 0012 0013 #include <Akonadi/AttributeFactory> 0014 #include <Akonadi/CollectionColorAttribute> 0015 #include <Akonadi/CollectionFetchScope> 0016 #include <Akonadi/EntityDisplayAttribute> 0017 #include <Akonadi/ItemFetchJob> 0018 #include <Akonadi/ItemFetchScope> 0019 #include <Akonadi/Monitor> 0020 0021 EventModel::EventModel(QObject *parent) 0022 : Akonadi::CalendarBase(parent) 0023 { 0024 Akonadi::AttributeFactory::registerAttribute<Akonadi::CollectionColorAttribute>(); 0025 } 0026 0027 EventModel::~EventModel() = default; 0028 0029 void EventModel::createMonitor() 0030 { 0031 if (mMonitor) { 0032 return; 0033 } 0034 0035 mMonitor = new Akonadi::Monitor(this); 0036 mMonitor->setObjectName(QLatin1StringView("PlasmaEventModelMonitor")); 0037 mMonitor->itemFetchScope().fetchFullPayload(true); 0038 mMonitor->collectionFetchScope().fetchAttribute<Akonadi::EntityDisplayAttribute>(); 0039 mMonitor->collectionFetchScope().fetchAttribute<Akonadi::CollectionColorAttribute>(); 0040 mMonitor->fetchCollection(true); 0041 0042 connect(mMonitor, &Akonadi::Monitor::itemAdded, this, [this](const Akonadi::Item &item) { 0043 // This is super-ugly, but the only way how to insert into CalendarBase 0044 // without having direct access to CalendarBasePrivate. 0045 // changeId is luckily ignored by CalendarBase. 0046 Q_EMIT incidenceChanger()->createFinished(0, item, Akonadi::IncidenceChanger::ResultCodeSuccess, QString()); 0047 }); 0048 connect(mMonitor, &Akonadi::Monitor::itemChanged, this, [this](const Akonadi::Item &item) { 0049 if (!item.hasPayload<KCalendarCore::Incidence::Ptr>()) { 0050 qCDebug(PIMEVENTSPLUGIN_LOG) << "Item" << item.id() << "has no payload!"; 0051 return; 0052 } 0053 0054 auto incidence = item.payload<KCalendarCore::Incidence::Ptr>(); 0055 if (!incidence) { 0056 return; // HUH?! 0057 } 0058 const KCalendarCore::Incidence::Ptr oldIncidence = this->incidence(incidence->instanceIdentifier()); 0059 if (!oldIncidence) { 0060 // Change for event we don't know about -> discard 0061 return; 0062 } 0063 0064 // Unfortunately the plasma applet does not handle event moves 0065 // so we have to simulate via remove+add 0066 if (oldIncidence->allDay() != incidence->allDay() || oldIncidence->dtStart() != incidence->dtStart() 0067 || oldIncidence->dateTime(KCalendarCore::IncidenceBase::RoleEnd) != incidence->dateTime(KCalendarCore::IncidenceBase::RoleEnd)) { 0068 Q_EMIT incidenceChanger()->deleteFinished(0, {item.id()}, Akonadi::IncidenceChanger::ResultCodeSuccess, QString()); 0069 Q_EMIT incidenceChanger()->createFinished(0, item, Akonadi::IncidenceChanger::ResultCodeSuccess, QString()); 0070 } else { 0071 Q_EMIT incidenceChanger()->modifyFinished(0, item, Akonadi::IncidenceChanger::ResultCodeSuccess, QString()); 0072 } 0073 }); 0074 connect(mMonitor, &Akonadi::Monitor::itemRemoved, this, [this](const Akonadi::Item &item) { 0075 Q_EMIT incidenceChanger()->deleteFinished(0, {item.id()}, Akonadi::IncidenceChanger::ResultCodeSuccess, QString()); 0076 }); 0077 connect(mMonitor, &Akonadi::Monitor::collectionRemoved, this, &EventModel::removeCalendar); 0078 } 0079 0080 void EventModel::addCalendar(const Akonadi::Collection &col) 0081 { 0082 if (!mCols.contains(col)) { 0083 mCols.push_back(col); 0084 0085 createMonitor(); 0086 mMonitor->setCollectionMonitored(col, true); 0087 0088 populateCollection(col); 0089 } 0090 } 0091 0092 void EventModel::removeCalendar(const Akonadi::Collection &col) 0093 { 0094 auto it = std::find(mCols.begin(), mCols.end(), col); 0095 if (it != mCols.end()) { 0096 mCols.erase(it); 0097 if (mMonitor) { 0098 mMonitor->setCollectionMonitored(col, false); 0099 } 0100 0101 removeCollection(col); 0102 } 0103 } 0104 0105 QList<Akonadi::Collection> EventModel::collections() const 0106 { 0107 return mCols; 0108 } 0109 0110 Akonadi::Collection EventModel::collection(qint64 id) const 0111 { 0112 auto it = std::find(mCols.cbegin(), mCols.cend(), Akonadi::Collection(id)); 0113 return it == mCols.cend() ? Akonadi::Collection(id) : *it; 0114 } 0115 0116 void EventModel::populateCollection(const Akonadi::Collection &col) 0117 { 0118 qCDebug(PIMEVENTSPLUGIN_LOG) << "Populating events from collection" << col.id(); 0119 auto job = new Akonadi::ItemFetchJob(col, this); 0120 job->fetchScope().fetchFullPayload(true); 0121 job->fetchScope().setAncestorRetrieval(Akonadi::ItemFetchScope::Parent); 0122 job->setDeliveryOption(Akonadi::ItemFetchJob::EmitItemsInBatches); 0123 mFetchJobs.insert(col.id(), job); 0124 connect(job, &Akonadi::ItemFetchJob::itemsReceived, this, &EventModel::onItemsReceived); 0125 connect(job, &Akonadi::ItemFetchJob::result, job, [this, col](KJob *job) { 0126 mFetchJobs.remove(col.id()); 0127 auto fetch = qobject_cast<Akonadi::ItemFetchJob *>(job); 0128 qCDebug(PIMEVENTSPLUGIN_LOG) << "Received" << fetch->count() << "events for collection" << col.id(); 0129 }); 0130 } 0131 0132 void EventModel::onItemsReceived(const Akonadi::Item::List &items) 0133 { 0134 qCDebug(PIMEVENTSPLUGIN_LOG) << "Batch: received" << items.count() << "items"; 0135 for (const auto &item : items) { 0136 if (item.hasPayload<KCalendarCore::Incidence::Ptr>()) { 0137 Q_EMIT incidenceChanger()->createFinished(0, item, Akonadi::IncidenceChanger::ResultCodeSuccess, QString()); 0138 } else { 0139 qCDebug(PIMEVENTSPLUGIN_LOG) << "Item" << item.id() << "has no payload"; 0140 } 0141 } 0142 } 0143 0144 void EventModel::removeCollection(const Akonadi::Collection &col) 0145 { 0146 if (KJob *job = mFetchJobs.take(col.id())) { 0147 disconnect(job, nullptr, this, nullptr); 0148 job->kill(); 0149 } 0150 0151 const auto items = this->items(col.id()); 0152 qCDebug(PIMEVENTSPLUGIN_LOG) << "Removing" << items.count() << "events for collection" << col.id(); 0153 if (items.isEmpty()) { 0154 return; 0155 } 0156 0157 QList<Akonadi::Item::Id> ids; 0158 ids.reserve(items.size()); 0159 std::transform(items.cbegin(), items.cend(), std::back_inserter(ids), std::mem_fn(&Akonadi::Item::id)); 0160 0161 Q_EMIT incidenceChanger()->deleteFinished(0, ids, Akonadi::IncidenceChanger::ResultCodeSuccess, QString()); 0162 } 0163 0164 #include "moc_eventmodel.cpp"