20 #include "etmcalendar.h"
21 #include "etmcalendar_p.h"
22 #include "blockalarmsattribute.h"
23 #include "calendarmodel_p.h"
24 #include "kcolumnfilterproxymodel_p.h"
25 #include "calfilterproxymodel_p.h"
28 #include <akonadi/item.h>
29 #include <akonadi/session.h>
30 #include <akonadi/collection.h>
31 #include <akonadi/changerecorder.h>
32 #include <akonadi/itemfetchscope.h>
33 #include <akonadi/entitydisplayattribute.h>
34 #include <akonadi/entitymimetypefiltermodel.h>
35 #include <akonadi/collectionfilterproxymodel.h>
36 #include <KSelectionProxyModel>
37 #include <KDescendantsProxyModel>
39 #include <QItemSelectionModel>
43 using namespace KCalCore;
47 ETMCalendarPrivate::ETMCalendarPrivate(
ETMCalendar *qq) : CalendarBasePrivate(qq)
50 , mCheckableProxyModel(0)
51 , mCollectionProxyModel(0)
52 , mCalFilterProxyModel(0)
54 , mCollectionFilteringEnabled(true)
57 mListensForNewItems =
true;
60 void ETMCalendarPrivate::init()
77 QStringList allMimeTypes;
78 allMimeTypes << KCalCore::Event::eventMimeType() << KCalCore::Todo::todoMimeType()
79 << KCalCore::Journal::journalMimeType();
81 foreach(
const QString &mimetype, allMimeTypes) {
85 mETM = CalendarModel::create(monitor);
86 mETM->setObjectName(
"ETM");
91 connect(q, SIGNAL(filterChanged()), SLOT(onFilterChanged()));
95 connect(mETM.data(), SIGNAL(rowsInserted(QModelIndex,
int,
int)),
96 SLOT(onRowsInserted(QModelIndex,
int,
int)));
97 connect(mETM.data(), SIGNAL(dataChanged(QModelIndex,QModelIndex)),
98 SLOT(onDataChanged(QModelIndex,QModelIndex)));
99 connect(mETM.data(), SIGNAL(rowsMoved(QModelIndex,
int,
int,QModelIndex,
int)),
100 SLOT(onRowsMoved(QModelIndex,
int,
int,QModelIndex,
int)));
101 connect(mETM.data(), SIGNAL(rowsRemoved(QModelIndex,
int,
int)),
102 SLOT(onRowsRemoved(QModelIndex,
int,
int)));
104 connect(mFilteredETM, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
105 SLOT(onDataChangedInFilteredModel(QModelIndex,QModelIndex)));
106 connect(mFilteredETM, SIGNAL(layoutChanged()),
107 SLOT(onLayoutChangedInFilteredModel()));
108 connect(mFilteredETM, SIGNAL(modelReset()),
109 SLOT(onModelResetInFilteredModel()));
110 connect(mFilteredETM, SIGNAL(rowsInserted(QModelIndex,
int,
int)),
111 SLOT(onRowsInsertedInFilteredModel(QModelIndex,
int,
int)));
112 connect(mFilteredETM, SIGNAL(rowsAboutToBeRemoved(QModelIndex,
int,
int)),
113 SLOT(onRowsAboutToBeRemovedInFilteredModel(QModelIndex,
int,
int)));
119 const QSet<QByteArray> &attributeNames)
121 Q_ASSERT(collection.isValid());
123 if (attributeNames.contains(
"AccessRights")) {
124 Akonadi::Item::List items = q->items();
125 foreach(
const Akonadi::Item &item, items) {
126 if (item.storageCollectionId() == collection.id()) {
127 KCalCore::Incidence::Ptr incidence = CalendarUtils::incidence(item);
134 emit q->collectionChanged(collection, attributeNames);
137 void ETMCalendarPrivate::setupFilteredETM()
141 columnFilterProxy->setSourceModel(mETM.data());
143 columnFilterProxy->setObjectName(
"Remove columns");
146 mCollectionProxyModel->setObjectName(
"Only show collections");
147 mCollectionProxyModel->setDynamicSortFilter(
true);
148 mCollectionProxyModel->addMimeTypeFilter(QString::fromLatin1(
"text/calendar"));
149 mCollectionProxyModel->setExcludeVirtualCollections(
true);
150 mCollectionProxyModel->setSortCaseSensitivity(Qt::CaseInsensitive);
151 mCollectionProxyModel->setSourceModel(columnFilterProxy);
154 QItemSelectionModel* selectionModel =
new QItemSelectionModel(mCollectionProxyModel);
155 selectionModel->setObjectName(
"Calendar Selection Model");
158 mCheckableProxyModel =
new CheckableProxyModel(
this);
159 mCheckableProxyModel->setSelectionModel(selectionModel);
160 mCheckableProxyModel->setSourceModel(mCollectionProxyModel);
161 mCheckableProxyModel->setObjectName(
"Add checkboxes");
163 mSelectionProxy =
new KSelectionProxyModel(selectionModel,
this);
164 mSelectionProxy->setObjectName(
"Only show items of selected collection");
165 mSelectionProxy->setFilterBehavior(KSelectionProxyModel::ChildrenOfExactSelection);
166 mSelectionProxy->setSourceModel(mETM.data());
168 mCalFilterProxyModel =
new CalFilterProxyModel(
this);
169 mCalFilterProxyModel->setFilter(q->filter());
170 mCalFilterProxyModel->setSourceModel(mSelectionProxy);
171 mCalFilterProxyModel->setObjectName(
"KCalCore::CalFilter filtering");
174 mFilteredETM->setSourceModel(mCalFilterProxyModel);
176 mFilteredETM->setSortRole(CalendarModel::SortRole);
177 mFilteredETM->setObjectName(
"Show headers");
179 #ifdef AKONADI_CALENDAR_DEBUG_MODEL
180 QTreeView *view =
new QTreeView;
181 view->setModel(mFilteredETM);
186 ETMCalendarPrivate::~ETMCalendarPrivate()
190 void ETMCalendarPrivate::loadFromETM()
192 itemsAdded(itemsFromModel(mFilteredETM));
195 void ETMCalendarPrivate::clear()
197 mCollectionMap.clear();
198 mItemsByCollection.clear();
200 itemsRemoved(mItemById.values());
202 if (!mItemById.isEmpty()) {
204 kDebug() <<
"This shouldnt happen: !mItemById.isEmpty()";
205 foreach(Akonadi::Item::Id
id, mItemById.keys()) {
206 kDebug() <<
"Id = " << id;
213 if (!mItemIdByUid.isEmpty()) {
215 kDebug() <<
"This shouldnt happen: !mItemIdByUid.isEmpty()";
216 foreach(
const QString &uid, mItemIdByUid.keys()) {
217 kDebug() <<
"uid: " << uid;
219 mItemIdByUid.clear();
222 mParentUidToChildrenUid.clear();
226 Akonadi::Item::List ETMCalendarPrivate::itemsFromModel(
const QAbstractItemModel *model,
227 const QModelIndex &parentIndex,
230 const int endRow = end >= 0 ? end : model->rowCount(parentIndex) - 1;
231 Akonadi::Item::List items;
233 QModelIndex i = model->index(row, 0, parentIndex);
234 while (row <= endRow) {
235 const Akonadi::Item item = itemFromIndex(i);
236 if (item.hasPayload<KCalCore::Incidence::Ptr>()) {
239 const QModelIndex childIndex = i.child(0, 0);
240 if (childIndex.isValid()) {
241 items << itemsFromModel(model, i);
245 i = i.sibling(row, 0);
251 const QModelIndex &parentIndex,
254 const int endRow = end >= 0 ? end : model->rowCount(parentIndex) - 1;
257 QModelIndex i = model->index(row, 0, parentIndex);
258 while (row <= endRow) {
260 if (collection.isValid()) {
261 collections << collection;
262 QModelIndex childIndex = i.child(0, 0);
263 if (childIndex.isValid()) {
264 collections << collectionsFromModel(model, i);
268 i = i.sibling(row, 0);
273 Akonadi::Item ETMCalendarPrivate::itemFromIndex(
const QModelIndex &idx)
276 item.setParentCollection(
281 void ETMCalendarPrivate::itemsAdded(
const Akonadi::Item::List &items)
283 if (!items.isEmpty()) {
284 foreach(
const Akonadi::Item &item, items) {
285 internalInsert(item);
289 if (mPopulatedCollectionIds.contains(
id)) {
292 emit q->calendarChanged();
297 void ETMCalendarPrivate::itemsRemoved(
const Akonadi::Item::List &items)
299 foreach(
const Akonadi::Item &item, items) {
300 internalRemove(item);
302 emit q->calendarChanged();
310 void ETMCalendarPrivate::onRowsInserted(
const QModelIndex &index,
317 mCollectionMap[collection.id()] = collection;
320 if (!collections.isEmpty())
321 emit q->collectionsAdded(collections);
326 mPopulatedCollectionIds.insert(
id);
327 emit q->calendarChanged();
330 void ETMCalendarPrivate::onRowsRemoved(
const QModelIndex &index,
int start,
int end)
334 mCollectionMap.remove(collection.id());
337 if (!collections.isEmpty())
338 emit q->collectionsRemoved(collections);
341 void ETMCalendarPrivate::onDataChanged(
const QModelIndex &topLeft,
342 const QModelIndex &bottomRight)
345 Q_ASSERT(topLeft.row() <= bottomRight.row());
346 const int endRow = bottomRight.row();
347 QModelIndex i(topLeft);
349 while (row <= endRow) {
353 mCollectionMap.insert(col.
id(), col);
359 void ETMCalendarPrivate::onRowsMoved(
const QModelIndex &sourceParent,
362 const QModelIndex &destinationParent,
366 Q_UNUSED(sourceParent);
367 Q_UNUSED(sourceStart);
369 Q_UNUSED(destinationParent);
370 Q_UNUSED(destinationRow);
373 void ETMCalendarPrivate::onLayoutChangedInFilteredModel()
379 void ETMCalendarPrivate::onModelResetInFilteredModel()
385 void ETMCalendarPrivate::onDataChangedInFilteredModel(
const QModelIndex &topLeft,
386 const QModelIndex &bottomRight)
388 Q_ASSERT(topLeft.row() <= bottomRight.row());
389 const int endRow = bottomRight.row();
390 QModelIndex i(topLeft);
392 while (row <= endRow) {
393 const Akonadi::Item item = itemFromIndex(i);
394 if (item.isValid() && item.hasPayload<KCalCore::Incidence::Ptr>())
398 i = i.sibling(row, topLeft.column());
401 emit q->calendarChanged();
404 void ETMCalendarPrivate::updateItem(
const Akonadi::Item &item)
406 Incidence::Ptr newIncidence = CalendarUtils::incidence(item);
407 Q_ASSERT(newIncidence);
408 Q_ASSERT(!newIncidence->uid().isEmpty());
409 newIncidence->setCustomProperty(
"VOLATILE",
"AKONADI-ID", QString::number(item.id()));
410 IncidenceBase::Ptr existingIncidence = q->incidence(newIncidence->uid(), newIncidence->recurrenceId());
412 if (!existingIncidence && !mItemById.contains(item.id())) {
417 mItemsByCollection.insert(item.storageCollectionId(), item);
418 Akonadi::Item oldItem = mItemById.value(item.id());
420 if (existingIncidence) {
422 Akonadi::Item updatedItem = item;
423 updatedItem.setPayload<KCalCore::Incidence::Ptr>(existingIncidence.staticCast<KCalCore::Incidence>());
424 mItemById.insert(item.id(), updatedItem);
427 handleParentChanged(newIncidence);
428 *(existingIncidence.data()) = *(newIncidence.data());
430 mItemById.insert(item.id(), item);
432 handleUidChange(oldItem, item, newIncidence->instanceIdentifier());
436 void ETMCalendarPrivate::onRowsInsertedInFilteredModel(
const QModelIndex &index,
439 itemsAdded(itemsFromModel(mFilteredETM, index, start, end));
442 void ETMCalendarPrivate::onRowsAboutToBeRemovedInFilteredModel(
const QModelIndex &index,
445 itemsRemoved(itemsFromModel(mFilteredETM, index, start, end));
448 void ETMCalendarPrivate::onFilterChanged()
450 mCalFilterProxyModel->setFilter(q->filter());
462 d->mMimeTypes = mimeTypes;
473 d->mETM =
model->weakPointer().toStrongRef();
487 return d->mCollectionMap.value(
id);
501 return col.
rights() & right;
507 return d->mFilteredETM;
513 return d->mCheckableProxyModel;
516 KCalCore::Alarm::List ETMCalendar::alarms(
const KDateTime &from,
518 bool excludeBlockedAlarms)
const
521 KCalCore::Alarm::List alarmList;
522 QHashIterator<Akonadi::Item::Id, Akonadi::Item> i(d->mItemById);
523 while (i.hasNext()) {
524 const Akonadi::Item
item = i.next().value();
528 if (excludeBlockedAlarms) {
541 KCalCore::Incidence::Ptr incidence;
542 if (
item.isValid() &&
item.hasPayload<KCalCore::Incidence::Ptr>()) {
543 incidence = KCalCore::Incidence::Ptr(
item.payload<KCalCore::Incidence::Ptr>()->clone());
554 Q_FOREACH(
const KCalCore::Alarm::Ptr &alarm, incidence->alarms()) {
556 incidence->removeAlarm(alarm);
561 if (incidence->alarms().isEmpty()) {
566 if (incidence->recurs()) {
567 appendRecurringAlarms(tmpList, incidence, from, to);
569 appendAlarms(tmpList, incidence, from, to);
575 QVectorIterator<Alarm::Ptr> a(tmpList);
576 while (a.hasNext()) {
577 a.next()->setCustomProperty(
"ETMCalendar",
"parentUid", incidence->uid());
579 alarmList += tmpList;
587 return d->mETM.data();
593 if (d->mCollectionFilteringEnabled != enable) {
594 d->mCollectionFilteringEnabled = enable;
596 d->mSelectionProxy->setSourceModel(d->mETM.data());
597 QAbstractItemModel *oldModel = d->mCalFilterProxyModel->sourceModel();
598 d->mCalFilterProxyModel->setSourceModel(d->mSelectionProxy);
599 delete qobject_cast<KDescendantsProxyModel *>(oldModel);
601 KDescendantsProxyModel *flatner =
new KDescendantsProxyModel(
this);
602 flatner->setSourceModel(d->mETM.data());
603 d->mCalFilterProxyModel->setSourceModel(flatner);
611 return d->mCollectionFilteringEnabled;
630 #include "moc_etmcalendar.cpp"
631 #include "moc_etmcalendar_p.cpp"