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

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
  •   richtextbuilders
  • kpimutils
  • kresources
  • ktnef
  • kxmlrpcclient
  • mailtransport
  • qgpgme
  • syndication
  •   atom
  •   rdf
  •   rss2
Generated for KDE-PIM Libraries by doxygen 1.5.7
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