2 * alarmcalendar.cpp - KAlarm calendar file access
4 * Copyright © 2001-2014 by David Jarvie <djarvie@kde.org>
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.
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.
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.
22 #include "alarmcalendar.h"
24 #include "collectionmodel.h"
25 #include "filedialog.h"
26 #include "functions.h"
27 #include "kalarmapp.h"
28 #include "mainwindow.h"
29 #include "messagebox.h"
30 #include "preferences.h"
32 #include <KCalCore/MemoryCalendar>
33 #include <KCalCore/ICalFormat>
36 #include <KLocalizedString>
37 #include <KIO/StoredTransferJob>
38 #include <KJobWidgets>
39 #include <kfileitem.h>
40 #include <KSharedConfig>
41 #include <QTemporaryFile>
42 #include <QStandardPaths>
43 #include "kalarm_debug.h"
45 using namespace Akonadi
;
46 using namespace KCalCore
;
47 using namespace KAlarmCal
;
49 static KACalendar::Compat
fix(const KCalCore::FileStorage::Ptr
&);
51 static const QString displayCalendarName
= QStringLiteral("displaying.ics");
52 static const Collection::Id DISPLAY_COL_ID
= -1; // collection ID used for displaying calendar
54 AlarmCalendar
* AlarmCalendar::mResourcesCalendar
= Q_NULLPTR
;
55 AlarmCalendar
* AlarmCalendar::mDisplayCalendar
= Q_NULLPTR
;
58 /******************************************************************************
59 * Initialise the alarm calendars, and ensure that their file names are different.
60 * There are 2 calendars:
61 * 1) A resources calendar containing the active alarms, archived alarms and
63 * 2) A user-specific one which contains details of alarms which are currently
64 * being displayed to that user and which have not yet been acknowledged;
65 * Reply = true if success, false if calendar name error.
67 bool AlarmCalendar::initialiseCalendars()
70 dir
.mkpath(QStandardPaths::writableLocation(QStandardPaths::DataLocation
));
71 QString displayCal
= QStandardPaths::writableLocation(QStandardPaths::DataLocation
) + QLatin1Char('/') + displayCalendarName
;
72 AkonadiModel::instance();
73 CollectionControlModel::setAskDestinationPolicy(Preferences::askResource());
74 Preferences::setBackend(Preferences::Akonadi
);
75 Preferences::self()->save();
76 mResourcesCalendar
= new AlarmCalendar();
77 mDisplayCalendar
= new AlarmCalendar(displayCal
, CalEvent::DISPLAYING
);
78 KACalendar::setProductId(KALARM_NAME
, KALARM_VERSION
);
79 CalFormat::setApplication(QStringLiteral(KALARM_NAME
), QString::fromLatin1(KACalendar::icalProductId()));
83 /******************************************************************************
84 * Terminate access to all calendars.
86 void AlarmCalendar::terminateCalendars()
88 delete mResourcesCalendar
;
89 mResourcesCalendar
= Q_NULLPTR
;
90 delete mDisplayCalendar
;
91 mDisplayCalendar
= Q_NULLPTR
;
94 /******************************************************************************
95 * Return the display calendar, opening it first if necessary.
97 AlarmCalendar
* AlarmCalendar::displayCalendarOpen()
99 if (mDisplayCalendar
->open())
100 return mDisplayCalendar
;
101 qCCritical(KALARM_LOG
) << "Open error";
105 /******************************************************************************
106 * Find and return the event with the specified ID.
107 * The calendar searched is determined by the calendar identifier in the ID.
109 KAEvent
* AlarmCalendar::getEvent(const EventId
& eventId
)
111 if (eventId
.eventId().isEmpty())
113 return mResourcesCalendar
->event(eventId
);
116 /******************************************************************************
117 * Constructor for the resources calendar.
119 AlarmCalendar::AlarmCalendar()
122 mEventType(CalEvent::EMPTY
),
126 mHaveDisabledAlarms(false)
128 AkonadiModel
* model
= AkonadiModel::instance();
129 connect(model
, &AkonadiModel::eventsAdded
, this, &AlarmCalendar::slotEventsAdded
);
130 connect(model
, &AkonadiModel::eventsToBeRemoved
, this, &AlarmCalendar::slotEventsToBeRemoved
);
131 connect(model
, &AkonadiModel::eventChanged
, this, &AlarmCalendar::slotEventChanged
);
132 connect(model
, &AkonadiModel::collectionStatusChanged
, this, &AlarmCalendar::slotCollectionStatusChanged
);
133 Preferences::connect(SIGNAL(askResourceChanged(bool)), this, SLOT(setAskResource(bool)));
136 /******************************************************************************
137 * Constructor for a calendar file.
139 AlarmCalendar::AlarmCalendar(const QString
& path
, CalEvent::Type type
)
145 mHaveDisabledAlarms(false)
149 case CalEvent::ACTIVE
:
150 case CalEvent::ARCHIVED
:
151 case CalEvent::TEMPLATE
:
152 case CalEvent::DISPLAYING
:
155 Q_ASSERT(false); // invalid event type for a calendar
158 mUrl
= QUrl::fromUserInput(path
, QString(), QUrl::AssumeLocalFile
);
159 QString icalPath
= path
;
160 icalPath
.replace(QStringLiteral("\\.vcs$"), QStringLiteral(".ics"));
161 mICalUrl
= QUrl::fromUserInput(icalPath
, QString(), QUrl::AssumeLocalFile
);
162 mCalType
= (path
== icalPath
) ? LOCAL_ICAL
: LOCAL_VCAL
; // is the calendar in ICal or VCal format?
165 AlarmCalendar::~AlarmCalendar()
170 /******************************************************************************
171 * Check whether the calendar is open.
173 bool AlarmCalendar::isOpen()
178 /******************************************************************************
179 * Open the calendar if not already open, and load it into memory.
181 bool AlarmCalendar::open()
185 if (mCalType
== RESOURCES
)
194 qCDebug(KALARM_LOG
) << mUrl
.toDisplayString();
195 if (!mCalendarStorage
)
197 MemoryCalendar::Ptr
calendar(new MemoryCalendar(Preferences::timeZone(true)));
198 mCalendarStorage
= FileStorage::Ptr(new FileStorage(calendar
));
201 // Check for file's existence, assuming that it does exist when uncertain,
202 // to avoid overwriting it.
203 auto statJob
= KIO::stat(mUrl
.url(), KIO::StatJob::SourceSide
, 2);
204 KJobWidgets::setWindow(statJob
, MainWindow::mainMainWindow());
205 if (!statJob
->exec() || load() == 0)
207 // The calendar file doesn't yet exist, or it's zero length, so create a new one
208 bool created
= false;
209 if (mICalUrl
.isLocalFile())
210 created
= saveCal(mICalUrl
.toLocalFile());
213 QTemporaryFile tmpFile
;
214 tmpFile
.setAutoRemove(false);
216 created
= saveCal(tmpFile
.fileName());
224 mCalendarStorage
->calendar().clear();
225 mCalendarStorage
.clear();
230 /******************************************************************************
231 * Load the calendar into memory.
232 * Reply = 1 if success
233 * = 0 if zero-length file exists.
234 * = -1 if failure to load calendar file
235 * = -2 if instance uninitialised.
237 int AlarmCalendar::load()
239 if (mCalType
== RESOURCES
)
244 if (!mCalendarStorage
)
248 qCDebug(KALARM_LOG
) << mUrl
.toDisplayString();
249 if (!mUrl
.isLocalFile()) {
250 auto getJob
= KIO::storedGet(mUrl
.url());
251 KJobWidgets::setWindow(getJob
, MainWindow::mainMainWindow());
254 qCCritical(KALARM_LOG
) << "Download failure";
255 KAMessageBox::error(MainWindow::mainMainWindow(),
256 xi18nc("@info", "Cannot download calendar: <filename>%1</filename>", mUrl
.toDisplayString()));
259 QTemporaryFile tmpFile
;
260 tmpFile
.setAutoRemove(false);
261 tmpFile
.write(getJob
->data());
262 qCDebug(KALARM_LOG
) << "--- Downloaded to" << tmpFile
.fileName();
263 filename
= tmpFile
.fileName();
265 filename
= mUrl
.toLocalFile();
267 mCalendarStorage
->calendar()->setTimeSpec(Preferences::timeZone(true));
268 mCalendarStorage
->setFileName(filename
);
269 if (!mCalendarStorage
->load())
271 // Check if the file is zero length
272 if (mUrl
.isLocalFile()) {
273 auto statJob
= KIO::stat(KIO::upUrl(mUrl
));
274 KJobWidgets::setWindow(statJob
, MainWindow::mainMainWindow());
276 KFileItem
fi(statJob
->statResult(), mUrl
);
278 return 0; // file is zero length
281 qCCritical(KALARM_LOG
) << "Error loading calendar file '" << filename
<<"'";
282 KAMessageBox::error(MainWindow::mainMainWindow(),
283 xi18nc("@info", "<para>Error loading calendar:</para><para><filename>%1</filename></para><para>Please fix or delete the file.</para>", mUrl
.toDisplayString()));
284 // load() could have partially populated the calendar, so clear it out
285 mCalendarStorage
->calendar()->close();
286 mCalendarStorage
->calendar().clear();
287 mCalendarStorage
.clear();
291 if (!mLocalFile
.isEmpty())
292 if (mLocalFile
.startsWith(QDir::tempPath())) {
293 QFile::remove(mLocalFile
);
295 mLocalFile
= filename
;
296 fix(mCalendarStorage
); // convert events to current KAlarm format for when calendar is saved
297 updateDisplayKAEvents();
303 /******************************************************************************
304 * Reload the calendar file into memory.
306 bool AlarmCalendar::reload()
308 if (mCalType
== RESOURCES
)
310 if (!mCalendarStorage
)
313 qCDebug(KALARM_LOG
) << mUrl
.toDisplayString();
319 /******************************************************************************
320 * Save the calendar from memory to file.
321 * If a filename is specified, create a new calendar file.
323 bool AlarmCalendar::saveCal(const QString
& newFile
)
325 if (mCalType
== RESOURCES
)
327 if (!mCalendarStorage
)
330 if (!mOpen
&& newFile
.isNull())
333 qCDebug(KALARM_LOG
) << "\"" << newFile
<< "\"," << mEventType
;
334 QString saveFilename
= newFile
.isNull() ? mLocalFile
: newFile
;
335 if (mCalType
== LOCAL_VCAL
&& newFile
.isNull() && mUrl
.isLocalFile())
336 saveFilename
= mICalUrl
.toLocalFile();
337 mCalendarStorage
->setFileName(saveFilename
);
338 mCalendarStorage
->setSaveFormat(new ICalFormat
);
339 if (!mCalendarStorage
->save())
341 qCCritical(KALARM_LOG
) << "Saving" << saveFilename
<< "failed.";
342 KAMessageBox::error(MainWindow::mainMainWindow(),
343 xi18nc("@info", "Failed to save calendar to <filename>%1</filename>", mICalUrl
.toDisplayString()));
347 if (!mICalUrl
.isLocalFile())
349 QFile
file(saveFilename
);
350 file
.open(QIODevice::ReadOnly
);
351 auto putJob
= KIO::storedPut(&file
, mICalUrl
.url(), -1);
352 KJobWidgets::setWindow(putJob
, MainWindow::mainMainWindow());
355 qCCritical(KALARM_LOG
) << saveFilename
<< "upload failed.";
356 KAMessageBox::error(MainWindow::mainMainWindow(),
357 xi18nc("@info", "Cannot upload calendar to <filename>%1</filename>", mICalUrl
.toDisplayString()));
362 if (mCalType
== LOCAL_VCAL
)
364 // The file was in vCalendar format, but has now been saved in iCalendar format.
366 mCalType
= LOCAL_ICAL
;
368 Q_EMIT
calendarSaved(this);
375 /******************************************************************************
376 * Delete any temporary file at program exit.
378 void AlarmCalendar::close()
380 if (mCalType
!= RESOURCES
)
382 if (!mLocalFile
.isEmpty())
384 if (mLocalFile
.startsWith(QDir::tempPath())) { // removes it only if it IS a temporary file
385 QFile::remove(mLocalFile
);
387 mLocalFile
= QStringLiteral("");
390 // Flag as closed now to prevent removeKAEvents() doing silly things
391 // when it's called again
393 if (mCalendarStorage
)
395 mCalendarStorage
->calendar()->close();
396 mCalendarStorage
->calendar().clear();
397 mCalendarStorage
.clear();
399 // Resource map should be empty, but just in case...
400 while (!mResourceMap
.isEmpty())
401 removeKAEvents(mResourceMap
.begin().key(), true, CalEvent::ACTIVE
| CalEvent::ARCHIVED
| CalEvent::TEMPLATE
| CalEvent::DISPLAYING
);
405 /******************************************************************************
406 * Update whether to prompt for the resource to store new alarms in.
408 void AlarmCalendar::setAskResource(bool ask
)
410 CollectionControlModel::setAskDestinationPolicy(ask
);
414 void AlarmCalendar::updateDisplayKAEvents()
416 if (mCalType
== RESOURCES
)
419 const Collection::Id key
= DISPLAY_COL_ID
;
420 KAEvent::List
& events
= mResourceMap
[key
];
422 for (i
= 0, end
= events
.count(); i
< end
; ++i
)
424 KAEvent
* event
= events
[i
];
425 mEventMap
.remove(EventId(key
, event
->id()));
429 mEarliestAlarm
[key
] = Q_NULLPTR
;
430 Calendar::Ptr cal
= mCalendarStorage
->calendar();
434 Event::List kcalevents
= cal
->rawEvents();
435 for (i
= 0, end
= kcalevents
.count(); i
< end
; ++i
)
437 Event::Ptr kcalevent
= kcalevents
[i
];
438 if (kcalevent
->alarms().isEmpty())
439 continue; // ignore events without alarms
441 KAEvent
* event
= new KAEvent(kcalevent
);
442 if (!event
->isValid())
444 qCWarning(KALARM_LOG
) << "Ignoring unusable event" << kcalevent
->uid();
446 continue; // ignore events without usable alarms
448 event
->setCollectionId(key
);
450 mEventMap
[EventId(key
, kcalevent
->uid())] = event
;
455 /******************************************************************************
456 * Delete a calendar and all its KAEvent instances of specified alarm types from
458 * Called after the calendar is deleted or alarm types have been disabled, or
459 * the AlarmCalendar is closed.
461 void AlarmCalendar::removeKAEvents(Collection::Id key
, bool closing
, CalEvent::Types types
)
463 bool removed
= false;
464 ResourceMap::Iterator rit
= mResourceMap
.find(key
);
465 if (rit
!= mResourceMap
.end())
468 KAEvent::List
& events
= rit
.value();
469 for (int i
= 0, end
= events
.count(); i
< end
; ++i
)
471 KAEvent
* event
= events
[i
];
472 bool remove
= (event
->collectionId() != key
);
475 if (key
!= DISPLAY_COL_ID
)
476 qCCritical(KALARM_LOG
) << "Event" << event
->id() << ", collection" << event
->collectionId() << "Indexed under collection" << key
;
479 remove
= event
->category() & types
;
482 mEventMap
.remove(EventId(key
, event
->id()));
490 mResourceMap
.erase(rit
);
494 mEarliestAlarm
.remove(key
);
495 // Emit signal only if we're not in the process of closing the calendar
496 if (!closing
&& mOpen
)
498 Q_EMIT
earliestAlarmChanged();
499 if (mHaveDisabledAlarms
)
500 checkForDisabledAlarms();
505 /******************************************************************************
506 * Called when the enabled or read-only status of a collection has changed.
507 * If the collection is now disabled, remove its events from the calendar.
509 void AlarmCalendar::slotCollectionStatusChanged(const Collection
& collection
, AkonadiModel::Change change
, const QVariant
& value
, bool inserted
)
511 if (!inserted
&& change
== AkonadiModel::Enabled
)
513 // For each alarm type which has been disabled, remove the collection's
514 // events from the map, but not from AkonadiModel.
515 CalEvent::Types enabled
= static_cast<CalEvent::Types
>(value
.toInt());
516 CalEvent::Types disabled
= ~enabled
& (CalEvent::ACTIVE
| CalEvent::ARCHIVED
| CalEvent::TEMPLATE
);
517 removeKAEvents(collection
.id(), false, disabled
);
521 /******************************************************************************
522 * Called when events have been added to AkonadiModel.
523 * Add corresponding KAEvent instances to those held by AlarmCalendar.
525 void AlarmCalendar::slotEventsAdded(const AkonadiModel::EventList
& events
)
527 for (int i
= 0, count
= events
.count(); i
< count
; ++i
)
528 slotEventChanged(events
[i
]);
531 /******************************************************************************
532 * Called when an event has been changed in AkonadiModel.
533 * Change the corresponding KAEvent instance held by AlarmCalendar.
535 void AlarmCalendar::slotEventChanged(const AkonadiModel::Event
& event
)
537 if (!event
.isConsistent())
539 qCCritical(KALARM_LOG
) << "Inconsistent AkonadiModel::Event: event:" << event
.event
.collectionId() << ", collection" << event
.collection
.id();
544 bool updated
= false;
545 KAEventMap::Iterator it
= mEventMap
.find(event
.eventId());
546 if (it
!= mEventMap
.end())
548 // The event ID already exists - remove the existing event first
549 KAEvent
* storedEvent
= it
.value();
550 if (event
.event
.category() == storedEvent
->category())
552 // The existing event is the same type - update it in place
553 *storedEvent
= event
.event
;
554 addNewEvent(event
.collection
, storedEvent
, true);
562 addNewEvent(event
.collection
, new KAEvent(event
.event
));
564 bool enabled
= event
.event
.enabled();
565 checkForDisabledAlarms(!enabled
, enabled
);
566 if (added
&& enabled
&& event
.event
.category() == CalEvent::ACTIVE
567 && event
.event
.repeatAtLogin())
568 Q_EMIT
atLoginEventAdded(event
.event
);
571 /******************************************************************************
572 * Called when events are about to be removed from AkonadiModel.
573 * Remove the corresponding KAEvent instances held by AlarmCalendar.
575 void AlarmCalendar::slotEventsToBeRemoved(const AkonadiModel::EventList
& events
)
577 for (int i
= 0, count
= events
.count(); i
< count
; ++i
)
579 if (!events
[i
].isConsistent())
580 qCCritical(KALARM_LOG
) << "Inconsistent AkonadiModel::Event: event:" << events
[i
].event
.collectionId() << ", collection" << events
[i
].collection
.id();
581 else if (mEventMap
.contains(events
[i
].eventId()))
582 deleteEventInternal(events
[i
].event
, events
[i
].collection
, false);
586 /******************************************************************************
587 * Import alarms from an external calendar and merge them into KAlarm's calendar.
588 * The alarms are given new unique event IDs.
589 * Parameters: parent = parent widget for error message boxes
590 * Reply = true if all alarms in the calendar were successfully imported
591 * = false if any alarms failed to be imported.
593 bool AlarmCalendar::importAlarms(QWidget
* parent
, Collection
* collection
)
596 QUrl url
= KFileDialog::getOpenUrl(QUrl(QStringLiteral("filedialog:///importalarms")),
597 QStringLiteral("*.vcs *.ics|%1").arg(i18nc("@info", "Calendar Files")), parent
);
600 qCCritical(KALARM_LOG
) << "Empty URL";
605 qCDebug(KALARM_LOG
) << "Invalid URL";
608 qCDebug(KALARM_LOG
) << url
.toDisplayString();
612 bool local
= url
.isLocalFile();
615 filename
= url
.toLocalFile();
616 if (!QFile::exists(filename
))
618 qCDebug(KALARM_LOG
) << "File '" << url
.toDisplayString() <<"' not found";
619 KAMessageBox::error(parent
, xi18nc("@info", "Could not load calendar <filename>%1</filename>.", url
.toDisplayString()));
625 auto getJob
= KIO::storedGet(url
.url());
626 KJobWidgets::setWindow(getJob
, MainWindow::mainMainWindow());
629 qCCritical(KALARM_LOG
) << "Download failure";
630 KAMessageBox::error(parent
, xi18nc("@info", "Cannot download calendar: <filename>%1</filename>", url
.toDisplayString()));
633 QTemporaryFile tmpFile
;
634 tmpFile
.setAutoRemove(false);
635 tmpFile
.write(getJob
->data());
637 filename
= tmpFile
.fileName();
638 qCDebug(KALARM_LOG
) << "--- Downloaded to" << filename
;
641 // Read the calendar and add its alarms to the current calendars
642 MemoryCalendar::Ptr
cal(new MemoryCalendar(Preferences::timeZone(true)));
643 FileStorage::Ptr
calStorage(new FileStorage(cal
, filename
));
644 success
= calStorage
->load();
647 qCDebug(KALARM_LOG
) << "Error loading calendar '" << filename
<<"'";
648 KAMessageBox::error(parent
, xi18nc("@info", "Could not load calendar <filename>%1</filename>.", url
.toDisplayString()));
652 KACalendar::Compat caltype
= fix(calStorage
);
653 CalEvent::Types wantedTypes
= collection
&& collection
->isValid() ? CalEvent::types(collection
->contentMimeTypes()) : CalEvent::EMPTY
;
654 Collection activeColl
, archiveColl
, templateColl
;
655 Event::List events
= cal
->rawEvents();
656 for (int i
= 0, end
= events
.count(); i
< end
; ++i
)
658 Event::Ptr event
= events
[i
];
659 if (event
->alarms().isEmpty() || !KAEvent(event
).isValid())
660 continue; // ignore events without alarms, or usable alarms
661 CalEvent::Type type
= CalEvent::status(event
);
662 if (type
== CalEvent::TEMPLATE
)
664 // If we know the event was not created by KAlarm, don't treat it as a template
665 if (caltype
== KACalendar::Incompatible
)
666 type
= CalEvent::ACTIVE
;
669 if (collection
&& collection
->isValid())
671 if (!(type
& wantedTypes
))
679 case CalEvent::ACTIVE
: coll
= &activeColl
; break;
680 case CalEvent::ARCHIVED
: coll
= &archiveColl
; break;
681 case CalEvent::TEMPLATE
: coll
= &templateColl
; break;
684 if (!coll
->isValid())
685 *coll
= CollectionControlModel::destination(type
);
688 Event::Ptr
newev(new Event(*event
));
690 // If there is a display alarm without display text, use the event
691 // summary text instead.
692 if (type
== CalEvent::ACTIVE
&& !newev
->summary().isEmpty())
694 const Alarm::List
& alarms
= newev
->alarms();
695 for (int ai
= 0, aend
= alarms
.count(); ai
< aend
; ++ai
)
697 Alarm::Ptr alarm
= alarms
[ai
];
698 if (alarm
->type() == Alarm::Display
&& alarm
->text().isEmpty())
699 alarm
->setText(newev
->summary());
701 newev
->setSummary(QString()); // KAlarm only uses summary for template names
704 // Give the event a new ID and add it to the calendars
705 newev
->setUid(CalEvent::uid(CalFormat::createUniqueId(), type
));
706 KAEvent
* newEvent
= new KAEvent(newev
);
707 if (!AkonadiModel::instance()->addEvent(*newEvent
, *coll
))
713 QFile::remove(filename
);
717 /******************************************************************************
718 * Export all selected alarms to an external calendar.
719 * The alarms are given new unique event IDs.
720 * Parameters: parent = parent widget for error message boxes
721 * Reply = true if all alarms in the calendar were successfully exported
722 * = false if any alarms failed to be exported.
724 bool AlarmCalendar::exportAlarms(const KAEvent::List
& events
, QWidget
* parent
)
727 QString file
= FileDialog::getSaveFileName(QUrl(QStringLiteral("kfiledialog:///exportalarms")),
728 QStringLiteral("*.ics|%1").arg(i18nc("@info", "Calendar Files")),
729 parent
, i18nc("@title:window", "Choose Export Calendar"),
737 qCDebug(KALARM_LOG
) << "Invalid URL";
740 qCDebug(KALARM_LOG
) << url
.toDisplayString();
742 MemoryCalendar::Ptr
calendar(new MemoryCalendar(Preferences::timeZone(true)));
743 FileStorage::Ptr
calStorage(new FileStorage(calendar
, file
));
744 if (append
&& !calStorage
->load())
747 auto statJob
= KIO::stat(url
.url(), KIO::StatJob::SourceSide
, 2);
748 KJobWidgets::setWindow(statJob
, parent
);
750 KFileItem
fi(statJob
->statResult(), url
);
753 qCCritical(KALARM_LOG
) << "Error loading calendar file" << file
<< "for append";
754 KAMessageBox::error(MainWindow::mainMainWindow(),
755 xi18nc("@info", "Error loading calendar to append to:<nl/><filename>%1</filename>", url
.toDisplayString()));
759 KACalendar::setKAlarmVersion(calendar
);
761 // Add the alarms to the calendar
763 bool exported
= false;
764 for (int i
= 0, end
= events
.count(); i
< end
; ++i
)
766 const KAEvent
* event
= events
[i
];
767 Event::Ptr
kcalEvent(new Event
);
768 CalEvent::Type type
= event
->category();
769 QString id
= CalEvent::uid(kcalEvent
->uid(), type
);
770 kcalEvent
->setUid(id
);
771 event
->updateKCalEvent(kcalEvent
, KAEvent::UID_IGNORE
);
772 if (calendar
->addEvent(kcalEvent
))
780 // One or more alarms have been exported to the calendar.
781 // Save the calendar to file.
782 QTemporaryFile
* tempFile
= Q_NULLPTR
;
783 bool local
= url
.isLocalFile();
786 tempFile
= new QTemporaryFile
;
787 file
= tempFile
->fileName();
789 calStorage
->setFileName(file
);
790 calStorage
->setSaveFormat(new ICalFormat
);
791 if (!calStorage
->save())
793 qCCritical(KALARM_LOG
) << file
<< ": failed";
794 KAMessageBox::error(MainWindow::mainMainWindow(),
795 xi18nc("@info", "Failed to save new calendar to:<nl/><filename>%1</filename>", url
.toDisplayString()));
801 qFile
.open(QIODevice::ReadOnly
);
802 auto uploadJob
= KIO::storedPut(&qFile
, url
.url(), -1);
803 KJobWidgets::setWindow(uploadJob
, parent
);
804 if (!uploadJob
->exec())
806 qCCritical(KALARM_LOG
) << file
<< ": upload failed";
807 KAMessageBox::error(MainWindow::mainMainWindow(),
808 xi18nc("@info", "Cannot upload new calendar to:<nl/><filename>%1</filename>", url
.toDisplayString()));
818 /******************************************************************************
819 * Flag the start of a group of calendar update calls.
820 * The purpose is to avoid multiple calendar saves during a group of operations.
822 void AlarmCalendar::startUpdate()
827 /******************************************************************************
828 * Flag the end of a group of calendar update calls.
829 * The calendar is saved if appropriate.
831 bool AlarmCalendar::endUpdate()
833 if (mUpdateCount
> 0)
843 /******************************************************************************
844 * Save the calendar, or flag it for saving if in a group of calendar update calls.
845 * Note that this method has no effect for Akonadi calendars.
847 bool AlarmCalendar::save()
858 /******************************************************************************
859 * This method must only be called from the main KAlarm queue processing loop,
860 * to prevent asynchronous calendar operations interfering with one another.
862 * Purge a list of archived events from the calendar.
864 void AlarmCalendar::purgeEvents(const KAEvent::List
& events
)
866 for (int i
= 0, end
= events
.count(); i
< end
; ++i
)
868 deleteEventInternal(*events
[i
]);
870 if (mHaveDisabledAlarms
)
871 checkForDisabledAlarms();
875 /******************************************************************************
876 * Add the specified event to the calendar.
877 * If it is an active event and 'useEventID' is false, a new event ID is
878 * created. In all other cases, the event ID is taken from 'event' (if non-null).
879 * 'event' is updated with the actual event ID.
880 * The event is added to 'resource' if specified; otherwise the default resource
881 * is used or the user is prompted, depending on policy. If 'noPrompt' is true,
882 * the user will not be prompted so that if no default resource is defined, the
883 * function will fail.
884 * Reply = true if 'event' was written to the calendar, in which case (not
885 * Akonadi) ownership of 'event' is taken by the calendar. 'event'
887 * = false if an error occurred, in which case 'event' is unchanged.
889 bool AlarmCalendar::addEvent(KAEvent
& evnt
, QWidget
* promptParent
, bool useEventID
, Collection
* collection
, bool noPrompt
, bool* cancelled
)
895 // Check that the event type is valid for the calendar
896 qCDebug(KALARM_LOG
) << evnt
.id();
897 CalEvent::Type type
= evnt
.category();
898 if (type
!= mEventType
)
902 case CalEvent::ACTIVE
:
903 case CalEvent::ARCHIVED
:
904 case CalEvent::TEMPLATE
:
905 if (mEventType
== CalEvent::EMPTY
)
907 // fall through to default
913 Collection::Id key
= (collection
&& collection
->isValid()) ? collection
->id() : -1;
914 Event::Ptr
kcalEvent((mCalType
== RESOURCES
) ? (Event
*)Q_NULLPTR
: new Event
);
915 KAEvent
* event
= new KAEvent(evnt
);
916 QString id
= event
->id();
917 if (type
== CalEvent::ACTIVE
)
921 else if (!useEventID
)
927 id
= (mCalType
== RESOURCES
) ? CalFormat::createUniqueId() : kcalEvent
->uid();
930 id
= CalEvent::uid(id
, type
);
932 kcalEvent
->setUid(id
);
934 event
->setEventId(id
);
937 if (mCalType
== RESOURCES
)
940 if (collection
&& CollectionControlModel::isEnabled(*collection
, type
))
943 col
= CollectionControlModel::destination(type
, promptParent
, noPrompt
, cancelled
);
946 // Don't add event to mEventMap yet - its Akonadi item id is not yet known.
947 // It will be added once it is inserted into AkonadiModel.
948 ok
= AkonadiModel::instance()->addEvent(*event
, col
);
949 remove
= ok
; // if success, delete the local event instance on exit
950 if (ok
&& type
== CalEvent::ACTIVE
&& !event
->enabled())
951 checkForDisabledAlarms(true, false);
956 // It's the display calendar
957 event
->updateKCalEvent(kcalEvent
, KAEvent::UID_IGNORE
);
958 key
= DISPLAY_COL_ID
;
959 if (!mEventMap
.contains(EventId(key
, event
->id())))
961 addNewEvent(Collection(), event
);
962 ok
= mCalendarStorage
->calendar()->addEvent(kcalEvent
);
970 // Adding to mCalendar failed, so undo AlarmCalendar::addEvent()
971 mEventMap
.remove(EventId(key
, event
->id()));
972 KAEvent::List
& events
= mResourceMap
[key
];
973 int i
= events
.indexOf(event
);
976 if (mEarliestAlarm
[key
] == event
)
977 findEarliestAlarm(key
);
989 /******************************************************************************
990 * Internal method to add an already checked event to the calendar.
991 * mEventMap takes ownership of the KAEvent.
992 * If 'replace' is true, an existing event is being updated (NOTE: its category()
993 * must remain the same).
995 void AlarmCalendar::addNewEvent(const Collection
& collection
, KAEvent
* event
, bool replace
)
997 Collection::Id key
= collection
.isValid() ? collection
.id() : -1;
998 event
->setCollectionId(key
);
1001 mResourceMap
[key
] += event
;
1002 mEventMap
[EventId(key
, event
->id())] = event
;
1004 if (collection
.isValid() && (AkonadiModel::types(collection
) & CalEvent::ACTIVE
)
1005 && event
->category() == CalEvent::ACTIVE
)
1007 // Update the earliest alarm to trigger
1008 KAEvent
* earliest
= mEarliestAlarm
.value(key
, (KAEvent
*)Q_NULLPTR
);
1009 if (replace
&& earliest
== event
)
1010 findEarliestAlarm(key
);
1013 KDateTime dt
= event
->nextTrigger(KAEvent::ALL_TRIGGER
).effectiveKDateTime();
1015 && (!earliest
|| dt
< earliest
->nextTrigger(KAEvent::ALL_TRIGGER
)))
1017 mEarliestAlarm
[key
] = event
;
1018 Q_EMIT
earliestAlarmChanged();
1024 /******************************************************************************
1025 * Modify the specified event in the calendar with its new contents.
1026 * The new event must have a different event ID from the old one.
1027 * It is assumed to be of the same event type as the old one (active, etc.)
1028 * Reply = true if 'newEvent' was written to the calendar, in which case (not
1029 * Akonadi) ownership of 'newEvent' is taken by the calendar.
1030 * 'newEvent' is updated.
1031 * = false if an error occurred, in which case 'newEvent' is unchanged.
1033 bool AlarmCalendar::modifyEvent(const EventId
& oldEventId
, KAEvent
& newEvent
)
1035 EventId
newId(oldEventId
.collectionId(), newEvent
.id());
1036 qCDebug(KALARM_LOG
) << oldEventId
<< "->" << newId
;
1037 bool noNewId
= newId
.isEmpty();
1038 if (!noNewId
&& oldEventId
== newId
)
1040 qCCritical(KALARM_LOG
) << "Same IDs";
1045 if (mCalType
== RESOURCES
)
1047 // Set the event's ID and Akonadi ID, and update the old
1048 // event in Akonadi.
1049 KAEvent
* storedEvent
= event(oldEventId
);
1052 qCCritical(KALARM_LOG
) << "Old event not found";
1056 newEvent
.setEventId(CalFormat::createUniqueId());
1057 Collection c
= AkonadiModel::instance()->collectionById(oldEventId
.collectionId());
1060 // Don't add new event to mEventMap yet - its Akonadi item id is not yet known
1061 if (!AkonadiModel::instance()->addEvent(newEvent
, c
))
1063 // Note: deleteEventInternal() will delete storedEvent before using the
1064 // event parameter, so need to pass a copy as the parameter.
1065 deleteEventInternal(KAEvent(*storedEvent
), c
);
1066 if (mHaveDisabledAlarms
)
1067 checkForDisabledAlarms();
1071 // This functionality isn't needed for the display calendar.
1072 // The calendar would take ownership of newEvent.
1078 /******************************************************************************
1079 * Update the specified event in the calendar with its new contents.
1080 * The event retains the same ID. The event must be in the resource calendar.
1081 * Reply = event which has been updated
1084 KAEvent
* AlarmCalendar::updateEvent(const KAEvent
& evnt
)
1086 return updateEvent(&evnt
);
1088 KAEvent
* AlarmCalendar::updateEvent(const KAEvent
* evnt
)
1090 if (!mOpen
|| mCalType
!= RESOURCES
)
1092 KAEvent
* kaevnt
= event(EventId(*evnt
));
1095 KAEvent
newEvnt(*evnt
);
1096 newEvnt
.setItemId(evnt
->itemId());
1097 if (AkonadiModel::instance()->updateEvent(newEvnt
))
1103 qCDebug(KALARM_LOG
) << "error";
1108 /******************************************************************************
1109 * Delete the specified event from the resource calendar, if it exists.
1110 * The calendar is then optionally saved.
1112 bool AlarmCalendar::deleteEvent(const KAEvent
& event
, bool saveit
)
1114 if (mOpen
&& mCalType
== RESOURCES
)
1116 CalEvent::Type status
= deleteEventInternal(event
);
1117 if (mHaveDisabledAlarms
)
1118 checkForDisabledAlarms();
1119 if (status
!= CalEvent::EMPTY
)
1129 /******************************************************************************
1130 * Delete the specified event from the calendar, if it exists.
1131 * The calendar is then optionally saved.
1133 bool AlarmCalendar::deleteDisplayEvent(const QString
& eventID
, bool saveit
)
1135 if (mOpen
&& mCalType
!= RESOURCES
)
1137 CalEvent::Type status
= deleteEventInternal(eventID
);
1138 if (mHaveDisabledAlarms
)
1139 checkForDisabledAlarms();
1140 if (status
!= CalEvent::EMPTY
)
1150 /******************************************************************************
1151 * Internal method to delete the specified event from the calendar and lists.
1152 * Reply = event status, if it was found in the resource calendar/collection or
1154 * = CalEvent::EMPTY otherwise.
1156 CalEvent::Type
AlarmCalendar::deleteEventInternal(const KAEvent
& event
, bool deleteFromAkonadi
)
1158 Collection collection
= AkonadiModel::instance()->collectionById(event
.collectionId());
1159 if (!collection
.isValid())
1160 return CalEvent::EMPTY
;
1161 return deleteEventInternal(event
.id(), event
, collection
, deleteFromAkonadi
);
1164 CalEvent::Type
AlarmCalendar::deleteEventInternal(const KAEvent
& event
, const Collection
& collection
, bool deleteFromAkonadi
)
1166 if (!collection
.isValid())
1167 return CalEvent::EMPTY
;
1168 if (event
.collectionId() != collection
.id())
1170 qCCritical(KALARM_LOG
) << "Event" << event
.id() << ": collection" << event
.collectionId() << "differs from 'collection'" << collection
.id();
1171 return CalEvent::EMPTY
;
1173 return deleteEventInternal(event
.id(), event
, collection
, deleteFromAkonadi
);
1176 CalEvent::Type
AlarmCalendar::deleteEventInternal(const QString
& eventID
, const KAEvent
& event
, const Collection
& collection
, bool deleteFromAkonadi
)
1178 // Make a copy of the KAEvent and the ID QString, since the supplied
1179 // references might be destructed when the event is deleted below.
1180 const QString id
= eventID
;
1181 const KAEvent paramEvent
= event
;
1183 Event::Ptr kcalEvent
;
1184 if (mCalendarStorage
)
1185 kcalEvent
= mCalendarStorage
->calendar()->event(id
);
1186 Collection::Id key
= collection
.isValid() ? collection
.id() : -1;
1187 KAEventMap::Iterator it
= mEventMap
.find(EventId(key
, id
));
1188 if (it
!= mEventMap
.end())
1190 KAEvent
* ev
= it
.value();
1191 mEventMap
.erase(it
);
1192 KAEvent::List
& events
= mResourceMap
[key
];
1193 int i
= events
.indexOf(ev
);
1197 if (mEarliestAlarm
[key
] == ev
)
1198 findEarliestAlarm(collection
);
1202 for (EarliestMap::Iterator eit
= mEarliestAlarm
.begin(); eit
!= mEarliestAlarm
.end(); ++eit
)
1204 KAEvent
* ev
= eit
.value();
1205 if (ev
&& ev
->id() == id
)
1207 findEarliestAlarm(eit
.key());
1212 CalEvent::Type status
= CalEvent::EMPTY
;
1215 status
= CalEvent::status(kcalEvent
);
1216 mCalendarStorage
->calendar()->deleteEvent(kcalEvent
);
1218 else if (deleteFromAkonadi
)
1220 // It's an Akonadi event
1221 CalEvent::Type s
= paramEvent
.category();
1222 if (AkonadiModel::instance()->deleteEvent(paramEvent
))
1229 /******************************************************************************
1230 * Return the event with the specified ID.
1231 * If 'checkDuplicates' is true, and the collection ID is invalid, if there is
1232 * a unique event with the given ID, it will be returned.
1234 KAEvent
* AlarmCalendar::event(const EventId
& uniqueID
, bool checkDuplicates
)
1238 const QString eventId
= uniqueID
.eventId();
1239 if (uniqueID
.collectionId() == -1 && checkDuplicates
)
1241 // The collection isn't known, but use the event ID if it is
1242 // unique among all collections.
1243 KAEvent::List list
= events(eventId
);
1244 if (list
.count() > 1)
1246 qCWarning(KALARM_LOG
) << "Multiple events found with ID" << eventId
;
1253 KAEventMap::ConstIterator it
= mEventMap
.constFind(uniqueID
);
1254 if (it
== mEventMap
.constEnd())
1259 /******************************************************************************
1260 * Return the event with the specified ID.
1261 * For the Akonadi version, this method is for the display calendar only.
1263 Event::Ptr
AlarmCalendar::kcalEvent(const QString
& uniqueID
)
1265 Q_ASSERT(mCalType
!= RESOURCES
); // only allowed for display calendar
1266 if (!mCalendarStorage
)
1267 return Event::Ptr();
1268 return mCalendarStorage
->calendar()->event(uniqueID
);
1271 /******************************************************************************
1272 * Find the alarm template with the specified name.
1273 * Reply = 0 if not found.
1275 KAEvent
* AlarmCalendar::templateEvent(const QString
& templateName
)
1277 if (templateName
.isEmpty())
1279 KAEvent::List eventlist
= events(CalEvent::TEMPLATE
);
1280 for (int i
= 0, end
= eventlist
.count(); i
< end
; ++i
)
1282 if (eventlist
[i
]->templateName() == templateName
)
1283 return eventlist
[i
];
1288 /******************************************************************************
1289 * Return all events with the specified ID, from all calendars.
1291 KAEvent::List
AlarmCalendar::events(const QString
& uniqueId
) const
1294 if (mCalType
== RESOURCES
&& isValid())
1296 for (ResourceMap::ConstIterator rit
= mResourceMap
.constBegin(); rit
!= mResourceMap
.constEnd(); ++rit
)
1298 const Collection::Id id
= rit
.key();
1299 KAEventMap::ConstIterator it
= mEventMap
.constFind(EventId(id
, uniqueId
));
1300 if (it
!= mEventMap
.constEnd())
1307 /******************************************************************************
1308 * Return all events in the calendar which contain alarms.
1309 * Optionally the event type can be filtered, using an OR of event types.
1311 KAEvent::List
AlarmCalendar::events(const Collection
& collection
, CalEvent::Types type
) const
1314 if (mCalType
!= RESOURCES
&& (!mCalendarStorage
|| collection
.isValid()))
1316 if (collection
.isValid())
1318 Collection::Id key
= collection
.isValid() ? collection
.id() : -1;
1319 ResourceMap::ConstIterator rit
= mResourceMap
.constFind(key
);
1320 if (rit
== mResourceMap
.constEnd())
1322 const KAEvent::List events
= rit
.value();
1323 if (type
== CalEvent::EMPTY
)
1325 for (int i
= 0, end
= events
.count(); i
< end
; ++i
)
1326 if (type
& events
[i
]->category())
1331 for (ResourceMap::ConstIterator rit
= mResourceMap
.constBegin(); rit
!= mResourceMap
.constEnd(); ++rit
)
1333 const KAEvent::List events
= rit
.value();
1334 if (type
== CalEvent::EMPTY
)
1338 for (int i
= 0, end
= events
.count(); i
< end
; ++i
)
1339 if (type
& events
[i
]->category())
1347 /******************************************************************************
1348 * Return all events in the calendar which contain usable alarms.
1349 * For the Akonadi version, this method is for the display calendar only.
1350 * Optionally the event type can be filtered, using an OR of event types.
1352 Event::List
AlarmCalendar::kcalEvents(CalEvent::Type type
)
1355 Q_ASSERT(mCalType
!= RESOURCES
); // only allowed for display calendar
1356 if (!mCalendarStorage
)
1358 list
= mCalendarStorage
->calendar()->rawEvents();
1359 for (int i
= 0; i
< list
.count(); )
1361 Event::Ptr event
= list
[i
];
1362 if (event
->alarms().isEmpty()
1363 || (type
!= CalEvent::EMPTY
&& !(type
& CalEvent::status(event
)))
1364 || !KAEvent(event
).isValid())
1373 /******************************************************************************
1374 * Return whether an event is read-only.
1375 * Display calendar events are always returned as read-only.
1377 bool AlarmCalendar::eventReadOnly(Item::Id id
) const
1379 if (mCalType
!= RESOURCES
)
1381 AkonadiModel
* model
= AkonadiModel::instance();
1382 Collection collection
= model
->collectionForItem(id
);
1383 KAEvent event
= model
->event(id
);
1384 if (!CollectionControlModel::isWritableEnabled(collection
, event
.category()))
1386 return !event
.isValid() || event
.isReadOnly();
1387 // || compatibility(event) != KACalendar::Current;
1390 /******************************************************************************
1391 * Return the collection containing a specified event.
1393 Collection
AlarmCalendar::collectionForEvent(Item::Id itemId
) const
1395 if (mCalType
!= RESOURCES
)
1396 return Collection();
1397 return AkonadiModel::instance()->collectionForItem(itemId
);
1400 /******************************************************************************
1401 * Called when an alarm's enabled status has changed.
1403 void AlarmCalendar::disabledChanged(const KAEvent
* event
)
1405 if (event
->category() == CalEvent::ACTIVE
)
1407 bool status
= event
->enabled();
1408 checkForDisabledAlarms(!status
, status
);
1412 /******************************************************************************
1413 * Check whether there are any individual disabled alarms, following an alarm
1414 * creation or modification. Must only be called for an ACTIVE alarm.
1416 void AlarmCalendar::checkForDisabledAlarms(bool oldEnabled
, bool newEnabled
)
1418 if (mCalType
== RESOURCES
&& newEnabled
!= oldEnabled
)
1420 if (newEnabled
&& mHaveDisabledAlarms
)
1421 checkForDisabledAlarms();
1422 else if (!newEnabled
&& !mHaveDisabledAlarms
)
1424 mHaveDisabledAlarms
= true;
1425 Q_EMIT
haveDisabledAlarmsChanged(true);
1430 /******************************************************************************
1431 * Check whether there are any individual disabled alarms.
1433 void AlarmCalendar::checkForDisabledAlarms()
1435 if (mCalType
!= RESOURCES
)
1437 bool disabled
= false;
1438 KAEvent::List eventlist
= events(CalEvent::ACTIVE
);
1439 for (int i
= 0, end
= eventlist
.count(); i
< end
; ++i
)
1441 if (!eventlist
[i
]->enabled())
1447 if (disabled
!= mHaveDisabledAlarms
)
1449 mHaveDisabledAlarms
= disabled
;
1450 Q_EMIT
haveDisabledAlarmsChanged(disabled
);
1454 /******************************************************************************
1455 * Return a list of all active at-login alarms.
1457 KAEvent::List
AlarmCalendar::atLoginAlarms() const
1459 KAEvent::List atlogins
;
1460 if (mCalType
!= RESOURCES
)
1462 AkonadiModel
* model
= AkonadiModel::instance();
1463 if (!mCalendarStorage
|| mCalType
!= RESOURCES
)
1465 for (ResourceMap::ConstIterator rit
= mResourceMap
.constBegin(); rit
!= mResourceMap
.constEnd(); ++rit
)
1467 const Collection::Id id
= rit
.key();
1469 || !(AkonadiModel::types(model
->collectionById(id
)) & CalEvent::ACTIVE
))
1471 const KAEvent::List
& events
= rit
.value();
1472 for (int i
= 0, end
= events
.count(); i
< end
; ++i
)
1474 KAEvent
* event
= events
[i
];
1475 if (event
->category() == CalEvent::ACTIVE
&& event
->repeatAtLogin())
1482 /******************************************************************************
1483 * Find and note the active alarm with the earliest trigger time for a calendar.
1485 void AlarmCalendar::findEarliestAlarm(const Collection
& collection
)
1487 if (mCalType
!= RESOURCES
)
1489 if (!collection
.isValid()
1490 || !(AkonadiModel::types(collection
) & CalEvent::ACTIVE
))
1492 findEarliestAlarm(collection
.id());
1495 void AlarmCalendar::findEarliestAlarm(Collection::Id key
)
1497 EarliestMap::Iterator eit
= mEarliestAlarm
.find(key
);
1498 if (eit
!= mEarliestAlarm
.end())
1499 eit
.value() = Q_NULLPTR
;
1500 if (mCalType
!= RESOURCES
1503 ResourceMap::ConstIterator rit
= mResourceMap
.constFind(key
);
1504 if (rit
== mResourceMap
.constEnd())
1506 const KAEvent::List
& events
= rit
.value();
1507 KAEvent
* earliest
= Q_NULLPTR
;
1508 KDateTime earliestTime
;
1509 for (int i
= 0, end
= events
.count(); i
< end
; ++i
)
1511 KAEvent
* event
= events
[i
];
1512 if (event
->category() != CalEvent::ACTIVE
1513 || mPendingAlarms
.contains(event
->id()))
1515 KDateTime dt
= event
->nextTrigger(KAEvent::ALL_TRIGGER
).effectiveKDateTime();
1516 if (dt
.isValid() && (!earliest
|| dt
< earliestTime
))
1522 mEarliestAlarm
[key
] = earliest
;
1523 Q_EMIT
earliestAlarmChanged();
1526 /******************************************************************************
1527 * Return the active alarm with the earliest trigger time.
1528 * Reply = 0 if none.
1530 KAEvent
* AlarmCalendar::earliestAlarm() const
1532 KAEvent
* earliest
= Q_NULLPTR
;
1533 KDateTime earliestTime
;
1534 for (EarliestMap::ConstIterator eit
= mEarliestAlarm
.constBegin(); eit
!= mEarliestAlarm
.constEnd(); ++eit
)
1536 KAEvent
* event
= eit
.value();
1539 KDateTime dt
= event
->nextTrigger(KAEvent::ALL_TRIGGER
).effectiveKDateTime();
1540 if (dt
.isValid() && (!earliest
|| dt
< earliestTime
))
1549 /******************************************************************************
1550 * Note that an alarm which has triggered is now being processed. While pending,
1551 * it will be ignored for the purposes of finding the earliest trigger time.
1553 void AlarmCalendar::setAlarmPending(KAEvent
* event
, bool pending
)
1555 QString id
= event
->id();
1556 bool wasPending
= mPendingAlarms
.contains(id
);
1557 qCDebug(KALARM_LOG
) << id
<< "," << pending
<< "(was" << wasPending
<< ")";
1562 mPendingAlarms
.append(id
);
1568 mPendingAlarms
.removeAll(id
);
1570 // Now update the earliest alarm to trigger for its calendar
1571 findEarliestAlarm(AkonadiModel::instance()->collection(*event
));
1574 /******************************************************************************
1575 * Called when the user changes the start-of-day time.
1576 * Adjust the start times of all date-only alarms' recurrences.
1578 void AlarmCalendar::adjustStartOfDay()
1582 for (ResourceMap::ConstIterator rit
= mResourceMap
.constBegin(); rit
!= mResourceMap
.constEnd(); ++rit
)
1583 KAEvent::adjustStartOfDay(rit
.value());
1586 /******************************************************************************
1587 * Find the version of KAlarm which wrote the calendar file, and do any
1588 * necessary conversions to the current format.
1590 KACalendar::Compat
fix(const FileStorage::Ptr
& fileStorage
)
1592 QString versionString
;
1593 int version
= KACalendar::updateVersion(fileStorage
, versionString
);
1594 if (version
== KACalendar::IncompatibleFormat
)
1595 return KACalendar::Incompatible
; // calendar was created by another program, or an unknown version of KAlarm
1596 return KACalendar::Current
;