1 | /* |
2 | This file is part of the kcal library. |
3 | |
4 | Copyright (c) 2002 Cornelius Schumacher <schumacher@kde.org> |
5 | Copyright (C) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com> |
6 | |
7 | This library is free software; you can redistribute it and/or |
8 | modify it under the terms of the GNU Library General Public |
9 | License as published by the Free Software Foundation; either |
10 | version 2 of the License, or (at your option) any later version. |
11 | |
12 | This library is distributed in the hope that it will be useful, |
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
15 | Library General Public License for more details. |
16 | |
17 | You should have received a copy of the GNU Library General Public License |
18 | along with this library; see the file COPYING.LIB. If not, write to |
19 | the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
20 | Boston, MA 02110-1301, USA. |
21 | */ |
22 | /** |
23 | @file |
24 | This file is part of the API for handling calendar data and defines |
25 | classes for managing compatibility between different calendar formats. |
26 | |
27 | @brief |
28 | Classes that provide compatibility to older or "broken" calendar formats. |
29 | |
30 | @author Cornelius Schumacher \<schumacher@kde.org\> |
31 | @author Reinhold Kainhofer \<reinhold@kainhofer.com\> |
32 | */ |
33 | |
34 | #include "compat.h" |
35 | #include "incidence.h" |
36 | |
37 | #include <kdatetime.h> |
38 | #include <kdebug.h> |
39 | |
40 | #include <QtCore/QRegExp> |
41 | #include <QtCore/QList> |
42 | |
43 | using namespace KCal; |
44 | |
45 | Compat *CompatFactory::createCompat( const QString &productId ) |
46 | { |
47 | Compat *compat = 0; |
48 | |
49 | int korg = productId.indexOf( "KOrganizer" ); |
50 | int outl9 = productId.indexOf( "Outlook 9.0" ); |
51 | |
52 | // TODO: Use the version of LibKCal to determine the compat class... |
53 | if ( korg >= 0 ) { |
54 | int versionStart = productId.indexOf( " " , korg ); |
55 | if ( versionStart >= 0 ) { |
56 | int versionStop = productId.indexOf( QRegExp( "[ /]" ), versionStart + 1 ); |
57 | if ( versionStop >= 0 ) { |
58 | QString version = productId.mid( versionStart + 1, |
59 | versionStop - versionStart - 1 ); |
60 | |
61 | int versionNum = version.section( '.', 0, 0 ).toInt() * 10000 + |
62 | version.section( '.', 1, 1 ).toInt() * 100 + |
63 | version.section( '.', 2, 2 ).toInt(); |
64 | int releaseStop = productId.indexOf( "/" , versionStop ); |
65 | QString release; |
66 | if ( releaseStop > versionStop ) { |
67 | release = productId.mid( versionStop+1, releaseStop-versionStop-1 ); |
68 | } |
69 | if ( versionNum < 30100 ) { |
70 | compat = new CompatPre31; |
71 | } else if ( versionNum < 30200 ) { |
72 | compat = new CompatPre32; |
73 | } else if ( versionNum == 30200 && release == "pre" ) { |
74 | kDebug() << "Generating compat for KOrganizer 3.2 pre" ; |
75 | compat = new Compat32PrereleaseVersions; |
76 | } else if ( versionNum < 30400 ) { |
77 | compat = new CompatPre34; |
78 | } else if ( versionNum < 30500 ) { |
79 | compat = new CompatPre35; |
80 | } |
81 | } |
82 | } |
83 | } else if ( outl9 >= 0 ) { |
84 | kDebug() << "Generating compat for Outlook < 2000 (Outlook 9.0)" ; |
85 | compat = new CompatOutlook9; |
86 | } |
87 | |
88 | if ( !compat ) { |
89 | compat = new Compat; |
90 | } |
91 | |
92 | return compat; |
93 | } |
94 | |
95 | void Compat::fixEmptySummary( Incidence *incidence ) |
96 | { |
97 | // some stupid vCal exporters ignore the standard and use Description |
98 | // instead of Summary for the default field. Correct for this: Copy the |
99 | // first line of the description to the summary (if summary is just one |
100 | // line, move it) |
101 | if ( incidence->summary().isEmpty() && !( incidence->description().isEmpty() ) ) { |
102 | QString oldDescription = incidence->description().trimmed(); |
103 | QString newSummary( oldDescription ); |
104 | newSummary.remove( QRegExp( "\n.*" ) ); |
105 | incidence->setSummary( newSummary ); |
106 | if ( oldDescription == newSummary ) { |
107 | incidence->setDescription( "" ); |
108 | } |
109 | } |
110 | } |
111 | |
112 | void Compat::fixFloatingEnd( QDate &date ) |
113 | { |
114 | Q_UNUSED( date ); |
115 | } |
116 | |
117 | void Compat::fixRecurrence( Incidence *incidence ) |
118 | { |
119 | Q_UNUSED( incidence ); |
120 | // Prevent use of compatibility mode during subsequent changes by the application |
121 | // incidence->recurrence()->setCompatVersion(); |
122 | } |
123 | |
124 | void CompatPre35::fixRecurrence( Incidence *incidence ) |
125 | { |
126 | Recurrence *recurrence = incidence->recurrence(); |
127 | if ( recurrence ) { |
128 | KDateTime start( incidence->dtStart() ); |
129 | // kde < 3.5 only had one rrule, so no need to loop over all RRULEs. |
130 | RecurrenceRule *r = recurrence->defaultRRule(); |
131 | if ( r && !r->dateMatchesRules( start ) ) { |
132 | recurrence->addExDateTime( start ); |
133 | } |
134 | } |
135 | |
136 | // Call base class method now that everything else is done |
137 | Compat::fixRecurrence( incidence ); |
138 | } |
139 | |
140 | int CompatPre34::fixPriority( int priority ) |
141 | { |
142 | if ( 0 < priority && priority < 6 ) { |
143 | // adjust 1->1, 2->3, 3->5, 4->7, 5->9 |
144 | return 2 * priority - 1; |
145 | } else { |
146 | return priority; |
147 | } |
148 | } |
149 | |
150 | void CompatPre32::fixRecurrence( Incidence *incidence ) |
151 | { |
152 | Recurrence *recurrence = incidence->recurrence(); |
153 | if ( recurrence->recurs() && recurrence->duration() > 0 ) { |
154 | recurrence->setDuration( recurrence->duration() + incidence->recurrence()->exDates().count() ); |
155 | } |
156 | // Call base class method now that everything else is done |
157 | CompatPre35::fixRecurrence( incidence ); |
158 | } |
159 | |
160 | void CompatPre31::fixFloatingEnd( QDate &endDate ) |
161 | { |
162 | endDate = endDate.addDays( 1 ); |
163 | } |
164 | |
165 | void CompatPre31::fixRecurrence( Incidence *incidence ) |
166 | { |
167 | CompatPre32::fixRecurrence( incidence ); |
168 | |
169 | Recurrence *recur = incidence->recurrence(); |
170 | RecurrenceRule *r = 0; |
171 | if ( recur ) { |
172 | r = recur->defaultRRule(); |
173 | } |
174 | if ( recur && r ) { |
175 | int duration = r->duration(); |
176 | if ( duration > 0 ) { |
177 | // Backwards compatibility for KDE < 3.1. |
178 | // rDuration was set to the number of time periods to recur, |
179 | // with week start always on a Monday. |
180 | // Convert this to the number of occurrences. |
181 | r->setDuration( -1 ); |
182 | QDate end( r->startDt().date() ); |
183 | bool doNothing = false; |
184 | // # of periods: |
185 | int tmp = ( duration - 1 ) * r->frequency(); |
186 | switch ( r->recurrenceType() ) { |
187 | case RecurrenceRule::rWeekly: |
188 | { |
189 | end = end.addDays( tmp * 7 + 7 - end.dayOfWeek() ); |
190 | break; |
191 | } |
192 | case RecurrenceRule::rMonthly: |
193 | { |
194 | int month = end.month() - 1 + tmp; |
195 | end.setYMD( end.year() + month / 12, month % 12 + 1, 31 ); |
196 | break; |
197 | } |
198 | case RecurrenceRule::rYearly: |
199 | { |
200 | end.setYMD( end.year() + tmp, 12, 31 ); |
201 | break; |
202 | } |
203 | default: |
204 | doNothing = true; |
205 | break; |
206 | } |
207 | if ( !doNothing ) { |
208 | duration = r->durationTo( |
209 | KDateTime( end, QTime( 0, 0, 0 ), incidence->dtStart().timeSpec() ) ); |
210 | r->setDuration( duration ); |
211 | } |
212 | } |
213 | |
214 | /* addYearlyNum */ |
215 | // Dates were stored as day numbers, with a fiddle to take account of |
216 | // leap years. Convert the day number to a month. |
217 | QList<int> days = r->byYearDays(); |
218 | if ( !days.isEmpty() ) { |
219 | QList<int> months = r->byMonths(); |
220 | for ( int i = 0; i < months.size(); ++i ) { |
221 | int newmonth = |
222 | QDate( r->startDt().date().year(), 1, 1 ).addDays( months.at( i ) - 1 ).month(); |
223 | if ( !months.contains( newmonth ) ) { |
224 | months.append( newmonth ); |
225 | } |
226 | } |
227 | |
228 | r->setByMonths( months ); |
229 | days.clear(); |
230 | r->setByYearDays( days ); |
231 | } |
232 | } |
233 | } |
234 | |
235 | void CompatOutlook9::fixAlarms( Incidence *incidence ) |
236 | { |
237 | if ( !incidence ) { |
238 | return; |
239 | } |
240 | Alarm::List alarms = incidence->alarms(); |
241 | Alarm::List::Iterator it; |
242 | for ( it = alarms.begin(); it != alarms.end(); ++it ) { |
243 | Alarm *al = *it; |
244 | if ( al && al->hasStartOffset() ) { |
245 | Duration offsetDuration = al->startOffset(); |
246 | int offs = offsetDuration.asSeconds(); |
247 | if ( offs > 0 ) { |
248 | offsetDuration = Duration( -offs ); |
249 | } |
250 | al->setStartOffset( offsetDuration ); |
251 | } |
252 | } |
253 | } |
254 | |