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>
42 using namespace Akonadi;
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()
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)),
101 connect(mETM.data(), SIGNAL(rowsRemoved(
QModelIndex,
int,
int)),
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)));
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()
146 mCollectionProxyModel->setObjectName(
"Only show collections");
147 mCollectionProxyModel->setDynamicSortFilter(
true);
149 mCollectionProxyModel->setExcludeVirtualCollections(
true);
150 mCollectionProxyModel->setSortCaseSensitivity(Qt::CaseInsensitive);
151 mCollectionProxyModel->setSourceModel(columnFilterProxy);
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
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,
230 const int endRow = end >= 0 ? end : model->
rowCount(parentIndex) - 1;
231 Akonadi::Item::List items;
234 while (row <= endRow) {
235 const Akonadi::Item item = itemFromIndex(i);
236 if (item.hasPayload<KCalCore::Incidence::Ptr>()) {
241 items << itemsFromModel(model, i);
254 const int endRow = end >= 0 ? end : model->
rowCount(parentIndex) - 1;
258 while (row <= endRow) {
261 collections << collection;
264 collections << collectionsFromModel(model, i);
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;
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());
338 emit q->collectionsRemoved(collections);
341 void ETMCalendarPrivate::onDataChanged(
const QModelIndex &topLeft,
345 Q_ASSERT(topLeft.
row() <= bottomRight.
row());
346 const int endRow = bottomRight.
row();
349 while (row <= endRow) {
353 mCollectionMap.insert(col.
id(), col);
359 void ETMCalendarPrivate::onRowsMoved(
const QModelIndex &sourceParent,
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,
388 Q_ASSERT(topLeft.
row() <= bottomRight.
row());
389 const int endRow = bottomRight.
row();
392 while (row <= endRow) {
393 const Akonadi::Item item = itemFromIndex(i);
394 if (item.isValid() && item.hasPayload<KCalCore::Incidence::Ptr>())
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;
471 CalendarModel *model = qobject_cast<Akonadi::CalendarModel*>(other->
entityTreeModel());
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;
523 while (i.hasNext()) {
524 const Akonadi::Item item = i.next().value();
528 if (excludeBlockedAlarms) {
530 Akonadi::Collection parentCollection = d->mCollectionMap.value(item.storageCollectionId());
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);
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());
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"
void fetchAttribute(const QByteArray &type, bool fetch=true)
Sets whether the attribute of the given type should be fetched.
virtual int rowCount(const QModelIndex &parent) const =0
iterator remove(iterator pos)
virtual QModelIndex index(int row, int column, const QModelIndex &parent) const =0
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.
virtual void setSourceModel(QAbstractItemModel *sourceModel)
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.
QString number(int n, int base)
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...
void setObjectName(const QString &name)
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.
bool contains(const T &value) const
QModelIndex child(int row, int column) const
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.
QVariant data(int role) const
QModelIndex sibling(int row, int column) const
bool hasAttribute(const QByteArray &name) const
Returns true if the entity has an attribute of the given type name, false otherwise.
virtual void setModel(QAbstractItemModel *model)
A model for collections and items together.
bool isLoaded() const
Returns if the calendar already finished loading.
QString fromLatin1(const char *str, int size)
~ETMCalendar()
Destroys this ETMCalendar.
The base class for all akonadi aware calendars.
bool isValid() const
Returns whether the entity is valid.
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.