Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | File Members | Related Pages

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 }

Generated on Sat Nov 5 16:18:05 2005 for OPIE by  doxygen 1.4.2