libopie PIM API Documentation

orecur.cpp

Go to the documentation of this file.
00001 #include <time.h>
00002 
00003 #include <qshared.h>
00004 
00005 #include <qtopia/timeconversion.h>
00006 
00007 #include "otimezone.h"
00008 #include "orecur.h"
00009 
00010 struct ORecur::Data : public QShared {
00011     Data() : QShared() {
00012         type = ORecur::NoRepeat;
00013         freq = -1;
00014         days = 0;
00015         pos = 0;
00016         create = QDateTime::currentDateTime();
00017         hasEnd = FALSE;
00018         end = QDate::currentDate();
00019     }
00020     char days; // Q_UINT8 for 8 seven days;)
00021     ORecur::RepeatType type;
00022     int freq;
00023     int pos;
00024     bool hasEnd : 1;
00025     QDate end;
00026     QDateTime create;
00027     int rep;
00028     QString app;
00029     ExceptionList list;
00030     QDate start;
00031 };
00032 
00033 
00034 ORecur::ORecur() {
00035     data = new Data;
00036 }
00037 
00038 ORecur::ORecur( const QMap<int, QString>& map )
00039 {
00040     ORecur();
00041     fromMap( map );
00042 }
00043 
00044 
00045 ORecur::ORecur( const ORecur& rec)
00046     : data( rec.data )
00047 {
00048     data->ref();
00049 }
00050 ORecur::~ORecur() {
00051     if ( data->deref() ) {
00052         delete data;
00053         data = 0l;
00054     }
00055 }
00056 void ORecur::deref() {
00057     if ( data->deref() ) {
00058         delete data;
00059         data = 0l;
00060     }
00061 }
00062 bool ORecur::operator==( const ORecur& )const {
00063     return false;
00064 }
00065 ORecur &ORecur::operator=( const ORecur& re) {
00066     if ( *this == re ) return *this;
00067 
00068     re.data->ref();
00069     deref();
00070     data = re.data;
00071 
00072     return *this;
00073 }
00074 bool ORecur::doesRecur()const {
00075     return !( type() == NoRepeat );
00076 }
00077 /*
00078  * we try to be smart here
00079  *
00080  */
00081 bool ORecur::doesRecur( const QDate& date ) {
00082     /* the day before the recurrance */
00083     QDate da = date.addDays(-1);
00084 
00085     QDate recur;
00086     if (!nextOcurrence( da, recur ) )
00087         return false;
00088 
00089     return (recur == date);
00090 }
00091 // FIXME unuglify!
00092 // GPL from Datebookdb.cpp
00093 // FIXME exception list!
00094 bool ORecur::nextOcurrence( const QDate& from, QDate& next ) {
00095     bool stillLooking;
00096     stillLooking  = p_nextOccurrence( from, next );
00097     while ( stillLooking && data->list.contains(next) )
00098         stillLooking = p_nextOccurrence( next.addDays(1), next );
00099 
00100     return stillLooking;
00101 }
00102 bool ORecur::p_nextOccurrence( const QDate& from, QDate& next ) {
00103 
00104    // easy checks, first are we too far in the future or too far in the past?
00105     QDate tmpDate;
00106     int freq = frequency();
00107     int diff, diff2, a;
00108     int iday, imonth, iyear;
00109     int dayOfWeek = 0;
00110     int firstOfWeek = 0;
00111     int weekOfMonth;
00112 
00113 
00114     if (hasEndDate() && endDate() < from)
00115     return FALSE;
00116 
00117     if (start() >= from  ) {
00118     next = start();
00119     return TRUE;
00120     }
00121 
00122     switch ( type() ) {
00123     case Weekly:
00124         /* weekly is just daily by 7 */
00125         /* first convert the repeatPattern.Days() mask to the next
00126            day of week valid after from */
00127             dayOfWeek = from.dayOfWeek();
00128         dayOfWeek--; /* we want 0-6, doco for above specs 1-7 */
00129 
00130         /* this is done in case freq > 1 and from in week not
00131            for this round */
00132         // firstOfWeek = 0; this is already done at decl.
00133         while(!((1 << firstOfWeek) & days() ))
00134         firstOfWeek++;
00135 
00136         /* there is at least one 'day', or there would be no event */
00137         while(!((1 << (dayOfWeek % 7)) & days() ))
00138         dayOfWeek++;
00139 
00140         dayOfWeek = dayOfWeek % 7; /* the actual day of week */
00141         dayOfWeek -= start().dayOfWeek() -1;
00142 
00143         firstOfWeek = firstOfWeek % 7; /* the actual first of week */
00144         firstOfWeek -= start().dayOfWeek() -1;
00145 
00146         // dayOfWeek may be negitive now
00147         // day of week is number of days to add to start day
00148 
00149         freq *= 7;
00150         // FALL-THROUGH !!!!!
00151     case Daily:
00152         // the add is for the possible fall through from weekly */
00153         if(start().addDays(dayOfWeek) > from) {
00154         /* first week exception */
00155         next = QDate(start().addDays(dayOfWeek) );
00156         if ((next > endDate())
00157             && hasEndDate() )
00158             return FALSE;
00159         return TRUE;
00160         }
00161         /* if from is middle of a non-week */
00162 
00163         diff = start().addDays(dayOfWeek).daysTo(from) % freq;
00164         diff2 = start().addDays(firstOfWeek).daysTo(from) % freq;
00165 
00166         if(diff != 0)
00167         diff = freq - diff;
00168         if(diff2 != 0)
00169         diff2 = freq - diff2;
00170         diff = QMIN(diff, diff2);
00171 
00172         next = QDate(from.addDays(diff));
00173         if ( (next > endDate())
00174          && hasEndDate() )
00175         return FALSE;
00176         return TRUE;
00177     case MonthlyDay:
00178         iday = from.day();
00179         iyear = from.year();
00180         imonth = from.month();
00181         /* find equivelent day of month for this month */
00182         dayOfWeek = start().dayOfWeek();
00183         weekOfMonth = (start().day() - 1) / 7;
00184 
00185         /* work out when the next valid month is */
00186         a = from.year() - start().year();
00187         a *= 12;
00188         a = a + (imonth - start().month());
00189         /* a is e.start()monthsFrom(from); */
00190         if(a % freq) {
00191         a = freq - (a % freq);
00192         imonth = from.month() + a;
00193         if (imonth > 12) {
00194             imonth--;
00195             iyear += imonth / 12;
00196             imonth = imonth % 12;
00197             imonth++;
00198         }
00199         }
00200         /* imonth is now the first month after or on
00201            from that matches the frequency given */
00202 
00203         /* find for this month */
00204         tmpDate = QDate( iyear, imonth, 1 );
00205 
00206         iday = 1;
00207         iday += (7 + dayOfWeek - tmpDate.dayOfWeek()) % 7;
00208         iday += 7 * weekOfMonth;
00209         while (iday > tmpDate.daysInMonth()) {
00210         imonth += freq;
00211         if (imonth > 12) {
00212             imonth--;
00213             iyear += imonth / 12;
00214             imonth = imonth % 12;
00215             imonth++;
00216         }
00217         tmpDate = QDate( iyear, imonth, 1 );
00218         /* these loops could go for a while, check end case now */
00219         if ((tmpDate > endDate()) && hasEndDate() )
00220             return FALSE;
00221         iday = 1;
00222         iday += (7 + dayOfWeek - tmpDate.dayOfWeek()) % 7;
00223         iday += 7 * weekOfMonth;
00224         }
00225         tmpDate = QDate(iyear, imonth, iday);
00226 
00227         if (tmpDate >= from) {
00228         next = tmpDate;
00229         if ((next > endDate() ) && hasEndDate() )
00230             return FALSE;
00231         return TRUE;
00232         }
00233 
00234         /* need to find the next iteration */
00235         do {
00236         imonth += freq;
00237         if (imonth > 12) {
00238             imonth--;
00239             iyear += imonth / 12;
00240             imonth = imonth % 12;
00241             imonth++;
00242         }
00243         tmpDate = QDate( iyear, imonth, 1 );
00244         /* these loops could go for a while, check end case now */
00245         if ((tmpDate > endDate()) && hasEndDate() )
00246             return FALSE;
00247         iday = 1;
00248         iday += (7 + dayOfWeek - tmpDate.dayOfWeek()) % 7;
00249         iday += 7 * weekOfMonth;
00250         } while (iday > tmpDate.daysInMonth());
00251         tmpDate = QDate(iyear, imonth, iday);
00252 
00253         next = tmpDate;
00254         if ((next > endDate()) && hasEndDate() )
00255         return FALSE;
00256         return TRUE;
00257     case MonthlyDate:
00258         iday = start().day();
00259         iyear = from.year();
00260         imonth = from.month();
00261 
00262         a = from.year() - start().year();
00263         a *= 12;
00264         a = a + (imonth - start().month());
00265         /* a is e.start()monthsFrom(from); */
00266         if(a % freq) {
00267         a = freq - (a % freq);
00268         imonth = from.month() + a;
00269         if (imonth > 12) {
00270             imonth--;
00271             iyear += imonth / 12;
00272             imonth = imonth % 12;
00273             imonth++;
00274         }
00275         }
00276         /* imonth is now the first month after or on
00277            from that matches the frequencey given */
00278 
00279         /* this could go for a while, worse case, 4*12 iterations, probably */
00280         while(!QDate::isValid(iyear, imonth, iday) ) {
00281         imonth += freq;
00282         if (imonth > 12) {
00283             imonth--;
00284             iyear += imonth / 12;
00285             imonth = imonth % 12;
00286             imonth++;
00287         }
00288         /* these loops could go for a while, check end case now */
00289         if ((QDate(iyear, imonth, 1) > endDate()) && hasEndDate() )
00290             return FALSE;
00291         }
00292 
00293         if(QDate(iyear, imonth, iday) >= from) {
00294         /* done */
00295         next = QDate(iyear, imonth, iday);
00296         if ((next > endDate()) && hasEndDate() )
00297             return FALSE;
00298         return TRUE;
00299         }
00300 
00301         /* ok, need to cycle */
00302         imonth += freq;
00303         imonth--;
00304         iyear += imonth / 12;
00305         imonth = imonth % 12;
00306         imonth++;
00307 
00308         while(!QDate::isValid(iyear, imonth, iday) ) {
00309         imonth += freq;
00310         imonth--;
00311         iyear += imonth / 12;
00312         imonth = imonth % 12;
00313         imonth++;
00314         if ((QDate(iyear, imonth, 1) > endDate()) && hasEndDate() )
00315             return FALSE;
00316         }
00317 
00318         next = QDate(iyear, imonth, iday);
00319         if ((next > endDate()) && hasEndDate() )
00320         return FALSE;
00321         return TRUE;
00322     case Yearly:
00323         iday = start().day();
00324         imonth = start().month();
00325         iyear = from.year(); // after all, we want to start in this year
00326 
00327         diff = 1;
00328         if(imonth == 2 && iday > 28) {
00329         /* leap year, and it counts, calculate actual frequency */
00330         if(freq % 4)
00331             if (freq % 2)
00332             freq = freq * 4;
00333             else
00334             freq = freq * 2;
00335         /* else divides by 4 already, leave freq alone */
00336         diff = 4;
00337         }
00338 
00339         a = from.year() - start().year();
00340         if(a % freq) {
00341         a = freq - (a % freq);
00342         iyear = iyear + a;
00343         }
00344 
00345         /* under the assumption we won't hit one of the special not-leap years twice */
00346         if(!QDate::isValid(iyear, imonth, iday)) {
00347         /* must have been skipping by leap years and hit one that wasn't, (e.g. 2100) */
00348         iyear += freq;
00349         }
00350 
00351         if(QDate(iyear, imonth, iday) >= from) {
00352         next = QDate(iyear, imonth, iday);
00353 
00354         if ((next > endDate()) && hasEndDate() )
00355             return FALSE;
00356         return TRUE;
00357         }
00358         /* iyear == from.year(), need to advance again */
00359         iyear += freq;
00360         /* under the assumption we won't hit one of the special not-leap years twice */
00361         if(!QDate::isValid(iyear, imonth, iday)) {
00362         /* must have been skipping by leap years and hit one that wasn't, (e.g. 2100) */
00363         iyear += freq;
00364         }
00365 
00366         next = QDate(iyear, imonth, iday);
00367         if ((next > endDate()) && hasEndDate() )
00368         return FALSE;
00369         return TRUE;
00370     default:
00371         return FALSE;
00372     }
00373 }
00374 ORecur::RepeatType ORecur::type()const{
00375     return data->type;
00376 }
00377 int ORecur::frequency()const {
00378     return data->freq;
00379 }
00380 int ORecur::position()const {
00381     return data->pos;
00382 }
00383 char ORecur::days() const{
00384     return data->days;
00385 }
00386 bool ORecur::hasEndDate()const {
00387     return data->hasEnd;
00388 }
00389 QDate ORecur::endDate()const {
00390     return data->end;
00391 }
00392 QDate ORecur::start()const{
00393     return data->start;
00394 }
00395 QDateTime ORecur::createdDateTime()const {
00396     return data->create;
00397 }
00398 int ORecur::repetition()const {
00399     return data->rep;
00400 }
00401 QString ORecur::service()const {
00402     return data->app;
00403 }
00404 ORecur::ExceptionList& ORecur::exceptions() {
00405     return data->list;
00406 }
00407 void ORecur::setType( const RepeatType& z) {
00408     checkOrModify();
00409     data->type = z;
00410 }
00411 void ORecur::setFrequency( int freq ) {
00412     checkOrModify();
00413     data->freq = freq;
00414 }
00415 void ORecur::setPosition( int pos ) {
00416     checkOrModify();
00417     data->pos = pos;
00418 }
00419 void ORecur::setDays( char c ) {
00420     checkOrModify();
00421     data->days = c;
00422 }
00423 void ORecur::setEndDate( const QDate& dt) {
00424     checkOrModify();
00425     data->end = dt;
00426 }
00427 void ORecur::setCreatedDateTime( const QDateTime& t) {
00428     checkOrModify();
00429     data->create = t;
00430 }
00431 void ORecur::setHasEndDate( bool b) {
00432     checkOrModify();
00433     data->hasEnd = b;
00434 }
00435 void ORecur::setRepitition( int rep ) {
00436     checkOrModify();
00437     data->rep = rep;
00438 }
00439 void ORecur::setService( const QString& app ) {
00440     checkOrModify();
00441     data->app = app;
00442 }
00443 void ORecur::setStart( const QDate& dt ) {
00444     checkOrModify();
00445     data->start = dt;
00446 }
00447 void ORecur::checkOrModify() {
00448     if ( data->count !=  1 ) {
00449         data->deref();
00450         Data* d2 = new Data;
00451         d2->days = data->days;
00452         d2->type = data->type;
00453         d2->freq = data->freq;
00454         d2->pos  = data->pos;
00455         d2->hasEnd = data->hasEnd;
00456         d2->end  = data->end;
00457         d2->create = data->create;
00458         d2->rep = data->rep;
00459         d2->app = data->app;
00460         d2->list = data->list;
00461         d2->start = data->start;
00462         data = d2;
00463     }
00464 }
00465 QString ORecur::toString()const {
00466     QString buf;
00467     QMap<int, QString> recMap = toMap();
00468 
00469     buf += " rtype=\"";
00470     buf += recMap[ORecur::RType];
00471     buf += "\"";
00472     if (data->days > 0 )
00473     buf += " rweekdays=\"" + recMap[ORecur::RWeekdays] + "\"";
00474     if ( data->pos != 0 )
00475     buf += " rposition=\"" + recMap[ORecur::RPosition] + "\"";
00476 
00477     buf += " rfreq=\"" + recMap[ORecur::RFreq] + "\"";
00478     buf += " rhasenddate=\"" + recMap[ORecur::RHasEndDate]+ "\"";
00479     if ( data->hasEnd )
00480     buf += " enddt=\""
00481            + recMap[ORecur::EndDate]
00482            + "\"";
00483     buf += " created=\"" + recMap[ORecur::Created] + "\"";
00484 
00485     if ( data->list.isEmpty() ) return buf;
00486     buf += " exceptions=\"";
00487     buf += recMap[ORecur::Exceptions];
00488     buf += "\" ";
00489 
00490     return buf;
00491 }
00492 
00493 QString ORecur::rTypeString() const
00494 {
00495     QString retString;
00496     switch ( data->type ) {
00497     case ORecur::Daily:
00498         retString = "Daily";
00499         break;
00500     case ORecur::Weekly:
00501         retString = "Weekly";
00502         break;
00503     case ORecur::MonthlyDay:
00504         retString = "MonthlyDay";
00505         break;
00506     case ORecur::MonthlyDate:
00507         retString = "MonthlyDate";
00508         break;
00509     case ORecur::Yearly:
00510         retString = "Yearly";
00511         break;
00512     default:
00513         retString = "NoRepeat";
00514         break;
00515 
00516     }
00517 
00518     return retString;
00519 }
00520 
00521 QMap<QString, ORecur::RepeatType> ORecur::rTypeValueConvertMap() const
00522 {
00523     QMap<QString, RepeatType> convertMap;
00524 
00525     convertMap.insert( QString( "Daily" ), ORecur::Daily );
00526     convertMap.insert( QString( "Weekly" ), ORecur::Weekly );
00527     convertMap.insert( QString( "MonthlyDay" ), ORecur::MonthlyDay );
00528     convertMap.insert( QString( "MonthlyDate" ), ORecur::MonthlyDate );
00529     convertMap.insert( QString( "Yearly" ), ORecur::Yearly );
00530     convertMap.insert( QString( "NoRepeat" ), ORecur::NoRepeat );
00531 
00532     return convertMap;
00533 }
00534 
00535 
00536 QMap<int, QString> ORecur::toMap() const
00537 {
00538     QMap<int, QString> retMap;
00539     
00540     retMap.insert( ORecur::RType, rTypeString() );
00541     retMap.insert( ORecur::RWeekdays, QString::number( static_cast<int>( data->days ) ) );
00542     retMap.insert( ORecur::RPosition, QString::number(data->pos ) );
00543     retMap.insert( ORecur::RFreq, QString::number( data->freq ) );
00544     retMap.insert( ORecur::RHasEndDate, QString::number( static_cast<int>( data->hasEnd ) ) );
00545     if( data -> hasEnd )
00546         retMap.insert( ORecur::EndDate, QString::number( OTimeZone::utc().fromUTCDateTime( QDateTime( data->end, QTime(12,0,0) ) ) ) );
00547     retMap.insert( ORecur::Created, QString::number( OTimeZone::utc().fromUTCDateTime( data->create ) ) );
00548     
00549     if ( data->list.isEmpty() ) return retMap;
00550 
00551     // save exceptions list here!!
00552     ExceptionList::ConstIterator it;
00553     ExceptionList list = data->list;
00554     QString exceptBuf;
00555     QDate date;
00556     for ( it = list.begin(); it != list.end(); ++it ) {
00557         date = (*it);
00558         if ( it != list.begin() ) exceptBuf += " ";
00559         
00560         exceptBuf += QCString().sprintf("%04d%02d%02d", date.year(), date.month(), date.day() );
00561     }
00562 
00563     retMap.insert( ORecur::Exceptions, exceptBuf );
00564 
00565     return retMap;
00566 }
00567 
00568 void ORecur::fromMap( const QMap<int, QString>& map )
00569 {
00570     QMap<QString, RepeatType> repTypeMap = rTypeValueConvertMap(); 
00571 
00572     data -> type  = repTypeMap[ map [ORecur::RType] ];
00573     data -> days  = (char) map[ ORecur::RWeekdays ].toInt();
00574     data -> pos   = map[ ORecur::RPosition ].toInt();
00575     data -> freq = map[ ORecur::RFreq ].toInt();
00576     data -> hasEnd= map[ ORecur::RHasEndDate ].toInt() ? true : false;
00577     OTimeZone utc = OTimeZone::utc();
00578     if ( data -> hasEnd ){
00579         data -> end = utc.fromUTCDateTime( (time_t) map[ ORecur::EndDate ].toLong() ).date();
00580     }
00581     data -> create = utc.fromUTCDateTime( (time_t) map[ ORecur::Created ].toLong() ).date();
00582 
00583 #if 0
00584     // FIXME: Exceptions currently not supported...
00585     // Convert the list of exceptions from QString into ExceptionList
00586     data -> list.clear();
00587     QString exceptStr = map[ ORecur::Exceptions ];
00588     QStringList exceptList = QStringList::split( " ", exceptStr );
00589     ...
00590 #endif
00591     
00592     
00593 }
KDE Logo
This file is part of the documentation for OPIE Version 1.1.
Documentation copyright © 1997-2003 the KDE developers. 2003 OPIE developers
Generated on Tue Feb 10 20:25:21 2004 by doxygen 1.3.5 written by Dimitri van Heesch, © 1997-2001