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

kalarm

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

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