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

datebookdb.cpp

Go to the documentation of this file.
00001 /**********************************************************************
00002 ** Copyright (C) 2000 Trolltech AS.  All rights reserved.
00003 **
00004 ** This file is part of Qtopia Environment.
00005 **
00006 ** This file may be distributed and/or modified under the terms of the
00007 ** GNU General Public License version 2 as published by the Free Software
00008 ** Foundation and appearing in the file LICENSE.GPL included in the
00009 ** packaging of this file.
00010 **
00011 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
00012 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
00013 **
00014 ** See http://www.trolltech.com/gpl/ for GPL licensing information.
00015 **
00016 ** Contact info@trolltech.com if any conditions of this licensing are
00017 ** not clear to you.
00018 **
00019 **********************************************************************/
00020 
00021 #include <qasciidict.h>
00022 #include <qmessagebox.h>
00023 #include <qtl.h>
00024 
00025 #include <qpe/alarmserver.h>
00026 #include <qpe/global.h>
00027 #include "datebookdb.h"
00028 #include <qpe/stringutil.h>
00029 
00030 #include <errno.h>
00031 #include <stdlib.h>
00032 
00033 
00034 class DateBookDBPrivate
00035 {
00036 public:
00037     bool clean;  // indcate whether we need to write to disk...
00038 };
00039 
00040 
00041 // Helper functions
00042 
00043 static QString dateBookJournalFile()
00044 {
00045     QString str = getenv("HOME");
00046     return QString( str +"/.caljournal" );
00047 }
00048 
00049 static QString dateBookFilename()
00050 {
00051     return Global::applicationFileName("datebook","datebook.xml");
00052 }
00053 
00054 /* Calculating the next event of a recuring event is actually
00055    computationally inexpensive, esp. compared to checking each day
00056    individually.  There are bad worse cases for say the 29th of
00057    february or the 31st of some other months.  However
00058    these are still bounded */
00059 bool nextOccurance(const Event &e, const QDate &from, QDateTime &next)
00060 {
00061     // easy checks, first are we too far in the future or too far in the past?
00062     QDate tmpDate;
00063     int freq = e.repeatPattern().frequency;
00064     int diff, diff2, a;
00065     int iday, imonth, iyear;
00066     int dayOfWeek = 0;
00067     int firstOfWeek = 0;
00068     int weekOfMonth;
00069 
00070 
00071     if (e.repeatPattern().hasEndDate && e.repeatPattern().endDate() < from)
00072         return FALSE;
00073 
00074     if (e.start() >= from) {
00075         next = e.start();
00076         return TRUE;
00077     }
00078 
00079     switch ( e.repeatPattern().type ) {
00080         case Event::Weekly:
00081             /* weekly is just daily by 7 */
00082             /* first convert the repeatPattern.Days() mask to the next
00083                day of week valid after from */
00084             dayOfWeek = from.dayOfWeek();
00085             dayOfWeek--; /* we want 0-6, doco for above specs 1-7 */
00086 
00087             /* this is done in case freq > 1 and from in week not
00088                for this round */
00089             // firstOfWeek = 0; this is already done at decl.
00090             while(!((1 << firstOfWeek) & e.repeatPattern().days))
00091                 firstOfWeek++;
00092 
00093 
00094 
00095             /* there is at least one 'day', or there would be no event */
00096             while(!((1 << (dayOfWeek % 7)) & e.repeatPattern().days))
00097                 dayOfWeek++;
00098 
00099 
00100             dayOfWeek = dayOfWeek % 7; /* the actual day of week */
00101             dayOfWeek -= e.start().date().dayOfWeek() -1;
00102 
00103             firstOfWeek = firstOfWeek % 7; /* the actual first of week */
00104             firstOfWeek -= e.start().date().dayOfWeek() -1;
00105 
00106             // dayOfWeek may be negitive now
00107             // day of week is number of days to add to start day
00108 
00109             freq *= 7;
00110             // FALL-THROUGH !!!!!
00111         case Event::Daily:
00112             // the add is for the possible fall through from weekly */
00113             if(e.start().date().addDays(dayOfWeek) > from) {
00114                 /* first week exception */
00115                 next = QDateTime(e.start().date().addDays(dayOfWeek),
00116                                  e.start().time());
00117                 if ((next.date() > e.repeatPattern().endDate())
00118                     && e.repeatPattern().hasEndDate)
00119                     return FALSE;
00120                 return TRUE;
00121             }
00122             /* if from is middle of a non-week */
00123 
00124             diff = e.start().date().addDays(dayOfWeek).daysTo(from) % freq;
00125             diff2 = e.start().date().addDays(firstOfWeek).daysTo(from) % freq;
00126 
00127             if(diff != 0)
00128                 diff = freq - diff;
00129             if(diff2 != 0)
00130                 diff2 = freq - diff2;
00131             diff = QMIN(diff, diff2);
00132 
00133             next = QDateTime(from.addDays(diff), e.start().time());
00134             if ( (next.date() > e.repeatPattern().endDate())
00135                  && e.repeatPattern().hasEndDate )
00136                 return FALSE;
00137             return TRUE;
00138         case Event::MonthlyDay:
00139             iday = from.day();
00140             iyear = from.year();
00141             imonth = from.month();
00142             /* find equivelent day of month for this month */
00143             dayOfWeek = e.start().date().dayOfWeek();
00144             weekOfMonth = (e.start().date().day() - 1) / 7;
00145 
00146             /* work out when the next valid month is */
00147             a = from.year() - e.start().date().year();
00148             a *= 12;
00149             a = a + (imonth - e.start().date().month());
00150             /* a is e.start()monthsFrom(from); */
00151             if(a % freq) {
00152                 a = freq - (a % freq);
00153                 imonth = from.month() + a;
00154                 if (imonth > 12) {
00155                     imonth--;
00156                     iyear += imonth / 12;
00157                     imonth = imonth % 12;
00158                     imonth++;
00159                 }
00160             }
00161             /* imonth is now the first month after or on
00162                from that matches the frequency given */
00163 
00164             /* find for this month */
00165             tmpDate = QDate( iyear, imonth, 1 );
00166 
00167             iday = 1;
00168             iday += (7 + dayOfWeek - tmpDate.dayOfWeek()) % 7;
00169             iday += 7 * weekOfMonth;
00170             while (iday > tmpDate.daysInMonth()) {
00171                 imonth += freq;
00172                 if (imonth > 12) {
00173                     imonth--;
00174                     iyear += imonth / 12;
00175                     imonth = imonth % 12;
00176                     imonth++;
00177                 }
00178                 tmpDate = QDate( iyear, imonth, 1 );
00179                 /* these loops could go for a while, check end case now */
00180                 if ((tmpDate > e.repeatPattern().endDate()) && e.repeatPattern().hasEndDate)
00181                     return FALSE;
00182                 iday = 1;
00183                 iday += (7 + dayOfWeek - tmpDate.dayOfWeek()) % 7;
00184                 iday += 7 * weekOfMonth;
00185             }
00186             tmpDate = QDate(iyear, imonth, iday);
00187 
00188             if (tmpDate >= from) {
00189                 next = QDateTime(tmpDate, e.start().time());
00190                 if ((next.date() > e.repeatPattern().endDate()) && e.repeatPattern().hasEndDate)
00191                     return FALSE;
00192                 return TRUE;
00193             }
00194 
00195             /* need to find the next iteration */
00196             do {
00197                 imonth += freq;
00198                 if (imonth > 12) {
00199                     imonth--;
00200                     iyear += imonth / 12;
00201                     imonth = imonth % 12;
00202                     imonth++;
00203                 }
00204                 tmpDate = QDate( iyear, imonth, 1 );
00205                 /* these loops could go for a while, check end case now */
00206                 if ((tmpDate > e.repeatPattern().endDate()) && e.repeatPattern().hasEndDate)
00207                     return FALSE;
00208                 iday = 1;
00209                 iday += (7 + dayOfWeek - tmpDate.dayOfWeek()) % 7;
00210                 iday += 7 * weekOfMonth;
00211             } while (iday > tmpDate.daysInMonth());
00212             tmpDate = QDate(iyear, imonth, iday);
00213 
00214             next = QDateTime(tmpDate, e.start().time());
00215             if ((next.date() > e.repeatPattern().endDate()) && e.repeatPattern().hasEndDate)
00216                 return FALSE;
00217             return TRUE;
00218         case Event::MonthlyDate:
00219             iday = e.start().date().day();
00220             iyear = from.year();
00221             imonth = from.month();
00222 
00223             a = from.year() - e.start().date().year();
00224             a *= 12;
00225             a = a + (imonth - e.start().date().month());
00226             /* a is e.start()monthsFrom(from); */
00227             if(a % freq) {
00228                 a = freq - (a % freq);
00229                 imonth = from.month() + a;
00230                 if (imonth > 12) {
00231                     imonth--;
00232                     iyear += imonth / 12;
00233                     imonth = imonth % 12;
00234                     imonth++;
00235                 }
00236             }
00237             /* imonth is now the first month after or on
00238                from that matches the frequencey given */
00239 
00240             /* this could go for a while, worse case, 4*12 iterations, probably */
00241             while(!QDate::isValid(iyear, imonth, iday) ) {
00242                 imonth += freq;
00243                 if (imonth > 12) {
00244                     imonth--;
00245                     iyear += imonth / 12;
00246                     imonth = imonth % 12;
00247                     imonth++;
00248                 }
00249                 /* these loops could go for a while, check end case now */
00250                 if ((QDate(iyear, imonth, 1) > e.repeatPattern().endDate()) && e.repeatPattern().hasEndDate)
00251                     return FALSE;
00252             }
00253 
00254             if(QDate(iyear, imonth, iday) >= from) {
00255                 /* done */
00256                 next = QDateTime(QDate(iyear, imonth, iday),
00257                         e.start().time());
00258                 if ((next.date() > e.repeatPattern().endDate()) && e.repeatPattern().hasEndDate)
00259                     return FALSE;
00260                 return TRUE;
00261             }
00262 
00263             /* ok, need to cycle */
00264             imonth += freq;
00265             imonth--;
00266             iyear += imonth / 12;
00267             imonth = imonth % 12;
00268             imonth++;
00269 
00270             while(!QDate::isValid(iyear, imonth, iday) ) {
00271                 imonth += freq;
00272                 imonth--;
00273                 iyear += imonth / 12;
00274                 imonth = imonth % 12;
00275                 imonth++;
00276                 if ((QDate(iyear, imonth, 1) > e.repeatPattern().endDate()) && e.repeatPattern().hasEndDate)
00277                     return FALSE;
00278             }
00279 
00280             next = QDateTime(QDate(iyear, imonth, iday), e.start().time());
00281             if ((next.date() > e.repeatPattern().endDate()) && e.repeatPattern().hasEndDate)
00282                 return FALSE;
00283             return TRUE;
00284         case Event::Yearly:
00285             iday = e.start().date().day();
00286             imonth = e.start().date().month();
00287             iyear = from.year(); // after all, we want to start in this year
00288 
00289             diff = 1;
00290             if(imonth == 2 && iday > 28) {
00291                 /* leap year, and it counts, calculate actual frequency */
00292                 if(freq % 4)
00293                     if (freq % 2)
00294                         freq = freq * 4;
00295                     else
00296                         freq = freq * 2;
00297                 /* else divides by 4 already, leave freq alone */
00298                 diff = 4;
00299             }
00300 
00301             a = from.year() - e.start().date().year();
00302             if(a % freq) {
00303                 a = freq - (a % freq);
00304                 iyear = iyear + a;
00305             }
00306 
00307             /* under the assumption we won't hit one of the special not-leap years twice */
00308             if(!QDate::isValid(iyear, imonth, iday)) {
00309                 /* must have been skipping by leap years and hit one that wasn't, (e.g. 2100) */
00310                 iyear += freq;
00311             }
00312 
00313             if(QDate(iyear, imonth, iday) >= from) {
00314                 next = QDateTime(QDate(iyear, imonth, iday),
00315                         e.start().time());
00316                 if ((next.date() > e.repeatPattern().endDate()) && e.repeatPattern().hasEndDate)
00317                     return FALSE;
00318                 return TRUE;
00319             }
00320             /* iyear == from.year(), need to advance again */
00321             iyear += freq;
00322             /* under the assumption we won't hit one of the special not-leap years twice */
00323             if(!QDate::isValid(iyear, imonth, iday)) {
00324                 /* must have been skipping by leap years and hit one that wasn't, (e.g. 2100) */
00325                 iyear += freq;
00326             }
00327 
00328             next = QDateTime(QDate(iyear, imonth, iday), e.start().time());
00329             if ((next.date() > e.repeatPattern().endDate()) && e.repeatPattern().hasEndDate)
00330                 return FALSE;
00331             return TRUE;
00332         default:
00333             return FALSE;
00334     }
00335 }
00336 
00337 static bool nextAlarm( const Event &ev, QDateTime& when, int& warn)
00338 {
00339     QDateTime now = QDateTime::currentDateTime();
00340     if ( ev.hasRepeat() ) {
00341         QDateTime ralarm;
00342         if (nextOccurance(ev, now.date(), ralarm)) {
00343             ralarm = ralarm.addSecs(-ev.alarmTime()*60);
00344             if ( ralarm > now ) {
00345                 when = ralarm;
00346                 warn = ev.alarmTime();
00347             } else if ( nextOccurance(ev, now.date().addDays(1), ralarm) ) {
00348                 ralarm = ralarm.addSecs( -ev.alarmTime()*60 );
00349                 if ( ralarm > now ) {
00350                     when = ralarm;
00351                     warn = ev.alarmTime();
00352                 }
00353             }
00354         }
00355     } else {
00356         warn = ev.alarmTime();
00357         when = ev.start().addSecs( -ev.alarmTime()*60 );
00358     }
00359     return when > now;
00360 }
00361 
00362 static void addEventAlarm( const Event &ev )
00363 {
00364     QDateTime when;
00365     int warn;
00366     if ( nextAlarm(ev,when,warn) )
00367         AlarmServer::addAlarm( when,
00368                                "QPE/Application/datebook",
00369                                "alarm(QDateTime,int)", warn );
00370 }
00371 
00372 static void delEventAlarm( const Event &ev )
00373 {
00374     QDateTime when;
00375     int warn;
00376     if ( nextAlarm(ev,when,warn) )
00377         AlarmServer::deleteAlarm( when,
00378                                   "QPE/Application/datebook",
00379                                   "alarm(QDateTime,int)", warn );
00380 }
00381 
00382 
00383 DateBookDB::DateBookDB()
00384 {
00385     init();
00386 }
00387 
00388 DateBookDB::~DateBookDB()
00389 {
00390     save();
00391     eventList.clear();
00392     repeatEvents.clear();
00393 
00394     delete d;
00395     d=0;
00396 }
00397 
00398 
00399 //#### Why is this code duplicated in getEffectiveEvents ?????
00400 //#### Addendum.  Don't use this function, lets faze it out if we can.
00401 QValueList<Event> DateBookDB::getEvents( const QDate &from, const QDate &to )
00402 {
00403     QValueList<Event> tmpList;
00404     tmpList = getNonRepeatingEvents( from, to );
00405 
00406     // check for repeating events...
00407     for (QValueList<Event>::ConstIterator it = repeatEvents.begin();
00408          it != repeatEvents.end(); ++it) {
00409         QDate itDate = from;
00410         QDateTime due;
00411 
00412         /* create a false end date, to short circuit on hard
00413            MonthlyDay recurences */
00414         Event dummy_event = *it;
00415         Event::RepeatPattern r = dummy_event.repeatPattern();
00416         if ( !r.hasEndDate || r.endDate() > to ) {
00417             r.setEndDate( to );
00418             r.hasEndDate = TRUE;
00419         }
00420         dummy_event.setRepeat(TRUE, r);
00421 
00422         while (nextOccurance(dummy_event, itDate, due)) {
00423             if (due.date() > to)
00424                 break;
00425             Event newEvent = *it;
00426             newEvent.setStart(due);
00427             newEvent.setEnd(due.addSecs((*it).start().secsTo((*it).end())));
00428 
00429             tmpList.append(newEvent);
00430             itDate = due.date().addDays(1);  /* the next event */
00431         }
00432     }
00433     qHeapSort(tmpList);
00434     return tmpList;
00435 }
00436 
00437 QValueList<Event> DateBookDB::getEvents( const QDateTime &start )
00438 {
00439     QValueList<Event> day = getEvents(start.date(),start.date());
00440 
00441     QValueListConstIterator<Event> it;
00442     QDateTime dtTmp;
00443     QValueList<Event> tmpList;
00444     for (it = day.begin(); it != day.end(); ++it ) {
00445         dtTmp = (*it).start(TRUE);
00446         if ( dtTmp == start )
00447             tmpList.append( *it );
00448     }
00449     return tmpList;
00450 }
00451 
00452 //#### Why is this code duplicated in getEvents ?????
00453 
00454 QValueList<EffectiveEvent> DateBookDB::getEffectiveEvents( const QDate &from,
00455                                                            const QDate &to )
00456 {
00457     QValueList<EffectiveEvent> tmpList;
00458     QValueListIterator<Event> it;
00459 
00460     EffectiveEvent effEv;
00461     QDateTime dtTmp,
00462               dtEnd;
00463 
00464     for (it = eventList.begin(); it != eventList.end(); ++it ) {
00465         if (!(*it).isValidUid())
00466             (*it).assignUid(); // FIXME: Hack to restore cleared uids
00467 
00468         dtTmp = (*it).start(TRUE);
00469         dtEnd = (*it).end(TRUE);
00470 
00471         if ( dtTmp.date() >= from && dtTmp.date() <= to ) {
00472             Event tmpEv = *it;
00473             effEv.setEvent(tmpEv);
00474             effEv.setDate( dtTmp.date() );
00475             effEv.setStart( dtTmp.time() );
00476             if ( dtTmp.date() != dtEnd.date() )
00477                 effEv.setEnd( QTime(23, 59, 0) );
00478             else
00479                 effEv.setEnd( dtEnd.time() );
00480             tmpList.append( effEv );
00481         }
00482         // we must also check for end date information...
00483         if ( dtEnd.date() != dtTmp.date() && dtEnd.date() >= from ) {
00484             QDateTime dt = dtTmp.addDays( 1 );
00485             dt.setTime( QTime(0, 0, 0) );
00486             QDateTime dtStop;
00487             if ( dtEnd > to ) {
00488                 dtStop = to;
00489             } else
00490                 dtStop = dtEnd;
00491             while ( dt <= dtStop ) {
00492                 Event tmpEv = *it;
00493                 effEv.setEvent( tmpEv );
00494                 effEv.setDate( dt.date() );
00495                 if ( dt >= from ) {
00496                     effEv.setStart( QTime(0, 0, 0) );
00497                     if ( dt.date() == dtEnd.date() )
00498                         effEv.setEnd( dtEnd.time() );
00499                     else
00500                         effEv.setEnd( QTime(23, 59, 59) );
00501                     tmpList.append( effEv );
00502                 }
00503                 dt = dt.addDays( 1 );
00504             }
00505         }
00506     }
00507     // check for repeating events...
00508     QDateTime repeat;
00509     for ( it = repeatEvents.begin(); it != repeatEvents.end(); ++it ) {
00510         if (!(*it).isValidUid())
00511             (*it).assignUid(); // FIXME: Hack to restore cleared uids
00512 
00513         /* create a false end date, to short circuit on hard
00514            MonthlyDay recurences */
00515         Event dummy_event = *it;
00516         int duration = (*it).start().date().daysTo( (*it).end().date() );
00517         QDate itDate = from.addDays(-duration);
00518 
00519         Event::RepeatPattern r = dummy_event.repeatPattern();
00520         if ( !r.hasEndDate || r.endDate() > to ) {
00521             r.setEndDate( to );
00522             r.hasEndDate = TRUE;
00523         }
00524         dummy_event.setRepeat(TRUE, r);
00525 
00526         while (nextOccurance(dummy_event, itDate, repeat)) {
00527             if(repeat.date() > to)
00528                 break;
00529             effEv.setDate( repeat.date() );
00530             if ((*it).type() == Event::AllDay) {
00531                 effEv.setStart( QTime(0,0,0) );
00532                 effEv.setEnd( QTime(23,59,59) );
00533             } else {
00534                 /* we only occur by days, not hours/minutes/seconds.  Hence
00535                    the actual end and start times will be the same for
00536                    every repeated event.  For multi day events this is
00537                    fixed up later if on wronge day span */
00538                 effEv.setStart( (*it).start().time() );
00539                 effEv.setEnd( (*it).end().time() );
00540             }
00541             if ( duration != 0 ) {
00542                 // multi-day repeating events
00543                 QDate sub_it = QMAX( repeat.date(), from );
00544                 QDate startDate = repeat.date();
00545                 QDate endDate = startDate.addDays( duration );
00546 
00547                 while ( sub_it <= endDate && sub_it  <= to ) {
00548                     EffectiveEvent tmpEffEv = effEv;
00549                     Event tmpEv = *it;
00550                     tmpEffEv.setEvent( tmpEv );
00551 
00552                     if ( sub_it != startDate )
00553                         tmpEffEv.setStart( QTime(0,0,0) );
00554                     if ( sub_it != endDate )
00555                         tmpEffEv.setEnd( QTime(23,59,59) );
00556                     tmpEffEv.setDate( sub_it );
00557                     tmpEffEv.setEffectiveDates( startDate, endDate );
00558                     tmpList.append( tmpEffEv );
00559                     sub_it = sub_it.addDays( 1 );
00560                 }
00561                 itDate = endDate;
00562             } else {
00563                 Event tmpEv = *it;
00564                 effEv.setEvent( tmpEv );
00565                 tmpList.append( effEv );
00566                 itDate = repeat.date().addDays( 1 );
00567             }
00568         }
00569     }
00570 
00571     qHeapSort( tmpList );
00572     return tmpList;
00573 }
00574 
00575 QValueList<EffectiveEvent> DateBookDB::getEffectiveEvents( const QDateTime &dt)
00576 {
00577     QValueList<EffectiveEvent> day = getEffectiveEvents(dt.date(), dt.date());
00578     QValueListConstIterator<EffectiveEvent> it;
00579     QValueList<EffectiveEvent> tmpList;
00580     QDateTime dtTmp;
00581 
00582     for (it = day.begin(); it != day.end(); ++it ) {
00583         dtTmp = QDateTime( (*it).date(), (*it).start() );
00584         // at the moment we don't have second granularity, be nice about that..
00585         if ( QABS(dt.secsTo(dtTmp)) < 60 )
00586             tmpList.append( *it );
00587     }
00588     return tmpList;
00589 }
00590 
00591 void DateBookDB::addEvent( const Event &ev, bool doalarm )
00592 {
00593     // write to the journal...
00594     saveJournalEntry( ev, ACTION_ADD, -1, false );
00595     addJFEvent( ev, doalarm );
00596     d->clean = false;
00597 }
00598 
00599 void DateBookDB::addJFEvent( const Event &ev, bool doalarm )
00600 {
00601     if ( doalarm && ev.hasAlarm() )
00602         addEventAlarm( ev );
00603     if ( ev.hasRepeat() )
00604         repeatEvents.append( ev );
00605     else
00606         eventList.append( ev );
00607 }
00608 
00609 void DateBookDB::editEvent( const Event &old, Event &editedEv )
00610 {
00611     int oldIndex=0;
00612     bool oldHadRepeat = old.hasRepeat();
00613     Event orig;
00614 
00615     // write to the journal...
00616     if ( oldHadRepeat ) {
00617         if ( origRepeat( old, orig ) ) // should work always...
00618             oldIndex = repeatEvents.findIndex( orig );
00619     } else
00620         oldIndex = eventList.findIndex( old );
00621     saveJournalEntry( editedEv, ACTION_REPLACE, oldIndex, oldHadRepeat );
00622 
00623     // Delete old event
00624     if ( old.hasAlarm() )
00625         delEventAlarm( old );
00626     if ( oldHadRepeat ) {
00627         if ( editedEv.hasRepeat() ) { // This mean that origRepeat was run above and
00628                                       // orig is initialized
00629             // assumption, when someone edits a repeating event, they
00630             // want to change them all, maybe not perfect, but it works
00631             // for the moment...
00632             repeatEvents.remove( orig );
00633         } else
00634             removeRepeat( old );
00635     } else {
00636         QValueList<Event>::Iterator it = eventList.find( old );
00637         if ( it != eventList.end() )
00638             eventList.remove( it );
00639     }
00640 
00641     // Add new event
00642     if ( editedEv.hasAlarm() )
00643         addEventAlarm( editedEv );
00644     if ( editedEv.hasRepeat() )
00645         repeatEvents.append( editedEv );
00646     else
00647         eventList.append( editedEv );
00648 
00649     d->clean = false;
00650 }
00651 
00652 void DateBookDB::removeEvent( const Event &ev )
00653 {
00654     // write to the journal...
00655     saveJournalEntry( ev, ACTION_REMOVE, -1, false );
00656     removeJFEvent( ev );
00657     d->clean = false;
00658 }
00659 
00660 void DateBookDB::removeJFEvent( const Event&ev )
00661 {
00662     if ( ev.hasAlarm() )
00663         delEventAlarm( ev );
00664     if ( ev.hasRepeat() ) {
00665         removeRepeat( ev );
00666     } else {
00667         QValueList<Event>::Iterator it = eventList.find( ev );
00668         if ( it != eventList.end() )
00669             eventList.remove( it );
00670     }
00671 }
00672 
00673 // also handles journaling...
00674 void DateBookDB::loadFile( const QString &strFile )
00675 {
00676 
00677     QFile f( strFile );
00678     if ( !f.open( IO_ReadOnly ) )
00679         return;
00680 
00681     enum Attribute {
00682         FDescription = 0,
00683         FLocation,
00684         FCategories,
00685         FUid,
00686         FType,
00687         FAlarm,
00688         FSound,
00689         FRType,
00690         FRWeekdays,
00691         FRPosition,
00692         FRFreq,
00693         FRHasEndDate,
00694         FREndDate,
00695         FRStart,
00696         FREnd,
00697         FNote,
00698         FCreated,
00699         FAction,
00700         FActionKey,
00701         FJournalOrigHadRepeat
00702     };
00703 
00704     QAsciiDict<int> dict( 97 );
00705     dict.setAutoDelete( TRUE );
00706     dict.insert( "description", new int(FDescription) );
00707     dict.insert( "location", new int(FLocation) );
00708     dict.insert( "categories", new int(FCategories) );
00709     dict.insert( "uid", new int(FUid) );
00710     dict.insert( "type", new int(FType) );
00711     dict.insert( "alarm", new int(FAlarm) );
00712     dict.insert( "sound", new int(FSound) );
00713     dict.insert( "rtype", new int(FRType) );
00714     dict.insert( "rweekdays", new int(FRWeekdays) );
00715     dict.insert( "rposition", new int(FRPosition) );
00716     dict.insert( "rfreq", new int(FRFreq) );
00717     dict.insert( "rhasenddate", new int(FRHasEndDate) );
00718     dict.insert( "enddt", new int(FREndDate) );
00719     dict.insert( "start", new int(FRStart) );
00720     dict.insert( "end", new int(FREnd) );
00721     dict.insert( "note", new int(FNote) );
00722     dict.insert( "created", new int(FCreated) );
00723     dict.insert( "action", new int(FAction) );
00724     dict.insert( "actionkey", new int(FActionKey) );
00725     dict.insert( "actionorig", new int (FJournalOrigHadRepeat) );
00726 
00727 
00728     QByteArray ba = f.readAll();
00729     char* dt = ba.data();
00730     int len = ba.size();
00731     int currentAction,
00732         journalKey,
00733         origHadRepeat;  // should be bool, but we need tri-state(not being used)
00734 
00735     int i = 0;
00736     char *point;
00737     // hack to get rid of segfaults after reading </DATEBOOK>
00738     while ( (dt+i != 0) && (( point = strstr( dt+i, "<event " ) ) != 0 )) {
00739         i = point - dt;
00740         // if we are reading in events in the general case,
00741         // we are just adding them, so let the actions represent that...
00742         currentAction = ACTION_ADD;
00743         journalKey = -1;
00744         origHadRepeat = -1;
00745         // some temporary variables for dates and times ...
00746         //int startY = 0, startM = 0, startD = 0, starth = 0, startm = 0, starts = 0;
00747         //int endY = 0, endM = 0, endD = 0, endh = 0, endm = 0, ends = 0;
00748         //int enddtY = 0, enddtM = 0, enddtD = 0;
00749 
00750         // ... for the alarm settings ...
00751         int alarmTime = -1; Event::SoundTypeChoice alarmSound = Event::Silent;
00752         // ... and for the recurrence
00753         Event::RepeatPattern rp;
00754         Event e;
00755 
00756         i += 7;
00757 
00758         while( 1 ) {
00759             while ( i < len && (dt[i] == ' ' || dt[i] == '\n' || dt[i] == '\r') )
00760                 ++i;
00761             if ( i >= len-2 || (dt[i] == '/' && dt[i+1] == '>') )
00762                 break;
00763             // we have another attribute, read it.
00764             int j = i;
00765             while ( j < len && dt[j] != '=' )
00766                 ++j;
00767             char *attr = dt+i;
00768             dt[j] = '\0';
00769             i = ++j; // skip =
00770             while ( i < len && dt[i] != '"' )
00771                 ++i;
00772             j = ++i;
00773             bool haveAmp = FALSE;
00774             bool haveUtf = FALSE;
00775             while ( j < len && dt[j] != '"' ) {
00776                 if ( dt[j] == '&' )
00777                     haveAmp = TRUE;
00778                 if ( ((unsigned char)dt[j]) > 0x7f )
00779                     haveUtf = TRUE;
00780                 ++j;
00781             }
00782 
00783             if ( i == j ) {
00784                 // leave out empty attributes
00785                 i = j + 1;
00786                 continue;
00787             }
00788 
00789             QString value = haveUtf ? QString::fromUtf8( dt+i, j-i )
00790                             : QString::fromLatin1( dt+i, j-i );
00791             if ( haveAmp )
00792                 value = Qtopia::plainString( value );
00793             i = j + 1;
00794 
00795             //qDebug("attr='%s' value='%s'", attr.data(), value.latin1() );
00796             int * find = dict[ attr ];
00797 #if 1
00798             if ( !find ) {
00799                 // custom field
00800                 e.setCustomField(attr, value);
00801                 continue;
00802             }
00803 
00804             switch( *find ) {
00805             case FDescription:
00806                 e.setDescription( value );
00807                 break;
00808             case FLocation:
00809                 e.setLocation( value );
00810                 break;
00811             case FCategories:
00812                 e.setCategories( Qtopia::Record::idsFromString( value ) );
00813                 break;
00814             case FUid:
00815                 e.setUid( value.toInt() );
00816                 break;
00817             case FType:
00818                 if ( value == "AllDay" )
00819                     e.setType( Event::AllDay );
00820                 else
00821                     e.setType( Event::Normal );
00822                 break;
00823             case FAlarm:
00824                 alarmTime = value.toInt();
00825                 break;
00826             case FSound:
00827                 alarmSound = value == "loud" ? Event::Loud : Event::Silent;
00828                 break;
00829                 // recurrence stuff
00830             case FRType:
00831                 if ( value == "Daily" )
00832                     rp.type = Event::Daily;
00833                 else if ( value == "Weekly" )
00834                     rp.type = Event::Weekly;
00835                 else if ( value == "MonthlyDay" )
00836                     rp.type = Event::MonthlyDay;
00837                 else if ( value == "MonthlyDate" )
00838                     rp.type = Event::MonthlyDate;
00839                 else if ( value == "Yearly" )
00840                     rp.type = Event::Yearly;
00841                 else
00842                     rp.type = Event::NoRepeat;
00843                 break;
00844             case FRWeekdays:
00845                     // QtopiaDesktop 1.6 sometimes creates 'rweekdays="0"'
00846                     // when it goes mad. This causes datebook to crash.. (se)
00847                     if ( value.toInt() != 0 )
00848                             rp.days = value.toInt();
00849                     else
00850                             rp.days = 1;
00851                 break;
00852             case FRPosition:
00853                 rp.position = value.toInt();
00854                 break;
00855             case FRFreq:
00856                 rp.frequency = value.toInt();
00857                 break;
00858             case FRHasEndDate:
00859                 rp.hasEndDate = value.toInt();
00860                 break;
00861                 case FREndDate: {
00862                 rp.endDateUTC = (time_t) value.toLong();
00863                 break;
00864                 }
00865                 case FRStart: {
00866                 e.setStart( (time_t) value.toLong() );
00867                 break;
00868                 }
00869                 case FREnd: {
00870                 e.setEnd( (time_t) value.toLong() );
00871                 break;
00872                 }
00873             case FNote:
00874                 e.setNotes( value );
00875                 break;
00876             case FCreated:
00877                 rp.createTime = value.toInt();
00878                 break;
00879             case FAction:
00880                 currentAction = value.toInt();
00881                 break;
00882             case FActionKey:
00883                 journalKey = value.toInt();
00884                 break;
00885             case FJournalOrigHadRepeat:
00886                 origHadRepeat = value.toInt();
00887                 break;
00888             default:
00889                 qDebug( "huh??? missing enum? -- attr.: %s", attr );
00890                 break;
00891             }
00892 #endif
00893         }
00894         // "post processing" (dates, times, alarm, recurrence)
00895 
00896         // other half of 1169 fixlet without getting into regression
00897         // if rp.days == 0 and rp.type == Event::Weekly
00898         if ( rp.type == Event::Weekly && rp.days == 0 )
00899             rp.days = Event::day( e.start().date().dayOfWeek() );
00900 
00901 
00902         // start date/time
00903         e.setRepeat( rp.type != Event::NoRepeat, rp );
00904 
00905         if ( alarmTime != -1 )
00906             e.setAlarm( TRUE, alarmTime, alarmSound );
00907 
00908         // now do our action based on the current action...
00909         switch ( currentAction ) {
00910         case ACTION_ADD:
00911             addJFEvent( e );
00912             break;
00913         case ACTION_REMOVE:
00914             removeJFEvent( e );
00915             break;
00916         case ACTION_REPLACE:
00917             // be a little bit careful,
00918             // in case of a messed up journal...
00919             if ( journalKey > -1 && origHadRepeat > -1 ) {
00920                 // get the original from proper list...
00921                 if ( origHadRepeat )
00922                     removeJFEvent( *(repeatEvents.at(journalKey)) );
00923                 else
00924                     removeJFEvent( *(eventList.at(journalKey)) );
00925                 addJFEvent( e );
00926             }
00927             break;
00928         default:
00929             break;
00930         }
00931     }
00932     f.close();
00933 }
00934 
00935 void DateBookDB::init()
00936 {
00937     d = new DateBookDBPrivate;
00938     d->clean = false;
00939     QString str = dateBookFilename();
00940     if ( str.isNull() ) {
00941         QMessageBox::warning( 0, QObject::tr("Out of Space"),
00942                               QObject::tr("Unable to create start up files\n"
00943                                           "Please free up some space\n"
00944                                           "before entering data") );
00945     }
00946     // continuing along, we call this datebook filename again,
00947     // because they may fix it before continuing, though it seems
00948     // pretty unlikely...
00949     loadFile( dateBookFilename() );
00950 
00951     if ( QFile::exists( dateBookJournalFile() ) ) {
00952         // merge the journal
00953         loadFile( dateBookJournalFile() );
00954         // save in our changes and remove the journal...
00955         save();
00956     }
00957     d->clean = true;
00958 }
00959 
00960 bool DateBookDB::save()
00961 {
00962     if ( d->clean == true )
00963         return true;
00964     QValueListIterator<Event> it;
00965     int total_written;
00966     QString strFileNew = dateBookFilename() + ".new";
00967 
00968     QFile f( strFileNew );
00969     if ( !f.open( IO_WriteOnly|IO_Raw ) )
00970         return FALSE;
00971 
00972     QString buf( "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" );
00973     buf += "<!DOCTYPE DATEBOOK><DATEBOOK>\n";
00974     buf += "<events>\n";
00975     QCString str = buf.utf8();
00976     total_written = f.writeBlock( str.data(), str.length() );
00977     if ( total_written != int(str.length()) ) {
00978         f.close();
00979         QFile::remove( strFileNew );
00980         return false;
00981     }
00982 
00983     for ( it = eventList.begin(); it != eventList.end(); ++it ) {
00984         buf = "<event";
00985         (*it).save( buf );
00986         buf += " />\n";
00987         str = buf.utf8();
00988         total_written = f.writeBlock( str.data(), str.length() );
00989         if ( total_written != int(str.length()) ) {
00990             f.close();
00991             QFile::remove( strFileNew );
00992             return false;
00993         }
00994     }
00995     for ( it = repeatEvents.begin(); it != repeatEvents.end(); ++it ) {
00996         buf = "<event";
00997         (*it).save( buf );
00998         buf += " />\n";
00999         str = buf.utf8();
01000         total_written = f.writeBlock( str.data(), str.length() );
01001         if ( total_written != int(str.length()) ) {
01002             f.close();
01003             QFile::remove( strFileNew );
01004             return false;
01005         }
01006     }
01007     buf = "</events>\n</DATEBOOK>\n";
01008     str = buf.utf8();
01009     total_written = f.writeBlock( str.data(), str.length() );
01010     if ( total_written != int(str.length()) ) {
01011         f.close();
01012         QFile::remove( strFileNew );
01013         return false;
01014     }
01015     f.close();
01016 
01017     // now rename... I like to use the systemcall
01018     if ( ::rename( strFileNew, dateBookFilename() ) < 0 ) {
01019         qWarning( "problem renaming file %s to %s errno %d",
01020                   strFileNew.latin1(), dateBookFilename().latin1(), errno  );
01021         // remove the file, otherwise it will just stick around...
01022         QFile::remove( strFileNew );
01023     }
01024 
01025     // may as well remove the journal file...
01026     QFile::remove( dateBookJournalFile() );
01027     d->clean = true;
01028     return true;
01029 }
01030 
01031 void DateBookDB::reload()
01032 {
01033     QValueList<Event>::Iterator it = eventList.begin();
01034     for ( ; it != eventList.end(); ++it ) {
01035         if ( (*it).hasAlarm() )
01036             delEventAlarm( *it );
01037         if ( (*it).hasRepeat() )
01038             removeRepeat( *it );
01039     }
01040     eventList.clear();
01041     repeatEvents.clear(); // should be a NOP
01042     init();
01043 }
01044 
01045 bool DateBookDB::removeRepeat( const Event &ev )
01046 {
01047     time_t removeMe = ev.repeatPattern().createTime;
01048     QValueListIterator<Event> it;
01049     for ( it = repeatEvents.begin(); it != repeatEvents.end(); ++it ) {
01050         if ( removeMe == (*it).repeatPattern().createTime ) {
01051             (void)repeatEvents.remove( it );
01052             // best break, or we are going into undefined territory!
01053             return TRUE;
01054         }
01055     }
01056     return FALSE;
01057 }
01058 
01059 bool DateBookDB::origRepeat( const Event &ev, Event &orig ) const
01060 {
01061     time_t removeMe = ev.repeatPattern().createTime;
01062     QValueListConstIterator<Event> it;
01063     for ( it = repeatEvents.begin(); it != repeatEvents.end(); ++it ) {
01064         if ( removeMe == (*it).repeatPattern().createTime ) {
01065             orig = (*it);
01066             return TRUE;
01067         }
01068     }
01069     return FALSE;
01070 }
01071 
01072 void DateBookDB::saveJournalEntry( const Event &ev, journal_action action )
01073 {
01074     saveJournalEntry( ev, action, -1, false );
01075 }
01076 
01077 bool DateBookDB::saveJournalEntry( const Event &evOld, journal_action action,
01078                                    int key, bool origHadRepeat )
01079 {
01080     bool status = false;
01081     Event ev = evOld;
01082     // write our log based on the action
01083     QFile f( dateBookJournalFile() );
01084     if ( !f.open( IO_WriteOnly|IO_Append ) )
01085         return false;
01086     QString buf = "<event";
01087     ev.save( buf );
01088     buf += " action=";
01089     buf += "\"" + QString::number(action) + "\"";
01090     buf += " actionkey=\"" + QString::number(key) + "\"";
01091     buf += " actionorig=\"" + QString::number(origHadRepeat) +"\"";
01092     buf += " />\n";
01093     QString str = buf.utf8();
01094     status = ( f.writeBlock( str.data(), str.length() ) == int(str.length()) );
01095     f.close();
01096     return status;
01097 }
01098 
01099 QValueList<Event> DateBookDB::getRawRepeats() const
01100 {
01101     return repeatEvents;
01102 }
01103 
01104 QValueList<Event> DateBookDB::getNonRepeatingEvents( const QDate &from,
01105                                                      const QDate &to ) const
01106 {
01107     QValueListConstIterator<Event> it;
01108     QDateTime dtTmp, dtEnd;
01109     QValueList<Event> tmpList;
01110     for (it = eventList.begin(); it != eventList.end(); ++it ) {
01111         dtTmp = (*it).start(TRUE);
01112         dtEnd = (*it).end(TRUE);
01113 
01114         if ( dtTmp.date() >= from && dtTmp.date() <= to ) {
01115             Event e = *it;
01116             if ( dtTmp.date() != dtEnd.date() )
01117                 e.setEnd( QDateTime(dtTmp.date(), QTime(23, 59, 0)) );
01118             tmpList.append( e );
01119         }
01120         // we must also check for end date information...
01121         if ( dtEnd.date() != dtTmp.date() && dtEnd.date() >= from ) {
01122             QDateTime dt = dtTmp.addDays( 1 );
01123             dt.setTime( QTime(0, 0, 0) );
01124             QDateTime dtStop;
01125             if ( dtEnd > to ) {
01126                 dtStop = to;
01127             } else
01128                 dtStop = dtEnd;
01129             while ( dt <= dtStop ) {
01130                 Event ev = *it;
01131                 if ( dt >= from ) {
01132                     ev.setStart( QDateTime(dt.date(), QTime(0, 0, 0)) );
01133                     if ( dt.date() == dtEnd.date() )
01134                         ev.setEnd( QDateTime(dt.date(), dtEnd.time()) );
01135                     else
01136                         ev.setEnd( QDateTime(dt.date(), QTime(23, 59, 0)) );
01137                     tmpList.append( ev );
01138                 }
01139                 dt = dt.addDays( 1 );
01140             }
01141         }
01142     }
01143     return tmpList;
01144 }

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