• 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
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.h"
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  Event::List events = cal->rawEvents();
938 #else
939  KACalendar::Compat caltype = CalendarCompat::fix(cal, filename);
940  CalEvent::Type wantedType = resource ? resource->alarmType() : CalEvent::EMPTY;
941  AlarmResources* resources = AlarmResources::instance();
942  AlarmResource* activeRes = 0;
943  AlarmResource* archivedRes = 0;
944  AlarmResource* templateRes = 0;
945  bool saveRes = false;
946  bool enabled = true;
947  KAEvent::List newEvents;
948  Event::List events = cal.rawEvents();
949 #endif
950  for (int i = 0, end = events.count(); i < end; ++i)
951  {
952 #ifdef USE_AKONADI
953  Event::Ptr event = events[i];
954 #else
955  const Event* event = events[i];
956 #endif
957  if (event->alarms().isEmpty() || !KAEvent(event).isValid())
958  continue; // ignore events without alarms, or usable alarms
959  CalEvent::Type type = CalEvent::status(event);
960  if (type == CalEvent::TEMPLATE)
961  {
962  // If we know the event was not created by KAlarm, don't treat it as a template
963  if (caltype == KACalendar::Incompatible)
964  type = CalEvent::ACTIVE;
965  }
966 #ifdef USE_AKONADI
967  Collection* coll;
968  if (collection && collection->isValid())
969  {
970  if (!(type & wantedTypes))
971  continue;
972  coll = collection;
973  }
974  else
975  {
976  switch (type)
977  {
978  case CalEvent::ACTIVE: coll = &activeColl; break;
979  case CalEvent::ARCHIVED: coll = &archiveColl; break;
980  case CalEvent::TEMPLATE: coll = &templateColl; break;
981  default: continue;
982  }
983  if (!coll->isValid())
984  *coll = CollectionControlModel::destination(type);
985  }
986 
987  Event::Ptr newev(new Event(*event));
988 #else
989  AlarmResource** res;
990  if (resource)
991  {
992  if (type != wantedType)
993  continue;
994  res = &resource;
995  }
996  else
997  {
998  switch (type)
999  {
1000  case CalEvent::ACTIVE: res = &activeRes; break;
1001  case CalEvent::ARCHIVED: res = &archivedRes; break;
1002  case CalEvent::TEMPLATE: res = &templateRes; break;
1003  default: continue;
1004  }
1005  if (!*res)
1006  *res = resources->destination(type);
1007  }
1008 
1009  Event* newev = new Event(*event);
1010 #endif
1011 
1012  // If there is a display alarm without display text, use the event
1013  // summary text instead.
1014  if (type == CalEvent::ACTIVE && !newev->summary().isEmpty())
1015  {
1016  const Alarm::List& alarms = newev->alarms();
1017  for (int ai = 0, aend = alarms.count(); ai < aend; ++ai)
1018  {
1019 #ifdef USE_AKONADI
1020  Alarm::Ptr alarm = alarms[ai];
1021 #else
1022  Alarm* alarm = alarms[ai];
1023 #endif
1024  if (alarm->type() == Alarm::Display && alarm->text().isEmpty())
1025  alarm->setText(newev->summary());
1026  }
1027  newev->setSummary(QString()); // KAlarm only uses summary for template names
1028  }
1029 
1030  // Give the event a new ID and add it to the calendars
1031  newev->setUid(CalEvent::uid(CalFormat::createUniqueId(), type));
1032 #ifdef USE_AKONADI
1033  KAEvent* newEvent = new KAEvent(newev);
1034  if (!AkonadiModel::instance()->addEvent(*newEvent, *coll))
1035  success = false;
1036 #else
1037  if (resources->addEvent(newev, *res))
1038  {
1039  saveRes = true;
1040  KAEvent* ev = mResourcesCalendar->addEvent(*res, newev);
1041  if (type != CalEvent::TEMPLATE)
1042  newEvents += ev;
1043  if (type == CalEvent::ACTIVE && !ev->enabled())
1044  enabled = false;
1045  }
1046  else
1047  success = false;
1048 #endif
1049  }
1050 
1051 #ifndef USE_AKONADI
1052  // Save the resources if they have been modified
1053  if (saveRes)
1054  {
1055  resources->save();
1056  EventListModel::alarms()->addEvents(newEvents);
1057  if (!enabled)
1058  mResourcesCalendar->checkForDisabledAlarms(true, enabled);
1059  }
1060 #endif
1061  }
1062  if (!local)
1063  KIO::NetAccess::removeTempFile(filename);
1064  return success;
1065 }
1066 
1067 /******************************************************************************
1068 * Export all selected alarms to an external calendar.
1069 * The alarms are given new unique event IDs.
1070 * Parameters: parent = parent widget for error message boxes
1071 * Reply = true if all alarms in the calendar were successfully exported
1072 * = false if any alarms failed to be exported.
1073 */
1074 bool AlarmCalendar::exportAlarms(const KAEvent::List& events, QWidget* parent)
1075 {
1076  bool append;
1077  QString file = FileDialog::getSaveFileName(KUrl("kfiledialog:///exportalarms"),
1078  QString::fromLatin1("*.ics|%1").arg(i18nc("@info/plain", "Calendar Files")),
1079  parent, i18nc("@title:window", "Choose Export Calendar"),
1080  &append);
1081  if (file.isEmpty())
1082  return false;
1083  KUrl url;
1084  url.setPath(file);
1085  if (!url.isValid())
1086  {
1087  kDebug() << "Invalid URL";
1088  return false;
1089  }
1090  kDebug() << url.prettyUrl();
1091 
1092 #ifdef USE_AKONADI
1093  MemoryCalendar::Ptr calendar(new MemoryCalendar(Preferences::timeZone(true)));
1094  FileStorage::Ptr calStorage(new FileStorage(calendar, file));
1095  if (append && !calStorage->load())
1096 #else
1097  CalendarLocal calendar(Preferences::timeZone(true));
1098  if (append && !calendar.load(file))
1099 #endif
1100  {
1101  KIO::UDSEntry uds;
1102  KIO::NetAccess::stat(url, uds, parent);
1103  KFileItem fi(uds, url);
1104  if (fi.size())
1105  {
1106  kError() << "Error loading calendar file" << file << "for append";
1107  KAMessageBox::error(MainWindow::mainMainWindow(),
1108  i18nc("@info", "Error loading calendar to append to:<nl/><filename>%1</filename>", url.prettyUrl()));
1109  return false;
1110  }
1111  }
1112  KACalendar::setKAlarmVersion(calendar);
1113 
1114  // Add the alarms to the calendar
1115  bool success = true;
1116  bool exported = false;
1117  for (int i = 0, end = events.count(); i < end; ++i)
1118  {
1119  const KAEvent* event = events[i];
1120 #ifdef USE_AKONADI
1121  Event::Ptr kcalEvent(new Event);
1122 #else
1123  Event* kcalEvent = new Event;
1124 #endif
1125  CalEvent::Type type = event->category();
1126  QString id = CalEvent::uid(kcalEvent->uid(), type);
1127  kcalEvent->setUid(id);
1128  event->updateKCalEvent(kcalEvent, KAEvent::UID_IGNORE);
1129 #ifdef USE_AKONADI
1130  if (calendar->addEvent(kcalEvent))
1131 #else
1132  if (calendar.addEvent(kcalEvent))
1133 #endif
1134  exported = true;
1135  else
1136  success = false;
1137  }
1138 
1139  if (exported)
1140  {
1141  // One or more alarms have been exported to the calendar.
1142  // Save the calendar to file.
1143  KTemporaryFile* tempFile = 0;
1144  bool local = url.isLocalFile();
1145  if (!local)
1146  {
1147  tempFile = new KTemporaryFile;
1148  file = tempFile->fileName();
1149  }
1150 #ifdef USE_AKONADI
1151  calStorage->setFileName(file);
1152  calStorage->setSaveFormat(new ICalFormat);
1153  if (!calStorage->save())
1154 #else
1155  if (!calendar.save(file, new ICalFormat))
1156 #endif
1157  {
1158  kError() << file << ": failed";
1159  KAMessageBox::error(MainWindow::mainMainWindow(),
1160  i18nc("@info", "Failed to save new calendar to:<nl/><filename>%1</filename>", url.prettyUrl()));
1161  success = false;
1162  }
1163  else if (!local && !KIO::NetAccess::upload(file, url, parent))
1164  {
1165  kError() << file << ": upload failed";
1166  KAMessageBox::error(MainWindow::mainMainWindow(),
1167  i18nc("@info", "Cannot upload new calendar to:<nl/><filename>%1</filename>", url.prettyUrl()));
1168  success = false;
1169  }
1170  delete tempFile;
1171  }
1172 #ifdef USE_AKONADI
1173  calendar->close();
1174 #else
1175  calendar.close();
1176 #endif
1177  return success;
1178 }
1179 
1180 /******************************************************************************
1181 * Flag the start of a group of calendar update calls.
1182 * The purpose is to avoid multiple calendar saves during a group of operations.
1183 */
1184 void AlarmCalendar::startUpdate()
1185 {
1186  ++mUpdateCount;
1187 }
1188 
1189 /******************************************************************************
1190 * Flag the end of a group of calendar update calls.
1191 * The calendar is saved if appropriate.
1192 */
1193 bool AlarmCalendar::endUpdate()
1194 {
1195  if (mUpdateCount > 0)
1196  --mUpdateCount;
1197  if (!mUpdateCount)
1198  {
1199  if (mUpdateSave)
1200  return saveCal();
1201  }
1202  return true;
1203 }
1204 
1205 /******************************************************************************
1206 * Save the calendar, or flag it for saving if in a group of calendar update calls.
1207 * Note that this method has no effect for Akonadi calendars.
1208 */
1209 bool AlarmCalendar::save()
1210 {
1211  if (mUpdateCount)
1212  {
1213  mUpdateSave = true;
1214  return true;
1215  }
1216  else
1217  return saveCal();
1218 }
1219 
1220 /******************************************************************************
1221 * This method must only be called from the main KAlarm queue processing loop,
1222 * to prevent asynchronous calendar operations interfering with one another.
1223 *
1224 * Purge a list of archived events from the calendar.
1225 */
1226 void AlarmCalendar::purgeEvents(const KAEvent::List& events)
1227 {
1228  for (int i = 0, end = events.count(); i < end; ++i)
1229  {
1230 #ifdef USE_AKONADI
1231  deleteEventInternal(*events[i]);
1232 #else
1233  deleteEventInternal(events[i]->id());
1234 #endif
1235  }
1236  if (mHaveDisabledAlarms)
1237  checkForDisabledAlarms();
1238  saveCal();
1239 }
1240 
1241 /******************************************************************************
1242 * Add the specified event to the calendar.
1243 * If it is an active event and 'useEventID' is false, a new event ID is
1244 * created. In all other cases, the event ID is taken from 'event' (if non-null).
1245 * 'event' is updated with the actual event ID.
1246 * The event is added to 'resource' if specified; otherwise the default resource
1247 * is used or the user is prompted, depending on policy. If 'noPrompt' is true,
1248 * the user will not be prompted so that if no default resource is defined, the
1249 * function will fail.
1250 * Reply = true if 'event' was written to the calendar, in which case (not
1251 * Akonadi) ownership of 'event' is taken by the calendar. 'event'
1252 * is updated.
1253 * = false if an error occurred, in which case 'event' is unchanged.
1254 */
1255 #ifdef USE_AKONADI
1256 bool AlarmCalendar::addEvent(KAEvent& evnt, QWidget* promptParent, bool useEventID, Collection* collection, bool noPrompt, bool* cancelled)
1257 #else
1258 bool AlarmCalendar::addEvent(KAEvent* event, QWidget* promptParent, bool useEventID, AlarmResource* resource, bool noPrompt, bool* cancelled)
1259 #endif
1260 {
1261  if (cancelled)
1262  *cancelled = false;
1263  if (!mOpen)
1264  return false;
1265  // Check that the event type is valid for the calendar
1266 #ifdef USE_AKONADI
1267  kDebug() << evnt.id();
1268  CalEvent::Type type = evnt.category();
1269 #else
1270  kDebug() << event->id();
1271  CalEvent::Type type = event->category();
1272 #endif
1273  if (type != mEventType)
1274  {
1275  switch (type)
1276  {
1277  case CalEvent::ACTIVE:
1278  case CalEvent::ARCHIVED:
1279  case CalEvent::TEMPLATE:
1280  if (mEventType == CalEvent::EMPTY)
1281  break;
1282  // fall through to default
1283  default:
1284  return false;
1285  }
1286  }
1287 
1288 #ifdef USE_AKONADI
1289  Collection::Id key = (collection && collection->isValid()) ? collection->id() : -1;
1290  Event::Ptr kcalEvent((mCalType == RESOURCES) ? (Event*)0 : new Event);
1291  KAEvent* event = new KAEvent(evnt);
1292 #else
1293  AlarmResource* key = resource;
1294  Event* kcalEvent = new Event;
1295  KAEvent oldEvent(*event); // so that we can reinstate it if there's an error
1296 #endif
1297  QString id = event->id();
1298  if (type == CalEvent::ACTIVE)
1299  {
1300  if (id.isEmpty())
1301  useEventID = false;
1302  else if (!useEventID)
1303  id.clear();
1304  }
1305  else
1306  useEventID = true;
1307  if (id.isEmpty())
1308 #ifdef USE_AKONADI
1309  id = (mCalType == RESOURCES) ? CalFormat::createUniqueId() : kcalEvent->uid();
1310 #else
1311  id = kcalEvent->uid();
1312 #endif
1313  if (useEventID)
1314  {
1315  id = CalEvent::uid(id, type);
1316 #ifdef USE_AKONADI
1317  if (kcalEvent)
1318 #endif
1319  kcalEvent->setUid(id);
1320  }
1321  event->setEventId(id);
1322 #ifndef USE_AKONADI
1323  event->updateKCalEvent(kcalEvent, KAEvent::UID_IGNORE);
1324 #endif
1325  bool ok = false;
1326  bool remove = false;
1327  if (mCalType == RESOURCES)
1328  {
1329 #ifdef USE_AKONADI
1330  Collection col;
1331  if (collection && CollectionControlModel::isEnabled(*collection, type))
1332  col = *collection;
1333  else
1334  col = CollectionControlModel::destination(type, promptParent, noPrompt, cancelled);
1335  if (col.isValid())
1336 #else
1337  if (!resource)
1338  resource = AlarmResources::instance()->destination(type, promptParent, noPrompt, cancelled);
1339  if (resource && addEvent(resource, event))
1340 #endif
1341  {
1342 #ifdef USE_AKONADI
1343  // Don't add event to mEventMap yet - its Akonadi item id is not yet known.
1344  // It will be added once it is inserted into AkonadiModel.
1345  ok = AkonadiModel::instance()->addEvent(*event, col);
1346  remove = ok; // if success, delete the local event instance on exit
1347 #else
1348  ok = AlarmResources::instance()->addEvent(kcalEvent, resource);
1349  kcalEvent = 0; // if there was an error, kcalEvent is deleted by AlarmResources::addEvent()
1350  remove = !ok;
1351 #endif
1352  if (ok && type == CalEvent::ACTIVE && !event->enabled())
1353  checkForDisabledAlarms(true, false);
1354  }
1355  }
1356  else
1357  {
1358  // It's the display calendar
1359 #ifdef USE_AKONADI
1360  event->updateKCalEvent(kcalEvent, KAEvent::UID_IGNORE);
1361  key = DISPLAY_COL_ID;
1362  if (!mEventMap.contains(EventId(key, event->id())))
1363  {
1364  addNewEvent(Collection(), event);
1365  ok = mCalendarStorage->calendar()->addEvent(kcalEvent);
1366  remove = !ok;
1367  }
1368 #else
1369  key = 0;
1370  if (addEvent(0, event))
1371  {
1372  ok = mCalendar->addEvent(kcalEvent);
1373  remove = !ok;
1374  }
1375 #endif
1376  }
1377  if (!ok)
1378  {
1379  if (remove)
1380  {
1381  // Adding to mCalendar failed, so undo AlarmCalendar::addEvent()
1382 #ifdef USE_AKONADI
1383  mEventMap.remove(EventId(key, event->id()));
1384 #else
1385  mEventMap.remove(event->id());
1386 #endif
1387  KAEvent::List& events = mResourceMap[key];
1388  int i = events.indexOf(event);
1389  if (i >= 0)
1390  events.remove(i);
1391  if (mEarliestAlarm[key] == event)
1392  findEarliestAlarm(key);
1393  }
1394 #ifdef USE_AKONADI
1395  delete event;
1396 #else
1397  *event = oldEvent;
1398  delete kcalEvent;
1399 #endif
1400  return false;
1401  }
1402 #ifdef USE_AKONADI
1403  evnt = *event;
1404  if (remove)
1405  delete event;
1406 #endif
1407  return true;
1408 }
1409 
1410 #ifndef USE_AKONADI
1411 /******************************************************************************
1412 * Internal method to add an event to the calendar.
1413 * The calendar takes ownership of 'event'.
1414 * Reply = true if success
1415 * = false if error because the event ID already exists.
1416 */
1417 bool AlarmCalendar::addEvent(AlarmResource* resource, KAEvent* event)
1418 {
1419  kDebug() << "KAEvent:" << event->id();
1420  if (mEventMap.contains(event->id()))
1421  return false;
1422  addNewEvent(resource, event);
1423  return true;
1424 }
1425 
1426 /******************************************************************************
1427 * Internal method to add an event to the calendar.
1428 * Reply = event as stored in calendar
1429 * = 0 if error because the event ID already exists.
1430 */
1431 KAEvent* AlarmCalendar::addEvent(AlarmResource* resource, const Event* kcalEvent)
1432 {
1433  kDebug() << "Event:" << kcalEvent->uid();
1434  if (mEventMap.contains(kcalEvent->uid()))
1435  return 0;
1436  // Create a new event
1437  KAEvent* ev = new KAEvent(kcalEvent);
1438  addNewEvent(resource, ev);
1439  return ev;
1440 }
1441 #endif
1442 
1443 /******************************************************************************
1444 * Internal method to add an already checked event to the calendar.
1445 * mEventMap takes ownership of the KAEvent.
1446 * If 'replace' is true, an existing event is being updated (NOTE: its category()
1447 * must remain the same).
1448 */
1449 #ifdef USE_AKONADI
1450 void AlarmCalendar::addNewEvent(const Collection& collection, KAEvent* event, bool replace)
1451 #else
1452 void AlarmCalendar::addNewEvent(AlarmResource* resource, KAEvent* event)
1453 #endif
1454 {
1455 #ifdef USE_AKONADI
1456  Collection::Id key = collection.isValid() ? collection.id() : -1;
1457  event->setCollectionId(key);
1458  if (!replace)
1459  {
1460  mResourceMap[key] += event;
1461  mEventMap[EventId(key, event->id())] = event;
1462  }
1463  if (collection.isValid() && (AkonadiModel::types(collection) & CalEvent::ACTIVE)
1464  && event->category() == CalEvent::ACTIVE)
1465 #else
1466  AlarmResource* key = resource;
1467  mResourceMap[key] += event;
1468  mEventMap[event->id()] = event;
1469  if (resource && resource->alarmType() == CalEvent::ACTIVE
1470  && event->category() == CalEvent::ACTIVE)
1471 #endif
1472  {
1473  // Update the earliest alarm to trigger
1474  KAEvent* earliest = mEarliestAlarm.value(key, (KAEvent*)0);
1475 #ifdef USE_AKONADI
1476  if (replace && earliest == event)
1477  findEarliestAlarm(key);
1478  else
1479 #endif
1480  {
1481  KDateTime dt = event->nextTrigger(KAEvent::ALL_TRIGGER).effectiveKDateTime();
1482  if (dt.isValid()
1483  && (!earliest || dt < earliest->nextTrigger(KAEvent::ALL_TRIGGER)))
1484  {
1485  mEarliestAlarm[key] = event;
1486  emit earliestAlarmChanged();
1487  }
1488  }
1489  }
1490 }
1491 
1492 /******************************************************************************
1493 * Modify the specified event in the calendar with its new contents.
1494 * The new event must have a different event ID from the old one.
1495 * It is assumed to be of the same event type as the old one (active, etc.)
1496 * Reply = true if 'newEvent' was written to the calendar, in which case (not
1497 * Akonadi) ownership of 'newEvent' is taken by the calendar.
1498 * 'newEvent' is updated.
1499 * = false if an error occurred, in which case 'newEvent' is unchanged.
1500 */
1501 #ifdef USE_AKONADI
1502 bool AlarmCalendar::modifyEvent(const EventId& oldEventId, KAEvent& newEvent)
1503 #else
1504 bool AlarmCalendar::modifyEvent(const QString& oldEventId, KAEvent* newEvent)
1505 #endif
1506 {
1507 #ifdef USE_AKONADI
1508  EventId newId(oldEventId.collectionId(), newEvent.id());
1509 #else
1510  QString newId = newEvent->id();
1511 #endif
1512  kDebug() << oldEventId << "->" << newId;
1513  bool noNewId = newId.isEmpty();
1514  if (!noNewId && oldEventId == newId)
1515  {
1516  kError() << "Same IDs";
1517  return false;
1518  }
1519  if (!mOpen)
1520  return false;
1521  if (mCalType == RESOURCES)
1522  {
1523 #ifdef USE_AKONADI
1524  // Set the event's ID and Akonadi ID, and update the old
1525  // event in Akonadi.
1526  KAEvent* storedEvent = event(oldEventId);
1527  if (!storedEvent)
1528  {
1529  kError() << "Old event not found";
1530  return false;
1531  }
1532  if (noNewId)
1533  newEvent.setEventId(CalFormat::createUniqueId());
1534  Collection c = AkonadiModel::instance()->collectionById(oldEventId.collectionId());
1535  if (!c.isValid())
1536  return false;
1537  // Don't add new event to mEventMap yet - its Akonadi item id is not yet known
1538  if (!AkonadiModel::instance()->addEvent(newEvent, c))
1539  return false;
1540  // Note: deleteEventInternal() will delete storedEvent before using the
1541  // event parameter, so need to pass a copy as the parameter.
1542  deleteEventInternal(KAEvent(*storedEvent), c);
1543  if (mHaveDisabledAlarms)
1544  checkForDisabledAlarms();
1545 #else
1546  // Create a new KCal::Event, keeping any custom properties from the old event.
1547  // Ensure it has a new ID.
1548  Event* kcalEvent = createKCalEvent(newEvent, oldEventId);
1549  if (noNewId)
1550  kcalEvent->setUid(CalFormat::createUniqueId());
1551  AlarmResources* resources = AlarmResources::instance();
1552  AlarmResource* resource = resources->resourceForIncidence(oldEventId);
1553  if (!resources->addEvent(kcalEvent, resource))
1554  return false; // kcalEvent has been deleted by AlarmResources::addEvent()
1555  if (noNewId)
1556  newEvent->setEventId(kcalEvent->uid());
1557  addEvent(resource, newEvent);
1558  deleteEvent(oldEventId); // this calls checkForDisabledAlarms()
1559 #endif
1560  }
1561  else
1562  {
1563 #ifdef USE_AKONADI
1564  // This functionality isn't needed for the display calendar.
1565  // The calendar would take ownership of newEvent.
1566  return false;
1567 #else
1568  if (!addEvent(newEvent, 0, true))
1569  return false;
1570  deleteEvent(oldEventId); // this calls checkForDisabledAlarms()
1571 #endif
1572  }
1573  return true;
1574 }
1575 
1576 /******************************************************************************
1577 * Update the specified event in the calendar with its new contents.
1578 * The event retains the same ID. The event must be in the resource calendar.
1579 * Reply = event which has been updated
1580 * = 0 if error.
1581 */
1582 KAEvent* AlarmCalendar::updateEvent(const KAEvent& evnt)
1583 {
1584  return updateEvent(&evnt);
1585 }
1586 KAEvent* AlarmCalendar::updateEvent(const KAEvent* evnt)
1587 {
1588  if (!mOpen || mCalType != RESOURCES)
1589  return 0;
1590 #ifdef USE_AKONADI
1591  KAEvent* kaevnt = event(EventId(*evnt));
1592  if (kaevnt)
1593  {
1594  KAEvent newEvnt(*evnt);
1595  newEvnt.setItemId(evnt->itemId());
1596  if (AkonadiModel::instance()->updateEvent(newEvnt))
1597  {
1598  *kaevnt = newEvnt;
1599  return kaevnt;
1600  }
1601  }
1602 #else
1603  QString id = evnt->id();
1604  KAEvent* kaevnt = event(id);
1605  Event* kcalEvent = mCalendar ? mCalendar->event(id) : 0;
1606  if (kaevnt && kcalEvent)
1607  {
1608  evnt->updateKCalEvent(kcalEvent, KAEvent::UID_CHECK);
1609  bool oldEnabled = kaevnt->enabled();
1610  if (kaevnt != evnt)
1611  *kaevnt = *evnt; // update the event instance in our lists, keeping the same pointer
1612  findEarliestAlarm(AlarmResources::instance()->resource(kcalEvent));
1613  if (mCalType == RESOURCES && evnt->category() == CalEvent::ACTIVE)
1614  checkForDisabledAlarms(oldEnabled, evnt->enabled());
1615  return kaevnt;
1616  }
1617 #endif
1618  kDebug() << "error";
1619  return 0;
1620 }
1621 
1622 
1623 #ifdef USE_AKONADI
1624 /******************************************************************************
1625 * Delete the specified event from the resource calendar, if it exists.
1626 * The calendar is then optionally saved.
1627 */
1628 bool AlarmCalendar::deleteEvent(const KAEvent& event, bool saveit)
1629 {
1630  if (mOpen && mCalType == RESOURCES)
1631  {
1632  CalEvent::Type status = deleteEventInternal(event);
1633  if (mHaveDisabledAlarms)
1634  checkForDisabledAlarms();
1635  if (status != CalEvent::EMPTY)
1636  {
1637  if (saveit)
1638  return save();
1639  return true;
1640  }
1641  }
1642  return false;
1643 }
1644 #endif
1645 
1646 /******************************************************************************
1647 * Delete the specified event from the calendar, if it exists.
1648 * The calendar is then optionally saved.
1649 */
1650 #ifdef USE_AKONADI
1651 bool AlarmCalendar::deleteDisplayEvent(const QString& eventID, bool saveit)
1652 #else
1653 bool AlarmCalendar::deleteEvent(const QString& eventID, bool saveit)
1654 #endif
1655 {
1656 #ifdef USE_AKONADI
1657  if (mOpen && mCalType != RESOURCES)
1658 #else
1659  if (mOpen)
1660 #endif
1661  {
1662  CalEvent::Type status = deleteEventInternal(eventID);
1663  if (mHaveDisabledAlarms)
1664  checkForDisabledAlarms();
1665  if (status != CalEvent::EMPTY)
1666  {
1667  if (saveit)
1668  return save();
1669  return true;
1670  }
1671  }
1672  return false;
1673 }
1674 
1675 /******************************************************************************
1676 * Internal method to delete the specified event from the calendar and lists.
1677 * Reply = event status, if it was found in the resource calendar/collection or
1678 * local calendar
1679 * = CalEvent::EMPTY otherwise.
1680 */
1681 #ifdef USE_AKONADI
1682 CalEvent::Type AlarmCalendar::deleteEventInternal(const KAEvent& event, bool deleteFromAkonadi)
1683 {
1684  Collection collection = AkonadiModel::instance()->collectionById(event.collectionId());
1685  if (!collection.isValid())
1686  return CalEvent::EMPTY;
1687  return deleteEventInternal(event.id(), event, collection, deleteFromAkonadi);
1688 }
1689 
1690 CalEvent::Type AlarmCalendar::deleteEventInternal(const KAEvent& event, const Collection& collection, bool deleteFromAkonadi)
1691 {
1692  if (!collection.isValid())
1693  return CalEvent::EMPTY;
1694  if (event.collectionId() != collection.id())
1695  {
1696  kError() << "Event" << event.id() << ": collection" << event.collectionId() << "differs from 'collection'" << collection.id();
1697  return CalEvent::EMPTY;
1698  }
1699  return deleteEventInternal(event.id(), event, collection, deleteFromAkonadi);
1700 }
1701 
1702 CalEvent::Type AlarmCalendar::deleteEventInternal(const QString& eventID, const KAEvent& event, const Collection& collection, bool deleteFromAkonadi)
1703 #else
1704 CalEvent::Type AlarmCalendar::deleteEventInternal(const QString& eventID)
1705 #endif
1706 {
1707  // Make a copy of the KAEvent and the ID QString, since the supplied
1708  // references might be destructed when the event is deleted below.
1709  const QString id = eventID;
1710 #ifdef USE_AKONADI
1711  const KAEvent paramEvent = event;
1712 
1713  Event::Ptr kcalEvent;
1714  if (mCalendarStorage)
1715  kcalEvent = mCalendarStorage->calendar()->event(id);
1716  Collection::Id key = collection.isValid() ? collection.id() : -1;
1717  KAEventMap::Iterator it = mEventMap.find(EventId(key, id));
1718 #else
1719  Event* kcalEvent = mCalendar ? mCalendar->event(id) : 0;
1720  KAEventMap::Iterator it = mEventMap.find(id);
1721 #endif
1722  if (it != mEventMap.end())
1723  {
1724  KAEvent* ev = it.value();
1725  mEventMap.erase(it);
1726 #ifndef USE_AKONADI
1727  AlarmResource* key = AlarmResources::instance()->resource(kcalEvent);
1728 #endif
1729  KAEvent::List& events = mResourceMap[key];
1730  int i = events.indexOf(ev);
1731  if (i >= 0)
1732  events.remove(i);
1733  delete ev;
1734  if (mEarliestAlarm[key] == ev)
1735 #ifdef USE_AKONADI
1736  findEarliestAlarm(collection);
1737 #else
1738  findEarliestAlarm(key);
1739 #endif
1740  }
1741  else
1742  {
1743  for (EarliestMap::Iterator eit = mEarliestAlarm.begin(); eit != mEarliestAlarm.end(); ++eit)
1744  {
1745  KAEvent* ev = eit.value();
1746  if (ev && ev->id() == id)
1747  {
1748  findEarliestAlarm(eit.key());
1749  break;
1750  }
1751  }
1752  }
1753  CalEvent::Type status = CalEvent::EMPTY;
1754  if (kcalEvent)
1755  {
1756  status = CalEvent::status(kcalEvent);
1757 #ifdef USE_AKONADI
1758  mCalendarStorage->calendar()->deleteEvent(kcalEvent);
1759 #else
1760  mCalendar->deleteEvent(kcalEvent);
1761 #endif
1762  }
1763 #ifdef USE_AKONADI
1764  else if (deleteFromAkonadi)
1765  {
1766  // It's an Akonadi event
1767  CalEvent::Type s = paramEvent.category();
1768  if (AkonadiModel::instance()->deleteEvent(paramEvent))
1769  status = s;
1770  }
1771 #else
1772 
1773  // Delete any command execution error flags for the alarm.
1774  KConfigGroup config(KGlobal::config(), KAEvent::commandErrorConfigGroup());
1775  if (config.hasKey(id))
1776  {
1777  config.deleteEntry(id);
1778  config.sync();
1779  }
1780 #endif
1781  return status;
1782 }
1783 
1784 #ifndef USE_AKONADI
1785 /******************************************************************************
1786 * Return a new KCal::Event representing the specified KAEvent.
1787 * If the event exists in the calendar, custom properties are copied from there.
1788 * The caller takes ownership of the returned KCal::Event. Note that the ID of
1789 * the returned KCal::Event may be the same as an existing calendar event, so
1790 * be careful not to end up duplicating IDs.
1791 * If it's an archived alarm, the event start date/time is adjusted to its
1792 * original value instead of its next occurrence, and the expired main alarm is
1793 * reinstated.
1794 */
1795 Event* AlarmCalendar::createKCalEvent(const KAEvent* ev, const QString& baseID) const
1796 {
1797  if (mCalType != RESOURCES)
1798  kFatal() << "AlarmCalendar::createKCalEvent(KAEvent): invalid for display calendar";
1799  // If the event exists in the calendar, we want to keep any custom
1800  // properties. So copy the calendar KCal::Event to base the new one on.
1801  QString id = baseID.isEmpty() ? ev->id() : baseID;
1802  Event* calEvent = id.isEmpty() ? 0 : AlarmResources::instance()->event(id);
1803  Event* newEvent = calEvent ? new Event(*calEvent) : new Event;
1804  ev->updateKCalEvent(newEvent, KAEvent::UID_SET);
1805  return newEvent;
1806 }
1807 #endif
1808 
1809 /******************************************************************************
1810 * Return the event with the specified ID.
1811 * If 'checkDuplicates' is true, and the collection ID is invalid, if there is
1812 * a unique event with the given ID, it will be returned.
1813 */
1814 #ifdef USE_AKONADI
1815 KAEvent* AlarmCalendar::event(const EventId& uniqueID, bool checkDuplicates)
1816 #else
1817 KAEvent* AlarmCalendar::event(const QString& uniqueID)
1818 #endif
1819 {
1820  if (!isValid())
1821  return 0;
1822 #ifdef USE_AKONADI
1823  const QString eventId = uniqueID.eventId();
1824  if (uniqueID.collectionId() == -1 && checkDuplicates)
1825  {
1826  // The collection isn't known, but use the event ID if it is
1827  // unique among all collections.
1828  KAEvent::List list = events(eventId);
1829  if (list.count() > 1)
1830  {
1831  kWarning() << "Multiple events found with ID" << eventId;
1832  return 0;
1833  }
1834  if (list.isEmpty())
1835  return 0;
1836  return list[0];
1837  }
1838 #endif
1839  KAEventMap::ConstIterator it = mEventMap.constFind(uniqueID);
1840  if (it == mEventMap.constEnd())
1841  return 0;
1842  return it.value();
1843 }
1844 
1845 /******************************************************************************
1846 * Return the event with the specified ID.
1847 * For the Akonadi version, this method is for the display calendar only.
1848 */
1849 #ifdef USE_AKONADI
1850 Event::Ptr AlarmCalendar::kcalEvent(const QString& uniqueID)
1851 {
1852  Q_ASSERT(mCalType != RESOURCES); // only allowed for display calendar
1853  if (!mCalendarStorage)
1854  return Event::Ptr();
1855  return mCalendarStorage->calendar()->event(uniqueID);
1856 }
1857 #else
1858 Event* AlarmCalendar::kcalEvent(const QString& uniqueID)
1859 {
1860  return mCalendar ? mCalendar->event(uniqueID) : 0;
1861 }
1862 #endif
1863 
1864 /******************************************************************************
1865 * Find the alarm template with the specified name.
1866 * Reply = 0 if not found.
1867 */
1868 KAEvent* AlarmCalendar::templateEvent(const QString& templateName)
1869 {
1870  if (templateName.isEmpty())
1871  return 0;
1872  KAEvent::List eventlist = events(CalEvent::TEMPLATE);
1873  for (int i = 0, end = eventlist.count(); i < end; ++i)
1874  {
1875  if (eventlist[i]->templateName() == templateName)
1876  return eventlist[i];
1877  }
1878  return 0;
1879 }
1880 
1881 #ifdef USE_AKONADI
1882 /******************************************************************************
1883 * Return all events with the specified ID, from all calendars.
1884 */
1885 KAEvent::List AlarmCalendar::events(const QString& uniqueId) const
1886 {
1887  KAEvent::List list;
1888  if (mCalType == RESOURCES && isValid())
1889  {
1890  for (ResourceMap::ConstIterator rit = mResourceMap.constBegin(); rit != mResourceMap.constEnd(); ++rit)
1891  {
1892  const Collection::Id id = rit.key();
1893  KAEventMap::ConstIterator it = mEventMap.constFind(EventId(id, uniqueId));
1894  if (it != mEventMap.constEnd())
1895  list += it.value();
1896  }
1897  }
1898  return list;
1899 }
1900 #endif
1901 
1902 /******************************************************************************
1903 * Return all events in the calendar which contain alarms.
1904 * Optionally the event type can be filtered, using an OR of event types.
1905 */
1906 #ifdef USE_AKONADI
1907 KAEvent::List AlarmCalendar::events(const Collection& collection, CalEvent::Types type) const
1908 #else
1909 KAEvent::List AlarmCalendar::events(AlarmResource* resource, CalEvent::Types type) const
1910 #endif
1911 {
1912  KAEvent::List list;
1913 #ifdef USE_AKONADI
1914  if (mCalType != RESOURCES && (!mCalendarStorage || collection.isValid()))
1915  return list;
1916  if (collection.isValid())
1917 #else
1918  if (!mCalendar || (resource && mCalType != RESOURCES))
1919  return list;
1920  if (resource)
1921 #endif
1922  {
1923 #ifdef USE_AKONADI
1924  Collection::Id key = collection.isValid() ? collection.id() : -1;
1925  ResourceMap::ConstIterator rit = mResourceMap.constFind(key);
1926 #else
1927  ResourceMap::ConstIterator rit = mResourceMap.constFind(resource);
1928 #endif
1929  if (rit == mResourceMap.constEnd())
1930  return list;
1931  const KAEvent::List events = rit.value();
1932  if (type == CalEvent::EMPTY)
1933  return events;
1934  for (int i = 0, end = events.count(); i < end; ++i)
1935  if (type & events[i]->category())
1936  list += events[i];
1937  }
1938  else
1939  {
1940  for (ResourceMap::ConstIterator rit = mResourceMap.constBegin(); rit != mResourceMap.constEnd(); ++rit)
1941  {
1942  const KAEvent::List events = rit.value();
1943  if (type == CalEvent::EMPTY)
1944  list += events;
1945  else
1946  {
1947  for (int i = 0, end = events.count(); i < end; ++i)
1948  if (type & events[i]->category())
1949  list += events[i];
1950  }
1951  }
1952  }
1953  return list;
1954 }
1955 
1956 /******************************************************************************
1957 * Return all events in the calendar which contain usable alarms.
1958 * For the Akonadi version, this method is for the display calendar only.
1959 * Optionally the event type can be filtered, using an OR of event types.
1960 */
1961 #ifdef USE_AKONADI
1962 Event::List AlarmCalendar::kcalEvents(CalEvent::Type type)
1963 #else
1964 Event::List AlarmCalendar::kcalEvents(AlarmResource* resource, CalEvent::Type type)
1965 #endif
1966 {
1967  Event::List list;
1968 #ifdef USE_AKONADI
1969  Q_ASSERT(mCalType != RESOURCES); // only allowed for display calendar
1970  if (!mCalendarStorage)
1971  return list;
1972  list = mCalendarStorage->calendar()->rawEvents();
1973 #else
1974  if (!mCalendar || (resource && mCalType != RESOURCES))
1975  return list;
1976  list = resource ? AlarmResources::instance()->rawEvents(resource) : mCalendar->rawEvents();
1977 #endif
1978  for (int i = 0; i < list.count(); )
1979  {
1980 #ifdef USE_AKONADI
1981  Event::Ptr event = list[i];
1982 #else
1983  Event* event = list[i];
1984 #endif
1985  if (event->alarms().isEmpty()
1986  || (type != CalEvent::EMPTY && !(type & CalEvent::status(event)))
1987  || !KAEvent(event).isValid())
1988 #ifdef USE_AKONADI
1989  list.remove(i);
1990 #else
1991  list.removeAt(i);
1992 #endif
1993  else
1994  ++i;
1995  }
1996  return list;
1997 }
1998 
1999 #ifndef USE_AKONADI
2000 /******************************************************************************
2001 * Return all events which have alarms falling within the specified time range.
2002 * 'type' is the OR'ed desired event types.
2003 */
2004 KAEvent::List AlarmCalendar::events(const KDateTime& from, const KDateTime& to, CalEvent::Types type)
2005 {
2006  kDebug() << from << "-" << to;
2007  KAEvent::List evnts;
2008  if (!isValid())
2009  return evnts;
2010  KDateTime dt;
2011  AlarmResources* resources = AlarmResources::instance();
2012  KAEvent::List allEvents = events(type);
2013  for (int i = 0, end = allEvents.count(); i < end; ++i)
2014  {
2015  KAEvent* event = allEvents[i];
2016  Event* e = resources->event(event->id());
2017  bool recurs = e->recurs();
2018  int endOffset = 0;
2019  bool endOffsetValid = false;
2020  const Alarm::List& alarms = e->alarms();
2021  for (int ai = 0, aend = alarms.count(); ai < aend; ++ai)
2022  {
2023  Alarm* alarm = alarms[ai];
2024  if (alarm->enabled())
2025  {
2026  if (recurs)
2027  {
2028  if (alarm->hasTime())
2029  dt = alarm->time();
2030  else
2031  {
2032  // The alarm time is defined by an offset from the event start or end time.
2033  // Find the offset from the event start time, which is also used as the
2034  // offset from the recurrence time.
2035  int offset = 0;
2036  if (alarm->hasStartOffset())
2037  offset = alarm->startOffset().asSeconds();
2038  else if (alarm->hasEndOffset())
2039  {
2040  if (!endOffsetValid)
2041  {
2042  endOffset = e->hasDuration() ? e->duration().asSeconds() : e->hasEndDate() ? e->dtStart().secsTo(e->dtEnd()) : 0;
2043  endOffsetValid = true;
2044  }
2045  offset = alarm->endOffset().asSeconds() + endOffset;
2046  }
2047  // Adjust the 'from' date/time and find the next recurrence at or after it
2048  KDateTime pre = from.addSecs(-offset - 1);
2049  if (e->allDay() && pre.time() < Preferences::startOfDay())
2050  pre = pre.addDays(-1); // today's recurrence (if today recurs) is still to come
2051  dt = e->recurrence()->getNextDateTime(pre);
2052  if (!dt.isValid())
2053  continue;
2054  dt = dt.addSecs(offset);
2055  }
2056  }
2057  else
2058  dt = alarm->time();
2059  if (dt >= from && dt <= to)
2060  {
2061  kDebug() << "'" << e->summary() << "':" << dt;
2062  evnts.append(event);
2063  break;
2064  }
2065  }
2066  }
2067  }
2068  return evnts;
2069 }
2070 #endif
2071 
2072 /******************************************************************************
2073 * Return whether an event is read-only.
2074 * Display calendar events are always returned as read-only.
2075 */
2076 #ifdef USE_AKONADI
2077 bool AlarmCalendar::eventReadOnly(Item::Id id) const
2078 {
2079  if (mCalType != RESOURCES)
2080  return true;
2081  AkonadiModel* model = AkonadiModel::instance();
2082  Collection collection = model->collectionForItem(id);
2083  KAEvent event = model->event(id);
2084  if (!CollectionControlModel::isWritableEnabled(collection, event.category()))
2085  return true;
2086  return !event.isValid() || event.isReadOnly();
2087  // || compatibility(event) != KACalendar::Current;
2088 }
2089 #else
2090 bool AlarmCalendar::eventReadOnly(const QString& uniqueID) const
2091 {
2092  if (!mCalendar || mCalType != RESOURCES)
2093  return true;
2094  AlarmResources* resources = AlarmResources::instance();
2095  const Event* event = resources->event(uniqueID);
2096  AlarmResource* resource = resources->resource(event);
2097  if (!resource)
2098  return true;
2099  return !resource->writable(event);
2100 }
2101 #endif
2102 
2103 #ifdef USE_AKONADI
2104 /******************************************************************************
2105 * Return the collection containing a specified event.
2106 */
2107 Collection AlarmCalendar::collectionForEvent(Item::Id itemId) const
2108 {
2109  if (mCalType != RESOURCES)
2110  return Collection();
2111  return AkonadiModel::instance()->collectionForItem(itemId);
2112 }
2113 #else
2114 /******************************************************************************
2115 * Return the resource containing a specified event.
2116 */
2117 AlarmResource* AlarmCalendar::resourceForEvent(const QString& eventID) const
2118 {
2119  if (!mCalendar || mCalType != RESOURCES)
2120  return 0;
2121  return AlarmResources::instance()->resourceForIncidence(eventID);
2122 }
2123 #endif
2124 
2125 /******************************************************************************
2126 * Called when an alarm's enabled status has changed.
2127 */
2128 void AlarmCalendar::disabledChanged(const KAEvent* event)
2129 {
2130  if (event->category() == CalEvent::ACTIVE)
2131  {
2132  bool status = event->enabled();
2133  checkForDisabledAlarms(!status, status);
2134  }
2135 }
2136 
2137 /******************************************************************************
2138 * Check whether there are any individual disabled alarms, following an alarm
2139 * creation or modification. Must only be called for an ACTIVE alarm.
2140 */
2141 void AlarmCalendar::checkForDisabledAlarms(bool oldEnabled, bool newEnabled)
2142 {
2143  if (mCalType == RESOURCES && newEnabled != oldEnabled)
2144  {
2145  if (newEnabled && mHaveDisabledAlarms)
2146  checkForDisabledAlarms();
2147  else if (!newEnabled && !mHaveDisabledAlarms)
2148  {
2149  mHaveDisabledAlarms = true;
2150  emit haveDisabledAlarmsChanged(true);
2151  }
2152  }
2153 }
2154 
2155 /******************************************************************************
2156 * Check whether there are any individual disabled alarms.
2157 */
2158 void AlarmCalendar::checkForDisabledAlarms()
2159 {
2160  if (mCalType != RESOURCES)
2161  return;
2162  bool disabled = false;
2163  KAEvent::List eventlist = events(CalEvent::ACTIVE);
2164  for (int i = 0, end = eventlist.count(); i < end; ++i)
2165  {
2166  if (!eventlist[i]->enabled())
2167  {
2168  disabled = true;
2169  break;
2170  }
2171  }
2172  if (disabled != mHaveDisabledAlarms)
2173  {
2174  mHaveDisabledAlarms = disabled;
2175  emit haveDisabledAlarmsChanged(disabled);
2176  }
2177 }
2178 
2179 /******************************************************************************
2180 * Return a list of all active at-login alarms.
2181 */
2182 KAEvent::List AlarmCalendar::atLoginAlarms() const
2183 {
2184  KAEvent::List atlogins;
2185 #ifdef USE_AKONADI
2186  if (mCalType != RESOURCES)
2187  return atlogins;
2188  AkonadiModel* model = AkonadiModel::instance();
2189  if (!mCalendarStorage || mCalType != RESOURCES)
2190  return atlogins;
2191 #else
2192  if (!mCalendar || mCalType != RESOURCES)
2193  return atlogins;
2194 #endif
2195  for (ResourceMap::ConstIterator rit = mResourceMap.constBegin(); rit != mResourceMap.constEnd(); ++rit)
2196  {
2197 #ifdef USE_AKONADI
2198  const Collection::Id id = rit.key();
2199  if (id < 0
2200  || !(AkonadiModel::types(model->collectionById(id)) & CalEvent::ACTIVE))
2201  continue;
2202 #else
2203  const AlarmResource* resource = rit.key();
2204  if (!resource || resource->alarmType() != CalEvent::ACTIVE)
2205  continue;
2206 #endif
2207  const KAEvent::List& events = rit.value();
2208  for (int i = 0, end = events.count(); i < end; ++i)
2209  {
2210  KAEvent* event = events[i];
2211  if (event->category() == CalEvent::ACTIVE && event->repeatAtLogin())
2212  atlogins += event;
2213  }
2214  }
2215  return atlogins;
2216 }
2217 
2218 /******************************************************************************
2219 * Find and note the active alarm with the earliest trigger time for a calendar.
2220 */
2221 #ifdef USE_AKONADI
2222 void AlarmCalendar::findEarliestAlarm(const Collection& collection)
2223 {
2224  if (mCalType != RESOURCES)
2225  return;
2226  if (!collection.isValid()
2227  || !(AkonadiModel::types(collection) & CalEvent::ACTIVE))
2228  return;
2229  findEarliestAlarm(collection.id());
2230 }
2231 
2232 void AlarmCalendar::findEarliestAlarm(Collection::Id key)
2233 #else
2234 void AlarmCalendar::findEarliestAlarm(AlarmResource* key)
2235 #endif
2236 {
2237  EarliestMap::Iterator eit = mEarliestAlarm.find(key);
2238  if (eit != mEarliestAlarm.end())
2239  eit.value() = 0;
2240 #ifdef USE_AKONADI
2241  if (mCalType != RESOURCES
2242  || key < 0)
2243  return;
2244 #else
2245  if (!mCalendar || mCalType != RESOURCES
2246  || !key || key->alarmType() != CalEvent::ACTIVE)
2247  return;
2248 #endif
2249  ResourceMap::ConstIterator rit = mResourceMap.constFind(key);
2250  if (rit == mResourceMap.constEnd())
2251  return;
2252  const KAEvent::List& events = rit.value();
2253  KAEvent* earliest = 0;
2254  KDateTime earliestTime;
2255  for (int i = 0, end = events.count(); i < end; ++i)
2256  {
2257  KAEvent* event = events[i];
2258  if (event->category() != CalEvent::ACTIVE
2259  || mPendingAlarms.contains(event->id()))
2260  continue;
2261  KDateTime dt = event->nextTrigger(KAEvent::ALL_TRIGGER).effectiveKDateTime();
2262  if (dt.isValid() && (!earliest || dt < earliestTime))
2263  {
2264  earliestTime = dt;
2265  earliest = event;
2266  }
2267  }
2268  mEarliestAlarm[key] = earliest;
2269  emit earliestAlarmChanged();
2270 }
2271 
2272 /******************************************************************************
2273 * Return the active alarm with the earliest trigger time.
2274 * Reply = 0 if none.
2275 */
2276 KAEvent* AlarmCalendar::earliestAlarm() const
2277 {
2278  KAEvent* earliest = 0;
2279  KDateTime earliestTime;
2280  for (EarliestMap::ConstIterator eit = mEarliestAlarm.constBegin(); eit != mEarliestAlarm.constEnd(); ++eit)
2281  {
2282  KAEvent* event = eit.value();
2283  if (!event)
2284  continue;
2285  KDateTime dt = event->nextTrigger(KAEvent::ALL_TRIGGER).effectiveKDateTime();
2286  if (dt.isValid() && (!earliest || dt < earliestTime))
2287  {
2288  earliestTime = dt;
2289  earliest = event;
2290  }
2291  }
2292  return earliest;
2293 }
2294 
2295 /******************************************************************************
2296 * Note that an alarm which has triggered is now being processed. While pending,
2297 * it will be ignored for the purposes of finding the earliest trigger time.
2298 */
2299 void AlarmCalendar::setAlarmPending(KAEvent* event, bool pending)
2300 {
2301  QString id = event->id();
2302  bool wasPending = mPendingAlarms.contains(id);
2303  kDebug() << id << "," << pending << "(was" << wasPending << ")";
2304  if (pending)
2305  {
2306  if (wasPending)
2307  return;
2308  mPendingAlarms.append(id);
2309  }
2310  else
2311  {
2312  if (!wasPending)
2313  return;
2314  mPendingAlarms.removeAll(id);
2315  }
2316  // Now update the earliest alarm to trigger for its calendar
2317 #ifdef USE_AKONADI
2318  findEarliestAlarm(AkonadiModel::instance()->collection(*event));
2319 #else
2320  findEarliestAlarm(AlarmResources::instance()->resourceForIncidence(id));
2321 #endif
2322 }
2323 
2324 /******************************************************************************
2325 * Called when the user changes the start-of-day time.
2326 * Adjust the start times of all date-only alarms' recurrences.
2327 */
2328 void AlarmCalendar::adjustStartOfDay()
2329 {
2330  if (!isValid())
2331  return;
2332  for (ResourceMap::ConstIterator rit = mResourceMap.constBegin(); rit != mResourceMap.constEnd(); ++rit)
2333  KAEvent::adjustStartOfDay(rit.value());
2334 }
2335 
2336 #ifdef USE_AKONADI
2337 /******************************************************************************
2338 * Find the version of KAlarm which wrote the calendar file, and do any
2339 * necessary conversions to the current format.
2340 */
2341 KACalendar::Compat fix(const FileStorage::Ptr& fileStorage)
2342 {
2343  QString versionString;
2344  int version = KACalendar::updateVersion(fileStorage, versionString);
2345  if (version == KACalendar::IncompatibleFormat)
2346  return KACalendar::Incompatible; // calendar was created by another program, or an unknown version of KAlarm
2347  return KACalendar::Current;
2348 }
2349 #endif
2350 #include "moc_alarmcalendar.cpp"
2351 
2352 // vim: et sw=4:
EventListModel::resourceStatusChanged
static void resourceStatusChanged(AlarmResource *, AlarmResources::Change)
Definition: eventlistmodel.cpp:545
QWidget
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:1653
QMap::erase
iterator erase(iterator pos)
QMap::contains
bool contains(const Key &key) const
EventListModel::alarms
static EventListModel * alarms()
Definition: eventlistmodel.cpp:55
AlarmCalendar::disabledChanged
void disabledChanged(const KAEvent *)
Definition: alarmcalendar.cpp:2128
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:2182
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:968
AkonadiModel::updateEvent
bool updateEvent(KAEvent &event)
Definition: akonadimodel.cpp:1378
AlarmCalendar::reloadFromCache
void reloadFromCache(const QString &resourceID)
Definition: alarmcalendar.cpp:853
AlarmCalendar::eventReadOnly
bool eventReadOnly(const QString &uniqueID) const
Definition: alarmcalendar.cpp:2090
AlarmCalendar::updateEvent
KAEvent * updateEvent(const KAEvent &)
Definition: alarmcalendar.cpp:1582
AlarmCalendar::kcalEvent
KCal::Event * kcalEvent(const QString &uniqueId)
Definition: alarmcalendar.cpp:1858
AlarmCalendar::earliestAlarmChanged
void earliestAlarmChanged()
AlarmCalendar::loadResource
void loadResource(AlarmResource *, QWidget *parent)
Definition: alarmcalendar.cpp:536
QMap::constBegin
const_iterator constBegin() const
AlarmCalendar::resources
static AlarmCalendar * resources()
Definition: alarmcalendar.h:130
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:1184
AkonadiModel::Enabled
Definition: akonadimodel.h:50
AlarmCalendar::addEvent
bool addEvent(KAEvent *, QWidget *promptParent=0, bool useEventID=false, AlarmResource *=0, bool noPrompt=false, bool *cancelled=0)
Definition: alarmcalendar.cpp:1258
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:740
QMap::constFind
const_iterator constFind(const Key &key) const
alarmcalendar.h
displayCalendarName
static const QString displayCalendarName
Definition: alarmcalendar.cpp:70
AlarmCalendar::resourceForEvent
AlarmResource * resourceForEvent(const QString &eventID) const
Definition: alarmcalendar.cpp:2117
from
QString from() const
AlarmCalendar::templateEvent
KAEvent * templateEvent(const QString &templateName)
Definition: alarmcalendar.cpp:1868
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
AlarmCalendar::open
bool open()
Definition: alarmcalendar.cpp:250
AlarmCalendar::isOpen
bool isOpen()
Definition: alarmcalendar.cpp:235
CollectionControlModel::isEnabled
static bool isEnabled(const Akonadi::Collection &, CalEvent::Type)
Return whether a collection is enabled (and valid).
Definition: collectionmodel.cpp:715
QString::isNull
bool isNull() const
FileDialog::getSaveFileName
static QString getSaveFileName(const KUrl &dir=KUrl(), const QString &filter=QString(), QWidget *parent=0, const QString &caption=QString(), bool *append=0)
QList::indexOf
int indexOf(const T &value, int from) const
QString::clear
void clear()
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:75
AlarmCalendar::earliestAlarm
KAEvent * earliestAlarm() const
Definition: alarmcalendar.cpp:2276
QList::count
int count(const T &value) const
QList::append
void append(const T &value)
AlarmCalendar::modifyEvent
bool modifyEvent(const QString &oldEventId, KAEvent *newEvent)
Definition: alarmcalendar.cpp:1504
Preferences::startOfDay
static QTime startOfDay()
Definition: preferences.h:57
AlarmCalendar::adjustStartOfDay
void adjustStartOfDay()
Definition: alarmcalendar.cpp:2328
QVariant::toInt
int toInt(bool *ok) const
AlarmCalendar::purgeEvents
void purgeEvents(const KAEvent::List &)
Definition: alarmcalendar.cpp:1226
AkonadiModel::Event::event
KAEvent event
Definition: akonadimodel.h:80
Preferences::timeZone
static KTimeZone timeZone(bool reload=false)
Definition: preferences.cpp:199
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:1180
AkonadiModel::instance
static AkonadiModel * instance()
Definition: akonadimodel.cpp:83
AkonadiModel::Event::collection
Akonadi::Collection collection
Definition: akonadimodel.h:81
CalendarCompat::fix
KACalendar::Compat fix(KCal::CalendarLocal &calendar, const QString &localFile, AlarmResource *resource, AlarmResource::FixFunc conv, bool *wrongType)
Definition: calendarcompat.cpp:41
QString::isEmpty
bool isEmpty() const
QList::removeAll
int removeAll(const T &value)
QMap::constEnd
const_iterator constEnd() const
mainwindow.h
main application window
to
QString to() const
eventlistmodel.h
AkonadiModel::Change
Change
Definition: akonadimodel.h:50
messagebox.h
QString
QList
QMap::end
iterator end()
AlarmCalendar::path
QString path() const
Definition: alarmcalendar.h:124
AlarmCalendar::endUpdate
bool endUpdate()
Definition: alarmcalendar.cpp:1193
QMap::begin
iterator begin()
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:369
QList::contains
bool contains(const T &value) const
QString::replace
QString & replace(int position, int n, QChar after)
collectionmodel.h
EventId
Unique event identifier for Akonadi.
Definition: eventid.h:38
AkonadiModel::types
static CalEvent::Types types(const Akonadi::Collection &)
Definition: akonadimodel.cpp:1926
AlarmCalendar::kcalEvents
KCal::Event::List kcalEvents(CalEvent::Type s=CalEvent::EMPTY)
Definition: alarmcalendar.h:107
AkonadiModel::Event::eventId
EventId eventId() const
Definition: akonadimodel.h:78
AlarmCalendar::~AlarmCalendar
virtual ~AlarmCalendar()
Definition: alarmcalendar.cpp:227
AkonadiModel::Event::isConsistent
bool isConsistent() const
Definition: akonadimodel.h:79
QLatin1String
functions.h
miscellaneous functions
kalarm.h
AlarmCalendar::setAlarmPending
void setAlarmPending(KAEvent *, bool pending=true)
Definition: alarmcalendar.cpp:2299
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
QString::fromLatin1
QString fromLatin1(const char *str, int size)
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
QMap::isEmpty
bool isEmpty() const
QString::find
int find(QChar c, int i, bool cs) const
QMap< AlarmResource *, KAEvent::List >::Iterator
typedef Iterator
AlarmCalendar::exportAlarms
static bool exportAlarms(const KAEvent::List &, QWidget *parent)
Definition: alarmcalendar.cpp:1074
AlarmCalendar::event
KAEvent * event(const QString &uniqueId)
Definition: alarmcalendar.cpp:1817
AlarmCalendar::save
bool save()
Definition: alarmcalendar.cpp:1209
QMap< AlarmResource *, KAEvent::List >::ConstIterator
typedef ConstIterator
MainWindow::mainMainWindow
static MainWindow * mainMainWindow()
Definition: mainwindow.cpp:286
QObject::connect
bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
calendarcompat.h
AkonadiModel::collectionForItem
Akonadi::Collection collectionForItem(Akonadi::Item::Id) const
Definition: akonadimodel.cpp:1876
AlarmCalendar::haveDisabledAlarmsChanged
void haveDisabledAlarmsChanged(bool haveDisabled)
KAlarmApp::displayFatalError
static void displayFatalError(const QString &message)
Definition: kalarmapp.cpp:765
AkonadiModel
Definition: akonadimodel.h:46
QMap::find
iterator find(const Key &key)
AlarmCalendar::close
void close()
Definition: alarmcalendar.cpp:495
AkonadiModel::addEvent
bool addEvent(KAEvent &, Akonadi::Collection &)
Definition: akonadimodel.cpp:1351
QMap::remove
int remove(const Key &key)
QVariant
filedialog.h
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