KCalendarCore

compat.cpp
Go to the documentation of this file.
1/*
2 This file is part of the kcalcore library.
3
4 SPDX-FileCopyrightText: 2002 Cornelius Schumacher <schumacher@kde.org>
5 SPDX-FileCopyrightText: 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com>
6 SPDX-FileCopyrightText: 2012 Christian Mollekopf <mollekopf@kolabsys.com>
7
8 SPDX-License-Identifier: LGPL-2.0-or-later
9*/
10/**
11 @file
12 This file is part of the API for handling calendar data and defines
13 classes for managing compatibility between different calendar formats.
14
15 @brief
16 Classes that provide compatibility to older or "broken" calendar formats.
17
18 @author Cornelius Schumacher <schumacher@kde.org>
19 @author Reinhold Kainhofer <reinhold@kainhofer.com>
20*/
21
22#include "compat_p.h"
23#include "incidence.h"
24
25#include "kcalendarcore_debug.h"
26
27#include <QDate>
28#include <QRegularExpression>
29#include <QString>
30
31using namespace KCalendarCore;
32
33Compat *CompatFactory::createCompat(const QString &productId, const QString &implementationVersion)
34{
35 Compat *compat = nullptr;
36
37 int korg = productId.indexOf(QLatin1String("KOrganizer"));
38 int outl9 = productId.indexOf(QLatin1String("Outlook 9.0"));
39
40 if (korg >= 0) {
41 int versionStart = productId.indexOf(QLatin1Char(' '), korg);
42 if (versionStart >= 0) {
43 int versionStop = productId.indexOf(QRegularExpression(QStringLiteral("[ /]")), versionStart + 1);
44 if (versionStop >= 0) {
45 QString version = productId.mid(versionStart + 1, versionStop - versionStart - 1);
46
47 int versionNum = version.section(QLatin1Char('.'), 0, 0).toInt() * 10000 + version.section(QLatin1Char('.'), 1, 1).toInt() * 100
48 + version.section(QLatin1Char('.'), 2, 2).toInt();
49 int releaseStop = productId.indexOf(QLatin1Char('/'), versionStop);
50 QString release;
51 if (releaseStop > versionStop) {
52 release = productId.mid(versionStop + 1, releaseStop - versionStop - 1);
53 }
54 if (versionNum < 30100) {
55 compat = new CompatPre31;
56 } else if (versionNum < 30200) {
57 compat = new CompatPre32;
58 } else if (versionNum == 30200 && release == QLatin1String("pre")) {
59 qCDebug(KCALCORE_LOG) << "Generating compat for KOrganizer 3.2 pre";
60 compat = new Compat32PrereleaseVersions;
61 } else if (versionNum < 30400) {
62 compat = new CompatPre34;
63 } else if (versionNum < 30500) {
64 compat = new CompatPre35;
65 }
66 }
67 }
68 } else if (outl9 >= 0) {
69 qCDebug(KCALCORE_LOG) << "Generating compat for Outlook < 2000 (Outlook 9.0)";
70 compat = new CompatOutlook9;
71 }
72 if (!compat) {
73 compat = new Compat;
74 }
75 // Older implementations lacked the implementation version,
76 // so apply this fix if it is a file from kontact and the version is missing.
77 if (implementationVersion.isEmpty()
78 && (productId.contains(QLatin1String("libkcal")) || productId.contains(QLatin1String("KOrganizer")) || productId.contains(QLatin1String("KAlarm")))) {
79 compat = new CompatPre410(compat);
80 }
81
82 return compat;
83}
84
85Compat::~Compat() = default;
86
87void Compat::fixEmptySummary(const Incidence::Ptr &incidence)
88{
89 // some stupid vCal exporters ignore the standard and use Description
90 // instead of Summary for the default field. Correct for this: Copy the
91 // first line of the description to the summary (if summary is just one
92 // line, move it)
93 if (incidence->summary().isEmpty() && !(incidence->description().isEmpty())) {
94 QString oldDescription = incidence->description().trimmed();
95 QString newSummary(oldDescription);
96 newSummary.remove(QRegularExpression(QStringLiteral("\n.*")));
97 incidence->setSummary(newSummary);
98 if (oldDescription == newSummary) {
99 incidence->setDescription(QLatin1String(""));
100 }
101 }
102}
103
104void Compat::fixAlarms(const Incidence::Ptr &incidence)
105{
106 Q_UNUSED(incidence);
107}
108
109void Compat::fixFloatingEnd(QDate &date)
110{
111 Q_UNUSED(date);
112}
113
114void Compat::fixRecurrence(const Incidence::Ptr &incidence)
115{
116 Q_UNUSED(incidence);
117 // Prevent use of compatibility mode during subsequent changes by the application
118 // incidence->recurrence()->setCompatVersion();
119}
120
121int Compat::fixPriority(int priority)
122{
123 return priority;
124}
125
126bool Compat::useTimeZoneShift() const
127{
128 return true;
129}
130
131void Compat::setCreatedToDtStamp(const Incidence::Ptr &incidence, const QDateTime &dtstamp)
132{
133 Q_UNUSED(incidence);
134 Q_UNUSED(dtstamp);
135}
136
137CompatDecorator::CompatDecorator(Compat *compat)
138 : m_compat(compat)
139{
140}
141
142CompatDecorator::~CompatDecorator() = default;
143
144void CompatDecorator::fixEmptySummary(const Incidence::Ptr &incidence)
145{
146 m_compat->fixEmptySummary(incidence);
147}
148
149void CompatDecorator::fixAlarms(const Incidence::Ptr &incidence)
150{
151 m_compat->fixAlarms(incidence);
152}
153
154void CompatDecorator::fixFloatingEnd(QDate &date)
155{
156 m_compat->fixFloatingEnd(date);
157}
158
159void CompatDecorator::fixRecurrence(const Incidence::Ptr &incidence)
160{
161 m_compat->fixRecurrence(incidence);
162}
163
164int CompatDecorator::fixPriority(int priority)
165{
166 return m_compat->fixPriority(priority);
167}
168
169bool CompatDecorator::useTimeZoneShift() const
170{
171 return m_compat->useTimeZoneShift();
172}
173
174void CompatDecorator::setCreatedToDtStamp(const Incidence::Ptr &incidence, const QDateTime &dtstamp)
175{
176 m_compat->setCreatedToDtStamp(incidence, dtstamp);
177}
178
179void CompatPre35::fixRecurrence(const Incidence::Ptr &incidence)
180{
181 Recurrence *recurrence = incidence->recurrence();
182 if (recurrence) {
183 QDateTime start(incidence->dtStart());
184 // kde < 3.5 only had one rrule, so no need to loop over all RRULEs.
185 RecurrenceRule *r = recurrence->defaultRRule();
186 if (r && !r->dateMatchesRules(start)) {
187 recurrence->addExDateTime(start);
188 }
189 }
190
191 // Call base class method now that everything else is done
192 Compat::fixRecurrence(incidence);
193}
194
195int CompatPre34::fixPriority(int priority)
196{
197 if (0 < priority && priority < 6) {
198 // adjust 1->1, 2->3, 3->5, 4->7, 5->9
199 return 2 * priority - 1;
200 } else {
201 return priority;
202 }
203}
204
205void CompatPre32::fixRecurrence(const Incidence::Ptr &incidence)
206{
207 Recurrence *recurrence = incidence->recurrence();
208 if (recurrence->recurs() && recurrence->duration() > 0) {
209 recurrence->setDuration(recurrence->duration() + incidence->recurrence()->exDates().count());
210 }
211 // Call base class method now that everything else is done
212 CompatPre35::fixRecurrence(incidence);
213}
214
215void CompatPre31::fixFloatingEnd(QDate &endDate)
216{
217 endDate = endDate.addDays(1);
218}
219
220void CompatPre31::fixRecurrence(const Incidence::Ptr &incidence)
221{
222 CompatPre32::fixRecurrence(incidence);
223
224 Recurrence *recur = incidence->recurrence();
225 RecurrenceRule *r = nullptr;
226 if (recur) {
227 r = recur->defaultRRule();
228 }
229 if (recur && r) {
230 int duration = r->duration();
231 if (duration > 0) {
232 // Backwards compatibility for KDE < 3.1.
233 // rDuration was set to the number of time periods to recur,
234 // with week start always on a Monday.
235 // Convert this to the number of occurrences.
236 r->setDuration(-1);
237 QDate end(r->startDt().date());
238 bool doNothing = false;
239 // # of periods:
240 int tmp = (duration - 1) * r->frequency();
241 switch (r->recurrenceType()) {
242 case RecurrenceRule::rWeekly: {
243 end = end.addDays(tmp * 7 + 7 - end.dayOfWeek());
244 break;
245 }
246 case RecurrenceRule::rMonthly: {
247 int month = end.month() - 1 + tmp;
248 end.setDate(end.year() + month / 12, month % 12 + 1, 31);
249 break;
250 }
251 case RecurrenceRule::rYearly: {
252 end.setDate(end.year() + tmp, 12, 31);
253 break;
254 }
255 default:
256 doNothing = true;
257 break;
258 }
259 if (!doNothing) {
260 duration = r->durationTo(QDateTime(end, QTime(0, 0, 0), incidence->dtStart().timeZone()));
261 r->setDuration(duration);
262 }
263 }
264
265 /* addYearlyNum */
266 // Dates were stored as day numbers, with a fiddle to take account of
267 // leap years. Convert the day number to a month.
268 QList<int> days = r->byYearDays();
269 if (!days.isEmpty()) {
270 QList<int> months = r->byMonths();
271 for (int i = 0; i < months.size(); ++i) {
272 int newmonth = QDate(r->startDt().date().year(), 1, 1).addDays(months.at(i) - 1).month();
273 if (!months.contains(newmonth)) {
274 months.append(newmonth);
275 }
276 }
277
278 r->setByMonths(months);
279 days.clear();
280 r->setByYearDays(days);
281 }
282 }
283}
284
285void CompatOutlook9::fixAlarms(const Incidence::Ptr &incidence)
286{
287 if (!incidence) {
288 return;
289 }
290
291 const Alarm::List alarms = incidence->alarms();
292 for (const Alarm::Ptr &al : alarms) {
293 if (al && al->hasStartOffset()) {
294 Duration offsetDuration = al->startOffset();
295 int offs = offsetDuration.asSeconds();
296 if (offs > 0) {
297 offsetDuration = Duration(-offs);
298 }
299 al->setStartOffset(offsetDuration);
300 }
301 }
302}
303
304bool Compat32PrereleaseVersions::useTimeZoneShift() const
305{
306 return false;
307}
308
309CompatPre410::CompatPre410(Compat *decoratedCompat)
310 : CompatDecorator(decoratedCompat)
311{
312}
313
314void CompatPre410::setCreatedToDtStamp(const Incidence::Ptr &incidence, const QDateTime &dtstamp)
315{
316 if (dtstamp.isValid()) {
317 incidence->setCreated(dtstamp);
318 }
319}
Represents a span of time measured in seconds or days.
Definition duration.h:44
int asSeconds() const
Returns the length of the duration in seconds.
Definition duration.cpp:186
This class represents a recurrence rule for a calendar incidence.
QDateTime startDt() const
Returns the recurrence start date/time.
void setDuration(int duration)
Sets the total number of times the event is to occur, including both the first and last.
uint frequency() const
Returns the recurrence frequency, in terms of the recurrence time period type.
int duration() const
Returns -1 if the event recurs infinitely, 0 if the end date is set, otherwise the total number of re...
bool dateMatchesRules(const QDateTime &dt) const
Returns true if the date matches the rules.
int durationTo(const QDateTime &dt) const
Returns the number of recurrences up to and including the date/time specified.
This class represents a recurrence rule for a calendar incidence.
Definition recurrence.h:77
bool recurs() const
Returns whether the event recurs at all.
int duration() const
Returns -1 if the event recurs infinitely, 0 if the end date is set, otherwise the total number of re...
void setDuration(int duration)
Sets the total number of times the event is to occur, including both the first and last.
Q_SCRIPTABLE Q_NOREPLY void start()
This file is part of the API for handling calendar data and defines the Incidence class.
AKONADI_CALENDAR_EXPORT KCalendarCore::Incidence::Ptr incidence(const Akonadi::Item &item)
Namespace for all KCalendarCore types.
Definition alarm.h:37
KDB_EXPORT KDbVersionInfo version()
const QList< QKeySequence > & end()
QDate addDays(qint64 ndays) const const
int month() const const
int year() const const
QDate date() const const
bool isValid() const const
void append(QList< T > &&value)
const_reference at(qsizetype i) const const
void clear()
bool contains(const AT &value) const const
bool isEmpty() const const
qsizetype size() const const
bool contains(QChar ch, Qt::CaseSensitivity cs) const const
qsizetype indexOf(QChar ch, qsizetype from, Qt::CaseSensitivity cs) const const
bool isEmpty() const const
QString mid(qsizetype position, qsizetype n) const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:13:47 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.