• Skip to content
  • Skip to link menu
KDE API Reference
  • KDE API Reference
  • kdepim API Reference
  • KDE Home
  • Contact Us
 

kalarm

  • sources
  • kde-4.12
  • kdepim
  • kalarm
akonadimodel.cpp
Go to the documentation of this file.
1 /*
2  * akonadimodel.cpp - KAlarm calendar file access using Akonadi
3  * Program: kalarm
4  * Copyright © 2007-2012 by David Jarvie <djarvie@kde.org>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License along
17  * with this program; if not, write to the Free Software Foundation, Inc.,
18  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19  */
20 
21 #include "akonadimodel.h"
22 #include "alarmtime.h"
23 #include "autoqpointer.h"
24 #include "calendarmigrator.h"
25 #include "mainwindow.h"
26 #include "messagebox.h"
27 #include "preferences.h"
28 #include "synchtimer.h"
29 #include "kalarmsettings.h"
30 #include "kalarmdirsettings.h"
31 
32 #include <kalarmcal/alarmtext.h>
33 #include <kalarmcal/collectionattribute.h>
34 #include <kalarmcal/compatibilityattribute.h>
35 #include <kalarmcal/eventattribute.h>
36 
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>
52 
53 #include <klocale.h>
54 #include <kcolorutils.h>
55 
56 #include <QApplication>
57 #include <QFileInfo>
58 #include <QTimer>
59 
60 using namespace Akonadi;
61 using namespace KAlarmCal;
62 
63 static Collection::Rights writableRights = Collection::CanChangeItem | Collection::CanCreateItem | Collection::CanDeleteItem;
64 
65 //static bool checkItem_true(const Item&) { return true; }
66 
67 /*=============================================================================
68 = Class: AkonadiModel
69 =============================================================================*/
70 
71 AkonadiModel* AkonadiModel::mInstance = 0;
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;
79 
80 /******************************************************************************
81 * Construct and return the singleton.
82 */
83 AkonadiModel* AkonadiModel::instance()
84 {
85  if (!mInstance)
86  mInstance = new AkonadiModel(new ChangeRecorder(qApp), qApp);
87  return mInstance;
88 }
89 
90 /******************************************************************************
91 * Constructor.
92 */
93 AkonadiModel::AkonadiModel(ChangeRecorder* monitor, QObject* parent)
94  : EntityTreeModel(monitor, parent),
95  mMonitor(monitor),
96  mResourcesChecked(false),
97  mMigrating(false)
98 {
99  // Set lazy population to enable the contents of unselected collections to be ignored
100  setItemPopulationStrategy(LazyPopulation);
101 
102  // Restrict monitoring to collections containing the KAlarm mime types
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>();
111 
112  AttributeFactory::registerAttribute<CollectionAttribute>();
113  AttributeFactory::registerAttribute<CompatibilityAttribute>();
114  AttributeFactory::registerAttribute<EventAttribute>();
115 
116  if (!mTextIcon)
117  {
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());
124  }
125 
126 #ifdef __GNUC__
127 #warning Only want to monitor collection properties, not content, when this becomes possible
128 #endif
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)));
131  connect(CalendarMigrator::instance(), SIGNAL(creating(QString,Akonadi::Collection::Id,bool)),
132  SLOT(slotCollectionBeingCreated(QString,Akonadi::Collection::Id,bool)));
133  connect(CalendarMigrator::instance(), SIGNAL(destroyed(QObject*)), SLOT(slotMigrationCompleted()));
134  MinuteTimer::connect(this, SLOT(slotUpdateTimeTo()));
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()));
139 
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>)));
143 
144  connect(ServerManager::self(), SIGNAL(stateChanged(Akonadi::ServerManager::State)),
145  SLOT(checkResources(Akonadi::ServerManager::State)));
146  checkResources(ServerManager::state());
147 }
148 
149 /******************************************************************************
150 * Called when the server manager is running, i.e. the agent manager knows about
151 * all existing resources.
152 * Once it is running, if necessary migrate any KResources alarm calendars from
153 * pre-Akonadi versions of KAlarm, or create default Akonadi calendar resources
154 * if any are missing.
155 */
156 void AkonadiModel::checkResources(ServerManager::State state)
157 {
158  if (!mResourcesChecked && state == ServerManager::Running)
159  {
160  mResourcesChecked = true;
161  mMigrating = true;
162  CalendarMigrator::execute();
163  }
164 }
165 
166 /******************************************************************************
167 * Return whether calendar migration has completed.
168 */
169 bool AkonadiModel::isMigrationCompleted() const
170 {
171  return mResourcesChecked && !mMigrating;
172 }
173 
174 /******************************************************************************
175 * Return the data for a given role, for a specified item.
176 */
177 QVariant AkonadiModel::data(const QModelIndex& index, int role) const
178 {
179  // First check that it's a role we're interested in - if not, use the base method
180  switch (role)
181  {
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:
191  case SortRole:
192  case ValueRole:
193  case StatusRole:
194  case AlarmActionsRole:
195  case AlarmSubActionRole:
196  case EnabledRole:
197  case EnabledTypesRole:
198  case CommandErrorRole:
199  case BaseColourRole:
200  case AlarmTypeRole:
201  case IsStandardRole:
202  break;
203  default:
204  return EntityTreeModel::data(index, role);
205  }
206 
207  const Collection collection = index.data(CollectionRole).value<Collection>();
208  if (collection.isValid())
209  {
210  // This is a Collection row
211  switch (role)
212  {
213  case Qt::DisplayRole:
214  return collection.displayName();
215  case EnabledTypesRole:
216  if (!collection.hasAttribute<CollectionAttribute>())
217  return 0;
218  return static_cast<int>(collection.attribute<CollectionAttribute>()->enabled());
219  case BaseColourRole:
220  role = Qt::BackgroundRole;
221  break;
222  case Qt::BackgroundRole:
223  {
224  QColor colour = backgroundColor_p(collection);
225  if (colour.isValid())
226  return colour;
227  break;
228  }
229  case Qt::ForegroundRole:
230  return foregroundColor(collection, collection.contentMimeTypes());
231  case Qt::ToolTipRole:
232  return tooltip(collection, CalEvent::ACTIVE | CalEvent::ARCHIVED | CalEvent::TEMPLATE);
233  case AlarmTypeRole:
234  return static_cast<int>(types(collection));
235  case IsStandardRole:
236  if (!collection.hasAttribute<CollectionAttribute>()
237  || !isCompatible(collection))
238  return 0;
239  return static_cast<int>(collection.attribute<CollectionAttribute>()->standard());
240  case KeepFormatRole:
241  if (!collection.hasAttribute<CollectionAttribute>())
242  return false;
243  return collection.attribute<CollectionAttribute>()->keepFormat();
244  default:
245  break;
246  }
247  }
248  else
249  {
250  const Item item = index.data(ItemRole).value<Item>();
251  if (item.isValid())
252  {
253  // This is an Item row
254  QString mime = item.mimeType();
255  if ((mime != KAlarmCal::MIME_ACTIVE && mime != KAlarmCal::MIME_ARCHIVED && mime != KAlarmCal::MIME_TEMPLATE)
256  || !item.hasPayload<KAEvent>())
257  return QVariant();
258  switch (role)
259  {
260  case StatusRole:
261  // Mime type has a one-to-one relationship to event's category()
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;
268  return QVariant();
269  case CommandErrorRole:
270  if (!item.hasAttribute<EventAttribute>())
271  return KAEvent::CMD_NO_ERROR;
272  return item.attribute<EventAttribute>()->commandError();
273  default:
274  break;
275  }
276  int column = index.column();
277  if (role == Qt::WhatsThisRole)
278  return whatsThisText(column);
279  const KAEvent event(this->event(item));
280  if (!event.isValid())
281  return QVariant();
282  if (role == AlarmActionsRole)
283  return event.actionTypes();
284  if (role == AlarmSubActionRole)
285  return event.actionSubType();
286  bool calendarColour = false;
287  switch (column)
288  {
289  case TimeColumn:
290  switch (role)
291  {
292  case Qt::BackgroundRole:
293  calendarColour = true;
294  break;
295  case Qt::DisplayRole:
296  if (event.expired())
297  return AlarmTime::alarmTimeText(event.startDateTime());
298  return AlarmTime::alarmTimeText(event.nextTrigger(KAEvent::DISPLAY_TRIGGER));
299  case SortRole:
300  {
301  DateTime due;
302  if (event.expired())
303  due = event.startDateTime();
304  else
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));
308  }
309  default:
310  break;
311  }
312  break;
313  case TimeToColumn:
314  switch (role)
315  {
316  case Qt::BackgroundRole:
317  calendarColour = true;
318  break;
319  case Qt::DisplayRole:
320  if (event.expired())
321  return QString();
322  return AlarmTime::timeToAlarmText(event.nextTrigger(KAEvent::DISPLAY_TRIGGER));
323  case SortRole:
324  {
325  if (event.expired())
326  return -1;
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;
332  }
333  }
334  break;
335  case RepeatColumn:
336  switch (role)
337  {
338  case Qt::BackgroundRole:
339  calendarColour = true;
340  break;
341  case Qt::DisplayRole:
342  return repeatText(event);
343  case Qt::TextAlignmentRole:
344  return Qt::AlignHCenter;
345  case SortRole:
346  return repeatOrder(event);
347  }
348  break;
349  case ColourColumn:
350  switch (role)
351  {
352  case Qt::BackgroundRole:
353  {
354  KAEvent::Actions type = event.actionTypes();
355  if (type & KAEvent::ACT_DISPLAY)
356  return event.bgColour();
357  if (type == KAEvent::ACT_COMMAND)
358  {
359  if (event.commandError() != KAEvent::CMD_NO_ERROR)
360  return Qt::red;
361  }
362  break;
363  }
364  case Qt::ForegroundRole:
365  if (event.commandError() != KAEvent::CMD_NO_ERROR)
366  {
367  if (event.actionTypes() == KAEvent::ACT_COMMAND)
368  return Qt::white;
369  QColor colour = Qt::red;
370  int r, g, b;
371  event.bgColour().getRgb(&r, &g, &b);
372  if (r > 128 && g <= 128 && b <= 128)
373  colour = Qt::white;
374  return colour;
375  }
376  break;
377  case Qt::DisplayRole:
378  if (event.commandError() != KAEvent::CMD_NO_ERROR)
379  return QString::fromLatin1("!");
380  break;
381  case SortRole:
382  {
383  unsigned i = (event.actionTypes() == KAEvent::ACT_DISPLAY)
384  ? event.bgColour().rgb() : 0;
385  return QString::fromLatin1("%1").arg(i, 6, 10, QLatin1Char('0'));
386  }
387  default:
388  break;
389  }
390  break;
391  case TypeColumn:
392  switch (role)
393  {
394  case Qt::BackgroundRole:
395  calendarColour = true;
396  break;
397  case Qt::DecorationRole:
398  {
399  QVariant v;
400  v.setValue(*eventIcon(event));
401  return v;
402  }
403  case Qt::TextAlignmentRole:
404  return Qt::AlignHCenter;
405  case Qt::SizeHintRole:
406  return mIconSize;
407  case Qt::AccessibleTextRole:
408 #ifdef __GNUC__
409 #warning Implement this
410 #endif
411  return QString();
412  case ValueRole:
413  return static_cast<int>(event.actionSubType());
414  case SortRole:
415  return QString::fromLatin1("%1").arg(event.actionSubType(), 2, 10, QLatin1Char('0'));
416  }
417  break;
418  case TextColumn:
419  switch (role)
420  {
421  case Qt::BackgroundRole:
422  calendarColour = true;
423  break;
424  case Qt::DisplayRole:
425  case SortRole:
426  return AlarmText::summary(event, 1);
427  case Qt::ToolTipRole:
428  return AlarmText::summary(event, 10);
429  default:
430  break;
431  }
432  break;
433  case TemplateNameColumn:
434  switch (role)
435  {
436  case Qt::BackgroundRole:
437  calendarColour = true;
438  break;
439  case Qt::DisplayRole:
440  return event.templateName();
441  case SortRole:
442  return event.templateName().toUpper();
443  }
444  break;
445  default:
446  break;
447  }
448 
449  switch (role)
450  {
451  case Qt::ForegroundRole:
452  if (!event.enabled())
453  return Preferences::disabledColour();
454  if (event.expired())
455  return Preferences::archivedColour();
456  break; // use the default for normal active alarms
457  case Qt::ToolTipRole:
458  // Show the last command execution error message
459  switch (event.commandError())
460  {
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");
469  default:
470  case KAEvent::CMD_NO_ERROR:
471  break;
472  }
473  break;
474  case EnabledRole:
475  return event.enabled();
476  default:
477  break;
478  }
479 
480  if (calendarColour)
481  {
482  Collection parent = item.parentCollection();
483  QColor colour = backgroundColor(parent);
484  if (colour.isValid())
485  return colour;
486  }
487  }
488  }
489  return EntityTreeModel::data(index, role);
490 }
491 
492 /******************************************************************************
493 * Set the font to use for all items, or the checked state of one item.
494 * The font must always be set at initialisation.
495 */
496 bool AkonadiModel::setData(const QModelIndex& index, const QVariant& value, int role)
497 {
498  if (!index.isValid())
499  return false;
500  // NOTE: need to emit dataChanged() whenever something is updated (except via a job).
501  Collection collection = index.data(CollectionRole).value<Collection>();
502  if (collection.isValid())
503  {
504  // This is a Collection row
505  bool updateCollection = false;
506  CollectionAttribute* attr = 0;
507  switch (role)
508  {
509  case Qt::BackgroundRole:
510  {
511  QColor colour = value.value<QColor>();
512  attr = collection.attribute<CollectionAttribute>(Entity::AddIfMissing);
513  if (attr->backgroundColor() == colour)
514  return true; // no change
515  attr->setBackgroundColor(colour);
516  updateCollection = true;
517  break;
518  }
519  case EnabledTypesRole:
520  {
521  CalEvent::Types types = static_cast<CalEvent::Types>(value.value<int>());
522  attr = collection.attribute<CollectionAttribute>(Entity::AddIfMissing);
523  if (attr->enabled() == types)
524  return true; // no change
525  kDebug() << "Set enabled:" << types << ", was=" << attr->enabled();
526  attr->setEnabled(types);
527  updateCollection = true;
528  break;
529  }
530  case IsStandardRole:
531  if (collection.hasAttribute<CollectionAttribute>()
532  && isCompatible(collection))
533  {
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;
539  }
540  break;
541  case KeepFormatRole:
542  {
543  bool keepFormat = value.value<bool>();
544  attr = collection.attribute<CollectionAttribute>(Entity::AddIfMissing);
545  if (attr->keepFormat() == keepFormat)
546  return true; // no change
547  attr->setKeepFormat(keepFormat);
548  updateCollection = true;
549  break;
550  }
551  default:
552  break;
553  }
554  if (updateCollection)
555  {
556  // Update the CollectionAttribute value.
557  // Note that we can't supply 'collection' to CollectionModifyJob since
558  // that also contains the CompatibilityAttribute value, which is read-only
559  // for applications. So create a new Collection instance and only set a
560  // value for CollectionAttribute.
561  Collection c(collection.id());
562  CollectionAttribute* att = c.attribute<CollectionAttribute>(Entity::AddIfMissing);
563  *att = *attr;
564  CollectionModifyJob* job = new CollectionModifyJob(c, this);
565  connect(job, SIGNAL(result(KJob*)), this, SLOT(modifyCollectionJobDone(KJob*)));
566  return true;
567  }
568  }
569  else
570  {
571  Item item = index.data(ItemRole).value<Item>();
572  if (item.isValid())
573  {
574  bool updateItem = false;
575  switch (role)
576  {
577  case CommandErrorRole:
578  {
579  KAEvent::CmdErrType err = static_cast<KAEvent::CmdErrType>(value.toInt());
580  switch (err)
581  {
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:
587  {
588  if (err == KAEvent::CMD_NO_ERROR && !item.hasAttribute<EventAttribute>())
589  return true; // no change
590  EventAttribute* attr = item.attribute<EventAttribute>(Entity::AddIfMissing);
591  if (attr->commandError() == err)
592  return true; // no change
593  attr->setCommandError(err);
594  updateItem = true;
595 kDebug()<<"Item:"<<item.id()<<" CommandErrorRole ->"<<err;
596  break;
597  }
598  default:
599  return false;
600  }
601  break;
602  }
603  default:
604 kDebug()<<"Item: passing to EntityTreeModel::setData("<<role<<")";
605  break;
606  }
607  if (updateItem)
608  {
609  queueItemModifyJob(item);
610  return true;
611  }
612  }
613  }
614 
615  return EntityTreeModel::setData(index, value, role);
616 }
617 
618 /******************************************************************************
619 * Return the number of columns for either a collection or an item.
620 */
621 int AkonadiModel::entityColumnCount(HeaderGroup group) const
622 {
623  switch (group)
624  {
625  case CollectionTreeHeaders:
626  return 1;
627  case ItemListHeaders:
628  return ColumnCount;
629  default:
630  return EntityTreeModel::entityColumnCount(group);
631  }
632 }
633 
634 /******************************************************************************
635 * Return data for a column heading.
636 */
637 QVariant AkonadiModel::entityHeaderData(int section, Qt::Orientation orientation, int role, HeaderGroup group) const
638 {
639  if (orientation == Qt::Horizontal)
640  {
641  switch (group)
642  {
643  case CollectionTreeHeaders:
644  if (section != 0)
645  return QVariant();
646  if (role == Qt::DisplayRole)
647  return i18nc("@title:column", "Calendars");
648  break;
649 
650  case ItemListHeaders:
651  if (section < 0 || section >= ColumnCount)
652  return QVariant();
653  if (role == Qt::DisplayRole)
654  {
655  switch (section)
656  {
657  case TimeColumn:
658  return i18nc("@title:column", "Time");
659  case TimeToColumn:
660  return i18nc("@title:column", "Time To");
661  case RepeatColumn:
662  return i18nc("@title:column", "Repeat");
663  case ColourColumn:
664  return QString();
665  case TypeColumn:
666  return QString();
667  case TextColumn:
668  return i18nc("@title:column", "Message, File or Command");
669  case TemplateNameColumn:
670  return i18nc("@title:column Template name", "Name");
671  }
672  }
673  else if (role == Qt::WhatsThisRole)
674  return whatsThisText(section);
675  break;
676 
677  default:
678  break;
679  }
680  }
681  return EntityTreeModel::entityHeaderData(section, orientation, role, group);
682 }
683 
684 /******************************************************************************
685 * Recursive function to emit the dataChanged() signal for all items in a
686 * specified column range.
687 */
688 void AkonadiModel::signalDataChanged(bool (*checkFunc)(const Item&), int startColumn, int endColumn, const QModelIndex& parent)
689 {
690  int start = -1;
691  int end = -1;
692  for (int row = 0, count = rowCount(parent); row < count; ++row)
693  {
694  const QModelIndex ix = index(row, 0, parent);
695  const Item item = data(ix, ItemRole).value<Item>();
696  bool isItem = item.isValid();
697  if (isItem)
698  {
699  if ((*checkFunc)(item))
700  {
701  // For efficiency, emit a single signal for each group of
702  // consecutive items, rather than a separate signal for each item.
703  if (start < 0)
704  start = row;
705  end = row;
706  continue;
707  }
708  }
709  if (start >= 0)
710  emit dataChanged(index(start, startColumn, parent), index(end, endColumn, parent));
711  start = -1;
712  if (!isItem)
713  signalDataChanged(checkFunc, startColumn, endColumn, ix);
714  }
715 
716  if (start >= 0)
717  emit dataChanged(index(start, startColumn, parent), index(end, endColumn, parent));
718 }
719 
720 /******************************************************************************
721 * Signal every minute that the time-to-alarm values have changed.
722 */
723 static bool checkItem_isActive(const Item& item)
724 { return item.mimeType() == KAlarmCal::MIME_ACTIVE; }
725 
726 void AkonadiModel::slotUpdateTimeTo()
727 {
728  signalDataChanged(&checkItem_isActive, TimeToColumn, TimeToColumn, QModelIndex());
729 }
730 
731 
732 /******************************************************************************
733 * Called when the colour used to display archived alarms has changed.
734 */
735 static bool checkItem_isArchived(const Item& item)
736 { return item.mimeType() == KAlarmCal::MIME_ARCHIVED; }
737 
738 void AkonadiModel::slotUpdateArchivedColour(const QColor&)
739 {
740  kDebug();
741  signalDataChanged(&checkItem_isArchived, 0, ColumnCount - 1, QModelIndex());
742 }
743 
744 /******************************************************************************
745 * Called when the colour used to display disabled alarms has changed.
746 */
747 static bool checkItem_isDisabled(const Item& item)
748 {
749  if (item.hasPayload<KAEvent>())
750  {
751  const KAEvent event = item.payload<KAEvent>();
752  if (event.isValid())
753  return !event.enabled();
754  }
755  return false;
756 }
757 
758 void AkonadiModel::slotUpdateDisabledColour(const QColor&)
759 {
760  kDebug();
761  signalDataChanged(&checkItem_isDisabled, 0, ColumnCount - 1, QModelIndex());
762 }
763 
764 /******************************************************************************
765 * Called when the definition of holidays has changed.
766 */
767 static bool checkItem_excludesHolidays(const Item& item)
768 {
769  if (item.hasPayload<KAEvent>())
770  {
771  const KAEvent event = item.payload<KAEvent>();
772  if (event.isValid() && event.holidaysExcluded())
773  return true;
774  }
775  return false;
776 }
777 
778 void AkonadiModel::slotUpdateHolidays()
779 {
780  kDebug();
781  Q_ASSERT(TimeToColumn == TimeColumn + 1); // signal should be emitted only for TimeTo and Time columns
782  signalDataChanged(&checkItem_excludesHolidays, TimeColumn, TimeToColumn, QModelIndex());
783 }
784 
785 /******************************************************************************
786 * Called when the definition of working hours has changed.
787 */
788 static bool checkItem_workTimeOnly(const Item& item)
789 {
790  if (item.hasPayload<KAEvent>())
791  {
792  const KAEvent event = item.payload<KAEvent>();
793  if (event.isValid() && event.workTimeOnly())
794  return true;
795  }
796  return false;
797 }
798 
799 void AkonadiModel::slotUpdateWorkingHours()
800 {
801  kDebug();
802  Q_ASSERT(TimeToColumn == TimeColumn + 1); // signal should be emitted only for TimeTo and Time columns
803  signalDataChanged(&checkItem_workTimeOnly, TimeColumn, TimeToColumn, QModelIndex());
804 }
805 
806 /******************************************************************************
807 * Called when the command error status of an alarm has changed, to save the new
808 * status and update the visual command error indication.
809 */
810 void AkonadiModel::updateCommandError(const KAEvent& event)
811 {
812  QModelIndex ix = itemIndex(event.itemId());
813  if (ix.isValid())
814  setData(ix, QVariant(static_cast<int>(event.commandError())), CommandErrorRole);
815 }
816 
817 /******************************************************************************
818 * Return the foreground color for displaying a collection, based on the
819 * supplied mime types which it contains, and on whether it is fully writable.
820 */
821 QColor AkonadiModel::foregroundColor(const Akonadi::Collection& collection, const QStringList& mimeTypes)
822 {
823  QColor colour;
824  if (mimeTypes.contains(KAlarmCal::MIME_ACTIVE))
825  colour = KColorScheme(QPalette::Active).foreground(KColorScheme::NormalText).color();
826  else if (mimeTypes.contains(KAlarmCal::MIME_ARCHIVED))
827  colour = Preferences::archivedColour();
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);
832  return colour;
833 }
834 
835 /******************************************************************************
836 * Set the background color for displaying the collection and its alarms.
837 */
838 void AkonadiModel::setBackgroundColor(Collection& collection, const QColor& colour)
839 {
840  QModelIndex ix = modelIndexForCollection(this, collection);
841  if (ix.isValid())
842  setData(ix, QVariant(colour), Qt::BackgroundRole);
843 }
844 
845 /******************************************************************************
846 * Return the background color for displaying the collection and its alarms,
847 * after updating the collection from the Akonadi database.
848 */
849 QColor AkonadiModel::backgroundColor(Akonadi::Collection& collection) const
850 {
851  if (!collection.isValid())
852  return QColor();
853  refresh(collection);
854  return backgroundColor_p(collection);
855 }
856 
857 /******************************************************************************
858 * Return the background color for displaying the collection and its alarms.
859 */
860 QColor AkonadiModel::backgroundColor_p(const Akonadi::Collection& collection) const
861 {
862  if (!collection.isValid() || !collection.hasAttribute<CollectionAttribute>())
863  return QColor();
864  return collection.attribute<CollectionAttribute>()->backgroundColor();
865 }
866 
867 /******************************************************************************
868 * Return the display name for the collection, after updating the collection
869 * from the Akonadi database.
870 */
871 QString AkonadiModel::displayName(Akonadi::Collection& collection) const
872 {
873  if (!collection.isValid())
874  return QString();
875  refresh(collection);
876  return collection.displayName();
877 }
878 
879 /******************************************************************************
880 * Return the storage type (file, directory, etc.) for the collection.
881 */
882 QString AkonadiModel::storageType(const Akonadi::Collection& collection) const
883 {
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");
888 }
889 
890 /******************************************************************************
891 * Return a collection's tooltip text. The collection's enabled status is
892 * evaluated for specified alarm types.
893 */
894 QString AkonadiModel::tooltip(const Collection& collection, CalEvent::Types types) const
895 {
896  QString name = QLatin1Char('@') + collection.displayName(); // insert markers for stripping out name
897  KUrl url = collection.remoteId();
898  QString type = QLatin1Char('@') + storageType(collection); // file/directory/URL etc.
899  QString locn = url.pathOrUrl();
900  bool inactive = !collection.hasAttribute<CollectionAttribute>()
901  || !(collection.attribute<CollectionAttribute>()->enabled() & types);
902  QString disabled = i18nc("@info/plain", "Disabled");
903  QString readonly = readOnlyTooltip(collection);
904  bool writable = readonly.isEmpty();
905  if (inactive && !writable)
906  return i18nc("@info:tooltip",
907  "%1"
908  "<nl/>%2: <filename>%3</filename>"
909  "<nl/>%4, %5",
910  name, type, locn, disabled, readonly);
911  if (inactive || !writable)
912  return i18nc("@info:tooltip",
913  "%1"
914  "<nl/>%2: <filename>%3</filename>"
915  "<nl/>%4",
916  name, type, locn, (inactive ? disabled : readonly));
917  return i18nc("@info:tooltip",
918  "%1"
919  "<nl/>%2: <filename>%3</filename>",
920  name, type, locn);
921 }
922 
923 /******************************************************************************
924 * Return the read-only status tooltip for a collection.
925 * A null string is returned if the collection is fully writable.
926 */
927 QString AkonadiModel::readOnlyTooltip(const Collection& collection)
928 {
929  KACalendar::Compat compat;
930  switch (AkonadiModel::isWritable(collection, compat))
931  {
932  case 1:
933  return QString();
934  case 0:
935  return i18nc("@info/plain", "Read-only (old format)");
936  default:
937  if (compat == KACalendar::Current)
938  return i18nc("@info/plain", "Read-only");
939  return i18nc("@info/plain", "Read-only (other format)");
940  }
941 }
942 
943 /******************************************************************************
944 * Return the repetition text.
945 */
946 QString AkonadiModel::repeatText(const KAEvent& event) const
947 {
948  QString repeatText = event.recurrenceText(true);
949  if (repeatText.isEmpty())
950  repeatText = event.repetitionText(true);
951  return repeatText;
952 }
953 
954 /******************************************************************************
955 * Return a string for sorting the repetition column.
956 */
957 QString AkonadiModel::repeatOrder(const KAEvent& event) const
958 {
959  int repeatOrder = 0;
960  int repeatInterval = 0;
961  if (event.repeatAtLogin())
962  repeatOrder = 1;
963  else
964  {
965  repeatInterval = event.recurInterval();
966  switch (event.recurType())
967  {
968  case KARecurrence::MINUTELY:
969  repeatOrder = 2;
970  break;
971  case KARecurrence::DAILY:
972  repeatOrder = 3;
973  break;
974  case KARecurrence::WEEKLY:
975  repeatOrder = 4;
976  break;
977  case KARecurrence::MONTHLY_DAY:
978  case KARecurrence::MONTHLY_POS:
979  repeatOrder = 5;
980  break;
981  case KARecurrence::ANNUAL_DATE:
982  case KARecurrence::ANNUAL_POS:
983  repeatOrder = 6;
984  break;
985  case KARecurrence::NO_RECUR:
986  default:
987  break;
988  }
989  }
990  return QString::fromLatin1("%1%2").arg(static_cast<char>('0' + repeatOrder)).arg(repeatInterval, 8, 10, QLatin1Char('0'));
991 }
992 
993 /******************************************************************************
994 * Return the icon associated with the event's action.
995 */
996 QPixmap* AkonadiModel::eventIcon(const KAEvent& event) const
997 {
998  switch (event.actionTypes())
999  {
1000  case KAEvent::ACT_EMAIL:
1001  return mEmailIcon;
1002  case KAEvent::ACT_AUDIO:
1003  return mAudioIcon;
1004  case KAEvent::ACT_COMMAND:
1005  return mCommandIcon;
1006  case KAEvent::ACT_DISPLAY:
1007  if (event.actionSubType() == KAEvent::FILE)
1008  return mFileIcon;
1009  // fall through to ACT_DISPLAY_COMMAND
1010  case KAEvent::ACT_DISPLAY_COMMAND:
1011  default:
1012  return mTextIcon;
1013  }
1014 }
1015 
1016 /******************************************************************************
1017 * Returns the QWhatsThis text for a specified column.
1018 */
1019 QString AkonadiModel::whatsThisText(int column) const
1020 {
1021  switch (column)
1022  {
1023  case TimeColumn:
1024  return i18nc("@info:whatsthis", "Next scheduled date and time of the alarm");
1025  case TimeToColumn:
1026  return i18nc("@info:whatsthis", "How long until the next scheduled trigger of the alarm");
1027  case RepeatColumn:
1028  return i18nc("@info:whatsthis", "How often the alarm recurs");
1029  case ColourColumn:
1030  return i18nc("@info:whatsthis", "Background color of alarm message");
1031  case TypeColumn:
1032  return i18nc("@info:whatsthis", "Alarm type (message, file, command or email)");
1033  case TextColumn:
1034  return i18nc("@info:whatsthis", "Alarm message text, URL of text file to display, command to execute, or email subject line");
1035  case TemplateNameColumn:
1036  return i18nc("@info:whatsthis", "Name of the alarm template");
1037  default:
1038  return QString();
1039  }
1040 }
1041 
1042 /******************************************************************************
1043 * Remove a collection from Akonadi. The calendar file is not removed.
1044 */
1045 bool AkonadiModel::removeCollection(const Akonadi::Collection& collection)
1046 {
1047  if (!collection.isValid())
1048  return false;
1049  kDebug() << collection.id();
1050  Collection col = collection;
1051  mCollectionsDeleting << collection.id();
1052  // Note: CollectionDeleteJob deletes the backend storage also.
1053  AgentManager* agentManager = AgentManager::self();
1054  const AgentInstance instance = agentManager->instance(collection.resource());
1055  if (instance.isValid())
1056  agentManager->removeInstance(instance);
1057 #if 0
1058  CollectionDeleteJob* job = new CollectionDeleteJob(col);
1059  connect(job, SIGNAL(result(KJob*)), SLOT(deleteCollectionJobDone(KJob*)));
1060  mPendingCollectionJobs[job] = CollJobData(col.id(), displayName(col));
1061  job->start();
1062 #endif
1063  return true;
1064 }
1065 
1066 /******************************************************************************
1067 * Return whether a collection is currently being deleted.
1068 */
1069 bool AkonadiModel::isCollectionBeingDeleted(Collection::Id id) const
1070 {
1071  return mCollectionsDeleting.contains(id);
1072 }
1073 
1074 #if 0
1075 /******************************************************************************
1076 * Called when a collection deletion job has completed.
1077 * Checks for any error.
1078 */
1079 void AkonadiModel::deleteCollectionJobDone(KJob* j)
1080 {
1081  QMap<KJob*, CollJobData>::iterator it = mPendingCollectionJobs.find(j);
1082  CollJobData jobData;
1083  if (it != mPendingCollectionJobs.end())
1084  {
1085  jobData = it.value();
1086  mPendingCollectionJobs.erase(it);
1087  }
1088  if (j->error())
1089  {
1090  emit collectionDeleted(jobData.id, false);
1091  QString errMsg = i18nc("@info", "Failed to remove calendar <resource>%1</resource>.", jobData.displayName);
1092  kError() << errMsg << ":" << j->errorString();
1093  KAMessageBox::error(MainWindow::mainMainWindow(), i18nc("@info", "%1<nl/>(%2)", errMsg, j->errorString()));
1094  }
1095  else
1096  emit collectionDeleted(jobData.id, true);
1097 }
1098 #endif
1099 
1100 /******************************************************************************
1101 * Reload a collection from Akonadi storage. The backend data is not reloaded.
1102 */
1103 bool AkonadiModel::reloadCollection(const Akonadi::Collection& collection)
1104 {
1105  if (!collection.isValid())
1106  return false;
1107  kDebug() << collection.id();
1108  mMonitor->setCollectionMonitored(collection, false);
1109  mMonitor->setCollectionMonitored(collection, true);
1110  return true;
1111 }
1112 
1113 /******************************************************************************
1114 * Reload a collection from Akonadi storage. The backend data is not reloaded.
1115 */
1116 void AkonadiModel::reload()
1117 {
1118  kDebug();
1119  const Collection::List collections = mMonitor->collectionsMonitored();
1120  foreach (const Collection& collection, collections)
1121  {
1122  mMonitor->setCollectionMonitored(collection, false);
1123  mMonitor->setCollectionMonitored(collection, true);
1124  }
1125 }
1126 
1127 /******************************************************************************
1128 * Called when a collection modification job has completed.
1129 * Checks for any error.
1130 */
1131 void AkonadiModel::modifyCollectionJobDone(KJob* j)
1132 {
1133  Collection collection = static_cast<CollectionModifyJob*>(j)->collection();
1134  Collection::Id id = collection.id();
1135  if (j->error())
1136  {
1137  emit collectionModified(id, false);
1138  if (mCollectionsDeleted.contains(id))
1139  mCollectionsDeleted.removeAll(id);
1140  else
1141  {
1142  QString errMsg = i18nc("@info", "Failed to update calendar <resource>%1</resource>.", displayName(collection));
1143  kError() << "Id:" << collection.id() << errMsg << ":" << j->errorString();
1144  KAMessageBox::error(MainWindow::mainMainWindow(), i18nc("@info", "%1<nl/>(%2)", errMsg, j->errorString()));
1145  }
1146  }
1147  else
1148  emit collectionModified(id, true);
1149 }
1150 
1151 /******************************************************************************
1152 * Returns the index to a specified event.
1153 */
1154 QModelIndex AkonadiModel::eventIndex(const KAEvent& event)
1155 {
1156  return itemIndex(event.itemId());
1157 }
1158 
1159 #if 0
1160 /******************************************************************************
1161 * Return all events of a given type belonging to a collection.
1162 */
1163 KAEvent::List AkonadiModel::events(Akonadi::Collection& collection, CalEvent::Type type) const
1164 {
1165  KAEvent::List list;
1166  QModelIndex ix = modelIndexForCollection(this, collection);
1167  if (ix.isValid())
1168  getChildEvents(ix, type, list);
1169  return list;
1170 }
1171 
1172 /******************************************************************************
1173 * Recursive function to append all child Events with a given mime type.
1174 */
1175 void AkonadiModel::getChildEvents(const QModelIndex& parent, CalEvent::Type type, KAEvent::List& events) const
1176 {
1177  for (int row = 0, count = rowCount(parent); row < count; ++row)
1178  {
1179  const QModelIndex ix = index(row, 0, parent);
1180  const Item item = data(ix, ItemRole).value<Item>();
1181  if (item.isValid())
1182  {
1183  if (item.hasPayload<KAEvent>())
1184  {
1185  KAEvent event = item.payload<KAEvent>();
1186  if (event.isValid() && event.category() == type)
1187  events += event;
1188  }
1189  }
1190  else
1191  {
1192  Collection c = ix.data(CollectionRole).value<Collection>();
1193  if (c.isValid())
1194  getChildEvents(ix, type, events);
1195  }
1196  }
1197 }
1198 #endif
1199 
1200 KAEvent AkonadiModel::event(Item::Id itemId) const
1201 {
1202  QModelIndex ix = itemIndex(itemId);
1203  if (!ix.isValid())
1204  return KAEvent();
1205  return event(ix.data(ItemRole).value<Item>(), ix, 0);
1206 }
1207 
1208 KAEvent AkonadiModel::event(const QModelIndex& index) const
1209 {
1210  return event(index.data(ItemRole).value<Item>(), index, 0);
1211 }
1212 
1213 KAEvent AkonadiModel::event(const Item& item, const QModelIndex& index, Collection* collection) const
1214 {
1215  if (!item.isValid() || !item.hasPayload<KAEvent>())
1216  return KAEvent();
1217  const QModelIndex ix = index.isValid() ? index : itemIndex(item.id());
1218  if (!ix.isValid())
1219  return KAEvent();
1220  KAEvent e = item.payload<KAEvent>();
1221  if (e.isValid())
1222  {
1223 
1224  Collection c = data(ix, ParentCollectionRole).value<Collection>();
1225  // Set collection ID using a const method, to avoid unnecessary copying of KAEvent
1226  e.setCollectionId_const(c.id());
1227  if (collection)
1228  *collection = c;
1229  }
1230  return e;
1231 }
1232 
1233 #if 0
1234 /******************************************************************************
1235 * Add an event to the default or a user-selected Collection.
1236 */
1237 AkonadiModel::Result AkonadiModel::addEvent(KAEvent* event, CalEvent::Type type, QWidget* promptParent, bool noPrompt)
1238 {
1239  kDebug() << event->id();
1240 
1241  // Determine parent collection - prompt or use default
1242  bool cancelled;
1243  Collection collection = destination(type, Collection::CanCreateItem, promptParent, noPrompt, &cancelled);
1244  if (!collection.isValid())
1245  {
1246  delete event;
1247  if (cancelled)
1248  return Cancelled;
1249  kDebug() << "No collection";
1250  return Failed;
1251  }
1252  if (!addEvent(event, collection))
1253  {
1254  kDebug() << "Failed";
1255  return Failed; // event was deleted by addEvent()
1256  }
1257  return Success;
1258 }
1259 #endif
1260 
1261 /******************************************************************************
1262 * Add events to a specified Collection.
1263 * Events which are scheduled to be added to the collection are updated with
1264 * their Akonadi item ID.
1265 * The caller must connect to the itemDone() signal to check whether events
1266 * have been added successfully. Note that the first signal may be emitted
1267 * before this function returns.
1268 * Reply = true if item creation has been scheduled for all events,
1269 * = false if at least one item creation failed to be scheduled.
1270 */
1271 bool AkonadiModel::addEvents(const KAEvent::List& events, Collection& collection)
1272 {
1273  bool ok = true;
1274  for (int i = 0, count = events.count(); i < count; ++i)
1275  ok = ok && addEvent(*events[i], collection);
1276  return ok;
1277 }
1278 
1279 /******************************************************************************
1280 * Add an event to a specified Collection.
1281 * If the event is scheduled to be added to the collection, it is updated with
1282 * its Akonadi item ID.
1283 * The event's 'updated' flag is cleared.
1284 * The caller must connect to the itemDone() signal to check whether events
1285 * have been added successfully.
1286 * Reply = true if item creation has been scheduled.
1287 */
1288 bool AkonadiModel::addEvent(KAEvent& event, Collection& collection)
1289 {
1290  kDebug() << "ID:" << event.id();
1291  Item item;
1292  if (!event.setItemPayload(item, collection.contentMimeTypes()))
1293  {
1294  kWarning() << "Invalid mime type for collection";
1295  return false;
1296  }
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();
1302  job->start();
1303 kDebug()<<"...exiting";
1304  return true;
1305 }
1306 
1307 /******************************************************************************
1308 * Update an event in its collection.
1309 * The event retains its existing Akonadi item ID.
1310 * The event's 'updated' flag is cleared.
1311 * The caller must connect to the itemDone() signal to check whether the event
1312 * has been updated successfully.
1313 * Reply = true if item update has been scheduled.
1314 */
1315 bool AkonadiModel::updateEvent(KAEvent& event)
1316 {
1317  kDebug() << "ID:" << event.id();
1318  return updateEvent(event.itemId(), event);
1319 }
1320 bool AkonadiModel::updateEvent(Akonadi::Entity::Id itemId, KAEvent& newEvent)
1321 {
1322 kDebug()<<"item id="<<itemId;
1323  const QModelIndex ix = itemIndex(itemId);
1324  if (!ix.isValid())
1325  return false;
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()))
1330  {
1331  kWarning() << "Invalid mime type for collection";
1332  return false;
1333  }
1334 // setData(ix, QVariant::fromValue(item), ItemRole);
1335  queueItemModifyJob(item);
1336  return true;
1337 }
1338 
1339 /******************************************************************************
1340 * Delete an event from its collection.
1341 */
1342 bool AkonadiModel::deleteEvent(const KAEvent& event)
1343 {
1344  return deleteEvent(event.itemId());
1345 }
1346 bool AkonadiModel::deleteEvent(Akonadi::Entity::Id itemId)
1347 {
1348  kDebug() << itemId;
1349  QModelIndex ix = itemIndex(itemId);
1350  if (!ix.isValid())
1351  return false;
1352  if (mCollectionsDeleting.contains(ix.data(ParentCollectionRole).value<Collection>().id()))
1353  {
1354  kDebug() << "Collection being deleted";
1355  return true; // the event's collection is being deleted
1356  }
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;
1361  job->start();
1362  return true;
1363 }
1364 
1365 /******************************************************************************
1366 * Queue an ItemModifyJob for execution. Ensure that only one job is
1367 * simultaneously active for any one Item.
1368 *
1369 * This is necessary because we can't call two ItemModifyJobs for the same Item
1370 * at the same time; otherwise Akonadi will detect a conflict and require manual
1371 * intervention to resolve it.
1372 */
1373 void AkonadiModel::queueItemModifyJob(const Item& item)
1374 {
1375  kDebug() << item.id();
1376  QMap<Item::Id, Item>::Iterator it = mItemModifyJobQueue.find(item.id());
1377  if (it != mItemModifyJobQueue.end())
1378  {
1379  // A job is already queued for this item. Replace the queued item value with the new one.
1380  kDebug() << "Replacing previously queued job";
1381  it.value() = item;
1382  }
1383  else
1384  {
1385  // There is no job already queued for this item
1386  if (mItemsBeingCreated.contains(item.id()))
1387  {
1388  kDebug() << "Waiting for item initialisation";
1389  mItemModifyJobQueue[item.id()] = item; // wait for item initialisation to complete
1390  }
1391  else
1392  {
1393  Item newItem = item;
1394  Item current = itemById(item.id()); // fetch the up-to-date item
1395  if (current.isValid())
1396  newItem.setRevision(current.revision());
1397  mItemModifyJobQueue[item.id()] = Item(); // mark the queued item as now executing
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();
1403  }
1404  }
1405 }
1406 
1407 /******************************************************************************
1408 * Called when an item job has completed.
1409 * Checks for any error.
1410 * Note that for an ItemModifyJob, the item revision number may not be updated
1411 * to the post-modification value. The next queued ItemModifyJob is therefore
1412 * not kicked off from here, but instead from the slot attached to the
1413 * itemChanged() signal, which has the revision updated.
1414 */
1415 void AkonadiModel::itemJobDone(KJob* j)
1416 {
1417  QMap<KJob*, Entity::Id>::iterator it = mPendingItemJobs.find(j);
1418  Entity::Id itemId = -1;
1419  if (it != mPendingItemJobs.end())
1420  {
1421  itemId = it.value();
1422  mPendingItemJobs.erase(it);
1423  }
1424  QByteArray jobClass = j->metaObject()->className();
1425  kDebug() << jobClass;
1426  if (j->error())
1427  {
1428  QString errMsg;
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.");
1435  else
1436  Q_ASSERT(0);
1437  kError() << errMsg << itemId << ":" << j->errorString();
1438  emit itemDone(itemId, false);
1439 
1440  if (itemId >= 0 && jobClass == "Akonadi::ItemModifyJob")
1441  {
1442  // Execute the next queued job for this item
1443  Item current = itemById(itemId); // fetch the up-to-date item
1444  checkQueuedItemModifyJob(current);
1445  }
1446  KAMessageBox::error(MainWindow::mainMainWindow(), i18nc("@info", "%1<nl/>(%2)", errMsg, j->errorString()));
1447  }
1448  else
1449  {
1450  if (jobClass == "Akonadi::ItemCreateJob")
1451  {
1452  // Prevent modification of the item until it is fully initialised.
1453  // Either slotMonitoredItemChanged() or slotRowsInserted(), or both,
1454  // will be called when the item is done.
1455  kDebug() << "item id=" << static_cast<ItemCreateJob*>(j)->item().id();
1456  mItemsBeingCreated << static_cast<ItemCreateJob*>(j)->item().id();
1457  }
1458  emit itemDone(itemId);
1459  }
1460 
1461 /* if (itemId >= 0 && jobClass == "Akonadi::ItemModifyJob")
1462  {
1463  QMap<Item::Id, Item>::iterator it = mItemModifyJobQueue.find(itemId);
1464  if (it != mItemModifyJobQueue.end())
1465  {
1466  if (!it.value().isValid())
1467  mItemModifyJobQueue.erase(it); // there are no more jobs queued for the item
1468  }
1469  }*/
1470 }
1471 
1472 /******************************************************************************
1473 * Check whether there are any ItemModifyJobs waiting for a specified item, and
1474 * if so execute the first one provided its creation has completed. This
1475 * prevents clashes in Akonadi conflicts between simultaneous ItemModifyJobs for
1476 * the same item.
1477 *
1478 * Note that when an item is newly created (e.g. via addEvent()), the KAlarm
1479 * resource itemAdded() function creates an ItemModifyJob to give it a remote
1480 * ID. Until that job is complete, any other ItemModifyJob for the item will
1481 * cause a conflict.
1482 */
1483 void AkonadiModel::checkQueuedItemModifyJob(const Item& item)
1484 {
1485  if (mItemsBeingCreated.contains(item.id()))
1486 {kDebug()<<"Still being created";
1487  return; // the item hasn't been fully initialised yet
1488 }
1489  QMap<Item::Id, Item>::iterator it = mItemModifyJobQueue.find(item.id());
1490  if (it == mItemModifyJobQueue.end())
1491 {kDebug()<<"No jobs queued";
1492  return; // there are no jobs queued for the item
1493 }
1494  Item qitem = it.value();
1495  if (!qitem.isValid())
1496  {
1497  // There is no further job queued for the item, so remove the item from the list
1498 kDebug()<<"No more jobs queued";
1499  mItemModifyJobQueue.erase(it);
1500  }
1501  else
1502  {
1503  // Queue the next job for the Item, after updating the Item's
1504  // revision number to match that set by the job just completed.
1505  qitem.setRevision(item.revision());
1506  mItemModifyJobQueue[item.id()] = Item(); // mark the queued item as now executing
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();
1512  }
1513 }
1514 
1515 /******************************************************************************
1516 * Called when rows have been inserted into the model.
1517 */
1518 void AkonadiModel::slotRowsInserted(const QModelIndex& parent, int start, int end)
1519 {
1520  kDebug() << start << "-" << end << "(parent =" << parent << ")";
1521  for (int row = start; row <= end; ++row)
1522  {
1523  const QModelIndex ix = index(row, 0, parent);
1524  const Collection collection = ix.data(CollectionRole).value<Collection>();
1525  if (collection.isValid())
1526  {
1527  // A collection has been inserted.
1528  // Ignore it if it isn't owned by a valid resource.
1529  kDebug() << "Collection" << collection.id() << collection.name();
1530  if (AgentManager::self()->instance(collection.resource()).isValid())
1531  {
1532  QSet<QByteArray> attrs;
1533  attrs += CollectionAttribute::name();
1534  setCollectionChanged(collection, attrs, true);
1535  emit collectionAdded(collection);
1536 
1537  if (!mCollectionsBeingCreated.contains(collection.remoteId())
1538  && (collection.rights() & writableRights) == writableRights)
1539  {
1540  // Update to current KAlarm format if necessary, and if the user agrees
1541  CalendarMigrator::updateToCurrentFormat(collection, false, MainWindow::mainMainWindow());
1542  }
1543  }
1544  }
1545  else
1546  {
1547  // An item has been inserted
1548  const Item item = ix.data(ItemRole).value<Item>();
1549  if (item.isValid())
1550  {
1551  kDebug() << "item id=" << item.id() << ", revision=" << item.revision();
1552  if (mItemsBeingCreated.removeAll(item.id())) // the new item has now been initialised
1553  checkQueuedItemModifyJob(item); // execute the next job queued for the item
1554  }
1555  }
1556  }
1557  EventList events = eventList(parent, start, end);
1558  if (!events.isEmpty())
1559  emit eventsAdded(events);
1560 }
1561 
1562 /******************************************************************************
1563 * Called when rows are about to be removed from the model.
1564 */
1565 void AkonadiModel::slotRowsAboutToBeRemoved(const QModelIndex& parent, int start, int end)
1566 {
1567  kDebug() << start << "-" << end << "(parent =" << parent << ")";
1568  EventList events = eventList(parent, start, end);
1569  if (!events.isEmpty())
1570  {
1571  foreach (const Event& event, events)
1572  kDebug() << "Collection:" << event.collection.id() << ", Event ID:" << event.event.id();
1573  emit eventsToBeRemoved(events);
1574  }
1575 }
1576 
1577 /******************************************************************************
1578 * Return a list of KAEvent/Collection pairs for a given range of rows.
1579 */
1580 AkonadiModel::EventList AkonadiModel::eventList(const QModelIndex& parent, int start, int end)
1581 {
1582  EventList events;
1583  for (int row = start; row <= end; ++row)
1584  {
1585  Collection c;
1586  QModelIndex ix = index(row, 0, parent);
1587  KAEvent evnt = event(ix.data(ItemRole).value<Item>(), ix, &c);
1588  if (evnt.isValid())
1589  events += Event(evnt, c);
1590  }
1591  return events;
1592 }
1593 
1594 /******************************************************************************
1595 * Called when a monitored collection's properties or content have changed.
1596 * Optionally emits a signal if properties of interest have changed.
1597 */
1598 void AkonadiModel::setCollectionChanged(const Collection& collection, const QSet<QByteArray>& attributeNames, bool rowInserted)
1599 {
1600  // Check for a read/write permission change
1601  Collection::Rights oldRights = mCollectionRights.value(collection.id(), Collection::AllRights);
1602  Collection::Rights newRights = collection.rights() & writableRights;
1603  if (newRights != oldRights)
1604  {
1605  kDebug() << "Collection" << collection.id() << ": rights ->" << newRights;
1606  mCollectionRights[collection.id()] = newRights;
1607  emit collectionStatusChanged(collection, ReadOnly, (newRights != writableRights), rowInserted);
1608  }
1609 
1610  // Check for a change in content mime types
1611  // (e.g. when a collection is first created at startup).
1612  CalEvent::Types oldAlarmTypes = mCollectionAlarmTypes.value(collection.id(), CalEvent::EMPTY);
1613  CalEvent::Types newAlarmTypes = CalEvent::types(collection.contentMimeTypes());
1614  if (newAlarmTypes != oldAlarmTypes)
1615  {
1616  kDebug() << "Collection" << collection.id() << ": alarm types ->" << newAlarmTypes;
1617  mCollectionAlarmTypes[collection.id()] = newAlarmTypes;
1618  emit collectionStatusChanged(collection, AlarmTypes, static_cast<int>(newAlarmTypes), rowInserted);
1619  }
1620 
1621  // Check for the collection being enabled/disabled
1622  if (attributeNames.contains(CollectionAttribute::name()))
1623  {
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)
1628  {
1629  kDebug() << "Collection" << collection.id() << ": enabled ->" << newEnabled;
1630  first = false;
1631  mCollectionEnabled[collection.id()] = newEnabled;
1632  emit collectionStatusChanged(collection, Enabled, static_cast<int>(newEnabled), rowInserted);
1633  }
1634  }
1635 
1636  // Check for the backend calendar format changing
1637  if (attributeNames.contains(CompatibilityAttribute::name()))
1638  {
1639  // Update to current KAlarm format if necessary, and if the user agrees
1640  kDebug() << "CompatibilityAttribute";
1641  Collection col(collection);
1642  refresh(col);
1643  CalendarMigrator::updateToCurrentFormat(col, false, MainWindow::mainMainWindow());
1644  }
1645 
1646  if (mMigrating)
1647  {
1648  mCollectionIdsBeingCreated.removeAll(collection.id());
1649  if (mCollectionsBeingCreated.isEmpty() && mCollectionIdsBeingCreated.isEmpty()
1650  && CalendarMigrator::completed())
1651  {
1652  kDebug() << "Migration completed";
1653  mMigrating = false;
1654  emit migrationCompleted();
1655  }
1656  }
1657 }
1658 
1659 /******************************************************************************
1660 * Called when a monitored collection is removed.
1661 */
1662 void AkonadiModel::slotCollectionRemoved(const Collection& collection)
1663 {
1664  Collection::Id id = collection.id();
1665  kDebug() << id;
1666  mCollectionRights.remove(id);
1667  mCollectionsDeleting.removeAll(id);
1668  while (mCollectionsDeleted.count() > 20) // don't let list grow indefinitely
1669  mCollectionsDeleted.removeFirst();
1670  mCollectionsDeleted << id;
1671 }
1672 
1673 /******************************************************************************
1674 * Called when a collection creation is about to start, or has completed.
1675 */
1676 void AkonadiModel::slotCollectionBeingCreated(const QString& path, Akonadi::Collection::Id id, bool finished)
1677 {
1678  if (finished)
1679  {
1680  mCollectionsBeingCreated.removeAll(path);
1681  mCollectionIdsBeingCreated << id;
1682  }
1683  else
1684  mCollectionsBeingCreated << path;
1685 }
1686 
1687 /******************************************************************************
1688 * Called when calendar migration has completed.
1689 */
1690 void AkonadiModel::slotMigrationCompleted()
1691 {
1692  if (mCollectionsBeingCreated.isEmpty() && mCollectionIdsBeingCreated.isEmpty())
1693  {
1694  kDebug() << "Migration completed";
1695  mMigrating = false;
1696  emit migrationCompleted();
1697  }
1698 }
1699 
1700 /******************************************************************************
1701 * Called when an item in the monitored collections has changed.
1702 */
1703 void AkonadiModel::slotMonitoredItemChanged(const Akonadi::Item& item, const QSet<QByteArray>&)
1704 {
1705  kDebug() << "item id=" << item.id() << ", revision=" << item.revision();
1706  mItemsBeingCreated.removeAll(item.id()); // the new item has now been initialised
1707  checkQueuedItemModifyJob(item); // execute the next job queued for the item
1708 
1709  KAEvent evnt = event(item);
1710  if (!evnt.isValid())
1711  return;
1712  const QModelIndexList indexes = modelIndexesForItem(this, item);
1713  foreach (const QModelIndex& index, indexes)
1714  {
1715  if (index.isValid())
1716  {
1717  // Wait to ensure that the base EntityTreeModel has processed the
1718  // itemChanged() signal first, before we emit eventChanged().
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()));
1723  break;
1724  }
1725  }
1726 }
1727 
1728 /******************************************************************************
1729 * Called to emit a signal when an event in the monitored collections has
1730 * changed.
1731 */
1732 void AkonadiModel::slotEmitEventChanged()
1733 {
1734  while (!mPendingEventChanges.isEmpty())
1735  {
1736  emit eventChanged(mPendingEventChanges.dequeue());
1737  }
1738 }
1739 
1740 /******************************************************************************
1741 * Refresh the specified Collection with up to date data.
1742 * Return: true if successful, false if collection not found.
1743 */
1744 bool AkonadiModel::refresh(Akonadi::Collection& collection) const
1745 {
1746  QModelIndex ix = modelIndexForCollection(this, collection);
1747  if (!ix.isValid())
1748  return false;
1749  collection = ix.data(CollectionRole).value<Collection>();
1750  return true;
1751 }
1752 
1753 /******************************************************************************
1754 * Refresh the specified Item with up to date data.
1755 * Return: true if successful, false if item not found.
1756 */
1757 bool AkonadiModel::refresh(Akonadi::Item& item) const
1758 {
1759  const QModelIndexList ixs = modelIndexesForItem(this, item);
1760  if (ixs.isEmpty() || !ixs[0].isValid())
1761  return false;
1762  item = ixs[0].data(ItemRole).value<Item>();
1763  return true;
1764 }
1765 
1766 /******************************************************************************
1767 * Find the QModelIndex of a collection.
1768 */
1769 QModelIndex AkonadiModel::collectionIndex(const Collection& collection) const
1770 {
1771  QModelIndex ix = modelIndexForCollection(this, collection);
1772  if (!ix.isValid())
1773  return QModelIndex();
1774  return ix;
1775 }
1776 
1777 /******************************************************************************
1778 * Return the up to date collection with the specified Akonadi ID.
1779 */
1780 Collection AkonadiModel::collectionById(Collection::Id id) const
1781 {
1782  QModelIndex ix = modelIndexForCollection(this, Collection(id));
1783  if (!ix.isValid())
1784  return Collection();
1785  return ix.data(CollectionRole).value<Collection>();
1786 }
1787 
1788 /******************************************************************************
1789 * Find the QModelIndex of an item.
1790 */
1791 QModelIndex AkonadiModel::itemIndex(const Item& item) const
1792 {
1793  const QModelIndexList ixs = modelIndexesForItem(this, item);
1794  if (ixs.isEmpty() || !ixs[0].isValid())
1795  return QModelIndex();
1796  return ixs[0];
1797 }
1798 
1799 /******************************************************************************
1800 * Return the up to date item with the specified Akonadi ID.
1801 */
1802 Item AkonadiModel::itemById(Item::Id id) const
1803 {
1804  const QModelIndexList ixs = modelIndexesForItem(this, Item(id));
1805  if (ixs.isEmpty() || !ixs[0].isValid())
1806  return Item();
1807  return ixs[0].data(ItemRole).value<Item>();
1808 }
1809 
1810 /******************************************************************************
1811 * Find the collection containing the specified Akonadi item ID.
1812 */
1813 Collection AkonadiModel::collectionForItem(Item::Id id) const
1814 {
1815  QModelIndex ix = itemIndex(id);
1816  if (!ix.isValid())
1817  return Collection();
1818  return ix.data(ParentCollectionRole).value<Collection>();
1819 }
1820 
1821 bool AkonadiModel::isCompatible(const Collection& collection)
1822 {
1823  return collection.hasAttribute<CompatibilityAttribute>()
1824  && collection.attribute<CompatibilityAttribute>()->compatibility() == KACalendar::Current;
1825 }
1826 
1827 /******************************************************************************
1828 * Return whether a collection is fully writable.
1829 */
1830 int AkonadiModel::isWritable(const Akonadi::Collection& collection)
1831 {
1832  KACalendar::Compat format;
1833  return isWritable(collection, format);
1834 }
1835 
1836 int AkonadiModel::isWritable(const Akonadi::Collection& collection, KACalendar::Compat& format)
1837 {
1838  format = KACalendar::Incompatible;
1839  if (!collection.isValid())
1840  return -1;
1841  Collection col = collection;
1842  instance()->refresh(col); // update with latest data
1843  if ((col.rights() & writableRights) != writableRights)
1844  {
1845  format = KACalendar::Current;
1846  return -1;
1847  }
1848  if (!col.hasAttribute<CompatibilityAttribute>())
1849  return -1;
1850  format = col.attribute<CompatibilityAttribute>()->compatibility();
1851  switch (format)
1852  {
1853  case KACalendar::Current:
1854  return 1;
1855  case KACalendar::Converted:
1856  case KACalendar::Convertible:
1857  return 0;
1858  default:
1859  return -1;
1860  }
1861 }
1862 
1863 CalEvent::Types AkonadiModel::types(const Collection& collection)
1864 {
1865  return CalEvent::types(collection.contentMimeTypes());
1866 }
1867 
1868 // vim: et sw=4:
AkonadiModel::eventsAdded
void eventsAdded(const AkonadiModel::EventList &)
Signal emitted when events have been added to the model.
AkonadiModel::collectionStatusChanged
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.
AkonadiModel::StatusRole
Definition: akonadimodel.h:68
synchtimer.h
calendarmigrator.h
AkonadiModel::readOnlyTooltip
static QString readOnlyTooltip(const Akonadi::Collection &)
Return the read-only status tooltip for a collection.
Definition: akonadimodel.cpp:927
checkItem_isDisabled
static bool checkItem_isDisabled(const Item &item)
Definition: akonadimodel.cpp:747
AkonadiModel::updateEvent
bool updateEvent(KAEvent &event)
Definition: akonadimodel.cpp:1315
AkonadiModel::TextColumn
Definition: akonadimodel.h:55
AkonadiModel::IsStandardRole
Definition: akonadimodel.h:64
AkonadiModel::isMigrationCompleted
bool isMigrationCompleted() const
Return whether calendar migration/creation at initialisation has completed.
Definition: akonadimodel.cpp:169
alarmtime.h
QWidget
CalendarMigrator::updateToCurrentFormat
static void updateToCurrentFormat(const Akonadi::Collection &, bool ignoreKeepFormat, QWidget *parent)
Definition: calendarmigrator.cpp:367
KAMessageBox::error
static void error(QWidget *parent, const QString &text, const QString &caption=QString(), Options options=Options(Notify|WindowModal))
AkonadiModel::displayName
QString displayName(Akonadi::Collection &) const
Return the display name for a collection.
Definition: akonadimodel.cpp:871
AkonadiModel::refresh
bool refresh(Akonadi::Collection &) const
Refresh the specified collection instance with up to date data.
Definition: akonadimodel.cpp:1744
AkonadiModel::Enabled
Definition: akonadimodel.h:52
AkonadiModel::isCollectionBeingDeleted
bool isCollectionBeingDeleted(Akonadi::Collection::Id) const
Definition: akonadimodel.cpp:1069
AkonadiModel::collectionDeleted
void collectionDeleted(Akonadi::Collection::Id, bool status=true)
Signal emitted when Akonadi has completed a collection deletion.
AkonadiModel::collectionIndex
QModelIndex collectionIndex(Akonadi::Collection::Id id) const
Definition: akonadimodel.h:119
AkonadiModel::AlarmSubActionRole
Definition: akonadimodel.h:70
AkonadiModel::EnabledTypesRole
Definition: akonadimodel.h:61
QObject
AkonadiModel::event
KAEvent event(const Akonadi::Item &item) const
Return the alarm with the specified unique identifier.
Definition: akonadimodel.h:150
AkonadiModel::collectionById
Akonadi::Collection collectionById(Akonadi::Collection::Id) const
Definition: akonadimodel.cpp:1780
AkonadiModel::CommandErrorRole
Definition: akonadimodel.h:73
checkItem_workTimeOnly
static bool checkItem_workTimeOnly(const Item &item)
Definition: akonadimodel.cpp:788
AkonadiModel::storageType
QString storageType(const Akonadi::Collection &) const
Return the storage type (file/directory/URL etc.) for a collection.
Definition: akonadimodel.cpp:882
akonadimodel.h
AkonadiModel::EventList
QList< Event > EventList
Definition: akonadimodel.h:85
AkonadiModel::TemplateNameColumn
Definition: akonadimodel.h:56
autoqpointer.h
AkonadiModel::collectionAdded
void collectionAdded(const Akonadi::Collection &)
Signal emitted when a collection has been added to the model.
AkonadiModel::ValueRole
Definition: akonadimodel.h:71
AkonadiModel::instance
static AkonadiModel * instance()
Definition: akonadimodel.cpp:83
AkonadiModel::entityColumnCount
virtual int entityColumnCount(HeaderGroup) const
Definition: akonadimodel.cpp:621
checkItem_isArchived
static bool checkItem_isArchived(const Item &item)
Definition: akonadimodel.cpp:735
AkonadiModel::ColourColumn
Definition: akonadimodel.h:55
PreferencesBase::disabledColour
static QColor disabledColour()
Get Disabled alarm color.
Definition: kalarmconfig.h:863
AlarmTime::timeToAlarmText
static QString timeToAlarmText(const KAlarmCal::DateTime &dateTime)
Definition: alarmtime.cpp:77
mainwindow.h
main application window
AkonadiModel::eventsToBeRemoved
void eventsToBeRemoved(const AkonadiModel::EventList &)
Signal emitted when events are about to be removed from the model.
AkonadiModel::SortRole
Definition: akonadimodel.h:72
CalendarMigrator::completed
static bool completed()
Definition: calendarmigrator.h:53
CalendarMigrator::instance
static CalendarMigrator * instance()
Definition: calendarmigrator.cpp:153
MinuteTimer::connect
static void connect(QObject *receiver, const char *member)
AkonadiModel::eventChanged
void eventChanged(const AkonadiModel::Event &)
Signal emitted when an event in the model has changed.
AkonadiModel::tooltip
QString tooltip(const Akonadi::Collection &, CalEvent::Types) const
Get the tooltip for a collection.
Definition: akonadimodel.cpp:894
messagebox.h
AkonadiModel::collectionModified
void collectionModified(Akonadi::Collection::Id, bool status=true)
Signal emitted when Akonadi has completed a collection modification.
checkItem_excludesHolidays
static bool checkItem_excludesHolidays(const Item &item)
Definition: akonadimodel.cpp:767
AkonadiModel::TimeToColumn
Definition: akonadimodel.h:55
AkonadiModel::removeCollection
bool removeCollection(const Akonadi::Collection &)
Remove a collection from Akonadi.
Definition: akonadimodel.cpp:1045
AkonadiModel::reloadCollection
bool reloadCollection(const Akonadi::Collection &)
Reload a collection's data from Akonadi storage (not from the backend).
Definition: akonadimodel.cpp:1103
AkonadiModel::AlarmTypes
Definition: akonadimodel.h:52
writableRights
static Collection::Rights writableRights
Definition: akonadimodel.cpp:63
AkonadiModel::addEvents
bool addEvents(const KAEvent::List &, Akonadi::Collection &)
Definition: akonadimodel.cpp:1271
AkonadiModel::data
virtual QVariant data(const QModelIndex &, int role=Qt::DisplayRole) const
Definition: akonadimodel.cpp:177
AkonadiModel::EnabledRole
Definition: akonadimodel.h:67
preferences.h
AkonadiModel::ReadOnly
Definition: akonadimodel.h:52
AkonadiModel::entityHeaderData
virtual QVariant entityHeaderData(int section, Qt::Orientation, int role, HeaderGroup) const
Definition: akonadimodel.cpp:637
AkonadiModel::ColumnCount
Definition: akonadimodel.h:57
Preferences::connect
static void connect(const char *signal, const QObject *receiver, const char *member)
Definition: preferences.cpp:292
AkonadiModel::RepeatColumn
Definition: akonadimodel.h:55
PreferencesBase::archivedColour
static QColor archivedColour()
Get Archived alarm color.
Definition: kalarmconfig.h:892
AkonadiModel::eventIndex
QModelIndex eventIndex(const KAEvent &)
Definition: akonadimodel.cpp:1154
AkonadiModel::types
static CalEvent::Types types(const Akonadi::Collection &)
Definition: akonadimodel.cpp:1863
AlarmTime::alarmTimeText
static QString alarmTimeText(const KAlarmCal::DateTime &dateTime)
Definition: alarmtime.cpp:39
checkItem_isActive
static bool checkItem_isActive(const Item &item)
Definition: akonadimodel.cpp:723
AkonadiModel::isCompatible
static bool isCompatible(const Akonadi::Collection &)
Check whether a collection is stored in the current KAlarm calendar format.
Definition: akonadimodel.cpp:1821
AkonadiModel::itemDone
void itemDone(Akonadi::Item::Id, bool status=true)
Signal emitted when Akonadi has completed an item creation, update or deletion.
AkonadiModel::BaseColourRole
Definition: akonadimodel.h:62
AkonadiModel::reload
void reload()
Reload all collections' data from Akonadi storage (not from the backend).
Definition: akonadimodel.cpp:1116
AkonadiModel::collection
Akonadi::Collection collection(const KAEvent &e) const
Definition: akonadimodel.h:124
AkonadiModel::itemById
Akonadi::Item itemById(Akonadi::Item::Id) const
Definition: akonadimodel.cpp:1802
AkonadiModel::setBackgroundColor
void setBackgroundColor(Akonadi::Collection &, const QColor &)
Set the background color for a collection and its alarms.
Definition: akonadimodel.cpp:838
AkonadiModel::AlarmActionsRole
Definition: akonadimodel.h:69
CalendarMigrator::execute
static void execute()
Definition: calendarmigrator.cpp:163
indexes
static QMap< Preferences::SoundType, int > indexes
Definition: soundpicker.cpp:45
AkonadiModel::TimeColumn
Definition: akonadimodel.h:55
AkonadiModel::migrationCompleted
void migrationCompleted()
Signal emitted when calendar migration/creation has completed.
AkonadiModel::deleteEvent
bool deleteEvent(const KAEvent &event)
Definition: akonadimodel.cpp:1342
AkonadiModel::foregroundColor
static QColor foregroundColor(const Akonadi::Collection &, const QStringList &mimeTypes)
Get the foreground color for a collection, based on specified mime types.
Definition: akonadimodel.cpp:821
MainWindow::mainMainWindow
static MainWindow * mainMainWindow()
Definition: mainwindow.cpp:288
AkonadiModel::backgroundColor
QColor backgroundColor(Akonadi::Collection &) const
Get the background color for a collection and its alarms.
Definition: akonadimodel.cpp:849
AkonadiModel::collectionForItem
Akonadi::Collection collectionForItem(Akonadi::Item::Id) const
Definition: akonadimodel.cpp:1813
KJob
AkonadiModel
Definition: akonadimodel.h:48
AkonadiModel::KeepFormatRole
Definition: akonadimodel.h:65
AkonadiModel::AlarmTypeRole
Definition: akonadimodel.h:63
AkonadiModel::addEvent
bool addEvent(KAEvent &, Akonadi::Collection &)
Definition: akonadimodel.cpp:1288
AkonadiModel::isWritable
static int isWritable(const Akonadi::Collection &)
Return whether a collection is fully writable, i.e.
Definition: akonadimodel.cpp:1830
AkonadiModel::TypeColumn
Definition: akonadimodel.h:55
QList
AkonadiModel::setData
virtual bool setData(const QModelIndex &, const QVariant &value, int role)
Definition: akonadimodel.cpp:496
AkonadiModel::updateCommandError
void updateCommandError(const KAEvent &)
To be called when the command error status of an alarm has changed, to set in the Akonadi database an...
Definition: akonadimodel.cpp:810
AkonadiModel::itemIndex
QModelIndex itemIndex(Akonadi::Item::Id id) const
Definition: akonadimodel.h:142
This file is part of the KDE documentation.
Documentation copyright © 1996-2014 The KDE developers.
Generated on Tue Oct 14 2014 22:59:09 by doxygen 1.8.7 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

kalarm

Skip menu "kalarm"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • File Members

kdepim API Reference

Skip menu "kdepim API Reference"
  • akonadi_next
  • akregator
  • blogilo
  • calendarsupport
  • console
  •   kabcclient
  •   konsolekalendar
  • kaddressbook
  • kalarm
  •   lib
  • kdgantt2
  • kjots
  • kleopatra
  • kmail
  • knode
  • knotes
  • kontact
  • korgac
  • korganizer
  • ktimetracker
  • libkdepim
  • libkleo
  • libkpgp
  • mailcommon
  • messagelist
  • messageviewer

Search



Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal