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

opimrecurrence.cpp

Go to the documentation of this file.
00001 /*
00002                              This file is part of the Opie Project
00003                              Copyright (C) The Main Author <main-author@whereever.org>
00004               =.             Copyright (C) The Opie Team <opie-devel@handhelds.org>
00005             .=l.
00006            .>+-=
00007  _;:,     .>    :=|.         This program is free software; you can
00008 .> <`_,   >  .   <=          redistribute it and/or  modify it under
00009 :`=1 )Y*s>-.--   :           the terms of the GNU Library General Public
00010 .="- .-=="i,     .._         License as published by the Free Software
00011  - .   .-<_>     .<>         Foundation; either version 2 of the License,
00012      ._= =}       :          or (at your option) any later version.
00013     .%`+i>       _;_.
00014     .i_,=:_.      -<s.       This program is distributed in the hope that
00015      +  .  -:.       =       it will be useful,  but WITHOUT ANY WARRANTY;
00016     : ..    .:,     . . .    without even the implied warranty of
00017     =_        +     =;=|`    MERCHANTABILITY or FITNESS FOR A
00018   _.=:.       :    :=>`:     PARTICULAR PURPOSE. See the GNU
00019 ..}^=.=       =       ;      Library General Public License for more
00020 ++=   -.     .`     .:       details.
00021  :     =  ...= . :.=-
00022  -.   .:....=;==+<;          You should have received a copy of the GNU
00023   -_. . .   )=.  =           Library General Public License along with
00024     --        :-=`           this library; see the file COPYING.LIB.
00025                              If not, write to the Free Software Foundation,
00026                              Inc., 59 Temple Place - Suite 330,
00027                              Boston, MA 02111-1307, USA.
00028 */
00029 
00030 #include "opimrecurrence.h"
00031 
00032 /* OPIE */
00033 #include <opie2/opimtimezone.h>
00034 #include <qpe/timeconversion.h>
00035 
00036 /* QT */
00037 #include <qshared.h>
00038 
00039 /* STD */
00040 #include <time.h>
00041 
00042 namespace Opie {
00043 
00044 struct OPimRecurrence::Data : public QShared {
00045     Data() : QShared() {
00046         type = OPimRecurrence::NoRepeat;
00047         freq = -1;
00048         days = 0;
00049         pos = 0;
00050         create = QDateTime::currentDateTime();
00051         hasEnd = FALSE;
00052         end = QDate::currentDate();
00053     }
00054     char days; // Q_UINT8 for 8 seven days;)
00055     OPimRecurrence::RepeatType type;
00056     int freq;
00057     int pos;
00058     bool hasEnd : 1;
00059     QDate end;
00060     QDateTime create;
00061     int rep;
00062     QString app;
00063     ExceptionList list;
00064     QDate start;
00065 };
00066 
00067 
00068 OPimRecurrence::OPimRecurrence() {
00069     data = new Data;
00070 }
00071 
00072 OPimRecurrence::OPimRecurrence( const QMap<int, QString>& map )
00073 {
00074     data = new Data;
00075     fromMap( map );
00076 }
00077 
00078 
00079 OPimRecurrence::OPimRecurrence( const OPimRecurrence& rec)
00080     : data( rec.data )
00081 {
00082     data->ref();
00083 }
00084 
00085 
00086 OPimRecurrence::~OPimRecurrence() {
00087     if ( data->deref() ) {
00088         delete data;
00089         data = 0l;
00090     }
00091 }
00092 
00093 
00094 void OPimRecurrence::deref() {
00095     if ( data->deref() ) {
00096         delete data;
00097         data = 0l;
00098     }
00099 }
00100 
00101 
00102 bool OPimRecurrence::operator==( const OPimRecurrence& )const {
00103     return false;
00104 }
00105 
00106 
00107 OPimRecurrence &OPimRecurrence::operator=( const OPimRecurrence& re) {
00108     if ( *this == re ) return *this;
00109 
00110     re.data->ref();
00111     deref();
00112     data = re.data;
00113 
00114     return *this;
00115 }
00116 
00117 
00118 bool OPimRecurrence::doesRecur()const {
00119     return !( type() == NoRepeat );
00120 }
00121 
00122 
00123 /*
00124  * we try to be smart here
00125  *
00126  */
00127 bool OPimRecurrence::doesRecur( const QDate& date ) {
00128     /* the day before the recurrance */
00129     QDate da = date.addDays(-1);
00130 
00131     QDate recur;
00132     if (!nextOcurrence( da, recur ) )
00133         return false;
00134 
00135     return (recur == date);
00136 }
00137 
00138 
00139 // FIXME unuglify!
00140 // GPL from Datebookdb.cpp
00141 // FIXME exception list!
00142 bool OPimRecurrence::nextOcurrence( const QDate& from, QDate& next ) {
00143     bool stillLooking;
00144     stillLooking  = p_nextOccurrence( from, next );
00145     while ( stillLooking && data->list.contains(next) )
00146         stillLooking = p_nextOccurrence( next.addDays(1), next );
00147 
00148     return stillLooking;
00149 }
00150 
00151 
00152 bool OPimRecurrence::p_nextOccurrence( const QDate& from, QDate& next ) {
00153 
00154    // easy checks, first are we too far in the future or too far in the past?
00155     QDate tmpDate;
00156     int freq = frequency();
00157     int diff, diff2, a;
00158     int iday, imonth, iyear;
00159     int dayOfWeek = 0;
00160     int firstOfWeek = 0;
00161     int weekOfMonth;
00162 
00163 
00164     if (hasEndDate() && endDate() < from)
00165         return FALSE;
00166 
00167     if (start() >= from  ) {
00168         next = start();
00169         return TRUE;
00170     }
00171 
00172     switch ( type() ) {
00173         case Weekly:
00174             /* weekly is just daily by 7 */
00175             /* first convert the repeatPattern.Days() mask to the next
00176             day of week valid after from */
00177             dayOfWeek = from.dayOfWeek();
00178             dayOfWeek--; /* we want 0-6, doco for above specs 1-7 */
00179 
00180             /* this is done in case freq > 1 and from in week not
00181             for this round */
00182             // firstOfWeek = 0; this is already done at decl.
00183             while(!((1 << firstOfWeek) & days() ))
00184                 firstOfWeek++;
00185 
00186             /* there is at least one 'day', or there would be no event */
00187             while(!((1 << (dayOfWeek % 7)) & days() ))
00188                 dayOfWeek++;
00189 
00190             dayOfWeek = dayOfWeek % 7; /* the actual day of week */
00191             dayOfWeek -= start().dayOfWeek() -1;
00192 
00193             firstOfWeek = firstOfWeek % 7; /* the actual first of week */
00194             firstOfWeek -= start().dayOfWeek() -1;
00195 
00196             // dayOfWeek may be negitive now
00197             // day of week is number of days to add to start day
00198 
00199             freq *= 7;
00200             // FALL-THROUGH !!!!!
00201         case Daily:
00202             // the add is for the possible fall through from weekly */
00203             if(start().addDays(dayOfWeek) > from) {
00204                 /* first week exception */
00205                 next = QDate(start().addDays(dayOfWeek) );
00206                 if ((next > endDate())
00207                     && hasEndDate() )
00208                     return FALSE;
00209                 return TRUE;
00210             }
00211             /* if from is middle of a non-week */
00212 
00213             diff = start().addDays(dayOfWeek).daysTo(from) % freq;
00214             diff2 = start().addDays(firstOfWeek).daysTo(from) % freq;
00215 
00216             if(diff != 0)
00217                 diff = freq - diff;
00218             if(diff2 != 0)
00219                 diff2 = freq - diff2;
00220             diff = QMIN(diff, diff2);
00221 
00222             next = QDate(from.addDays(diff));
00223             if ( (next > endDate())
00224                 && hasEndDate() )
00225                 return FALSE;
00226             return TRUE;
00227         case MonthlyDay:
00228             iday = from.day();
00229             iyear = from.year();
00230             imonth = from.month();
00231             /* find equivelent day of month for this month */
00232             dayOfWeek = start().dayOfWeek();
00233             weekOfMonth = (start().day() - 1) / 7;
00234 
00235             /* work out when the next valid month is */
00236             a = from.year() - start().year();
00237             a *= 12;
00238             a = a + (imonth - start().month());
00239             /* a is e.start()monthsFrom(from); */
00240             if(a % freq) {
00241                 a = freq - (a % freq);
00242                 imonth = from.month() + a;
00243                 if (imonth > 12) {
00244                     imonth--;
00245                     iyear += imonth / 12;
00246                     imonth = imonth % 12;
00247                     imonth++;
00248                 }
00249             }
00250             /* imonth is now the first month after or on
00251             from that matches the frequency given */
00252 
00253             /* find for this month */
00254             tmpDate = QDate( iyear, imonth, 1 );
00255 
00256             iday = 1;
00257             iday += (7 + dayOfWeek - tmpDate.dayOfWeek()) % 7;
00258             iday += 7 * weekOfMonth;
00259             while (iday > tmpDate.daysInMonth()) {
00260                 imonth += freq;
00261                 if (imonth > 12) {
00262                     imonth--;
00263                     iyear += imonth / 12;
00264                     imonth = imonth % 12;
00265                     imonth++;
00266                 }
00267                 tmpDate = QDate( iyear, imonth, 1 );
00268                 /* these loops could go for a while, check end case now */
00269                 if ((tmpDate > endDate()) && hasEndDate() )
00270                     return FALSE;
00271                 iday = 1;
00272                 iday += (7 + dayOfWeek - tmpDate.dayOfWeek()) % 7;
00273                 iday += 7 * weekOfMonth;
00274             }
00275             tmpDate = QDate(iyear, imonth, iday);
00276 
00277             if (tmpDate >= from) {
00278                 next = tmpDate;
00279                 if ((next > endDate() ) && hasEndDate() )
00280                     return FALSE;
00281                 return TRUE;
00282             }
00283 
00284             /* need to find the next iteration */
00285             do {
00286                 imonth += freq;
00287                 if (imonth > 12) {
00288                     imonth--;
00289                     iyear += imonth / 12;
00290                     imonth = imonth % 12;
00291                     imonth++;
00292                 }
00293                 tmpDate = QDate( iyear, imonth, 1 );
00294                 /* these loops could go for a while, check end case now */
00295                 if ((tmpDate > endDate()) && hasEndDate() )
00296                     return FALSE;
00297                 iday = 1;
00298                 iday += (7 + dayOfWeek - tmpDate.dayOfWeek()) % 7;
00299                 iday += 7 * weekOfMonth;
00300             } while (iday > tmpDate.daysInMonth());
00301             tmpDate = QDate(iyear, imonth, iday);
00302 
00303             next = tmpDate;
00304             if ((next > endDate()) && hasEndDate() )
00305                 return FALSE;
00306             return TRUE;
00307         case MonthlyDate:
00308             iday = start().day();
00309             iyear = from.year();
00310             imonth = from.month();
00311 
00312             a = from.year() - start().year();
00313             a *= 12;
00314             a = a + (imonth - start().month());
00315             /* a is e.start()monthsFrom(from); */
00316             if(a % freq) {
00317                 a = freq - (a % freq);
00318                 imonth = from.month() + a;
00319                 if (imonth > 12) {
00320                     imonth--;
00321                     iyear += imonth / 12;
00322                     imonth = imonth % 12;
00323                     imonth++;
00324                 }
00325             }
00326             /* imonth is now the first month after or on
00327             from that matches the frequencey given */
00328 
00329             /* this could go for a while, worse case, 4*12 iterations, probably */
00330             while(!QDate::isValid(iyear, imonth, iday) ) {
00331                 imonth += freq;
00332                 if (imonth > 12) {
00333                     imonth--;
00334                     iyear += imonth / 12;
00335                     imonth = imonth % 12;
00336                     imonth++;
00337                 }
00338                 /* these loops could go for a while, check end case now */
00339                 if ((QDate(iyear, imonth, 1) > endDate()) && hasEndDate() )
00340                     return FALSE;
00341             }
00342 
00343             if(QDate(iyear, imonth, iday) >= from) {
00344                 /* done */
00345                 next = QDate(iyear, imonth, iday);
00346                 if ((next > endDate()) && hasEndDate() )
00347                     return FALSE;
00348                 return TRUE;
00349             }
00350 
00351             /* ok, need to cycle */
00352             imonth += freq;
00353             imonth--;
00354             iyear += imonth / 12;
00355             imonth = imonth % 12;
00356             imonth++;
00357 
00358             while(!QDate::isValid(iyear, imonth, iday) ) {
00359                 imonth += freq;
00360                 imonth--;
00361                 iyear += imonth / 12;
00362                 imonth = imonth % 12;
00363                 imonth++;
00364                 if ((QDate(iyear, imonth, 1) > endDate()) && hasEndDate() )
00365                     return FALSE;
00366             }
00367 
00368             next = QDate(iyear, imonth, iday);
00369             if ((next > endDate()) && hasEndDate() )
00370                 return FALSE;
00371             return TRUE;
00372         case Yearly:
00373             iday = start().day();
00374             imonth = start().month();
00375             iyear = from.year(); // after all, we want to start in this year
00376 
00377             diff = 1;
00378             if(imonth == 2 && iday > 28) {
00379                 /* leap year, and it counts, calculate actual frequency */
00380                 if(freq % 4)
00381                     if (freq % 2)
00382                         freq = freq * 4;
00383                     else
00384                         freq = freq * 2;
00385                 /* else divides by 4 already, leave freq alone */
00386                 diff = 4;
00387             }
00388 
00389             a = from.year() - start().year();
00390             if(a % freq) {
00391                 a = freq - (a % freq);
00392                 iyear = iyear + a;
00393             }
00394 
00395             /* under the assumption we won't hit one of the special not-leap years twice */
00396             if(!QDate::isValid(iyear, imonth, iday)) {
00397                 /* must have been skipping by leap years and hit one that wasn't, (e.g. 2100) */
00398                 iyear += freq;
00399             }
00400 
00401             if(QDate(iyear, imonth, iday) >= from) {
00402                 next = QDate(iyear, imonth, iday);
00403 
00404                 if ((next > endDate()) && hasEndDate() )
00405                     return FALSE;
00406                 return TRUE;
00407             }
00408             /* iyear == from.year(), need to advance again */
00409             iyear += freq;
00410             /* under the assumption we won't hit one of the special not-leap years twice */
00411             if(!QDate::isValid(iyear, imonth, iday)) {
00412                 /* must have been skipping by leap years and hit one that wasn't, (e.g. 2100) */
00413                 iyear += freq;
00414             }
00415 
00416             next = QDate(iyear, imonth, iday);
00417             if ((next > endDate()) && hasEndDate() )
00418                 return FALSE;
00419             return TRUE;
00420         default:
00421             return FALSE;
00422     }
00423 }
00424 
00425 
00426 OPimRecurrence::RepeatType OPimRecurrence::type()const{
00427     return data->type;
00428 }
00429 
00430 
00431 int OPimRecurrence::frequency()const {
00432     return data->freq;
00433 }
00434 
00435 
00436 int OPimRecurrence::position()const {
00437     return data->pos;
00438 }
00439 
00440 
00441 char OPimRecurrence::days() const{
00442     return data->days;
00443 }
00444 
00445 
00446 bool OPimRecurrence::hasEndDate()const {
00447     return data->hasEnd;
00448 }
00449 
00450 
00451 QDate OPimRecurrence::endDate()const {
00452     return data->end;
00453 }
00454 
00455 
00456 QDate OPimRecurrence::start()const{
00457     return data->start;
00458 }
00459 
00460 
00461 QDateTime OPimRecurrence::createdDateTime()const {
00462     return data->create;
00463 }
00464 
00465 
00466 int OPimRecurrence::repetition()const {
00467     return data->rep;
00468 }
00469 
00470 
00471 QString OPimRecurrence::service()const {
00472     return data->app;
00473 }
00474 
00475 
00476 OPimRecurrence::ExceptionList& OPimRecurrence::exceptions() {
00477     return data->list;
00478 }
00479 
00480 
00481 void OPimRecurrence::setType( const RepeatType& z) {
00482     checkOrModify();
00483     data->type = z;
00484 }
00485 
00486 
00487 void OPimRecurrence::setFrequency( int freq ) {
00488     checkOrModify();
00489     data->freq = freq;
00490 }
00491 
00492 
00493 void OPimRecurrence::setPosition( int pos ) {
00494     checkOrModify();
00495     data->pos = pos;
00496 }
00497 
00498 
00499 void OPimRecurrence::setDays( char c ) {
00500     checkOrModify();
00501     data->days = c;
00502 }
00503 
00504 
00505 void OPimRecurrence::setEndDate( const QDate& dt) {
00506     checkOrModify();
00507     data->end = dt;
00508 }
00509 
00510 
00511 void OPimRecurrence::setCreatedDateTime( const QDateTime& t) {
00512     checkOrModify();
00513     data->create = t;
00514 }
00515 
00516 
00517 void OPimRecurrence::setHasEndDate( bool b) {
00518     checkOrModify();
00519     data->hasEnd = b;
00520 }
00521 
00522 
00523 void OPimRecurrence::setRepitition( int rep ) {
00524     checkOrModify();
00525     data->rep = rep;
00526 }
00527 
00528 
00529 void OPimRecurrence::setService( const QString& app ) {
00530     checkOrModify();
00531     data->app = app;
00532 }
00533 
00534 
00535 void OPimRecurrence::setStart( const QDate& dt ) {
00536     checkOrModify();
00537     data->start = dt;
00538 }
00539 
00540 
00541 void OPimRecurrence::checkOrModify() {
00542     if ( data->count !=  1 ) {
00543         data->deref();
00544         Data* d2 = new Data;
00545         d2->days = data->days;
00546         d2->type = data->type;
00547         d2->freq = data->freq;
00548         d2->pos  = data->pos;
00549         d2->hasEnd = data->hasEnd;
00550         d2->end  = data->end;
00551         d2->create = data->create;
00552         d2->rep = data->rep;
00553         d2->app = data->app;
00554         d2->list = data->list;
00555         d2->start = data->start;
00556         data = d2;
00557     }
00558 }
00559 
00560 
00561 QString OPimRecurrence::toString()const {
00562     QString buf;
00563     QMap<int, QString> recMap = toMap();
00564 
00565     buf += " rtype=\"";
00566     buf += recMap[OPimRecurrence::RType];
00567     buf += "\"";
00568     if (data->days > 0 )
00569         buf += " rweekdays=\"" + recMap[OPimRecurrence::RWeekdays] + "\"";
00570     if ( data->pos != 0 )
00571         buf += " rposition=\"" + recMap[OPimRecurrence::RPosition] + "\"";
00572 
00573     buf += " rfreq=\"" + recMap[OPimRecurrence::RFreq] + "\"";
00574     buf += " rhasenddate=\"" + recMap[OPimRecurrence::RHasEndDate]+ "\"";
00575     if ( data->hasEnd )
00576         buf += " enddt=\""
00577             + recMap[OPimRecurrence::EndDate]
00578             + "\"";
00579     buf += " created=\"" + recMap[OPimRecurrence::Created] + "\"";
00580 
00581     if ( data->list.isEmpty() ) return buf;
00582     buf += " exceptions=\"";
00583     buf += recMap[OPimRecurrence::Exceptions];
00584     buf += "\" ";
00585 
00586     return buf;
00587 }
00588 
00589 QString OPimRecurrence::rTypeString() const
00590 {
00591     QString retString;
00592     switch ( data->type ) {
00593     case OPimRecurrence::Daily:
00594             retString = "Daily";
00595             break;
00596     case OPimRecurrence::Weekly:
00597             retString = "Weekly";
00598             break;
00599     case OPimRecurrence::MonthlyDay:
00600             retString = "MonthlyDay";
00601             break;
00602     case OPimRecurrence::MonthlyDate:
00603             retString = "MonthlyDate";
00604             break;
00605     case OPimRecurrence::Yearly:
00606             retString = "Yearly";
00607             break;
00608     default:
00609             retString = "NoRepeat";
00610             break;
00611 
00612     }
00613 
00614     return retString;
00615 }
00616 
00617 QMap<QString, OPimRecurrence::RepeatType> OPimRecurrence::rTypeValueConvertMap() const
00618 {
00619         QMap<QString, RepeatType> convertMap;
00620 
00621         convertMap.insert( QString( "Daily" ), OPimRecurrence::Daily );
00622         convertMap.insert( QString( "Weekly" ), OPimRecurrence::Weekly );
00623         convertMap.insert( QString( "MonthlyDay" ), OPimRecurrence::MonthlyDay );
00624         convertMap.insert( QString( "MonthlyDate" ), OPimRecurrence::MonthlyDate );
00625         convertMap.insert( QString( "Yearly" ), OPimRecurrence::Yearly );
00626         convertMap.insert( QString( "NoRepeat" ), OPimRecurrence::NoRepeat );
00627 
00628         return convertMap;
00629 }
00630 
00631 
00632 QMap<int, QString> OPimRecurrence::toMap() const
00633 {
00634     QMap<int, QString> retMap;
00635 
00636     retMap.insert( OPimRecurrence::RType, rTypeString() );
00637     retMap.insert( OPimRecurrence::RWeekdays, QString::number( static_cast<int>( data->days ) ) );
00638     retMap.insert( OPimRecurrence::RPosition, QString::number(data->pos ) );
00639     retMap.insert( OPimRecurrence::RFreq, QString::number( data->freq ) );
00640     retMap.insert( OPimRecurrence::RHasEndDate, QString::number( static_cast<int>( data->hasEnd ) ) );
00641     if( data -> hasEnd )
00642             retMap.insert( OPimRecurrence::EndDate, QString::number( OPimTimeZone::current().fromUTCDateTime( QDateTime( data->end, QTime(12,0,0) ) ) ) );
00643     retMap.insert( OPimRecurrence::Created, QString::number( OPimTimeZone::current().fromUTCDateTime( data->create ) ) );
00644 
00645     if ( data->list.isEmpty() ) return retMap;
00646 
00647     // save exceptions list here!!
00648     ExceptionList::ConstIterator it;
00649     ExceptionList list = data->list;
00650     QString exceptBuf;
00651     QDate date;
00652     for ( it = list.begin(); it != list.end(); ++it ) {
00653             date = (*it);
00654             if ( it != list.begin() ) exceptBuf += " ";
00655 
00656             exceptBuf += QCString().sprintf("%04d%02d%02d", date.year(), date.month(), date.day() );
00657     }
00658 
00659     retMap.insert( OPimRecurrence::Exceptions, exceptBuf );
00660 
00661     return retMap;
00662 }
00663 
00664 void OPimRecurrence::fromMap( const QMap<int, QString>& map )
00665 {
00666     QMap<QString, RepeatType> repTypeMap = rTypeValueConvertMap();
00667 
00668     data -> type  = repTypeMap[ map [OPimRecurrence::RType] ];
00669     data -> days  = (char) map[ OPimRecurrence::RWeekdays ].toInt();
00670     data -> pos   = map[ OPimRecurrence::RPosition ].toInt();
00671     data -> freq = map[ OPimRecurrence::RFreq ].toInt();
00672     data -> hasEnd= map[ OPimRecurrence::RHasEndDate ].toInt() ? true : false;
00673     OPimTimeZone cur = OPimTimeZone::current();
00674     if ( data -> hasEnd ){
00675             data -> end = cur.fromUTCDateTime( (time_t) map[ OPimRecurrence::EndDate ].toLong() ).date();
00676     }
00677     data -> create = cur.fromUTCDateTime( (time_t) map[ OPimRecurrence::Created ].toLong() ).date();
00678 
00679 #if 0
00680     // FIXME: Exceptions currently not supported...
00681     // Convert the list of exceptions from QString into ExceptionList
00682     data -> list.clear();
00683     QString exceptStr = map[ OPimRecurrence::Exceptions ];
00684     QStringList exceptList = QStringList::split( " ", exceptStr );
00685     ...
00686 #endif
00687 
00688 
00689 }
00690 
00691 }

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