• Skip to content
  • Skip to link menu
KDE 4.1 API Reference
  • KDE API Reference
  • KDE-PIM Libraries
  • Sitemap
  • Contact Us
 

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

KCal Library

Skip menu "KCal Library"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

KDE-PIM Libraries

Skip menu "KDE-PIM Libraries"
  • akonadi
  • kabc
  • kblog
  • kcal
  • kimap
  • kioslave
  •   imap4
  •   mbox
  • kldap
  • kmime
  • kpimidentities
  • kpimutils
  • kresources
  • ktnef
  • kxmlrpcclient
  • mailtransport
  • qgpgme
  • syndication
  •   atom
  •   rdf
  •   rss2
Generated for KDE-PIM Libraries by doxygen 1.5.4
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal