Qtopia library API Documentation

datebookdb.cpp

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