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 <QItemSelectionModel>
43 using namespace Akonadi;
44 using namespace KCalCore;
48 ETMCalendarPrivate::ETMCalendarPrivate(
ETMCalendar *qq) : CalendarBasePrivate(qq)
51 , mCheckableProxyModel(0)
52 , mCollectionProxyModel(0)
53 , mCalFilterProxyModel(0)
55 , mCollectionFilteringEnabled(true)
58 mListensForNewItems =
true;
61 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 = CalendarModel::create(monitor);
87 mETM->setObjectName(
"ETM");
92 connect(q, SIGNAL(filterChanged()), SLOT(onFilterChanged()));
96 connect(mETM.data(), SIGNAL(rowsInserted(QModelIndex,
int,
int)),
97 SLOT(onRowsInserted(QModelIndex,
int,
int)));
98 connect(mETM.data(), SIGNAL(dataChanged(QModelIndex,QModelIndex)),
99 SLOT(onDataChanged(QModelIndex,QModelIndex)));
100 connect(mETM.data(), SIGNAL(rowsMoved(QModelIndex,
int,
int,QModelIndex,
int)),
101 SLOT(onRowsMoved(QModelIndex,
int,
int,QModelIndex,
int)));
102 connect(mETM.data(), SIGNAL(rowsRemoved(QModelIndex,
int,
int)),
103 SLOT(onRowsRemoved(QModelIndex,
int,
int)));
105 connect(mFilteredETM, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
106 SLOT(onDataChangedInFilteredModel(QModelIndex,QModelIndex)));
107 connect(mFilteredETM, SIGNAL(layoutChanged()),
108 SLOT(onLayoutChangedInFilteredModel()));
109 connect(mFilteredETM, SIGNAL(modelReset()),
110 SLOT(onModelResetInFilteredModel()));
111 connect(mFilteredETM, SIGNAL(rowsInserted(QModelIndex,
int,
int)),
112 SLOT(onRowsInsertedInFilteredModel(QModelIndex,
int,
int)));
113 connect(mFilteredETM, SIGNAL(rowsAboutToBeRemoved(QModelIndex,
int,
int)),
114 SLOT(onRowsAboutToBeRemovedInFilteredModel(QModelIndex,
int,
int)));
120 const QSet<QByteArray> &attributeNames)
122 Q_ASSERT(collection.
isValid());
124 if (attributeNames.contains(
"AccessRights")) {
125 Akonadi::Item::List items = q->items();
126 foreach(
const Akonadi::Item &item, items) {
127 if (item.storageCollectionId() == collection.
id()) {
128 KCalCore::Incidence::Ptr incidence = CalendarUtils::incidence(item);
135 emit q->collectionChanged(collection, attributeNames);
138 void ETMCalendarPrivate::setupFilteredETM()
142 columnFilterProxy->setSourceModel(mETM.data());
144 columnFilterProxy->setObjectName(
"Remove columns");
147 mCollectionProxyModel->setObjectName(
"Only show collections");
148 mCollectionProxyModel->setDynamicSortFilter(
true);
149 mCollectionProxyModel->addMimeTypeFilter(QString::fromLatin1(
"text/calendar"));
150 mCollectionProxyModel->setExcludeVirtualCollections(
true);
151 mCollectionProxyModel->setSortCaseSensitivity(Qt::CaseInsensitive);
152 mCollectionProxyModel->setSourceModel(columnFilterProxy);
155 QItemSelectionModel* selectionModel =
new QItemSelectionModel(mCollectionProxyModel);
156 selectionModel->setObjectName(
"Calendar Selection Model");
159 mCheckableProxyModel =
new CheckableProxyModel(
this);
160 mCheckableProxyModel->setSelectionModel(selectionModel);
161 mCheckableProxyModel->setSourceModel(mCollectionProxyModel);
162 mCheckableProxyModel->setObjectName(
"Add checkboxes");
164 mSelectionProxy =
new KSelectionProxyModel(selectionModel,
this);
165 mSelectionProxy->setObjectName(
"Only show items of selected collection");
166 mSelectionProxy->setFilterBehavior(KSelectionProxyModel::ChildrenOfExactSelection);
167 mSelectionProxy->setSourceModel(mETM.data());
169 mCalFilterProxyModel =
new CalFilterProxyModel(
this);
170 mCalFilterProxyModel->setFilter(q->filter());
171 mCalFilterProxyModel->setSourceModel(mSelectionProxy);
172 mCalFilterProxyModel->setObjectName(
"KCalCore::CalFilter filtering");
175 mFilteredETM->setSourceModel(mCalFilterProxyModel);
177 mFilteredETM->setSortRole(CalendarModel::SortRole);
178 mFilteredETM->setObjectName(
"Show headers");
180 #ifdef AKONADI_CALENDAR_DEBUG_MODEL
181 QTreeView *view =
new QTreeView;
182 view->setModel(mFilteredETM);
187 ETMCalendarPrivate::~ETMCalendarPrivate()
191 void ETMCalendarPrivate::loadFromETM()
193 itemsAdded(itemsFromModel(mFilteredETM));
196 void ETMCalendarPrivate::clear()
198 mCollectionMap.clear();
199 mItemsByCollection.clear();
201 itemsRemoved(mItemById.values());
203 if (!mItemById.isEmpty()) {
205 kDebug() <<
"This shouldnt happen: !mItemById.isEmpty()";
206 foreach(Akonadi::Item::Id
id, mItemById.keys()) {
207 kDebug() <<
"Id = " << id;
214 if (!mItemIdByUid.isEmpty()) {
216 kDebug() <<
"This shouldnt happen: !mItemIdByUid.isEmpty()";
217 foreach(
const QString &uid, mItemIdByUid.keys()) {
218 kDebug() <<
"uid: " << uid;
220 mItemIdByUid.clear();
223 mParentUidToChildrenUid.clear();
227 Akonadi::Item::List ETMCalendarPrivate::itemsFromModel(
const QAbstractItemModel *model,
228 const QModelIndex &parentIndex,
231 const int endRow = end >= 0 ? end : model->rowCount(parentIndex) - 1;
232 Akonadi::Item::List items;
234 QModelIndex i = model->index(row, 0, parentIndex);
235 while (row <= endRow) {
236 const Akonadi::Item item = itemFromIndex(i);
237 if (item.hasPayload<KCalCore::Incidence::Ptr>()) {
240 const QModelIndex childIndex = i.child(0, 0);
241 if (childIndex.isValid()) {
242 items << itemsFromModel(model, i);
246 i = i.sibling(row, 0);
252 const QModelIndex &parentIndex,
255 const int endRow = end >= 0 ? end : model->rowCount(parentIndex) - 1;
258 QModelIndex i = model->index(row, 0, parentIndex);
259 while (row <= endRow) {
262 collections << collection;
263 QModelIndex childIndex = i.child(0, 0);
264 if (childIndex.isValid()) {
265 collections << collectionsFromModel(model, i);
269 i = i.sibling(row, 0);
274 Akonadi::Item ETMCalendarPrivate::itemFromIndex(
const QModelIndex &idx)
277 item.setParentCollection(
282 void ETMCalendarPrivate::itemsAdded(
const Akonadi::Item::List &items)
284 if (!items.isEmpty()) {
285 foreach(
const Akonadi::Item &item, items) {
286 internalInsert(item);
290 if (mPopulatedCollectionIds.contains(
id)) {
293 emit q->calendarChanged();
298 void ETMCalendarPrivate::itemsRemoved(
const Akonadi::Item::List &items)
300 foreach(
const Akonadi::Item &item, items) {
301 internalRemove(item);
303 emit q->calendarChanged();
311 void ETMCalendarPrivate::onRowsInserted(
const QModelIndex &index,
318 mCollectionMap[collection.
id()] = collection;
321 if (!collections.isEmpty())
322 emit q->collectionsAdded(collections);
327 mPopulatedCollectionIds.insert(
id);
328 emit q->calendarChanged();
331 void ETMCalendarPrivate::onRowsRemoved(
const QModelIndex &index,
int start,
int end)
335 mCollectionMap.remove(collection.
id());
338 if (!collections.isEmpty())
339 emit q->collectionsRemoved(collections);
342 void ETMCalendarPrivate::onDataChanged(
const QModelIndex &topLeft,
343 const QModelIndex &bottomRight)
346 Q_ASSERT(topLeft.row() <= bottomRight.row());
347 const int endRow = bottomRight.row();
348 QModelIndex i(topLeft);
350 while (row <= endRow) {
354 mCollectionMap.insert(col.
id(), col);
360 void ETMCalendarPrivate::onRowsMoved(
const QModelIndex &sourceParent,
363 const QModelIndex &destinationParent,
367 Q_UNUSED(sourceParent);
368 Q_UNUSED(sourceStart);
370 Q_UNUSED(destinationParent);
371 Q_UNUSED(destinationRow);
374 void ETMCalendarPrivate::onLayoutChangedInFilteredModel()
380 void ETMCalendarPrivate::onModelResetInFilteredModel()
386 void ETMCalendarPrivate::onDataChangedInFilteredModel(
const QModelIndex &topLeft,
387 const QModelIndex &bottomRight)
389 Q_ASSERT(topLeft.row() <= bottomRight.row());
390 const int endRow = bottomRight.row();
391 QModelIndex i(topLeft);
393 while (row <= endRow) {
394 const Akonadi::Item item = itemFromIndex(i);
395 if (item.isValid() && item.hasPayload<KCalCore::Incidence::Ptr>())
399 i = i.sibling(row, topLeft.column());
402 emit q->calendarChanged();
405 void ETMCalendarPrivate::updateItem(
const Akonadi::Item &item)
407 Incidence::Ptr newIncidence = CalendarUtils::incidence(item);
408 Q_ASSERT(newIncidence);
409 Q_ASSERT(!newIncidence->uid().isEmpty());
410 newIncidence->setCustomProperty(
"VOLATILE",
"AKONADI-ID", QString::number(item.id()));
411 IncidenceBase::Ptr existingIncidence = q->incidence(newIncidence->uid(), newIncidence->recurrenceId());
413 if (!existingIncidence && !mItemById.contains(item.id())) {
418 mItemsByCollection.insert(item.storageCollectionId(), item);
419 Akonadi::Item oldItem = mItemById.value(item.id());
421 if (existingIncidence) {
423 Akonadi::Item updatedItem = item;
424 updatedItem.setPayload<KCalCore::Incidence::Ptr>(existingIncidence.staticCast<KCalCore::Incidence>());
425 mItemById.insert(item.id(), updatedItem);
428 handleParentChanged(newIncidence);
429 *(existingIncidence.data()) = *(newIncidence.data());
431 mItemById.insert(item.id(), item);
433 handleUidChange(oldItem, item, newIncidence->instanceIdentifier());
437 void ETMCalendarPrivate::onRowsInsertedInFilteredModel(
const QModelIndex &index,
440 itemsAdded(itemsFromModel(mFilteredETM, index, start, end));
443 void ETMCalendarPrivate::onRowsAboutToBeRemovedInFilteredModel(
const QModelIndex &index,
446 itemsRemoved(itemsFromModel(mFilteredETM, index, start, end));
449 void ETMCalendarPrivate::onFilterChanged()
451 mCalFilterProxyModel->setFilter(q->filter());
463 d->mMimeTypes = mimeTypes;
472 CalendarModel *model = qobject_cast<Akonadi::CalendarModel*>(other->
entityTreeModel());
474 d->mETM = model->weakPointer().toStrongRef();
488 return d->mCollectionMap.value(
id);
502 return col.
rights() & right;
508 return d->mFilteredETM;
514 return d->mCheckableProxyModel;
517 KCalCore::Alarm::List ETMCalendar::alarms(
const KDateTime &from,
519 bool excludeBlockedAlarms)
const
522 KCalCore::Alarm::List alarmList;
523 QHashIterator<Akonadi::Item::Id, Akonadi::Item> i(d->mItemById);
524 while (i.hasNext()) {
525 const Akonadi::Item item = i.next().value();
529 if (excludeBlockedAlarms) {
531 Akonadi::Collection parentCollection = d->mCollectionMap.value(item.storageCollectionId());
542 KCalCore::Incidence::Ptr incidence;
543 if (item.isValid() && item.hasPayload<KCalCore::Incidence::Ptr>()) {
544 incidence = KCalCore::Incidence::Ptr(item.payload<KCalCore::Incidence::Ptr>()->clone());
555 Q_FOREACH(
const KCalCore::Alarm::Ptr &alarm, incidence->alarms()) {
557 incidence->removeAlarm(alarm);
562 if (incidence->alarms().isEmpty()) {
567 if (incidence->recurs()) {
568 appendRecurringAlarms(tmpList, incidence, from, to);
570 appendAlarms(tmpList, incidence, from, to);
576 QVectorIterator<Alarm::Ptr> a(tmpList);
577 while (a.hasNext()) {
578 a.next()->setCustomProperty(
"ETMCalendar",
"parentUid", incidence->uid());
580 alarmList += tmpList;
588 return d->mETM.data();
594 if (d->mCollectionFilteringEnabled != enable) {
595 d->mCollectionFilteringEnabled = enable;
597 d->mSelectionProxy->setSourceModel(d->mETM.data());
598 QAbstractItemModel *oldModel = d->mCalFilterProxyModel->sourceModel();
599 d->mCalFilterProxyModel->setSourceModel(d->mSelectionProxy);
600 delete qobject_cast<KDescendantsProxyModel *>(oldModel);
602 KDescendantsProxyModel *flatner =
new KDescendantsProxyModel(
this);
603 flatner->setSourceModel(d->mETM.data());
604 d->mCalFilterProxyModel->setSourceModel(flatner);
612 return d->mCollectionFilteringEnabled;
615 #include "moc_etmcalendar.cpp"
616 #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.