Commit 10a8886f authored by David Jarvie's avatar David Jarvie
Browse files

Refactor AlarmCalendar to split out resources and display calendars

Split into independent ResourcesCalendar and DisplayCalendar classes.
parent 3ec87914
Pipeline #23114 passed with stage
in 12 minutes and 52 seconds
......@@ -82,14 +82,15 @@ set(resources_SRCS
set(kalarm_bin_SRCS ${libkalarm_SRCS} ${resources_SRCS}
${libkalarm_common_SRCS}
main.cpp
birthdaydlg.cpp
birthdaymodel.cpp
main.cpp
editdlg.cpp
editdlgtypes.cpp
soundpicker.cpp
sounddlg.cpp
alarmcalendar.cpp
displaycalendar.cpp
resourcescalendar.cpp
undo.cpp
kalarmapp.cpp
mainwindowbase.cpp
......@@ -213,4 +214,4 @@ add_executable(kalarm_helper rtcwakeaction.cpp ${libkalarm_common_SRCS})
target_link_libraries(kalarm_helper KF5::AuthCore KF5::I18n)
install(TARGETS kalarm_helper DESTINATION ${KAUTH_HELPER_INSTALL_DIR})
kauth_install_helper_files(kalarm_helper org.kde.kalarm.rtcwake root)
kauth_install_actions(org.kde.kalarm.rtcwake org.kde.kalarm.rtcwake.actions)
#kauth_install_actions(org.kde.kalarm.rtcwake org.kde.kalarm.rtcwake.actions)
......@@ -20,7 +20,6 @@
#include "birthdaydlg.h"
#include "alarmcalendar.h"
#include "birthdaymodel.h"
#include "editdlgtypes.h"
#include "fontcolourbutton.h"
......@@ -29,6 +28,7 @@
#include "preferences.h"
#include "reminder.h"
#include "repetitionbutton.h"
#include "resourcescalendar.h"
#include "soundpicker.h"
#include "specialactions.h"
#include "lib/checkbox.h"
......
......@@ -21,7 +21,7 @@
#include "birthdaymodel.h"
#include "alarmcalendar.h"
#include "resourcescalendar.h"
#include <KAlarmCal/KAEvent>
......
......@@ -20,12 +20,12 @@
#include "dbushandler.h"
#include "alarmcalendar.h"
#include "functions.h"
#include "kalarmapp.h"
#include "kamail.h"
#include "mainwindow.h"
#include "preferences.h"
#include "resourcescalendar.h"
#include "resources/resources.h"
#include <kalarmadaptor.h>
#include "kalarm_debug.h"
......
......@@ -20,10 +20,10 @@
#include "deferdlg.h"
#include "alarmcalendar.h"
#include "alarmtimewidget.h"
#include "functions.h"
#include "kalarmapp.h"
#include "resourcescalendar.h"
#include "lib/messagebox.h"
#include "kalarm_debug.h"
......
/*
* displaycalendar.cpp - KAlarm display calendar file access
* Program: kalarm
* Copyright © 2001-2020 David Jarvie <djarvie@kde.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "displaycalendar.h"
//#include "kalarm.h"
//#include "functions.h"
//#include "kalarmapp.h"
#include "mainwindow.h"
#include "preferences.h"
//#include "resources/datamodel.h"
//#include "resources/resources.h"
#include "lib/messagebox.h"
#include "kalarm_debug.h"
#include <KCalendarCore/MemoryCalendar>
#include <KCalendarCore/ICalFormat>
#include <KLocalizedString>
//#include <KIO/StatJob>
//#include <KIO/StoredTransferJob>
//#include <KSharedConfig>
//#include <kio_version.h>
#include <QStandardPaths>
#include <QDir>
using namespace KCalendarCore;
using namespace KAlarmCal;
static const QString displayCalendarName = QStringLiteral("displaying.ics");
static const ResourceId DISPLAY_RES_ID = -1; // resource ID used for displaying calendar
DisplayCalendar* DisplayCalendar::mInstance = nullptr;
/******************************************************************************
* Initialise the display alarm calendar.
* It is user-specific, and contains details of alarms which are currently being
* displayed to that user and which have not yet been acknowledged;
*/
void DisplayCalendar::initialise()
{
QDir dir;
dir.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation));
const QString displayCal = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QLatin1Char('/') + displayCalendarName;
mInstance = new DisplayCalendar(displayCal);
}
/******************************************************************************
* Terminate access to the display calendar.
*/
void DisplayCalendar::terminate()
{
delete mInstance;
mInstance = nullptr;
}
/******************************************************************************
* Return the display calendar, opening it first if necessary.
*/
DisplayCalendar* DisplayCalendar::instanceOpen()
{
if (mInstance->open())
return mInstance;
qCCritical(KALARM_LOG) << "DisplayCalendar::instanceOpen: Open error";
return nullptr;
}
/******************************************************************************
* Constructor for the display calendar file.
*/
DisplayCalendar::DisplayCalendar(const QString& path)
: mDisplayCalPath(path)
, mDisplayICalPath(path)
{
mDisplayICalPath.replace(QStringLiteral("\\.vcs$"), QStringLiteral(".ics"));
mCalType = (mDisplayCalPath == mDisplayICalPath) ? LOCAL_ICAL : LOCAL_VCAL; // is the calendar in ICal or VCal format?
}
DisplayCalendar::~DisplayCalendar()
{
close();
}
/******************************************************************************
* Open the calendar if not already open, and load it into memory.
*/
bool DisplayCalendar::open()
{
if (isOpen())
return true;
// Open the display calendar.
qCDebug(KALARM_LOG) << "DisplayCalendar::open:" << mDisplayCalPath;
if (!mCalendarStorage)
{
MemoryCalendar::Ptr calendar(new MemoryCalendar(Preferences::timeSpecAsZone()));
mCalendarStorage = FileStorage::Ptr(new FileStorage(calendar, mDisplayCalPath));
}
// Check for file's existence, assuming that it does exist when uncertain,
// to avoid overwriting it.
QFileInfo fi(mDisplayCalPath);
if (!fi.exists() || !fi.isFile() || load() == 0)
{
// The calendar file doesn't yet exist, or it's zero length, so create a new one
if (saveCal(mDisplayICalPath))
load();
}
if (!mOpen)
{
mCalendarStorage->calendar().clear();
mCalendarStorage.clear();
}
return isOpen();
}
/******************************************************************************
* Load the calendar into memory.
* Reply = 1 if success
* = 0 if zero-length file exists.
* = -1 if failure to load calendar file
* = -2 if instance uninitialised.
*/
int DisplayCalendar::load()
{
// Load the display calendar.
if (!mCalendarStorage)
return -2;
qCDebug(KALARM_LOG) << "DisplayCalendar::load:" << mDisplayCalPath;
if (!mCalendarStorage->load())
{
// Load error. Check if the file is zero length
QFileInfo fi(mDisplayCalPath);
if (fi.exists() && !fi.size())
return 0; // file is zero length
qCCritical(KALARM_LOG) << "DisplayCalendar::load: Error loading calendar file '" << mDisplayCalPath <<"'";
KAMessageBox::error(MainWindow::mainMainWindow(),
xi18nc("@info", "<para>Error loading calendar:</para><para><filename>%1</filename></para><para>Please fix or delete the file.</para>", mDisplayCalPath));
// load() could have partially populated the calendar, so clear it out
mCalendarStorage->calendar()->close();
mCalendarStorage->calendar().clear();
mCalendarStorage.clear();
mOpen = false;
return -1;
}
QString versionString;
KACalendar::updateVersion(mCalendarStorage, versionString); // convert events to current KAlarm format for when calendar is saved
updateKAEvents();
mOpen = true;
return 1;
}
/******************************************************************************
* Save the calendar.
*/
bool DisplayCalendar::save()
{
return saveCal();
}
/******************************************************************************
* Save the calendar from memory to file.
* If a filename is specified, create a new calendar file.
*/
bool DisplayCalendar::saveCal(const QString& newFile)
{
if (!mCalendarStorage)
return false;
if (!mOpen && newFile.isEmpty())
return false;
qCDebug(KALARM_LOG) << "DisplayCalendar::saveCal:" << "\"" << newFile;
QString saveFilename = newFile.isEmpty() ? mDisplayCalPath : newFile;
if (mCalType == LOCAL_VCAL && newFile.isNull())
saveFilename = mDisplayICalPath;
mCalendarStorage->setFileName(saveFilename);
mCalendarStorage->setSaveFormat(new ICalFormat);
if (!mCalendarStorage->save())
{
qCCritical(KALARM_LOG) << "DisplayCalendar::saveCal: Saving" << saveFilename << "failed.";
KAMessageBox::error(MainWindow::mainMainWindow(),
xi18nc("@info", "Failed to save calendar to <filename>%1</filename>", mDisplayICalPath));
return false;
}
if (mCalType == LOCAL_VCAL)
{
// The file was in vCalendar format, but has now been saved in iCalendar format.
mDisplayCalPath = mDisplayICalPath;
mCalType = LOCAL_ICAL;
}
return true;
}
/******************************************************************************
* Close display calendar file at program exit.
*/
void DisplayCalendar::close()
{
if (mCalendarStorage)
{
mCalendarStorage->calendar()->close();
mCalendarStorage->calendar().clear();
mCalendarStorage.clear();
}
// Flag as closed now to prevent removeKAEvents() doing silly things
// when it's called again
mOpen = false;
// Events list should be empty, but just in case...
for (KAEvent* event : mEventList)
{
mEventMap.remove(event->id());
delete event;
}
mEventList.clear();
}
/******************************************************************************
* Create a KAEvent instance corresponding to each KCalendarCore::Event in the
* display calendar, and store them in the event map in place of the old set.
* Called after the display calendar has completed loading.
*/
void DisplayCalendar::updateKAEvents()
{
qCDebug(KALARM_LOG) << "DisplayCalendar::updateKAEvents";
for (KAEvent* event : mEventList)
{
mEventMap.remove(event->id());
delete event;
}
mEventList.clear();
Calendar::Ptr cal = mCalendarStorage->calendar();
if (!cal)
return;
const Event::List kcalevents = cal->rawEvents();
for (Event::Ptr kcalevent : kcalevents)
{
if (kcalevent->alarms().isEmpty())
continue; // ignore events without alarms
KAEvent* event = new KAEvent(kcalevent);
if (!event->isValid())
{
qCWarning(KALARM_LOG) << "DisplayCalendar::updateKAEvents: Ignoring unusable event" << kcalevent->uid();
delete event;
continue; // ignore events without usable alarms
}
event->setResourceId(DISPLAY_RES_ID);
mEventList += event;
mEventMap[kcalevent->uid()] = event;
}
}
/******************************************************************************
* Add the specified event to the calendar.
* Reply = true if 'evnt' was written to the calendar. 'evnt' is updated.
* = false if an error occurred, in which case 'evnt' is unchanged.
*/
bool DisplayCalendar::addEvent(KAEvent& evnt)
{
if (!mOpen)
return false;
qCDebug(KALARM_LOG) << "DisplayCalendar::addEvent:" << evnt.id();
// Check that the event type is valid for the calendar
const CalEvent::Type type = evnt.category();
if (type != CalEvent::DISPLAYING)
return false;
Event::Ptr kcalEvent(new Event);
KAEvent* event = new KAEvent(evnt);
QString id = event->id();
if (id.isEmpty())
id = kcalEvent->uid();
id = CalEvent::uid(id, type); // include the alarm type tag in the ID
kcalEvent->setUid(id);
event->setEventId(id);
event->updateKCalEvent(kcalEvent, KAEvent::UID_IGNORE);
bool ok = false;
bool remove = false;
if (!mEventMap.contains(event->id()))
{
event->setResourceId(DISPLAY_RES_ID);
mEventList += event;
mEventMap[event->id()] = event;
ok = mCalendarStorage->calendar()->addEvent(kcalEvent);
remove = !ok;
}
if (!ok)
{
if (remove)
{
// Adding to mCalendar failed, so undo AlarmCalendar::addEvent()
mEventMap.remove(event->id());
int i = mEventList.indexOf(event);
if (i >= 0)
mEventList.remove(i);
}
delete event;
return false;
}
evnt = *event;
if (remove)
delete event;
return true;
}
/******************************************************************************
* Delete the specified event from the calendar, if it exists.
* The calendar is then optionally saved.
*/
bool DisplayCalendar::deleteEvent(const QString& eventID, bool saveit)
{
if (mOpen)
{
Event::Ptr kcalEvent;
if (mCalendarStorage)
kcalEvent = mCalendarStorage->calendar()->event(eventID); // display calendar
// Make a copy of the ID QString, since the supplied reference might be
// destructed when the event is deleted below.
const QString id = eventID;
KAEventMap::Iterator it = mEventMap.find(id);
if (it != mEventMap.end())
{
KAEvent* ev = it.value();
mEventMap.erase(it);
int i = mEventList.indexOf(ev);
if (i >= 0)
mEventList.remove(i);
delete ev;
}
CalEvent::Type status = CalEvent::EMPTY;
if (kcalEvent)
{
status = CalEvent::status(kcalEvent);
mCalendarStorage->calendar()->deleteEvent(kcalEvent);
}
if (status != CalEvent::EMPTY)
{
if (saveit)
return saveCal();
return true;
}
}
return false;
}
/******************************************************************************
* Return the event with the specified ID.
* This method is for the display calendar only.
*/
Event::Ptr DisplayCalendar::kcalEvent(const QString& uniqueID)
{
if (!mCalendarStorage)
return Event::Ptr();
return mCalendarStorage->calendar()->event(uniqueID);
}
/******************************************************************************
* Return all events in the calendar which contain usable alarms.
* This method is for the display calendar only.
* Optionally the event type can be filtered, using an OR of event types.
*/
Event::List DisplayCalendar::kcalEvents(CalEvent::Type type)
{
Event::List list;
if (!mCalendarStorage)
return list;
list = mCalendarStorage->calendar()->rawEvents();
for (int i = 0; i < list.count(); )
{
Event::Ptr event = list.at(i);
if (event->alarms().isEmpty()
|| (type != CalEvent::EMPTY && !(type & CalEvent::status(event)))
|| !KAEvent(event).isValid())
list.remove(i);
else
++i;
}
return list;
}
/******************************************************************************
* Called when the user changes the start-of-day time.
* Adjust the start times of all date-only alarms' recurrences.
*/
void DisplayCalendar::adjustStartOfDay()
{
if (!isValid())
return;
KAEvent::adjustStartOfDay(mEventList);
}
// vim: et sw=4:
/*
* displaycalendar.h - KAlarm display calendar file access
* Program: kalarm
* Copyright © 2001-2020 David Jarvie <djarvie@kde.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef DISPLAYCALENDAR_H
#define DISPLAYCALENDAR_H
#include "eventid.h"
#include "resources/resource.h"
#include <KAlarmCal/KAEvent>
#include <KCalendarCore/FileStorage>
#include <KCalendarCore/Event>
#include <QHash>
#include <QObject>
using namespace KAlarmCal;
/** Provides read and write access to the display calendar.
*/
class DisplayCalendar : public QObject
{
Q_OBJECT
public:
~DisplayCalendar() override;
bool save();
KCalendarCore::Event::Ptr kcalEvent(const QString& uniqueID);
KCalendarCore::Event::List kcalEvents(CalEvent::Type s = CalEvent::EMPTY);
bool addEvent(KAEvent&);
bool deleteEvent(const QString& eventID, bool save = false);
bool isOpen() const { return mOpen; }
void adjustStartOfDay();
static void initialise();
static void terminate();
static DisplayCalendar* instance() { return mInstance; }
static DisplayCalendar* instanceOpen();
private:
enum CalType { LOCAL_ICAL, LOCAL_VCAL };
typedef QHash<QString, KAEvent*> KAEventMap; // indexed by event UID
explicit DisplayCalendar(const QString& file);
bool open();
int load();
void close();
bool saveCal(const QString& newFile = QString());
bool isValid() const { return mCalendarStorage; }
void updateKAEvents();
static DisplayCalendar* mInstance; // the unique instance
KAEvent::List mEventList;
KAEventMap mEventMap; // lookup of all events by UID
KCalendarCore::FileStorage::Ptr mCalendarStorage;
QString mDisplayCalPath; // path of display calendar file
QString mDisplayICalPath; // path of display iCalendar file
CalType mCalType; // what type of calendar mCalendar is (ical/vcal)
bool mOpen {false}; // true if the calendar file is open
};
#endif // DISPLAYCALENDAR_H
// vim: et sw=4:
......@@ -22,7 +22,6 @@
#include "editdlg_p.h"
#include "editdlgtypes.h"
#include "alarmcalendar.h"
#include "alarmtimewidget.h"
#include "deferdlg.h"
#include "functions.h"
......@@ -32,6 +31,7 @@
#include "preferences.h"
#include "recurrenceedit.h"
#include "reminder.h"
#include "resourcescalendar.h"
#include "resources/datamodel.h"
#include "resources/resources.h"
#include "lib/autoqpointer.h"
......
......@@ -22,13 +22,14 @@
#include "functions_p.h"
#include "akonadicollectionsearch.h"
#include "alarmcalendar.h"
#include "displaycalendar.h"
#include "editdlg.h"
#include "kalarmapp.h"
#include "kamail.h"
#include "mainwindow.h"
#include "messagewin.h"
#include "preferences.h"
#include "resourcescalendar.h"
#include "templatelistview.h"
#include "templatemenuaction.h"
#include "resources/calendarfunctions.h"
......
......@@ -20,9 +20,9 @@
#include "kalarmapp.h"
#include "alarmcalendar.h"