/* * alarmresource.cpp - base class for a KAlarm alarm calendar resource * Program: kalarm * Copyright © 2006-2011 by David Jarvie * * 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 "alarmresource.moc" using namespace KCal; #include "alarmresources.h" #include #include #include #include #include #include #include void (*AlarmResource::mCalIDFunction)(CalendarLocal&) = 0; void (*AlarmResource::mCustomEventFunction)(AlarmResource*, CalendarLocal*) = 0; KACalendar::Compat (*AlarmResource::mFixFunction)(CalendarLocal&, const QString&, AlarmResource*, AlarmResource::FixFunc, bool* wrongType) = 0; int AlarmResource::mDebugArea = 0; bool AlarmResource::mNoGui = false; AlarmResource::AlarmResource() : ResourceCached(), mLock(0), mType(static_cast(0)), // invalid mStandard(false), mCloseAfterSave(false), mWrongAlarmType(false), mCompatibility(KACalendar::Incompatible), mReconfiguring(0), mLoaded(false), mLoading(false) { // Prevent individual events being set read-only when loading a read-only resource setNoReadOnlyOnLoad(true); init(); } AlarmResource::AlarmResource(const KConfigGroup& group) : ResourceCached(group), mLock(0), mType(static_cast(0)), // invalid mStandard(false), mCloseAfterSave(false), mWrongAlarmType(false), mCompatibility(KACalendar::Incompatible), mReconfiguring(0), mLoaded(false), mLoading(false) { // Prevent individual events being set read-only when loading a read-only resource setNoReadOnlyOnLoad(true); ResourceCached::readConfig(group); int type = group.readEntry("AlarmType", static_cast(CalEvent::ACTIVE)); switch (type) { case CalEvent::ACTIVE: case CalEvent::ARCHIVED: case CalEvent::TEMPLATE: mType = static_cast(type); mStandard = group.readEntry("Standard", true); break; default: break; } mColour = group.readEntry("Color", QColor()); init(); } AlarmResource::AlarmResource(CalEvent::Type type) : ResourceCached(), mLock(0), mType(type), mStandard(false), mCloseAfterSave(false), mCompatibility(KACalendar::Incompatible), mReconfiguring(0), mLoaded(false), mLoading(false) { init(); } void AlarmResource::init() { enableChangeNotification(); if (mType == CalEvent::ARCHIVED) { // Prevent unnecessary multiple saves of archived alarm resources. // When multiple alarms are deleted as a group, the archive // resource would be saved once for each alarm. Ironically, setting // the resource to be automatically saved will prevent this, since // automatic saving delays for a second after each change before // actually saving the resource, thereby ensuring that they are // saved as a group. setSavePolicy(SaveAlways); } } AlarmResource::~AlarmResource() { delete mLock; } void AlarmResource::writeConfig(KConfigGroup& group) { group.writeEntry("AlarmType", static_cast(mType)); if (mColour.isValid()) group.writeEntry("Color", mColour); else group.deleteEntry("Color"); group.writeEntry("Standard", mStandard); ResourceCached::writeConfig(group); ResourceCalendar::writeConfig(group); } void AlarmResource::startReconfig() { mOldReadOnly = ResourceCached::readOnly(); mNewReadOnly = mOldReadOnly; mReconfiguring = 1; } void AlarmResource::applyReconfig() { if (!mReconfiguring) return; if (mReconfiguring == 1) { // Called before derived classes do their stuff ResourceCached::setReadOnly(mNewReadOnly); mReconfiguring = 2; } else { // Called when derived classes have done their stuff setReadOnly(mNewReadOnly); mReconfiguring = 0; } } /****************************************************************************** * If a function is defined to convert alarms to the current format, call it. * Set the resource to read-only if it isn't the current format version, or if * its format is unknown. */ void AlarmResource::checkCompatibility(const QString& filename) { bool wrongType = false; bool oldReadOnly = readOnly(); mCompatibility = KACalendar::Incompatible; // assume the worst if (mFixFunction) { // Check whether the version is compatible (and convert it if desired) mCompatibility = (*mFixFunction)(*calendar(), filename, this, PROMPT, &wrongType); if (wrongType) kDebug(KARES_DEBUG) << resourceName() << ": contains wrong alarm type(s)"; if (mCompatibility == KACalendar::Converted) { // Set mCompatibility first to ensure that readOnly() returns // the correct value and that save() therefore works. mCompatibility = KACalendar::Current; save(); } if (mCompatibility != KACalendar::Current && mCompatibility != KACalendar::ByEvent) { // It's not in the current KAlarm format, so it will be read-only to prevent incompatible updates kDebug(KARES_DEBUG) << resourceName() << ": opened read-only (not current KAlarm format)"; } } setWrongAlarmType(wrongType); if (readOnly() != oldReadOnly) emit readOnlyChanged(this); // the effective read-only status has changed } /****************************************************************************** * If a function is defined to convert alarms to the current format, call it to * convert an individual file within the overall resource. */ KACalendar::Compat AlarmResource::checkCompatibility(CalendarLocal& calendar, const QString& filename, FixFunc conv, bool* wrongType) { if (wrongType) *wrongType = false; KACalendar::Compat compat = KACalendar::Incompatible; // assume the worst if (mFixFunction) { // Check whether the version is compatible (and convert it if desired) compat = (*mFixFunction)(calendar, filename, this, conv, wrongType); if (compat == KACalendar::Converted) calendar.save(filename); } return compat; } KACalendar::Compat AlarmResource::compatibility(const Event* event) const { if (mCompatibility != KACalendar::ByEvent) return mCompatibility; CompatibilityMap::ConstIterator it = mCompatibilityMap.find(event); if (it == mCompatibilityMap.constEnd()) return KACalendar::Incompatible; // event not found!?! - assume the worst return it.value(); } /****************************************************************************** * Set whether the resource contains only the wrong alarm types. * If the wrong types, disable the resource. */ void AlarmResource::setWrongAlarmType(bool wrongType, bool emitSignal) { if (wrongType != mWrongAlarmType) { mWrongAlarmType = wrongType; if (emitSignal) emit wrongAlarmTypeChanged(this); if (mWrongAlarmType) setEnabled(false); } } /****************************************************************************** * If a function is defined to update KAlarm event instances, call it. */ void AlarmResource::updateCustomEvents(bool useCalendar) { if (mCustomEventFunction && isEnabled()) (*mCustomEventFunction)(this, useCalendar ? calendar() : 0); } bool AlarmResource::writable(const Event* event) const { return isActive() && !KCal::ResourceCached::readOnly() && compatibility(event) == KACalendar::Current; } bool AlarmResource::readOnly() const { return KCal::ResourceCached::readOnly() || (isActive() && mCompatibility != KACalendar::Current && mCompatibility != KACalendar::ByEvent); } void AlarmResource::setReadOnly(bool ronly) { if (mReconfiguring == 1) { mNewReadOnly = ronly; return; } kDebug(KARES_DEBUG) << ronly; bool oldRCronly = (mReconfiguring == 2) ? mOldReadOnly : ResourceCached::readOnly(); bool oldronly = (oldRCronly || (mCompatibility != KACalendar::Current && mCompatibility != KACalendar::ByEvent)); if (!ronly && isActive()) { // Trying to change the resource to read-write. // Only allow this if it is in, or can be converted to, the current KAlarm format. switch (mCompatibility) { case KACalendar::Incompatible: emit notWritable(this); // allow an error message to be output return; case KACalendar::Convertible: if (mReconfiguring <= 2) { if (!isOpen()) return; load(NoSyncCache); // give user the option of converting it } if (mCompatibility != KACalendar::Current) return; // not converted, so keep as read-only break; case KACalendar::Current: case KACalendar::ByEvent: case KACalendar::Converted: // shouldn't ever happen break; } } if (ronly != oldRCronly) ResourceCached::setReadOnly(ronly); if ((ronly || (mCompatibility != KACalendar::Current && mCompatibility != KACalendar::ByEvent)) != oldronly) emit readOnlyChanged(this); // the effective read-only status has changed } void AlarmResource::setEnabled(bool enable) { if (mWrongAlarmType) enable = false; if (isActive() != enable) { setActive(enable); enableResource(enable); emit enabledChanged(this); } } void AlarmResource::setColour(const QColor& colour) { if (colour != mColour) { mColour = colour; emit colourChanged(this); } } bool AlarmResource::saveAndClose(CacheAction action, Incidence* incidence) { bool result = save(action, incidence); if (isSaving()) mCloseAfterSave = true; // ensure it's closed if saving is asynchronous else close(); return result; } void AlarmResource::doClose() { mCloseAfterSave = false; emit invalidate(this); KCal::ResourceCached::doClose(); mLoaded = mLoading = false; mCompatibilityMap.clear(); } QString AlarmResource::infoText() const { KRES::Factory* factory = KRES::Factory::self("alarms"); QString atype; switch (mType) { case CalEvent::ACTIVE: atype = i18nc("@info/plain", "Active alarms"); break; case CalEvent::ARCHIVED: atype = i18nc("@info/plain", "Archived alarms"); break; case CalEvent::TEMPLATE: atype = i18nc("@info/plain", "Alarm templates"); break; default: break; } QString perms = readOnly() ? i18nc("@info/plain", "Read-only") : i18nc("@info/plain", "Read-write"); QString enabled = isEnabled() ? i18nc("@info/plain", "Enabled") : mWrongAlarmType ? i18nc("@info/plain", "Disabled (wrong alarm type)") : i18nc("@info/plain", "Disabled"); QString std = (AlarmResources::instance()->getStandardResource(mType) == this) ? i18nc("@info/plain Parameter in 'Default calendar: Yes/No'", "Yes") : i18nc("@info/plain Parameter in 'Default calendar: Yes/No'", "No"); return i18nc("@info", "%1" "Calendar type: %2" "Contents: %3" "%4: %5" "Permissions: %6" "Status: %7" "Default calendar: %8", resourceName(), factory->typeName(type()), atype, displayType(), displayLocation(), perms, enabled, std); } void AlarmResource::lock(const QString& path) { delete mLock; if (path.isNull()) mLock = 0; else if (path.isEmpty()) mLock = new KABC::LockNull(true); else mLock = new KABC::Lock(path); } /****************************************************************************** * Check whether the alarm types in a calendar correspond with the resource's * alarm type. * Reply = true if at least 1 alarm is the right type. */ bool AlarmResource::checkAlarmTypes(KCal::CalendarLocal& calendar) const { if (mType != CalEvent::EMPTY) { bool have = false; bool other = false; const Event::List events = calendar.rawEvents(); for (int i = 0, iend = events.count(); i < iend; ++i) { CalEvent::Type s = CalEvent::status(events[i]); if (mType == s) have = true; else other = true; if (have && other) break; } if (!have && other) return false; // contains only wrong alarm types } return true; } /* void AlarmResource::kaCheckCalendar(CalendarLocal& cal) { mTypes = CalEvent::EMPTY; Event::List events = cal.rawEvents(); for (int i = 0, iend = events.count(); i < iend; ++i) { switch (CalEvent::status(events[i])) { case CalEvent::ACTIVE: mTypes = static_cast(mTypes | CalEvent::ACTIVE); break; case CalEvent::ARCHIVED: mTypes = static_cast(mTypes | CalEvent::ARCHIVED); break; case CalEvent::TEMPLATE: mTypes = static_cast(mTypes | CalEvent::TEMPLATE); break; default: break; } if (mTypes == CalEvent::ALL) break; } } */ #ifndef NDEBUG QByteArray AlarmResource::typeName() const { switch (mType) { case CalEvent::ACTIVE: return "Active"; case CalEvent::ARCHIVED: return "Archived"; case CalEvent::TEMPLATE: return "Template"; default: return "Empty"; } } #endif // vim: et sw=4: