KCal Library
vcalformat.cpp
Go to the documentation of this file.
00001 /* 00002 This file is part of the kcal library. 00003 00004 Copyright (c) 1998 Preston Brown <pbrown@kde.org> 00005 Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org> 00006 00007 This library is free software; you can redistribute it and/or 00008 modify it under the terms of the GNU Library General Public 00009 License as published by the Free Software Foundation; either 00010 version 2 of the License, or (at your option) any later version. 00011 00012 This library is distributed in the hope that it will be useful, 00013 but WITHOUT ANY WARRANTY; without even the implied warranty of 00014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00015 Library General Public License for more details. 00016 00017 You should have received a copy of the GNU Library General Public License 00018 along with this library; see the file COPYING.LIB. If not, write to 00019 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00020 Boston, MA 02110-1301, USA. 00021 */ 00038 #include "vcalformat.h" 00039 #include "calendar.h" 00040 #include "versit/vcc.h" 00041 #include "versit/vobject.h" 00042 00043 #include <kdebug.h> 00044 #include <kdatetime.h> 00045 #include <klocale.h> 00046 00047 #include <QtCore/QString> 00048 #include <QtCore/QRegExp> 00049 #include <QtCore/QFile> 00050 #include <QtCore/QByteArray> 00051 #include <QtGui/QTextDocument> 00052 00053 using namespace KCal; 00054 00059 //@cond PRIVATE 00060 class KCal::VCalFormat::Private 00061 { 00062 public: 00063 Calendar *mCalendar; 00064 Event::List mEventsRelate; // Events with relations 00065 Todo::List mTodosRelate; // To-dos with relations 00066 }; 00067 //@endcond 00068 00069 VCalFormat::VCalFormat() : d( new KCal::VCalFormat::Private ) 00070 { 00071 } 00072 00073 VCalFormat::~VCalFormat() 00074 { 00075 delete d; 00076 } 00077 00078 bool VCalFormat::load( Calendar *calendar, const QString &fileName ) 00079 { 00080 d->mCalendar = calendar; 00081 00082 clearException(); 00083 00084 kDebug() << fileName; 00085 00086 VObject *vcal = 0; 00087 00088 // this is not necessarily only 1 vcal. Could be many vcals, or include 00089 // a vcard... 00090 vcal = Parse_MIME_FromFileName( const_cast<char *>( QFile::encodeName( fileName ).data() ) ); 00091 00092 if ( !vcal ) { 00093 setException( new ErrorFormat( ErrorFormat::CalVersionUnknown ) ); 00094 return false; 00095 } 00096 00097 // any other top-level calendar stuff should be added/initialized here 00098 00099 // put all vobjects into their proper places 00100 populate( vcal ); 00101 00102 // clean up from vcal API stuff 00103 cleanVObjects( vcal ); 00104 cleanStrTbl(); 00105 00106 return true; 00107 } 00108 00109 bool VCalFormat::save( Calendar *calendar, const QString &fileName ) 00110 { 00111 d->mCalendar = calendar; 00112 00113 QString tmpStr; 00114 VObject *vcal, *vo; 00115 00116 kDebug() << fileName; 00117 00118 vcal = newVObject( VCCalProp ); 00119 00120 // addPropValue(vcal,VCLocationProp, "0.0"); 00121 addPropValue( vcal, VCProdIdProp, productId().toLatin1() ); 00122 addPropValue( vcal, VCVersionProp, _VCAL_VERSION ); 00123 00124 // TODO STUFF 00125 Todo::List todoList = d->mCalendar->rawTodos(); 00126 Todo::List::ConstIterator it; 00127 for ( it = todoList.constBegin(); it != todoList.constEnd(); ++it ) { 00128 vo = eventToVTodo( *it ); 00129 addVObjectProp( vcal, vo ); 00130 } 00131 00132 // EVENT STUFF 00133 Event::List events = d->mCalendar->rawEvents(); 00134 Event::List::ConstIterator it2; 00135 for ( it2 = events.constBegin(); it2 != events.constEnd(); ++it2 ) { 00136 vo = eventToVEvent( *it2 ); 00137 addVObjectProp( vcal, vo ); 00138 } 00139 00140 writeVObjectToFile( QFile::encodeName( fileName ).data(), vcal ); 00141 cleanVObjects( vcal ); 00142 cleanStrTbl(); 00143 00144 if ( QFile::exists( fileName ) ) { 00145 return true; 00146 } else { 00147 return false; // error 00148 } 00149 00150 return false; 00151 } 00152 00153 bool VCalFormat::fromString( Calendar *calendar, const QString &string ) 00154 { 00155 return fromRawString( calendar, string.toUtf8() ); 00156 } 00157 00158 bool VCalFormat::fromRawString( Calendar *calendar, const QByteArray &string ) 00159 { 00160 d->mCalendar = calendar; 00161 00162 if ( !string.size() ) { 00163 return false; 00164 } 00165 00166 VObject *vcal = Parse_MIME( string.data(), string.size() ); 00167 if ( !vcal ) { 00168 return false; 00169 } 00170 00171 VObjectIterator i; 00172 VObject *curvo; 00173 initPropIterator( &i, vcal ); 00174 00175 // we only take the first object. TODO: parse all incidences. 00176 do { 00177 curvo = nextVObject( &i ); 00178 } while ( strcmp( vObjectName( curvo ), VCEventProp ) && 00179 strcmp( vObjectName( curvo ), VCTodoProp ) ); 00180 00181 if ( strcmp( vObjectName( curvo ), VCEventProp ) == 0 ) { 00182 Event *event = VEventToEvent( curvo ); 00183 calendar->addEvent( event ); 00184 } else { 00185 kDebug() << "Unknown object type."; 00186 deleteVObject( vcal ); 00187 return false; 00188 } 00189 00190 deleteVObject( vcal ); 00191 00192 return true; 00193 } 00194 00195 QString VCalFormat::toString( Calendar *calendar ) 00196 { 00197 // TODO: Factor out VCalFormat::asString() 00198 d->mCalendar = calendar; 00199 00200 VObject *vcal = newVObject( VCCalProp ); 00201 00202 addPropValue( vcal, VCProdIdProp, CalFormat::productId().toLatin1() ); 00203 addPropValue( vcal, VCVersionProp, _VCAL_VERSION ); 00204 00205 // TODO: Use all data. 00206 Event::List events = calendar->events(); 00207 if( events.isEmpty() ) { 00208 cleanVObject ( vcal ); 00209 return QString(); 00210 } 00211 Event *event = events.first(); 00212 if ( !event ) { 00213 cleanVObject ( vcal ); 00214 return QString(); 00215 } 00216 VObject *vevent = eventToVEvent( event ); 00217 00218 addVObjectProp( vcal, vevent ); 00219 00220 char *buf = writeMemVObject( 0, 0, vcal ); 00221 00222 QString result( buf ); 00223 00224 cleanVObject( vcal ); 00225 00226 return result; 00227 } 00228 00229 VObject *VCalFormat::eventToVTodo( const Todo *anEvent ) 00230 { 00231 VObject *vtodo; 00232 QString tmpStr; 00233 00234 vtodo = newVObject( VCTodoProp ); 00235 00236 // due date 00237 if ( anEvent->hasDueDate() ) { 00238 tmpStr = kDateTimeToISO( anEvent->dtDue(), !anEvent->allDay() ); 00239 addPropValue( vtodo, VCDueProp, tmpStr.toLocal8Bit() ); 00240 } 00241 00242 // start date 00243 if ( anEvent->hasStartDate() ) { 00244 tmpStr = kDateTimeToISO( anEvent->dtStart(), !anEvent->allDay() ); 00245 addPropValue( vtodo, VCDTstartProp, tmpStr.toLocal8Bit() ); 00246 } 00247 00248 // creation date 00249 tmpStr = kDateTimeToISO( anEvent->created() ); 00250 addPropValue( vtodo, VCDCreatedProp, tmpStr.toLocal8Bit() ); 00251 00252 // unique id 00253 addPropValue( vtodo, VCUniqueStringProp, 00254 anEvent->uid().toLocal8Bit() ); 00255 00256 // revision 00257 tmpStr.sprintf( "%i", anEvent->revision() ); 00258 addPropValue( vtodo, VCSequenceProp, tmpStr.toLocal8Bit() ); 00259 00260 // last modification date 00261 tmpStr = kDateTimeToISO( anEvent->lastModified() ); 00262 addPropValue( vtodo, VCLastModifiedProp, tmpStr.toLocal8Bit() ); 00263 00264 // organizer stuff 00265 // @TODO: How about the common name? 00266 if ( !anEvent->organizer().email().isEmpty() ) { 00267 tmpStr = "MAILTO:" + anEvent->organizer().email(); 00268 addPropValue( vtodo, ICOrganizerProp, tmpStr.toLocal8Bit() ); 00269 } 00270 00271 // attendees 00272 if ( anEvent->attendeeCount() > 0 ) { 00273 Attendee::List::ConstIterator it; 00274 Attendee *curAttendee; 00275 for ( it = anEvent->attendees().begin(); it != anEvent->attendees().end(); 00276 ++it ) { 00277 curAttendee = *it; 00278 if ( !curAttendee->email().isEmpty() && 00279 !curAttendee->name().isEmpty() ) { 00280 tmpStr = "MAILTO:" + curAttendee->name() + " <" + curAttendee->email() + '>'; 00281 } else if ( curAttendee->name().isEmpty() ) { 00282 tmpStr = "MAILTO: " + curAttendee->email(); 00283 } else if ( curAttendee->email().isEmpty() ) { 00284 tmpStr = "MAILTO: " + curAttendee->name(); 00285 } else if ( curAttendee->name().isEmpty() && curAttendee->email().isEmpty() ) { 00286 kDebug() << "warning! this Event has an attendee w/o name or email!"; 00287 } 00288 VObject *aProp = addPropValue( vtodo, VCAttendeeProp, tmpStr.toLocal8Bit() ); 00289 addPropValue( aProp, VCRSVPProp, curAttendee->RSVP() ? "TRUE" : "FALSE" ); 00290 addPropValue( aProp, VCStatusProp, writeStatus( curAttendee->status() ) ); 00291 } 00292 } 00293 00294 // description BL: 00295 if ( !anEvent->description().isEmpty() ) { 00296 VObject *dObject = addPropValue( vtodo, VCDescriptionProp, 00297 anEvent->description().toLocal8Bit() ); 00298 if ( anEvent->description().indexOf( '\n' ) != -1 ) { 00299 addPropValue( dObject, VCEncodingProp, VCQuotedPrintableProp ); 00300 } 00301 } 00302 00303 // summary 00304 if ( !anEvent->summary().isEmpty() ) { 00305 addPropValue( vtodo, VCSummaryProp, anEvent->summary().toLocal8Bit() ); 00306 } 00307 00308 // location 00309 if ( !anEvent->location().isEmpty() ) { 00310 addPropValue( vtodo, VCLocationProp, anEvent->location().toLocal8Bit() ); 00311 } 00312 00313 // completed status 00314 // backward compatibility, KOrganizer used to interpret only these two values 00315 addPropValue( vtodo, VCStatusProp, anEvent->isCompleted() ? "COMPLETED" : "NEEDS_ACTION" ); 00316 00317 // completion date 00318 if ( anEvent->hasCompletedDate() ) { 00319 tmpStr = kDateTimeToISO( anEvent->completed() ); 00320 addPropValue( vtodo, VCCompletedProp, tmpStr.toLocal8Bit() ); 00321 } 00322 00323 // priority 00324 tmpStr.sprintf( "%i", anEvent->priority() ); 00325 addPropValue( vtodo, VCPriorityProp, tmpStr.toLocal8Bit() ); 00326 00327 // related event 00328 if ( anEvent->relatedTo() ) { 00329 addPropValue( vtodo, VCRelatedToProp, 00330 anEvent->relatedTo()->uid().toLocal8Bit() ); 00331 } 00332 00333 // categories 00334 const QStringList tmpStrList = anEvent->categories(); 00335 tmpStr = ""; 00336 QString catStr; 00337 QStringList::const_iterator its; 00338 for ( its = tmpStrList.constBegin(); its != tmpStrList.constEnd(); ++its ) { 00339 catStr = *its; 00340 if ( catStr[0] == ' ' ) { 00341 tmpStr += catStr.mid( 1 ); 00342 } else { 00343 tmpStr += catStr; 00344 } 00345 // this must be a ';' character as the vCalendar specification requires! 00346 // vcc.y has been hacked to translate the ';' to a ',' when the vcal is 00347 // read in. 00348 tmpStr += ';'; 00349 } 00350 if ( !tmpStr.isEmpty() ) { 00351 tmpStr.truncate( tmpStr.length() - 1 ); 00352 addPropValue( vtodo, VCCategoriesProp, tmpStr.toLocal8Bit() ); 00353 } 00354 00355 // alarm stuff 00356 Alarm::List::ConstIterator it; 00357 for ( it = anEvent->alarms().begin(); it != anEvent->alarms().end(); ++it ) { 00358 Alarm *alarm = *it; 00359 if ( alarm->enabled() ) { 00360 VObject *a = addProp( vtodo, VCDAlarmProp ); 00361 tmpStr = kDateTimeToISO( alarm->time() ); 00362 addPropValue( a, VCRunTimeProp, tmpStr.toLocal8Bit() ); 00363 addPropValue( a, VCRepeatCountProp, "1" ); 00364 addPropValue( a, VCDisplayStringProp, "beep!" ); 00365 if ( alarm->type() == Alarm::Audio ) { 00366 a = addProp( vtodo, VCAAlarmProp ); 00367 addPropValue( a, VCRunTimeProp, tmpStr.toLocal8Bit() ); 00368 addPropValue( a, VCRepeatCountProp, "1" ); 00369 addPropValue( a, VCAudioContentProp, QFile::encodeName( alarm->audioFile() ) ); 00370 } else if ( alarm->type() == Alarm::Procedure ) { 00371 a = addProp( vtodo, VCPAlarmProp ); 00372 addPropValue( a, VCRunTimeProp, tmpStr.toLocal8Bit() ); 00373 addPropValue( a, VCRepeatCountProp, "1" ); 00374 addPropValue( a, VCProcedureNameProp, QFile::encodeName( alarm->programFile() ) ); 00375 } 00376 } 00377 } 00378 00379 QString pilotId = anEvent->nonKDECustomProperty( KPilotIdProp ); 00380 if ( !pilotId.isEmpty() ) { 00381 // pilot sync stuff 00382 addPropValue( vtodo, KPilotIdProp, pilotId.toLocal8Bit() ); 00383 addPropValue( vtodo, KPilotStatusProp, 00384 anEvent->nonKDECustomProperty( KPilotStatusProp ).toLocal8Bit() ); 00385 } 00386 00387 return vtodo; 00388 } 00389 00390 VObject *VCalFormat::eventToVEvent( const Event *anEvent ) 00391 { 00392 VObject *vevent; 00393 QString tmpStr; 00394 00395 vevent = newVObject( VCEventProp ); 00396 00397 // start and end time 00398 tmpStr = kDateTimeToISO( anEvent->dtStart(), !anEvent->allDay() ); 00399 addPropValue( vevent, VCDTstartProp, tmpStr.toLocal8Bit() ); 00400 00401 // events that have time associated but take up no time should 00402 // not have both DTSTART and DTEND. 00403 if ( anEvent->dtStart() != anEvent->dtEnd() ) { 00404 tmpStr = kDateTimeToISO( anEvent->dtEnd(), !anEvent->allDay() ); 00405 addPropValue( vevent, VCDTendProp, tmpStr.toLocal8Bit() ); 00406 } 00407 00408 // creation date 00409 tmpStr = kDateTimeToISO( anEvent->created() ); 00410 addPropValue( vevent, VCDCreatedProp, tmpStr.toLocal8Bit() ); 00411 00412 // unique id 00413 addPropValue( vevent, VCUniqueStringProp, 00414 anEvent->uid().toLocal8Bit() ); 00415 00416 // revision 00417 tmpStr.sprintf( "%i", anEvent->revision() ); 00418 addPropValue( vevent, VCSequenceProp, tmpStr.toLocal8Bit() ); 00419 00420 // last modification date 00421 tmpStr = kDateTimeToISO( anEvent->lastModified() ); 00422 addPropValue( vevent, VCLastModifiedProp, tmpStr.toLocal8Bit() ); 00423 00424 // attendee and organizer stuff 00425 // TODO: What to do with the common name? 00426 if ( !anEvent->organizer().email().isEmpty() ) { 00427 tmpStr = "MAILTO:" + anEvent->organizer().email(); 00428 addPropValue( vevent, ICOrganizerProp, tmpStr.toLocal8Bit() ); 00429 } 00430 00431 // TODO: Put this functionality into Attendee class 00432 if ( anEvent->attendeeCount() > 0 ) { 00433 Attendee::List::ConstIterator it; 00434 for ( it = anEvent->attendees().constBegin(); it != anEvent->attendees().constEnd(); 00435 ++it ) { 00436 Attendee *curAttendee = *it; 00437 if ( !curAttendee->email().isEmpty() && !curAttendee->name().isEmpty() ) { 00438 tmpStr = "MAILTO:" + curAttendee->name() + " <" + curAttendee->email() + '>'; 00439 } else if ( curAttendee->name().isEmpty() ) { 00440 tmpStr = "MAILTO: " + curAttendee->email(); 00441 } else if ( curAttendee->email().isEmpty() ) { 00442 tmpStr = "MAILTO: " + curAttendee->name(); 00443 } else if ( curAttendee->name().isEmpty() && curAttendee->email().isEmpty() ) { 00444 kDebug() << "warning! this Event has an attendee w/o name or email!"; 00445 } 00446 VObject *aProp = addPropValue( vevent, VCAttendeeProp, tmpStr.toLocal8Bit() ); 00447 addPropValue( aProp, VCRSVPProp, curAttendee->RSVP() ? "TRUE" : "FALSE" ); 00448 addPropValue( aProp, VCStatusProp, writeStatus( curAttendee->status() ) ); 00449 } 00450 } 00451 00452 // recurrence rule stuff 00453 const Recurrence *recur = anEvent->recurrence(); 00454 if ( recur->recurs() ) { 00455 bool validRecur = true; 00456 QString tmpStr2; 00457 switch ( recur->recurrenceType() ) { 00458 case Recurrence::rDaily: 00459 tmpStr.sprintf( "D%i ", recur->frequency() ); 00460 break; 00461 case Recurrence::rWeekly: 00462 tmpStr.sprintf( "W%i ", recur->frequency() ); 00463 for ( int i = 0; i < 7; ++i ) { 00464 QBitArray days ( recur->days() ); 00465 if ( days.testBit(i) ) { 00466 tmpStr += dayFromNum( i ); 00467 } 00468 } 00469 break; 00470 case Recurrence::rMonthlyPos: 00471 { 00472 tmpStr.sprintf( "MP%i ", recur->frequency() ); 00473 // write out all rMonthPos's 00474 QList<RecurrenceRule::WDayPos> tmpPositions = recur->monthPositions(); 00475 for ( QList<RecurrenceRule::WDayPos>::ConstIterator posit = tmpPositions.constBegin(); 00476 posit != tmpPositions.constEnd(); ++posit ) { 00477 int pos = (*posit).pos(); 00478 tmpStr2.sprintf( "%i", ( pos > 0 ) ? pos : (-pos) ); 00479 if ( pos < 0 ) { 00480 tmpStr2 += "- "; 00481 } else { 00482 tmpStr2 += "+ "; 00483 } 00484 tmpStr += tmpStr2; 00485 tmpStr += dayFromNum( (*posit).day() - 1 ); 00486 } 00487 break; 00488 } 00489 case Recurrence::rMonthlyDay: 00490 { 00491 tmpStr.sprintf( "MD%i ", recur->frequency() ); 00492 // write out all rMonthDays; 00493 const QList<int> tmpDays = recur->monthDays(); 00494 for ( QList<int>::ConstIterator tmpDay = tmpDays.constBegin(); 00495 tmpDay != tmpDays.constEnd(); ++tmpDay ) { 00496 tmpStr2.sprintf( "%i ", *tmpDay ); 00497 tmpStr += tmpStr2; 00498 } 00499 break; 00500 } 00501 case Recurrence::rYearlyMonth: 00502 { 00503 tmpStr.sprintf( "YM%i ", recur->frequency() ); 00504 // write out all the months;' 00505 // TODO: Any way to write out the day within the month??? 00506 const QList<int> months = recur->yearMonths(); 00507 for ( QList<int>::ConstIterator mit = months.constBegin(); 00508 mit != months.constEnd(); ++mit ) { 00509 tmpStr2.sprintf( "%i ", *mit ); 00510 tmpStr += tmpStr2; 00511 } 00512 break; 00513 } 00514 case Recurrence::rYearlyDay: 00515 { 00516 tmpStr.sprintf( "YD%i ", recur->frequency() ); 00517 // write out all the rYearNums; 00518 const QList<int> tmpDays = recur->yearDays(); 00519 for ( QList<int>::ConstIterator tmpDay = tmpDays.begin(); 00520 tmpDay != tmpDays.end(); ++tmpDay ) { 00521 tmpStr2.sprintf( "%i ", *tmpDay ); 00522 tmpStr += tmpStr2; 00523 } 00524 break; 00525 } 00526 default: 00527 // TODO: Write rYearlyPos and arbitrary rules! 00528 kDebug() << "ERROR, it should never get here in eventToVEvent!"; 00529 validRecur = false; 00530 break; 00531 } // switch 00532 00533 if ( recur->duration() > 0 ) { 00534 tmpStr2.sprintf( "#%i", recur->duration() ); 00535 tmpStr += tmpStr2; 00536 } else if ( recur->duration() == -1 ) { 00537 tmpStr += "#0"; // defined as repeat forever 00538 } else { 00539 tmpStr += kDateTimeToISO( recur->endDateTime(), false ); 00540 } 00541 // Only write out the rrule if we have a valid recurrence (i.e. a known 00542 // type in thee switch above) 00543 if ( validRecur ) { 00544 addPropValue( vevent, VCRRuleProp, tmpStr.toLocal8Bit() ); 00545 } 00546 00547 } // event repeats 00548 00549 // exceptions to recurrence 00550 DateList dateList = recur->exDates(); 00551 DateList::ConstIterator it; 00552 QString tmpStr2; 00553 00554 for ( it = dateList.constBegin(); it != dateList.constEnd(); ++it ) { 00555 tmpStr = qDateToISO(*it) + ';'; 00556 tmpStr2 += tmpStr; 00557 } 00558 if ( !tmpStr2.isEmpty() ) { 00559 tmpStr2.truncate( tmpStr2.length() - 1 ); 00560 addPropValue( vevent, VCExpDateProp, tmpStr2.toLocal8Bit() ); 00561 } 00562 00563 // description 00564 if ( !anEvent->description().isEmpty() ) { 00565 VObject *dObject = addPropValue( vevent, VCDescriptionProp, 00566 anEvent->description().toLocal8Bit() ); 00567 if ( anEvent->description().indexOf( '\n' ) != -1 ) { 00568 addPropValue( dObject, VCEncodingProp, VCQuotedPrintableProp ); 00569 } 00570 } 00571 00572 // summary 00573 if ( !anEvent->summary().isEmpty() ) { 00574 addPropValue( vevent, VCSummaryProp, anEvent->summary().toLocal8Bit() ); 00575 } 00576 00577 // location 00578 if ( !anEvent->location().isEmpty() ) { 00579 addPropValue( vevent, VCLocationProp, anEvent->location().toLocal8Bit() ); 00580 } 00581 00582 // status 00583 // TODO: define Event status 00584 // addPropValue( vevent, VCStatusProp, anEvent->statusStr().toLocal8Bit() ); 00585 00586 // secrecy 00587 const char *text = 0; 00588 switch ( anEvent->secrecy() ) { 00589 case Incidence::SecrecyPublic: 00590 text = "PUBLIC"; 00591 break; 00592 case Incidence::SecrecyPrivate: 00593 text = "PRIVATE"; 00594 break; 00595 case Incidence::SecrecyConfidential: 00596 text = "CONFIDENTIAL"; 00597 break; 00598 } 00599 if ( text ) { 00600 addPropValue( vevent, VCClassProp, text ); 00601 } 00602 00603 // categories 00604 QStringList tmpStrList = anEvent->categories(); 00605 tmpStr = ""; 00606 QString catStr; 00607 for ( QStringList::const_iterator it = tmpStrList.constBegin(); it != tmpStrList.constEnd(); 00608 ++it ) { 00609 catStr = *it; 00610 if ( catStr[0] == ' ' ) { 00611 tmpStr += catStr.mid( 1 ); 00612 } else { 00613 tmpStr += catStr; 00614 } 00615 // this must be a ';' character as the vCalendar specification requires! 00616 // vcc.y has been hacked to translate the ';' to a ',' when the vcal is 00617 // read in. 00618 tmpStr += ';'; 00619 } 00620 if ( !tmpStr.isEmpty() ) { 00621 tmpStr.truncate( tmpStr.length() - 1 ); 00622 addPropValue( vevent, VCCategoriesProp, tmpStr.toLocal8Bit() ); 00623 } 00624 00625 // attachments 00626 // TODO: handle binary attachments! 00627 Attachment::List attachments = anEvent->attachments(); 00628 Attachment::List::ConstIterator atIt; 00629 for ( atIt = attachments.constBegin(); atIt != attachments.constEnd(); ++atIt ) { 00630 addPropValue( vevent, VCAttachProp, (*atIt)->uri().toLocal8Bit() ); 00631 } 00632 00633 // resources 00634 tmpStrList = anEvent->resources(); 00635 tmpStr = tmpStrList.join( ";" ); 00636 if ( !tmpStr.isEmpty() ) { 00637 addPropValue( vevent, VCResourcesProp, tmpStr.toLocal8Bit() ); 00638 } 00639 00640 // alarm stuff 00641 Alarm::List::ConstIterator it2; 00642 for ( it2 = anEvent->alarms().constBegin(); it2 != anEvent->alarms().constEnd(); ++it2 ) { 00643 Alarm *alarm = *it2; 00644 if ( alarm->enabled() ) { 00645 VObject *a = addProp( vevent, VCDAlarmProp ); 00646 tmpStr = kDateTimeToISO( alarm->time() ); 00647 addPropValue( a, VCRunTimeProp, tmpStr.toLocal8Bit() ); 00648 addPropValue( a, VCRepeatCountProp, "1" ); 00649 addPropValue( a, VCDisplayStringProp, "beep!" ); 00650 if ( alarm->type() == Alarm::Audio ) { 00651 a = addProp( vevent, VCAAlarmProp ); 00652 addPropValue( a, VCRunTimeProp, tmpStr.toLocal8Bit() ); 00653 addPropValue( a, VCRepeatCountProp, "1" ); 00654 addPropValue( a, VCAudioContentProp, QFile::encodeName( alarm->audioFile() ) ); 00655 } 00656 if ( alarm->type() == Alarm::Procedure ) { 00657 a = addProp( vevent, VCPAlarmProp ); 00658 addPropValue( a, VCRunTimeProp, tmpStr.toLocal8Bit() ); 00659 addPropValue( a, VCRepeatCountProp, "1" ); 00660 addPropValue( a, VCProcedureNameProp, QFile::encodeName( alarm->programFile() ) ); 00661 } 00662 } 00663 } 00664 00665 // priority 00666 tmpStr.sprintf( "%i", anEvent->priority() ); 00667 addPropValue( vevent, VCPriorityProp, tmpStr.toLocal8Bit() ); 00668 00669 // transparency 00670 tmpStr.sprintf( "%i", anEvent->transparency() ); 00671 addPropValue( vevent, VCTranspProp, tmpStr.toLocal8Bit() ); 00672 00673 // related event 00674 if ( anEvent->relatedTo() ) { 00675 addPropValue( vevent, VCRelatedToProp, anEvent->relatedTo()->uid().toLocal8Bit() ); 00676 } 00677 00678 QString pilotId = anEvent->nonKDECustomProperty( KPilotIdProp ); 00679 if ( !pilotId.isEmpty() ) { 00680 // pilot sync stuff 00681 addPropValue( vevent, KPilotIdProp, pilotId.toLocal8Bit() ); 00682 addPropValue( vevent, KPilotStatusProp, 00683 anEvent->nonKDECustomProperty( KPilotStatusProp ).toLocal8Bit() ); 00684 } 00685 00686 return vevent; 00687 } 00688 00689 Todo *VCalFormat::VTodoToEvent( VObject *vtodo ) 00690 { 00691 VObject *vo; 00692 VObjectIterator voi; 00693 char *s; 00694 00695 Todo *anEvent = new Todo; 00696 00697 // creation date 00698 if ( ( vo = isAPropertyOf( vtodo, VCDCreatedProp ) ) != 0 ) { 00699 anEvent->setCreated( ISOToKDateTime( s = fakeCString( vObjectUStringZValue( vo ) ) ) ); 00700 deleteStr( s ); 00701 } 00702 00703 // unique id 00704 vo = isAPropertyOf( vtodo, VCUniqueStringProp ); 00705 // while the UID property is preferred, it is not required. We'll use the 00706 // default Event UID if none is given. 00707 if ( vo ) { 00708 anEvent->setUid( s = fakeCString( vObjectUStringZValue( vo ) ) ); 00709 deleteStr( s ); 00710 } 00711 00712 // last modification date 00713 if ( ( vo = isAPropertyOf( vtodo, VCLastModifiedProp ) ) != 0 ) { 00714 anEvent->setLastModified( ISOToKDateTime( s = fakeCString( vObjectUStringZValue( vo ) ) ) ); 00715 deleteStr( s ); 00716 } else { 00717 anEvent->setLastModified( KDateTime::currentUtcDateTime() ); 00718 } 00719 00720 // organizer 00721 // if our extension property for the event's ORGANIZER exists, add it. 00722 if ( ( vo = isAPropertyOf( vtodo, ICOrganizerProp ) ) != 0 ) { 00723 anEvent->setOrganizer( s = fakeCString( vObjectUStringZValue( vo ) ) ); 00724 deleteStr( s ); 00725 } else { 00726 anEvent->setOrganizer( d->mCalendar->owner() ); 00727 } 00728 00729 // attendees. 00730 initPropIterator( &voi, vtodo ); 00731 while ( moreIteration( &voi ) ) { 00732 vo = nextVObject( &voi ); 00733 if ( strcmp( vObjectName( vo ), VCAttendeeProp ) == 0 ) { 00734 Attendee *a; 00735 VObject *vp; 00736 s = fakeCString( vObjectUStringZValue( vo ) ); 00737 QString tmpStr = QString::fromLocal8Bit( s ); 00738 deleteStr( s ); 00739 tmpStr = tmpStr.simplified(); 00740 int emailPos1, emailPos2; 00741 if ( ( emailPos1 = tmpStr.indexOf( '<' ) ) > 0 ) { 00742 // both email address and name 00743 emailPos2 = tmpStr.lastIndexOf( '>' ); 00744 a = new Attendee( tmpStr.left( emailPos1 - 1 ), 00745 tmpStr.mid( emailPos1 + 1, 00746 emailPos2 - ( emailPos1 + 1 ) ) ); 00747 } else if ( tmpStr.indexOf( '@' ) > 0 ) { 00748 // just an email address 00749 a = new Attendee( 0, tmpStr ); 00750 } else { 00751 // just a name 00752 // WTF??? Replacing the spaces of a name and using this as email? 00753 QString email = tmpStr.replace( ' ', '.' ); 00754 a = new Attendee( tmpStr, email ); 00755 } 00756 00757 // is there an RSVP property? 00758 if ( ( vp = isAPropertyOf( vo, VCRSVPProp ) ) != 0 ) { 00759 a->setRSVP( vObjectStringZValue( vp ) ); 00760 } 00761 // is there a status property? 00762 if ( ( vp = isAPropertyOf( vo, VCStatusProp ) ) != 0 ) { 00763 a->setStatus( readStatus( vObjectStringZValue( vp ) ) ); 00764 } 00765 // add the attendee 00766 anEvent->addAttendee( a ); 00767 } 00768 } 00769 00770 // description for todo 00771 if ( ( vo = isAPropertyOf( vtodo, VCDescriptionProp ) ) != 0 ) { 00772 s = fakeCString( vObjectUStringZValue( vo ) ); 00773 anEvent->setDescription( QString::fromLocal8Bit( s ), Qt::mightBeRichText( s ) ); 00774 deleteStr( s ); 00775 } 00776 00777 // summary 00778 if ( ( vo = isAPropertyOf( vtodo, VCSummaryProp ) ) ) { 00779 s = fakeCString( vObjectUStringZValue( vo ) ); 00780 anEvent->setSummary( QString::fromLocal8Bit( s ), Qt::mightBeRichText( s ) ); 00781 deleteStr( s ); 00782 } 00783 00784 // location 00785 if ( ( vo = isAPropertyOf( vtodo, VCLocationProp ) ) != 0 ) { 00786 s = fakeCString( vObjectUStringZValue( vo ) ); 00787 anEvent->setLocation( QString::fromLocal8Bit( s ), Qt::mightBeRichText( s ) ); 00788 deleteStr( s ); 00789 } 00790 00791 // completed 00792 // was: status 00793 if ( ( vo = isAPropertyOf( vtodo, VCStatusProp ) ) != 0 ) { 00794 s = fakeCString( vObjectUStringZValue( vo ) ); 00795 if ( s && strcmp( s, "COMPLETED" ) == 0 ) { 00796 anEvent->setCompleted( true ); 00797 } else { 00798 anEvent->setCompleted( false ); 00799 } 00800 deleteStr( s ); 00801 } else { 00802 anEvent->setCompleted( false ); 00803 } 00804 00805 // completion date 00806 if ( ( vo = isAPropertyOf( vtodo, VCCompletedProp ) ) != 0 ) { 00807 anEvent->setCompleted( ISOToKDateTime( s = fakeCString( vObjectUStringZValue( vo ) ) ) ); 00808 deleteStr( s ); 00809 } 00810 00811 // priority 00812 if ( ( vo = isAPropertyOf( vtodo, VCPriorityProp ) ) ) { 00813 s = fakeCString( vObjectUStringZValue( vo ) ); 00814 if ( s ) { 00815 anEvent->setPriority( atoi( s ) ); 00816 deleteStr( s ); 00817 } 00818 } 00819 00820 // due date 00821 if ( ( vo = isAPropertyOf( vtodo, VCDueProp ) ) != 0 ) { 00822 anEvent->setDtDue( ISOToKDateTime( s = fakeCString( vObjectUStringZValue( vo ) ) ) ); 00823 deleteStr( s ); 00824 anEvent->setHasDueDate( true ); 00825 } else { 00826 anEvent->setHasDueDate( false ); 00827 } 00828 00829 // start time 00830 if ( ( vo = isAPropertyOf( vtodo, VCDTstartProp ) ) != 0 ) { 00831 anEvent->setDtStart( ISOToKDateTime( s = fakeCString( vObjectUStringZValue( vo ) ) ) ); 00832 deleteStr( s ); 00833 anEvent->setHasStartDate( true ); 00834 } else { 00835 anEvent->setHasStartDate( false ); 00836 } 00837 00838 // alarm stuff 00839 if ( ( vo = isAPropertyOf( vtodo, VCDAlarmProp ) ) ) { 00840 Alarm *alarm = anEvent->newAlarm(); 00841 VObject *a; 00842 if ( ( a = isAPropertyOf( vo, VCRunTimeProp ) ) ) { 00843 alarm->setTime( ISOToKDateTime( s = fakeCString( vObjectUStringZValue( a ) ) ) ); 00844 deleteStr( s ); 00845 } 00846 alarm->setEnabled( true ); 00847 if ( ( vo = isAPropertyOf( vtodo, VCPAlarmProp ) ) ) { 00848 if ( ( a = isAPropertyOf( vo, VCProcedureNameProp ) ) ) { 00849 s = fakeCString( vObjectUStringZValue( a ) ); 00850 alarm->setProcedureAlarm( QFile::decodeName( s ) ); 00851 deleteStr( s ); 00852 } 00853 } 00854 if ( ( vo = isAPropertyOf( vtodo, VCAAlarmProp ) ) ) { 00855 if ( ( a = isAPropertyOf( vo, VCAudioContentProp ) ) ) { 00856 s = fakeCString( vObjectUStringZValue( a ) ); 00857 alarm->setAudioAlarm( QFile::decodeName( s ) ); 00858 deleteStr( s ); 00859 } 00860 } 00861 } 00862 00863 // related todo 00864 if ( ( vo = isAPropertyOf( vtodo, VCRelatedToProp ) ) != 0 ) { 00865 anEvent->setRelatedToUid( s = fakeCString( vObjectUStringZValue( vo ) ) ); 00866 deleteStr( s ); 00867 d->mTodosRelate.append( anEvent ); 00868 } 00869 00870 // categories 00871 if ( ( vo = isAPropertyOf( vtodo, VCCategoriesProp ) ) != 0 ) { 00872 s = fakeCString( vObjectUStringZValue( vo ) ); 00873 QString categories = QString::fromLocal8Bit( s ); 00874 deleteStr( s ); 00875 QStringList tmpStrList = categories.split( ';' ); 00876 anEvent->setCategories( tmpStrList ); 00877 } 00878 00879 /* PILOT SYNC STUFF */ 00880 if ( ( vo = isAPropertyOf( vtodo, KPilotIdProp ) ) ) { 00881 anEvent->setNonKDECustomProperty( 00882 KPilotIdProp, QString::fromLocal8Bit( s = fakeCString( vObjectUStringZValue( vo ) ) ) ); 00883 deleteStr( s ); 00884 if ( ( vo = isAPropertyOf( vtodo, KPilotStatusProp ) ) ) { 00885 anEvent->setNonKDECustomProperty( 00886 KPilotStatusProp, QString::fromLocal8Bit( s = fakeCString( vObjectUStringZValue( vo ) ) ) ); 00887 deleteStr( s ); 00888 } else { 00889 anEvent->setNonKDECustomProperty( KPilotStatusProp, QString::number( SYNCMOD ) ); 00890 } 00891 } 00892 00893 return anEvent; 00894 } 00895 00896 Event *VCalFormat::VEventToEvent( VObject *vevent ) 00897 { 00898 VObject *vo; 00899 VObjectIterator voi; 00900 char *s; 00901 00902 Event *anEvent = new Event; 00903 00904 // creation date 00905 if ( ( vo = isAPropertyOf( vevent, VCDCreatedProp ) ) != 0 ) { 00906 anEvent->setCreated( ISOToKDateTime( s = fakeCString( vObjectUStringZValue( vo ) ) ) ); 00907 deleteStr( s ); 00908 } 00909 00910 // unique id 00911 vo = isAPropertyOf( vevent, VCUniqueStringProp ); 00912 // while the UID property is preferred, it is not required. We'll use the 00913 // default Event UID if none is given. 00914 if ( vo ) { 00915 anEvent->setUid( s = fakeCString( vObjectUStringZValue( vo ) ) ); 00916 deleteStr( s ); 00917 } 00918 00919 // revision 00920 // again NSCAL doesn't give us much to work with, so we improvise... 00921 anEvent->setRevision( 0 ); 00922 if ( ( vo = isAPropertyOf( vevent, VCSequenceProp ) ) != 0 ) { 00923 s = fakeCString( vObjectUStringZValue( vo ) ); 00924 if ( s ) { 00925 anEvent->setRevision( atoi( s ) ); 00926 deleteStr( s ); 00927 } 00928 } 00929 00930 // last modification date 00931 if ( ( vo = isAPropertyOf( vevent, VCLastModifiedProp ) ) != 0 ) { 00932 anEvent->setLastModified( ISOToKDateTime( s = fakeCString( vObjectUStringZValue( vo ) ) ) ); 00933 deleteStr( s ); 00934 } else { 00935 anEvent->setLastModified( KDateTime::currentUtcDateTime() ); 00936 } 00937 00938 // organizer 00939 // if our extension property for the event's ORGANIZER exists, add it. 00940 if ( ( vo = isAPropertyOf( vevent, ICOrganizerProp ) ) != 0 ) { 00941 // FIXME: Also use the full name, not just the email address 00942 anEvent->setOrganizer( s = fakeCString( vObjectUStringZValue( vo ) ) ); 00943 deleteStr( s ); 00944 } else { 00945 anEvent->setOrganizer( d->mCalendar->owner() ); 00946 } 00947 00948 // deal with attendees. 00949 initPropIterator( &voi, vevent ); 00950 while ( moreIteration( &voi ) ) { 00951 vo = nextVObject( &voi ); 00952 if ( strcmp( vObjectName( vo ), VCAttendeeProp ) == 0 ) { 00953 Attendee *a; 00954 VObject *vp; 00955 s = fakeCString( vObjectUStringZValue( vo ) ); 00956 QString tmpStr = QString::fromLocal8Bit( s ); 00957 deleteStr( s ); 00958 tmpStr = tmpStr.simplified(); 00959 int emailPos1, emailPos2; 00960 if ( ( emailPos1 = tmpStr.indexOf( '<' ) ) > 0 ) { 00961 // both email address and name 00962 emailPos2 = tmpStr.lastIndexOf( '>' ); 00963 a = new Attendee( tmpStr.left( emailPos1 - 1 ), 00964 tmpStr.mid( emailPos1 + 1, 00965 emailPos2 - ( emailPos1 + 1 ) ) ); 00966 } else if ( tmpStr.indexOf( '@' ) > 0 ) { 00967 // just an email address 00968 a = new Attendee( 0, tmpStr ); 00969 } else { 00970 // just a name 00971 QString email = tmpStr.replace( ' ', '.' ); 00972 a = new Attendee( tmpStr, email ); 00973 } 00974 00975 // is there an RSVP property? 00976 if ( ( vp = isAPropertyOf( vo, VCRSVPProp ) ) != 0 ) { 00977 a->setRSVP( vObjectStringZValue( vp ) ); 00978 } 00979 // is there a status property? 00980 if ( ( vp = isAPropertyOf( vo, VCStatusProp ) ) != 0 ) { 00981 a->setStatus( readStatus( vObjectStringZValue( vp ) ) ); 00982 } 00983 // add the attendee 00984 anEvent->addAttendee( a ); 00985 } 00986 } 00987 00988 // This isn't strictly true. An event that doesn't have a start time 00989 // or an end time isn't all-day, it has an anchor in time but it doesn't 00990 // "take up" any time. 00991 /*if ((isAPropertyOf(vevent, VCDTstartProp) == 0) || 00992 (isAPropertyOf(vevent, VCDTendProp) == 0)) { 00993 anEvent->setAllDay(true); 00994 } else { 00995 }*/ 00996 00997 anEvent->setAllDay( false ); 00998 00999 // start time 01000 if ( ( vo = isAPropertyOf( vevent, VCDTstartProp ) ) != 0 ) { 01001 anEvent->setDtStart( ISOToKDateTime( s = fakeCString( vObjectUStringZValue( vo ) ) ) ); 01002 deleteStr( s ); 01003 if ( anEvent->dtStart().time().isNull() ) { 01004 anEvent->setAllDay( true ); 01005 } 01006 } 01007 01008 // stop time 01009 if ( ( vo = isAPropertyOf( vevent, VCDTendProp ) ) != 0 ) { 01010 anEvent->setDtEnd( ISOToKDateTime( s = fakeCString( vObjectUStringZValue( vo ) ) ) ); 01011 deleteStr( s ); 01012 if ( anEvent->dtEnd().time().isNull() ) { 01013 anEvent->setAllDay( true ); 01014 } 01015 } 01016 01017 // at this point, there should be at least a start or end time. 01018 // fix up for events that take up no time but have a time associated 01019 if ( !( vo = isAPropertyOf( vevent, VCDTstartProp ) ) ) { 01020 anEvent->setDtStart( anEvent->dtEnd() ); 01021 } 01022 if ( !( vo = isAPropertyOf( vevent, VCDTendProp ) ) ) { 01023 anEvent->setDtEnd( anEvent->dtStart() ); 01024 } 01025 01027 01028 // repeat stuff 01029 if ( ( vo = isAPropertyOf( vevent, VCRRuleProp ) ) != 0 ) { 01030 QString tmpStr = ( s = fakeCString( vObjectUStringZValue( vo ) ) ); 01031 deleteStr( s ); 01032 tmpStr.simplified(); 01033 tmpStr = tmpStr.toUpper(); 01034 01035 // first, read the type of the recurrence 01036 int typelen = 1; 01037 uint type = Recurrence::rNone; 01038 if ( tmpStr.left(1) == "D" ) { 01039 type = Recurrence::rDaily; 01040 } else if ( tmpStr.left(1) == "W" ) { 01041 type = Recurrence::rWeekly; 01042 } else { 01043 typelen = 2; 01044 if ( tmpStr.left(2) == "MP" ) { 01045 type = Recurrence::rMonthlyPos; 01046 } else if ( tmpStr.left(2) == "MD" ) { 01047 type = Recurrence::rMonthlyDay; 01048 } else if ( tmpStr.left(2) == "YM" ) { 01049 type = Recurrence::rYearlyMonth; 01050 } else if ( tmpStr.left(2) == "YD" ) { 01051 type = Recurrence::rYearlyDay; 01052 } 01053 } 01054 01055 if ( type != Recurrence::rNone ) { 01056 01057 // Immediately after the type is the frequency 01058 int index = tmpStr.indexOf( ' ' ); 01059 int last = tmpStr.lastIndexOf( ' ' ) + 1; // find last entry 01060 int rFreq = tmpStr.mid( typelen, ( index - 1 ) ).toInt(); 01061 ++index; // advance to beginning of stuff after freq 01062 01063 // Read the type-specific settings 01064 switch ( type ) { 01065 case Recurrence::rDaily: 01066 anEvent->recurrence()->setDaily(rFreq); 01067 break; 01068 01069 case Recurrence::rWeekly: 01070 { 01071 QBitArray qba(7); 01072 QString dayStr; 01073 if ( index == last ) { 01074 // e.g. W1 #0 01075 qba.setBit( anEvent->dtStart().date().dayOfWeek() - 1 ); 01076 } else { 01077 // e.g. W1 SU #0 01078 while ( index < last ) { 01079 dayStr = tmpStr.mid( index, 3 ); 01080 int dayNum = numFromDay( dayStr ); 01081 if ( dayNum >= 0 ) { 01082 qba.setBit( dayNum ); 01083 } 01084 index += 3; // advance to next day, or possibly "#" 01085 } 01086 } 01087 anEvent->recurrence()->setWeekly( rFreq, qba ); 01088 break; 01089 } 01090 01091 case Recurrence::rMonthlyPos: 01092 { 01093 anEvent->recurrence()->setMonthly( rFreq ); 01094 01095 QBitArray qba(7); 01096 short tmpPos; 01097 if ( index == last ) { 01098 // e.g. MP1 #0 01099 tmpPos = anEvent->dtStart().date().day() / 7 + 1; 01100 if ( tmpPos == 5 ) { 01101 tmpPos = -1; 01102 } 01103 qba.setBit( anEvent->dtStart().date().dayOfWeek() - 1 ); 01104 anEvent->recurrence()->addMonthlyPos( tmpPos, qba ); 01105 } else { 01106 // e.g. MP1 1+ SU #0 01107 while ( index < last ) { 01108 tmpPos = tmpStr.mid( index, 1 ).toShort(); 01109 index += 1; 01110 if ( tmpStr.mid( index, 1 ) == "-" ) { 01111 // convert tmpPos to negative 01112 tmpPos = 0 - tmpPos; 01113 } 01114 index += 2; // advance to day(s) 01115 while ( numFromDay( tmpStr.mid( index, 3 ) ) >= 0 ) { 01116 int dayNum = numFromDay( tmpStr.mid( index, 3 ) ); 01117 qba.setBit( dayNum ); 01118 index += 3; // advance to next day, or possibly pos or "#" 01119 } 01120 anEvent->recurrence()->addMonthlyPos( tmpPos, qba ); 01121 qba.detach(); 01122 qba.fill( false ); // clear out 01123 } // while != "#" 01124 } 01125 break; 01126 } 01127 01128 case Recurrence::rMonthlyDay: 01129 anEvent->recurrence()->setMonthly( rFreq ); 01130 if( index == last ) { 01131 // e.g. MD1 #0 01132 short tmpDay = anEvent->dtStart().date().day(); 01133 anEvent->recurrence()->addMonthlyDate( tmpDay ); 01134 } else { 01135 // e.g. MD1 3 #0 01136 while ( index < last ) { 01137 int index2 = tmpStr.indexOf( ' ', index ); 01138 short tmpDay = tmpStr.mid( index, ( index2 - index ) ).toShort(); 01139 index = index2 - 1; 01140 if ( tmpStr.mid( index, 1 ) == "-" ) { 01141 tmpDay = 0 - tmpDay; 01142 } 01143 index += 2; // advance the index; 01144 anEvent->recurrence()->addMonthlyDate( tmpDay ); 01145 } // while != # 01146 } 01147 break; 01148 01149 case Recurrence::rYearlyMonth: 01150 anEvent->recurrence()->setYearly( rFreq ); 01151 01152 if ( index == last ) { 01153 // e.g. YM1 #0 01154 short tmpMonth = anEvent->dtStart().date().month(); 01155 anEvent->recurrence()->addYearlyMonth( tmpMonth ); 01156 } else { 01157 // e.g. YM1 3 #0 01158 while ( index < last ) { 01159 int index2 = tmpStr.indexOf( ' ', index ); 01160 short tmpMonth = tmpStr.mid( index, ( index2 - index ) ).toShort(); 01161 index = index2 + 1; 01162 anEvent->recurrence()->addYearlyMonth( tmpMonth ); 01163 } // while != # 01164 } 01165 break; 01166 01167 case Recurrence::rYearlyDay: 01168 anEvent->recurrence()->setYearly( rFreq ); 01169 01170 if ( index == last ) { 01171 // e.g. YD1 #0 01172 short tmpDay = anEvent->dtStart().date().dayOfYear(); 01173 anEvent->recurrence()->addYearlyDay( tmpDay ); 01174 } else { 01175 // e.g. YD1 123 #0 01176 while ( index < last ) { 01177 int index2 = tmpStr.indexOf( ' ', index ); 01178 short tmpDay = tmpStr.mid( index, ( index2 - index ) ).toShort(); 01179 index = index2 + 1; 01180 anEvent->recurrence()->addYearlyDay( tmpDay ); 01181 } // while != # 01182 } 01183 break; 01184 01185 default: 01186 break; 01187 } 01188 01189 // find the last field, which is either the duration or the end date 01190 index = last; 01191 if ( tmpStr.mid( index, 1 ) == "#" ) { 01192 // Nr of occurrences 01193 index++; 01194 int rDuration = tmpStr.mid( index, tmpStr.length() - index ).toInt(); 01195 if ( rDuration > 0 ) { 01196 anEvent->recurrence()->setDuration( rDuration ); 01197 } 01198 } else if ( tmpStr.indexOf( 'T', index ) != -1 ) { 01199 KDateTime rEndDate = ISOToKDateTime( tmpStr.mid( index, tmpStr.length() - index ) ); 01200 rEndDate.setDateOnly( true ); 01201 anEvent->recurrence()->setEndDateTime( rEndDate ); 01202 } 01203 // anEvent->recurrence()->dump(); 01204 01205 } else { 01206 kDebug() << "we don't understand this type of recurrence!"; 01207 } // if known recurrence type 01208 } // repeats 01209 01210 // recurrence exceptions 01211 if ( ( vo = isAPropertyOf( vevent, VCExpDateProp ) ) != 0 ) { 01212 s = fakeCString( vObjectUStringZValue( vo ) ); 01213 QStringList exDates = QString::fromLocal8Bit( s ).split( ',' ); 01214 QStringList::ConstIterator it; 01215 for ( it = exDates.constBegin(); it != exDates.constEnd(); ++it ) { 01216 anEvent->recurrence()->addExDate( ISOToQDate(*it) ); 01217 } 01218 deleteStr( s ); 01219 } 01220 01221 // summary 01222 if ( ( vo = isAPropertyOf( vevent, VCSummaryProp ) ) ) { 01223 s = fakeCString( vObjectUStringZValue( vo ) ); 01224 anEvent->setSummary( QString::fromLocal8Bit( s ), Qt::mightBeRichText( s ) ); 01225 deleteStr( s ); 01226 } 01227 01228 // description 01229 if ( ( vo = isAPropertyOf( vevent, VCDescriptionProp ) ) != 0 ) { 01230 s = fakeCString( vObjectUStringZValue( vo ) ); 01231 bool isRich = Qt::mightBeRichText( s ); 01232 if ( !anEvent->description().isEmpty() ) { 01233 anEvent->setDescription( 01234 anEvent->description() + '\n' + QString::fromLocal8Bit( s ), isRich ); 01235 } else { 01236 anEvent->setDescription( QString::fromLocal8Bit( s ), isRich ); 01237 } 01238 deleteStr( s ); 01239 } 01240 01241 // location 01242 if ( ( vo = isAPropertyOf( vevent, VCLocationProp ) ) != 0 ) { 01243 s = fakeCString( vObjectUStringZValue( vo ) ); 01244 anEvent->setLocation( QString::fromLocal8Bit( s ), Qt::mightBeRichText( s ) ); 01245 deleteStr( s ); 01246 } 01247 01248 // some stupid vCal exporters ignore the standard and use Description 01249 // instead of Summary for the default field. Correct for this. 01250 if ( anEvent->summary().isEmpty() && !( anEvent->description().isEmpty() ) ) { 01251 QString tmpStr = anEvent->description().simplified(); 01252 anEvent->setDescription( "" ); 01253 anEvent->setSummary( tmpStr ); 01254 } 01255 01256 #if 0 01257 // status 01258 if ( ( vo = isAPropertyOf( vevent, VCStatusProp ) ) != 0 ) { 01259 QString tmpStr( s = fakeCString( vObjectUStringZValue( vo ) ) ); 01260 deleteStr( s ); 01261 // TODO: Define Event status 01262 // anEvent->setStatus( tmpStr ); 01263 } else { 01264 // anEvent->setStatus( "NEEDS ACTION" ); 01265 } 01266 #endif 01267 01268 // secrecy 01269 Incidence::Secrecy secrecy = Incidence::SecrecyPublic; 01270 if ( ( vo = isAPropertyOf( vevent, VCClassProp ) ) != 0 ) { 01271 s = fakeCString( vObjectUStringZValue( vo ) ); 01272 if ( s && strcmp( s, "PRIVATE" ) == 0 ) { 01273 secrecy = Incidence::SecrecyPrivate; 01274 } else if ( s && strcmp( s, "CONFIDENTIAL" ) == 0 ) { 01275 secrecy = Incidence::SecrecyConfidential; 01276 } 01277 deleteStr( s ); 01278 } 01279 anEvent->setSecrecy( secrecy ); 01280 01281 // categories 01282 if ( ( vo = isAPropertyOf( vevent, VCCategoriesProp ) ) != 0 ) { 01283 s = fakeCString( vObjectUStringZValue( vo ) ); 01284 QString categories = QString::fromLocal8Bit( s ); 01285 deleteStr( s ); 01286 QStringList tmpStrList = categories.split( ',' ); 01287 anEvent->setCategories( tmpStrList ); 01288 } 01289 01290 // attachments 01291 initPropIterator( &voi, vevent ); 01292 while ( moreIteration( &voi ) ) { 01293 vo = nextVObject( &voi ); 01294 if ( strcmp( vObjectName( vo ), VCAttachProp ) == 0 ) { 01295 s = fakeCString( vObjectUStringZValue( vo ) ); 01296 anEvent->addAttachment( new Attachment( QString( s ) ) ); 01297 deleteStr( s ); 01298 } 01299 } 01300 01301 // resources 01302 if ( ( vo = isAPropertyOf( vevent, VCResourcesProp ) ) != 0 ) { 01303 QString resources = ( s = fakeCString( vObjectUStringZValue( vo ) ) ); 01304 deleteStr( s ); 01305 QStringList tmpStrList = resources.split( ';' ); 01306 anEvent->setResources( tmpStrList ); 01307 } 01308 01309 // alarm stuff 01310 if ( ( vo = isAPropertyOf( vevent, VCDAlarmProp ) ) ) { 01311 Alarm *alarm = anEvent->newAlarm(); 01312 VObject *a; 01313 if ( ( a = isAPropertyOf( vo, VCRunTimeProp ) ) ) { 01314 alarm->setTime( ISOToKDateTime( s = fakeCString( vObjectUStringZValue( a ) ) ) ); 01315 deleteStr( s ); 01316 } 01317 alarm->setEnabled( true ); 01318 if ( ( vo = isAPropertyOf( vevent, VCPAlarmProp ) ) ) { 01319 if ( ( a = isAPropertyOf( vo, VCProcedureNameProp ) ) ) { 01320 s = fakeCString( vObjectUStringZValue( a ) ); 01321 alarm->setProcedureAlarm( QFile::decodeName( s ) ); 01322 deleteStr( s ); 01323 } 01324 } 01325 if ( ( vo = isAPropertyOf( vevent, VCAAlarmProp ) ) ) { 01326 if ( ( a = isAPropertyOf( vo, VCAudioContentProp ) ) ) { 01327 s = fakeCString( vObjectUStringZValue( a ) ); 01328 alarm->setAudioAlarm( QFile::decodeName( s ) ); 01329 deleteStr( s ); 01330 } 01331 } 01332 } 01333 01334 // priority 01335 if ( ( vo = isAPropertyOf( vevent, VCPriorityProp ) ) ) { 01336 s = fakeCString( vObjectUStringZValue( vo ) ); 01337 if ( s ) { 01338 anEvent->setPriority( atoi( s ) ); 01339 deleteStr( s ); 01340 } 01341 } 01342 01343 // transparency 01344 if ( ( vo = isAPropertyOf( vevent, VCTranspProp ) ) != 0 ) { 01345 s = fakeCString( vObjectUStringZValue( vo ) ); 01346 if ( s ) { 01347 int i = atoi( s ); 01348 anEvent->setTransparency( i == 1 ? Event::Transparent : Event::Opaque ); 01349 deleteStr( s ); 01350 } 01351 } 01352 01353 // related event 01354 if ( ( vo = isAPropertyOf( vevent, VCRelatedToProp ) ) != 0 ) { 01355 anEvent->setRelatedToUid( s = fakeCString( vObjectUStringZValue( vo ) ) ); 01356 deleteStr( s ); 01357 d->mEventsRelate.append( anEvent ); 01358 } 01359 01360 /* PILOT SYNC STUFF */ 01361 if ( ( vo = isAPropertyOf( vevent, KPilotIdProp ) ) ) { 01362 anEvent->setNonKDECustomProperty( 01363 KPilotIdProp, QString::fromLocal8Bit( s = fakeCString( vObjectUStringZValue( vo ) ) ) ); 01364 deleteStr( s ); 01365 if ( ( vo = isAPropertyOf( vevent, KPilotStatusProp ) ) ) { 01366 anEvent->setNonKDECustomProperty( 01367 KPilotStatusProp, QString::fromLocal8Bit( s = fakeCString( vObjectUStringZValue( vo ) ) ) ); 01368 deleteStr( s ); 01369 } else { 01370 anEvent->setNonKDECustomProperty( KPilotStatusProp, QString::number( SYNCMOD ) ); 01371 } 01372 } 01373 01374 return anEvent; 01375 } 01376 01377 QString VCalFormat::qDateToISO( const QDate &qd ) 01378 { 01379 QString tmpStr; 01380 01381 if ( !qd.isValid() ) { 01382 return QString(); 01383 } 01384 01385 tmpStr.sprintf( "%.2d%.2d%.2d", qd.year(), qd.month(), qd.day() ); 01386 return tmpStr; 01387 01388 } 01389 01390 QString VCalFormat::kDateTimeToISO( const KDateTime &dt, bool zulu ) 01391 { 01392 QString tmpStr; 01393 01394 if ( !dt.isValid() ) { 01395 return QString(); 01396 } 01397 01398 QDateTime tmpDT; 01399 if ( zulu ) { 01400 tmpDT = dt.toUtc().dateTime(); 01401 } else { 01402 tmpDT = dt.toTimeSpec( d->mCalendar->timeSpec() ).dateTime(); 01403 } 01404 tmpStr.sprintf( "%.2d%.2d%.2dT%.2d%.2d%.2d", 01405 tmpDT.date().year(), tmpDT.date().month(), 01406 tmpDT.date().day(), tmpDT.time().hour(), 01407 tmpDT.time().minute(), tmpDT.time().second() ); 01408 if ( zulu ) { 01409 tmpStr += 'Z'; 01410 } 01411 return tmpStr; 01412 } 01413 01414 KDateTime VCalFormat::ISOToKDateTime( const QString &dtStr ) 01415 { 01416 QDate tmpDate; 01417 QTime tmpTime; 01418 QString tmpStr; 01419 int year, month, day, hour, minute, second; 01420 01421 tmpStr = dtStr; 01422 year = tmpStr.left( 4 ).toInt(); 01423 month = tmpStr.mid( 4, 2 ).toInt(); 01424 day = tmpStr.mid( 6, 2 ).toInt(); 01425 hour = tmpStr.mid( 9, 2 ).toInt(); 01426 minute = tmpStr.mid( 11, 2 ).toInt(); 01427 second = tmpStr.mid( 13, 2 ).toInt(); 01428 tmpDate.setYMD( year, month, day ); 01429 tmpTime.setHMS( hour, minute, second ); 01430 01431 if ( tmpDate.isValid() && tmpTime.isValid() ) { 01432 // correct for GMT if string is in Zulu format 01433 if ( dtStr.at( dtStr.length() - 1 ) == 'Z' ) { 01434 return KDateTime( tmpDate, tmpTime, KDateTime::UTC ); 01435 } else { 01436 return KDateTime( tmpDate, tmpTime, d->mCalendar->timeSpec() ); 01437 } 01438 } else { 01439 return KDateTime(); 01440 } 01441 } 01442 01443 QDate VCalFormat::ISOToQDate( const QString &dateStr ) 01444 { 01445 int year, month, day; 01446 01447 year = dateStr.left( 4 ).toInt(); 01448 month = dateStr.mid( 4, 2 ).toInt(); 01449 day = dateStr.mid( 6, 2 ).toInt(); 01450 01451 return QDate( year, month, day ); 01452 } 01453 01454 // take a raw vcalendar (i.e. from a file on disk, clipboard, etc. etc. 01455 // and break it down from it's tree-like format into the dictionary format 01456 // that is used internally in the VCalFormat. 01457 void VCalFormat::populate( VObject *vcal ) 01458 { 01459 // this function will populate the caldict dictionary and other event 01460 // lists. It turns vevents into Events and then inserts them. 01461 01462 VObjectIterator i; 01463 VObject *curVO, *curVOProp; 01464 Event *anEvent; 01465 01466 if ( ( curVO = isAPropertyOf( vcal, ICMethodProp ) ) != 0 ) { 01467 char *methodType = 0; 01468 methodType = fakeCString( vObjectUStringZValue( curVO ) ); 01469 kDebug() << "This calendar is an iTIP transaction of type '" 01470 << methodType << "'"; 01471 deleteStr( methodType ); 01472 } 01473 01474 // warn the user that we might have trouble reading non-known calendar. 01475 if ( ( curVO = isAPropertyOf( vcal, VCProdIdProp ) ) != 0 ) { 01476 char *s = fakeCString( vObjectUStringZValue( curVO ) ); 01477 if ( !s || strcmp( productId().toLocal8Bit(), s ) != 0 ) { 01478 kDebug() << "This vCalendar file was not created by KOrganizer or" 01479 << "any other product we support. Loading anyway..."; 01480 } 01481 setLoadedProductId( s ); 01482 deleteStr( s ); 01483 } 01484 01485 // warn the user we might have trouble reading this unknown version. 01486 if ( ( curVO = isAPropertyOf( vcal, VCVersionProp ) ) != 0 ) { 01487 char *s = fakeCString( vObjectUStringZValue( curVO ) ); 01488 if ( !s || strcmp( _VCAL_VERSION, s ) != 0 ) { 01489 kDebug() << "This vCalendar file has version" << s 01490 << "We only support" << _VCAL_VERSION; 01491 } 01492 deleteStr( s ); 01493 } 01494 01495 #if 0 01496 // set the time zone (this is a property of the view, so just discard!) 01497 if ( ( curVO = isAPropertyOf( vcal, VCTimeZoneProp ) ) != 0 ) { 01498 char *s = fakeCString( vObjectUStringZValue( curVO ) ); 01499 d->mCalendar->setTimeZone( s ); 01500 deleteStr( s ); 01501 } 01502 #endif 01503 01504 // Store all events with a relatedTo property in a list for post-processing 01505 d->mEventsRelate.clear(); 01506 d->mTodosRelate.clear(); 01507 01508 initPropIterator( &i, vcal ); 01509 01510 // go through all the vobjects in the vcal 01511 while ( moreIteration( &i ) ) { 01512 curVO = nextVObject( &i ); 01513 01514 /************************************************************************/ 01515 01516 // now, check to see that the object is an event or todo. 01517 if ( strcmp( vObjectName( curVO ), VCEventProp ) == 0 ) { 01518 01519 if ( ( curVOProp = isAPropertyOf( curVO, KPilotStatusProp ) ) != 0 ) { 01520 char *s; 01521 s = fakeCString( vObjectUStringZValue( curVOProp ) ); 01522 // check to see if event was deleted by the kpilot conduit 01523 if ( s ) { 01524 if ( atoi( s ) == SYNCDEL ) { 01525 deleteStr( s ); 01526 kDebug() << "skipping pilot-deleted event"; 01527 goto SKIP; 01528 } 01529 deleteStr( s ); 01530 } 01531 } 01532 01533 // this code checks to see if we are trying to read in an event 01534 // that we already find to be in the calendar. If we find this 01535 // to be the case, we skip the event. 01536 if ( ( curVOProp = isAPropertyOf( curVO, VCUniqueStringProp ) ) != 0 ) { 01537 char *s = fakeCString( vObjectUStringZValue( curVOProp ) ); 01538 QString tmpStr( s ); 01539 deleteStr( s ); 01540 01541 if ( d->mCalendar->incidence( tmpStr ) ) { 01542 goto SKIP; 01543 } 01544 } 01545 01546 if ( ( !( curVOProp = isAPropertyOf( curVO, VCDTstartProp ) ) ) && 01547 ( !( curVOProp = isAPropertyOf( curVO, VCDTendProp ) ) ) ) { 01548 kDebug() << "found a VEvent with no DTSTART and no DTEND! Skipping..."; 01549 goto SKIP; 01550 } 01551 01552 anEvent = VEventToEvent( curVO ); 01553 // we now use addEvent instead of insertEvent so that the 01554 // signal/slot get connected. 01555 if ( anEvent ) { 01556 if ( anEvent->dtStart().isValid() && anEvent->dtEnd().isValid() ) { 01557 d->mCalendar->addEvent( anEvent ); 01558 } 01559 } else { 01560 // some sort of error must have occurred while in translation. 01561 goto SKIP; 01562 } 01563 } else if ( strcmp( vObjectName( curVO ), VCTodoProp ) == 0 ) { 01564 Todo *aTodo = VTodoToEvent( curVO ); 01565 01566 Todo *old = d->mCalendar->todo( aTodo->uid() ); 01567 if ( old ) { 01568 d->mCalendar->deleteTodo( old ); 01569 d->mTodosRelate.removeAll( old ); 01570 } 01571 01572 d->mCalendar->addTodo( aTodo ); 01573 } else if ( ( strcmp( vObjectName( curVO ), VCVersionProp ) == 0 ) || 01574 ( strcmp( vObjectName( curVO ), VCProdIdProp ) == 0 ) || 01575 ( strcmp( vObjectName( curVO ), VCTimeZoneProp ) == 0 ) ) { 01576 // do nothing, we know these properties and we want to skip them. 01577 // we have either already processed them or are ignoring them. 01578 ; 01579 } else { 01580 kDebug() << "Ignoring unknown vObject \"" << vObjectName(curVO) << "\""; 01581 } 01582 SKIP: 01583 ; 01584 } // while 01585 01586 // Post-Process list of events with relations, put Event objects in relation 01587 Event::List::ConstIterator eIt; 01588 for ( eIt = d->mEventsRelate.constBegin(); eIt != d->mEventsRelate.constEnd(); ++eIt ) { 01589 (*eIt)->setRelatedTo( d->mCalendar->incidence( (*eIt)->relatedToUid() ) ); 01590 } 01591 Todo::List::ConstIterator tIt; 01592 for ( tIt = d->mTodosRelate.constBegin(); tIt != d->mTodosRelate.constEnd(); ++tIt ) { 01593 (*tIt)->setRelatedTo( d->mCalendar->incidence( (*tIt)->relatedToUid() ) ); 01594 } 01595 } 01596 01597 const char *VCalFormat::dayFromNum( int day ) 01598 { 01599 const char *days[7] = { "MO ", "TU ", "WE ", "TH ", "FR ", "SA ", "SU " }; 01600 01601 return days[day]; 01602 } 01603 01604 int VCalFormat::numFromDay( const QString &day ) 01605 { 01606 if ( day == "MO " ) { 01607 return 0; 01608 } 01609 if ( day == "TU " ) { 01610 return 1; 01611 } 01612 if ( day == "WE " ) { 01613 return 2; 01614 } 01615 if ( day == "TH " ) { 01616 return 3; 01617 } 01618 if ( day == "FR " ) { 01619 return 4; 01620 } 01621 if ( day == "SA " ) { 01622 return 5; 01623 } 01624 if ( day == "SU " ) { 01625 return 6; 01626 } 01627 01628 return -1; // something bad happened. :) 01629 } 01630 01631 Attendee::PartStat VCalFormat::readStatus( const char *s ) const 01632 { 01633 QString statStr = s; 01634 statStr = statStr.toUpper(); 01635 Attendee::PartStat status; 01636 01637 if ( statStr == "X-ACTION" ) { 01638 status = Attendee::NeedsAction; 01639 } else if ( statStr == "NEEDS ACTION" ) { 01640 status = Attendee::NeedsAction; 01641 } else if ( statStr == "ACCEPTED" ) { 01642 status = Attendee::Accepted; 01643 } else if ( statStr == "SENT" ) { 01644 status = Attendee::NeedsAction; 01645 } else if ( statStr == "TENTATIVE" ) { 01646 status = Attendee::Tentative; 01647 } else if ( statStr == "CONFIRMED" ) { 01648 status = Attendee::Accepted; 01649 } else if ( statStr == "DECLINED" ) { 01650 status = Attendee::Declined; 01651 } else if ( statStr == "COMPLETED" ) { 01652 status = Attendee::Completed; 01653 } else if ( statStr == "DELEGATED" ) { 01654 status = Attendee::Delegated; 01655 } else { 01656 kDebug() << "error setting attendee mStatus, unknown mStatus!"; 01657 status = Attendee::NeedsAction; 01658 } 01659 01660 return status; 01661 } 01662 01663 QByteArray VCalFormat::writeStatus( Attendee::PartStat status ) const 01664 { 01665 switch( status ) { 01666 default: 01667 case Attendee::NeedsAction: 01668 return "NEEDS ACTION"; 01669 break; 01670 case Attendee::Accepted: 01671 return "ACCEPTED"; 01672 break; 01673 case Attendee::Declined: 01674 return "DECLINED"; 01675 break; 01676 case Attendee::Tentative: 01677 return "TENTATIVE"; 01678 break; 01679 case Attendee::Delegated: 01680 return "DELEGATED"; 01681 break; 01682 case Attendee::Completed: 01683 return "COMPLETED"; 01684 break; 01685 case Attendee::InProcess: 01686 return "NEEDS ACTION"; 01687 break; 01688 } 01689 }
This file is part of the KDE documentation.
Documentation copyright © 1996-2012 The KDE developers.
Generated on Thu Aug 2 2012 15:25:53 by doxygen 1.7.5 written by Dimitri van Heesch, © 1997-2006
Documentation copyright © 1996-2012 The KDE developers.
Generated on Thu Aug 2 2012 15:25:53 by doxygen 1.7.5 written by Dimitri van Heesch, © 1997-2006
KDE's Doxygen guidelines are available online.