KAlarm Library
kacalendar.cpp
00001 /* 00002 * kacalendar.cpp - KAlarm kcal library calendar and event functions 00003 * This file is part of kalarmcal library, which provides access to KAlarm 00004 * calendar data. 00005 * Copyright © 2001-2012 by David Jarvie <djarvie@kde.org> 00006 * 00007 * This library is free software; you can redistribute it and/or modify 00008 * it under the terms of the GNU Library General Public License as published 00009 * by the Free Software Foundation; either version 2 of the License, or (at 00010 * your option) any later version. 00011 * 00012 * This library is distributed in the hope that it will be useful, but WITHOUT 00013 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 00014 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public 00015 * License for more details. 00016 * 00017 * You should have received a copy of the GNU Library General Public License 00018 * along with this library; see the file COPYING.LIB. If not, write to the 00019 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 00020 * MA 02110-1301, USA. 00021 */ 00022 00023 #include "kacalendar.h" 00024 00025 #include "kaevent.h" 00026 #include "version.h" 00027 00028 #ifndef KALARMCAL_USE_KRESOURCES 00029 #include "collectionattribute.h" 00030 00031 #include <kcalcore/event.h> 00032 #include <kcalcore/alarm.h> 00033 #include <kcalcore/memorycalendar.h> 00034 00035 #include <kmessagebox.h> 00036 #else 00037 #include <kcal/event.h> 00038 #include <kcal/alarm.h> 00039 #include <kcal/calendarlocal.h> 00040 #endif 00041 00042 #include <kglobal.h> 00043 #include <klocale.h> 00044 #include <kdebug.h> 00045 00046 #include <QMap> 00047 #include <QFile> 00048 #include <QFileInfo> 00049 #include <QTextStream> 00050 00051 #ifndef KALARMCAL_USE_KRESOURCES 00052 using namespace KCalCore; 00053 using Akonadi::Collection; 00054 #else 00055 using namespace KCal; 00056 #endif 00057 00058 00059 static const KCatalogLoader loader("libkalarmcal"); 00060 00061 namespace KAlarmCal 00062 { 00063 00064 #ifndef KALARMCAL_USE_KRESOURCES 00065 const QLatin1String MIME_BASE("application/x-vnd.kde.alarm"); 00066 const QLatin1String MIME_ACTIVE("application/x-vnd.kde.alarm.active"); 00067 const QLatin1String MIME_ARCHIVED("application/x-vnd.kde.alarm.archived"); 00068 const QLatin1String MIME_TEMPLATE("application/x-vnd.kde.alarm.template"); 00069 #endif 00070 00071 static const QByteArray VERSION_PROPERTY("VERSION"); // X-KDE-KALARM-VERSION VCALENDAR property 00072 00073 static bool isUTC(const QString& localFile); 00074 00075 00076 class Private 00077 { 00078 public: 00079 #ifndef KALARMCAL_USE_KRESOURCES 00080 static int readKAlarmVersion(const FileStorage::Ptr&, QString& subVersion, QString& versionString); 00081 #else 00082 static int readKAlarmVersion(CalendarLocal&, const QString& localFile, QString& subVersion, QString& versionString); 00083 #endif 00084 00085 static QByteArray mIcalProductId; 00086 }; 00087 00088 QByteArray Private::mIcalProductId; 00089 00090 00091 namespace KACalendar 00092 { 00093 00094 const QByteArray APPNAME("KALARM"); 00095 00096 void setProductId(const QByteArray& progName, const QByteArray& progVersion) 00097 { 00098 Private::mIcalProductId = QByteArray("-//K Desktop Environment//NONSGML " + progName + " " + progVersion + "//EN"); 00099 } 00100 00101 QByteArray icalProductId() 00102 { 00103 return Private::mIcalProductId.isEmpty() ? QByteArray("-//K Desktop Environment//NONSGML //EN") : Private::mIcalProductId; 00104 } 00105 00106 /****************************************************************************** 00107 * Set the X-KDE-KALARM-VERSION property in a calendar. 00108 */ 00109 #ifndef KALARMCAL_USE_KRESOURCES 00110 void setKAlarmVersion(const Calendar::Ptr& calendar) 00111 { 00112 calendar->setCustomProperty(APPNAME, VERSION_PROPERTY, QString::fromLatin1(KAEvent::currentCalendarVersionString())); 00113 } 00114 #else 00115 void setKAlarmVersion(CalendarLocal& calendar) 00116 { 00117 calendar.setCustomProperty(APPNAME, VERSION_PROPERTY, QString::fromLatin1(KAEvent::currentCalendarVersionString())); 00118 } 00119 #endif 00120 00121 /****************************************************************************** 00122 * Check the version of KAlarm which wrote a calendar file, and convert it in 00123 * memory to the current KAlarm format if possible. The storage file is not 00124 * updated. The compatibility of the calendar format is indicated by the return 00125 * value. 00126 */ 00127 #ifndef KALARMCAL_USE_KRESOURCES 00128 int updateVersion(const FileStorage::Ptr& fileStorage, QString& versionString) 00129 #else 00130 int updateVersion(CalendarLocal& calendar, const QString& localFile, QString& versionString) 00131 #endif 00132 { 00133 QString subVersion; 00134 #ifndef KALARMCAL_USE_KRESOURCES 00135 int version = Private::readKAlarmVersion(fileStorage, subVersion, versionString); 00136 #else 00137 int version = Private::readKAlarmVersion(calendar, localFile, subVersion, versionString); 00138 #endif 00139 if (version == CurrentFormat) 00140 return CurrentFormat; // calendar is in the current KAlarm format 00141 if (version == IncompatibleFormat || version > KAEvent::currentCalendarVersion()) 00142 return IncompatibleFormat; // calendar was created by another program, or an unknown version of KAlarm 00143 00144 // Calendar was created by an earlier version of KAlarm. 00145 // Convert it to the current format. 00146 #ifndef KALARMCAL_USE_KRESOURCES 00147 const QString localFile = fileStorage->fileName(); 00148 #endif 00149 int ver = version; 00150 if (version == KAlarmCal::Version(0,5,7) && !localFile.isEmpty()) 00151 { 00152 // KAlarm version 0.5.7 - check whether times are stored in UTC, in which 00153 // case it is the KDE 3.0.0 version, which needs adjustment of summer times. 00154 if (isUTC(localFile)) 00155 ver = -version; 00156 kDebug() << "KAlarm version 0.5.7 (" << (ver < 0 ? "" : "non-") << "UTC)"; 00157 } 00158 else 00159 kDebug() << "KAlarm version" << version; 00160 00161 // Convert events to current KAlarm format for when/if the calendar is saved 00162 #ifndef KALARMCAL_USE_KRESOURCES 00163 KAEvent::convertKCalEvents(fileStorage->calendar(), ver); 00164 #else 00165 KAEvent::convertKCalEvents(calendar, ver); 00166 #endif 00167 return version; 00168 } 00169 00170 } // namespace KACalendar 00171 00172 /****************************************************************************** 00173 * Return the KAlarm version which wrote the calendar which has been loaded. 00174 * The format is, for example, 000507 for 0.5.7. 00175 * Reply = CurrentFormat if the calendar was created by the current version of KAlarm 00176 * = IncompatibleFormat if it was created by KAlarm pre-0.3.5, or another program 00177 * = version number if created by another KAlarm version. 00178 */ 00179 #ifndef KALARMCAL_USE_KRESOURCES 00180 int Private::readKAlarmVersion(const FileStorage::Ptr& fileStorage, QString& subVersion, QString& versionString) 00181 #else 00182 int Private::readKAlarmVersion(CalendarLocal& calendar, const QString& localFile, QString& subVersion, QString& versionString) 00183 #endif 00184 { 00185 subVersion.clear(); 00186 #ifndef KALARMCAL_USE_KRESOURCES 00187 Calendar::Ptr calendar = fileStorage->calendar(); 00188 versionString = calendar->customProperty(KACalendar::APPNAME, VERSION_PROPERTY); 00189 kDebug() << "File=" << fileStorage->fileName() << ", version=" << versionString; 00190 00191 #else 00192 versionString = calendar.customProperty(KACalendar::APPNAME, VERSION_PROPERTY); 00193 #endif 00194 00195 if (versionString.isEmpty()) 00196 { 00197 // Pre-KAlarm 1.4 defined the KAlarm version number in the PRODID field. 00198 // If another application has written to the file, this may not be present. 00199 #ifndef KALARMCAL_USE_KRESOURCES 00200 const QString prodid = calendar->productId(); 00201 #else 00202 const QString prodid = calendar.productId(); 00203 #endif 00204 if (prodid.isEmpty()) 00205 { 00206 // Check whether the calendar file is empty, in which case 00207 // it can be written to freely. 00208 #ifndef KALARMCAL_USE_KRESOURCES 00209 QFileInfo fi(fileStorage->fileName()); 00210 #else 00211 QFileInfo fi(localFile); 00212 #endif 00213 if (!fi.size()) 00214 return KACalendar::CurrentFormat; 00215 } 00216 00217 // Find the KAlarm identifier 00218 QString progname = QLatin1String(" KAlarm "); 00219 int i = prodid.indexOf(progname, 0, Qt::CaseInsensitive); 00220 if (i < 0) 00221 { 00222 // Older versions used KAlarm's translated name in the product ID, which 00223 // could have created problems using a calendar in different locales. 00224 progname = QString(" ") + i18n("KAlarm") + ' '; 00225 i = prodid.indexOf(progname, 0, Qt::CaseInsensitive); 00226 if (i < 0) 00227 return KACalendar::IncompatibleFormat; // calendar wasn't created by KAlarm 00228 } 00229 00230 // Extract the KAlarm version string 00231 versionString = prodid.mid(i + progname.length()).trimmed(); 00232 i = versionString.indexOf('/'); 00233 int j = versionString.indexOf(' '); 00234 if (j >= 0 && j < i) 00235 i = j; 00236 if (i <= 0) 00237 return KACalendar::IncompatibleFormat; // missing version string 00238 versionString = versionString.left(i); // 'versionString' now contains the KAlarm version string 00239 } 00240 if (versionString == KAEvent::currentCalendarVersionString()) 00241 return KACalendar::CurrentFormat; // the calendar is in the current KAlarm format 00242 int ver = KAlarmCal::getVersionNumber(versionString, &subVersion); 00243 if (ver == KAEvent::currentCalendarVersion()) 00244 return KACalendar::CurrentFormat; // the calendar is in the current KAlarm format 00245 return KAlarmCal::getVersionNumber(versionString, &subVersion); 00246 } 00247 00248 /****************************************************************************** 00249 * Check whether the calendar file has its times stored as UTC times, 00250 * indicating that it was written by the KDE 3.0.0 version of KAlarm 0.5.7. 00251 * Reply = true if times are stored in UTC 00252 * = false if the calendar is a vCalendar, times are not UTC, or any error occurred. 00253 */ 00254 bool isUTC(const QString& localFile) 00255 { 00256 // Read the calendar file into a string 00257 QFile file(localFile); 00258 if (!file.open(QIODevice::ReadOnly)) 00259 return false; 00260 QTextStream ts(&file); 00261 ts.setCodec("ISO 8859-1"); 00262 QByteArray text = ts.readAll().toLocal8Bit(); 00263 file.close(); 00264 00265 // Extract the CREATED property for the first VEVENT from the calendar 00266 const QByteArray BEGIN_VCALENDAR("BEGIN:VCALENDAR"); 00267 const QByteArray BEGIN_VEVENT("BEGIN:VEVENT"); 00268 const QByteArray CREATED("CREATED:"); 00269 QList<QByteArray> lines = text.split('\n'); 00270 for (int i = 0, end = lines.count(); i < end; ++i) 00271 { 00272 if (lines[i].startsWith(BEGIN_VCALENDAR)) 00273 { 00274 while (++i < end) 00275 { 00276 if (lines[i].startsWith(BEGIN_VEVENT)) 00277 { 00278 while (++i < end) 00279 { 00280 if (lines[i].startsWith(CREATED)) 00281 return lines[i].endsWith('Z'); 00282 } 00283 } 00284 } 00285 break; 00286 } 00287 } 00288 return false; 00289 } 00290 00291 00292 namespace CalEvent 00293 { 00294 00295 // Struct to contain static strings, to allow use of K_GLOBAL_STATIC 00296 // to delete them on program termination. 00297 struct StaticStrings 00298 { 00299 StaticStrings() 00300 : STATUS_PROPERTY("TYPE"), 00301 ACTIVE_STATUS(QLatin1String("ACTIVE")), 00302 TEMPLATE_STATUS(QLatin1String("TEMPLATE")), 00303 ARCHIVED_STATUS(QLatin1String("ARCHIVED")), 00304 DISPLAYING_STATUS(QLatin1String("DISPLAYING")), 00305 ARCHIVED_UID(QLatin1String("-exp-")), 00306 DISPLAYING_UID(QLatin1String("-disp-")), 00307 TEMPLATE_UID(QLatin1String("-tmpl-")) 00308 {} 00309 // Event custom properties. 00310 // Note that all custom property names are prefixed with X-KDE-KALARM- in the calendar file. 00311 const QByteArray STATUS_PROPERTY; // X-KDE-KALARM-TYPE property 00312 const QString ACTIVE_STATUS; 00313 const QString TEMPLATE_STATUS; 00314 const QString ARCHIVED_STATUS; 00315 const QString DISPLAYING_STATUS; 00316 00317 // Event ID identifiers 00318 const QString ARCHIVED_UID; 00319 const QString DISPLAYING_UID; 00320 00321 // Old KAlarm format identifiers 00322 const QString TEMPLATE_UID; 00323 }; 00324 K_GLOBAL_STATIC(StaticStrings, staticStrings) 00325 00326 /****************************************************************************** 00327 * Convert a unique ID to indicate that the event is in a specified calendar file. 00328 */ 00329 QString uid(const QString& id, Type status) 00330 { 00331 QString result = id; 00332 Type oldType; 00333 int i, len; 00334 if ((i = result.indexOf(staticStrings->ARCHIVED_UID)) > 0) 00335 { 00336 oldType = ARCHIVED; 00337 len = staticStrings->ARCHIVED_UID.length(); 00338 } 00339 else if ((i = result.indexOf(staticStrings->DISPLAYING_UID)) > 0) 00340 { 00341 oldType = DISPLAYING; 00342 len = staticStrings->DISPLAYING_UID.length(); 00343 } 00344 else 00345 { 00346 oldType = ACTIVE; 00347 i = result.lastIndexOf('-'); 00348 len = 1; 00349 if (i < 0) 00350 { 00351 i = result.length(); 00352 len = 0; 00353 } 00354 else 00355 len = 1; 00356 } 00357 if (status != oldType && i > 0) 00358 { 00359 QString part; 00360 switch (status) 00361 { 00362 case ARCHIVED: part = staticStrings->ARCHIVED_UID; break; 00363 case DISPLAYING: part = staticStrings->DISPLAYING_UID; break; 00364 case ACTIVE: 00365 case TEMPLATE: 00366 case EMPTY: 00367 default: part = QLatin1String("-"); break; 00368 } 00369 result.replace(i, len, part); 00370 } 00371 return result; 00372 } 00373 00374 /****************************************************************************** 00375 * Check an event to determine its type - active, archived, template or empty. 00376 * The default type is active if it contains alarms and there is nothing to 00377 * indicate otherwise. 00378 * Note that the mere fact that all an event's alarms have passed does not make 00379 * an event archived, since it may be that they have not yet been able to be 00380 * triggered. They will be archived once KAlarm tries to handle them. 00381 * Do not call this function for the displaying alarm calendar. 00382 */ 00383 #ifndef KALARMCAL_USE_KRESOURCES 00384 Type status(const Event::Ptr& event, QString* param) 00385 #else 00386 Type status(const Event* event, QString* param) 00387 #endif 00388 { 00389 // Set up a static quick lookup for type strings 00390 typedef QMap<QString, Type> PropertyMap; 00391 static PropertyMap properties; 00392 if (properties.isEmpty()) 00393 { 00394 properties[staticStrings->ACTIVE_STATUS] = ACTIVE; 00395 properties[staticStrings->TEMPLATE_STATUS] = TEMPLATE; 00396 properties[staticStrings->ARCHIVED_STATUS] = ARCHIVED; 00397 properties[staticStrings->DISPLAYING_STATUS] = DISPLAYING; 00398 } 00399 00400 if (param) 00401 param->clear(); 00402 if (!event) 00403 return EMPTY; 00404 Alarm::List alarms = event->alarms(); 00405 if (alarms.isEmpty()) 00406 return EMPTY; 00407 00408 const QString property = event->customProperty(KACalendar::APPNAME, staticStrings->STATUS_PROPERTY); 00409 if (!property.isEmpty()) 00410 { 00411 // There's a X-KDE-KALARM-TYPE property. 00412 // It consists of the event type, plus an optional parameter. 00413 PropertyMap::ConstIterator it = properties.constFind(property); 00414 if (it != properties.constEnd()) 00415 return it.value(); 00416 int i = property.indexOf(';'); 00417 if (i < 0) 00418 return EMPTY; 00419 it = properties.constFind(property.left(i)); 00420 if (it == properties.constEnd()) 00421 return EMPTY; 00422 if (param) 00423 *param = property.mid(i + 1); 00424 return it.value(); 00425 } 00426 00427 // The event either wasn't written by KAlarm, or was written by a pre-2.0 version. 00428 // Check first for an old KAlarm format, which indicated the event type in its UID. 00429 QString uid = event->uid(); 00430 if (uid.indexOf(staticStrings->ARCHIVED_UID) > 0) 00431 return ARCHIVED; 00432 if (uid.indexOf(staticStrings->TEMPLATE_UID) > 0) 00433 return TEMPLATE; 00434 00435 // Otherwise, assume it's an active alarm 00436 return ACTIVE; 00437 } 00438 00439 /****************************************************************************** 00440 * Set the event's type - active, archived, template, etc. 00441 * If a parameter is supplied, it will be appended as a second parameter to the 00442 * custom property. 00443 */ 00444 #ifndef KALARMCAL_USE_KRESOURCES 00445 void setStatus(const Event::Ptr& event, Type status, const QString& param) 00446 #else 00447 void setStatus(Event* event, Type status, const QString& param) 00448 #endif 00449 { 00450 if (!event) 00451 return; 00452 QString text; 00453 switch (status) 00454 { 00455 case ACTIVE: text = staticStrings->ACTIVE_STATUS; break; 00456 case TEMPLATE: text = staticStrings->TEMPLATE_STATUS; break; 00457 case ARCHIVED: text = staticStrings->ARCHIVED_STATUS; break; 00458 case DISPLAYING: text = staticStrings->DISPLAYING_STATUS; break; 00459 default: 00460 event->removeCustomProperty(KACalendar::APPNAME, staticStrings->STATUS_PROPERTY); 00461 return; 00462 } 00463 if (!param.isEmpty()) 00464 text += ';' + param; 00465 event->setCustomProperty(KACalendar::APPNAME, staticStrings->STATUS_PROPERTY, text); 00466 } 00467 00468 #ifndef KALARMCAL_USE_KRESOURCES 00469 Type type(const QString& mimeType) 00470 { 00471 if (mimeType == MIME_ACTIVE) 00472 return ACTIVE; 00473 if (mimeType == MIME_ARCHIVED) 00474 return ARCHIVED; 00475 if (mimeType == MIME_TEMPLATE) 00476 return TEMPLATE; 00477 return EMPTY; 00478 } 00479 00480 Types types(const QStringList& mimeTypes) 00481 { 00482 Types types = 0; 00483 foreach (const QString& type, mimeTypes) 00484 { 00485 if (type == MIME_ACTIVE) 00486 types |= ACTIVE; 00487 if (type == MIME_ARCHIVED) 00488 types |= ARCHIVED; 00489 if (type == MIME_TEMPLATE) 00490 types |= TEMPLATE; 00491 } 00492 return types; 00493 } 00494 00495 QString mimeType(Type type) 00496 { 00497 switch (type) 00498 { 00499 case ACTIVE: return MIME_ACTIVE; 00500 case ARCHIVED: return MIME_ARCHIVED; 00501 case TEMPLATE: return MIME_TEMPLATE; 00502 default: return QString(); 00503 } 00504 } 00505 00506 QStringList mimeTypes(Types types) 00507 { 00508 QStringList mimes; 00509 for (int i = 1; types; i <<= 1) 00510 { 00511 if (types & i) 00512 { 00513 mimes += mimeType(Type(i)); 00514 types &= ~i; 00515 } 00516 } 00517 return mimes; 00518 } 00519 #endif 00520 00521 } // namespace CalEvent 00522 00523 } // namespace KAlarmCal 00524 00525 // vim: et sw=4:
This file is part of the KDE documentation.
Documentation copyright © 1996-2012 The KDE developers.
Generated on Mon May 14 2012 05:13:34 by doxygen 1.7.5 written by Dimitri van Heesch, © 1997-2006
Documentation copyright © 1996-2012 The KDE developers.
Generated on Mon May 14 2012 05:13:34 by doxygen 1.7.5 written by Dimitri van Heesch, © 1997-2006
KDE's Doxygen guidelines are available online.