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
46using namespace KCal;
47
48//@cond PRIVATE
49class 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
285QMap<QString,QString> QtopiaParser::mCategoriesMap;
286//@endcond
287
288QtopiaFormat::QtopiaFormat() : d( 0 )
289{
290}
291
292QtopiaFormat::~QtopiaFormat()
293{
294}
295
296bool 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
310bool 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
337bool QtopiaFormat::fromString( Calendar *, const QString & )
338{
339 kDebug() << "not yet implemented.";
340 return false;
341}
342
343bool QtopiaFormat::fromRawString( Calendar *, const QByteArray & )
344{
345 kDebug() << "not yet implemented.";
346 return false;
347}
348
349QString QtopiaFormat::toString( Calendar * )
350{
351 return QString();
352}
353