1 | /* |
2 | This file is part of the kcal library. |
3 | |
4 | Copyright (c) 1998 Preston Brown <pbrown@kde.org> |
5 | Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org> |
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 |
25 | defines the VCalFormat base class. |
26 | |
27 | This class implements the vCalendar format. It provides methods for |
28 | loading/saving/converting vCalendar format data into the internal |
29 | representation as Calendar and Incidences. |
30 | |
31 | @brief |
32 | vCalendar format implementation. |
33 | |
34 | @author Preston Brown \<pbrown@kde.org\> |
35 | @author Cornelius Schumacher \<schumacher@kde.org\> |
36 | */ |
37 | |
38 | #include "vcalformat.h" |
39 | #include "calendar.h" |
40 | #include "versit/vcc.h" |
41 | #include "versit/vobject.h" |
42 | |
43 | #include <kdebug.h> |
44 | #include <kdatetime.h> |
45 | #include <klocalizedstring.h> |
46 | |
47 | #include <QtCore/QString> |
48 | #include <QtCore/QRegExp> |
49 | #include <QtCore/QFile> |
50 | #include <QtCore/QByteArray> |
51 | #include <QTextDocument> |
52 | |
53 | using namespace KCal; |
54 | |
55 | /** |
56 | Private class that helps to provide binary compatibility between releases. |
57 | @internal |
58 | */ |
59 | //@cond PRIVATE |
60 | class KCal::VCalFormat::Private |
61 | { |
62 | public: |
63 | Calendar *mCalendar; |
64 | Event::List mEventsRelate; // Events with relations |
65 | Todo::List mTodosRelate; // To-dos with relations |
66 | }; |
67 | //@endcond |
68 | |
69 | VCalFormat::VCalFormat() : d( new KCal::VCalFormat::Private ) |
70 | { |
71 | } |
72 | |
73 | VCalFormat::~VCalFormat() |
74 | { |
75 | delete d; |
76 | } |
77 | |
78 | bool VCalFormat::load( Calendar *calendar, const QString &fileName ) |
79 | { |
80 | d->mCalendar = calendar; |
81 | |
82 | clearException(); |
83 | |
84 | kDebug() << fileName; |
85 | |
86 | VObject *vcal = 0; |
87 | |
88 | // this is not necessarily only 1 vcal. Could be many vcals, or include |
89 | // a vcard... |
90 | vcal = Parse_MIME_FromFileName( const_cast<char *>( QFile::encodeName( fileName ).data() ) ); |
91 | |
92 | if ( !vcal ) { |
93 | setException( new ErrorFormat( ErrorFormat::CalVersionUnknown ) ); |
94 | return false; |
95 | } |
96 | |
97 | // any other top-level calendar stuff should be added/initialized here |
98 | |
99 | // put all vobjects into their proper places |
100 | populate( vcal ); |
101 | |
102 | // clean up from vcal API stuff |
103 | cleanVObjects( vcal ); |
104 | cleanStrTbl(); |
105 | |
106 | return true; |
107 | } |
108 | |
109 | bool VCalFormat::save( Calendar *calendar, const QString &fileName ) |
110 | { |
111 | d->mCalendar = calendar; |
112 | |
113 | QString tmpStr; |
114 | VObject *vcal, *vo; |
115 | |
116 | kDebug() << fileName; |
117 | |
118 | vcal = newVObject( VCCalProp ); |
119 | |
120 | // addPropValue(vcal,VCLocationProp, "0.0"); |
121 | addPropValue( vcal, VCProdIdProp, productId().toLatin1() ); |
122 | addPropValue( vcal, VCVersionProp, _VCAL_VERSION ); |
123 | |
124 | // TODO STUFF |
125 | Todo::List todoList = d->mCalendar->rawTodos(); |
126 | Todo::List::ConstIterator it; |
127 | for ( it = todoList.constBegin(); it != todoList.constEnd(); ++it ) { |
128 | vo = eventToVTodo( *it ); |
129 | addVObjectProp( vcal, vo ); |
130 | } |
131 | |
132 | // EVENT STUFF |
133 | Event::List events = d->mCalendar->rawEvents(); |
134 | Event::List::ConstIterator it2; |
135 | for ( it2 = events.constBegin(); it2 != events.constEnd(); ++it2 ) { |
136 | vo = eventToVEvent( *it2 ); |
137 | addVObjectProp( vcal, vo ); |
138 | } |
139 | |
140 | writeVObjectToFile( QFile::encodeName( fileName ).data(), vcal ); |
141 | cleanVObjects( vcal ); |
142 | cleanStrTbl(); |
143 | |
144 | if ( QFile::exists( fileName ) ) { |
145 | return true; |
146 | } else { |
147 | return false; // error |
148 | } |
149 | |
150 | return false; |
151 | } |
152 | |
153 | bool VCalFormat::fromString( Calendar *calendar, const QString &string ) |
154 | { |
155 | return fromRawString( calendar, string.toUtf8() ); |
156 | } |
157 | |
158 | bool VCalFormat::fromRawString( Calendar *calendar, const QByteArray &string ) |
159 | { |
160 | d->mCalendar = calendar; |
161 | |
162 | if ( !string.size() ) { |
163 | return false; |
164 | } |
165 | |
166 | VObject *vcal = Parse_MIME( string.data(), string.size() ); |
167 | if ( !vcal ) { |
168 | return false; |
169 | } |
170 | |
171 | VObjectIterator i; |
172 | VObject *curvo; |
173 | initPropIterator( &i, vcal ); |
174 | |
175 | // we only take the first object. TODO: parse all incidences. |
176 | do { |
177 | curvo = nextVObject( &i ); |
178 | } while ( strcmp( vObjectName( curvo ), VCEventProp ) && |
179 | strcmp( vObjectName( curvo ), VCTodoProp ) ); |
180 | |
181 | if ( strcmp( vObjectName( curvo ), VCEventProp ) == 0 ) { |
182 | Event *event = VEventToEvent( curvo ); |
183 | calendar->addEvent( event ); |
184 | } else { |
185 | kDebug() << "Unknown object type." ; |
186 | deleteVObject( vcal ); |
187 | return false; |
188 | } |
189 | |
190 | deleteVObject( vcal ); |
191 | |
192 | return true; |
193 | } |
194 | |
195 | QString VCalFormat::toString( Calendar *calendar ) |
196 | { |
197 | // TODO: Factor out VCalFormat::asString() |
198 | d->mCalendar = calendar; |
199 | |
200 | VObject *vcal = newVObject( VCCalProp ); |
201 | |
202 | addPropValue( vcal, VCProdIdProp, CalFormat::productId().toLatin1() ); |
203 | addPropValue( vcal, VCVersionProp, _VCAL_VERSION ); |
204 | |
205 | // TODO: Use all data. |
206 | Event::List events = calendar->events(); |
207 | if( events.isEmpty() ) { |
208 | cleanVObject ( vcal ); |
209 | return QString(); |
210 | } |
211 | Event *event = events.first(); |
212 | if ( !event ) { |
213 | cleanVObject ( vcal ); |
214 | return QString(); |
215 | } |
216 | VObject *vevent = eventToVEvent( event ); |
217 | |
218 | addVObjectProp( vcal, vevent ); |
219 | |
220 | char *buf = writeMemVObject( 0, 0, vcal ); |
221 | |
222 | QString result( buf ); |
223 | |
224 | cleanVObject( vcal ); |
225 | |
226 | return result; |
227 | } |
228 | |
229 | VObject *VCalFormat::eventToVTodo( const Todo *anEvent ) |
230 | { |
231 | VObject *vtodo; |
232 | QString tmpStr; |
233 | |
234 | vtodo = newVObject( VCTodoProp ); |
235 | |
236 | // due date |
237 | if ( anEvent->hasDueDate() ) { |
238 | tmpStr = kDateTimeToISO( anEvent->dtDue(), !anEvent->allDay() ); |
239 | addPropValue( vtodo, VCDueProp, tmpStr.toLocal8Bit() ); |
240 | } |
241 | |
242 | // start date |
243 | if ( anEvent->hasStartDate() ) { |
244 | tmpStr = kDateTimeToISO( anEvent->dtStart(), !anEvent->allDay() ); |
245 | addPropValue( vtodo, VCDTstartProp, tmpStr.toLocal8Bit() ); |
246 | } |
247 | |
248 | // creation date |
249 | tmpStr = kDateTimeToISO( anEvent->created() ); |
250 | addPropValue( vtodo, VCDCreatedProp, tmpStr.toLocal8Bit() ); |
251 | |
252 | // unique id |
253 | addPropValue( vtodo, VCUniqueStringProp, |
254 | anEvent->uid().toLocal8Bit() ); |
255 | |
256 | // revision |
257 | tmpStr.sprintf( "%i" , anEvent->revision() ); |
258 | addPropValue( vtodo, VCSequenceProp, tmpStr.toLocal8Bit() ); |
259 | |
260 | // last modification date |
261 | tmpStr = kDateTimeToISO( anEvent->lastModified() ); |
262 | addPropValue( vtodo, VCLastModifiedProp, tmpStr.toLocal8Bit() ); |
263 | |
264 | // organizer stuff |
265 | // @TODO: How about the common name? |
266 | if ( !anEvent->organizer().email().isEmpty() ) { |
267 | tmpStr = "MAILTO:" + anEvent->organizer().email(); |
268 | addPropValue( vtodo, ICOrganizerProp, tmpStr.toLocal8Bit() ); |
269 | } |
270 | |
271 | // attendees |
272 | if ( anEvent->attendeeCount() > 0 ) { |
273 | Attendee::List::ConstIterator it; |
274 | Attendee *curAttendee; |
275 | for ( it = anEvent->attendees().begin(); it != anEvent->attendees().end(); |
276 | ++it ) { |
277 | curAttendee = *it; |
278 | if ( !curAttendee->email().isEmpty() && |
279 | !curAttendee->name().isEmpty() ) { |
280 | tmpStr = "MAILTO:" + curAttendee->name() + " <" + curAttendee->email() + '>'; |
281 | } else if ( curAttendee->name().isEmpty() ) { |
282 | tmpStr = "MAILTO: " + curAttendee->email(); |
283 | } else if ( curAttendee->email().isEmpty() ) { |
284 | tmpStr = "MAILTO: " + curAttendee->name(); |
285 | } else if ( curAttendee->name().isEmpty() && curAttendee->email().isEmpty() ) { |
286 | kDebug() << "warning! this Event has an attendee w/o name or email!" ; |
287 | } |
288 | VObject *aProp = addPropValue( vtodo, VCAttendeeProp, tmpStr.toLocal8Bit() ); |
289 | addPropValue( aProp, VCRSVPProp, curAttendee->RSVP() ? "TRUE" : "FALSE" ); |
290 | addPropValue( aProp, VCStatusProp, writeStatus( curAttendee->status() ) ); |
291 | } |
292 | } |
293 | |
294 | // description BL: |
295 | if ( !anEvent->description().isEmpty() ) { |
296 | VObject *dObject = addPropValue( vtodo, VCDescriptionProp, |
297 | anEvent->description().toLocal8Bit() ); |
298 | if ( anEvent->description().indexOf( '\n' ) != -1 ) { |
299 | addPropValue( dObject, VCEncodingProp, VCQuotedPrintableProp ); |
300 | } |
301 | } |
302 | |
303 | // summary |
304 | if ( !anEvent->summary().isEmpty() ) { |
305 | addPropValue( vtodo, VCSummaryProp, anEvent->summary().toLocal8Bit() ); |
306 | } |
307 | |
308 | // location |
309 | if ( !anEvent->location().isEmpty() ) { |
310 | addPropValue( vtodo, VCLocationProp, anEvent->location().toLocal8Bit() ); |
311 | } |
312 | |
313 | // completed status |
314 | // backward compatibility, KOrganizer used to interpret only these two values |
315 | addPropValue( vtodo, VCStatusProp, anEvent->isCompleted() ? "COMPLETED" : "NEEDS_ACTION" ); |
316 | |
317 | // completion date |
318 | if ( anEvent->hasCompletedDate() ) { |
319 | tmpStr = kDateTimeToISO( anEvent->completed() ); |
320 | addPropValue( vtodo, VCCompletedProp, tmpStr.toLocal8Bit() ); |
321 | } |
322 | |
323 | // priority |
324 | tmpStr.sprintf( "%i" , anEvent->priority() ); |
325 | addPropValue( vtodo, VCPriorityProp, tmpStr.toLocal8Bit() ); |
326 | |
327 | // related event |
328 | if ( anEvent->relatedTo() ) { |
329 | addPropValue( vtodo, VCRelatedToProp, |
330 | anEvent->relatedTo()->uid().toLocal8Bit() ); |
331 | } |
332 | |
333 | // categories |
334 | const QStringList tmpStrList = anEvent->categories(); |
335 | tmpStr = "" ; |
336 | QString catStr; |
337 | QStringList::const_iterator its; |
338 | for ( its = tmpStrList.constBegin(); its != tmpStrList.constEnd(); ++its ) { |
339 | catStr = *its; |
340 | if ( catStr[0] == ' ' ) { |
341 | tmpStr += catStr.mid( 1 ); |
342 | } else { |
343 | tmpStr += catStr; |
344 | } |
345 | // this must be a ';' character as the vCalendar specification requires! |
346 | // vcc.y has been hacked to translate the ';' to a ',' when the vcal is |
347 | // read in. |
348 | tmpStr += ';'; |
349 | } |
350 | if ( !tmpStr.isEmpty() ) { |
351 | tmpStr.truncate( tmpStr.length() - 1 ); |
352 | addPropValue( vtodo, VCCategoriesProp, tmpStr.toLocal8Bit() ); |
353 | } |
354 | |
355 | // alarm stuff |
356 | Alarm::List::ConstIterator it; |
357 | for ( it = anEvent->alarms().begin(); it != anEvent->alarms().end(); ++it ) { |
358 | Alarm *alarm = *it; |
359 | if ( alarm->enabled() ) { |
360 | VObject *a = addProp( vtodo, VCDAlarmProp ); |
361 | tmpStr = kDateTimeToISO( alarm->time() ); |
362 | addPropValue( a, VCRunTimeProp, tmpStr.toLocal8Bit() ); |
363 | addPropValue( a, VCRepeatCountProp, "1" ); |
364 | addPropValue( a, VCDisplayStringProp, "beep!" ); |
365 | if ( alarm->type() == Alarm::Audio ) { |
366 | a = addProp( vtodo, VCAAlarmProp ); |
367 | addPropValue( a, VCRunTimeProp, tmpStr.toLocal8Bit() ); |
368 | addPropValue( a, VCRepeatCountProp, "1" ); |
369 | addPropValue( a, VCAudioContentProp, QFile::encodeName( alarm->audioFile() ) ); |
370 | } else if ( alarm->type() == Alarm::Procedure ) { |
371 | a = addProp( vtodo, VCPAlarmProp ); |
372 | addPropValue( a, VCRunTimeProp, tmpStr.toLocal8Bit() ); |
373 | addPropValue( a, VCRepeatCountProp, "1" ); |
374 | addPropValue( a, VCProcedureNameProp, QFile::encodeName( alarm->programFile() ) ); |
375 | } |
376 | } |
377 | } |
378 | |
379 | QString pilotId = anEvent->nonKDECustomProperty( KPilotIdProp ); |
380 | if ( !pilotId.isEmpty() ) { |
381 | // pilot sync stuff |
382 | addPropValue( vtodo, KPilotIdProp, pilotId.toLocal8Bit() ); |
383 | addPropValue( vtodo, KPilotStatusProp, |
384 | anEvent->nonKDECustomProperty( KPilotStatusProp ).toLocal8Bit() ); |
385 | } |
386 | |
387 | return vtodo; |
388 | } |
389 | |
390 | VObject *VCalFormat::eventToVEvent( const Event *anEvent ) |
391 | { |
392 | VObject *vevent; |
393 | QString tmpStr; |
394 | |
395 | vevent = newVObject( VCEventProp ); |
396 | |
397 | // start and end time |
398 | tmpStr = kDateTimeToISO( anEvent->dtStart(), !anEvent->allDay() ); |
399 | addPropValue( vevent, VCDTstartProp, tmpStr.toLocal8Bit() ); |
400 | |
401 | // events that have time associated but take up no time should |
402 | // not have both DTSTART and DTEND. |
403 | if ( anEvent->dtStart() != anEvent->dtEnd() ) { |
404 | tmpStr = kDateTimeToISO( anEvent->dtEnd(), !anEvent->allDay() ); |
405 | addPropValue( vevent, VCDTendProp, tmpStr.toLocal8Bit() ); |
406 | } |
407 | |
408 | // creation date |
409 | tmpStr = kDateTimeToISO( anEvent->created() ); |
410 | addPropValue( vevent, VCDCreatedProp, tmpStr.toLocal8Bit() ); |
411 | |
412 | // unique id |
413 | addPropValue( vevent, VCUniqueStringProp, |
414 | anEvent->uid().toLocal8Bit() ); |
415 | |
416 | // revision |
417 | tmpStr.sprintf( "%i" , anEvent->revision() ); |
418 | addPropValue( vevent, VCSequenceProp, tmpStr.toLocal8Bit() ); |
419 | |
420 | // last modification date |
421 | tmpStr = kDateTimeToISO( anEvent->lastModified() ); |
422 | addPropValue( vevent, VCLastModifiedProp, tmpStr.toLocal8Bit() ); |
423 | |
424 | // attendee and organizer stuff |
425 | // TODO: What to do with the common name? |
426 | if ( !anEvent->organizer().email().isEmpty() ) { |
427 | tmpStr = "MAILTO:" + anEvent->organizer().email(); |
428 | addPropValue( vevent, ICOrganizerProp, tmpStr.toLocal8Bit() ); |
429 | } |
430 | |
431 | // TODO: Put this functionality into Attendee class |
432 | if ( anEvent->attendeeCount() > 0 ) { |
433 | Attendee::List::ConstIterator it; |
434 | for ( it = anEvent->attendees().constBegin(); it != anEvent->attendees().constEnd(); |
435 | ++it ) { |
436 | Attendee *curAttendee = *it; |
437 | if ( !curAttendee->email().isEmpty() && !curAttendee->name().isEmpty() ) { |
438 | tmpStr = "MAILTO:" + curAttendee->name() + " <" + curAttendee->email() + '>'; |
439 | } else if ( curAttendee->name().isEmpty() ) { |
440 | tmpStr = "MAILTO: " + curAttendee->email(); |
441 | } else if ( curAttendee->email().isEmpty() ) { |
442 | tmpStr = "MAILTO: " + curAttendee->name(); |
443 | } else if ( curAttendee->name().isEmpty() && curAttendee->email().isEmpty() ) { |
444 | kDebug() << "warning! this Event has an attendee w/o name or email!" ; |
445 | } |
446 | VObject *aProp = addPropValue( vevent, VCAttendeeProp, tmpStr.toLocal8Bit() ); |
447 | addPropValue( aProp, VCRSVPProp, curAttendee->RSVP() ? "TRUE" : "FALSE" ); |
448 | addPropValue( aProp, VCStatusProp, writeStatus( curAttendee->status() ) ); |
449 | } |
450 | } |
451 | |
452 | // recurrence rule stuff |
453 | const Recurrence *recur = anEvent->recurrence(); |
454 | if ( recur->recurs() ) { |
455 | bool validRecur = true; |
456 | QString tmpStr2; |
457 | switch ( recur->recurrenceType() ) { |
458 | case Recurrence::rDaily: |
459 | tmpStr.sprintf( "D%i " , recur->frequency() ); |
460 | break; |
461 | case Recurrence::rWeekly: |
462 | tmpStr.sprintf( "W%i " , recur->frequency() ); |
463 | for ( int i = 0; i < 7; ++i ) { |
464 | QBitArray days ( recur->days() ); |
465 | if ( days.testBit(i) ) { |
466 | tmpStr += dayFromNum( i ); |
467 | } |
468 | } |
469 | break; |
470 | case Recurrence::rMonthlyPos: |
471 | { |
472 | tmpStr.sprintf( "MP%i " , recur->frequency() ); |
473 | // write out all rMonthPos's |
474 | QList<RecurrenceRule::WDayPos> tmpPositions = recur->monthPositions(); |
475 | for ( QList<RecurrenceRule::WDayPos>::ConstIterator posit = tmpPositions.constBegin(); |
476 | posit != tmpPositions.constEnd(); ++posit ) { |
477 | int pos = (*posit).pos(); |
478 | tmpStr2.sprintf( "%i" , ( pos > 0 ) ? pos : (-pos) ); |
479 | if ( pos < 0 ) { |
480 | tmpStr2 += "- " ; |
481 | } else { |
482 | tmpStr2 += "+ " ; |
483 | } |
484 | tmpStr += tmpStr2; |
485 | tmpStr += dayFromNum( (*posit).day() - 1 ); |
486 | } |
487 | break; |
488 | } |
489 | case Recurrence::rMonthlyDay: |
490 | { |
491 | tmpStr.sprintf( "MD%i " , recur->frequency() ); |
492 | // write out all rMonthDays; |
493 | const QList<int> tmpDays = recur->monthDays(); |
494 | for ( QList<int>::ConstIterator tmpDay = tmpDays.constBegin(); |
495 | tmpDay != tmpDays.constEnd(); ++tmpDay ) { |
496 | tmpStr2.sprintf( "%i " , *tmpDay ); |
497 | tmpStr += tmpStr2; |
498 | } |
499 | break; |
500 | } |
501 | case Recurrence::rYearlyMonth: |
502 | { |
503 | tmpStr.sprintf( "YM%i " , recur->frequency() ); |
504 | // write out all the months;' |
505 | // TODO: Any way to write out the day within the month??? |
506 | const QList<int> months = recur->yearMonths(); |
507 | for ( QList<int>::ConstIterator mit = months.constBegin(); |
508 | mit != months.constEnd(); ++mit ) { |
509 | tmpStr2.sprintf( "%i " , *mit ); |
510 | tmpStr += tmpStr2; |
511 | } |
512 | break; |
513 | } |
514 | case Recurrence::rYearlyDay: |
515 | { |
516 | tmpStr.sprintf( "YD%i " , recur->frequency() ); |
517 | // write out all the rYearNums; |
518 | const QList<int> tmpDays = recur->yearDays(); |
519 | for ( QList<int>::ConstIterator tmpDay = tmpDays.begin(); |
520 | tmpDay != tmpDays.end(); ++tmpDay ) { |
521 | tmpStr2.sprintf( "%i " , *tmpDay ); |
522 | tmpStr += tmpStr2; |
523 | } |
524 | break; |
525 | } |
526 | default: |
527 | // TODO: Write rYearlyPos and arbitrary rules! |
528 | kDebug() << "ERROR, it should never get here in eventToVEvent!" ; |
529 | validRecur = false; |
530 | break; |
531 | } // switch |
532 | |
533 | if ( recur->duration() > 0 ) { |
534 | tmpStr2.sprintf( "#%i" , recur->duration() ); |
535 | tmpStr += tmpStr2; |
536 | } else if ( recur->duration() == -1 ) { |
537 | tmpStr += "#0" ; // defined as repeat forever |
538 | } else { |
539 | tmpStr += kDateTimeToISO( recur->endDateTime(), false ); |
540 | } |
541 | // Only write out the rrule if we have a valid recurrence (i.e. a known |
542 | // type in thee switch above) |
543 | if ( validRecur ) { |
544 | addPropValue( vevent, VCRRuleProp, tmpStr.toLocal8Bit() ); |
545 | } |
546 | |
547 | } // event repeats |
548 | |
549 | // exceptions to recurrence |
550 | DateList dateList = recur->exDates(); |
551 | DateList::ConstIterator it; |
552 | QString tmpStr2; |
553 | |
554 | for ( it = dateList.constBegin(); it != dateList.constEnd(); ++it ) { |
555 | tmpStr = qDateToISO(*it) + ';'; |
556 | tmpStr2 += tmpStr; |
557 | } |
558 | if ( !tmpStr2.isEmpty() ) { |
559 | tmpStr2.truncate( tmpStr2.length() - 1 ); |
560 | addPropValue( vevent, VCExpDateProp, tmpStr2.toLocal8Bit() ); |
561 | } |
562 | |
563 | // description |
564 | if ( !anEvent->description().isEmpty() ) { |
565 | VObject *dObject = addPropValue( vevent, VCDescriptionProp, |
566 | anEvent->description().toLocal8Bit() ); |
567 | if ( anEvent->description().indexOf( '\n' ) != -1 ) { |
568 | addPropValue( dObject, VCEncodingProp, VCQuotedPrintableProp ); |
569 | } |
570 | } |
571 | |
572 | // summary |
573 | if ( !anEvent->summary().isEmpty() ) { |
574 | addPropValue( vevent, VCSummaryProp, anEvent->summary().toLocal8Bit() ); |
575 | } |
576 | |
577 | // location |
578 | if ( !anEvent->location().isEmpty() ) { |
579 | addPropValue( vevent, VCLocationProp, anEvent->location().toLocal8Bit() ); |
580 | } |
581 | |
582 | // status |
583 | // TODO: define Event status |
584 | // addPropValue( vevent, VCStatusProp, anEvent->statusStr().toLocal8Bit() ); |
585 | |
586 | // secrecy |
587 | const char *text = 0; |
588 | switch ( anEvent->secrecy() ) { |
589 | case Incidence::SecrecyPublic: |
590 | text = "PUBLIC" ; |
591 | break; |
592 | case Incidence::SecrecyPrivate: |
593 | text = "PRIVATE" ; |
594 | break; |
595 | case Incidence::SecrecyConfidential: |
596 | text = "CONFIDENTIAL" ; |
597 | break; |
598 | } |
599 | if ( text ) { |
600 | addPropValue( vevent, VCClassProp, text ); |
601 | } |
602 | |
603 | // categories |
604 | QStringList tmpStrList = anEvent->categories(); |
605 | tmpStr = "" ; |
606 | QString catStr; |
607 | for ( QStringList::const_iterator it = tmpStrList.constBegin(); it != tmpStrList.constEnd(); |
608 | ++it ) { |
609 | catStr = *it; |
610 | if ( catStr[0] == ' ' ) { |
611 | tmpStr += catStr.mid( 1 ); |
612 | } else { |
613 | tmpStr += catStr; |
614 | } |
615 | // this must be a ';' character as the vCalendar specification requires! |
616 | // vcc.y has been hacked to translate the ';' to a ',' when the vcal is |
617 | // read in. |
618 | tmpStr += ';'; |
619 | } |
620 | if ( !tmpStr.isEmpty() ) { |
621 | tmpStr.truncate( tmpStr.length() - 1 ); |
622 | addPropValue( vevent, VCCategoriesProp, tmpStr.toLocal8Bit() ); |
623 | } |
624 | |
625 | // attachments |
626 | // TODO: handle binary attachments! |
627 | Attachment::List attachments = anEvent->attachments(); |
628 | Attachment::List::ConstIterator atIt; |
629 | for ( atIt = attachments.constBegin(); atIt != attachments.constEnd(); ++atIt ) { |
630 | addPropValue( vevent, VCAttachProp, (*atIt)->uri().toLocal8Bit() ); |
631 | } |
632 | |
633 | // resources |
634 | tmpStrList = anEvent->resources(); |
635 | tmpStr = tmpStrList.join( ";" ); |
636 | if ( !tmpStr.isEmpty() ) { |
637 | addPropValue( vevent, VCResourcesProp, tmpStr.toLocal8Bit() ); |
638 | } |
639 | |
640 | // alarm stuff |
641 | Alarm::List::ConstIterator it2; |
642 | for ( it2 = anEvent->alarms().constBegin(); it2 != anEvent->alarms().constEnd(); ++it2 ) { |
643 | Alarm *alarm = *it2; |
644 | if ( alarm->enabled() ) { |
645 | VObject *a = addProp( vevent, VCDAlarmProp ); |
646 | tmpStr = kDateTimeToISO( alarm->time() ); |
647 | addPropValue( a, VCRunTimeProp, tmpStr.toLocal8Bit() ); |
648 | addPropValue( a, VCRepeatCountProp, "1" ); |
649 | addPropValue( a, VCDisplayStringProp, "beep!" ); |
650 | if ( alarm->type() == Alarm::Audio ) { |
651 | a = addProp( vevent, VCAAlarmProp ); |
652 | addPropValue( a, VCRunTimeProp, tmpStr.toLocal8Bit() ); |
653 | addPropValue( a, VCRepeatCountProp, "1" ); |
654 | addPropValue( a, VCAudioContentProp, QFile::encodeName( alarm->audioFile() ) ); |
655 | } |
656 | if ( alarm->type() == Alarm::Procedure ) { |
657 | a = addProp( vevent, VCPAlarmProp ); |
658 | addPropValue( a, VCRunTimeProp, tmpStr.toLocal8Bit() ); |
659 | addPropValue( a, VCRepeatCountProp, "1" ); |
660 | addPropValue( a, VCProcedureNameProp, QFile::encodeName( alarm->programFile() ) ); |
661 | } |
662 | } |
663 | } |
664 | |
665 | // priority |
666 | tmpStr.sprintf( "%i" , anEvent->priority() ); |
667 | addPropValue( vevent, VCPriorityProp, tmpStr.toLocal8Bit() ); |
668 | |
669 | // transparency |
670 | tmpStr.sprintf( "%i" , anEvent->transparency() ); |
671 | addPropValue( vevent, VCTranspProp, tmpStr.toLocal8Bit() ); |
672 | |
673 | // related event |
674 | if ( anEvent->relatedTo() ) { |
675 | addPropValue( vevent, VCRelatedToProp, anEvent->relatedTo()->uid().toLocal8Bit() ); |
676 | } |
677 | |
678 | QString pilotId = anEvent->nonKDECustomProperty( KPilotIdProp ); |
679 | if ( !pilotId.isEmpty() ) { |
680 | // pilot sync stuff |
681 | addPropValue( vevent, KPilotIdProp, pilotId.toLocal8Bit() ); |
682 | addPropValue( vevent, KPilotStatusProp, |
683 | anEvent->nonKDECustomProperty( KPilotStatusProp ).toLocal8Bit() ); |
684 | } |
685 | |
686 | return vevent; |
687 | } |
688 | |
689 | Todo *VCalFormat::VTodoToEvent( VObject *vtodo ) |
690 | { |
691 | VObject *vo; |
692 | VObjectIterator voi; |
693 | char *s; |
694 | |
695 | Todo *anEvent = new Todo; |
696 | |
697 | // creation date |
698 | if ( ( vo = isAPropertyOf( vtodo, VCDCreatedProp ) ) != 0 ) { |
699 | anEvent->setCreated( ISOToKDateTime( s = fakeCString( vObjectUStringZValue( vo ) ) ) ); |
700 | deleteStr( s ); |
701 | } |
702 | |
703 | // unique id |
704 | vo = isAPropertyOf( vtodo, VCUniqueStringProp ); |
705 | // while the UID property is preferred, it is not required. We'll use the |
706 | // default Event UID if none is given. |
707 | if ( vo ) { |
708 | anEvent->setUid( s = fakeCString( vObjectUStringZValue( vo ) ) ); |
709 | deleteStr( s ); |
710 | } |
711 | |
712 | // last modification date |
713 | if ( ( vo = isAPropertyOf( vtodo, VCLastModifiedProp ) ) != 0 ) { |
714 | anEvent->setLastModified( ISOToKDateTime( s = fakeCString( vObjectUStringZValue( vo ) ) ) ); |
715 | deleteStr( s ); |
716 | } else { |
717 | anEvent->setLastModified( KDateTime::currentUtcDateTime() ); |
718 | } |
719 | |
720 | // organizer |
721 | // if our extension property for the event's ORGANIZER exists, add it. |
722 | if ( ( vo = isAPropertyOf( vtodo, ICOrganizerProp ) ) != 0 ) { |
723 | anEvent->setOrganizer( s = fakeCString( vObjectUStringZValue( vo ) ) ); |
724 | deleteStr( s ); |
725 | } else { |
726 | anEvent->setOrganizer( d->mCalendar->owner() ); |
727 | } |
728 | |
729 | // attendees. |
730 | initPropIterator( &voi, vtodo ); |
731 | while ( moreIteration( &voi ) ) { |
732 | vo = nextVObject( &voi ); |
733 | if ( strcmp( vObjectName( vo ), VCAttendeeProp ) == 0 ) { |
734 | Attendee *a; |
735 | VObject *vp; |
736 | s = fakeCString( vObjectUStringZValue( vo ) ); |
737 | QString tmpStr = QString::fromLocal8Bit( s ); |
738 | deleteStr( s ); |
739 | tmpStr = tmpStr.simplified(); |
740 | int emailPos1, emailPos2; |
741 | if ( ( emailPos1 = tmpStr.indexOf( '<' ) ) > 0 ) { |
742 | // both email address and name |
743 | emailPos2 = tmpStr.lastIndexOf( '>' ); |
744 | a = new Attendee( tmpStr.left( emailPos1 - 1 ), |
745 | tmpStr.mid( emailPos1 + 1, |
746 | emailPos2 - ( emailPos1 + 1 ) ) ); |
747 | } else if ( tmpStr.indexOf( '@' ) > 0 ) { |
748 | // just an email address |
749 | a = new Attendee( 0, tmpStr ); |
750 | } else { |
751 | // just a name |
752 | // WTF??? Replacing the spaces of a name and using this as email? |
753 | QString email = tmpStr.replace( ' ', '.' ); |
754 | a = new Attendee( tmpStr, email ); |
755 | } |
756 | |
757 | // is there an RSVP property? |
758 | if ( ( vp = isAPropertyOf( vo, VCRSVPProp ) ) != 0 ) { |
759 | a->setRSVP( vObjectStringZValue( vp ) ); |
760 | } |
761 | // is there a status property? |
762 | if ( ( vp = isAPropertyOf( vo, VCStatusProp ) ) != 0 ) { |
763 | a->setStatus( readStatus( vObjectStringZValue( vp ) ) ); |
764 | } |
765 | // add the attendee |
766 | anEvent->addAttendee( a ); |
767 | } |
768 | } |
769 | |
770 | // description for todo |
771 | if ( ( vo = isAPropertyOf( vtodo, VCDescriptionProp ) ) != 0 ) { |
772 | s = fakeCString( vObjectUStringZValue( vo ) ); |
773 | anEvent->setDescription( QString::fromLocal8Bit( s ), Qt::mightBeRichText( s ) ); |
774 | deleteStr( s ); |
775 | } |
776 | |
777 | // summary |
778 | if ( ( vo = isAPropertyOf( vtodo, VCSummaryProp ) ) ) { |
779 | s = fakeCString( vObjectUStringZValue( vo ) ); |
780 | anEvent->setSummary( QString::fromLocal8Bit( s ), Qt::mightBeRichText( s ) ); |
781 | deleteStr( s ); |
782 | } |
783 | |
784 | // location |
785 | if ( ( vo = isAPropertyOf( vtodo, VCLocationProp ) ) != 0 ) { |
786 | s = fakeCString( vObjectUStringZValue( vo ) ); |
787 | anEvent->setLocation( QString::fromLocal8Bit( s ), Qt::mightBeRichText( s ) ); |
788 | deleteStr( s ); |
789 | } |
790 | |
791 | // completed |
792 | // was: status |
793 | if ( ( vo = isAPropertyOf( vtodo, VCStatusProp ) ) != 0 ) { |
794 | s = fakeCString( vObjectUStringZValue( vo ) ); |
795 | if ( s && strcmp( s, "COMPLETED" ) == 0 ) { |
796 | anEvent->setCompleted( true ); |
797 | } else { |
798 | anEvent->setCompleted( false ); |
799 | } |
800 | deleteStr( s ); |
801 | } else { |
802 | anEvent->setCompleted( false ); |
803 | } |
804 | |
805 | // completion date |
806 | if ( ( vo = isAPropertyOf( vtodo, VCCompletedProp ) ) != 0 ) { |
807 | anEvent->setCompleted( ISOToKDateTime( s = fakeCString( vObjectUStringZValue( vo ) ) ) ); |
808 | deleteStr( s ); |
809 | } |
810 | |
811 | // priority |
812 | if ( ( vo = isAPropertyOf( vtodo, VCPriorityProp ) ) ) { |
813 | s = fakeCString( vObjectUStringZValue( vo ) ); |
814 | if ( s ) { |
815 | anEvent->setPriority( atoi( s ) ); |
816 | deleteStr( s ); |
817 | } |
818 | } |
819 | |
820 | // due date |
821 | if ( ( vo = isAPropertyOf( vtodo, VCDueProp ) ) != 0 ) { |
822 | anEvent->setDtDue( ISOToKDateTime( s = fakeCString( vObjectUStringZValue( vo ) ) ) ); |
823 | deleteStr( s ); |
824 | anEvent->setHasDueDate( true ); |
825 | } else { |
826 | anEvent->setHasDueDate( false ); |
827 | } |
828 | |
829 | // start time |
830 | if ( ( vo = isAPropertyOf( vtodo, VCDTstartProp ) ) != 0 ) { |
831 | anEvent->setDtStart( ISOToKDateTime( s = fakeCString( vObjectUStringZValue( vo ) ) ) ); |
832 | deleteStr( s ); |
833 | anEvent->setHasStartDate( true ); |
834 | } else { |
835 | anEvent->setHasStartDate( false ); |
836 | } |
837 | |
838 | // alarm stuff |
839 | if ( ( vo = isAPropertyOf( vtodo, VCDAlarmProp ) ) ) { |
840 | Alarm *alarm = anEvent->newAlarm(); |
841 | VObject *a; |
842 | if ( ( a = isAPropertyOf( vo, VCRunTimeProp ) ) ) { |
843 | alarm->setTime( ISOToKDateTime( s = fakeCString( vObjectUStringZValue( a ) ) ) ); |
844 | deleteStr( s ); |
845 | } |
846 | alarm->setEnabled( true ); |
847 | if ( ( vo = isAPropertyOf( vtodo, VCPAlarmProp ) ) ) { |
848 | if ( ( a = isAPropertyOf( vo, VCProcedureNameProp ) ) ) { |
849 | s = fakeCString( vObjectUStringZValue( a ) ); |
850 | alarm->setProcedureAlarm( QFile::decodeName( s ) ); |
851 | deleteStr( s ); |
852 | } |
853 | } |
854 | if ( ( vo = isAPropertyOf( vtodo, VCAAlarmProp ) ) ) { |
855 | if ( ( a = isAPropertyOf( vo, VCAudioContentProp ) ) ) { |
856 | s = fakeCString( vObjectUStringZValue( a ) ); |
857 | alarm->setAudioAlarm( QFile::decodeName( s ) ); |
858 | deleteStr( s ); |
859 | } |
860 | } |
861 | } |
862 | |
863 | // related todo |
864 | if ( ( vo = isAPropertyOf( vtodo, VCRelatedToProp ) ) != 0 ) { |
865 | anEvent->setRelatedToUid( s = fakeCString( vObjectUStringZValue( vo ) ) ); |
866 | deleteStr( s ); |
867 | d->mTodosRelate.append( anEvent ); |
868 | } |
869 | |
870 | // categories |
871 | if ( ( vo = isAPropertyOf( vtodo, VCCategoriesProp ) ) != 0 ) { |
872 | s = fakeCString( vObjectUStringZValue( vo ) ); |
873 | QString categories = QString::fromLocal8Bit( s ); |
874 | deleteStr( s ); |
875 | QStringList tmpStrList = categories.split( ';' ); |
876 | anEvent->setCategories( tmpStrList ); |
877 | } |
878 | |
879 | /* PILOT SYNC STUFF */ |
880 | if ( ( vo = isAPropertyOf( vtodo, KPilotIdProp ) ) ) { |
881 | anEvent->setNonKDECustomProperty( |
882 | KPilotIdProp, QString::fromLocal8Bit( s = fakeCString( vObjectUStringZValue( vo ) ) ) ); |
883 | deleteStr( s ); |
884 | if ( ( vo = isAPropertyOf( vtodo, KPilotStatusProp ) ) ) { |
885 | anEvent->setNonKDECustomProperty( |
886 | KPilotStatusProp, QString::fromLocal8Bit( s = fakeCString( vObjectUStringZValue( vo ) ) ) ); |
887 | deleteStr( s ); |
888 | } else { |
889 | anEvent->setNonKDECustomProperty( KPilotStatusProp, QString::number( SYNCMOD ) ); |
890 | } |
891 | } |
892 | |
893 | return anEvent; |
894 | } |
895 | |
896 | Event *VCalFormat::VEventToEvent( VObject *vevent ) |
897 | { |
898 | VObject *vo; |
899 | VObjectIterator voi; |
900 | char *s; |
901 | |
902 | Event *anEvent = new Event; |
903 | |
904 | // creation date |
905 | if ( ( vo = isAPropertyOf( vevent, VCDCreatedProp ) ) != 0 ) { |
906 | anEvent->setCreated( ISOToKDateTime( s = fakeCString( vObjectUStringZValue( vo ) ) ) ); |
907 | deleteStr( s ); |
908 | } |
909 | |
910 | // unique id |
911 | vo = isAPropertyOf( vevent, VCUniqueStringProp ); |
912 | // while the UID property is preferred, it is not required. We'll use the |
913 | // default Event UID if none is given. |
914 | if ( vo ) { |
915 | anEvent->setUid( s = fakeCString( vObjectUStringZValue( vo ) ) ); |
916 | deleteStr( s ); |
917 | } |
918 | |
919 | // revision |
920 | // again NSCAL doesn't give us much to work with, so we improvise... |
921 | anEvent->setRevision( 0 ); |
922 | if ( ( vo = isAPropertyOf( vevent, VCSequenceProp ) ) != 0 ) { |
923 | s = fakeCString( vObjectUStringZValue( vo ) ); |
924 | if ( s ) { |
925 | anEvent->setRevision( atoi( s ) ); |
926 | deleteStr( s ); |
927 | } |
928 | } |
929 | |
930 | // last modification date |
931 | if ( ( vo = isAPropertyOf( vevent, VCLastModifiedProp ) ) != 0 ) { |
932 | anEvent->setLastModified( ISOToKDateTime( s = fakeCString( vObjectUStringZValue( vo ) ) ) ); |
933 | deleteStr( s ); |
934 | } else { |
935 | anEvent->setLastModified( KDateTime::currentUtcDateTime() ); |
936 | } |
937 | |
938 | // organizer |
939 | // if our extension property for the event's ORGANIZER exists, add it. |
940 | if ( ( vo = isAPropertyOf( vevent, ICOrganizerProp ) ) != 0 ) { |
941 | // FIXME: Also use the full name, not just the email address |
942 | anEvent->setOrganizer( s = fakeCString( vObjectUStringZValue( vo ) ) ); |
943 | deleteStr( s ); |
944 | } else { |
945 | anEvent->setOrganizer( d->mCalendar->owner() ); |
946 | } |
947 | |
948 | // deal with attendees. |
949 | initPropIterator( &voi, vevent ); |
950 | while ( moreIteration( &voi ) ) { |
951 | vo = nextVObject( &voi ); |
952 | if ( strcmp( vObjectName( vo ), VCAttendeeProp ) == 0 ) { |
953 | Attendee *a; |
954 | VObject *vp; |
955 | s = fakeCString( vObjectUStringZValue( vo ) ); |
956 | QString tmpStr = QString::fromLocal8Bit( s ); |
957 | deleteStr( s ); |
958 | tmpStr = tmpStr.simplified(); |
959 | int emailPos1, emailPos2; |
960 | if ( ( emailPos1 = tmpStr.indexOf( '<' ) ) > 0 ) { |
961 | // both email address and name |
962 | emailPos2 = tmpStr.lastIndexOf( '>' ); |
963 | a = new Attendee( tmpStr.left( emailPos1 - 1 ), |
964 | tmpStr.mid( emailPos1 + 1, |
965 | emailPos2 - ( emailPos1 + 1 ) ) ); |
966 | } else if ( tmpStr.indexOf( '@' ) > 0 ) { |
967 | // just an email address |
968 | a = new Attendee( 0, tmpStr ); |
969 | } else { |
970 | // just a name |
971 | QString email = tmpStr.replace( ' ', '.' ); |
972 | a = new Attendee( tmpStr, email ); |
973 | } |
974 | |
975 | // is there an RSVP property? |
976 | if ( ( vp = isAPropertyOf( vo, VCRSVPProp ) ) != 0 ) { |
977 | a->setRSVP( vObjectStringZValue( vp ) ); |
978 | } |
979 | // is there a status property? |
980 | if ( ( vp = isAPropertyOf( vo, VCStatusProp ) ) != 0 ) { |
981 | a->setStatus( readStatus( vObjectStringZValue( vp ) ) ); |
982 | } |
983 | // add the attendee |
984 | anEvent->addAttendee( a ); |
985 | } |
986 | } |
987 | |
988 | // This isn't strictly true. An event that doesn't have a start time |
989 | // or an end time isn't all-day, it has an anchor in time but it doesn't |
990 | // "take up" any time. |
991 | /*if ((isAPropertyOf(vevent, VCDTstartProp) == 0) || |
992 | (isAPropertyOf(vevent, VCDTendProp) == 0)) { |
993 | anEvent->setAllDay(true); |
994 | } else { |
995 | }*/ |
996 | |
997 | anEvent->setAllDay( false ); |
998 | |
999 | // start time |
1000 | if ( ( vo = isAPropertyOf( vevent, VCDTstartProp ) ) != 0 ) { |
1001 | anEvent->setDtStart( ISOToKDateTime( s = fakeCString( vObjectUStringZValue( vo ) ) ) ); |
1002 | deleteStr( s ); |
1003 | if ( anEvent->dtStart().time().isNull() ) { |
1004 | anEvent->setAllDay( true ); |
1005 | } |
1006 | } |
1007 | |
1008 | // stop time |
1009 | if ( ( vo = isAPropertyOf( vevent, VCDTendProp ) ) != 0 ) { |
1010 | anEvent->setDtEnd( ISOToKDateTime( s = fakeCString( vObjectUStringZValue( vo ) ) ) ); |
1011 | deleteStr( s ); |
1012 | if ( anEvent->dtEnd().time().isNull() ) { |
1013 | anEvent->setAllDay( true ); |
1014 | } |
1015 | } |
1016 | |
1017 | // at this point, there should be at least a start or end time. |
1018 | // fix up for events that take up no time but have a time associated |
1019 | if ( !( vo = isAPropertyOf( vevent, VCDTstartProp ) ) ) { |
1020 | anEvent->setDtStart( anEvent->dtEnd() ); |
1021 | } |
1022 | if ( !( vo = isAPropertyOf( vevent, VCDTendProp ) ) ) { |
1023 | anEvent->setDtEnd( anEvent->dtStart() ); |
1024 | } |
1025 | |
1026 | /////////////////////////////////////////////////////////////////////////// |
1027 | |
1028 | // repeat stuff |
1029 | if ( ( vo = isAPropertyOf( vevent, VCRRuleProp ) ) != 0 ) { |
1030 | QString tmpStr = ( s = fakeCString( vObjectUStringZValue( vo ) ) ); |
1031 | deleteStr( s ); |
1032 | tmpStr = tmpStr.simplified(); |
1033 | tmpStr = tmpStr.toUpper(); |
1034 | |
1035 | // first, read the type of the recurrence |
1036 | int typelen = 1; |
1037 | uint type = Recurrence::rNone; |
1038 | if ( tmpStr.left(1) == "D" ) { |
1039 | type = Recurrence::rDaily; |
1040 | } else if ( tmpStr.left(1) == "W" ) { |
1041 | type = Recurrence::rWeekly; |
1042 | } else { |
1043 | typelen = 2; |
1044 | if ( tmpStr.left(2) == "MP" ) { |
1045 | type = Recurrence::rMonthlyPos; |
1046 | } else if ( tmpStr.left(2) == "MD" ) { |
1047 | type = Recurrence::rMonthlyDay; |
1048 | } else if ( tmpStr.left(2) == "YM" ) { |
1049 | type = Recurrence::rYearlyMonth; |
1050 | } else if ( tmpStr.left(2) == "YD" ) { |
1051 | type = Recurrence::rYearlyDay; |
1052 | } |
1053 | } |
1054 | |
1055 | if ( type != Recurrence::rNone ) { |
1056 | |
1057 | // Immediately after the type is the frequency |
1058 | int index = tmpStr.indexOf( ' ' ); |
1059 | int last = tmpStr.lastIndexOf( ' ' ) + 1; // find last entry |
1060 | int rFreq = tmpStr.mid( typelen, ( index - 1 ) ).toInt(); |
1061 | ++index; // advance to beginning of stuff after freq |
1062 | |
1063 | // Read the type-specific settings |
1064 | switch ( type ) { |
1065 | case Recurrence::rDaily: |
1066 | anEvent->recurrence()->setDaily(rFreq); |
1067 | break; |
1068 | |
1069 | case Recurrence::rWeekly: |
1070 | { |
1071 | QBitArray qba(7); |
1072 | QString dayStr; |
1073 | if ( index == last ) { |
1074 | // e.g. W1 #0 |
1075 | qba.setBit( anEvent->dtStart().date().dayOfWeek() - 1 ); |
1076 | } else { |
1077 | // e.g. W1 SU #0 |
1078 | while ( index < last ) { |
1079 | dayStr = tmpStr.mid( index, 3 ); |
1080 | int dayNum = numFromDay( dayStr ); |
1081 | if ( dayNum >= 0 ) { |
1082 | qba.setBit( dayNum ); |
1083 | } |
1084 | index += 3; // advance to next day, or possibly "#" |
1085 | } |
1086 | } |
1087 | anEvent->recurrence()->setWeekly( rFreq, qba ); |
1088 | break; |
1089 | } |
1090 | |
1091 | case Recurrence::rMonthlyPos: |
1092 | { |
1093 | anEvent->recurrence()->setMonthly( rFreq ); |
1094 | |
1095 | QBitArray qba(7); |
1096 | short tmpPos; |
1097 | if ( index == last ) { |
1098 | // e.g. MP1 #0 |
1099 | tmpPos = anEvent->dtStart().date().day() / 7 + 1; |
1100 | if ( tmpPos == 5 ) { |
1101 | tmpPos = -1; |
1102 | } |
1103 | qba.setBit( anEvent->dtStart().date().dayOfWeek() - 1 ); |
1104 | anEvent->recurrence()->addMonthlyPos( tmpPos, qba ); |
1105 | } else { |
1106 | // e.g. MP1 1+ SU #0 |
1107 | while ( index < last ) { |
1108 | tmpPos = tmpStr.mid( index, 1 ).toShort(); |
1109 | index += 1; |
1110 | if ( tmpStr.mid( index, 1 ) == "-" ) { |
1111 | // convert tmpPos to negative |
1112 | tmpPos = 0 - tmpPos; |
1113 | } |
1114 | index += 2; // advance to day(s) |
1115 | while ( numFromDay( tmpStr.mid( index, 3 ) ) >= 0 ) { |
1116 | int dayNum = numFromDay( tmpStr.mid( index, 3 ) ); |
1117 | qba.setBit( dayNum ); |
1118 | index += 3; // advance to next day, or possibly pos or "#" |
1119 | } |
1120 | anEvent->recurrence()->addMonthlyPos( tmpPos, qba ); |
1121 | qba.detach(); |
1122 | qba.fill( false ); // clear out |
1123 | } // while != "#" |
1124 | } |
1125 | break; |
1126 | } |
1127 | |
1128 | case Recurrence::rMonthlyDay: |
1129 | anEvent->recurrence()->setMonthly( rFreq ); |
1130 | if( index == last ) { |
1131 | // e.g. MD1 #0 |
1132 | short tmpDay = anEvent->dtStart().date().day(); |
1133 | anEvent->recurrence()->addMonthlyDate( tmpDay ); |
1134 | } else { |
1135 | // e.g. MD1 3 #0 |
1136 | while ( index < last ) { |
1137 | int index2 = tmpStr.indexOf( ' ', index ); |
1138 | short tmpDay = tmpStr.mid( index, ( index2 - index ) ).toShort(); |
1139 | index = index2 - 1; |
1140 | if ( tmpStr.mid( index, 1 ) == "-" ) { |
1141 | tmpDay = 0 - tmpDay; |
1142 | } |
1143 | index += 2; // advance the index; |
1144 | anEvent->recurrence()->addMonthlyDate( tmpDay ); |
1145 | } // while != # |
1146 | } |
1147 | break; |
1148 | |
1149 | case Recurrence::rYearlyMonth: |
1150 | anEvent->recurrence()->setYearly( rFreq ); |
1151 | |
1152 | if ( index == last ) { |
1153 | // e.g. YM1 #0 |
1154 | short tmpMonth = anEvent->dtStart().date().month(); |
1155 | anEvent->recurrence()->addYearlyMonth( tmpMonth ); |
1156 | } else { |
1157 | // e.g. YM1 3 #0 |
1158 | while ( index < last ) { |
1159 | int index2 = tmpStr.indexOf( ' ', index ); |
1160 | short tmpMonth = tmpStr.mid( index, ( index2 - index ) ).toShort(); |
1161 | index = index2 + 1; |
1162 | anEvent->recurrence()->addYearlyMonth( tmpMonth ); |
1163 | } // while != # |
1164 | } |
1165 | break; |
1166 | |
1167 | case Recurrence::rYearlyDay: |
1168 | anEvent->recurrence()->setYearly( rFreq ); |
1169 | |
1170 | if ( index == last ) { |
1171 | // e.g. YD1 #0 |
1172 | short tmpDay = anEvent->dtStart().date().dayOfYear(); |
1173 | anEvent->recurrence()->addYearlyDay( tmpDay ); |
1174 | } else { |
1175 | // e.g. YD1 123 #0 |
1176 | while ( index < last ) { |
1177 | int index2 = tmpStr.indexOf( ' ', index ); |
1178 | short tmpDay = tmpStr.mid( index, ( index2 - index ) ).toShort(); |
1179 | index = index2 + 1; |
1180 | anEvent->recurrence()->addYearlyDay( tmpDay ); |
1181 | } // while != # |
1182 | } |
1183 | break; |
1184 | |
1185 | default: |
1186 | break; |
1187 | } |
1188 | |
1189 | // find the last field, which is either the duration or the end date |
1190 | index = last; |
1191 | if ( tmpStr.mid( index, 1 ) == "#" ) { |
1192 | // Nr of occurrences |
1193 | index++; |
1194 | int rDuration = tmpStr.mid( index, tmpStr.length() - index ).toInt(); |
1195 | if ( rDuration > 0 ) { |
1196 | anEvent->recurrence()->setDuration( rDuration ); |
1197 | } |
1198 | } else if ( tmpStr.indexOf( 'T', index ) != -1 ) { |
1199 | KDateTime rEndDate = ISOToKDateTime( tmpStr.mid( index, tmpStr.length() - index ) ); |
1200 | rEndDate.setDateOnly( true ); |
1201 | anEvent->recurrence()->setEndDateTime( rEndDate ); |
1202 | } |
1203 | // anEvent->recurrence()->dump(); |
1204 | |
1205 | } else { |
1206 | kDebug() << "we don't understand this type of recurrence!" ; |
1207 | } // if known recurrence type |
1208 | } // repeats |
1209 | |
1210 | // recurrence exceptions |
1211 | if ( ( vo = isAPropertyOf( vevent, VCExpDateProp ) ) != 0 ) { |
1212 | s = fakeCString( vObjectUStringZValue( vo ) ); |
1213 | QStringList exDates = QString::fromLocal8Bit( s ).split( ',' ); |
1214 | QStringList::ConstIterator it; |
1215 | for ( it = exDates.constBegin(); it != exDates.constEnd(); ++it ) { |
1216 | anEvent->recurrence()->addExDate( ISOToQDate(*it) ); |
1217 | } |
1218 | deleteStr( s ); |
1219 | } |
1220 | |
1221 | // summary |
1222 | if ( ( vo = isAPropertyOf( vevent, VCSummaryProp ) ) ) { |
1223 | s = fakeCString( vObjectUStringZValue( vo ) ); |
1224 | anEvent->setSummary( QString::fromLocal8Bit( s ), Qt::mightBeRichText( s ) ); |
1225 | deleteStr( s ); |
1226 | } |
1227 | |
1228 | // description |
1229 | if ( ( vo = isAPropertyOf( vevent, VCDescriptionProp ) ) != 0 ) { |
1230 | s = fakeCString( vObjectUStringZValue( vo ) ); |
1231 | bool isRich = Qt::mightBeRichText( s ); |
1232 | if ( !anEvent->description().isEmpty() ) { |
1233 | anEvent->setDescription( |
1234 | anEvent->description() + '\n' + QString::fromLocal8Bit( s ), isRich ); |
1235 | } else { |
1236 | anEvent->setDescription( QString::fromLocal8Bit( s ), isRich ); |
1237 | } |
1238 | deleteStr( s ); |
1239 | } |
1240 | |
1241 | // location |
1242 | if ( ( vo = isAPropertyOf( vevent, VCLocationProp ) ) != 0 ) { |
1243 | s = fakeCString( vObjectUStringZValue( vo ) ); |
1244 | anEvent->setLocation( QString::fromLocal8Bit( s ), Qt::mightBeRichText( s ) ); |
1245 | deleteStr( s ); |
1246 | } |
1247 | |
1248 | // some stupid vCal exporters ignore the standard and use Description |
1249 | // instead of Summary for the default field. Correct for this. |
1250 | if ( anEvent->summary().isEmpty() && !( anEvent->description().isEmpty() ) ) { |
1251 | QString tmpStr = anEvent->description().simplified(); |
1252 | anEvent->setDescription( "" ); |
1253 | anEvent->setSummary( tmpStr ); |
1254 | } |
1255 | |
1256 | #if 0 |
1257 | // status |
1258 | if ( ( vo = isAPropertyOf( vevent, VCStatusProp ) ) != 0 ) { |
1259 | QString tmpStr( s = fakeCString( vObjectUStringZValue( vo ) ) ); |
1260 | deleteStr( s ); |
1261 | // TODO: Define Event status |
1262 | // anEvent->setStatus( tmpStr ); |
1263 | } else { |
1264 | // anEvent->setStatus( "NEEDS ACTION" ); |
1265 | } |
1266 | #endif |
1267 | |
1268 | // secrecy |
1269 | Incidence::Secrecy secrecy = Incidence::SecrecyPublic; |
1270 | if ( ( vo = isAPropertyOf( vevent, VCClassProp ) ) != 0 ) { |
1271 | s = fakeCString( vObjectUStringZValue( vo ) ); |
1272 | if ( s && strcmp( s, "PRIVATE" ) == 0 ) { |
1273 | secrecy = Incidence::SecrecyPrivate; |
1274 | } else if ( s && strcmp( s, "CONFIDENTIAL" ) == 0 ) { |
1275 | secrecy = Incidence::SecrecyConfidential; |
1276 | } |
1277 | deleteStr( s ); |
1278 | } |
1279 | anEvent->setSecrecy( secrecy ); |
1280 | |
1281 | // categories |
1282 | if ( ( vo = isAPropertyOf( vevent, VCCategoriesProp ) ) != 0 ) { |
1283 | s = fakeCString( vObjectUStringZValue( vo ) ); |
1284 | QString categories = QString::fromLocal8Bit( s ); |
1285 | deleteStr( s ); |
1286 | QStringList tmpStrList = categories.split( ',' ); |
1287 | anEvent->setCategories( tmpStrList ); |
1288 | } |
1289 | |
1290 | // attachments |
1291 | initPropIterator( &voi, vevent ); |
1292 | while ( moreIteration( &voi ) ) { |
1293 | vo = nextVObject( &voi ); |
1294 | if ( strcmp( vObjectName( vo ), VCAttachProp ) == 0 ) { |
1295 | s = fakeCString( vObjectUStringZValue( vo ) ); |
1296 | anEvent->addAttachment( new Attachment( QString( s ) ) ); |
1297 | deleteStr( s ); |
1298 | } |
1299 | } |
1300 | |
1301 | // resources |
1302 | if ( ( vo = isAPropertyOf( vevent, VCResourcesProp ) ) != 0 ) { |
1303 | QString resources = ( s = fakeCString( vObjectUStringZValue( vo ) ) ); |
1304 | deleteStr( s ); |
1305 | QStringList tmpStrList = resources.split( ';' ); |
1306 | anEvent->setResources( tmpStrList ); |
1307 | } |
1308 | |
1309 | // alarm stuff |
1310 | if ( ( vo = isAPropertyOf( vevent, VCDAlarmProp ) ) ) { |
1311 | Alarm *alarm = anEvent->newAlarm(); |
1312 | VObject *a; |
1313 | if ( ( a = isAPropertyOf( vo, VCRunTimeProp ) ) ) { |
1314 | alarm->setTime( ISOToKDateTime( s = fakeCString( vObjectUStringZValue( a ) ) ) ); |
1315 | deleteStr( s ); |
1316 | } |
1317 | alarm->setEnabled( true ); |
1318 | if ( ( vo = isAPropertyOf( vevent, VCPAlarmProp ) ) ) { |
1319 | if ( ( a = isAPropertyOf( vo, VCProcedureNameProp ) ) ) { |
1320 | s = fakeCString( vObjectUStringZValue( a ) ); |
1321 | alarm->setProcedureAlarm( QFile::decodeName( s ) ); |
1322 | deleteStr( s ); |
1323 | } |
1324 | } |
1325 | if ( ( vo = isAPropertyOf( vevent, VCAAlarmProp ) ) ) { |
1326 | if ( ( a = isAPropertyOf( vo, VCAudioContentProp ) ) ) { |
1327 | s = fakeCString( vObjectUStringZValue( a ) ); |
1328 | alarm->setAudioAlarm( QFile::decodeName( s ) ); |
1329 | deleteStr( s ); |
1330 | } |
1331 | } |
1332 | } |
1333 | |
1334 | // priority |
1335 | if ( ( vo = isAPropertyOf( vevent, VCPriorityProp ) ) ) { |
1336 | s = fakeCString( vObjectUStringZValue( vo ) ); |
1337 | if ( s ) { |
1338 | anEvent->setPriority( atoi( s ) ); |
1339 | deleteStr( s ); |
1340 | } |
1341 | } |
1342 | |
1343 | // transparency |
1344 | if ( ( vo = isAPropertyOf( vevent, VCTranspProp ) ) != 0 ) { |
1345 | s = fakeCString( vObjectUStringZValue( vo ) ); |
1346 | if ( s ) { |
1347 | int i = atoi( s ); |
1348 | anEvent->setTransparency( i == 1 ? Event::Transparent : Event::Opaque ); |
1349 | deleteStr( s ); |
1350 | } |
1351 | } |
1352 | |
1353 | // related event |
1354 | if ( ( vo = isAPropertyOf( vevent, VCRelatedToProp ) ) != 0 ) { |
1355 | anEvent->setRelatedToUid( s = fakeCString( vObjectUStringZValue( vo ) ) ); |
1356 | deleteStr( s ); |
1357 | d->mEventsRelate.append( anEvent ); |
1358 | } |
1359 | |
1360 | /* PILOT SYNC STUFF */ |
1361 | if ( ( vo = isAPropertyOf( vevent, KPilotIdProp ) ) ) { |
1362 | anEvent->setNonKDECustomProperty( |
1363 | KPilotIdProp, QString::fromLocal8Bit( s = fakeCString( vObjectUStringZValue( vo ) ) ) ); |
1364 | deleteStr( s ); |
1365 | if ( ( vo = isAPropertyOf( vevent, KPilotStatusProp ) ) ) { |
1366 | anEvent->setNonKDECustomProperty( |
1367 | KPilotStatusProp, QString::fromLocal8Bit( s = fakeCString( vObjectUStringZValue( vo ) ) ) ); |
1368 | deleteStr( s ); |
1369 | } else { |
1370 | anEvent->setNonKDECustomProperty( KPilotStatusProp, QString::number( SYNCMOD ) ); |
1371 | } |
1372 | } |
1373 | |
1374 | return anEvent; |
1375 | } |
1376 | |
1377 | QString VCalFormat::qDateToISO( const QDate &qd ) |
1378 | { |
1379 | QString tmpStr; |
1380 | |
1381 | if ( !qd.isValid() ) { |
1382 | return QString(); |
1383 | } |
1384 | |
1385 | tmpStr.sprintf( "%.2d%.2d%.2d" , qd.year(), qd.month(), qd.day() ); |
1386 | return tmpStr; |
1387 | |
1388 | } |
1389 | |
1390 | QString VCalFormat::kDateTimeToISO( const KDateTime &dt, bool zulu ) |
1391 | { |
1392 | QString tmpStr; |
1393 | |
1394 | if ( !dt.isValid() ) { |
1395 | return QString(); |
1396 | } |
1397 | |
1398 | QDateTime tmpDT; |
1399 | if ( zulu ) { |
1400 | tmpDT = dt.toUtc().dateTime(); |
1401 | } else { |
1402 | tmpDT = dt.toTimeSpec( d->mCalendar->timeSpec() ).dateTime(); |
1403 | } |
1404 | tmpStr.sprintf( "%.2d%.2d%.2dT%.2d%.2d%.2d" , |
1405 | tmpDT.date().year(), tmpDT.date().month(), |
1406 | tmpDT.date().day(), tmpDT.time().hour(), |
1407 | tmpDT.time().minute(), tmpDT.time().second() ); |
1408 | if ( zulu ) { |
1409 | tmpStr += 'Z'; |
1410 | } |
1411 | return tmpStr; |
1412 | } |
1413 | |
1414 | KDateTime VCalFormat::ISOToKDateTime( const QString &dtStr ) |
1415 | { |
1416 | QDate tmpDate; |
1417 | QTime tmpTime; |
1418 | QString tmpStr; |
1419 | int year, month, day, hour, minute, second; |
1420 | |
1421 | tmpStr = dtStr; |
1422 | year = tmpStr.left( 4 ).toInt(); |
1423 | month = tmpStr.mid( 4, 2 ).toInt(); |
1424 | day = tmpStr.mid( 6, 2 ).toInt(); |
1425 | hour = tmpStr.mid( 9, 2 ).toInt(); |
1426 | minute = tmpStr.mid( 11, 2 ).toInt(); |
1427 | second = tmpStr.mid( 13, 2 ).toInt(); |
1428 | tmpDate.setYMD( year, month, day ); |
1429 | tmpTime.setHMS( hour, minute, second ); |
1430 | |
1431 | if ( tmpDate.isValid() && tmpTime.isValid() ) { |
1432 | // correct for GMT if string is in Zulu format |
1433 | if ( dtStr.at( dtStr.length() - 1 ) == 'Z' ) { |
1434 | return KDateTime( tmpDate, tmpTime, KDateTime::UTC ); |
1435 | } else { |
1436 | return KDateTime( tmpDate, tmpTime, d->mCalendar->timeSpec() ); |
1437 | } |
1438 | } else { |
1439 | return KDateTime(); |
1440 | } |
1441 | } |
1442 | |
1443 | QDate VCalFormat::ISOToQDate( const QString &dateStr ) |
1444 | { |
1445 | int year, month, day; |
1446 | |
1447 | year = dateStr.left( 4 ).toInt(); |
1448 | month = dateStr.mid( 4, 2 ).toInt(); |
1449 | day = dateStr.mid( 6, 2 ).toInt(); |
1450 | |
1451 | return QDate( year, month, day ); |
1452 | } |
1453 | |
1454 | // take a raw vcalendar (i.e. from a file on disk, clipboard, etc. etc. |
1455 | // and break it down from it's tree-like format into the dictionary format |
1456 | // that is used internally in the VCalFormat. |
1457 | void VCalFormat::populate( VObject *vcal ) |
1458 | { |
1459 | // this function will populate the caldict dictionary and other event |
1460 | // lists. It turns vevents into Events and then inserts them. |
1461 | |
1462 | VObjectIterator i; |
1463 | VObject *curVO, *curVOProp; |
1464 | Event *anEvent; |
1465 | |
1466 | if ( ( curVO = isAPropertyOf( vcal, ICMethodProp ) ) != 0 ) { |
1467 | char *methodType = 0; |
1468 | methodType = fakeCString( vObjectUStringZValue( curVO ) ); |
1469 | kDebug() << "This calendar is an iTIP transaction of type '" |
1470 | << methodType << "'" ; |
1471 | deleteStr( methodType ); |
1472 | } |
1473 | |
1474 | // warn the user that we might have trouble reading non-known calendar. |
1475 | if ( ( curVO = isAPropertyOf( vcal, VCProdIdProp ) ) != 0 ) { |
1476 | char *s = fakeCString( vObjectUStringZValue( curVO ) ); |
1477 | if ( !s || strcmp( productId().toLocal8Bit(), s ) != 0 ) { |
1478 | kDebug() << "This vCalendar file was not created by KOrganizer or" |
1479 | << "any other product we support. Loading anyway..." ; |
1480 | } |
1481 | setLoadedProductId( s ); |
1482 | deleteStr( s ); |
1483 | } |
1484 | |
1485 | // warn the user we might have trouble reading this unknown version. |
1486 | if ( ( curVO = isAPropertyOf( vcal, VCVersionProp ) ) != 0 ) { |
1487 | char *s = fakeCString( vObjectUStringZValue( curVO ) ); |
1488 | if ( !s || strcmp( _VCAL_VERSION, s ) != 0 ) { |
1489 | kDebug() << "This vCalendar file has version" << s |
1490 | << "We only support" << _VCAL_VERSION; |
1491 | } |
1492 | deleteStr( s ); |
1493 | } |
1494 | |
1495 | #if 0 |
1496 | // set the time zone (this is a property of the view, so just discard!) |
1497 | if ( ( curVO = isAPropertyOf( vcal, VCTimeZoneProp ) ) != 0 ) { |
1498 | char *s = fakeCString( vObjectUStringZValue( curVO ) ); |
1499 | d->mCalendar->setTimeZone( s ); |
1500 | deleteStr( s ); |
1501 | } |
1502 | #endif |
1503 | |
1504 | // Store all events with a relatedTo property in a list for post-processing |
1505 | d->mEventsRelate.clear(); |
1506 | d->mTodosRelate.clear(); |
1507 | |
1508 | initPropIterator( &i, vcal ); |
1509 | |
1510 | // go through all the vobjects in the vcal |
1511 | while ( moreIteration( &i ) ) { |
1512 | curVO = nextVObject( &i ); |
1513 | |
1514 | /************************************************************************/ |
1515 | |
1516 | // now, check to see that the object is an event or todo. |
1517 | if ( strcmp( vObjectName( curVO ), VCEventProp ) == 0 ) { |
1518 | |
1519 | if ( ( curVOProp = isAPropertyOf( curVO, KPilotStatusProp ) ) != 0 ) { |
1520 | char *s; |
1521 | s = fakeCString( vObjectUStringZValue( curVOProp ) ); |
1522 | // check to see if event was deleted by the kpilot conduit |
1523 | if ( s ) { |
1524 | if ( atoi( s ) == SYNCDEL ) { |
1525 | deleteStr( s ); |
1526 | kDebug() << "skipping pilot-deleted event" ; |
1527 | goto SKIP; |
1528 | } |
1529 | deleteStr( s ); |
1530 | } |
1531 | } |
1532 | |
1533 | // this code checks to see if we are trying to read in an event |
1534 | // that we already find to be in the calendar. If we find this |
1535 | // to be the case, we skip the event. |
1536 | if ( ( curVOProp = isAPropertyOf( curVO, VCUniqueStringProp ) ) != 0 ) { |
1537 | char *s = fakeCString( vObjectUStringZValue( curVOProp ) ); |
1538 | QString tmpStr( s ); |
1539 | deleteStr( s ); |
1540 | |
1541 | if ( d->mCalendar->incidence( tmpStr ) ) { |
1542 | goto SKIP; |
1543 | } |
1544 | } |
1545 | |
1546 | if ( ( !( curVOProp = isAPropertyOf( curVO, VCDTstartProp ) ) ) && |
1547 | ( !( curVOProp = isAPropertyOf( curVO, VCDTendProp ) ) ) ) { |
1548 | kDebug() << "found a VEvent with no DTSTART and no DTEND! Skipping..." ; |
1549 | goto SKIP; |
1550 | } |
1551 | |
1552 | anEvent = VEventToEvent( curVO ); |
1553 | // we now use addEvent instead of insertEvent so that the |
1554 | // signal/slot get connected. |
1555 | if ( anEvent ) { |
1556 | if ( anEvent->dtStart().isValid() && anEvent->dtEnd().isValid() ) { |
1557 | d->mCalendar->addEvent( anEvent ); |
1558 | } |
1559 | } else { |
1560 | // some sort of error must have occurred while in translation. |
1561 | goto SKIP; |
1562 | } |
1563 | } else if ( strcmp( vObjectName( curVO ), VCTodoProp ) == 0 ) { |
1564 | Todo *aTodo = VTodoToEvent( curVO ); |
1565 | |
1566 | Todo *old = d->mCalendar->todo( aTodo->uid() ); |
1567 | if ( old ) { |
1568 | d->mCalendar->deleteTodo( old ); |
1569 | d->mTodosRelate.removeAll( old ); |
1570 | } |
1571 | |
1572 | d->mCalendar->addTodo( aTodo ); |
1573 | } else if ( ( strcmp( vObjectName( curVO ), VCVersionProp ) == 0 ) || |
1574 | ( strcmp( vObjectName( curVO ), VCProdIdProp ) == 0 ) || |
1575 | ( strcmp( vObjectName( curVO ), VCTimeZoneProp ) == 0 ) ) { |
1576 | // do nothing, we know these properties and we want to skip them. |
1577 | // we have either already processed them or are ignoring them. |
1578 | ; |
1579 | } else { |
1580 | kDebug() << "Ignoring unknown vObject \"" << vObjectName(curVO) << "\"" ; |
1581 | } |
1582 | SKIP: |
1583 | ; |
1584 | } // while |
1585 | |
1586 | // Post-Process list of events with relations, put Event objects in relation |
1587 | Event::List::ConstIterator eIt; |
1588 | for ( eIt = d->mEventsRelate.constBegin(); eIt != d->mEventsRelate.constEnd(); ++eIt ) { |
1589 | (*eIt)->setRelatedTo( d->mCalendar->incidence( (*eIt)->relatedToUid() ) ); |
1590 | } |
1591 | Todo::List::ConstIterator tIt; |
1592 | for ( tIt = d->mTodosRelate.constBegin(); tIt != d->mTodosRelate.constEnd(); ++tIt ) { |
1593 | (*tIt)->setRelatedTo( d->mCalendar->incidence( (*tIt)->relatedToUid() ) ); |
1594 | } |
1595 | } |
1596 | |
1597 | const char *VCalFormat::dayFromNum( int day ) |
1598 | { |
1599 | const char *days[7] = { "MO " , "TU " , "WE " , "TH " , "FR " , "SA " , "SU " }; |
1600 | |
1601 | return days[day]; |
1602 | } |
1603 | |
1604 | int VCalFormat::numFromDay( const QString &day ) |
1605 | { |
1606 | if ( day == "MO " ) { |
1607 | return 0; |
1608 | } |
1609 | if ( day == "TU " ) { |
1610 | return 1; |
1611 | } |
1612 | if ( day == "WE " ) { |
1613 | return 2; |
1614 | } |
1615 | if ( day == "TH " ) { |
1616 | return 3; |
1617 | } |
1618 | if ( day == "FR " ) { |
1619 | return 4; |
1620 | } |
1621 | if ( day == "SA " ) { |
1622 | return 5; |
1623 | } |
1624 | if ( day == "SU " ) { |
1625 | return 6; |
1626 | } |
1627 | |
1628 | return -1; // something bad happened. :) |
1629 | } |
1630 | |
1631 | Attendee::PartStat VCalFormat::readStatus( const char *s ) const |
1632 | { |
1633 | QString statStr = s; |
1634 | statStr = statStr.toUpper(); |
1635 | Attendee::PartStat status; |
1636 | |
1637 | if ( statStr == "X-ACTION" ) { |
1638 | status = Attendee::NeedsAction; |
1639 | } else if ( statStr == "NEEDS ACTION" ) { |
1640 | status = Attendee::NeedsAction; |
1641 | } else if ( statStr == "ACCEPTED" ) { |
1642 | status = Attendee::Accepted; |
1643 | } else if ( statStr == "SENT" ) { |
1644 | status = Attendee::NeedsAction; |
1645 | } else if ( statStr == "TENTATIVE" ) { |
1646 | status = Attendee::Tentative; |
1647 | } else if ( statStr == "CONFIRMED" ) { |
1648 | status = Attendee::Accepted; |
1649 | } else if ( statStr == "DECLINED" ) { |
1650 | status = Attendee::Declined; |
1651 | } else if ( statStr == "COMPLETED" ) { |
1652 | status = Attendee::Completed; |
1653 | } else if ( statStr == "DELEGATED" ) { |
1654 | status = Attendee::Delegated; |
1655 | } else { |
1656 | kDebug() << "error setting attendee mStatus, unknown mStatus!" ; |
1657 | status = Attendee::NeedsAction; |
1658 | } |
1659 | |
1660 | return status; |
1661 | } |
1662 | |
1663 | QByteArray VCalFormat::writeStatus( Attendee::PartStat status ) const |
1664 | { |
1665 | switch( status ) { |
1666 | default: |
1667 | case Attendee::NeedsAction: |
1668 | return "NEEDS ACTION" ; |
1669 | break; |
1670 | case Attendee::Accepted: |
1671 | return "ACCEPTED" ; |
1672 | break; |
1673 | case Attendee::Declined: |
1674 | return "DECLINED" ; |
1675 | break; |
1676 | case Attendee::Tentative: |
1677 | return "TENTATIVE" ; |
1678 | break; |
1679 | case Attendee::Delegated: |
1680 | return "DELEGATED" ; |
1681 | break; |
1682 | case Attendee::Completed: |
1683 | return "COMPLETED" ; |
1684 | break; |
1685 | case Attendee::InProcess: |
1686 | return "NEEDS ACTION" ; |
1687 | break; |
1688 | } |
1689 | } |
1690 | |