kpilot

vcalRecord.cc

Go to the documentation of this file.
00001 /* vcalRecord.cc                       KPilot
00002 **
00003 ** Copyright (C) 2006 by Adriaan de Groot <groot@kde.org>
00004 ** Copyright (C) 2002-2003 by Reinhold Kainhofer
00005 ** Copyright (C) 2001 by Dan Pilone
00006 **
00007 */
00008 
00009 /*
00010 ** This program is free software; you can redistribute it and/or modify
00011 ** it under the terms of the GNU General Public License as published by
00012 ** the Free Software Foundation; either version 2 of the License, or
00013 ** (at your option) any later version.
00014 **
00015 ** This program is distributed in the hope that it will be useful,
00016 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
00017 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00018 ** GNU General Public License for more details.
00019 **
00020 ** You should have received a copy of the GNU General Public License
00021 ** along with this program in a file called COPYING; if not, write to
00022 ** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
00023 ** MA 02110-1301, USA.
00024 */
00025 
00026 /*
00027 ** Bug reports and questions can be sent to kde-pim@kde.org
00028 */
00029 
00030 #include "options.h"
00031 
00032 #include <libkcal/calendar.h>
00033 #include <libkcal/calendarlocal.h>
00034 #include <libkcal/recurrence.h>
00035 #include <libkcal/vcalformat.h>
00036 
00037 #include "pilot.h"
00038 #include "pilotDateEntry.h"
00039 
00040 #include "kcalRecord.h"
00041 #include "vcalRecord.h"
00042 
00043 
00044 static void setStartEndTimes(KCal::Event *e, const PilotDateEntry *de)
00045 {
00046     FUNCTIONSETUP;
00047     DEBUGKPILOT << fname
00048         << "# Start time on Palm: "
00049         << readTm(de->getEventStart()).toString() << endl;
00050 
00051     e->setDtStart(readTm(de->getEventStart()));
00052     e->setFloats(de->isEvent());
00053 
00054     if (de->isMultiDay())
00055     {
00056         e->setDtEnd(readTm(de->getRepeatEnd()));
00057     }
00058     else
00059     {
00060         e->setDtEnd(readTm(de->getEventEnd()));
00061     }
00062 }
00063 
00064 static void setAlarms(KCal::Event *e, const PilotDateEntry *de)
00065 {
00066     FUNCTIONSETUP;
00067 
00068     if (!e) return;
00069     // Delete all the alarms now and add them one by one later on.
00070     e->clearAlarms();
00071     if (!de->isAlarmEnabled()) return;
00072 
00073 //  QDateTime alarmDT = readTm(de->getEventStart());
00074     int advanceUnits = de->getAdvanceUnits();
00075 
00076     switch (advanceUnits)
00077     {
00078     case advMinutes:
00079         advanceUnits = 1;
00080         break;
00081     case advHours:
00082         advanceUnits = 60;
00083         break;
00084     case advDays:
00085         advanceUnits = 60*24;
00086         break;
00087     default:
00088 #ifdef DEBUG
00089         DEBUGKPILOT << fname
00090             << ": Unknown advance units "
00091             << advanceUnits
00092             << endl;
00093 #endif
00094         advanceUnits=1;
00095     }
00096 
00097     KCal::Duration adv(-60*advanceUnits*de->getAdvance());
00098     KCal::Alarm*alm=e->newAlarm();
00099     if (!alm) return;
00100 
00101     alm->setStartOffset(adv);
00102     alm->setEnabled(true);
00103 }
00104 
00105 static void setRecurrence(KCal::Event *event,const PilotDateEntry *dateEntry)
00106 {
00107     FUNCTIONSETUP;
00108 
00109     if ((dateEntry->getRepeatType() == repeatNone) || dateEntry->isMultiDay())
00110     {
00111 #ifdef DEBUG
00112         DEBUGKPILOT<<fname<<": no recurrence to set"<<endl;
00113 #endif
00114         return;
00115     }
00116 
00117     KCal::Recurrence *recur = event->recurrence();
00118     int freq = dateEntry->getRepeatFrequency();
00119     bool repeatsForever = dateEntry->getRepeatForever();
00120     QDate endDate, evt;
00121 
00122     if (!repeatsForever)
00123     {
00124         endDate = readTm(dateEntry->getRepeatEnd()).date();
00125 #ifdef DEBUG
00126         DEBUGKPILOT << fname << "-- end " << endDate.toString() << endl;
00127     }
00128     else
00129     {
00130         DEBUGKPILOT << fname << "-- noend" << endl;
00131 #endif
00132     }
00133 
00134     QBitArray dayArray(7);
00135 
00136     switch(dateEntry->getRepeatType())
00137     {
00138     case repeatDaily:
00139         recur->setDaily(freq);
00140         break;
00141     case repeatWeekly:
00142         {
00143         const int *days = dateEntry->getRepeatDays();
00144 
00145 #ifdef DEBUG
00146         DEBUGKPILOT << fname
00147             << ": Got repeat-weekly entry, by-days="
00148             << days[0] << " "<< days[1] << " "<< days[2] << " "
00149             << days[3] << " "
00150             << days[4] << " "<< days[5] << " "<< days[6] << " "
00151             << endl;
00152 #endif
00153 
00154         // Rotate the days of the week, since day numbers on the Pilot and
00155         // in vCal / Events are different.
00156         //
00157         if (days[0]) dayArray.setBit(6);
00158         for (int i = 1; i < 7; i++)
00159         {
00160             if (days[i]) dayArray.setBit(i-1);
00161         }
00162         recur->setWeekly( freq, dayArray );
00163         }
00164         break;
00165     case repeatMonthlyByDay: {
00166         // Palm: Day=0(sun)-6(sat); week=0-4, 4=last week; pos=week*7+day
00167         // libkcal: day=bit0(mon)-bit6(sun); week=-5to-1(from end) and 1-5 (from beginning)
00168         // Palm->PC: w=pos/7
00169         // week: if w=4 -> week=-1, else week=w+1;
00170         // day: day=(pos-1)%7 (rotate by one day!)
00171         recur->setMonthly( freq );
00172 
00173         int day=dateEntry->getRepeatDay();
00174         int week=day/7;
00175         // week=4 means last, otherwise convert to 0-based
00176         if (week==4) week=-1; else week++;
00177         dayArray.setBit((day+6) % 7);
00178         recur->addMonthlyPos(week, dayArray);
00179         break;}
00180     case repeatMonthlyByDate:
00181         recur->setMonthly( freq );
00182         recur->addMonthlyDate( dateEntry->getEventStart().tm_mday );
00183         break;
00184     case repeatYearly:
00185         recur->setYearly( freq );
00186         evt=readTm(dateEntry->getEventStart()).date();
00187         recur->addYearlyMonth( evt.month() );
00188 //      dayArray.setBit((evt.day()-1) % 7);
00189 //      recur->addYearlyMonthPos( ( (evt.day()-1) / 7) + 1, dayArray );
00190         break;
00191     case repeatNone:
00192     default :
00193 #ifdef DEBUG
00194         DEBUGKPILOT << fname
00195             << ": Can't handle repeat type "
00196             << dateEntry->getRepeatType()
00197             << endl;
00198 #endif
00199         break;
00200     }
00201     if (!repeatsForever)
00202     {
00203         recur->setEndDate(endDate);
00204     }
00205 }
00206 
00207 static void setExceptions(KCal::Event *vevent,const PilotDateEntry *dateEntry)
00208 {
00209     FUNCTIONSETUP;
00210 
00211     // Start from an empty exception list, and if necessary, add exceptions.
00212     // At the end of the function, apply the (possibly empty) exception list.
00213     KCal::DateList dl;
00214 
00215     if ( !(dateEntry->isMultiDay() ) && dateEntry->getExceptionCount()>0 )
00216     {
00217         for (int i = 0; i < dateEntry->getExceptionCount(); i++)
00218         {
00219 //          vevent->addExDate(readTm(dateEntry->getExceptions()[i]).date());
00220             dl.append(readTm(dateEntry->getExceptions()[i]).date());
00221         }
00222     }
00223     else
00224     {
00225 #ifdef DEBUG
00226     if (dateEntry->getExceptionCount()>0)
00227     DEBUGKPILOT << fname
00228         << ": WARNING Exceptions ignored for multi-day event "
00229         << dateEntry->getDescription()
00230         << endl ;
00231 #endif
00232         return;
00233     }
00234     vevent->recurrence()->setExDates(dl);
00235 }
00236 
00237 static void setStartEndTimes(PilotDateEntry*de, const KCal::Event *e)
00238 {
00239     FUNCTIONSETUP;
00240     struct tm ttm=writeTm(e->dtStart());
00241     de->setEventStart(ttm);
00242     de->setFloats( e->doesFloat() );
00243 
00244     if (e->hasEndDate() && e->dtEnd().isValid())
00245     {
00246         ttm=writeTm(e->dtEnd());
00247     }
00248     else
00249     {
00250         ttm=writeTm(e->dtStart());
00251     }
00252     de->setEventEnd(ttm);
00253 }
00254 
00255 
00256 
00257 
00258 static void setAlarms(PilotDateEntry*de, const KCal::Event *e)
00259 {
00260     FUNCTIONSETUP;
00261 
00262     if (!de || !e )
00263     {
00264 #ifdef DEBUG
00265         DEBUGKPILOT << fname << ": NULL entry given to setAlarms. "<<endl;
00266 #endif
00267         return;
00268     }
00269 
00270     if ( !e->isAlarmEnabled() )
00271     {
00272         de->setAlarmEnabled( false );
00273         return;
00274     }
00275 
00276     // find the first enabled alarm
00277     KCal::Alarm::List alms=e->alarms();
00278     KCal::Alarm* alm=0;
00279     KCal::Alarm::List::ConstIterator it;
00280     for ( it = alms.begin(); it != alms.end(); ++it ) {
00281         if ((*it)->enabled()) alm=*it;
00282     }
00283 
00284     if (!alm )
00285     {
00286 #ifdef DEBUG
00287         DEBUGKPILOT << fname << ": no enabled alarm found (should exist!!!)"<<endl;
00288 #endif
00289         de->setAlarmEnabled( false );
00290         return;
00291     }
00292 
00293     // palm and PC offsets have a different sign!!
00294     int aoffs=-alm->startOffset().asSeconds()/60;
00295     int offs=(aoffs>0)?aoffs:-aoffs;
00296 
00297     // find the best Advance Unit
00298     if (offs>=100 || offs==60)
00299     {
00300         offs/=60;
00301         if (offs>=48 || offs==24)
00302         {
00303             offs/=24;
00304             de->setAdvanceUnits(advDays);
00305         }
00306         else
00307         {
00308             de->setAdvanceUnits(advHours);
00309         }
00310     }
00311     else
00312     {
00313         de->setAdvanceUnits(advMinutes);
00314     }
00315     de->setAdvance((aoffs>0)?offs:-offs);
00316     de->setAlarmEnabled( true );
00317 }
00318 
00319 
00320 
00321 static void setRecurrence(PilotDateEntry*dateEntry, const KCal::Event *event)
00322 {
00323     FUNCTIONSETUP;
00324     bool isMultiDay=false;
00325 
00326     // first we have 'fake type of recurrence' when a multi-day event is passed to the pilot, it is converted to an event
00327     // which recurs daily a number of times. if the event itself recurs, this will be overridden, and
00328     // only the first day will be included in the event!!!!
00329     QDateTime startDt(readTm(dateEntry->getEventStart())), endDt(readTm(dateEntry->getEventEnd()));
00330     if (startDt.daysTo(endDt))
00331     {
00332         isMultiDay=true;
00333         dateEntry->setRepeatType(repeatDaily);
00334         dateEntry->setRepeatFrequency(1);
00335         dateEntry->setRepeatEnd(dateEntry->getEventEnd());
00336 #ifdef DEBUG
00337         DEBUGKPILOT << fname <<": Setting single-day recurrence (" << startDt.toString() << " - " << endDt.toString() << ")" <<endl;
00338 #endif
00339     }
00340 
00341 
00342     KCal::Recurrence*r=event->recurrence();
00343     if (!r) return;
00344     ushort recType=r->recurrenceType();
00345     if ( recType==KCal::Recurrence::rNone )
00346     {
00347         if (!isMultiDay) dateEntry->setRepeatType(repeatNone);
00348         return;
00349     }
00350 
00351 
00352     int freq=r->frequency();
00353     QDate endDate=r->endDate();
00354 
00355     if ( r->duration() < 0 || !endDate.isValid() )
00356     {
00357         dateEntry->setRepeatForever();
00358     }
00359     else
00360     {
00361         dateEntry->setRepeatEnd(writeTm(endDate));
00362     }
00363     dateEntry->setRepeatFrequency(freq);
00364 #ifdef DEBUG
00365     DEBUGKPILOT<<" Event: "<<event->summary()<<" ("<<event->description()<<")"<<endl;
00366     DEBUGKPILOT<< "duration: "<<r->duration() << ", endDate: "<<endDate.toString()<< ", ValidEndDate: "<<endDate.isValid()<<", NullEndDate: "<<endDate.isNull()<<endl;
00367 #endif
00368 
00369     QBitArray dayArray(7), dayArrayPalm(7);
00370     switch(recType)
00371     {
00372     case KCal::Recurrence::rDaily:
00373         dateEntry->setRepeatType(repeatDaily);
00374         break;
00375     case KCal::Recurrence::rWeekly:
00376         dateEntry->setRepeatType(repeatWeekly);
00377         dayArray=r->days();
00378         // rotate the bits by one
00379         for (int i=0; i<7; i++)
00380         {
00381             dayArrayPalm.setBit( (i+1)%7, dayArray[i]);
00382         }
00383         dateEntry->setRepeatDays(dayArrayPalm);
00384         break;
00385     case KCal::Recurrence::rMonthlyPos:
00386         // Palm: Day=0(sun)-6(sat); week=0-4, 4=last week; pos=week*7+day
00387         // libkcal: day=bit0(mon)-bit6(sun); week=-5to-1(from end) and 1-5 (from beginning)
00388         // PC->Palm: pos=week*7+day
00389         //  week: if w=-1 -> week=4, else week=w-1
00390         //  day: day=(daybit+1)%7  (rotate because of the different offset)
00391         dateEntry->setRepeatType(repeatMonthlyByDay);
00392         if (r->monthPositions().count()>0)
00393         {
00394             // Only take the first monthly position, as the palm allows only one
00395             QValueList<KCal::RecurrenceRule::WDayPos> mps=r->monthPositions();
00396             KCal::RecurrenceRule::WDayPos mp=mps.first();
00397             int week = mp.pos();
00398             int day = (mp.day()+1) % 7; // rotate because of different offset
00399             // turn to 0-based and include starting from end of month
00400             // TODO: We don't handle counting from the end of the month yet!
00401             if (week==-1) week=4; else week--;
00402             dateEntry->setRepeatDay(static_cast<DayOfMonthType>(7*week + day));
00403         }
00404         break;
00405     case KCal::Recurrence::rMonthlyDay:
00406         dateEntry->setRepeatType(repeatMonthlyByDate);
00407 //TODO: is this needed?     dateEntry->setRepeatDay(static_cast<DayOfMonthType>(startDt.day()));
00408         break;
00409     case KCal::Recurrence::rYearlyDay:
00410     case KCal::Recurrence::rYearlyPos:
00411         DEBUGKPILOT << fname
00412             << "! Unsupported yearly recurrence type." << endl;
00413     case KCal::Recurrence::rYearlyMonth:
00414         dateEntry->setRepeatType(repeatYearly);
00415         break;
00416     case KCal::Recurrence::rNone:
00417         if (!isMultiDay) dateEntry->setRepeatType(repeatNone);
00418         break;
00419     default:
00420 #ifdef DEBUG
00421         DEBUGKPILOT << fname << ": Unknown recurrence type "<< recType << " with frequency "
00422             << freq << " and duration " << r->duration() << endl;
00423 #endif
00424         break;
00425     }
00426 }
00427 
00428 
00429 static void setExceptions(PilotDateEntry *dateEntry, const KCal::Event *vevent )
00430 {
00431     FUNCTIONSETUP;
00432     struct tm *ex_List;
00433 
00434     if (!dateEntry || !vevent)
00435     {
00436         WARNINGKPILOT << "NULL dateEntry or NULL vevent given for exceptions. Skipping exceptions" << endl;
00437         return;
00438     }
00439     // first, we need to delete the old exceptions list, if it existed...
00440     // This is no longer needed, as I fixed PilotDateEntry::setExceptions to do this automatically
00441 /*  ex_List=const_cast<structdateEntry->getExceptions();
00442     if (ex_List)
00443         KPILOT_DELETE(ex_List);*/
00444 
00445     KCal::DateList exDates = vevent->recurrence()->exDates();
00446     size_t excount = exDates.size();
00447     if (excount<1)
00448     {
00449         dateEntry->setExceptionCount(0);
00450         dateEntry->setExceptions(0);
00451         return;
00452     }
00453 
00454     // we have exceptions, so allocate mem and copy them there...
00455     ex_List=new struct tm[excount];
00456     if (!ex_List)
00457     {
00458         WARNINGKPILOT << "Couldn't allocate memory for the exceptions" << endl;
00459         dateEntry->setExceptionCount(0);
00460         dateEntry->setExceptions(0);
00461         return;
00462     }
00463 
00464     size_t n=0;
00465 
00466     KCal::DateList::ConstIterator dit;
00467     for (dit = exDates.begin(); dit != exDates.end(); ++dit ) {
00468         struct tm ttm=writeTm(*dit);
00469         ex_List[n++]=ttm;
00470     }
00471     dateEntry->setExceptionCount(excount);
00472     dateEntry->setExceptions(ex_List);
00473 }
00474 
00475 
00476 bool KCalSync::setEvent(KCal::Event *e,
00477     const PilotDateEntry *de,
00478     const CategoryAppInfo &info)
00479 {
00480     FUNCTIONSETUP;
00481     if (!e)
00482     {
00483         DEBUGKPILOT << fname
00484             << "! NULL event given... Skipping it" << endl;
00485         return false;
00486     }
00487     if (!de)
00488     {
00489         DEBUGKPILOT << fname
00490             << "! NULL date entry given... Skipping it" << endl;
00491         return false;
00492     }
00493 
00494 
00495     e->setSecrecy(de->isSecret() ?
00496         KCal::Event::SecrecyPrivate :
00497         KCal::Event::SecrecyPublic);
00498 
00499     e->setPilotId(de->id());
00500 
00501     setStartEndTimes(e,de);
00502     setAlarms(e,de);
00503     setRecurrence(e,de);
00504     setExceptions(e,de);
00505 
00506     e->setSummary(de->getDescription());
00507     e->setDescription(de->getNote());
00508     e->setLocation(de->getLocation());
00509 
00510     // used by e.g. Agendus and Datebk
00511     setCategory(e, de, info);
00512 
00513     // NOTE: This MUST be done last, since every other set* call
00514     // calls updated(), which will trigger an
00515     // setSyncStatus(SYNCMOD)!!!
00516     e->setSyncStatus(KCal::Incidence::SYNCNONE);
00517 
00518     return true;
00519 }
00520 
00521 bool KCalSync::setDateEntry(PilotDateEntry *de,
00522     const KCal::Event *e,
00523     const CategoryAppInfo &info)
00524 {
00525     FUNCTIONSETUP;
00526     if (!de || !e) {
00527         DEBUGKPILOT << fname
00528             << ": NULL event given... Skipping it" << endl;
00529         return false;
00530     }
00531 
00532     // set secrecy, start/end times, alarms, recurrence, exceptions, summary and description:
00533     if (e->secrecy()!=KCal::Event::SecrecyPublic)
00534     {
00535         de->setSecret( true );
00536     }
00537 
00538     setStartEndTimes(de, e);
00539     setAlarms(de, e);
00540     setRecurrence(de, e);
00541     setExceptions(de, e);
00542     de->setDescription(e->summary());
00543     de->setNote(e->description());
00544     de->setLocation(e->location());
00545     setCategory(de, e, info);
00546     return true;
00547 }
00548