29 #include "kalarmsettings.h"
30 #include "kalarmdirsettings.h"
32 #include <kalarmcal/alarmtext.h>
33 #include <kalarmcal/collectionattribute.h>
34 #include <kalarmcal/compatibilityattribute.h>
35 #include <kalarmcal/eventattribute.h>
37 #include <akonadi/agentfilterproxymodel.h>
38 #include <akonadi/agentinstancecreatejob.h>
39 #include <akonadi/agentmanager.h>
40 #include <akonadi/agenttype.h>
41 #include <akonadi/agenttypedialog.h>
42 #include <akonadi/attributefactory.h>
43 #include <akonadi/changerecorder.h>
44 #include <akonadi/collectiondeletejob.h>
45 #include <akonadi/collectionmodifyjob.h>
46 #include <akonadi/entitydisplayattribute.h>
47 #include <akonadi/item.h>
48 #include <akonadi/itemcreatejob.h>
49 #include <akonadi/itemmodifyjob.h>
50 #include <akonadi/itemdeletejob.h>
51 #include <akonadi/itemfetchscope.h>
54 #include <kcolorutils.h>
56 #include <QApplication>
60 using namespace Akonadi;
61 using namespace KAlarmCal;
63 static Collection::Rights
writableRights = Collection::CanChangeItem | Collection::CanCreateItem | Collection::CanDeleteItem;
72 QPixmap* AkonadiModel::mTextIcon = 0;
73 QPixmap* AkonadiModel::mFileIcon = 0;
74 QPixmap* AkonadiModel::mCommandIcon = 0;
75 QPixmap* AkonadiModel::mEmailIcon = 0;
76 QPixmap* AkonadiModel::mAudioIcon = 0;
77 QSize AkonadiModel::mIconSize;
78 int AkonadiModel::mTimeHourPos = -2;
86 mInstance =
new AkonadiModel(
new ChangeRecorder(qApp), qApp);
93 AkonadiModel::AkonadiModel(ChangeRecorder* monitor,
QObject* parent)
94 : EntityTreeModel(monitor, parent),
96 mResourcesChecked(false),
100 setItemPopulationStrategy(LazyPopulation);
103 monitor->setCollectionMonitored(Collection::root());
104 monitor->setResourceMonitored(
"akonadi_kalarm_resource");
105 monitor->setResourceMonitored(
"akonadi_kalarm_dir_resource");
106 monitor->setMimeTypeMonitored(KAlarmCal::MIME_ACTIVE);
107 monitor->setMimeTypeMonitored(KAlarmCal::MIME_ARCHIVED);
108 monitor->setMimeTypeMonitored(KAlarmCal::MIME_TEMPLATE);
109 monitor->itemFetchScope().fetchFullPayload();
110 monitor->itemFetchScope().fetchAttribute<EventAttribute>();
112 AttributeFactory::registerAttribute<CollectionAttribute>();
113 AttributeFactory::registerAttribute<CompatibilityAttribute>();
114 AttributeFactory::registerAttribute<EventAttribute>();
118 mTextIcon =
new QPixmap(SmallIcon(QLatin1String(
"dialog-information")));
119 mFileIcon =
new QPixmap(SmallIcon(QLatin1String(
"document-open")));
120 mCommandIcon =
new QPixmap(SmallIcon(QLatin1String(
"system-run")));
121 mEmailIcon =
new QPixmap(SmallIcon(QLatin1String(
"mail-message-unread")));
122 mAudioIcon =
new QPixmap(SmallIcon(QLatin1String(
"audio-x-generic")));
123 mIconSize = mTextIcon->size().expandedTo(mFileIcon->size()).expandedTo(mCommandIcon->size()).expandedTo(mEmailIcon->size()).expandedTo(mAudioIcon->size());
127 #warning Only want to monitor collection properties, not content, when this becomes possible
129 connect(monitor, SIGNAL(collectionChanged(Akonadi::Collection,QSet<QByteArray>)), SLOT(slotCollectionChanged(Akonadi::Collection,QSet<QByteArray>)));
130 connect(monitor, SIGNAL(collectionRemoved(Akonadi::Collection)), SLOT(slotCollectionRemoved(Akonadi::Collection)));
132 SLOT(slotCollectionBeingCreated(QString,Akonadi::Collection::Id,
bool)));
135 Preferences::connect(SIGNAL(archivedColourChanged(QColor)),
this, SLOT(slotUpdateArchivedColour(QColor)));
136 Preferences::connect(SIGNAL(disabledColourChanged(QColor)),
this, SLOT(slotUpdateDisabledColour(QColor)));
137 Preferences::connect(SIGNAL(holidaysChanged(KHolidays::HolidayRegion)),
this, SLOT(slotUpdateHolidays()));
138 Preferences::connect(SIGNAL(workTimeChanged(QTime,QTime,QBitArray)),
this, SLOT(slotUpdateWorkingHours()));
140 connect(
this, SIGNAL(rowsInserted(QModelIndex,
int,
int)), SLOT(slotRowsInserted(QModelIndex,
int,
int)));
141 connect(
this, SIGNAL(rowsAboutToBeRemoved(QModelIndex,
int,
int)), SLOT(slotRowsAboutToBeRemoved(QModelIndex,
int,
int)));
142 connect(monitor, SIGNAL(itemChanged(Akonadi::Item,QSet<QByteArray>)), SLOT(slotMonitoredItemChanged(Akonadi::Item,QSet<QByteArray>)));
144 connect(ServerManager::self(), SIGNAL(stateChanged(Akonadi::ServerManager::State)),
145 SLOT(checkResources(Akonadi::ServerManager::State)));
146 checkResources(ServerManager::state());
156 void AkonadiModel::checkResources(ServerManager::State state)
158 if (!mResourcesChecked && state == ServerManager::Running)
160 mResourcesChecked =
true;
171 return mResourcesChecked && !mMigrating;
182 case Qt::BackgroundRole:
183 case Qt::ForegroundRole:
184 case Qt::DisplayRole:
185 case Qt::TextAlignmentRole:
186 case Qt::DecorationRole:
187 case Qt::SizeHintRole:
188 case Qt::AccessibleTextRole:
189 case Qt::ToolTipRole:
190 case Qt::CheckStateRole:
204 return EntityTreeModel::data(index, role);
207 const Collection
collection = index.data(CollectionRole).value<Collection>();
208 if (collection.isValid())
213 case Qt::DisplayRole:
214 return collection.displayName();
216 if (!collection.hasAttribute<CollectionAttribute>())
218 return static_cast<int>(collection.attribute<CollectionAttribute>()->enabled());
220 role = Qt::BackgroundRole;
222 case Qt::BackgroundRole:
224 QColor colour = backgroundColor_p(collection);
225 if (colour.isValid())
229 case Qt::ForegroundRole:
231 case Qt::ToolTipRole:
232 return tooltip(collection, CalEvent::ACTIVE | CalEvent::ARCHIVED | CalEvent::TEMPLATE);
234 return static_cast<int>(
types(collection));
236 if (!collection.hasAttribute<CollectionAttribute>()
239 return static_cast<int>(collection.attribute<CollectionAttribute>()->standard());
241 if (!collection.hasAttribute<CollectionAttribute>())
243 return collection.attribute<CollectionAttribute>()->keepFormat();
250 const Item item = index.data(ItemRole).value<Item>();
254 QString mime = item.mimeType();
255 if ((mime != KAlarmCal::MIME_ACTIVE && mime != KAlarmCal::MIME_ARCHIVED && mime != KAlarmCal::MIME_TEMPLATE)
256 || !item.hasPayload<KAEvent>())
262 if (mime == KAlarmCal::MIME_ACTIVE)
263 return CalEvent::ACTIVE;
264 if (mime == KAlarmCal::MIME_ARCHIVED)
265 return CalEvent::ARCHIVED;
266 if (mime == KAlarmCal::MIME_TEMPLATE)
267 return CalEvent::TEMPLATE;
270 if (!item.hasAttribute<EventAttribute>())
271 return KAEvent::CMD_NO_ERROR;
272 return item.attribute<EventAttribute>()->commandError();
276 int column = index.column();
277 if (role == Qt::WhatsThisRole)
278 return whatsThisText(column);
280 if (!event.isValid())
283 return event.actionTypes();
285 return event.actionSubType();
286 bool calendarColour =
false;
292 case Qt::BackgroundRole:
293 calendarColour =
true;
295 case Qt::DisplayRole:
303 due = event.startDateTime();
305 due =
event.nextTrigger(KAEvent::DISPLAY_TRIGGER);
306 return due.isValid() ? due.effectiveKDateTime().toUtc().dateTime()
307 : QDateTime(QDate(9999,12,31), QTime(0,0,0));
316 case Qt::BackgroundRole:
317 calendarColour =
true;
319 case Qt::DisplayRole:
327 DateTime due =
event.nextTrigger(KAEvent::DISPLAY_TRIGGER);
328 KDateTime now = KDateTime::currentUtcDateTime();
329 if (due.isDateOnly())
330 return now.date().daysTo(due.date()) * 1440;
331 return (now.secsTo(due.effectiveKDateTime()) + 59) / 60;
338 case Qt::BackgroundRole:
339 calendarColour =
true;
341 case Qt::DisplayRole:
342 return repeatText(event);
343 case Qt::TextAlignmentRole:
344 return Qt::AlignHCenter;
346 return repeatOrder(event);
352 case Qt::BackgroundRole:
354 KAEvent::Actions type =
event.actionTypes();
355 if (type & KAEvent::ACT_DISPLAY)
356 return event.bgColour();
357 if (type == KAEvent::ACT_COMMAND)
359 if (event.commandError() != KAEvent::CMD_NO_ERROR)
364 case Qt::ForegroundRole:
365 if (event.commandError() != KAEvent::CMD_NO_ERROR)
367 if (event.actionTypes() == KAEvent::ACT_COMMAND)
369 QColor colour = Qt::red;
371 event.bgColour().getRgb(&r, &g, &b);
372 if (r > 128 && g <= 128 && b <= 128)
377 case Qt::DisplayRole:
378 if (event.commandError() != KAEvent::CMD_NO_ERROR)
379 return QString::fromLatin1(
"!");
383 unsigned i = (
event.actionTypes() == KAEvent::ACT_DISPLAY)
384 ? event.bgColour().rgb() : 0;
385 return QString::fromLatin1(
"%1").arg(i, 6, 10, QLatin1Char(
'0'));
394 case Qt::BackgroundRole:
395 calendarColour =
true;
397 case Qt::DecorationRole:
400 v.setValue(*eventIcon(event));
403 case Qt::TextAlignmentRole:
404 return Qt::AlignHCenter;
405 case Qt::SizeHintRole:
407 case Qt::AccessibleTextRole:
409 #warning Implement this
413 return static_cast<int>(
event.actionSubType());
415 return QString::fromLatin1(
"%1").arg(event.actionSubType(), 2, 10, QLatin1Char(
'0'));
421 case Qt::BackgroundRole:
422 calendarColour =
true;
424 case Qt::DisplayRole:
426 return AlarmText::summary(event, 1);
427 case Qt::ToolTipRole:
428 return AlarmText::summary(event, 10);
436 case Qt::BackgroundRole:
437 calendarColour =
true;
439 case Qt::DisplayRole:
440 return event.templateName();
442 return event.templateName().toUpper();
451 case Qt::ForegroundRole:
452 if (!event.enabled())
457 case Qt::ToolTipRole:
459 switch (event.commandError())
461 case KAEvent::CMD_ERROR:
462 return i18nc(
"@info:tooltip",
"Command execution failed");
463 case KAEvent::CMD_ERROR_PRE:
464 return i18nc(
"@info:tooltip",
"Pre-alarm action execution failed");
465 case KAEvent::CMD_ERROR_POST:
466 return i18nc(
"@info:tooltip",
"Post-alarm action execution failed");
467 case KAEvent::CMD_ERROR_PRE_POST:
468 return i18nc(
"@info:tooltip",
"Pre- and post-alarm action execution failed");
470 case KAEvent::CMD_NO_ERROR:
475 return event.enabled();
482 Collection parent = item.parentCollection();
484 if (colour.isValid())
489 return EntityTreeModel::data(index, role);
498 if (!index.isValid())
501 Collection
collection = index.data(CollectionRole).value<Collection>();
502 if (collection.isValid())
505 bool updateCollection =
false;
506 CollectionAttribute* attr = 0;
509 case Qt::BackgroundRole:
511 QColor colour = value.value<QColor>();
512 attr = collection.attribute<CollectionAttribute>(Entity::AddIfMissing);
513 if (attr->backgroundColor() == colour)
515 attr->setBackgroundColor(colour);
516 updateCollection =
true;
521 CalEvent::Types
types =
static_cast<CalEvent::Types
>(value.value<
int>());
522 attr = collection.attribute<CollectionAttribute>(Entity::AddIfMissing);
523 if (attr->enabled() ==
types)
525 kDebug() <<
"Set enabled:" << types <<
", was=" << attr->enabled();
526 attr->setEnabled(types);
527 updateCollection =
true;
531 if (collection.hasAttribute<CollectionAttribute>()
534 CalEvent::Types
types =
static_cast<CalEvent::Types
>(value.value<
int>());
535 attr = collection.attribute<CollectionAttribute>(Entity::AddIfMissing);
536 kDebug()<<
"Set standard:"<<types<<
", was="<<attr->standard();
537 attr->setStandard(types);
538 updateCollection =
true;
543 bool keepFormat = value.value<
bool>();
544 attr = collection.attribute<CollectionAttribute>(Entity::AddIfMissing);
545 if (attr->keepFormat() == keepFormat)
547 attr->setKeepFormat(keepFormat);
548 updateCollection =
true;
554 if (updateCollection)
561 Collection c(collection.id());
562 CollectionAttribute* att = c.attribute<CollectionAttribute>(Entity::AddIfMissing);
564 CollectionModifyJob* job =
new CollectionModifyJob(c,
this);
565 connect(job, SIGNAL(result(
KJob*)),
this, SLOT(modifyCollectionJobDone(
KJob*)));
571 Item item = index.data(ItemRole).value<Item>();
574 bool updateItem =
false;
579 KAEvent::CmdErrType err =
static_cast<KAEvent::CmdErrType
>(value.toInt());
582 case KAEvent::CMD_NO_ERROR:
583 case KAEvent::CMD_ERROR:
584 case KAEvent::CMD_ERROR_PRE:
585 case KAEvent::CMD_ERROR_POST:
586 case KAEvent::CMD_ERROR_PRE_POST:
588 if (err == KAEvent::CMD_NO_ERROR && !item.hasAttribute<EventAttribute>())
590 EventAttribute* attr = item.attribute<EventAttribute>(Entity::AddIfMissing);
591 if (attr->commandError() == err)
593 attr->setCommandError(err);
595 kDebug()<<
"Item:"<<item.id()<<
" CommandErrorRole ->"<<err;
604 kDebug()<<
"Item: passing to EntityTreeModel::setData("<<role<<
")";
609 queueItemModifyJob(item);
615 return EntityTreeModel::setData(index, value, role);
625 case CollectionTreeHeaders:
627 case ItemListHeaders:
630 return EntityTreeModel::entityColumnCount(group);
639 if (orientation == Qt::Horizontal)
643 case CollectionTreeHeaders:
646 if (role == Qt::DisplayRole)
647 return i18nc(
"@title:column",
"Calendars");
650 case ItemListHeaders:
653 if (role == Qt::DisplayRole)
658 return i18nc(
"@title:column",
"Time");
660 return i18nc(
"@title:column",
"Time To");
662 return i18nc(
"@title:column",
"Repeat");
668 return i18nc(
"@title:column",
"Message, File or Command");
670 return i18nc(
"@title:column Template name",
"Name");
673 else if (role == Qt::WhatsThisRole)
674 return whatsThisText(section);
681 return EntityTreeModel::entityHeaderData(section, orientation, role, group);
688 void AkonadiModel::signalDataChanged(
bool (*checkFunc)(
const Item&),
int startColumn,
int endColumn,
const QModelIndex& parent)
692 for (
int row = 0, count = rowCount(parent); row < count; ++row)
694 const QModelIndex ix = index(row, 0, parent);
695 const Item item =
data(ix, ItemRole).value<Item>();
696 bool isItem = item.isValid();
699 if ((*checkFunc)(item))
710 emit dataChanged(index(start, startColumn, parent), index(end, endColumn, parent));
713 signalDataChanged(checkFunc, startColumn, endColumn, ix);
717 emit dataChanged(index(start, startColumn, parent), index(end, endColumn, parent));
724 {
return item.mimeType() == KAlarmCal::MIME_ACTIVE; }
726 void AkonadiModel::slotUpdateTimeTo()
736 {
return item.mimeType() == KAlarmCal::MIME_ARCHIVED; }
738 void AkonadiModel::slotUpdateArchivedColour(
const QColor&)
749 if (item.hasPayload<KAEvent>())
751 const KAEvent
event = item.payload<KAEvent>();
753 return !event.enabled();
758 void AkonadiModel::slotUpdateDisabledColour(
const QColor&)
769 if (item.hasPayload<KAEvent>())
771 const KAEvent
event = item.payload<KAEvent>();
772 if (event.isValid() &&
event.holidaysExcluded())
778 void AkonadiModel::slotUpdateHolidays()
790 if (item.hasPayload<KAEvent>())
792 const KAEvent
event = item.payload<KAEvent>();
793 if (event.isValid() &&
event.workTimeOnly())
799 void AkonadiModel::slotUpdateWorkingHours()
812 QModelIndex ix =
itemIndex(event.itemId());
824 if (mimeTypes.contains(KAlarmCal::MIME_ACTIVE))
825 colour = KColorScheme(QPalette::Active).foreground(KColorScheme::NormalText).color();
826 else if (mimeTypes.contains(KAlarmCal::MIME_ARCHIVED))
828 else if (mimeTypes.contains(KAlarmCal::MIME_TEMPLATE))
829 colour = KColorScheme(QPalette::Active).foreground(KColorScheme::LinkText).color();
830 if (colour.isValid() &&
isWritable(collection) <= 0)
831 return KColorUtils::lighten(colour, 0.2);
840 QModelIndex ix = modelIndexForCollection(
this, collection);
842 setData(ix, QVariant(colour), Qt::BackgroundRole);
851 if (!collection.isValid())
854 return backgroundColor_p(collection);
860 QColor AkonadiModel::backgroundColor_p(
const Akonadi::Collection& collection)
const
862 if (!collection.isValid() || !collection.hasAttribute<CollectionAttribute>())
873 if (!collection.isValid())
876 return collection.displayName();
884 KUrl url = collection.remoteId();
885 return !url.isLocalFile() ? i18nc(
"@info/plain",
"URL")
886 : QFileInfo(url.toLocalFile()).isDir() ? i18nc(
"@info/plain Directory in filesystem",
"Directory")
887 : i18nc(
"@info/plain",
"File");
896 QString name = QLatin1Char(
'@') + collection.displayName();
897 KUrl url = collection.remoteId();
898 QString type = QLatin1Char(
'@') +
storageType(collection);
899 QString locn = url.pathOrUrl();
900 bool inactive = !collection.hasAttribute<CollectionAttribute>()
901 || !(collection.attribute<CollectionAttribute>()->enabled() &
types);
902 QString disabled = i18nc(
"@info/plain",
"Disabled");
904 bool writable = readonly.isEmpty();
905 if (inactive && !writable)
906 return i18nc(
"@info:tooltip",
908 "<nl/>%2: <filename>%3</filename>"
910 name, type, locn, disabled, readonly);
911 if (inactive || !writable)
912 return i18nc(
"@info:tooltip",
914 "<nl/>%2: <filename>%3</filename>"
916 name, type, locn, (inactive ? disabled : readonly));
917 return i18nc(
"@info:tooltip",
919 "<nl/>%2: <filename>%3</filename>",
929 KACalendar::Compat compat;
935 return i18nc(
"@info/plain",
"Read-only (old format)");
937 if (compat == KACalendar::Current)
938 return i18nc(
"@info/plain",
"Read-only");
939 return i18nc(
"@info/plain",
"Read-only (other format)");
946 QString AkonadiModel::repeatText(
const KAEvent& event)
const
948 QString repeatText =
event.recurrenceText(
true);
949 if (repeatText.isEmpty())
950 repeatText = event.repetitionText(
true);
957 QString AkonadiModel::repeatOrder(
const KAEvent& event)
const
960 int repeatInterval = 0;
961 if (event.repeatAtLogin())
965 repeatInterval =
event.recurInterval();
966 switch (event.recurType())
968 case KARecurrence::MINUTELY:
971 case KARecurrence::DAILY:
974 case KARecurrence::WEEKLY:
977 case KARecurrence::MONTHLY_DAY:
978 case KARecurrence::MONTHLY_POS:
981 case KARecurrence::ANNUAL_DATE:
982 case KARecurrence::ANNUAL_POS:
985 case KARecurrence::NO_RECUR:
990 return QString::fromLatin1(
"%1%2").arg(static_cast<char>(
'0' + repeatOrder)).arg(repeatInterval, 8, 10, QLatin1Char(
'0'));
996 QPixmap* AkonadiModel::eventIcon(
const KAEvent& event)
const
998 switch (event.actionTypes())
1000 case KAEvent::ACT_EMAIL:
1002 case KAEvent::ACT_AUDIO:
1004 case KAEvent::ACT_COMMAND:
1005 return mCommandIcon;
1006 case KAEvent::ACT_DISPLAY:
1007 if (event.actionSubType() == KAEvent::FILE)
1010 case KAEvent::ACT_DISPLAY_COMMAND:
1019 QString AkonadiModel::whatsThisText(
int column)
const
1024 return i18nc(
"@info:whatsthis",
"Next scheduled date and time of the alarm");
1026 return i18nc(
"@info:whatsthis",
"How long until the next scheduled trigger of the alarm");
1028 return i18nc(
"@info:whatsthis",
"How often the alarm recurs");
1030 return i18nc(
"@info:whatsthis",
"Background color of alarm message");
1032 return i18nc(
"@info:whatsthis",
"Alarm type (message, file, command or email)");
1034 return i18nc(
"@info:whatsthis",
"Alarm message text, URL of text file to display, command to execute, or email subject line");
1036 return i18nc(
"@info:whatsthis",
"Name of the alarm template");
1047 if (!collection.isValid())
1049 kDebug() << collection.id();
1051 mCollectionsDeleting << collection.id();
1053 AgentManager* agentManager = AgentManager::self();
1054 const AgentInstance
instance = agentManager->
instance(collection.resource());
1055 if (instance.isValid())
1056 agentManager->removeInstance(instance);
1058 CollectionDeleteJob* job =
new CollectionDeleteJob(col);
1059 connect(job, SIGNAL(result(
KJob*)), SLOT(deleteCollectionJobDone(
KJob*)));
1060 mPendingCollectionJobs[job] = CollJobData(col.id(),
displayName(col));
1071 return mCollectionsDeleting.contains(
id);
1079 void AkonadiModel::deleteCollectionJobDone(
KJob* j)
1081 QMap<KJob*, CollJobData>::iterator it = mPendingCollectionJobs.find(j);
1082 CollJobData jobData;
1083 if (it != mPendingCollectionJobs.end())
1085 jobData = it.value();
1086 mPendingCollectionJobs.erase(it);
1091 QString errMsg = i18nc(
"@info",
"Failed to remove calendar <resource>%1</resource>.", jobData.displayName);
1092 kError() << errMsg <<
":" << j->errorString();
1105 if (!collection.isValid())
1107 kDebug() << collection.id();
1108 mMonitor->setCollectionMonitored(collection,
false);
1109 mMonitor->setCollectionMonitored(collection,
true);
1119 const Collection::List collections = mMonitor->collectionsMonitored();
1120 foreach (
const Collection& collection, collections)
1122 mMonitor->setCollectionMonitored(collection,
false);
1123 mMonitor->setCollectionMonitored(collection,
true);
1131 void AkonadiModel::modifyCollectionJobDone(
KJob* j)
1133 Collection collection =
static_cast<CollectionModifyJob*
>(j)->
collection();
1134 Collection::Id
id = collection.id();
1138 if (mCollectionsDeleted.contains(
id))
1139 mCollectionsDeleted.removeAll(
id);
1142 QString errMsg = i18nc(
"@info",
"Failed to update calendar <resource>%1</resource>.",
displayName(collection));
1143 kError() <<
"Id:" << collection.id() << errMsg <<
":" << j->errorString();
1163 KAEvent::List AkonadiModel::events(Akonadi::Collection& collection, CalEvent::Type type)
const
1166 QModelIndex ix = modelIndexForCollection(
this, collection);
1168 getChildEvents(ix, type, list);
1175 void AkonadiModel::getChildEvents(
const QModelIndex& parent, CalEvent::Type type, KAEvent::List& events)
const
1177 for (
int row = 0, count = rowCount(parent); row < count; ++row)
1179 const QModelIndex ix = index(row, 0, parent);
1180 const Item item =
data(ix, ItemRole).value<Item>();
1183 if (item.hasPayload<KAEvent>())
1185 KAEvent
event = item.payload<KAEvent>();
1186 if (event.isValid() &&
event.category() == type)
1192 Collection c = ix.data(CollectionRole).value<Collection>();
1194 getChildEvents(ix, type, events);
1205 return event(ix.data(ItemRole).value<Item>(), ix, 0);
1210 return event(index.data(ItemRole).value<Item>(), index, 0);
1213 KAEvent
AkonadiModel::event(
const Item& item,
const QModelIndex& index, Collection* collection)
const
1215 if (!item.isValid() || !item.hasPayload<KAEvent>())
1217 const QModelIndex ix = index.isValid() ? index :
itemIndex(item.id());
1220 KAEvent e = item.payload<KAEvent>();
1224 Collection c =
data(ix, ParentCollectionRole).value<Collection>();
1226 e.setCollectionId_const(c.id());
1239 kDebug() <<
event->id();
1243 Collection collection = destination(type, Collection::CanCreateItem, promptParent, noPrompt, &cancelled);
1244 if (!collection.isValid())
1249 kDebug() <<
"No collection";
1254 kDebug() <<
"Failed";
1274 for (
int i = 0, count = events.count(); i < count; ++i)
1275 ok = ok &&
addEvent(*events[i], collection);
1290 kDebug() <<
"ID:" <<
event.id();
1292 if (!event.setItemPayload(item, collection.contentMimeTypes()))
1294 kWarning() <<
"Invalid mime type for collection";
1297 event.setItemId(item.id());
1298 kDebug()<<
"-> item id="<<item.id();
1299 ItemCreateJob* job =
new ItemCreateJob(item, collection);
1300 connect(job, SIGNAL(result(
KJob*)), SLOT(itemJobDone(
KJob*)));
1301 mPendingItemJobs[job] = item.id();
1303 kDebug()<<
"...exiting";
1317 kDebug() <<
"ID:" <<
event.id();
1322 kDebug()<<
"item id="<<itemId;
1323 const QModelIndex ix =
itemIndex(itemId);
1326 const Collection collection = ix.data(ParentCollectionRole).value<Collection>();
1327 Item item = ix.data(ItemRole).value<Item>();
1328 kDebug()<<
"item id="<<item.id()<<
", revision="<<item.revision();
1329 if (!newEvent.setItemPayload(item, collection.contentMimeTypes()))
1331 kWarning() <<
"Invalid mime type for collection";
1335 queueItemModifyJob(item);
1352 if (mCollectionsDeleting.contains(ix.data(ParentCollectionRole).value<Collection>().
id()))
1354 kDebug() <<
"Collection being deleted";
1357 Item item = ix.data(ItemRole).value<Item>();
1358 ItemDeleteJob* job =
new ItemDeleteJob(item);
1359 connect(job, SIGNAL(result(
KJob*)), SLOT(itemJobDone(
KJob*)));
1360 mPendingItemJobs[job] = itemId;
1373 void AkonadiModel::queueItemModifyJob(
const Item& item)
1375 kDebug() << item.id();
1376 QMap<Item::Id, Item>::Iterator it = mItemModifyJobQueue.find(item.id());
1377 if (it != mItemModifyJobQueue.end())
1380 kDebug() <<
"Replacing previously queued job";
1386 if (mItemsBeingCreated.contains(item.id()))
1388 kDebug() <<
"Waiting for item initialisation";
1389 mItemModifyJobQueue[item.id()] = item;
1393 Item newItem = item;
1394 Item current =
itemById(item.id());
1395 if (current.isValid())
1396 newItem.setRevision(current.revision());
1397 mItemModifyJobQueue[item.id()] = Item();
1398 ItemModifyJob* job =
new ItemModifyJob(newItem);
1399 job->disableRevisionCheck();
1400 connect(job, SIGNAL(result(
KJob*)), SLOT(itemJobDone(
KJob*)));
1401 mPendingItemJobs[job] = item.id();
1402 kDebug() <<
"Executing Modify job for item" << item.id() <<
", revision=" << newItem.revision();
1415 void AkonadiModel::itemJobDone(
KJob* j)
1417 QMap<KJob*, Entity::Id>::iterator it = mPendingItemJobs.find(j);
1418 Entity::Id itemId = -1;
1419 if (it != mPendingItemJobs.end())
1421 itemId = it.value();
1422 mPendingItemJobs.erase(it);
1424 QByteArray jobClass = j->metaObject()->className();
1425 kDebug() << jobClass;
1429 if (jobClass ==
"Akonadi::ItemCreateJob")
1430 errMsg = i18nc(
"@info/plain",
"Failed to create alarm.");
1431 else if (jobClass ==
"Akonadi::ItemModifyJob")
1432 errMsg = i18nc(
"@info/plain",
"Failed to update alarm.");
1433 else if (jobClass ==
"Akonadi::ItemDeleteJob")
1434 errMsg = i18nc(
"@info/plain",
"Failed to delete alarm.");
1437 kError() << errMsg << itemId <<
":" << j->errorString();
1440 if (itemId >= 0 && jobClass ==
"Akonadi::ItemModifyJob")
1444 checkQueuedItemModifyJob(current);
1450 if (jobClass ==
"Akonadi::ItemCreateJob")
1455 kDebug() <<
"item id=" <<
static_cast<ItemCreateJob*
>(j)->item().id();
1456 mItemsBeingCreated << static_cast<ItemCreateJob*>(j)->item().id();
1483 void AkonadiModel::checkQueuedItemModifyJob(
const Item& item)
1485 if (mItemsBeingCreated.contains(item.id()))
1486 {kDebug()<<
"Still being created";
1489 QMap<Item::Id, Item>::iterator it = mItemModifyJobQueue.find(item.id());
1490 if (it == mItemModifyJobQueue.end())
1491 {kDebug()<<
"No jobs queued";
1494 Item qitem = it.value();
1495 if (!qitem.isValid())
1498 kDebug()<<
"No more jobs queued";
1499 mItemModifyJobQueue.erase(it);
1505 qitem.setRevision(item.revision());
1506 mItemModifyJobQueue[item.id()] = Item();
1507 ItemModifyJob* job =
new ItemModifyJob(qitem);
1508 job->disableRevisionCheck();
1509 connect(job, SIGNAL(result(
KJob*)), SLOT(itemJobDone(
KJob*)));
1510 mPendingItemJobs[job] = qitem.id();
1511 kDebug() <<
"Executing queued Modify job for item" << qitem.id() <<
", revision=" << qitem.revision();
1518 void AkonadiModel::slotRowsInserted(
const QModelIndex& parent,
int start,
int end)
1520 kDebug() << start <<
"-" << end <<
"(parent =" << parent <<
")";
1521 for (
int row = start; row <= end; ++row)
1523 const QModelIndex ix = index(row, 0, parent);
1524 const Collection collection = ix.data(CollectionRole).value<Collection>();
1525 if (collection.isValid())
1529 kDebug() <<
"Collection" << collection.id() << collection.name();
1530 if (AgentManager::self()->
instance(collection.resource()).isValid())
1532 QSet<QByteArray> attrs;
1533 attrs += CollectionAttribute::name();
1534 setCollectionChanged(collection, attrs,
true);
1537 if (!mCollectionsBeingCreated.contains(collection.remoteId())
1548 const Item item = ix.data(ItemRole).value<Item>();
1551 kDebug() <<
"item id=" << item.id() <<
", revision=" << item.revision();
1552 if (mItemsBeingCreated.removeAll(item.id()))
1553 checkQueuedItemModifyJob(item);
1557 EventList events = eventList(parent, start, end);
1558 if (!events.isEmpty())
1565 void AkonadiModel::slotRowsAboutToBeRemoved(
const QModelIndex& parent,
int start,
int end)
1567 kDebug() << start <<
"-" << end <<
"(parent =" << parent <<
")";
1568 EventList events = eventList(parent, start, end);
1569 if (!events.isEmpty())
1571 foreach (
const Event& event, events)
1572 kDebug() <<
"Collection:" <<
event.collection.id() <<
", Event ID:" <<
event.event.id();
1583 for (
int row = start; row <= end; ++row)
1586 QModelIndex ix = index(row, 0, parent);
1587 KAEvent evnt =
event(ix.data(ItemRole).value<Item>(), ix, &c);
1589 events += Event(evnt, c);
1598 void AkonadiModel::setCollectionChanged(
const Collection& collection,
const QSet<QByteArray>& attributeNames,
bool rowInserted)
1601 Collection::Rights oldRights = mCollectionRights.value(collection.id(), Collection::AllRights);
1602 Collection::Rights newRights = collection.rights() &
writableRights;
1603 if (newRights != oldRights)
1605 kDebug() <<
"Collection" << collection.id() <<
": rights ->" << newRights;
1606 mCollectionRights[collection.id()] = newRights;
1612 CalEvent::Types oldAlarmTypes = mCollectionAlarmTypes.value(collection.id(), CalEvent::EMPTY);
1613 CalEvent::Types newAlarmTypes = CalEvent::types(collection.contentMimeTypes());
1614 if (newAlarmTypes != oldAlarmTypes)
1616 kDebug() <<
"Collection" << collection.id() <<
": alarm types ->" << newAlarmTypes;
1617 mCollectionAlarmTypes[collection.id()] = newAlarmTypes;
1622 if (attributeNames.contains(CollectionAttribute::name()))
1624 static bool first =
true;
1625 CalEvent::Types oldEnabled = mCollectionEnabled.value(collection.id(), CalEvent::EMPTY);
1626 CalEvent::Types newEnabled = collection.hasAttribute<CollectionAttribute>() ? collection.attribute<CollectionAttribute>()->enabled() : CalEvent::EMPTY;
1627 if (first || newEnabled != oldEnabled)
1629 kDebug() <<
"Collection" << collection.id() <<
": enabled ->" << newEnabled;
1631 mCollectionEnabled[collection.id()] = newEnabled;
1637 if (attributeNames.contains(CompatibilityAttribute::name()))
1640 kDebug() <<
"CompatibilityAttribute";
1641 Collection col(collection);
1648 mCollectionIdsBeingCreated.removeAll(collection.id());
1649 if (mCollectionsBeingCreated.isEmpty() && mCollectionIdsBeingCreated.isEmpty()
1652 kDebug() <<
"Migration completed";
1662 void AkonadiModel::slotCollectionRemoved(
const Collection& collection)
1664 Collection::Id
id = collection.id();
1666 mCollectionRights.remove(
id);
1667 mCollectionsDeleting.removeAll(
id);
1668 while (mCollectionsDeleted.count() > 20)
1669 mCollectionsDeleted.removeFirst();
1670 mCollectionsDeleted << id;
1676 void AkonadiModel::slotCollectionBeingCreated(
const QString& path, Akonadi::Collection::Id
id,
bool finished)
1680 mCollectionsBeingCreated.removeAll(path);
1681 mCollectionIdsBeingCreated << id;
1684 mCollectionsBeingCreated << path;
1690 void AkonadiModel::slotMigrationCompleted()
1692 if (mCollectionsBeingCreated.isEmpty() && mCollectionIdsBeingCreated.isEmpty())
1694 kDebug() <<
"Migration completed";
1703 void AkonadiModel::slotMonitoredItemChanged(
const Akonadi::Item& item,
const QSet<QByteArray>&)
1705 kDebug() <<
"item id=" << item.id() <<
", revision=" << item.revision();
1706 mItemsBeingCreated.removeAll(item.id());
1707 checkQueuedItemModifyJob(item);
1709 KAEvent evnt =
event(item);
1710 if (!evnt.isValid())
1712 const QModelIndexList
indexes = modelIndexesForItem(
this, item);
1713 foreach (
const QModelIndex& index, indexes)
1715 if (index.isValid())
1719 Collection c =
data(index, ParentCollectionRole).value<Collection>();
1720 evnt.setCollectionId(c.id());
1721 mPendingEventChanges.enqueue(Event(evnt, c));
1722 QTimer::singleShot(0,
this, SLOT(slotEmitEventChanged()));
1732 void AkonadiModel::slotEmitEventChanged()
1734 while (!mPendingEventChanges.isEmpty())
1746 QModelIndex ix = modelIndexForCollection(
this, collection);
1749 collection = ix.data(CollectionRole).value<Collection>();
1759 const QModelIndexList ixs = modelIndexesForItem(
this, item);
1760 if (ixs.isEmpty() || !ixs[0].isValid())
1762 item = ixs[0].data(ItemRole).value<Item>();
1771 QModelIndex ix = modelIndexForCollection(
this, collection);
1773 return QModelIndex();
1782 QModelIndex ix = modelIndexForCollection(
this, Collection(
id));
1784 return Collection();
1785 return ix.data(CollectionRole).value<Collection>();
1793 const QModelIndexList ixs = modelIndexesForItem(
this, item);
1794 if (ixs.isEmpty() || !ixs[0].isValid())
1795 return QModelIndex();
1804 const QModelIndexList ixs = modelIndexesForItem(
this, Item(
id));
1805 if (ixs.isEmpty() || !ixs[0].isValid())
1807 return ixs[0].data(ItemRole).value<Item>();
1817 return Collection();
1818 return ix.data(ParentCollectionRole).value<Collection>();
1823 return collection.hasAttribute<CompatibilityAttribute>()
1824 && collection.attribute<CompatibilityAttribute>()->compatibility() == KACalendar::Current;
1832 KACalendar::Compat format;
1838 format = KACalendar::Incompatible;
1839 if (!collection.isValid())
1845 format = KACalendar::Current;
1848 if (!col.hasAttribute<CompatibilityAttribute>())
1850 format = col.attribute<CompatibilityAttribute>()->compatibility();
1853 case KACalendar::Current:
1855 case KACalendar::Converted:
1856 case KACalendar::Convertible:
1865 return CalEvent::types(collection.contentMimeTypes());
void eventsAdded(const AkonadiModel::EventList &)
Signal emitted when events have been added to the model.
void collectionStatusChanged(const Akonadi::Collection &, AkonadiModel::Change, const QVariant &newValue, bool inserted)
Signal emitted when a collection's enabled or read-only status has changed.
static QString readOnlyTooltip(const Akonadi::Collection &)
Return the read-only status tooltip for a collection.
static bool checkItem_isDisabled(const Item &item)
bool updateEvent(KAEvent &event)
bool isMigrationCompleted() const
Return whether calendar migration/creation at initialisation has completed.
static void updateToCurrentFormat(const Akonadi::Collection &, bool ignoreKeepFormat, QWidget *parent)
static void error(QWidget *parent, const QString &text, const QString &caption=QString(), Options options=Options(Notify|WindowModal))
QString displayName(Akonadi::Collection &) const
Return the display name for a collection.
bool refresh(Akonadi::Collection &) const
Refresh the specified collection instance with up to date data.
bool isCollectionBeingDeleted(Akonadi::Collection::Id) const
void collectionDeleted(Akonadi::Collection::Id, bool status=true)
Signal emitted when Akonadi has completed a collection deletion.
QModelIndex collectionIndex(Akonadi::Collection::Id id) const
KAEvent event(const Akonadi::Item &item) const
Return the alarm with the specified unique identifier.
Akonadi::Collection collectionById(Akonadi::Collection::Id) const
static bool checkItem_workTimeOnly(const Item &item)
QString storageType(const Akonadi::Collection &) const
Return the storage type (file/directory/URL etc.) for a collection.
void collectionAdded(const Akonadi::Collection &)
Signal emitted when a collection has been added to the model.
static AkonadiModel * instance()
virtual int entityColumnCount(HeaderGroup) const
static bool checkItem_isArchived(const Item &item)
static QColor disabledColour()
Get Disabled alarm color.
static QString timeToAlarmText(const KAlarmCal::DateTime &dateTime)
void eventsToBeRemoved(const AkonadiModel::EventList &)
Signal emitted when events are about to be removed from the model.
static CalendarMigrator * instance()
static void connect(QObject *receiver, const char *member)
void eventChanged(const AkonadiModel::Event &)
Signal emitted when an event in the model has changed.
QString tooltip(const Akonadi::Collection &, CalEvent::Types) const
Get the tooltip for a collection.
void collectionModified(Akonadi::Collection::Id, bool status=true)
Signal emitted when Akonadi has completed a collection modification.
static bool checkItem_excludesHolidays(const Item &item)
bool removeCollection(const Akonadi::Collection &)
Remove a collection from Akonadi.
bool reloadCollection(const Akonadi::Collection &)
Reload a collection's data from Akonadi storage (not from the backend).
static Collection::Rights writableRights
bool addEvents(const KAEvent::List &, Akonadi::Collection &)
virtual QVariant data(const QModelIndex &, int role=Qt::DisplayRole) const
virtual QVariant entityHeaderData(int section, Qt::Orientation, int role, HeaderGroup) const
static void connect(const char *signal, const QObject *receiver, const char *member)
static QColor archivedColour()
Get Archived alarm color.
QModelIndex eventIndex(const KAEvent &)
static CalEvent::Types types(const Akonadi::Collection &)
static QString alarmTimeText(const KAlarmCal::DateTime &dateTime)
static bool checkItem_isActive(const Item &item)
static bool isCompatible(const Akonadi::Collection &)
Check whether a collection is stored in the current KAlarm calendar format.
void itemDone(Akonadi::Item::Id, bool status=true)
Signal emitted when Akonadi has completed an item creation, update or deletion.
void reload()
Reload all collections' data from Akonadi storage (not from the backend).
Akonadi::Collection collection(const KAEvent &e) const
Akonadi::Item itemById(Akonadi::Item::Id) const
void setBackgroundColor(Akonadi::Collection &, const QColor &)
Set the background color for a collection and its alarms.
static QMap< Preferences::SoundType, int > indexes
void migrationCompleted()
Signal emitted when calendar migration/creation has completed.
bool deleteEvent(const KAEvent &event)
static QColor foregroundColor(const Akonadi::Collection &, const QStringList &mimeTypes)
Get the foreground color for a collection, based on specified mime types.
static MainWindow * mainMainWindow()
QColor backgroundColor(Akonadi::Collection &) const
Get the background color for a collection and its alarms.
Akonadi::Collection collectionForItem(Akonadi::Item::Id) const
bool addEvent(KAEvent &, Akonadi::Collection &)
static int isWritable(const Akonadi::Collection &)
Return whether a collection is fully writable, i.e.
virtual bool setData(const QModelIndex &, const QVariant &value, int role)
void updateCommandError(const KAEvent &)
To be called when the command error status of an alarm has changed, to set in the Akonadi database an...
QModelIndex itemIndex(Akonadi::Item::Id id) const