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 const 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>();
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
130 connect(monitor, SIGNAL(collectionRemoved(Akonadi::Collection)), SLOT(slotCollectionRemoved(Akonadi::Collection)));
131 initCalendarMigrator();
135 Preferences::connect(SIGNAL(holidaysChanged(KHolidays::HolidayRegion)),
this, SLOT(slotUpdateHolidays()));
139 connect(
this, SIGNAL(rowsAboutToBeRemoved(
QModelIndex,
int,
int)), SLOT(slotRowsAboutToBeRemoved(
QModelIndex,
int,
int)));
142 connect(ServerManager::self(), SIGNAL(stateChanged(Akonadi::ServerManager::State)),
143 SLOT(checkResources(Akonadi::ServerManager::State)));
144 checkResources(ServerManager::state());
149 if (mInstance ==
this)
162 void AkonadiModel::checkResources(ServerManager::State state)
166 case ServerManager::Running:
167 if (!mResourcesChecked)
169 kDebug() <<
"Server running";
170 mResourcesChecked =
true;
175 case ServerManager::NotRunning:
176 kDebug() <<
"Server stopped";
177 mResourcesChecked =
false;
179 mCollectionAlarmTypes.
clear();
180 mCollectionRights.
clear();
181 mCollectionEnabled.
clear();
182 initCalendarMigrator();
194 void AkonadiModel::initCalendarMigrator()
198 SLOT(slotCollectionBeingCreated(
QString,Akonadi::Collection::Id,
bool)));
207 return mResourcesChecked && !mMigrating;
218 case Qt::BackgroundRole:
219 case Qt::ForegroundRole:
220 case Qt::DisplayRole:
221 case Qt::TextAlignmentRole:
222 case Qt::DecorationRole:
223 case Qt::SizeHintRole:
224 case Qt::AccessibleTextRole:
225 case Qt::ToolTipRole:
226 case Qt::CheckStateRole:
240 return EntityTreeModel::data(index, role);
244 if (collection.isValid())
249 case Qt::DisplayRole:
250 return collection.displayName();
252 if (!collection.hasAttribute<CollectionAttribute>())
254 return static_cast<int>(collection.attribute<CollectionAttribute>()->enabled());
256 role = Qt::BackgroundRole;
258 case Qt::BackgroundRole:
260 const QColor colour = backgroundColor_p(collection);
265 case Qt::ForegroundRole:
267 case Qt::ToolTipRole:
268 return tooltip(collection, CalEvent::ACTIVE | CalEvent::ARCHIVED | CalEvent::TEMPLATE);
270 return static_cast<int>(
types(collection));
272 if (!collection.hasAttribute<CollectionAttribute>()
275 return static_cast<int>(collection.attribute<CollectionAttribute>()->standard());
277 if (!collection.hasAttribute<CollectionAttribute>())
279 return collection.attribute<CollectionAttribute>()->keepFormat();
286 const Item item = index.
data(ItemRole).
value<Item>();
290 const QString mime = item.mimeType();
291 if ((mime != KAlarmCal::MIME_ACTIVE && mime != KAlarmCal::MIME_ARCHIVED && mime != KAlarmCal::MIME_TEMPLATE)
292 || !item.hasPayload<KAEvent>())
298 if (mime == KAlarmCal::MIME_ACTIVE)
299 return CalEvent::ACTIVE;
300 if (mime == KAlarmCal::MIME_ARCHIVED)
301 return CalEvent::ARCHIVED;
302 if (mime == KAlarmCal::MIME_TEMPLATE)
303 return CalEvent::TEMPLATE;
306 if (!item.hasAttribute<EventAttribute>())
307 return KAEvent::CMD_NO_ERROR;
308 return item.attribute<EventAttribute>()->commandError();
312 const int column = index.
column();
313 if (role == Qt::WhatsThisRole)
314 return whatsThisText(column);
316 if (!event.isValid())
319 return event.actionTypes();
321 return event.actionSubType();
322 bool calendarColour =
false;
328 case Qt::BackgroundRole:
329 calendarColour =
true;
331 case Qt::DisplayRole:
339 due = event.startDateTime();
341 due =
event.nextTrigger(KAEvent::DISPLAY_TRIGGER);
342 return due.isValid() ? due.effectiveKDateTime().toUtc().dateTime()
352 case Qt::BackgroundRole:
353 calendarColour =
true;
355 case Qt::DisplayRole:
363 const DateTime due =
event.nextTrigger(KAEvent::DISPLAY_TRIGGER);
364 const KDateTime now = KDateTime::currentUtcDateTime();
365 if (due.isDateOnly())
366 return now.date().daysTo(due.date()) * 1440;
367 return (now.secsTo(due.effectiveKDateTime()) + 59) / 60;
374 case Qt::BackgroundRole:
375 calendarColour =
true;
377 case Qt::DisplayRole:
378 return repeatText(event);
379 case Qt::TextAlignmentRole:
380 return Qt::AlignHCenter;
382 return repeatOrder(event);
388 case Qt::BackgroundRole:
390 const KAEvent::Actions type =
event.actionTypes();
391 if (type & KAEvent::ACT_DISPLAY)
392 return event.bgColour();
393 if (type == KAEvent::ACT_COMMAND)
395 if (event.commandError() != KAEvent::CMD_NO_ERROR)
400 case Qt::ForegroundRole:
401 if (event.commandError() != KAEvent::CMD_NO_ERROR)
403 if (event.actionTypes() == KAEvent::ACT_COMMAND)
407 event.bgColour().
getRgb(&r, &g, &b);
408 if (r > 128 && g <= 128 && b <= 128)
413 case Qt::DisplayRole:
414 if (event.commandError() != KAEvent::CMD_NO_ERROR)
419 const unsigned i = (
event.actionTypes() == KAEvent::ACT_DISPLAY)
420 ? event.bgColour().rgb() : 0;
430 case Qt::BackgroundRole:
431 calendarColour =
true;
433 case Qt::DecorationRole:
439 case Qt::TextAlignmentRole:
440 return Qt::AlignHCenter;
441 case Qt::SizeHintRole:
443 case Qt::AccessibleTextRole:
445 #warning Implement accessibility
449 return static_cast<int>(
event.actionSubType());
457 case Qt::BackgroundRole:
458 calendarColour =
true;
460 case Qt::DisplayRole:
462 return AlarmText::summary(event, 1);
463 case Qt::ToolTipRole:
464 return AlarmText::summary(event, 10);
472 case Qt::BackgroundRole:
473 calendarColour =
true;
475 case Qt::DisplayRole:
476 return event.templateName();
478 return event.templateName().toUpper();
487 case Qt::ForegroundRole:
488 if (!event.enabled())
489 return Preferences::disabledColour();
491 return Preferences::archivedColour();
493 case Qt::ToolTipRole:
495 switch (event.commandError())
497 case KAEvent::CMD_ERROR:
498 return i18nc(
"@info:tooltip",
"Command execution failed");
499 case KAEvent::CMD_ERROR_PRE:
500 return i18nc(
"@info:tooltip",
"Pre-alarm action execution failed");
501 case KAEvent::CMD_ERROR_POST:
502 return i18nc(
"@info:tooltip",
"Post-alarm action execution failed");
503 case KAEvent::CMD_ERROR_PRE_POST:
504 return i18nc(
"@info:tooltip",
"Pre- and post-alarm action execution failed");
506 case KAEvent::CMD_NO_ERROR:
511 return event.enabled();
518 Collection parent = item.parentCollection();
525 return EntityTreeModel::data(index, role);
538 if (collection.isValid())
541 bool updateCollection =
false;
542 CollectionAttribute* attr = 0;
545 case Qt::BackgroundRole:
548 attr = collection.attribute<CollectionAttribute>(Entity::AddIfMissing);
549 if (attr->backgroundColor() == colour)
551 attr->setBackgroundColor(colour);
552 updateCollection =
true;
557 const CalEvent::Types
types =
static_cast<CalEvent::Types
>(value.
value<
int>());
558 attr = collection.attribute<CollectionAttribute>(Entity::AddIfMissing);
559 if (attr->enabled() ==
types)
561 kDebug() <<
"Set enabled:" << types <<
", was=" << attr->enabled();
562 attr->setEnabled(types);
563 updateCollection =
true;
567 if (collection.hasAttribute<CollectionAttribute>()
570 const CalEvent::Types
types =
static_cast<CalEvent::Types
>(value.
value<
int>());
571 attr = collection.attribute<CollectionAttribute>(Entity::AddIfMissing);
572 kDebug()<<
"Set standard:"<<types<<
", was="<<attr->standard();
573 attr->setStandard(types);
574 updateCollection =
true;
579 const bool keepFormat = value.
value<
bool>();
580 attr = collection.attribute<CollectionAttribute>(Entity::AddIfMissing);
581 if (attr->keepFormat() == keepFormat)
583 attr->setKeepFormat(keepFormat);
584 updateCollection =
true;
590 if (updateCollection)
597 Collection c(collection.id());
598 CollectionAttribute* att = c.attribute<CollectionAttribute>(Entity::AddIfMissing);
600 CollectionModifyJob* job =
new CollectionModifyJob(c,
this);
601 connect(job, SIGNAL(result(
KJob*)),
this, SLOT(modifyCollectionJobDone(
KJob*)));
607 Item item = index.
data(ItemRole).
value<Item>();
610 bool updateItem =
false;
615 const KAEvent::CmdErrType err =
static_cast<KAEvent::CmdErrType
>(value.
toInt());
618 case KAEvent::CMD_NO_ERROR:
619 case KAEvent::CMD_ERROR:
620 case KAEvent::CMD_ERROR_PRE:
621 case KAEvent::CMD_ERROR_POST:
622 case KAEvent::CMD_ERROR_PRE_POST:
624 if (err == KAEvent::CMD_NO_ERROR && !item.hasAttribute<EventAttribute>())
626 EventAttribute* attr = item.attribute<EventAttribute>(Entity::AddIfMissing);
627 if (attr->commandError() == err)
629 attr->setCommandError(err);
631 kDebug()<<
"Item:"<<item.id()<<
" CommandErrorRole ->"<<err;
640 kDebug()<<
"Item: passing to EntityTreeModel::setData("<<role<<
")";
645 queueItemModifyJob(item);
651 return EntityTreeModel::setData(index, value, role);
661 case CollectionTreeHeaders:
663 case ItemListHeaders:
666 return EntityTreeModel::entityColumnCount(group);
675 if (orientation == Qt::Horizontal)
679 case CollectionTreeHeaders:
682 if (role == Qt::DisplayRole)
683 return i18nc(
"@title:column",
"Calendars");
686 case ItemListHeaders:
689 if (role == Qt::DisplayRole)
694 return i18nc(
"@title:column",
"Time");
696 return i18nc(
"@title:column",
"Time To");
698 return i18nc(
"@title:column",
"Repeat");
704 return i18nc(
"@title:column",
"Message, File or Command");
706 return i18nc(
"@title:column Template name",
"Name");
709 else if (role == Qt::WhatsThisRole)
710 return whatsThisText(section);
717 return EntityTreeModel::entityHeaderData(section, orientation, role, group);
724 void AkonadiModel::signalDataChanged(
bool (*checkFunc)(
const Item&),
int startColumn,
int endColumn,
const QModelIndex& parent)
728 for (
int row = 0, count = rowCount(parent); row < count; ++row)
731 const Item item =
data(ix, ItemRole).
value<Item>();
732 const bool isItem = item.isValid();
735 if ((*checkFunc)(item))
746 emit dataChanged(index(start, startColumn, parent), index(end, endColumn, parent));
749 signalDataChanged(checkFunc, startColumn, endColumn, ix);
753 emit dataChanged(index(start, startColumn, parent), index(end, endColumn, parent));
760 {
return item.mimeType() == KAlarmCal::MIME_ACTIVE; }
762 void AkonadiModel::slotUpdateTimeTo()
772 {
return item.mimeType() == KAlarmCal::MIME_ARCHIVED; }
774 void AkonadiModel::slotUpdateArchivedColour(
const QColor&)
785 if (item.hasPayload<KAEvent>())
787 const KAEvent
event = item.payload<KAEvent>();
789 return !event.enabled();
794 void AkonadiModel::slotUpdateDisabledColour(
const QColor&)
805 if (item.hasPayload<KAEvent>())
807 const KAEvent
event = item.payload<KAEvent>();
808 if (event.isValid() &&
event.holidaysExcluded())
814 void AkonadiModel::slotUpdateHolidays()
826 if (item.hasPayload<KAEvent>())
828 const KAEvent
event = item.payload<KAEvent>();
829 if (event.isValid() &&
event.workTimeOnly())
835 void AkonadiModel::slotUpdateWorkingHours()
860 if (mimeTypes.
contains(KAlarmCal::MIME_ACTIVE))
861 colour = KColorScheme(QPalette::Active).foreground(KColorScheme::NormalText).color();
862 else if (mimeTypes.
contains(KAlarmCal::MIME_ARCHIVED))
863 colour = Preferences::archivedColour();
864 else if (mimeTypes.
contains(KAlarmCal::MIME_TEMPLATE))
865 colour = KColorScheme(QPalette::Active).foreground(KColorScheme::LinkText).color();
867 return KColorUtils::lighten(colour, 0.2);
876 const QModelIndex ix = modelIndexForCollection(
this, collection);
887 if (!collection.isValid())
890 return backgroundColor_p(collection);
896 QColor AkonadiModel::backgroundColor_p(
const Akonadi::Collection& collection)
const
898 if (!collection.isValid() || !collection.hasAttribute<CollectionAttribute>())
909 if (!collection.isValid())
912 return collection.displayName();
920 const KUrl url = collection.remoteId();
921 return !url.isLocalFile() ? i18nc(
"@info/plain",
"URL")
922 :
QFileInfo(url.toLocalFile()).isDir() ? i18nc(
"@info/plain Directory in filesystem",
"Directory")
923 : i18nc(
"@info/plain",
"File");
933 const KUrl url = collection.remoteId();
935 const QString locn = url.pathOrUrl();
936 const bool inactive = !collection.hasAttribute<CollectionAttribute>()
937 || !(collection.attribute<CollectionAttribute>()->enabled() &
types);
938 const QString disabled = i18nc(
"@info/plain",
"Disabled");
940 const bool writable = readonly.
isEmpty();
941 if (inactive && !writable)
942 return i18nc(
"@info:tooltip",
944 "<nl/>%2: <filename>%3</filename>"
946 name, type, locn, disabled, readonly);
947 if (inactive || !writable)
948 return i18nc(
"@info:tooltip",
950 "<nl/>%2: <filename>%3</filename>"
952 name, type, locn, (inactive ? disabled : readonly));
953 return i18nc(
"@info:tooltip",
955 "<nl/>%2: <filename>%3</filename>",
965 KACalendar::Compat compat;
971 return i18nc(
"@info/plain",
"Read-only (old format)");
973 if (compat == KACalendar::Current)
974 return i18nc(
"@info/plain",
"Read-only");
975 return i18nc(
"@info/plain",
"Read-only (other format)");
982 QString AkonadiModel::repeatText(
const KAEvent& event)
const
984 QString repeatText =
event.recurrenceText(
true);
986 repeatText = event.repetitionText(
true);
993 QString AkonadiModel::repeatOrder(
const KAEvent& event)
const
996 int repeatInterval = 0;
997 if (event.repeatAtLogin())
1001 repeatInterval =
event.recurInterval();
1002 switch (event.recurType())
1004 case KARecurrence::MINUTELY:
1007 case KARecurrence::DAILY:
1010 case KARecurrence::WEEKLY:
1013 case KARecurrence::MONTHLY_DAY:
1014 case KARecurrence::MONTHLY_POS:
1017 case KARecurrence::ANNUAL_DATE:
1018 case KARecurrence::ANNUAL_POS:
1021 case KARecurrence::NO_RECUR:
1032 QPixmap* AkonadiModel::eventIcon(
const KAEvent& event)
const
1034 switch (event.actionTypes())
1036 case KAEvent::ACT_EMAIL:
1038 case KAEvent::ACT_AUDIO:
1040 case KAEvent::ACT_COMMAND:
1041 return mCommandIcon;
1042 case KAEvent::ACT_DISPLAY:
1043 if (event.actionSubType() == KAEvent::FILE)
1046 case KAEvent::ACT_DISPLAY_COMMAND:
1055 QString AkonadiModel::whatsThisText(
int column)
const
1060 return i18nc(
"@info:whatsthis",
"Next scheduled date and time of the alarm");
1062 return i18nc(
"@info:whatsthis",
"How long until the next scheduled trigger of the alarm");
1064 return i18nc(
"@info:whatsthis",
"How often the alarm recurs");
1066 return i18nc(
"@info:whatsthis",
"Background color of alarm message");
1068 return i18nc(
"@info:whatsthis",
"Alarm type (message, file, command or email)");
1070 return i18nc(
"@info:whatsthis",
"Alarm message text, URL of text file to display, command to execute, or email subject line");
1072 return i18nc(
"@info:whatsthis",
"Name of the alarm template");
1083 if (!collection.isValid())
1085 kDebug() << collection.id();
1087 mCollectionsDeleting << collection.id();
1089 AgentManager* agentManager = AgentManager::self();
1090 const AgentInstance
instance = agentManager->
instance(collection.resource());
1091 if (instance.isValid())
1092 agentManager->removeInstance(instance);
1094 CollectionDeleteJob* job =
new CollectionDeleteJob(col);
1095 connect(job, SIGNAL(result(
KJob*)), SLOT(deleteCollectionJobDone(
KJob*)));
1096 mPendingCollectionJobs[job] = CollJobData(col.id(),
displayName(col));
1107 return mCollectionsDeleting.
contains(
id);
1115 void AkonadiModel::deleteCollectionJobDone(
KJob* j)
1118 CollJobData jobData;
1119 if (it != mPendingCollectionJobs.
end())
1121 jobData = it.
value();
1122 mPendingCollectionJobs.
erase(it);
1127 const QString errMsg = i18nc(
"@info",
"Failed to remove calendar <resource>%1</resource>.", jobData.displayName);
1128 kError() << errMsg <<
":" << j->errorString();
1141 if (!collection.isValid())
1143 kDebug() << collection.id();
1144 mMonitor->setCollectionMonitored(collection,
false);
1145 mMonitor->setCollectionMonitored(collection,
true);
1155 const Collection::List collections = mMonitor->collectionsMonitored();
1156 foreach (
const Collection& collection, collections)
1158 mMonitor->setCollectionMonitored(collection,
false);
1159 mMonitor->setCollectionMonitored(collection,
true);
1167 void AkonadiModel::modifyCollectionJobDone(
KJob* j)
1169 Collection collection =
static_cast<CollectionModifyJob*
>(j)->
collection();
1170 const Collection::Id
id = collection.id();
1174 if (mCollectionsDeleted.
contains(
id))
1178 const QString errMsg = i18nc(
"@info",
"Failed to update calendar <resource>%1</resource>.",
displayName(collection));
1179 kError() <<
"Id:" << collection.id() << errMsg <<
":" << j->errorString();
1201 Collection::Id colId =
event.collectionId();
1203 Qt::MatchFlags flags = (colId < 0) ? Qt::MatchExactly | Qt::MatchRecursive | Qt::MatchCaseSensitive | Qt::MatchWrap
1204 : Qt::MatchExactly | Qt::MatchRecursive | Qt::MatchCaseSensitive;
1205 const QModelIndexList
indexes = match(start, RemoteIdRole, event.id(), -1, flags);
1214 || ix.
data(ParentCollectionRole).
value<Collection>().
id() == colId)
1226 KAEvent::List AkonadiModel::events(Akonadi::Collection& collection, CalEvent::Type type)
const
1229 const QModelIndex ix = modelIndexForCollection(
this, collection);
1231 getChildEvents(ix, type, list);
1238 void AkonadiModel::getChildEvents(
const QModelIndex& parent, CalEvent::Type type, KAEvent::List& events)
const
1240 for (
int row = 0, count = rowCount(parent); row < count; ++row)
1243 const Item item =
data(ix, ItemRole).
value<Item>();
1246 if (item.hasPayload<KAEvent>())
1248 KAEvent
event = item.payload<KAEvent>();
1249 if (event.isValid() &&
event.category() == type)
1255 const Collection c = ix.
data(CollectionRole).
value<Collection>();
1257 getChildEvents(ix, type, events);
1278 if (!item.isValid() || !item.hasPayload<KAEvent>())
1283 KAEvent e = item.payload<KAEvent>();
1287 Collection c =
data(ix, ParentCollectionRole).
value<Collection>();
1289 e.setCollectionId_const(c.id());
1302 kDebug() <<
event->id();
1306 const Collection collection = destination(type, Collection::CanCreateItem, promptParent, noPrompt, &cancelled);
1307 if (!collection.isValid())
1312 kDebug() <<
"No collection";
1317 kDebug() <<
"Failed";
1337 for (
int i = 0, count = events.count(); i < count; ++i)
1338 ok = ok &&
addEvent(*events[i], collection);
1353 kDebug() <<
"ID:" <<
event.id();
1355 if (!event.setItemPayload(item, collection.contentMimeTypes()))
1357 kWarning() <<
"Invalid mime type for collection";
1360 event.setItemId(item.id());
1361 kDebug()<<
"-> item id="<<item.id();
1362 ItemCreateJob* job =
new ItemCreateJob(item, collection);
1363 connect(job, SIGNAL(result(
KJob*)), SLOT(itemJobDone(
KJob*)));
1364 mPendingItemJobs[job] = item.id();
1366 kDebug()<<
"...exiting";
1380 kDebug() <<
"ID:" <<
event.id();
1385 kDebug()<<
"item id="<<itemId;
1389 const Collection collection = ix.
data(ParentCollectionRole).
value<Collection>();
1390 Item item = ix.
data(ItemRole).
value<Item>();
1391 kDebug()<<
"item id="<<item.id()<<
", revision="<<item.revision();
1392 if (!newEvent.setItemPayload(item, collection.contentMimeTypes()))
1394 kWarning() <<
"Invalid mime type for collection";
1398 queueItemModifyJob(item);
1415 if (mCollectionsDeleting.
contains(ix.
data(ParentCollectionRole).
value<Collection>().
id()))
1417 kDebug() <<
"Collection being deleted";
1420 const Item item = ix.
data(ItemRole).
value<Item>();
1421 ItemDeleteJob* job =
new ItemDeleteJob(item);
1422 connect(job, SIGNAL(result(
KJob*)), SLOT(itemJobDone(
KJob*)));
1423 mPendingItemJobs[job] = itemId;
1436 void AkonadiModel::queueItemModifyJob(
const Item& item)
1438 kDebug() << item.id();
1440 if (it != mItemModifyJobQueue.
end())
1443 kDebug() <<
"Replacing previously queued job";
1449 if (mItemsBeingCreated.
contains(item.id()))
1451 kDebug() <<
"Waiting for item initialisation";
1452 mItemModifyJobQueue[item.id()] = item;
1456 Item newItem = item;
1457 const Item current =
itemById(item.id());
1458 if (current.isValid())
1459 newItem.setRevision(current.revision());
1460 mItemModifyJobQueue[item.id()] = Item();
1461 ItemModifyJob* job =
new ItemModifyJob(newItem);
1462 job->disableRevisionCheck();
1463 connect(job, SIGNAL(result(
KJob*)), SLOT(itemJobDone(
KJob*)));
1464 mPendingItemJobs[job] = item.id();
1465 kDebug() <<
"Executing Modify job for item" << item.id() <<
", revision=" << newItem.revision();
1478 void AkonadiModel::itemJobDone(
KJob* j)
1481 Entity::Id itemId = -1;
1482 if (it != mPendingItemJobs.
end())
1484 itemId = it.value();
1485 mPendingItemJobs.
erase(it);
1487 const QByteArray jobClass = j->metaObject()->className();
1488 kDebug() << jobClass;
1492 if (jobClass ==
"Akonadi::ItemCreateJob")
1493 errMsg = i18nc(
"@info/plain",
"Failed to create alarm.");
1494 else if (jobClass ==
"Akonadi::ItemModifyJob")
1495 errMsg = i18nc(
"@info/plain",
"Failed to update alarm.");
1496 else if (jobClass ==
"Akonadi::ItemDeleteJob")
1497 errMsg = i18nc(
"@info/plain",
"Failed to delete alarm.");
1500 kError() << errMsg << itemId <<
":" << j->errorString();
1503 if (itemId >= 0 && jobClass ==
"Akonadi::ItemModifyJob")
1506 const Item current =
itemById(itemId);
1507 checkQueuedItemModifyJob(current);
1513 if (jobClass ==
"Akonadi::ItemCreateJob")
1518 kDebug() <<
"item id=" <<
static_cast<ItemCreateJob*
>(j)->item().id();
1519 mItemsBeingCreated << static_cast<ItemCreateJob*>(j)->item().id();
1546 void AkonadiModel::checkQueuedItemModifyJob(
const Item& item)
1548 if (mItemsBeingCreated.
contains(item.id()))
1549 {kDebug()<<
"Still being created";
1553 if (it == mItemModifyJobQueue.
end())
1554 {kDebug()<<
"No jobs queued";
1557 Item qitem = it.value();
1558 if (!qitem.isValid())
1561 kDebug()<<
"No more jobs queued";
1562 mItemModifyJobQueue.
erase(it);
1568 qitem.setRevision(item.revision());
1569 mItemModifyJobQueue[item.id()] = Item();
1570 ItemModifyJob* job =
new ItemModifyJob(qitem);
1571 job->disableRevisionCheck();
1572 connect(job, SIGNAL(result(
KJob*)), SLOT(itemJobDone(
KJob*)));
1573 mPendingItemJobs[job] = qitem.id();
1574 kDebug() <<
"Executing queued Modify job for item" << qitem.id() <<
", revision=" << qitem.revision();
1581 void AkonadiModel::slotRowsInserted(
const QModelIndex& parent,
int start,
int end)
1583 kDebug() << start <<
"-" << end <<
"(parent =" << parent <<
")";
1584 for (
int row = start; row <= end; ++row)
1587 const Collection collection = ix.
data(CollectionRole).
value<Collection>();
1588 if (collection.isValid())
1592 kDebug() <<
"Collection" << collection.id() << collection.name();
1593 if (AgentManager::self()->
instance(collection.resource()).isValid())
1596 attrs += CollectionAttribute::name();
1597 setCollectionChanged(collection, attrs,
true);
1600 if (!mCollectionsBeingCreated.
contains(collection.remoteId())
1611 const Item item = ix.
data(ItemRole).
value<Item>();
1614 kDebug() <<
"item id=" << item.id() <<
", revision=" << item.revision();
1615 if (mItemsBeingCreated.
removeAll(item.id()))
1616 checkQueuedItemModifyJob(item);
1620 const EventList events = eventList(parent, start, end);
1621 if (!events.isEmpty())
1628 void AkonadiModel::slotRowsAboutToBeRemoved(
const QModelIndex& parent,
int start,
int end)
1630 kDebug() << start <<
"-" << end <<
"(parent =" << parent <<
")";
1631 const EventList events = eventList(parent, start, end);
1632 if (!events.isEmpty())
1634 foreach (
const Event& event, events)
1635 kDebug() <<
"Collection:" <<
event.collection.id() <<
", Event ID:" <<
event.event.id();
1646 for (
int row = start; row <= end; ++row)
1650 const KAEvent evnt =
event(ix.
data(ItemRole).
value<Item>(), ix, &c);
1652 events += Event(evnt, c);
1661 void AkonadiModel::setCollectionChanged(
const Collection& collection,
const QSet<QByteArray>& attributeNames,
bool rowInserted)
1664 const Collection::Rights oldRights = mCollectionRights.
value(collection.id(), Collection::AllRights);
1665 const Collection::Rights newRights = collection.rights() &
writableRights;
1666 if (newRights != oldRights)
1668 kDebug() <<
"Collection" << collection.id() <<
": rights ->" << newRights;
1669 mCollectionRights[collection.id()] = newRights;
1675 const CalEvent::Types oldAlarmTypes = mCollectionAlarmTypes.
value(collection.id(), CalEvent::EMPTY);
1676 const CalEvent::Types newAlarmTypes = CalEvent::types(collection.contentMimeTypes());
1677 if (newAlarmTypes != oldAlarmTypes)
1679 kDebug() <<
"Collection" << collection.id() <<
": alarm types ->" << newAlarmTypes;
1680 mCollectionAlarmTypes[collection.id()] = newAlarmTypes;
1685 if (attributeNames.
contains(CollectionAttribute::name()))
1687 static bool firstEnabled =
true;
1688 const CalEvent::Types oldEnabled = mCollectionEnabled.
value(collection.id(), CalEvent::EMPTY);
1689 const CalEvent::Types newEnabled = collection.hasAttribute<CollectionAttribute>() ? collection.attribute<CollectionAttribute>()->enabled() : CalEvent::EMPTY;
1690 if (firstEnabled || newEnabled != oldEnabled)
1692 kDebug() <<
"Collection" << collection.id() <<
": enabled ->" << newEnabled;
1693 firstEnabled =
false;
1694 mCollectionEnabled[collection.id()] = newEnabled;
1700 if (attributeNames.
contains(CompatibilityAttribute::name()))
1703 kDebug() <<
"CompatibilityAttribute";
1704 Collection col(collection);
1711 mCollectionIdsBeingCreated.
removeAll(collection.id());
1712 if (mCollectionsBeingCreated.
isEmpty() && mCollectionIdsBeingCreated.
isEmpty()
1715 kDebug() <<
"Migration completed";
1725 void AkonadiModel::slotCollectionRemoved(
const Collection& collection)
1727 const Collection::Id
id = collection.id();
1729 mCollectionRights.
remove(
id);
1731 while (mCollectionsDeleted.
count() > 20)
1733 mCollectionsDeleted << id;
1739 void AkonadiModel::slotCollectionBeingCreated(
const QString& path, Akonadi::Collection::Id
id,
bool finished)
1743 mCollectionsBeingCreated.
removeAll(path);
1744 mCollectionIdsBeingCreated << id;
1747 mCollectionsBeingCreated << path;
1753 void AkonadiModel::slotMigrationCompleted()
1755 if (mCollectionsBeingCreated.
isEmpty() && mCollectionIdsBeingCreated.
isEmpty())
1757 kDebug() <<
"Migration completed";
1766 void AkonadiModel::slotMonitoredItemChanged(
const Akonadi::Item& item,
const QSet<QByteArray>&)
1768 kDebug() <<
"item id=" << item.id() <<
", revision=" << item.revision();
1769 mItemsBeingCreated.
removeAll(item.id());
1770 checkQueuedItemModifyJob(item);
1772 KAEvent evnt =
event(item);
1773 if (!evnt.isValid())
1775 const QModelIndexList
indexes = modelIndexesForItem(
this, item);
1782 Collection c =
data(index, ParentCollectionRole).
value<Collection>();
1783 evnt.setCollectionId(c.id());
1784 mPendingEventChanges.enqueue(Event(evnt, c));
1795 void AkonadiModel::slotEmitEventChanged()
1797 while (!mPendingEventChanges.isEmpty())
1809 const QModelIndex ix = modelIndexForCollection(
this, collection);
1812 collection = ix.
data(CollectionRole).
value<Collection>();
1822 const QModelIndexList ixs = modelIndexesForItem(
this, item);
1823 if (ixs.isEmpty() || !ixs[0].isValid())
1825 item = ixs[0].data(ItemRole).value<Item>();
1834 const QModelIndex ix = modelIndexForCollection(
this, collection);
1845 const QModelIndex ix = modelIndexForCollection(
this, Collection(
id));
1847 return Collection();
1848 return ix.
data(CollectionRole).
value<Collection>();
1856 const QModelIndexList ixs = modelIndexesForItem(
this, item);
1857 if (ixs.isEmpty() || !ixs[0].isValid())
1867 const QModelIndexList ixs = modelIndexesForItem(
this, Item(
id));
1868 if (ixs.isEmpty() || !ixs[0].isValid())
1870 return ixs[0].data(ItemRole).value<Item>();
1880 return Collection();
1881 return ix.
data(ParentCollectionRole).
value<Collection>();
1886 return collection.hasAttribute<CompatibilityAttribute>()
1887 && collection.attribute<CompatibilityAttribute>()->compatibility() == KACalendar::Current;
1895 KACalendar::Compat format;
1901 format = KACalendar::Incompatible;
1902 if (!collection.isValid())
1908 format = KACalendar::Current;
1911 if (!col.hasAttribute<CompatibilityAttribute>())
1913 format = col.attribute<CompatibilityAttribute>()->compatibility();
1916 case KACalendar::Current:
1918 case KACalendar::Converted:
1919 case KACalendar::Convertible:
1928 return CalEvent::types(collection.contentMimeTypes());
qlonglong toLongLong(bool *ok) const
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.
iterator erase(iterator pos)
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))
bool contains(const QString &str, Qt::CaseSensitivity cs) const
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.
static const Collection::Rights writableRights
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.
int count(const T &value) const
int toInt(bool *ok) const
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)
int removeAll(const T &value)
static QString timeToAlarmText(const KAlarmCal::DateTime &dateTime)
void eventsToBeRemoved(const AkonadiModel::EventList &)
Signal emitted when events are about to be removed from the model.
void serverStopped()
Signal emitted when the Akonadi server has stopped.
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).
bool addEvents(const KAEvent::List &, Akonadi::Collection &)
Akonadi::Item::Id findItemId(const KAEvent &)
Search for an event's item ID.
virtual QVariant data(const QModelIndex &, int role=Qt::DisplayRole) const
virtual QVariant entityHeaderData(int section, Qt::Orientation, int role, HeaderGroup) const
void setValue(const T &value)
static void connect(const char *signal, const QObject *receiver, const char *member)
bool contains(const T &value) const
bool contains(const T &value) const
QModelIndex eventIndex(const KAEvent &)
Return an event's model index, based on its itemId() value.
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.
QVariant data(int role) const
QSize expandedTo(const QSize &otherSize) const
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 getRgb(int *r, int *g, int *b, int *a) const
void setBackgroundColor(Akonadi::Collection &, const QColor &)
Set the background color for a collection and its alarms.
QString fromLatin1(const char *str, int size)
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
QString arg(qlonglong a, int fieldWidth, int base, const QChar &fillChar) const
iterator find(const Key &key)
bool addEvent(KAEvent &, Akonadi::Collection &)
static int isWritable(const Akonadi::Collection &)
Return whether a collection is fully writable, i.e.
const T value(const Key &key) const
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...
int remove(const Key &key)
QModelIndex itemIndex(Akonadi::Item::Id id) const