You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
909 lines
27 KiB
909 lines
27 KiB
/*
|
|
* alarmcalendar.cpp - KAlarm calendar file access
|
|
* Program: kalarm
|
|
* Copyright © 2001-2006,2009 by 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 "kalarm.h"
|
|
#include <unistd.h>
|
|
#include <time.h>
|
|
|
|
#include <tqfile.h>
|
|
#include <tqtextstream.h>
|
|
#include <tqregexp.h>
|
|
#include <tqtimer.h>
|
|
|
|
#include <klocale.h>
|
|
#include <kmessagebox.h>
|
|
#include <kstandarddirs.h>
|
|
#include <kstaticdeleter.h>
|
|
#include <kconfig.h>
|
|
#include <kaboutdata.h>
|
|
#include <kio/netaccess.h>
|
|
#include <kfileitem.h>
|
|
#include <ktempfile.h>
|
|
#include <kfiledialog.h>
|
|
#include <dcopclient.h>
|
|
#include <kdebug.h>
|
|
|
|
extern "C" {
|
|
#include <libical/ical.h>
|
|
}
|
|
|
|
#include <libkcal/vcaldrag.h>
|
|
#include <libkcal/vcalformat.h>
|
|
#include <libkcal/icalformat.h>
|
|
|
|
#include "calendarcompat.h"
|
|
#include "daemon.h"
|
|
#include "functions.h"
|
|
#include "kalarmapp.h"
|
|
#include "mainwindow.h"
|
|
#include "preferences.h"
|
|
#include "startdaytimer.h"
|
|
#include "alarmcalendar.moc"
|
|
|
|
using namespace KCal;
|
|
|
|
TQString AlarmCalendar::icalProductId()
|
|
{
|
|
return TQString::fromLatin1("-//K Desktop Environment//NONSGML " KALARM_NAME " %1//EN").tqarg(KAlarm::currentCalendarVersionString());
|
|
}
|
|
|
|
static const KAEvent::Status eventTypes[AlarmCalendar::NCALS] = {
|
|
KAEvent::ACTIVE, KAEvent::EXPIRED, KAEvent::DISPLAYING, KAEvent::TEMPLATE
|
|
};
|
|
static const TQString calendarNames[AlarmCalendar::NCALS] = {
|
|
TQString::fromLatin1("calendar.ics"),
|
|
TQString::fromLatin1("expired.ics"),
|
|
TQString::fromLatin1("displaying.ics"),
|
|
TQString::fromLatin1("template.ics")
|
|
};
|
|
static KStaticDeleter<AlarmCalendar> calendarDeleter[AlarmCalendar::NCALS]; // ensure that the calendar destructors are called
|
|
|
|
AlarmCalendar* AlarmCalendar::mCalendars[NCALS] = { 0, 0, 0, 0 };
|
|
|
|
|
|
/******************************************************************************
|
|
* Initialise the alarm calendars, and ensure that their file names are different.
|
|
* There are 4 calendars:
|
|
* 1) A user-independent one containing the active alarms;
|
|
* 2) A historical one containing expired alarms;
|
|
* 3) A user-specific one which contains details of alarms which are currently
|
|
* being displayed to that user and which have not yet been acknowledged;
|
|
* 4) One containing alarm templates.
|
|
* Reply = true if success, false if calendar name error.
|
|
*/
|
|
bool AlarmCalendar::initialiseCalendars()
|
|
{
|
|
KConfig* config = kapp->config();
|
|
config->setGroup(TQString::fromLatin1("General"));
|
|
TQString activeKey = TQString::fromLatin1("Calendar");
|
|
TQString expiredKey = TQString::fromLatin1("ExpiredCalendar");
|
|
TQString templateKey = TQString::fromLatin1("TemplateCalendar");
|
|
TQString displayCal, activeCal, expiredCal, templateCal;
|
|
calendarDeleter[ACTIVE].setObject(mCalendars[ACTIVE], createCalendar(ACTIVE, config, activeCal, activeKey));
|
|
calendarDeleter[EXPIRED].setObject(mCalendars[EXPIRED], createCalendar(EXPIRED, config, expiredCal, expiredKey));
|
|
calendarDeleter[DISPLAY].setObject(mCalendars[DISPLAY], createCalendar(DISPLAY, config, displayCal));
|
|
calendarDeleter[TEMPLATE].setObject(mCalendars[TEMPLATE], createCalendar(TEMPLATE, config, templateCal, templateKey));
|
|
|
|
TQString errorKey1, errorKey2;
|
|
if (activeCal == displayCal)
|
|
errorKey1 = activeKey;
|
|
else if (expiredCal == displayCal)
|
|
errorKey1 = expiredKey;
|
|
else if (templateCal == displayCal)
|
|
errorKey1 = templateKey;
|
|
if (!errorKey1.isNull())
|
|
{
|
|
kdError(5950) << "AlarmCalendar::initialiseCalendars(): '" << errorKey1 << "' calendar name = display calendar name\n";
|
|
TQString file = config->readPathEntry(errorKey1);
|
|
KAlarmApp::displayFatalError(i18n("%1: file name not permitted: %2").tqarg(errorKey1).tqarg(file));
|
|
return false;
|
|
}
|
|
if (activeCal == expiredCal)
|
|
{
|
|
errorKey1 = activeKey;
|
|
errorKey2 = expiredKey;
|
|
}
|
|
else if (activeCal == templateCal)
|
|
{
|
|
errorKey1 = activeKey;
|
|
errorKey2 = templateKey;
|
|
}
|
|
else if (expiredCal == templateCal)
|
|
{
|
|
errorKey1 = expiredKey;
|
|
errorKey2 = templateKey;
|
|
}
|
|
if (!errorKey1.isNull())
|
|
{
|
|
kdError(5950) << "AlarmCalendar::initialiseCalendars(): calendar names clash: " << errorKey1 << ", " << errorKey2 << endl;
|
|
KAlarmApp::displayFatalError(i18n("%1, %2: file names must be different").tqarg(errorKey1).tqarg(errorKey2));
|
|
return false;
|
|
}
|
|
if (!mCalendars[ACTIVE]->valid())
|
|
{
|
|
TQString path = mCalendars[ACTIVE]->path();
|
|
kdError(5950) << "AlarmCalendar::initialiseCalendars(): invalid name: " << path << endl;
|
|
KAlarmApp::displayFatalError(i18n("Invalid calendar file name: %1").tqarg(path));
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* Create an alarm calendar instance.
|
|
* If 'configKey' is non-null, the calendar will be converted to ICal format.
|
|
*/
|
|
AlarmCalendar* AlarmCalendar::createCalendar(CalID type, KConfig* config, TQString& writePath, const TQString& configKey)
|
|
{
|
|
static TQRegExp vcsRegExp(TQString::fromLatin1("\\.vcs$"));
|
|
static TQString ical = TQString::fromLatin1(".ics");
|
|
|
|
if (configKey.isNull())
|
|
{
|
|
writePath = locateLocal("appdata", calendarNames[type]);
|
|
return new AlarmCalendar(writePath, type);
|
|
}
|
|
else
|
|
{
|
|
TQString readPath = config->readPathEntry(configKey, locateLocal("appdata", calendarNames[type]));
|
|
writePath = readPath;
|
|
writePath.replace(vcsRegExp, ical);
|
|
return new AlarmCalendar(readPath, type, writePath, configKey);
|
|
}
|
|
}
|
|
|
|
/******************************************************************************
|
|
* Terminate access to all calendars.
|
|
*/
|
|
void AlarmCalendar::terminateCalendars()
|
|
{
|
|
for (int i = 0; i < NCALS; ++i)
|
|
{
|
|
calendarDeleter[i].destructObject();
|
|
mCalendars[i] = 0;
|
|
}
|
|
}
|
|
|
|
/******************************************************************************
|
|
* Return a calendar, opening it first if not already open.
|
|
* Reply = calendar instance
|
|
* = 0 if calendar could not be opened.
|
|
*/
|
|
AlarmCalendar* AlarmCalendar::calendarOpen(CalID id)
|
|
{
|
|
AlarmCalendar* cal = mCalendars[id];
|
|
if (!cal->mPurgeDays)
|
|
return 0; // all events are automatically purged from the calendar
|
|
if (cal->open())
|
|
return cal;
|
|
kdError(5950) << "AlarmCalendar::calendarOpen(" << calendarNames[id] << "): open error\n";
|
|
return 0;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* Find and return the event with the specified ID.
|
|
* The calendar searched is determined by the calendar identifier in the ID.
|
|
*/
|
|
const KCal::Event* AlarmCalendar::getEvent(const TQString& uniqueID)
|
|
{
|
|
if (uniqueID.isEmpty())
|
|
return 0;
|
|
CalID calID;
|
|
switch (KAEvent::uidStatus(uniqueID))
|
|
{
|
|
case KAEvent::ACTIVE: calID = ACTIVE; break;
|
|
case KAEvent::TEMPLATE: calID = TEMPLATE; break;
|
|
case KAEvent::EXPIRED: calID = EXPIRED; break;
|
|
case KAEvent::DISPLAYING: calID = DISPLAY; break;
|
|
default:
|
|
return 0;
|
|
}
|
|
AlarmCalendar* cal = calendarOpen(calID);
|
|
if (!cal)
|
|
return 0;
|
|
return cal->event(uniqueID);
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
* Constructor.
|
|
* If 'icalPath' is non-null, the file will be always be saved in ICal format.
|
|
* If 'configKey' is also non-null, that config file entry will be updated when
|
|
* the file is saved in ICal format.
|
|
*/
|
|
AlarmCalendar::AlarmCalendar(const TQString& path, CalID type, const TQString& icalPath,
|
|
const TQString& configKey)
|
|
: mCalendar(0),
|
|
mConfigKey(icalPath.isNull() ? TQString() : configKey),
|
|
mType(eventTypes[type]),
|
|
mPurgeDays(-1), // default to not purging
|
|
mOpen(false),
|
|
mPurgeDaysQueued(-1),
|
|
mUpdateCount(0),
|
|
mUpdateSave(false)
|
|
{
|
|
mUrl.setPath(path); // N.B. constructor mUrl(path) doesn't work with UNIX paths
|
|
mICalUrl.setPath(icalPath.isNull() ? path : icalPath);
|
|
mVCal = (icalPath.isNull() || path != icalPath); // is the calendar in ICal or VCal format?
|
|
}
|
|
|
|
AlarmCalendar::~AlarmCalendar()
|
|
{
|
|
close();
|
|
}
|
|
|
|
/******************************************************************************
|
|
* Open the calendar file if not already open, and load it into memory.
|
|
*/
|
|
bool AlarmCalendar::open()
|
|
{
|
|
if (mOpen)
|
|
return true;
|
|
if (!mUrl.isValid())
|
|
return false;
|
|
|
|
kdDebug(5950) << "AlarmCalendar::open(" << mUrl.prettyURL() << ")\n";
|
|
if (!mCalendar)
|
|
mCalendar = new CalendarLocal(TQString::fromLatin1("UTC"));
|
|
mCalendar->setLocalTime(); // write out using local time (i.e. no time zone)
|
|
|
|
// Check for file's existence, assuming that it does exist when uncertain,
|
|
// to avoid overwriting it.
|
|
if (!KIO::NetAccess::exists(mUrl, true, MainWindow::mainMainWindow()))
|
|
{
|
|
// The calendar file doesn't yet exist, so create it
|
|
if (create())
|
|
load();
|
|
}
|
|
else
|
|
{
|
|
// Load the existing calendar file
|
|
if (load() == 0)
|
|
{
|
|
if (create()) // zero-length file - create a new one
|
|
load();
|
|
}
|
|
}
|
|
if (!mOpen)
|
|
{
|
|
delete mCalendar;
|
|
mCalendar = 0;
|
|
}
|
|
return mOpen;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* Private method to create a new calendar file.
|
|
* It is always created in iCalendar format.
|
|
*/
|
|
bool AlarmCalendar::create()
|
|
{
|
|
if (mICalUrl.isLocalFile())
|
|
return saveCal(mICalUrl.path());
|
|
else
|
|
{
|
|
KTempFile tmpFile;
|
|
return saveCal(tmpFile.name());
|
|
}
|
|
}
|
|
|
|
/******************************************************************************
|
|
* Load the calendar file into memory.
|
|
* Reply = 1 if success
|
|
* = 0 if zero-length file exists.
|
|
* = -1 if failure to load calendar file
|
|
* = -2 if instance uninitialised.
|
|
*/
|
|
int AlarmCalendar::load()
|
|
{
|
|
if (!mCalendar)
|
|
return -2;
|
|
|
|
kdDebug(5950) << "AlarmCalendar::load(): " << mUrl.prettyURL() << endl;
|
|
TQString tmpFile;
|
|
if (!KIO::NetAccess::download(mUrl, tmpFile, MainWindow::mainMainWindow()))
|
|
{
|
|
kdError(5950) << "AlarmCalendar::load(): Load failure" << endl;
|
|
KMessageBox::error(0, i18n("Cannot open calendar:\n%1").tqarg(mUrl.prettyURL()));
|
|
return -1;
|
|
}
|
|
kdDebug(5950) << "AlarmCalendar::load(): --- Downloaded to " << tmpFile << endl;
|
|
mCalendar->setTimeZoneId(TQString()); // default to the local time zone for reading
|
|
bool loaded = mCalendar->load(tmpFile);
|
|
mCalendar->setLocalTime(); // write using local time (i.e. no time zone)
|
|
if (!loaded)
|
|
{
|
|
// Check if the file is zero length
|
|
KIO::NetAccess::removeTempFile(tmpFile);
|
|
KIO::UDSEntry uds;
|
|
KIO::NetAccess::stat(mUrl, uds, MainWindow::mainMainWindow());
|
|
KFileItem fi(uds, mUrl);
|
|
if (!fi.size())
|
|
return 0; // file is zero length
|
|
kdError(5950) << "AlarmCalendar::load(): Error loading calendar file '" << tmpFile << "'" << endl;
|
|
KMessageBox::error(0, i18n("Error loading calendar:\n%1\n\nPlease fix or delete the file.").tqarg(mUrl.prettyURL()));
|
|
// load() could have partially populated the calendar, so clear it out
|
|
mCalendar->close();
|
|
delete mCalendar;
|
|
mCalendar = 0;
|
|
return -1;
|
|
}
|
|
if (!mLocalFile.isEmpty())
|
|
KIO::NetAccess::removeTempFile(mLocalFile); // removes it only if it IS a temporary file
|
|
mLocalFile = tmpFile;
|
|
|
|
CalendarCompat::fix(*mCalendar, mLocalFile); // convert events to current KAlarm format for when calendar is saved
|
|
mOpen = true;
|
|
return 1;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* Reload the calendar file into memory.
|
|
*/
|
|
bool AlarmCalendar::reload()
|
|
{
|
|
if (!mCalendar)
|
|
return false;
|
|
kdDebug(5950) << "AlarmCalendar::reload(): " << mUrl.prettyURL() << endl;
|
|
close();
|
|
bool result = open();
|
|
return result;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* Save the calendar from memory to file.
|
|
* If a filename is specified, create a new calendar file.
|
|
*/
|
|
bool AlarmCalendar::saveCal(const TQString& newFile)
|
|
{
|
|
if (!mCalendar || !mOpen && newFile.isNull())
|
|
return false;
|
|
|
|
kdDebug(5950) << "AlarmCalendar::saveCal(\"" << newFile << "\", " << mType << ")\n";
|
|
TQString saveFilename = newFile.isNull() ? mLocalFile : newFile;
|
|
if (mVCal && newFile.isNull() && mUrl.isLocalFile())
|
|
saveFilename = mICalUrl.path();
|
|
if (!mCalendar->save(saveFilename, new ICalFormat))
|
|
{
|
|
kdError(5950) << "AlarmCalendar::saveCal(" << saveFilename << "): failed.\n";
|
|
KMessageBox::error(0, i18n("Failed to save calendar to\n'%1'").tqarg(mICalUrl.prettyURL()));
|
|
return false;
|
|
}
|
|
|
|
if (!mICalUrl.isLocalFile())
|
|
{
|
|
if (!KIO::NetAccess::upload(saveFilename, mICalUrl, MainWindow::mainMainWindow()))
|
|
{
|
|
kdError(5950) << "AlarmCalendar::saveCal(" << saveFilename << "): upload failed.\n";
|
|
KMessageBox::error(0, i18n("Cannot upload calendar to\n'%1'").tqarg(mICalUrl.prettyURL()));
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (mVCal)
|
|
{
|
|
// The file was in vCalendar format, but has now been saved in iCalendar format.
|
|
// Save the change in the config file.
|
|
if (!mConfigKey.isNull())
|
|
{
|
|
KConfig* config = kapp->config();
|
|
config->setGroup(TQString::fromLatin1("General"));
|
|
config->writePathEntry(mConfigKey, mICalUrl.path());
|
|
config->sync();
|
|
}
|
|
mUrl = mICalUrl;
|
|
mVCal = false;
|
|
}
|
|
|
|
mUpdateSave = false;
|
|
emit calendarSaved(this);
|
|
return true;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* Delete any temporary file at program exit.
|
|
*/
|
|
void AlarmCalendar::close()
|
|
{
|
|
if (!mLocalFile.isEmpty())
|
|
{
|
|
KIO::NetAccess::removeTempFile(mLocalFile); // removes it only if it IS a temporary file
|
|
mLocalFile = "";
|
|
}
|
|
if (mCalendar)
|
|
{
|
|
mCalendar->close();
|
|
delete mCalendar;
|
|
mCalendar = 0;
|
|
}
|
|
mOpen = false;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* Import alarms from an external calendar and merge them into KAlarm's calendar.
|
|
* The alarms are given new unique event IDs.
|
|
* Parameters: parent = parent widget for error message boxes
|
|
* Reply = true if all alarms in the calendar were successfully imported
|
|
* = false if any alarms failed to be imported.
|
|
*/
|
|
bool AlarmCalendar::importAlarms(TQWidget* parent)
|
|
{
|
|
KURL url = KFileDialog::getOpenURL(TQString::fromLatin1(":importalarms"),
|
|
TQString::fromLatin1("*.vcs *.ics|%1").tqarg(i18n("Calendar Files")), parent);
|
|
if (url.isEmpty())
|
|
{
|
|
kdError(5950) << "AlarmCalendar::importAlarms(): Empty URL" << endl;
|
|
return false;
|
|
}
|
|
if (!url.isValid())
|
|
{
|
|
kdDebug(5950) << "AlarmCalendar::importAlarms(): Invalid URL" << endl;
|
|
return false;
|
|
}
|
|
kdDebug(5950) << "AlarmCalendar::importAlarms(" << url.prettyURL() << ")" << endl;
|
|
|
|
bool success = true;
|
|
TQString filename;
|
|
bool local = url.isLocalFile();
|
|
if (local)
|
|
{
|
|
filename = url.path();
|
|
if (!KStandardDirs::exists(filename))
|
|
{
|
|
kdDebug(5950) << "AlarmCalendar::importAlarms(): File '" << url.prettyURL() << "' not found" << endl;
|
|
KMessageBox::error(parent, i18n("Could not load calendar '%1'.").tqarg(url.prettyURL()));
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!KIO::NetAccess::download(url, filename, MainWindow::mainMainWindow()))
|
|
{
|
|
kdError(5950) << "AlarmCalendar::importAlarms(): Download failure" << endl;
|
|
KMessageBox::error(parent, i18n("Cannot download calendar:\n%1").tqarg(url.prettyURL()));
|
|
return false;
|
|
}
|
|
kdDebug(5950) << "--- Downloaded to " << filename << endl;
|
|
}
|
|
|
|
// Read the calendar and add its alarms to the current calendars
|
|
CalendarLocal cal(TQString::fromLatin1("UTC"));
|
|
cal.setLocalTime(); // write out using local time (i.e. no time zone)
|
|
success = cal.load(filename);
|
|
if (!success)
|
|
{
|
|
kdDebug(5950) << "AlarmCalendar::importAlarms(): error loading calendar '" << filename << "'" << endl;
|
|
KMessageBox::error(parent, i18n("Could not load calendar '%1'.").tqarg(url.prettyURL()));
|
|
}
|
|
else
|
|
{
|
|
CalendarCompat::fix(cal, filename);
|
|
bool saveActive = false;
|
|
bool saveExpired = false;
|
|
bool saveTemplate = false;
|
|
AlarmCalendar* active = activeCalendar();
|
|
AlarmCalendar* expired = expiredCalendar();
|
|
AlarmCalendar* templat = 0;
|
|
AlarmCalendar* acal;
|
|
Event::List events = cal.rawEvents();
|
|
for (Event::List::ConstIterator it = events.begin(); it != events.end(); ++it)
|
|
{
|
|
const Event* event = *it;
|
|
if (event->alarms().isEmpty() || !KAEvent(*event).valid())
|
|
continue; // ignore events without alarms, or usable alarms
|
|
KAEvent::Status type = KAEvent::uidStatus(event->uid());
|
|
switch (type)
|
|
{
|
|
case KAEvent::ACTIVE:
|
|
acal = active;
|
|
saveActive = true;
|
|
break;
|
|
case KAEvent::EXPIRED:
|
|
acal = expired;
|
|
saveExpired = true;
|
|
break;
|
|
case KAEvent::TEMPLATE:
|
|
if (!templat)
|
|
templat = templateCalendarOpen();
|
|
acal = templat;
|
|
saveTemplate = true;
|
|
break;
|
|
default:
|
|
continue;
|
|
}
|
|
if (!acal)
|
|
continue;
|
|
|
|
Event* newev = new Event(*event);
|
|
|
|
// If there is a display alarm without display text, use the event
|
|
// summary text instead.
|
|
if (type == KAEvent::ACTIVE && !newev->summary().isEmpty())
|
|
{
|
|
const Alarm::List& alarms = newev->alarms();
|
|
for (Alarm::List::ConstIterator ait = alarms.begin(); ait != alarms.end(); ++ait)
|
|
{
|
|
Alarm* alarm = *ait;
|
|
if (alarm->type() == Alarm::Display && alarm->text().isEmpty())
|
|
alarm->setText(newev->summary());
|
|
}
|
|
newev->setSummary(TQString()); // KAlarm only uses summary for template names
|
|
}
|
|
// Give the event a new ID and add it to the calendar
|
|
newev->setUid(KAEvent::uid(CalFormat::createUniqueId(), type));
|
|
if (!acal->mCalendar->addEvent(newev))
|
|
success = false;
|
|
}
|
|
|
|
// Save any calendars which have been modified
|
|
if (saveActive)
|
|
active->saveCal();
|
|
if (saveExpired)
|
|
expired->saveCal();
|
|
if (saveTemplate)
|
|
templat->saveCal();
|
|
}
|
|
if (!local)
|
|
KIO::NetAccess::removeTempFile(filename);
|
|
return success;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* Flag the start of a group of calendar update calls.
|
|
* The purpose is to avoid multiple calendar saves during a group of operations.
|
|
*/
|
|
void AlarmCalendar::startUpdate()
|
|
{
|
|
++mUpdateCount;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* Flag the end of a group of calendar update calls.
|
|
* The calendar is saved if appropriate.
|
|
*/
|
|
bool AlarmCalendar::endUpdate()
|
|
{
|
|
if (mUpdateCount > 0)
|
|
--mUpdateCount;
|
|
if (!mUpdateCount)
|
|
{
|
|
if (mUpdateSave)
|
|
return saveCal();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* Save the calendar, or flag it for saving if in a group of calendar update calls.
|
|
*/
|
|
bool AlarmCalendar::save()
|
|
{
|
|
if (mUpdateCount)
|
|
{
|
|
mUpdateSave = true;
|
|
return true;
|
|
}
|
|
else
|
|
return saveCal();
|
|
}
|
|
|
|
#if 0
|
|
/******************************************************************************
|
|
* If it is VCal format, convert the calendar URL to ICal and save the new URL
|
|
* in the config file.
|
|
*/
|
|
void AlarmCalendar::convertToICal()
|
|
{
|
|
if (mVCal)
|
|
{
|
|
if (!mConfigKey.isNull())
|
|
{
|
|
KConfig* config = kapp->config();
|
|
config->setGroup(TQString::fromLatin1("General"));
|
|
config->writePathEntry(mConfigKey, mICalUrl.path());
|
|
config->sync();
|
|
}
|
|
mUrl = mICalUrl;
|
|
mVCal = false;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/******************************************************************************
|
|
* Set the number of days to keep alarms.
|
|
* Alarms which are older are purged immediately, and at the start of each day.
|
|
*/
|
|
void AlarmCalendar::setPurgeDays(int days)
|
|
{
|
|
if (days != mPurgeDays)
|
|
{
|
|
int oldDays = mPurgeDays;
|
|
mPurgeDays = days;
|
|
if (mPurgeDays <= 0)
|
|
StartOfDayTimer::disconnect(this);
|
|
if (oldDays < 0 || days >= 0 && days < oldDays)
|
|
{
|
|
// Alarms are now being kept for less long, so purge them
|
|
if (open())
|
|
slotPurge();
|
|
}
|
|
else if (mPurgeDays > 0)
|
|
startPurgeTimer();
|
|
}
|
|
}
|
|
|
|
/******************************************************************************
|
|
* Called at the start of each day by the purge timer.
|
|
* Purge all events from the calendar whose end time is longer ago than 'mPurgeDays'.
|
|
*/
|
|
void AlarmCalendar::slotPurge()
|
|
{
|
|
purge(mPurgeDays);
|
|
startPurgeTimer();
|
|
}
|
|
|
|
/******************************************************************************
|
|
* Purge all events from the calendar whose end time is longer ago than
|
|
* 'daysToKeep'. All events are deleted if 'daysToKeep' is zero.
|
|
*/
|
|
void AlarmCalendar::purge(int daysToKeep)
|
|
{
|
|
if (mPurgeDaysQueued < 0 || daysToKeep < mPurgeDaysQueued)
|
|
mPurgeDaysQueued = daysToKeep;
|
|
|
|
// Do the purge once any other current operations are completed
|
|
theApp()->processQueue();
|
|
}
|
|
|
|
/******************************************************************************
|
|
* This method must only be called from the main KAlarm queue processing loop,
|
|
* to prevent asynchronous calendar operations interfering with one another.
|
|
*
|
|
* Purge all events from the calendar whose end time is longer ago than 'daysToKeep'.
|
|
* All events are deleted if 'daysToKeep' is zero.
|
|
* The calendar must already be open.
|
|
*/
|
|
void AlarmCalendar::purgeIfQueued()
|
|
{
|
|
if (mPurgeDaysQueued >= 0)
|
|
{
|
|
if (open())
|
|
{
|
|
kdDebug(5950) << "AlarmCalendar::purgeIfQueued(" << mPurgeDaysQueued << ")\n";
|
|
bool changed = false;
|
|
TQDate cutoff = TQDate::currentDate().addDays(-mPurgeDaysQueued);
|
|
Event::List events = mCalendar->rawEvents();
|
|
for (Event::List::ConstIterator it = events.begin(); it != events.end(); ++it)
|
|
{
|
|
Event* kcalEvent = *it;
|
|
if (!mPurgeDaysQueued || kcalEvent->created().date() < cutoff)
|
|
{
|
|
mCalendar->deleteEvent(kcalEvent);
|
|
changed = true;
|
|
}
|
|
}
|
|
if (changed)
|
|
{
|
|
saveCal();
|
|
emit purged();
|
|
}
|
|
mPurgeDaysQueued = -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
* Start the purge timer to expire at the start of the next day (using the user-
|
|
* defined start-of-day time).
|
|
*/
|
|
void AlarmCalendar::startPurgeTimer()
|
|
{
|
|
if (mPurgeDays > 0)
|
|
StartOfDayTimer::connect(this, TQT_SLOT(slotPurge()));
|
|
}
|
|
|
|
/******************************************************************************
|
|
* Add the specified event to the calendar.
|
|
* If it is the active calendar and 'useEventID' is false, a new event ID is
|
|
* created. In all other cases, the event ID is taken from 'event'.
|
|
* 'event' is updated with the actual event ID.
|
|
* Reply = the KCal::Event as written to the calendar.
|
|
*/
|
|
Event* AlarmCalendar::addEvent(KAEvent& event, bool useEventID)
|
|
{
|
|
if (!mOpen)
|
|
return 0;
|
|
TQString id = event.id();
|
|
Event* kcalEvent = new Event;
|
|
if (mType == KAEvent::ACTIVE)
|
|
{
|
|
if (id.isEmpty())
|
|
useEventID = false;
|
|
if (!useEventID)
|
|
event.setEventID(kcalEvent->uid());
|
|
}
|
|
else
|
|
{
|
|
if (id.isEmpty())
|
|
id = kcalEvent->uid();
|
|
useEventID = true;
|
|
}
|
|
if (useEventID)
|
|
{
|
|
id = KAEvent::uid(id, mType);
|
|
event.setEventID(id);
|
|
kcalEvent->setUid(id);
|
|
}
|
|
event.updateKCalEvent(*kcalEvent, false, (mType == KAEvent::EXPIRED), true);
|
|
mCalendar->addEvent(kcalEvent);
|
|
event.clearUpdated();
|
|
return kcalEvent;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* Update the specified event in the calendar with its new contents.
|
|
* The event retains the same ID.
|
|
*/
|
|
void AlarmCalendar::updateEvent(const KAEvent& evnt)
|
|
{
|
|
if (mOpen)
|
|
{
|
|
Event* kcalEvent = event(evnt.id());
|
|
if (kcalEvent)
|
|
{
|
|
evnt.updateKCalEvent(*kcalEvent);
|
|
evnt.clearUpdated();
|
|
if (mType == KAEvent::ACTIVE)
|
|
Daemon::savingEvent(evnt.id());
|
|
return;
|
|
}
|
|
}
|
|
if (mType == KAEvent::ACTIVE)
|
|
Daemon::eventHandled(evnt.id(), false);
|
|
}
|
|
|
|
/******************************************************************************
|
|
* Delete the specified event from the calendar, if it exists.
|
|
* The calendar is then optionally saved.
|
|
*/
|
|
bool AlarmCalendar::deleteEvent(const TQString& eventID, bool saveit)
|
|
{
|
|
if (mOpen)
|
|
{
|
|
Event* kcalEvent = event(eventID);
|
|
if (kcalEvent)
|
|
{
|
|
mCalendar->deleteEvent(kcalEvent);
|
|
if (mType == KAEvent::ACTIVE)
|
|
Daemon::savingEvent(eventID);
|
|
if (saveit)
|
|
return save();
|
|
return true;
|
|
}
|
|
}
|
|
if (mType == KAEvent::ACTIVE)
|
|
Daemon::eventHandled(eventID, false);
|
|
return false;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* Emit a signal to indicate whether the calendar is empty.
|
|
*/
|
|
void AlarmCalendar::emitEmptyStatus()
|
|
{
|
|
emit emptyStatus(events().isEmpty());
|
|
}
|
|
|
|
/******************************************************************************
|
|
* Return the event with the specified ID.
|
|
*/
|
|
KCal::Event* AlarmCalendar::event(const TQString& uniqueID)
|
|
{
|
|
return mCalendar ? mCalendar->event(uniqueID) : 0;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* Return all events in the calendar which contain usable alarms.
|
|
*/
|
|
KCal::Event::List AlarmCalendar::events()
|
|
{
|
|
if (!mCalendar)
|
|
return KCal::Event::List();
|
|
KCal::Event::List list = mCalendar->rawEvents();
|
|
KCal::Event::List::Iterator it = list.begin();
|
|
while (it != list.end())
|
|
{
|
|
KCal::Event* event = *it;
|
|
if (event->alarms().isEmpty() || !KAEvent(*event).valid())
|
|
it = list.remove(it);
|
|
else
|
|
++it;
|
|
}
|
|
return list;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* Return all events which have alarms falling within the specified time range.
|
|
*/
|
|
Event::List AlarmCalendar::eventsWithAlarms(const TQDateTime& from, const TQDateTime& to)
|
|
{
|
|
kdDebug(5950) << "AlarmCalendar::eventsWithAlarms(" << from.toString() << " - " << to.toString() << ")\n";
|
|
Event::List evnts;
|
|
TQDateTime dt;
|
|
Event::List allEvents = events(); // ignore events without usable alarms
|
|
for (Event::List::ConstIterator it = allEvents.begin(); it != allEvents.end(); ++it)
|
|
{
|
|
Event* e = *it;
|
|
bool recurs = e->doesRecur();
|
|
int endOffset = 0;
|
|
bool endOffsetValid = false;
|
|
const Alarm::List& alarms = e->alarms();
|
|
for (Alarm::List::ConstIterator ait = alarms.begin(); ait != alarms.end(); ++ait)
|
|
{
|
|
Alarm* alarm = *ait;
|
|
if (alarm->enabled())
|
|
{
|
|
if (recurs)
|
|
{
|
|
if (alarm->hasTime())
|
|
dt = alarm->time();
|
|
else
|
|
{
|
|
// The alarm time is defined by an offset from the event start or end time.
|
|
// Find the offset from the event start time, which is also used as the
|
|
// offset from the recurrence time.
|
|
int offset = 0;
|
|
if (alarm->hasStartOffset())
|
|
offset = alarm->startOffset().asSeconds();
|
|
else if (alarm->hasEndOffset())
|
|
{
|
|
if (!endOffsetValid)
|
|
{
|
|
endOffset = e->hasDuration() ? e->duration() : e->hasEndDate() ? e->dtStart().secsTo(e->dtEnd()) : 0;
|
|
endOffsetValid = true;
|
|
}
|
|
offset = alarm->endOffset().asSeconds() + endOffset;
|
|
}
|
|
// Adjust the 'from' date/time and find the next recurrence at or after it
|
|
TQDateTime pre = from.addSecs(-offset - 1);
|
|
if (e->doesFloat() && pre.time() < Preferences::startOfDay())
|
|
pre = pre.addDays(-1); // today's recurrence (if today recurs) is still to come
|
|
dt = e->recurrence()->getNextDateTime(pre);
|
|
if (!dt.isValid())
|
|
continue;
|
|
dt = dt.addSecs(offset);
|
|
}
|
|
}
|
|
else
|
|
dt = alarm->time();
|
|
if (dt >= from && dt <= to)
|
|
{
|
|
kdDebug(5950) << "AlarmCalendar::events() '" << e->summary()
|
|
<< "': " << dt.toString() << endl;
|
|
evnts.append(e);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return evnts;
|
|
}
|