20 #include "etmcalendar.h"
21 #include "etmcalendar_p.h"
22 #include "blockalarmsattribute.h"
23 #include "incidencefetchjob_p.h"
24 #include "calendarmodel_p.h"
25 #include "kcolumnfilterproxymodel_p.h"
26 #include "calfilterproxymodel_p.h"
29 #include <akonadi/item.h>
30 #include <akonadi/session.h>
31 #include <akonadi/collection.h>
32 #include <akonadi/changerecorder.h>
33 #include <akonadi/itemfetchscope.h>
34 #include <akonadi/entitydisplayattribute.h>
35 #include <akonadi/entitymimetypefiltermodel.h>
36 #include <akonadi/collectionfilterproxymodel.h>
37 #include <KSelectionProxyModel>
38 #include <KDescendantsProxyModel>
40 #include <QSortFilterProxyModel>
41 #include <QItemSelectionModel>
44 using namespace Akonadi;
45 using namespace KCalCore;
49 ETMCalendarPrivate::ETMCalendarPrivate(
ETMCalendar *qq) : CalendarBasePrivate(qq)
52 , mCheckableProxyModel(0)
53 , mCollectionProxyModel(0)
54 , mCalFilterProxyModel(0)
56 , mCollectionFilteringEnabled(true)
59 mListensForNewItems =
true;
62 void ETMCalendarPrivate::init()
78 QStringList allMimeTypes;
79 allMimeTypes << KCalCore::Event::eventMimeType() << KCalCore::Todo::todoMimeType()
80 << KCalCore::Journal::journalMimeType();
82 foreach(
const QString &mimetype, allMimeTypes) {
86 mETM =
new CalendarModel(monitor, q);
87 mETM->setObjectName(
"ETM");
91 connect(q, SIGNAL(filterChanged()), SLOT(onFilterChanged()));
95 connect(mETM, SIGNAL(rowsInserted(QModelIndex,
int,
int)),
96 SLOT(onRowsInserted(QModelIndex,
int,
int)));
97 connect(mETM, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
98 SLOT(onDataChanged(QModelIndex,QModelIndex)));
99 connect(mETM, SIGNAL(rowsMoved(QModelIndex,
int,
int,QModelIndex,
int)),
100 SLOT(onRowsMoved(QModelIndex,
int,
int,QModelIndex,
int)));
101 connect(mETM, 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);
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);
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(mETM));
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) {
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;
474 return d->mCollectionMap.value(
id);
488 return col.
rights() & right;
494 return d->mFilteredETM;
500 return d->mCheckableProxyModel;
503 KCalCore::Alarm::List ETMCalendar::alarms(
const KDateTime &from,
505 bool excludeBlockedAlarms)
const
508 KCalCore::Alarm::List alarmList;
509 QHashIterator<Akonadi::Item::Id, Akonadi::Item> i(d->mItemById);
510 while (i.hasNext()) {
511 const Akonadi::Item item = i.next().value();
515 if (excludeBlockedAlarms) {
517 Akonadi::Collection parentCollection = d->mCollectionMap.value(item.storageCollectionId());
528 KCalCore::Incidence::Ptr incidence;
529 if (item.isValid() && item.hasPayload<KCalCore::Incidence::Ptr>()) {
530 incidence = KCalCore::Incidence::Ptr(item.payload<KCalCore::Incidence::Ptr>()->clone());
541 Q_FOREACH(
const KCalCore::Alarm::Ptr &alarm, incidence->alarms()) {
543 incidence->removeAlarm(alarm);
548 if (incidence->alarms().isEmpty()) {
553 if (incidence->recurs()) {
554 appendRecurringAlarms(tmpList, incidence, from, to);
556 appendAlarms(tmpList, incidence, from, to);
562 QVectorIterator<Alarm::Ptr> a(tmpList);
563 while (a.hasNext()) {
564 a.next()->setCustomProperty(
"ETMCalendar",
"parentUid", incidence->uid());
566 alarmList += tmpList;
580 if (d->mCollectionFilteringEnabled != enable) {
581 d->mCollectionFilteringEnabled = enable;
583 d->mSelectionProxy->setSourceModel(d->mETM);
584 QAbstractItemModel *oldModel = d->mCalFilterProxyModel->sourceModel();
585 d->mCalFilterProxyModel->setSourceModel(d->mSelectionProxy);
586 delete qobject_cast<KDescendantsProxyModel *>(oldModel);
588 KDescendantsProxyModel *flatner =
new KDescendantsProxyModel(
this);
589 flatner->setSourceModel(d->mETM);
590 d->mCalFilterProxyModel->setSourceModel(flatner);
598 return d->mCollectionFilteringEnabled;
601 #include "moc_etmcalendar.cpp"
602 #include "moc_etmcalendar_p.cpp"
void fetchAttribute(const QByteArray &type, bool fetch=true)
Sets whether the attribute of the given type should be fetched.
A proxy model that filters collections by mime type.
void setSession(Akonadi::Session *session)
Sets the session used by the Monitor to communicate with the Akonadi server.
void setMimeTypeMonitored(const QString &mimetype, bool monitored=true)
Sets whether items of the specified mime type shall be monitored for changes.
void setCollectionMonitored(const Collection &collection, bool monitored=true)
Sets whether the specified collection shall be monitored for changes.
Akonadi::EntityTreeModel * entityTreeModel() const
Returns the underlying EntityTreeModel.
Represents a collection of PIM items.
Filter model to make only certain columns of a model visible.
void setItemFetchScope(const ItemFetchScope &fetchScope)
Sets the item fetch scope.
qint64 Id
Describes the unique id type.
Akonadi::Item item(const QString &uid) const
Returns the Item containing the incidence with uid uid or an invalid Item if the incidence isn't foun...
Can change items in this collection.
A proxy model that filters entities by mime type.
void fetchFullPayload(bool fetch=true)
Sets whether the full payload shall be fetched.
Attribute * attribute(const QByteArray &name) const
Returns the attribute of the given type name if available, 0 otherwise.
bool collectionFilteringEnabled() const
Returns whether collection filtering is enabled.
QAbstractItemModel * model() const
Convenience method to access the contents of this KCalCore::Calendar through a QAIM interface...
KCheckableProxyModel * checkableProxyModel() const
Returns the KCheckableProxyModel used to select from which collections should the calendar be populat...
static Collection root()
Returns the root collection.
ETMCalendar(QObject *parent=0)
Constructs a new ETMCalendar.
A communication session with the Akonadi storage.
The parent collection of the entity.
void fetchCollection(bool enable)
Enables automatic fetching of changed collections from the Akonadi storage.
Id id() const
Returns the unique identifier of the entity.
Rights rights() const
Returns the rights the user has on the collection.
Specifies which parts of an item should be fetched from the Akonadi storage.
Header information for a list of items.
Akonadi::Collection collection(Akonadi::Collection::Id) const
Returns the collection having id.
bool isAlarmTypeBlocked(KCalCore::Alarm::Type type) const
Returns whether given alarm type is blocked or not.
Right
Describes rights of a collection.
bool hasAttribute(const QByteArray &name) const
Returns true if the entity has an attribute of the given type name, false otherwise.
A model for collections and items together.
~ETMCalendar()
Destroys this ETMCalendar.
The base class for all akonadi aware calendars.
bool isValid() const
Returns whether the entity is valid.
QList< Collection > List
Describes a list of collections.
Attribute that stores the properties that are used to display an entity.
A KCalCore::Calendar that uses an EntityTreeModel to populate itself.
void setVisibleColumn(int column)
Convenience function.
bool hasRight(const Akonadi::Item &item, Akonadi::Collection::Right right) const
Returns true if the collection owning incidence has righ right.
void setCollectionFilteringEnabled(bool enable)
Enable or disable collection filtering.
Records and replays change notification.
An Attribute that marks that alarms from a calendar collection are blocked.