1 | /* |
2 | This file is part of the kcal library. |
3 | |
4 | Copyright (c) 2003 Cornelius Schumacher <schumacher@kde.org> |
5 | |
6 | This library is free software; you can redistribute it and/or |
7 | modify it under the terms of the GNU Library General Public |
8 | License as published by the Free Software Foundation; either |
9 | version 2 of the License, or (at your option) any later version. |
10 | |
11 | This library is distributed in the hope that it will be useful, |
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
14 | Library General Public License for more details. |
15 | |
16 | You should have received a copy of the GNU Library General Public License |
17 | along with this library; see the file COPYING.LIB. If not, write to |
18 | the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
19 | Boston, MA 02110-1301, USA. |
20 | */ |
21 | |
22 | #include "qtopiaformat.h" |
23 | |
24 | #include "calendar.h" |
25 | #include "calendarlocal.h" |
26 | |
27 | #include <QtCore/QDateTime> |
28 | #include <QtCore/QString> |
29 | #include <QtCore/QRegExp> |
30 | #include <QClipboard> |
31 | #include <QtCore/QFile> |
32 | #include <QtCore/QTextStream> |
33 | |
34 | #include <QTextDocument> |
35 | |
36 | #include <QtXml/QXmlAttributes> |
37 | #include <QtXml/QXmlDefaultHandler> |
38 | #include <QtXml/QXmlParseException> |
39 | #include <QtXml/QXmlInputSource> |
40 | #include <QtXml/QXmlSimpleReader> |
41 | |
42 | #include <kdebug.h> |
43 | #include <klocalizedstring.h> |
44 | #include <kdatetime.h> |
45 | |
46 | using namespace KCal; |
47 | |
48 | //@cond PRIVATE |
49 | class QtopiaParser : public QXmlDefaultHandler |
50 | { |
51 | public: |
52 | QtopiaParser( Calendar *calendar ) : mCalendar( calendar ) {} |
53 | |
54 | bool startElement( const QString &, const QString &, const QString &qName, |
55 | const QXmlAttributes &attributes ) |
56 | { |
57 | if ( qName == "event" ) { |
58 | Event *event = new Event; |
59 | QString uid = "Qtopia" + attributes.value( "uid" ); |
60 | event->setUid( uid ); |
61 | |
62 | event->setSummary( attributes.value( "description" ), |
63 | Qt::mightBeRichText( attributes.value( "description" ) ) ); |
64 | event->setLocation( attributes.value( "location" ), |
65 | Qt::mightBeRichText( attributes.value( "location" ) ) ); |
66 | event->setDescription( attributes.value( "note" ), |
67 | Qt::mightBeRichText( attributes.value( "note" ) ) ); |
68 | event->setDtStart( toDateTime( attributes.value( "start" ) ) ); |
69 | event->setDtEnd( toDateTime( attributes.value( "end" ) ) ); |
70 | |
71 | if ( attributes.value( "type" ) == "AllDay" ) { |
72 | event->setAllDay( true ); |
73 | } else { |
74 | event->setAllDay( false ); |
75 | } |
76 | |
77 | QString rtype = attributes.value( "rtype" ); |
78 | if ( !rtype.isEmpty() ) { |
79 | QDate startDate = event->dtStart().date(); |
80 | |
81 | QString freqStr = attributes.value( "rfreq" ); |
82 | int freq = freqStr.toInt(); |
83 | |
84 | QString hasEndDateStr = attributes.value( "rhasenddate" ); |
85 | bool hasEndDate = hasEndDateStr == "1" ; |
86 | |
87 | QString endDateStr = attributes.value( "enddt" ); |
88 | QDate endDate = toDateTime( endDateStr ).date(); |
89 | |
90 | QString weekDaysStr = attributes.value( "rweekdays" ); |
91 | int weekDaysNum = weekDaysStr.toInt(); |
92 | QBitArray weekDays( 7 ); |
93 | int i; |
94 | for ( i = 1; i <= 7; ++i ) { |
95 | weekDays.setBit( i - 1, ( 2 << i ) & weekDaysNum ); |
96 | } |
97 | |
98 | QString posStr = attributes.value( "rposition" ); |
99 | int pos = posStr.toInt(); |
100 | |
101 | Recurrence *r = event->recurrence(); |
102 | |
103 | if ( rtype == "Daily" ) { |
104 | r->setDaily( freq ); |
105 | if ( hasEndDate ) { |
106 | r->setEndDate( endDate ); |
107 | } |
108 | } else if ( rtype == "Weekly" ) { |
109 | r->setWeekly( freq, weekDays ); |
110 | if ( hasEndDate ) { |
111 | r->setEndDate( endDate ); |
112 | } |
113 | } else if ( rtype == "MonthlyDate" ) { |
114 | r->setMonthly( freq ); |
115 | if ( hasEndDate ) { |
116 | r->setEndDate( endDate ); |
117 | } |
118 | r->addMonthlyDate( static_cast<short>( startDate.day() ) ); |
119 | } else if ( rtype == "MonthlyDay" ) { |
120 | r->setMonthly( freq ); |
121 | if ( hasEndDate ) { |
122 | r->setEndDate( endDate ); |
123 | } |
124 | QBitArray days( 7 ); |
125 | days.fill( false ); |
126 | days.setBit( startDate.dayOfWeek() - 1 ); |
127 | r->addMonthlyPos( static_cast<short>( pos ), days ); |
128 | } else if ( rtype == "Yearly" ) { |
129 | r->setYearly( freq ); |
130 | if ( hasEndDate ) { |
131 | r->setEndDate( endDate ); |
132 | } |
133 | } |
134 | } |
135 | |
136 | QString categoryList = attributes.value( "categories" ); |
137 | event->setCategories( lookupCategories( categoryList ) ); |
138 | |
139 | QString alarmStr = attributes.value( "alarm" ); |
140 | if ( !alarmStr.isEmpty() ) { |
141 | kDebug() << "Alarm:" << alarmStr; |
142 | Alarm *alarm = new Alarm( event ); |
143 | alarm->setType( Alarm::Display ); |
144 | alarm->setEnabled( true ); |
145 | int alarmOffset = alarmStr.toInt(); |
146 | alarm->setStartOffset( Duration( alarmOffset * -60 ) ); |
147 | event->addAlarm( alarm ); |
148 | } |
149 | |
150 | Event *oldEvent = mCalendar->event( uid ); |
151 | if ( oldEvent ) { |
152 | mCalendar->deleteEvent( oldEvent ); |
153 | } |
154 | |
155 | mCalendar->addEvent( event ); |
156 | } else if ( qName == "Task" ) { |
157 | Todo *todo = new Todo; |
158 | |
159 | QString uid = "Qtopia" + attributes.value( "Uid" ); |
160 | todo->setUid( uid ); |
161 | |
162 | QString description = attributes.value( "Description" ); |
163 | int pos = description.indexOf( '\n' ); |
164 | if ( pos > 0 ) { |
165 | QString summary = description.left( pos ); |
166 | todo->setSummary( summary, Qt::mightBeRichText( summary ) ); |
167 | todo->setDescription( description, Qt::mightBeRichText( description ) ); |
168 | } else { |
169 | todo->setSummary( description, Qt::mightBeRichText( description ) ); |
170 | } |
171 | |
172 | int priority = attributes.value( "Priority" ).toInt(); |
173 | // if ( priority == 0 ) priority = 3; |
174 | todo->setPriority( priority ); |
175 | |
176 | QString categoryList = attributes.value( "Categories" ); |
177 | todo->setCategories( lookupCategories( categoryList ) ); |
178 | |
179 | QString completedStr = attributes.value( "Completed" ); |
180 | if ( completedStr == "1" ) { |
181 | todo->setCompleted( true ); |
182 | } |
183 | |
184 | QString hasDateStr = attributes.value( "HasDate" ); |
185 | if ( hasDateStr == "1" ) { |
186 | int year = attributes.value( "DateYear" ).toInt(); |
187 | int month = attributes.value( "DateMonth" ).toInt(); |
188 | int day = attributes.value( "DateDay" ).toInt(); |
189 | |
190 | todo->setDtDue( KDateTime( QDate( year, month, day ), KDateTime::UTC ) ); |
191 | todo->setHasDueDate( true ); |
192 | } |
193 | |
194 | Todo *oldTodo = mCalendar->todo( uid ); |
195 | if ( oldTodo ) { |
196 | mCalendar->deleteTodo( oldTodo ); |
197 | } |
198 | |
199 | mCalendar->addTodo( todo ); |
200 | } else if ( qName == "Category" ) { |
201 | QString id = attributes.value( "id" ); |
202 | QString name = attributes.value( "name" ); |
203 | setCategory( id, name ); |
204 | } |
205 | |
206 | return true; |
207 | } |
208 | |
209 | bool warning ( const QXmlParseException &exception ) |
210 | { |
211 | kDebug() << "WARNING" ; |
212 | printException( exception ); |
213 | return true; |
214 | } |
215 | |
216 | bool error ( const QXmlParseException &exception ) |
217 | { |
218 | kDebug() << "ERROR" ; |
219 | printException( exception ); |
220 | return false; |
221 | } |
222 | |
223 | bool fatalError ( const QXmlParseException &exception ) |
224 | { |
225 | kDebug() << "FATALERROR" ; |
226 | printException( exception ); |
227 | return false; |
228 | } |
229 | |
230 | QString errorString () const |
231 | { |
232 | return "QtopiaParser: Error!" ; |
233 | } |
234 | |
235 | protected: |
236 | void printException( const QXmlParseException &exception ) |
237 | { |
238 | kError() << "XML Parse Error (line" << exception.lineNumber() |
239 | << ", col" << exception.columnNumber() << "):" |
240 | << exception.message() << "(public ID: '" |
241 | << exception.publicId() << "' system ID: '" |
242 | << exception.systemId() << "')" ; |
243 | } |
244 | |
245 | KDateTime toDateTime( const QString &value ) |
246 | { |
247 | KDateTime dt; |
248 | dt.setTime_t( value.toUInt() ); |
249 | |
250 | return dt; |
251 | } |
252 | |
253 | QStringList lookupCategories( const QString &categoryList ) |
254 | { |
255 | const QStringList categoryIds = categoryList.split( ';' ); |
256 | QStringList categories; |
257 | QStringList::ConstIterator it; |
258 | for ( it = categoryIds.constBegin(); it != categoryIds.constEnd(); ++it ) { |
259 | categories.append( category( *it ) ); |
260 | } |
261 | return categories; |
262 | } |
263 | |
264 | private: |
265 | Calendar *mCalendar; |
266 | |
267 | static QString category( const QString &id ) |
268 | { |
269 | QMap<QString,QString>::ConstIterator it = mCategoriesMap.constFind( id ); |
270 | if ( it == mCategoriesMap.constEnd() ) { |
271 | return id; |
272 | } else { |
273 | return *it; |
274 | } |
275 | } |
276 | |
277 | static void setCategory( const QString &id, const QString &name ) |
278 | { |
279 | mCategoriesMap.insert( id, name ); |
280 | } |
281 | |
282 | static QMap<QString,QString> mCategoriesMap; |
283 | }; |
284 | |
285 | QMap<QString,QString> QtopiaParser::mCategoriesMap; |
286 | //@endcond |
287 | |
288 | QtopiaFormat::QtopiaFormat() : d( 0 ) |
289 | { |
290 | } |
291 | |
292 | QtopiaFormat::~QtopiaFormat() |
293 | { |
294 | } |
295 | |
296 | bool QtopiaFormat::load( Calendar *calendar, const QString &fileName ) |
297 | { |
298 | kDebug() << fileName; |
299 | |
300 | clearException(); |
301 | |
302 | QtopiaParser handler( calendar ); |
303 | QFile xmlFile( fileName ); |
304 | QXmlInputSource source( &xmlFile ); |
305 | QXmlSimpleReader reader; |
306 | reader.setContentHandler( &handler ); |
307 | return reader.parse( source ); |
308 | } |
309 | |
310 | bool QtopiaFormat::save( Calendar *calendar, const QString &fileName ) |
311 | { |
312 | kDebug() << fileName; |
313 | |
314 | clearException(); |
315 | |
316 | QString text = toString( calendar ); |
317 | |
318 | if ( text.isNull() ) { |
319 | return false; |
320 | } |
321 | |
322 | // TODO: write backup file |
323 | |
324 | QFile file( fileName ); |
325 | if (!file.open( QIODevice::WriteOnly ) ) { |
326 | setException( new ErrorFormat( ErrorFormat::SaveError, |
327 | i18n( "Could not open file '%1'" , fileName ) ) ); |
328 | return false; |
329 | } |
330 | QTextStream ts( &file ); |
331 | ts << text; |
332 | file.close(); |
333 | |
334 | return true; |
335 | } |
336 | |
337 | bool QtopiaFormat::fromString( Calendar *, const QString & ) |
338 | { |
339 | kDebug() << "not yet implemented." ; |
340 | return false; |
341 | } |
342 | |
343 | bool QtopiaFormat::fromRawString( Calendar *, const QByteArray & ) |
344 | { |
345 | kDebug() << "not yet implemented." ; |
346 | return false; |
347 | } |
348 | |
349 | QString QtopiaFormat::toString( Calendar * ) |
350 | { |
351 | return QString(); |
352 | } |
353 | |