• 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
alarmcalendar.cpp
Go to the documentation of this file.
1 /*
2  * alarmcalendar.cpp - KAlarm calendar file access
3  * Program: kalarm
4  * Copyright © 2001-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 "kalarm.h"
22 #include "alarmcalendar.moc"
23 
24 #ifdef USE_AKONADI
25 #include "collectionmodel.h"
26 #else
27 #include "alarmresources.h"
28 #include "calendarcompat.h"
29 #include "eventlistmodel.h"
30 #endif
31 #include "filedialog.h"
32 #include "functions.h"
33 #include "kalarmapp.h"
34 #include "mainwindow.h"
35 #include "messagebox.h"
36 #include "preferences.h"
37 
38 #ifdef USE_AKONADI
39 #include <kcalcore/memorycalendar.h>
40 #include <kcalcore/icalformat.h>
41 #else
42 #include <kcal/calendarlocal.h>
43 #include <kcal/icalformat.h>
44 #endif
45 
46 #include <kglobal.h>
47 #include <klocale.h>
48 #include <kstandarddirs.h>
49 #ifndef USE_AKONADI
50 #include <kconfig.h>
51 #endif
52 #include <kaboutdata.h>
53 #include <kio/netaccess.h>
54 #include <kfileitem.h>
55 #include <ktemporaryfile.h>
56 #include <kdebug.h>
57 
58 #ifdef USE_AKONADI
59 using namespace Akonadi;
60 using namespace KCalCore;
61 #else
62 using namespace KCal;
63 #endif
64 using namespace KAlarmCal;
65 
66 #ifdef USE_AKONADI
67 static KACalendar::Compat fix(const KCalCore::FileStorage::Ptr&);
68 #endif
69 
70 static const QString displayCalendarName = QLatin1String("displaying.ics");
71 #ifdef USE_AKONADI
72 static const Collection::Id DISPLAY_COL_ID = -1; // collection ID used for displaying calendar
73 #endif
74 
75 AlarmCalendar* AlarmCalendar::mResourcesCalendar = 0;
76 AlarmCalendar* AlarmCalendar::mDisplayCalendar = 0;
77 
78 
79 /******************************************************************************
80 * Initialise the alarm calendars, and ensure that their file names are different.
81 * There are 2 calendars:
82 * 1) A resources calendar containing the active alarms, archived alarms and
83 * alarm templates;
84 * 2) A user-specific one which contains details of alarms which are currently
85 * being displayed to that user and which have not yet been acknowledged;
86 * Reply = true if success, false if calendar name error.
87 */
88 bool AlarmCalendar::initialiseCalendars()
89 {
90  QString displayCal = KStandardDirs::locateLocal("appdata", displayCalendarName);
91 #ifdef USE_AKONADI
92  AkonadiModel::instance();
93  CollectionControlModel::setAskDestinationPolicy(Preferences::askResource());
94  Preferences::setBackend(Preferences::Akonadi);
95 #else
96  AlarmResources::setDebugArea(5951);
97  AlarmResources::setReservedFile(displayCal);
98  AlarmResources* resources = AlarmResources::create(Preferences::timeZone(true), false);
99  if (!resources)
100  {
101  KAlarmApp::displayFatalError(AlarmResources::creationError());
102  return false;
103  }
104  resources->setAskDestinationPolicy(Preferences::askResource());
105  resources->showProgress(true);
106  Preferences::setBackend(Preferences::Kresources);
107 #endif
108  Preferences::self()->writeConfig();
109  mResourcesCalendar = new AlarmCalendar();
110  mDisplayCalendar = new AlarmCalendar(displayCal, CalEvent::DISPLAYING);
111  KACalendar::setProductId(KALARM_NAME, KALARM_VERSION);
112  CalFormat::setApplication(QLatin1String(KALARM_NAME), QString::fromLatin1(KACalendar::icalProductId()));
113  return true;
114 }
115 
116 /******************************************************************************
117 * Terminate access to all calendars.
118 */
119 void AlarmCalendar::terminateCalendars()
120 {
121  delete mResourcesCalendar;
122  mResourcesCalendar = 0;
123  delete mDisplayCalendar;
124  mDisplayCalendar = 0;
125 }
126 
127 /******************************************************************************
128 * Return the display calendar, opening it first if necessary.
129 */
130 AlarmCalendar* AlarmCalendar::displayCalendarOpen()
131 {
132  if (mDisplayCalendar->open())
133  return mDisplayCalendar;
134  kError() << "Open error";
135  return 0;
136 }
137 
138 /******************************************************************************
139 * Find and return the event with the specified ID.
140 * The calendar searched is determined by the calendar identifier in the ID.
141 */
142 #ifdef USE_AKONADI
143 KAEvent* AlarmCalendar::getEvent(const EventId& eventId)
144 {
145  if (eventId.eventId().isEmpty())
146  return 0;
147  return mResourcesCalendar->event(eventId);
148 }
149 #else
150 KAEvent* AlarmCalendar::getEvent(const QString& uniqueId)
151 {
152  if (uniqueId.isEmpty())
153  return 0;
154  KAEvent* event = mResourcesCalendar->event(uniqueId);
155  if (!event)
156  event = mDisplayCalendar->event(uniqueId);
157  return event;
158 }
159 #endif
160 
161 /******************************************************************************
162 * Constructor for the resources calendar.
163 */
164 AlarmCalendar::AlarmCalendar()
165  :
166 #ifndef USE_AKONADI
167  mCalendar(0),
168 #endif
169  mCalType(RESOURCES),
170  mEventType(CalEvent::EMPTY),
171  mOpen(false),
172  mUpdateCount(0),
173  mUpdateSave(false),
174  mHaveDisabledAlarms(false)
175 {
176 #ifdef USE_AKONADI
177  AkonadiModel* model = AkonadiModel::instance();
178  connect(model, SIGNAL(eventsAdded(AkonadiModel::EventList)), SLOT(slotEventsAdded(AkonadiModel::EventList)));
179  connect(model, SIGNAL(eventsToBeRemoved(AkonadiModel::EventList)), SLOT(slotEventsToBeRemoved(AkonadiModel::EventList)));
180  connect(model, SIGNAL(eventChanged(AkonadiModel::Event)), SLOT(slotEventChanged(AkonadiModel::Event)));
181  connect(model, SIGNAL(collectionStatusChanged(Akonadi::Collection,AkonadiModel::Change,QVariant,bool)),
182  SLOT(slotCollectionStatusChanged(Akonadi::Collection,AkonadiModel::Change,QVariant,bool)));
183 #else
184  AlarmResources* resources = AlarmResources::instance();
185  resources->setCalIDFunction(&KACalendar::setKAlarmVersion);
186  resources->setFixFunction(&CalendarCompat::fix);
187  resources->setCustomEventFunction(&updateResourceKAEvents);
188  connect(resources, SIGNAL(resourceStatusChanged(AlarmResource*,AlarmResources::Change)), SLOT(slotResourceChange(AlarmResource*,AlarmResources::Change)));
189  connect(resources, SIGNAL(cacheDownloaded(AlarmResource*)), SLOT(slotCacheDownloaded(AlarmResource*)));
190  connect(resources, SIGNAL(resourceLoaded(AlarmResource*,bool)), SLOT(slotResourceLoaded(AlarmResource*,bool)));
191 #endif
192  Preferences::connect(SIGNAL(askResourceChanged(bool)), this, SLOT(setAskResource(bool)));
193 }
194 
195 /******************************************************************************
196 * Constructor for a calendar file.
197 */
198 AlarmCalendar::AlarmCalendar(const QString& path, CalEvent::Type type)
199  :
200 #ifndef USE_AKONADI
201  mCalendar(0),
202 #endif
203  mEventType(type),
204  mOpen(false),
205  mUpdateCount(0),
206  mUpdateSave(false),
207  mHaveDisabledAlarms(false)
208 {
209  switch (type)
210  {
211  case CalEvent::ACTIVE:
212  case CalEvent::ARCHIVED:
213  case CalEvent::TEMPLATE:
214  case CalEvent::DISPLAYING:
215  break;
216  default:
217  Q_ASSERT(false); // invalid event type for a calendar
218  break;
219  }
220  mUrl.setPath(path); // N.B. constructor mUrl(path) doesn't work with UNIX paths
221  QString icalPath = path;
222  icalPath.replace(QLatin1String("\\.vcs$"), QLatin1String(".ics"));
223  mICalUrl.setPath(icalPath);
224  mCalType = (path == icalPath) ? LOCAL_ICAL : LOCAL_VCAL; // is the calendar in ICal or VCal format?
225 }
226 
227 AlarmCalendar::~AlarmCalendar()
228 {
229  close();
230 }
231 
232 /******************************************************************************
233 * Check whether the calendar is open.
234 */
235 bool AlarmCalendar::isOpen()
236 {
237 #ifndef USE_AKONADI
238  if (mOpen && mCalType == RESOURCES && !AlarmResources::instance())
239  {
240  mCalendar = 0;
241  mOpen = false;
242  }
243 #endif
244  return mOpen;
245 }
246 
247 /******************************************************************************
248 * Open the calendar if not already open, and load it into memory.
249 */
250 bool AlarmCalendar::open()
251 {
252  if (isOpen())
253  return true;
254  if (mCalType == RESOURCES)
255  {
256 #ifdef USE_AKONADI
257  mOpen = true;
258 #else
259  kDebug() << "RESOURCES";
260  mCalendar = AlarmResources::instance();
261  load();
262 #endif
263  }
264  else
265  {
266  if (!mUrl.isValid())
267  return false;
268 
269  kDebug() << mUrl.prettyUrl();
270 #ifdef USE_AKONADI
271  if (!mCalendarStorage)
272  {
273  MemoryCalendar::Ptr calendar(new MemoryCalendar(Preferences::timeZone(true)));
274  mCalendarStorage = FileStorage::Ptr(new FileStorage(calendar));
275  }
276 #else
277  if (!mCalendar)
278  mCalendar = new CalendarLocal(Preferences::timeZone(true));
279 #endif
280 
281  // Check for file's existence, assuming that it does exist when uncertain,
282  // to avoid overwriting it.
283  if (!KIO::NetAccess::exists(mUrl, KIO::NetAccess::SourceSide, MainWindow::mainMainWindow())
284  || load() == 0)
285  {
286  // The calendar file doesn't yet exist, or it's zero length, so create a new one
287  bool created = false;
288  if (mICalUrl.isLocalFile())
289  created = saveCal(mICalUrl.toLocalFile());
290  else
291  {
292  KTemporaryFile tmpFile;
293  tmpFile.setAutoRemove(false);
294  tmpFile.open();
295  created = saveCal(tmpFile.fileName());
296  }
297  if (created)
298  load();
299  }
300  }
301  if (!mOpen)
302  {
303 #ifdef USE_AKONADI
304  mCalendarStorage->calendar().clear();
305  mCalendarStorage.clear();
306 #else
307  delete mCalendar;
308  mCalendar = 0;
309 #endif
310  }
311  return isOpen();
312 }
313 
314 /******************************************************************************
315 * Load the calendar into memory.
316 * Reply = 1 if success
317 * = 0 if zero-length file exists.
318 * = -1 if failure to load calendar file
319 * = -2 if instance uninitialised.
320 */
321 int AlarmCalendar::load()
322 {
323  if (mCalType == RESOURCES)
324  {
325 #ifndef USE_AKONADI
326  kDebug() << "RESOURCES";
327  static_cast<AlarmResources*>(mCalendar)->load();
328 #endif
329  }
330  else
331  {
332 #ifdef USE_AKONADI
333  if (!mCalendarStorage)
334  return -2;
335 #else
336  if (!mCalendar)
337  return -2;
338  CalendarLocal* calendar = static_cast<CalendarLocal*>(mCalendar);
339 #endif
340 
341  kDebug() << mUrl.prettyUrl();
342  QString tmpFile;
343  if (!KIO::NetAccess::download(mUrl, tmpFile, MainWindow::mainMainWindow()))
344  {
345  kError() << "Download failure";
346  KAMessageBox::error(MainWindow::mainMainWindow(),
347  i18nc("@info", "Cannot download calendar: <filename>%1</filename>", mUrl.prettyUrl()));
348  return -1;
349  }
350  kDebug() << "--- Downloaded to" << tmpFile;
351 #ifdef USE_AKONADI
352  mCalendarStorage->calendar()->setTimeSpec(Preferences::timeZone(true));
353  mCalendarStorage->setFileName(tmpFile);
354  if (!mCalendarStorage->load())
355 #else
356  calendar->setTimeSpec(Preferences::timeZone(true));
357  if (!calendar->load(tmpFile))
358 #endif
359  {
360  // Check if the file is zero length
361  KIO::NetAccess::removeTempFile(tmpFile);
362  KIO::UDSEntry uds;
363  KIO::NetAccess::stat(mUrl, uds, MainWindow::mainMainWindow());
364  KFileItem fi(uds, mUrl);
365  if (!fi.size())
366  return 0; // file is zero length
367  kError() << "Error loading calendar file '" << tmpFile <<"'";
368  KAMessageBox::error(MainWindow::mainMainWindow(),
369  i18nc("@info", "<para>Error loading calendar:</para><para><filename>%1</filename></para><para>Please fix or delete the file.</para>", mUrl.prettyUrl()));
370  // load() could have partially populated the calendar, so clear it out
371 #ifdef USE_AKONADI
372  mCalendarStorage->calendar()->close();
373  mCalendarStorage->calendar().clear();
374  mCalendarStorage.clear();
375 #else
376  calendar->close();
377  delete mCalendar;
378  mCalendar = 0;
379 #endif
380  mOpen = false;
381  return -1;
382  }
383  if (!mLocalFile.isEmpty())
384  KIO::NetAccess::removeTempFile(mLocalFile); // removes it only if it IS a temporary file
385  mLocalFile = tmpFile;
386 #ifdef USE_AKONADI
387  fix(mCalendarStorage); // convert events to current KAlarm format for when calendar is saved
388  updateDisplayKAEvents();
389 #else
390  CalendarCompat::fix(*calendar, mLocalFile); // convert events to current KAlarm format for when calendar is saved
391  updateKAEvents(0, calendar);
392 #endif
393  }
394  mOpen = true;
395  return 1;
396 }
397 
398 /******************************************************************************
399 * Reload the calendar file into memory.
400 */
401 bool AlarmCalendar::reload()
402 {
403 #ifdef USE_AKONADI
404  if (mCalType == RESOURCES)
405  return true;
406  if (!mCalendarStorage)
407  return false;
408 #else
409  if (!mCalendar)
410  return false;
411  if (mCalType == RESOURCES)
412  {
413  kDebug() << "RESOURCES";
414  return mCalendar->reload();
415  }
416  else
417 #endif
418  {
419  kDebug() << mUrl.prettyUrl();
420  close();
421  return open();
422  }
423 }
424 
425 /******************************************************************************
426 * Save the calendar from memory to file.
427 * If a filename is specified, create a new calendar file.
428 */
429 bool AlarmCalendar::saveCal(const QString& newFile)
430 {
431 #ifdef USE_AKONADI
432  if (mCalType == RESOURCES)
433  return true;
434  if (!mCalendarStorage)
435  return false;
436 #else
437  if (!mCalendar)
438  return false;
439  if (mCalType == RESOURCES)
440  {
441  kDebug() << "RESOURCES";
442  mCalendar->save(); // this emits signals resourceSaved(ResourceCalendar*)
443  }
444  else
445 #endif
446  {
447  if (!mOpen && newFile.isNull())
448  return false;
449 
450  kDebug() << "\"" << newFile << "\"," << mEventType;
451  QString saveFilename = newFile.isNull() ? mLocalFile : newFile;
452  if (mCalType == LOCAL_VCAL && newFile.isNull() && mUrl.isLocalFile())
453  saveFilename = mICalUrl.toLocalFile();
454 #ifdef USE_AKONADI
455  mCalendarStorage->setFileName(saveFilename);
456  mCalendarStorage->setSaveFormat(new ICalFormat);
457  if (!mCalendarStorage->save())
458 #else
459  if (!static_cast<CalendarLocal*>(mCalendar)->save(saveFilename, new ICalFormat))
460 #endif
461  {
462  kError() << "Saving" << saveFilename << "failed.";
463  KAMessageBox::error(MainWindow::mainMainWindow(),
464  i18nc("@info", "Failed to save calendar to <filename>%1</filename>", mICalUrl.prettyUrl()));
465  return false;
466  }
467 
468  if (!mICalUrl.isLocalFile())
469  {
470  if (!KIO::NetAccess::upload(saveFilename, mICalUrl, MainWindow::mainMainWindow()))
471  {
472  kError() << saveFilename << "upload failed.";
473  KAMessageBox::error(MainWindow::mainMainWindow(),
474  i18nc("@info", "Cannot upload calendar to <filename>%1</filename>", mICalUrl.prettyUrl()));
475  return false;
476  }
477  }
478 
479  if (mCalType == LOCAL_VCAL)
480  {
481  // The file was in vCalendar format, but has now been saved in iCalendar format.
482  mUrl = mICalUrl;
483  mCalType = LOCAL_ICAL;
484  }
485  emit calendarSaved(this);
486  }
487 
488  mUpdateSave = false;
489  return true;
490 }
491 
492 /******************************************************************************
493 * Delete any temporary file at program exit.
494 */
495 void AlarmCalendar::close()
496 {
497  if (mCalType != RESOURCES)
498  {
499  if (!mLocalFile.isEmpty())
500  {
501  KIO::NetAccess::removeTempFile(mLocalFile); // removes it only if it IS a temporary file
502  mLocalFile = QLatin1String("");
503  }
504  }
505  // Flag as closed now to prevent removeKAEvents() doing silly things
506  // when it's called again
507  mOpen = false;
508 #ifdef USE_AKONADI
509  if (mCalendarStorage)
510  {
511  mCalendarStorage->calendar()->close();
512  mCalendarStorage->calendar().clear();
513  mCalendarStorage.clear();
514  }
515 #else
516  if (mCalendar)
517  {
518  mCalendar->close();
519  delete mCalendar;
520  mCalendar = 0;
521  }
522 #endif
523  // Resource map should be empty, but just in case...
524  while (!mResourceMap.isEmpty())
525 #ifdef USE_AKONADI
526  removeKAEvents(mResourceMap.begin().key(), true, CalEvent::ACTIVE | CalEvent::ARCHIVED | CalEvent::TEMPLATE | CalEvent::DISPLAYING);
527 #else
528  removeKAEvents(mResourceMap.begin().key(), true);
529 #endif
530 }
531 
532 #ifndef USE_AKONADI
533 /******************************************************************************
534 * Load a single resource. If the resource is cached, the cache is refreshed.
535 */
536 void AlarmCalendar::loadResource(AlarmResource* resource, QWidget*)
537 {
538  if (!AlarmResources::instance()->load(resource, ResourceCached::SyncCache))
539  slotResourceLoaded(resource, false);
540 }
541 
542 /******************************************************************************
543 * Called when a remote resource cache has completed loading.
544 */
545 void AlarmCalendar::slotCacheDownloaded(AlarmResource* resource)
546 {
547  slotResourceLoaded(resource, false);
548 }
549 #endif
550 
551 /******************************************************************************
552 * Update whether to prompt for the resource to store new alarms in.
553 */
554 void AlarmCalendar::setAskResource(bool ask)
555 {
556 #ifdef USE_AKONADI
557  CollectionControlModel::setAskDestinationPolicy(ask);
558 #else
559  AlarmResources::instance()->setAskDestinationPolicy(ask);
560 #endif
561 }
562 
563 #ifndef USE_AKONADI
564 /******************************************************************************
565 * Create a KAEvent instance corresponding to each KCal::Event in a resource.
566 * Called after the resource has completed loading.
567 * The event list is simply cleared if 'cal' is null.
568 */
569 void AlarmCalendar::updateResourceKAEvents(AlarmResource* resource, KCal::CalendarLocal* cal)
570 {
571  mResourcesCalendar->updateKAEvents(resource, cal);
572 }
573 #endif
574 
575 #ifdef USE_AKONADI
576 void AlarmCalendar::updateDisplayKAEvents()
577 #else
578 void AlarmCalendar::updateKAEvents(AlarmResource* resource, KCal::CalendarLocal* cal)
579 #endif
580 {
581 #ifdef USE_AKONADI
582  if (mCalType == RESOURCES)
583  return;
584  kDebug();
585  const Collection::Id key = DISPLAY_COL_ID;
586 #else
587  kDebug() << (resource ? resource->resourceName() : "0");
588  AlarmResource* key = resource;
589 #endif
590  KAEvent::List& events = mResourceMap[key];
591  int i, end;
592  for (i = 0, end = events.count(); i < end; ++i)
593  {
594  KAEvent* event = events[i];
595 #ifdef USE_AKONADI
596  mEventMap.remove(EventId(key, event->id()));
597 #else
598  mEventMap.remove(event->id());
599 #endif
600  delete event;
601  }
602  events.clear();
603  mEarliestAlarm[key] = 0;
604 #ifdef USE_AKONADI
605  Calendar::Ptr cal = mCalendarStorage->calendar();
606 #endif
607  if (!cal)
608  return;
609 
610 #ifndef USE_AKONADI
611  KConfigGroup config(KGlobal::config(), KAEvent::commandErrorConfigGroup());
612 #endif
613  Event::List kcalevents = cal->rawEvents();
614  for (i = 0, end = kcalevents.count(); i < end; ++i)
615  {
616 #ifdef USE_AKONADI
617  Event::Ptr kcalevent = kcalevents[i];
618 #else
619  const Event* kcalevent = kcalevents[i];
620 #endif
621  if (kcalevent->alarms().isEmpty())
622  continue; // ignore events without alarms
623 
624  KAEvent* event = new KAEvent(kcalevent);
625  if (!event->isValid())
626  {
627  kWarning() << "Ignoring unusable event" << kcalevent->uid();
628  delete event;
629  continue; // ignore events without usable alarms
630  }
631 #ifdef USE_AKONADI
632  event->setCollectionId(key);
633  events += event;
634  mEventMap[EventId(key, kcalevent->uid())] = event;
635 #else
636  event->setResource(resource);
637  events += event;
638  mEventMap[kcalevent->uid()] = event;
639 
640  // Set any command execution error flags for the alarm.
641  // These are stored in the KAlarm config file, not the alarm
642  // calendar, since they are specific to the user's local system.
643  QString cmdErr = config.readEntry(event->id());
644  if (!cmdErr.isEmpty())
645  event->setCommandError(cmdErr);
646 #endif
647  }
648 
649 #ifndef USE_AKONADI
650  // Now scan the list of alarms to find the earliest one to trigger
651  findEarliestAlarm(resource);
652  checkForDisabledAlarms();
653 #endif
654 }
655 
656 #ifdef USE_AKONADI
657 /******************************************************************************
658 * Delete a calendar and all its KAEvent instances of specified alarm types from
659 * the lists.
660 * Called after the calendar is deleted or alarm types have been disabled, or
661 * the AlarmCalendar is closed.
662 */
663 void AlarmCalendar::removeKAEvents(Collection::Id key, bool closing, CalEvent::Types types)
664 {
665  bool removed = false;
666  ResourceMap::Iterator rit = mResourceMap.find(key);
667  if (rit != mResourceMap.end())
668  {
669  bool empty = true;
670  KAEvent::List& events = rit.value();
671  for (int i = 0, end = events.count(); i < end; ++i)
672  {
673  KAEvent* event = events[i];
674  bool remove = (event->collectionId() != key);
675  if (remove)
676  {
677  if (key != DISPLAY_COL_ID)
678  kError() << "Event" << event->id() << ", collection" << event->collectionId() << "Indexed under collection" << key;
679  }
680  else
681  remove = event->category() & types;
682  if (remove)
683  {
684  mEventMap.remove(EventId(key, event->id()));
685  delete event;
686  removed = true;
687  }
688  else
689  empty = false;
690  }
691  if (empty)
692  mResourceMap.erase(rit);
693  }
694  if (removed)
695  {
696  mEarliestAlarm.remove(key);
697  // Emit signal only if we're not in the process of closing the calendar
698  if (!closing && mOpen)
699  {
700  emit earliestAlarmChanged();
701  if (mHaveDisabledAlarms)
702  checkForDisabledAlarms();
703  }
704  }
705 }
706 #else
707 /******************************************************************************
708 * Delete a calendar and all its KAEvent instances from the lists.
709 * Called after the calendar is deleted or disabled, or the AlarmCalendar is
710 * closed.
711 */
712 void AlarmCalendar::removeKAEvents(AlarmResource* key, bool closing)
713 {
714  ResourceMap::Iterator rit = mResourceMap.find(key);
715  if (rit != mResourceMap.end())
716  {
717  KAEvent::List& events = rit.value();
718  for (int i = 0, end = events.count(); i < end; ++i)
719  {
720  KAEvent* event = events[i];
721  mEventMap.remove(event->id());
722  delete event;
723  }
724  mResourceMap.erase(rit);
725  }
726  mEarliestAlarm.remove(key);
727  // Emit signal only if we're not in the process of closing the calendar
728  if (!closing && mOpen)
729  {
730  emit earliestAlarmChanged();
731  if (mHaveDisabledAlarms)
732  checkForDisabledAlarms();
733  }
734 }
735 #endif
736 
737 #ifdef USE_AKONADI
738 /******************************************************************************
739 * Called when the enabled or read-only status of a collection has changed.
740 * If the collection is now disabled, remove its events from the calendar.
741 */
742 void AlarmCalendar::slotCollectionStatusChanged(const Collection& collection, AkonadiModel::Change change, const QVariant& value, bool inserted)
743 {
744  if (!inserted && change == AkonadiModel::Enabled)
745  {
746  // For each alarm type which has been disabled, remove the collection's
747  // events from the map, but not from AkonadiModel.
748  CalEvent::Types enabled = static_cast<CalEvent::Types>(value.toInt());
749  CalEvent::Types disabled = ~enabled & (CalEvent::ACTIVE | CalEvent::ARCHIVED | CalEvent::TEMPLATE);
750  removeKAEvents(collection.id(), false, disabled);
751  }
752 }
753 
754 /******************************************************************************
755 * Called when events have been added to AkonadiModel.
756 * Add corresponding KAEvent instances to those held by AlarmCalendar.
757 */
758 void AlarmCalendar::slotEventsAdded(const AkonadiModel::EventList& events)
759 {
760  for (int i = 0, count = events.count(); i < count; ++i)
761  slotEventChanged(events[i]);
762 }
763 
764 /******************************************************************************
765 * Called when an event has been changed in AkonadiModel.
766 * Change the corresponding KAEvent instance held by AlarmCalendar.
767 */
768 void AlarmCalendar::slotEventChanged(const AkonadiModel::Event& event)
769 {
770  if (!event.isConsistent())
771  {
772  kError() << "Inconsistent AkonadiModel::Event: event:" << event.event.collectionId() << ", collection" << event.collection.id();
773  return;
774  }
775 
776  bool added = true;
777  bool updated = false;
778  KAEventMap::Iterator it = mEventMap.find(event.eventId());
779  if (it != mEventMap.end())
780  {
781  // The event ID already exists - remove the existing event first
782  KAEvent* storedEvent = it.value();
783  if (event.event.category() == storedEvent->category())
784  {
785  // The existing event is the same type - update it in place
786  *storedEvent = event.event;
787  addNewEvent(event.collection, storedEvent, true);
788  updated = true;
789  }
790  else
791  delete storedEvent;
792  added = false;
793  }
794  if (!updated)
795  addNewEvent(event.collection, new KAEvent(event.event));
796 
797  bool enabled = event.event.enabled();
798  checkForDisabledAlarms(!enabled, enabled);
799  if (added && enabled && event.event.category() == CalEvent::ACTIVE
800  && event.event.repeatAtLogin())
801  emit atLoginEventAdded(event.event);
802 }
803 
804 /******************************************************************************
805 * Called when events are about to be removed from AkonadiModel.
806 * Remove the corresponding KAEvent instances held by AlarmCalendar.
807 */
808 void AlarmCalendar::slotEventsToBeRemoved(const AkonadiModel::EventList& events)
809 {
810  for (int i = 0, count = events.count(); i < count; ++i)
811  {
812  if (!events[i].isConsistent())
813  kError() << "Inconsistent AkonadiModel::Event: event:" << events[i].event.collectionId() << ", collection" << events[i].collection.id();
814  else if (mEventMap.contains(events[i].eventId()))
815  deleteEventInternal(events[i].event, events[i].collection, false);
816  }
817 }
818 #else
819 
820 void AlarmCalendar::slotResourceChange(AlarmResource* resource, AlarmResources::Change change)
821 {
822  switch (change)
823  {
824  case AlarmResources::Enabled:
825  if (resource->isActive())
826  return;
827  kDebug() << "Enabled (inactive)";
828  break;
829  case AlarmResources::Invalidated:
830  kDebug() << "Invalidated";
831  break;
832  case AlarmResources::Deleted:
833  kDebug() << "Deleted";
834  break;
835  default:
836  return;
837  }
838  // Ensure the data model is notified before deleting the KAEvent instances
839  EventListModel::resourceStatusChanged(resource, change);
840  removeKAEvents(resource);
841 }
842 
843 /******************************************************************************
844 * Called when a resource has completed loading.
845 */
846 void AlarmCalendar::slotResourceLoaded(AlarmResource* resource, bool success)
847 {
848 }
849 
850 /******************************************************************************
851 * Reload a resource from its cache file, without refreshing the cache first.
852 */
853 void AlarmCalendar::reloadFromCache(const QString& resourceID)
854 {
855  kDebug() << resourceID;
856  if (mCalendar && mCalType == RESOURCES)
857  {
858  AlarmResource* resource = static_cast<AlarmResources*>(mCalendar)->resourceWithId(resourceID);
859  if (resource)
860  resource->load(ResourceCached::NoSyncCache); // reload from cache
861  }
862 }
863 #endif
864 
865 /******************************************************************************
866 * Import alarms from an external calendar and merge them into KAlarm's calendar.
867 * The alarms are given new unique event IDs.
868 * Parameters: parent = parent widget for error message boxes
869 * Reply = true if all alarms in the calendar were successfully imported
870 * = false if any alarms failed to be imported.
871 */
872 #ifdef USE_AKONADI
873 bool AlarmCalendar::importAlarms(QWidget* parent, Collection* collection)
874 #else
875 bool AlarmCalendar::importAlarms(QWidget* parent, AlarmResource* resource)
876 #endif
877 {
878  kDebug();
879  KUrl url = KFileDialog::getOpenUrl(KUrl("filedialog:///importalarms"),
880  QString::fromLatin1("*.vcs *.ics|%1").arg(i18nc("@info/plain", "Calendar Files")), parent);
881  if (url.isEmpty())
882  {
883  kError() << "Empty URL";
884  return false;
885  }
886  if (!url.isValid())
887  {
888  kDebug() << "Invalid URL";
889  return false;
890  }
891  kDebug() << url.prettyUrl();
892 
893  bool success = true;
894  QString filename;
895  bool local = url.isLocalFile();
896  if (local)
897  {
898  filename = url.toLocalFile();
899  if (!KStandardDirs::exists(filename))
900  {
901  kDebug() << "File '" << url.prettyUrl() <<"' not found";
902  KAMessageBox::error(parent, i18nc("@info", "Could not load calendar <filename>%1</filename>.", url.prettyUrl()));
903  return false;
904  }
905  }
906  else
907  {
908  if (!KIO::NetAccess::download(url, filename, MainWindow::mainMainWindow()))
909  {
910  kError() << "Download failure";
911  KAMessageBox::error(parent, i18nc("@info", "Cannot download calendar: <filename>%1</filename>", url.prettyUrl()));
912  return false;
913  }
914  kDebug() << "--- Downloaded to" << filename;
915  }
916 
917  // Read the calendar and add its alarms to the current calendars
918 #ifdef USE_AKONADI
919  MemoryCalendar::Ptr cal(new MemoryCalendar(Preferences::timeZone(true)));
920  FileStorage::Ptr calStorage(new FileStorage(cal, filename));
921  success = calStorage->load();
922 #else
923  CalendarLocal cal(Preferences::timeZone(true));
924  success = cal.load(filename);
925 #endif
926  if (!success)
927  {
928  kDebug() << "Error loading calendar '" << filename <<"'";
929  KAMessageBox::error(parent, i18nc("@info", "Could not load calendar <filename>%1</filename>.", url.prettyUrl()));
930  }
931  else
932  {
933 #ifdef USE_AKONADI
934  KACalendar::Compat caltype = fix(calStorage);
935  CalEvent::Types wantedTypes = collection && collection->isValid() ? CalEvent::types(collection->contentMimeTypes()) : CalEvent::EMPTY;
936  Collection activeColl, archiveColl, templateColl;
937 #else
938  KACalendar::Compat caltype = CalendarCompat::fix(cal, filename);
939  CalEvent::Type wantedType = resource ? resource->alarmType() : CalEvent::EMPTY;
940  AlarmResources* resources = AlarmResources::instance();
941  AlarmResource* activeRes = 0;
942  AlarmResource* archivedRes = 0;
943  AlarmResource* templateRes = 0;
944  bool saveRes = false;
945  bool enabled = true;
946 #endif
947  KAEvent::List newEvents;
948 #ifdef USE_AKONADI
949  Event::List events = cal->rawEvents();
950 #else
951  Event::List events = cal.rawEvents();
952 #endif
953  for (int i = 0, end = events.count(); i < end; ++i)
954  {
955 #ifdef USE_AKONADI
956  Event::Ptr event = events[i];
957 #else
958  const Event* event = events[i];
959 #endif
960  if (event->alarms().isEmpty() || !KAEvent(event).isValid())
961  continue; // ignore events without alarms, or usable alarms
962  CalEvent::Type type = CalEvent::status(event);
963  if (type == CalEvent::TEMPLATE)
964  {
965  // If we know the event was not created by KAlarm, don't treat it as a template
966  if (caltype == KACalendar::Incompatible)
967  type = CalEvent::ACTIVE;
968  }
969 #ifdef USE_AKONADI
970  Collection* coll;
971  if (collection && collection->isValid())
972  {
973  if (!(type & wantedTypes))
974  continue;
975  coll = collection;
976  }
977  else
978  {
979  switch (type)
980  {
981  case CalEvent::ACTIVE: coll = &activeColl; break;
982  case CalEvent::ARCHIVED: coll = &archiveColl; break;
983  case CalEvent::TEMPLATE: coll = &templateColl; break;
984  default: continue;
985  }
986  if (!coll->isValid())
987  *coll = CollectionControlModel::destination(type);
988  }
989 
990  Event::Ptr newev(new Event(*event));
991 #else
992  AlarmResource** res;
993  if (resource)
994  {
995  if (type != wantedType)
996  continue;
997  res = &resource;
998  }
999  else
1000  {
1001  switch (type)
1002  {
1003  case CalEvent::ACTIVE: res = &activeRes; break;
1004  case CalEvent::ARCHIVED: res = &archivedRes; break;
1005  case CalEvent::TEMPLATE: res = &templateRes; break;
1006  default: continue;
1007  }
1008  if (!*res)
1009  *res = resources->destination(type);
1010  }
1011 
1012  Event* newev = new Event(*event);
1013 #endif
1014 
1015  // If there is a display alarm without display text, use the event
1016  // summary text instead.
1017  if (type == CalEvent::ACTIVE && !newev->summary().isEmpty())
1018  {
1019  const Alarm::List& alarms = newev->alarms();
1020  for (int ai = 0, aend = alarms.count(); ai < aend; ++ai)
1021  {
1022 #ifdef USE_AKONADI
1023  Alarm::Ptr alarm = alarms[ai];
1024 #else
1025  Alarm* alarm = alarms[ai];
1026 #endif
1027  if (alarm->type() == Alarm::Display && alarm->text().isEmpty())
1028  alarm->setText(newev->summary());
1029  }
1030  newev->setSummary(QString()); // KAlarm only uses summary for template names
1031  }
1032 
1033  // Give the event a new ID and add it to the calendars
1034  newev->setUid(CalEvent::uid(CalFormat::createUniqueId(), type));
1035 #ifdef USE_AKONADI
1036  KAEvent* newEvent = new KAEvent(newev);
1037  if (!AkonadiModel::instance()->addEvent(*newEvent, *coll))
1038  success = false;
1039 #else
1040  if (resources->addEvent(newev, *res))
1041  {
1042  saveRes = true;
1043  KAEvent* ev = mResourcesCalendar->addEvent(*res, newev);
1044  if (type != CalEvent::TEMPLATE)
1045  newEvents += ev;
1046  if (type == CalEvent::ACTIVE && !ev->enabled())
1047  enabled = false;
1048  }
1049  else
1050  success = false;
1051 #endif
1052  }
1053 
1054 #ifndef USE_AKONADI
1055  // Save the resources if they have been modified
1056  if (saveRes)
1057  {
1058  resources->save();
1059  EventListModel::alarms()->addEvents(newEvents);
1060  if (!enabled)
1061  mResourcesCalendar->checkForDisabledAlarms(true, enabled);
1062  }
1063 #endif
1064  }
1065  if (!local)
1066  KIO::NetAccess::removeTempFile(filename);
1067  return success;
1068 }
1069 
1070 /******************************************************************************
1071 * Export all selected alarms to an external calendar.
1072 * The alarms are given new unique event IDs.
1073 * Parameters: parent = parent widget for error message boxes
1074 * Reply = true if all alarms in the calendar were successfully exported
1075 * = false if any alarms failed to be exported.
1076 */
1077 bool AlarmCalendar::exportAlarms(const KAEvent::List& events, QWidget* parent)
1078 {
1079  bool append;
1080  QString file = FileDialog::getSaveFileName(KUrl("kfiledialog:///exportalarms"),
1081  QString::fromLatin1("*.ics|%1").arg(i18nc("@info/plain", "Calendar Files")),
1082  parent, i18nc("@title:window", "Choose Export Calendar"),
1083  &append);
1084  if (file.isEmpty())
1085  return false;
1086  KUrl url;
1087  url.setPath(file);
1088  if (!url.isValid())
1089  {
1090  kDebug() << "Invalid URL";
1091  return false;
1092  }
1093  kDebug() << url.prettyUrl();
1094 
1095 #ifdef USE_AKONADI
1096  MemoryCalendar::Ptr calendar(new MemoryCalendar(Preferences::timeZone(true)));
1097  FileStorage::Ptr calStorage(new FileStorage(calendar, file));
1098  if (append && !calStorage->load())
1099 #else
1100  CalendarLocal calendar(Preferences::timeZone(true));
1101  if (append && !calendar.load(file))
1102 #endif
1103  {
1104  KIO::UDSEntry uds;
1105  KIO::NetAccess::stat(url, uds, parent);
1106  KFileItem fi(uds, url);
1107  if (fi.size())
1108  {
1109  kError() << "Error loading calendar file" << file << "for append";
1110  KAMessageBox::error(MainWindow::mainMainWindow(),
1111  i18nc("@info", "Error loading calendar to append to:<nl/><filename>%1</filename>", url.prettyUrl()));
1112  return false;
1113  }
1114  }
1115  KACalendar::setKAlarmVersion(calendar);
1116 
1117  // Add the alarms to the calendar
1118  bool success = true;
1119  bool exported = false;
1120  for (int i = 0, end = events.count(); i < end; ++i)
1121  {
1122  const KAEvent* event = events[i];
1123 #ifdef USE_AKONADI
1124  Event::Ptr kcalEvent(new Event);
1125 #else
1126  Event* kcalEvent = new Event;
1127 #endif
1128  CalEvent::Type type = event->category();
1129  QString id = CalEvent::uid(kcalEvent->uid(), type);
1130  kcalEvent->setUid(id);
1131  event->updateKCalEvent(kcalEvent, KAEvent::UID_IGNORE);
1132 #ifdef USE_AKONADI
1133  if (calendar->addEvent(kcalEvent))
1134 #else
1135  if (calendar.addEvent(kcalEvent))
1136 #endif
1137  exported = true;
1138  else
1139  success = false;
1140  }
1141 
1142  if (exported)
1143  {
1144  // One or more alarms have been exported to the calendar.
1145  // Save the calendar to file.
1146  KTemporaryFile* tempFile = 0;
1147  bool local = url.isLocalFile();
1148  if (!local)
1149  {
1150  tempFile = new KTemporaryFile;
1151  file = tempFile->fileName();
1152  }
1153 #ifdef USE_AKONADI
1154  calStorage->setFileName(file);
1155  calStorage->setSaveFormat(new ICalFormat);
1156  if (!calStorage->save())
1157 #else
1158  if (!calendar.save(file, new ICalFormat))
1159 #endif
1160  {
1161  kError() << file << ": failed";
1162  KAMessageBox::error(MainWindow::mainMainWindow(),
1163  i18nc("@info", "Failed to save new calendar to:<nl/><filename>%1</filename>", url.prettyUrl()));
1164  success = false;
1165  }
1166  else if (!local && !KIO::NetAccess::upload(file, url, parent))
1167  {
1168  kError() << file << ": upload failed";
1169  KAMessageBox::error(MainWindow::mainMainWindow(),
1170  i18nc("@info", "Cannot upload new calendar to:<nl/><filename>%1</filename>", url.prettyUrl()));
1171  success = false;
1172  }
1173  delete tempFile;
1174  }
1175 #ifdef USE_AKONADI
1176  calendar->close();
1177 #else
1178  calendar.close();
1179 #endif
1180  return success;
1181 }
1182 
1183 /******************************************************************************
1184 * Flag the start of a group of calendar update calls.
1185 * The purpose is to avoid multiple calendar saves during a group of operations.
1186 */
1187 void AlarmCalendar::startUpdate()
1188 {
1189  ++mUpdateCount;
1190 }
1191 
1192 /******************************************************************************
1193 * Flag the end of a group of calendar update calls.
1194 * The calendar is saved if appropriate.
1195 */
1196 bool AlarmCalendar::endUpdate()
1197 {
1198  if (mUpdateCount > 0)
1199  --mUpdateCount;
1200  if (!mUpdateCount)
1201  {
1202  if (mUpdateSave)
1203  return saveCal();
1204  }
1205  return true;
1206 }
1207 
1208 /******************************************************************************
1209 * Save the calendar, or flag it for saving if in a group of calendar update calls.
1210 * Note that this method has no effect for Akonadi calendars.
1211 */
1212 bool AlarmCalendar::save()
1213 {
1214  if (mUpdateCount)
1215  {
1216  mUpdateSave = true;
1217  return true;
1218  }
1219  else
1220  return saveCal();
1221 }
1222 
1223 /******************************************************************************
1224 * This method must only be called from the main KAlarm queue processing loop,
1225 * to prevent asynchronous calendar operations interfering with one another.
1226 *
1227 * Purge a list of archived events from the calendar.
1228 */
1229 void AlarmCalendar::purgeEvents(const KAEvent::List& events)
1230 {
1231  for (int i = 0, end = events.count(); i < end; ++i)
1232  {
1233 #ifdef USE_AKONADI
1234  deleteEventInternal(*events[i]);
1235 #else
1236  deleteEventInternal(events[i]->id());
1237 #endif
1238  }
1239  if (mHaveDisabledAlarms)
1240  checkForDisabledAlarms();
1241  saveCal();
1242 }
1243 
1244 /******************************************************************************
1245 * Add the specified event to the calendar.
1246 * If it is an active event and 'useEventID' is false, a new event ID is
1247 * created. In all other cases, the event ID is taken from 'event' (if non-null).
1248 * 'event' is updated with the actual event ID.
1249 * The event is added to 'resource' if specified; otherwise the default resource
1250 * is used or the user is prompted, depending on policy. If 'noPrompt' is true,
1251 * the user will not be prompted so that if no default resource is defined, the
1252 * function will fail.
1253 * Reply = true if 'event' was written to the calendar, in which case (not
1254 * Akonadi) ownership of 'event' is taken by the calendar. 'event'
1255 * is updated.
1256 * = false if an error occurred, in which case 'event' is unchanged.
1257 */
1258 #ifdef USE_AKONADI
1259 bool AlarmCalendar::addEvent(KAEvent& evnt, QWidget* promptParent, bool useEventID, Collection* collection, bool noPrompt, bool* cancelled)
1260 #else
1261 bool AlarmCalendar::addEvent(KAEvent* event, QWidget* promptParent, bool useEventID, AlarmResource* resource, bool noPrompt, bool* cancelled)
1262 #endif
1263 {
1264  if (cancelled)
1265  *cancelled = false;
1266  if (!mOpen)
1267  return false;
1268  // Check that the event type is valid for the calendar
1269 #ifdef USE_AKONADI
1270  kDebug() << evnt.id();
1271  CalEvent::Type type = evnt.category();
1272 #else
1273  kDebug() << event->id();
1274  CalEvent::Type type = event->category();
1275 #endif
1276  if (type != mEventType)
1277  {
1278  switch (type)
1279  {
1280  case CalEvent::ACTIVE:
1281  case CalEvent::ARCHIVED:
1282  case CalEvent::TEMPLATE:
1283  if (mEventType == CalEvent::EMPTY)
1284  break;
1285  // fall through to default
1286  default:
1287  return false;
1288  }
1289  }
1290 
1291 #ifdef USE_AKONADI
1292  Collection::Id key = (collection && collection->isValid()) ? collection->id() : -1;
1293  Event::Ptr kcalEvent((mCalType == RESOURCES) ? (Event*)0 : new Event);
1294  KAEvent* event = new KAEvent(evnt);
1295 #else
1296  AlarmResource* key = resource;
1297  Event* kcalEvent = new Event;
1298  KAEvent oldEvent(*event); // so that we can reinstate it if there's an error
1299 #endif
1300  QString id = event->id();
1301  if (type == CalEvent::ACTIVE)
1302  {
1303  if (id.isEmpty())
1304  useEventID = false;
1305  else if (!useEventID)
1306  id.clear();
1307  }
1308  else
1309  useEventID = true;
1310  if (id.isEmpty())
1311 #ifdef USE_AKONADI
1312  id = (mCalType == RESOURCES) ? CalFormat::createUniqueId() : kcalEvent->uid();
1313 #else
1314  id = kcalEvent->uid();
1315 #endif
1316  if (useEventID)
1317  {
1318  id = CalEvent::uid(id, type);
1319 #ifdef USE_AKONADI
1320  if (kcalEvent)
1321 #endif
1322  kcalEvent->setUid(id);
1323  }
1324  event->setEventId(id);
1325 #ifndef USE_AKONADI
1326  event->updateKCalEvent(kcalEvent, KAEvent::UID_IGNORE);
1327 #endif
1328  bool ok = false;
1329  bool remove = false;
1330  if (mCalType == RESOURCES)
1331  {
1332 #ifdef USE_AKONADI
1333  Collection col;
1334  if (collection && CollectionControlModel::isEnabled(*collection, type))
1335  col = *collection;
1336  else
1337  col = CollectionControlModel::destination(type, promptParent, noPrompt, cancelled);
1338  if (col.isValid())
1339 #else
1340  if (!resource)
1341  resource = AlarmResources::instance()->destination(type, promptParent, noPrompt, cancelled);
1342  if (resource && addEvent(resource, event))
1343 #endif
1344  {
1345 #ifdef USE_AKONADI
1346  // Don't add event to mEventMap yet - its Akonadi item id is not yet known.
1347  // It will be added once it is inserted into AkonadiModel.
1348  ok = AkonadiModel::instance()->addEvent(*event, col);
1349  remove = ok; // if success, delete the local event instance on exit
1350 #else
1351  ok = AlarmResources::instance()->addEvent(kcalEvent, resource);
1352  kcalEvent = 0; // if there was an error, kcalEvent is deleted by AlarmResources::addEvent()
1353  remove = !ok;
1354 #endif
1355  if (ok && type == CalEvent::ACTIVE && !event->enabled())
1356  checkForDisabledAlarms(true, false);
1357  }
1358  }
1359  else
1360  {
1361  // It's the display calendar
1362 #ifdef USE_AKONADI
1363  event->updateKCalEvent(kcalEvent, KAEvent::UID_IGNORE);
1364  key = DISPLAY_COL_ID;
1365  if (!mEventMap.contains(EventId(key, event->id())))
1366  {
1367  addNewEvent(Collection(), event);
1368  ok = mCalendarStorage->calendar()->addEvent(kcalEvent);
1369  remove = !ok;
1370  }
1371 #else
1372  key = 0;
1373  if (addEvent(0, event))
1374  {
1375  ok = mCalendar->addEvent(kcalEvent);
1376  remove = !ok;
1377  }
1378 #endif
1379  }
1380  if (!ok)
1381  {
1382  if (remove)
1383  {
1384  // Adding to mCalendar failed, so undo AlarmCalendar::addEvent()
1385 #ifdef USE_AKONADI
1386  mEventMap.remove(EventId(key, event->id()));
1387 #else
1388  mEventMap.remove(event->id());
1389 #endif
1390  KAEvent::List& events = mResourceMap[key];
1391  int i = events.indexOf(event);
1392  if (i >= 0)
1393  events.remove(i);
1394  if (mEarliestAlarm[key] == event)
1395  findEarliestAlarm(key);
1396  }
1397 #ifdef USE_AKONADI
1398  delete event;
1399 #else
1400  *event = oldEvent;
1401  delete kcalEvent;
1402 #endif
1403  return false;
1404  }
1405 #ifdef USE_AKONADI
1406  evnt = *event;
1407  if (remove)
1408  delete event;
1409 #endif
1410  return true;
1411 }
1412 
1413 #ifndef USE_AKONADI
1414 /******************************************************************************
1415 * Internal method to add an event to the calendar.
1416 * The calendar takes ownership of 'event'.
1417 * Reply = true if success
1418 * = false if error because the event ID already exists.
1419 */
1420 bool AlarmCalendar::addEvent(AlarmResource* resource, KAEvent* event)
1421 {
1422  kDebug() << "KAEvent:" << event->id();
1423  if (mEventMap.contains(event->id()))
1424  return false;
1425  addNewEvent(resource, event);
1426  return true;
1427 }
1428 
1429 /******************************************************************************
1430 * Internal method to add an event to the calendar.
1431 * Reply = event as stored in calendar
1432 * = 0 if error because the event ID already exists.
1433 */
1434 KAEvent* AlarmCalendar::addEvent(AlarmResource* resource, const Event* kcalEvent)
1435 {
1436  kDebug() << "Event:" << kcalEvent->uid();
1437  if (mEventMap.contains(kcalEvent->uid()))
1438  return 0;
1439  // Create a new event
1440  KAEvent* ev = new KAEvent(kcalEvent);
1441  addNewEvent(resource, ev);
1442  return ev;
1443 }
1444 #endif
1445 
1446 /******************************************************************************
1447 * Internal method to add an already checked event to the calendar.
1448 * mEventMap takes ownership of the KAEvent.
1449 * If 'replace' is true, an existing event is being updated (NOTE: its category()
1450 * must remain the same).
1451 */
1452 #ifdef USE_AKONADI
1453 void AlarmCalendar::addNewEvent(const Collection& collection, KAEvent* event, bool replace)
1454 #else
1455 void AlarmCalendar::addNewEvent(AlarmResource* resource, KAEvent* event)
1456 #endif
1457 {
1458 #ifdef USE_AKONADI
1459  Collection::Id key = collection.isValid() ? collection.id() : -1;
1460  event->setCollectionId(key);
1461  if (!replace)
1462  {
1463  mResourceMap[key] += event;
1464  mEventMap[EventId(key, event->id())] = event;
1465  }
1466  if (collection.isValid() && (AkonadiModel::types(collection) & CalEvent::ACTIVE)
1467  && event->category() == CalEvent::ACTIVE)
1468 #else
1469  AlarmResource* key = resource;
1470  mResourceMap[key] += event;
1471  mEventMap[event->id()] = event;
1472  if (resource && resource->alarmType() == CalEvent::ACTIVE
1473  && event->category() == CalEvent::ACTIVE)
1474 #endif
1475  {
1476  // Update the earliest alarm to trigger
1477  KAEvent* earliest = mEarliestAlarm.value(key, (KAEvent*)0);
1478 #ifdef USE_AKONADI
1479  if (replace && earliest == event)
1480  findEarliestAlarm(key);
1481  else
1482 #endif
1483  {
1484  KDateTime dt = event->nextTrigger(KAEvent::ALL_TRIGGER).effectiveKDateTime();
1485  if (dt.isValid()
1486  && (!earliest || dt < earliest->nextTrigger(KAEvent::ALL_TRIGGER)))
1487  {
1488  mEarliestAlarm[key] = event;
1489  emit earliestAlarmChanged();
1490  }
1491  }
1492  }
1493 }
1494 
1495 /******************************************************************************
1496 * Modify the specified event in the calendar with its new contents.
1497 * The new event must have a different event ID from the old one.
1498 * It is assumed to be of the same event type as the old one (active, etc.)
1499 * Reply = true if 'newEvent' was written to the calendar, in which case (not
1500 * Akonadi) ownership of 'newEvent' is taken by the calendar.
1501 * 'newEvent' is updated.
1502 * = false if an error occurred, in which case 'newEvent' is unchanged.
1503 */
1504 #ifdef USE_AKONADI
1505 bool AlarmCalendar::modifyEvent(const EventId& oldEventId, KAEvent& newEvent)
1506 #else
1507 bool AlarmCalendar::modifyEvent(const QString& oldEventId, KAEvent* newEvent)
1508 #endif
1509 {
1510 #ifdef USE_AKONADI
1511  EventId newId(oldEventId.collectionId(), newEvent.id());
1512 #else
1513  QString newId = newEvent->id();
1514 #endif
1515  kDebug() << oldEventId << "->" << newId;
1516  bool noNewId = newId.isEmpty();
1517  if (!noNewId && oldEventId == newId)
1518  {
1519  kError() << "Same IDs";
1520  return false;
1521  }
1522  if (!mOpen)
1523  return false;
1524  if (mCalType == RESOURCES)
1525  {
1526 #ifdef USE_AKONADI
1527  // Set the event's ID and Akonadi ID, and update the old
1528  // event in Akonadi.
1529  KAEvent* storedEvent = event(oldEventId);
1530  if (!storedEvent)
1531  {
1532  kError() << "Old event not found";
1533  return false;
1534  }
1535  if (noNewId)
1536  newEvent.setEventId(CalFormat::createUniqueId());
1537  Collection c = AkonadiModel::instance()->collectionById(oldEventId.collectionId());
1538  if (!c.isValid())
1539  return false;
1540  // Don't add new event to mEventMap yet - its Akonadi item id is not yet known
1541  if (!AkonadiModel::instance()->addEvent(newEvent, c))
1542  return false;
1543  // Note: deleteEventInternal() will delete storedEvent before using the
1544  // event parameter, so need to pass a copy as the parameter.
1545  deleteEventInternal(KAEvent(*storedEvent), c);
1546  if (mHaveDisabledAlarms)
1547  checkForDisabledAlarms();
1548 #else
1549  // Create a new KCal::Event, keeping any custom properties from the old event.
1550  // Ensure it has a new ID.
1551  Event* kcalEvent = createKCalEvent(newEvent, oldEventId);
1552  if (noNewId)
1553  kcalEvent->setUid(CalFormat::createUniqueId());
1554  AlarmResources* resources = AlarmResources::instance();
1555  AlarmResource* resource = resources->resourceForIncidence(oldEventId);
1556  if (!resources->addEvent(kcalEvent, resource))
1557  return false; // kcalEvent has been deleted by AlarmResources::addEvent()
1558  if (noNewId)
1559  newEvent->setEventId(kcalEvent->uid());
1560  addEvent(resource, newEvent);
1561  deleteEvent(oldEventId); // this calls checkForDisabledAlarms()
1562 #endif
1563  }
1564  else
1565  {
1566 #ifdef USE_AKONADI
1567  // This functionality isn't needed for the display calendar.
1568  // The calendar would take ownership of newEvent.
1569  return false;
1570 #else
1571  if (!addEvent(newEvent, 0, true))
1572  return false;
1573  deleteEvent(oldEventId); // this calls checkForDisabledAlarms()
1574 #endif
1575  }
1576  return true;
1577 }
1578 
1579 /******************************************************************************
1580 * Update the specified event in the calendar with its new contents.
1581 * The event retains the same ID. The event must be in the resource calendar.
1582 * Reply = event which has been updated
1583 * = 0 if error.
1584 */
1585 KAEvent* AlarmCalendar::updateEvent(const KAEvent& evnt)
1586 {
1587  return updateEvent(&evnt);
1588 }
1589 KAEvent* AlarmCalendar::updateEvent(const KAEvent* evnt)
1590 {
1591  if (!mOpen || mCalType != RESOURCES)
1592  return 0;
1593 #ifdef USE_AKONADI
1594  KAEvent* kaevnt = event(EventId(*evnt));
1595  if (kaevnt)
1596  {
1597  KAEvent newEvnt(*evnt);
1598  newEvnt.setItemId(evnt->itemId());
1599  if (AkonadiModel::instance()->updateEvent(newEvnt))
1600  {
1601  *kaevnt = newEvnt;
1602  return kaevnt;
1603  }
1604  }
1605 #else
1606  QString id = evnt->id();
1607  KAEvent* kaevnt = event(id);
1608  Event* kcalEvent = mCalendar ? mCalendar->event(id) : 0;
1609  if (kaevnt && kcalEvent)
1610  {
1611  evnt->updateKCalEvent(kcalEvent, KAEvent::UID_CHECK);
1612  bool oldEnabled = kaevnt->enabled();
1613  if (kaevnt != evnt)
1614  *kaevnt = *evnt; // update the event instance in our lists, keeping the same pointer
1615  findEarliestAlarm(AlarmResources::instance()->resource(kcalEvent));
1616  if (mCalType == RESOURCES && evnt->category() == CalEvent::ACTIVE)
1617  checkForDisabledAlarms(oldEnabled, evnt->enabled());
1618  return kaevnt;
1619  }
1620 #endif
1621  kDebug() << "error";
1622  return 0;
1623 }
1624 
1625 
1626 #ifdef USE_AKONADI
1627 /******************************************************************************
1628 * Delete the specified event from the resource calendar, if it exists.
1629 * The calendar is then optionally saved.
1630 */
1631 bool AlarmCalendar::deleteEvent(const KAEvent& event, bool saveit)
1632 {
1633  if (mOpen && mCalType == RESOURCES)
1634  {
1635  CalEvent::Type status = deleteEventInternal(event);
1636  if (mHaveDisabledAlarms)
1637  checkForDisabledAlarms();
1638  if (status != CalEvent::EMPTY)
1639  {
1640  if (saveit)
1641  return save();
1642  return true;
1643  }
1644  }
1645  return false;
1646 }
1647 #endif
1648 
1649 /******************************************************************************
1650 * Delete the specified event from the calendar, if it exists.
1651 * The calendar is then optionally saved.
1652 */
1653 #ifdef USE_AKONADI
1654 bool AlarmCalendar::deleteDisplayEvent(const QString& eventID, bool saveit)
1655 #else
1656 bool AlarmCalendar::deleteEvent(const QString& eventID, bool saveit)
1657 #endif
1658 {
1659 #ifdef USE_AKONADI
1660  if (mOpen && mCalType != RESOURCES)
1661 #else
1662  if (mOpen)
1663 #endif
1664  {
1665  CalEvent::Type status = deleteEventInternal(eventID);
1666  if (mHaveDisabledAlarms)
1667  checkForDisabledAlarms();
1668  if (status != CalEvent::EMPTY)
1669  {
1670  if (saveit)
1671  return save();
1672  return true;
1673  }
1674  }
1675  return false;
1676 }
1677 
1678 /******************************************************************************
1679 * Internal method to delete the specified event from the calendar and lists.
1680 * Reply = event status, if it was found in the resource calendar/collection or
1681 * local calendar
1682 * = CalEvent::EMPTY otherwise.
1683 */
1684 #ifdef USE_AKONADI
1685 CalEvent::Type AlarmCalendar::deleteEventInternal(const KAEvent& event, bool deleteFromAkonadi)
1686 {
1687  Collection collection = AkonadiModel::instance()->collectionById(event.collectionId());
1688  if (!collection.isValid())
1689  return CalEvent::EMPTY;
1690  return deleteEventInternal(event.id(), event, collection, deleteFromAkonadi);
1691 }
1692 
1693 CalEvent::Type AlarmCalendar::deleteEventInternal(const KAEvent& event, const Collection& collection, bool deleteFromAkonadi)
1694 {
1695  if (!collection.isValid())
1696  return CalEvent::EMPTY;
1697  if (event.collectionId() != collection.id())
1698  {
1699  kError() << "Event" << event.id() << ": collection" << event.collectionId() << "differs from 'collection'" << collection.id();
1700  return CalEvent::EMPTY;
1701  }
1702  return deleteEventInternal(event.id(), event, collection, deleteFromAkonadi);
1703 }
1704 
1705 CalEvent::Type AlarmCalendar::deleteEventInternal(const QString& eventID, const KAEvent& event, const Collection& collection, bool deleteFromAkonadi)
1706 #else
1707 CalEvent::Type AlarmCalendar::deleteEventInternal(const QString& eventID)
1708 #endif
1709 {
1710  // Make a copy of the KAEvent and the ID QString, since the supplied
1711  // references might be destructed when the event is deleted below.
1712  const QString id = eventID;
1713 #ifdef USE_AKONADI
1714  const KAEvent paramEvent = event;
1715 
1716  Event::Ptr kcalEvent;
1717  if (mCalendarStorage)
1718  kcalEvent = mCalendarStorage->calendar()->event(id);
1719  Collection::Id key = collection.isValid() ? collection.id() : -1;
1720  KAEventMap::Iterator it = mEventMap.find(EventId(key, id));
1721 #else
1722  Event* kcalEvent = mCalendar ? mCalendar->event(id) : 0;
1723  KAEventMap::Iterator it = mEventMap.find(id);
1724 #endif
1725  if (it != mEventMap.end())
1726  {
1727  KAEvent* ev = it.value();
1728  mEventMap.erase(it);
1729 #ifndef USE_AKONADI
1730  AlarmResource* key = AlarmResources::instance()->resource(kcalEvent);
1731 #endif
1732  KAEvent::List& events = mResourceMap[key];
1733  int i = events.indexOf(ev);
1734  if (i >= 0)
1735  events.remove(i);
1736  delete ev;
1737  if (mEarliestAlarm[key] == ev)
1738 #ifdef USE_AKONADI
1739  findEarliestAlarm(collection);
1740 #else
1741  findEarliestAlarm(key);
1742 #endif
1743  }
1744  else
1745  {
1746  for (EarliestMap::Iterator eit = mEarliestAlarm.begin(); eit != mEarliestAlarm.end(); ++eit)
1747  {
1748  KAEvent* ev = eit.value();
1749  if (ev && ev->id() == id)
1750  {
1751  findEarliestAlarm(eit.key());
1752  break;
1753  }
1754  }
1755  }
1756  CalEvent::Type status = CalEvent::EMPTY;
1757  if (kcalEvent)
1758  {
1759  status = CalEvent::status(kcalEvent);
1760 #ifdef USE_AKONADI
1761  mCalendarStorage->calendar()->deleteEvent(kcalEvent);
1762 #else
1763  mCalendar->deleteEvent(kcalEvent);
1764 #endif
1765  }
1766 #ifdef USE_AKONADI
1767  else if (deleteFromAkonadi)
1768  {
1769  // It's an Akonadi event
1770  CalEvent::Type s = paramEvent.category();
1771  if (AkonadiModel::instance()->deleteEvent(paramEvent))
1772  status = s;
1773  }
1774 #else
1775 
1776  // Delete any command execution error flags for the alarm.
1777  KConfigGroup config(KGlobal::config(), KAEvent::commandErrorConfigGroup());
1778  if (config.hasKey(id))
1779  {
1780  config.deleteEntry(id);
1781  config.sync();
1782  }
1783 #endif
1784  return status;
1785 }
1786 
1787 #ifndef USE_AKONADI
1788 /******************************************************************************
1789 * Return a new KCal::Event representing the specified KAEvent.
1790 * If the event exists in the calendar, custom properties are copied from there.
1791 * The caller takes ownership of the returned KCal::Event. Note that the ID of
1792 * the returned KCal::Event may be the same as an existing calendar event, so
1793 * be careful not to end up duplicating IDs.
1794 * If it's an archived alarm, the event start date/time is adjusted to its
1795 * original value instead of its next occurrence, and the expired main alarm is
1796 * reinstated.
1797 */
1798 Event* AlarmCalendar::createKCalEvent(const KAEvent* ev, const QString& baseID) const
1799 {
1800  if (mCalType != RESOURCES)
1801  kFatal() << "AlarmCalendar::createKCalEvent(KAEvent): invalid for display calendar";
1802  // If the event exists in the calendar, we want to keep any custom
1803  // properties. So copy the calendar KCal::Event to base the new one on.
1804  QString id = baseID.isEmpty() ? ev->id() : baseID;
1805  Event* calEvent = id.isEmpty() ? 0 : AlarmResources::instance()->event(id);
1806  Event* newEvent = calEvent ? new Event(*calEvent) : new Event;
1807  ev->updateKCalEvent(newEvent, KAEvent::UID_SET);
1808  return newEvent;
1809 }
1810 #endif
1811 
1812 /******************************************************************************
1813 * Return the event with the specified ID.
1814 * If 'checkDuplicates' is true, and the collection ID is invalid, if there is
1815 * a unique event with the given ID, it will be returned.
1816 */
1817 #ifdef USE_AKONADI
1818 KAEvent* AlarmCalendar::event(const EventId& uniqueID, bool checkDuplicates)
1819 #else
1820 KAEvent* AlarmCalendar::event(const QString& uniqueID)
1821 #endif
1822 {
1823  if (!isValid())
1824  return 0;
1825 #ifdef USE_AKONADI
1826  const QString eventId = uniqueID.eventId();
1827  if (uniqueID.collectionId() == -1 && checkDuplicates)
1828  {
1829  // The collection isn't known, but use the event ID if it is
1830  // unique among all collections.
1831  KAEvent::List list = events(eventId);
1832  if (list.count() > 1)
1833  {
1834  kWarning() << "Multiple events found with ID" << eventId;
1835  return 0;
1836  }
1837  if (list.isEmpty())
1838  return 0;
1839  return list[0];
1840  }
1841 #endif
1842  KAEventMap::ConstIterator it = mEventMap.constFind(uniqueID);
1843  if (it == mEventMap.constEnd())
1844  return 0;
1845  return it.value();
1846 }
1847 
1848 /******************************************************************************
1849 * Return the event with the specified ID.
1850 * For the Akonadi version, this method is for the display calendar only.
1851 */
1852 #ifdef USE_AKONADI
1853 Event::Ptr AlarmCalendar::kcalEvent(const QString& uniqueID)
1854 {
1855  Q_ASSERT(mCalType != RESOURCES); // only allowed for display calendar
1856  if (!mCalendarStorage)
1857  return Event::Ptr();
1858  return mCalendarStorage->calendar()->event(uniqueID);
1859 }
1860 #else
1861 Event* AlarmCalendar::kcalEvent(const QString& uniqueID)
1862 {
1863  return mCalendar ? mCalendar->event(uniqueID) : 0;
1864 }
1865 #endif
1866 
1867 /******************************************************************************
1868 * Find the alarm template with the specified name.
1869 * Reply = 0 if not found.
1870 */
1871 KAEvent* AlarmCalendar::templateEvent(const QString& templateName)
1872 {
1873  if (templateName.isEmpty())
1874  return 0;
1875  KAEvent::List eventlist = events(CalEvent::TEMPLATE);
1876  for (int i = 0, end = eventlist.count(); i < end; ++i)
1877  {
1878  if (eventlist[i]->templateName() == templateName)
1879  return eventlist[i];
1880  }
1881  return 0;
1882 }
1883 
1884 #ifdef USE_AKONADI
1885 /******************************************************************************
1886 * Return all events with the specified ID, from all calendars.
1887 */
1888 KAEvent::List AlarmCalendar::events(const QString& uniqueId) const
1889 {
1890  KAEvent::List list;
1891  if (mCalType == RESOURCES && isValid())
1892  {
1893  for (ResourceMap::ConstIterator rit = mResourceMap.constBegin(); rit != mResourceMap.constEnd(); ++rit)
1894  {
1895  const Collection::Id id = rit.key();
1896  KAEventMap::ConstIterator it = mEventMap.constFind(EventId(id, uniqueId));
1897  if (it != mEventMap.constEnd())
1898  list += it.value();
1899  }
1900  }
1901  return list;
1902 }
1903 #endif
1904 
1905 /******************************************************************************
1906 * Return all events in the calendar which contain alarms.
1907 * Optionally the event type can be filtered, using an OR of event types.
1908 */
1909 #ifdef USE_AKONADI
1910 KAEvent::List AlarmCalendar::events(const Collection& collection, CalEvent::Types type) const
1911 #else
1912 KAEvent::List AlarmCalendar::events(AlarmResource* resource, CalEvent::Types type) const
1913 #endif
1914 {
1915  KAEvent::List list;
1916 #ifdef USE_AKONADI
1917  if (mCalType != RESOURCES && (!mCalendarStorage || collection.isValid()))
1918  return list;
1919  if (collection.isValid())
1920 #else
1921  if (!mCalendar || (resource && mCalType != RESOURCES))
1922  return list;
1923  if (resource)
1924 #endif
1925  {
1926 #ifdef USE_AKONADI
1927  Collection::Id key = collection.isValid() ? collection.id() : -1;
1928  ResourceMap::ConstIterator rit = mResourceMap.constFind(key);
1929 #else
1930  ResourceMap::ConstIterator rit = mResourceMap.constFind(resource);
1931 #endif
1932  if (rit == mResourceMap.constEnd())
1933  return list;
1934  const KAEvent::List events = rit.value();
1935  if (type == CalEvent::EMPTY)
1936  return events;
1937  for (int i = 0, end = events.count(); i < end; ++i)
1938  if (type & events[i]->category())
1939  list += events[i];
1940  }
1941  else
1942  {
1943  for (ResourceMap::ConstIterator rit = mResourceMap.constBegin(); rit != mResourceMap.constEnd(); ++rit)
1944  {
1945  const KAEvent::List events = rit.value();
1946  if (type == CalEvent::EMPTY)
1947  list += events;
1948  else
1949  {
1950  for (int i = 0, end = events.count(); i < end; ++i)
1951  if (type & events[i]->category())
1952  list += events[i];
1953  }
1954  }
1955  }
1956  return list;
1957 }
1958 
1959 /******************************************************************************
1960 * Return all events in the calendar which contain usable alarms.
1961 * For the Akonadi version, this method is for the display calendar only.
1962 * Optionally the event type can be filtered, using an OR of event types.
1963 */
1964 #ifdef USE_AKONADI
1965 Event::List AlarmCalendar::kcalEvents(CalEvent::Type type)
1966 #else
1967 Event::List AlarmCalendar::kcalEvents(AlarmResource* resource, CalEvent::Type type)
1968 #endif
1969 {
1970  Event::List list;
1971 #ifdef USE_AKONADI
1972  Q_ASSERT(mCalType != RESOURCES); // only allowed for display calendar
1973  if (!mCalendarStorage)
1974  return list;
1975  list = mCalendarStorage->calendar()->rawEvents();
1976 #else
1977  if (!mCalendar || (resource && mCalType != RESOURCES))
1978  return list;
1979  list = resource ? AlarmResources::instance()->rawEvents(resource) : mCalendar->rawEvents();
1980 #endif
1981  for (int i = 0; i < list.count(); )
1982  {
1983 #ifdef USE_AKONADI
1984  Event::Ptr event = list[i];
1985 #else
1986  Event* event = list[i];
1987 #endif
1988  if (event->alarms().isEmpty()
1989  || (type != CalEvent::EMPTY && !(type & CalEvent::status(event)))
1990  || !KAEvent(event).isValid())
1991 #ifdef USE_AKONADI
1992  list.remove(i);
1993 #else
1994  list.removeAt(i);
1995 #endif
1996  else
1997  ++i;
1998  }
1999  return list;
2000 }
2001 
2002 #ifndef USE_AKONADI
2003 /******************************************************************************
2004 * Return all events which have alarms falling within the specified time range.
2005 * 'type' is the OR'ed desired event types.
2006 */
2007 KAEvent::List AlarmCalendar::events(const KDateTime& from, const KDateTime& to, CalEvent::Types type)
2008 {
2009  kDebug() << from << "-" << to;
2010  KAEvent::List evnts;
2011  if (!isValid())
2012  return evnts;
2013  KDateTime dt;
2014  AlarmResources* resources = AlarmResources::instance();
2015  KAEvent::List allEvents = events(type);
2016  for (int i = 0, end = allEvents.count(); i < end; ++i)
2017  {
2018  KAEvent* event = allEvents[i];
2019  Event* e = resources->event(event->id());
2020  bool recurs = e->recurs();
2021  int endOffset = 0;
2022  bool endOffsetValid = false;
2023  const Alarm::List& alarms = e->alarms();
2024  for (int ai = 0, aend = alarms.count(); ai < aend; ++ai)
2025  {
2026  Alarm* alarm = alarms[ai];
2027  if (alarm->enabled())
2028  {
2029  if (recurs)
2030  {
2031  if (alarm->hasTime())
2032  dt = alarm->time();
2033  else
2034  {
2035  // The alarm time is defined by an offset from the event start or end time.
2036  // Find the offset from the event start time, which is also used as the
2037  // offset from the recurrence time.
2038  int offset = 0;
2039  if (alarm->hasStartOffset())
2040  offset = alarm->startOffset().asSeconds();
2041  else if (alarm->hasEndOffset())
2042  {
2043  if (!endOffsetValid)
2044  {
2045  endOffset = e->hasDuration() ? e->duration().asSeconds() : e->hasEndDate() ? e->dtStart().secsTo(e->dtEnd()) : 0;
2046  endOffsetValid = true;
2047  }
2048  offset = alarm->endOffset().asSeconds() + endOffset;
2049  }
2050  // Adjust the 'from' date/time and find the next recurrence at or after it
2051  KDateTime pre = from.addSecs(-offset - 1);
2052  if (e->allDay() && pre.time() < Preferences::startOfDay())
2053  pre = pre.addDays(-1); // today's recurrence (if today recurs) is still to come
2054  dt = e->recurrence()->getNextDateTime(pre);
2055  if (!dt.isValid())
2056  continue;
2057  dt = dt.addSecs(offset);
2058  }
2059  }
2060  else
2061  dt = alarm->time();
2062  if (dt >= from && dt <= to)
2063  {
2064  kDebug() << "'" << e->summary() << "':" << dt;
2065  evnts.append(event);
2066  break;
2067  }
2068  }
2069  }
2070  }
2071  return evnts;
2072 }
2073 #endif
2074 
2075 /******************************************************************************
2076 * Return whether an event is read-only.
2077 * Display calendar events are always returned as read-only.
2078 */
2079 #ifdef USE_AKONADI
2080 bool AlarmCalendar::eventReadOnly(Item::Id id) const
2081 {
2082  if (mCalType != RESOURCES)
2083  return true;
2084  AkonadiModel* model = AkonadiModel::instance();
2085  Collection collection = model->collectionForItem(id);
2086  KAEvent event = model->event(id);
2087  if (!CollectionControlModel::isWritableEnabled(collection, event.category()))
2088  return true;
2089  return !event.isValid() || event.isReadOnly();
2090  // || compatibility(event) != KACalendar::Current;
2091 }
2092 #else
2093 bool AlarmCalendar::eventReadOnly(const QString& uniqueID) const
2094 {
2095  if (!mCalendar || mCalType != RESOURCES)
2096  return true;
2097  AlarmResources* resources = AlarmResources::instance();
2098  const Event* event = resources->event(uniqueID);
2099  AlarmResource* resource = resources->resource(event);
2100  if (!resource)
2101  return true;
2102  return !resource->writable(event);
2103 }
2104 #endif
2105 
2106 #ifdef USE_AKONADI
2107 /******************************************************************************
2108 * Return the collection containing a specified event.
2109 */
2110 Collection AlarmCalendar::collectionForEvent(Item::Id itemId) const
2111 {
2112  if (mCalType != RESOURCES)
2113  return Collection();
2114  return AkonadiModel::instance()->collectionForItem(itemId);
2115 }
2116 #else
2117 /******************************************************************************
2118 * Return the resource containing a specified event.
2119 */
2120 AlarmResource* AlarmCalendar::resourceForEvent(const QString& eventID) const
2121 {
2122  if (!mCalendar || mCalType != RESOURCES)
2123  return 0;
2124  return AlarmResources::instance()->resourceForIncidence(eventID);
2125 }
2126 #endif
2127 
2128 /******************************************************************************
2129 * Called when an alarm's enabled status has changed.
2130 */
2131 void AlarmCalendar::disabledChanged(const KAEvent* event)
2132 {
2133  if (event->category() == CalEvent::ACTIVE)
2134  {
2135  bool status = event->enabled();
2136  checkForDisabledAlarms(!status, status);
2137  }
2138 }
2139 
2140 /******************************************************************************
2141 * Check whether there are any individual disabled alarms, following an alarm
2142 * creation or modification. Must only be called for an ACTIVE alarm.
2143 */
2144 void AlarmCalendar::checkForDisabledAlarms(bool oldEnabled, bool newEnabled)
2145 {
2146  if (mCalType == RESOURCES && newEnabled != oldEnabled)
2147  {
2148  if (newEnabled && mHaveDisabledAlarms)
2149  checkForDisabledAlarms();
2150  else if (!newEnabled && !mHaveDisabledAlarms)
2151  {
2152  mHaveDisabledAlarms = true;
2153  emit haveDisabledAlarmsChanged(true);
2154  }
2155  }
2156 }
2157 
2158 /******************************************************************************
2159 * Check whether there are any individual disabled alarms.
2160 */
2161 void AlarmCalendar::checkForDisabledAlarms()
2162 {
2163  if (mCalType != RESOURCES)
2164  return;
2165  bool disabled = false;
2166  KAEvent::List eventlist = events(CalEvent::ACTIVE);
2167  for (int i = 0, end = eventlist.count(); i < end; ++i)
2168  {
2169  if (!eventlist[i]->enabled())
2170  {
2171  disabled = true;
2172  break;
2173  }
2174  }
2175  if (disabled != mHaveDisabledAlarms)
2176  {
2177  mHaveDisabledAlarms = disabled;
2178  emit haveDisabledAlarmsChanged(disabled);
2179  }
2180 }
2181 
2182 /******************************************************************************
2183 * Return a list of all active at-login alarms.
2184 */
2185 KAEvent::List AlarmCalendar::atLoginAlarms() const
2186 {
2187  KAEvent::List atlogins;
2188 #ifdef USE_AKONADI
2189  if (mCalType != RESOURCES)
2190  return atlogins;
2191  AkonadiModel* model = AkonadiModel::instance();
2192  if (!mCalendarStorage || mCalType != RESOURCES)
2193  return atlogins;
2194 #else
2195  if (!mCalendar || mCalType != RESOURCES)
2196  return atlogins;
2197 #endif
2198  for (ResourceMap::ConstIterator rit = mResourceMap.constBegin(); rit != mResourceMap.constEnd(); ++rit)
2199  {
2200 #ifdef USE_AKONADI
2201  const Collection::Id id = rit.key();
2202  if (id < 0
2203  || !(AkonadiModel::types(model->collectionById(id)) & CalEvent::ACTIVE))
2204  continue;
2205 #else
2206  const AlarmResource* resource = rit.key();
2207  if (!resource || resource->alarmType() != CalEvent::ACTIVE)
2208  continue;
2209 #endif
2210  const KAEvent::List& events = rit.value();
2211  for (int i = 0, end = events.count(); i < end; ++i)
2212  {
2213  KAEvent* event = events[i];
2214  if (event->category() == CalEvent::ACTIVE && event->repeatAtLogin())
2215  atlogins += event;
2216  }
2217  }
2218  return atlogins;
2219 }
2220 
2221 /******************************************************************************
2222 * Find and note the active alarm with the earliest trigger time for a calendar.
2223 */
2224 #ifdef USE_AKONADI
2225 void AlarmCalendar::findEarliestAlarm(const Collection& collection)
2226 {
2227  if (mCalType != RESOURCES)
2228  return;
2229  if (!collection.isValid()
2230  || !(AkonadiModel::types(collection) & CalEvent::ACTIVE))
2231  return;
2232  findEarliestAlarm(collection.id());
2233 }
2234 
2235 void AlarmCalendar::findEarliestAlarm(Collection::Id key)
2236 #else
2237 void AlarmCalendar::findEarliestAlarm(AlarmResource* key)
2238 #endif
2239 {
2240  EarliestMap::Iterator eit = mEarliestAlarm.find(key);
2241  if (eit != mEarliestAlarm.end())
2242  eit.value() = 0;
2243 #ifdef USE_AKONADI
2244  if (mCalType != RESOURCES
2245  || key < 0)
2246  return;
2247 #else
2248  if (!mCalendar || mCalType != RESOURCES
2249  || !key || key->alarmType() != CalEvent::ACTIVE)
2250  return;
2251 #endif
2252  ResourceMap::ConstIterator rit = mResourceMap.constFind(key);
2253  if (rit == mResourceMap.constEnd())
2254  return;
2255  const KAEvent::List& events = rit.value();
2256  KAEvent* earliest = 0;
2257  KDateTime earliestTime;
2258  for (int i = 0, end = events.count(); i < end; ++i)
2259  {
2260  KAEvent* event = events[i];
2261  if (event->category() != CalEvent::ACTIVE
2262  || mPendingAlarms.contains(event->id()))
2263  continue;
2264  KDateTime dt = event->nextTrigger(KAEvent::ALL_TRIGGER).effectiveKDateTime();
2265  if (dt.isValid() && (!earliest || dt < earliestTime))
2266  {
2267  earliestTime = dt;
2268  earliest = event;
2269  }
2270  }
2271  mEarliestAlarm[key] = earliest;
2272  emit earliestAlarmChanged();
2273 }
2274 
2275 /******************************************************************************
2276 * Return the active alarm with the earliest trigger time.
2277 * Reply = 0 if none.
2278 */
2279 KAEvent* AlarmCalendar::earliestAlarm() const
2280 {
2281  KAEvent* earliest = 0;
2282  KDateTime earliestTime;
2283  for (EarliestMap::ConstIterator eit = mEarliestAlarm.constBegin(); eit != mEarliestAlarm.constEnd(); ++eit)
2284  {
2285  KAEvent* event = eit.value();
2286  if (!event)
2287  continue;
2288  KDateTime dt = event->nextTrigger(KAEvent::ALL_TRIGGER).effectiveKDateTime();
2289  if (dt.isValid() && (!earliest || dt < earliestTime))
2290  {
2291  earliestTime = dt;
2292  earliest = event;
2293  }
2294  }
2295  return earliest;
2296 }
2297 
2298 /******************************************************************************
2299 * Note that an alarm which has triggered is now being processed. While pending,
2300 * it will be ignored for the purposes of finding the earliest trigger time.
2301 */
2302 void AlarmCalendar::setAlarmPending(KAEvent* event, bool pending)
2303 {
2304  QString id = event->id();
2305  bool wasPending = mPendingAlarms.contains(id);
2306  kDebug() << id << "," << pending << "(was" << wasPending << ")";
2307  if (pending)
2308  {
2309  if (wasPending)
2310  return;
2311  mPendingAlarms.append(id);
2312  }
2313  else
2314  {
2315  if (!wasPending)
2316  return;
2317  mPendingAlarms.removeAll(id);
2318  }
2319  // Now update the earliest alarm to trigger for its calendar
2320 #ifdef USE_AKONADI
2321  findEarliestAlarm(AkonadiModel::instance()->collection(*event));
2322 #else
2323  findEarliestAlarm(AlarmResources::instance()->resourceForIncidence(id));
2324 #endif
2325 }
2326 
2327 /******************************************************************************
2328 * Called when the user changes the start-of-day time.
2329 * Adjust the start times of all date-only alarms' recurrences.
2330 */
2331 void AlarmCalendar::adjustStartOfDay()
2332 {
2333  if (!isValid())
2334  return;
2335  for (ResourceMap::ConstIterator rit = mResourceMap.constBegin(); rit != mResourceMap.constEnd(); ++rit)
2336  KAEvent::adjustStartOfDay(rit.value());
2337 }
2338 
2339 #ifdef USE_AKONADI
2340 /******************************************************************************
2341 * Find the version of KAlarm which wrote the calendar file, and do any
2342 * necessary conversions to the current format.
2343 */
2344 KACalendar::Compat fix(const FileStorage::Ptr& fileStorage)
2345 {
2346  QString versionString;
2347  int version = KACalendar::updateVersion(fileStorage, versionString);
2348  if (version == KACalendar::IncompatibleFormat)
2349  return KACalendar::Incompatible; // calendar was created by another program, or an unknown version of KAlarm
2350  return KACalendar::Current;
2351 }
2352 #endif
2353 
2354 // vim: et sw=4:
EventListModel::resourceStatusChanged
static void resourceStatusChanged(AlarmResource *, AlarmResources::Change)
Definition: eventlistmodel.cpp:546
CollectionControlModel::setAskDestinationPolicy
static void setAskDestinationPolicy(bool ask)
Set whether the user should be prompted for the destination collection to add alarms to...
Definition: collectionmodel.h:247
AlarmCalendar::deleteEvent
bool deleteEvent(const QString &eventID, bool save=false)
Definition: alarmcalendar.cpp:1656
EventListModel::alarms
static EventListModel * alarms()
Definition: eventlistmodel.cpp:56
AlarmCalendar::disabledChanged
void disabledChanged(const KAEvent *)
Definition: alarmcalendar.cpp:2131
AlarmCalendar::importAlarms
static bool importAlarms(QWidget *, AlarmResource *=0)
Definition: alarmcalendar.cpp:875
AlarmCalendar::type
CalEvent::Type type() const
Definition: alarmcalendar.h:64
AlarmCalendar::atLoginAlarms
KAEvent::List atLoginAlarms() const
Definition: alarmcalendar.cpp:2185
CollectionControlModel::isWritableEnabled
static int isWritableEnabled(const Akonadi::Collection &, CalEvent::Type)
Return whether a collection is both enabled and fully writable for a given alarm type, i.e.
Definition: collectionmodel.cpp:966
AkonadiModel::updateEvent
bool updateEvent(KAEvent &event)
Definition: akonadimodel.cpp:1315
AlarmCalendar::reloadFromCache
void reloadFromCache(const QString &resourceID)
Definition: alarmcalendar.cpp:853
AlarmCalendar::eventReadOnly
bool eventReadOnly(const QString &uniqueID) const
Definition: alarmcalendar.cpp:2093
AlarmCalendar::updateEvent
KAEvent * updateEvent(const KAEvent &)
Definition: alarmcalendar.cpp:1585
AlarmCalendar::kcalEvent
KCal::Event * kcalEvent(const QString &uniqueId)
Definition: alarmcalendar.cpp:1861
AlarmCalendar::earliestAlarmChanged
void earliestAlarmChanged()
AlarmCalendar::loadResource
void loadResource(AlarmResource *, QWidget *parent)
Definition: alarmcalendar.cpp:536
AlarmCalendar::resources
static AlarmCalendar * resources()
Definition: alarmcalendar.h:130
QWidget
KAMessageBox::error
static void error(QWidget *parent, const QString &text, const QString &caption=QString(), Options options=Options(Notify|WindowModal))
AlarmCalendar::calendarSaved
void calendarSaved(AlarmCalendar *)
AlarmCalendar::reload
bool reload()
Definition: alarmcalendar.cpp:401
KALARM_VERSION
#define KALARM_VERSION
Definition: kalarm.h:31
AlarmCalendar::startUpdate
void startUpdate()
Definition: alarmcalendar.cpp:1187
AkonadiModel::Enabled
Definition: akonadimodel.h:52
AlarmCalendar::addEvent
bool addEvent(KAEvent *, QWidget *promptParent=0, bool useEventID=false, AlarmResource *=0, bool noPrompt=false, bool *cancelled=0)
Definition: alarmcalendar.cpp:1261
PreferencesBase::Akonadi
Definition: kalarmconfig.h:20
AlarmCalendar::terminateCalendars
static void terminateCalendars()
Definition: alarmcalendar.cpp:119
EventId::eventId
QString eventId() const
Definition: eventid.h:52
EventListModel::addEvents
void addEvents(const KAEvent::List &)
Definition: eventlistmodel.cpp:741
displayCalendarName
static const QString displayCalendarName
Definition: alarmcalendar.cpp:70
AlarmCalendar::resourceForEvent
AlarmResource * resourceForEvent(const QString &eventID) const
Definition: alarmcalendar.cpp:2120
from
QString from() const
AlarmCalendar::templateEvent
KAEvent * templateEvent(const QString &templateName)
Definition: alarmcalendar.cpp:1871
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
AlarmCalendar::open
bool open()
Definition: alarmcalendar.cpp:250
AlarmCalendar::isOpen
bool isOpen()
Definition: alarmcalendar.cpp:235
PreferencesBase::Kresources
Definition: kalarmconfig.h:20
CollectionControlModel::isEnabled
static bool isEnabled(const Akonadi::Collection &, CalEvent::Type)
Return whether a collection is enabled (and valid).
Definition: collectionmodel.cpp:714
FileDialog::getSaveFileName
static QString getSaveFileName(const KUrl &dir=KUrl(), const QString &filter=QString(), QWidget *parent=0, const QString &caption=QString(), bool *append=0)
AlarmCalendar::getEvent
static KAEvent * getEvent(const QString &uniqueId)
Definition: alarmcalendar.cpp:150
kalarmapp.h
the KAlarm application object
AlarmCalendar::load
int load()
Definition: alarmcalendar.cpp:321
AkonadiModel::Event
Struct containing a KAEvent and its parent Collection.
Definition: akonadimodel.h:77
AlarmCalendar::earliestAlarm
KAEvent * earliestAlarm() const
Definition: alarmcalendar.cpp:2279
AlarmCalendar::modifyEvent
bool modifyEvent(const QString &oldEventId, KAEvent *newEvent)
Definition: alarmcalendar.cpp:1507
Preferences::startOfDay
static QTime startOfDay()
Definition: preferences.h:55
AlarmCalendar::adjustStartOfDay
void adjustStartOfDay()
Definition: alarmcalendar.cpp:2331
AlarmCalendar::purgeEvents
void purgeEvents(const KAEvent::List &)
Definition: alarmcalendar.cpp:1229
AkonadiModel::Event::event
KAEvent event
Definition: akonadimodel.h:82
Preferences::timeZone
static KTimeZone timeZone(bool reload=false)
Definition: preferences.cpp:122
CollectionControlModel::destination
static Akonadi::Collection destination(CalEvent::Type, QWidget *promptParent=0, bool noPrompt=false, bool *cancelled=0)
Find the collection to be used to store an event of a given type.
Definition: collectionmodel.cpp:1178
AkonadiModel::instance
static AkonadiModel * instance()
Definition: akonadimodel.cpp:83
AkonadiModel::Event::collection
Akonadi::Collection collection
Definition: akonadimodel.h:83
CalendarCompat::fix
KACalendar::Compat fix(KCal::CalendarLocal &calendar, const QString &localFile, AlarmResource *resource, AlarmResource::FixFunc conv, bool *wrongType)
Definition: calendarcompat.cpp:41
mainwindow.h
main application window
to
QString to() const
eventlistmodel.h
AkonadiModel::Change
Change
Definition: akonadimodel.h:52
messagebox.h
PreferencesBase::askResource
static bool askResource()
Get Prompt for which calendar to store in.
Definition: kalarmconfig.h:396
AlarmCalendar::path
QString path() const
Definition: alarmcalendar.h:124
AlarmCalendar::endUpdate
bool endUpdate()
Definition: alarmcalendar.cpp:1196
PreferencesBase::setBackend
static void setBackend(Backend v)
Set Data storage backend.
Definition: kalarmconfig.h:77
AlarmCalendar::createKCalEvent
KCal::Event * createKCalEvent(const KAEvent *e) const
Definition: alarmcalendar.h:85
preferences.h
Preferences::connect
static void connect(const char *signal, const QObject *receiver, const char *member)
Definition: preferences.cpp:292
collectionmodel.h
EventId
Unique event identifier for Akonadi.
Definition: eventid.h:38
AkonadiModel::types
static CalEvent::Types types(const Akonadi::Collection &)
Definition: akonadimodel.cpp:1863
AlarmCalendar::kcalEvents
KCal::Event::List kcalEvents(CalEvent::Type s=CalEvent::EMPTY)
Definition: alarmcalendar.h:107
AkonadiModel::Event::eventId
EventId eventId() const
Definition: akonadimodel.h:80
AlarmCalendar::~AlarmCalendar
virtual ~AlarmCalendar()
Definition: alarmcalendar.cpp:227
AkonadiModel::Event::isConsistent
bool isConsistent() const
Definition: akonadimodel.h:81
functions.h
miscellaneous functions
kalarm.h
AlarmCalendar::setAlarmPending
void setAlarmPending(KAEvent *, bool pending=true)
Definition: alarmcalendar.cpp:2302
AlarmCalendar::initialiseCalendars
static bool initialiseCalendars()
Definition: alarmcalendar.cpp:88
Preferences::self
static Preferences * self()
Definition: preferences.cpp:80
KALARM_NAME
#define KALARM_NAME
Definition: kalarm.h:33
AlarmCalendar
Provides read and write access to calendar files and resources.
Definition: alarmcalendar.h:58
AlarmCalendar::displayCalendarOpen
static AlarmCalendar * displayCalendarOpen()
Definition: alarmcalendar.cpp:130
AlarmCalendar::events
KAEvent::List events(CalEvent::Types s=CalEvent::EMPTY) const
Definition: alarmcalendar.h:96
AlarmCalendar::exportAlarms
static bool exportAlarms(const KAEvent::List &, QWidget *parent)
Definition: alarmcalendar.cpp:1077
AlarmCalendar::event
KAEvent * event(const QString &uniqueId)
Definition: alarmcalendar.cpp:1820
AlarmCalendar::save
bool save()
Definition: alarmcalendar.cpp:1212
MainWindow::mainMainWindow
static MainWindow * mainMainWindow()
Definition: mainwindow.cpp:288
calendarcompat.h
AkonadiModel::collectionForItem
Akonadi::Collection collectionForItem(Akonadi::Item::Id) const
Definition: akonadimodel.cpp:1813
AlarmCalendar::haveDisabledAlarmsChanged
void haveDisabledAlarmsChanged(bool haveDisabled)
KAlarmApp::displayFatalError
static void displayFatalError(const QString &message)
Definition: kalarmapp.cpp:745
AkonadiModel
Definition: akonadimodel.h:48
EventId::isEmpty
bool isEmpty() const
Return whether the instance contains any data.
Definition: eventid.h:49
AlarmCalendar::close
void close()
Definition: alarmcalendar.cpp:495
AkonadiModel::addEvent
bool addEvent(KAEvent &, Akonadi::Collection &)
Definition: akonadimodel.cpp:1288
QList
filedialog.h
This file is part of the KDE documentation.
Documentation copyright © 1996-2014 The KDE developers.
Generated on Tue Oct 14 2014 22:59:10 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