author | kergoth <kergoth> | 2002-01-25 22:14:26 (UTC) |
---|---|---|
committer | kergoth <kergoth> | 2002-01-25 22:14:26 (UTC) |
commit | 15318cad33835e4e2dc620d033e43cd930676cdd (patch) (side-by-side diff) | |
tree | c2fa0399a2c47fda8e2cd0092c73a809d17f68eb /library/datebookdb.cpp | |
download | opie-15318cad33835e4e2dc620d033e43cd930676cdd.zip opie-15318cad33835e4e2dc620d033e43cd930676cdd.tar.gz opie-15318cad33835e4e2dc620d033e43cd930676cdd.tar.bz2 |
Initial revision
-rw-r--r-- | library/datebookdb.cpp | 1121 |
1 files changed, 1121 insertions, 0 deletions
diff --git a/library/datebookdb.cpp b/library/datebookdb.cpp new file mode 100644 index 0000000..bf7fd94 --- a/dev/null +++ b/library/datebookdb.cpp @@ -0,0 +1,1121 @@ +/********************************************************************** +** Copyright (C) 2000 Trolltech AS. All rights reserved. +** +** This file is part of Qtopia Environment. +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** See http://www.trolltech.com/gpl/ for GPL licensing information. +** +** Contact info@trolltech.com if any conditions of this licensing are +** not clear to you. +** +**********************************************************************/ + +#include <qasciidict.h> +#include <qfile.h> +#include <qmessagebox.h> +#include <qstring.h> +#include <qtextcodec.h> +#include <qtextstream.h> +#include <qtl.h> + +#include <qpe/alarmserver.h> +#include <qpe/global.h> +#include "datebookdb.h" +#include <qpe/stringutil.h> +#include <qpe/timeconversion.h> + +#include <errno.h> +#include <stdlib.h> + + +class DateBookDBPrivate +{ +public: + bool clean; // indcate whether we need to write to disk... +}; + + +// Helper functions + +static QString dateBookJournalFile() +{ + QString str = getenv("HOME"); + return QString( str +"/.caljournal" ); +} + +static QString dateBookFilename() +{ + return Global::applicationFileName("datebook","datebook.xml"); +} + +/* Calculating the next event of a recuring event is actually + computationally inexpensive, esp. compared to checking each day + individually. There are bad worse cases for say the 29th of + february or the 31st of some other months. However + these are still bounded */ +bool nextOccurance(const Event &e, const QDate &from, QDateTime &next) +{ + // easy checks, first are we too far in the future or too far in the past? + QDate tmpDate; + int freq = e.repeatPattern().frequency; + int diff, diff2, a; + int iday, imonth, iyear; + int dayOfWeek = 0; + int firstOfWeek = 0; + int weekOfMonth; + + + if (e.repeatPattern().hasEndDate && e.repeatPattern().endDate() < from) + return FALSE; + + if (e.start() >= from) { + next = e.start(); + return TRUE; + } + + switch ( e.repeatPattern().type ) { + case Event::Weekly: + /* weekly is just daily by 7 */ + /* first convert the repeatPattern.Days() mask to the next + day of week valid after from */ + dayOfWeek = from.dayOfWeek(); + dayOfWeek--; /* we want 0-6, doco for above specs 1-7 */ + + /* this is done in case freq > 1 and from in week not + for this round */ + // firstOfWeek = 0; this is already done at decl. + while(!((1 << firstOfWeek) & e.repeatPattern().days)) + firstOfWeek++; + + /* there is at least one 'day', or there would be no event */ + while(!((1 << (dayOfWeek % 7)) & e.repeatPattern().days)) + dayOfWeek++; + + dayOfWeek = dayOfWeek % 7; /* the actual day of week */ + dayOfWeek -= e.start().date().dayOfWeek() -1; + + firstOfWeek = firstOfWeek % 7; /* the actual first of week */ + firstOfWeek -= e.start().date().dayOfWeek() -1; + + // dayOfWeek may be negitive now + // day of week is number of days to add to start day + + freq *= 7; + // FALL-THROUGH !!!!! + case Event::Daily: + // the add is for the possible fall through from weekly */ + if(e.start().date().addDays(dayOfWeek) > from) { + /* first week exception */ + next = QDateTime(e.start().date().addDays(dayOfWeek), + e.start().time()); + if ((next.date() > e.repeatPattern().endDate()) + && e.repeatPattern().hasEndDate) + return FALSE; + return TRUE; + } + /* if from is middle of a non-week */ + + diff = e.start().date().addDays(dayOfWeek).daysTo(from) % freq; + diff2 = e.start().date().addDays(firstOfWeek).daysTo(from) % freq; + + if(diff != 0) + diff = freq - diff; + if(diff2 != 0) + diff2 = freq - diff2; + diff = QMIN(diff, diff2); + + next = QDateTime(from.addDays(diff), e.start().time()); + if ( (next.date() > e.repeatPattern().endDate()) + && e.repeatPattern().hasEndDate ) + return FALSE; + return TRUE; + case Event::MonthlyDay: + iday = from.day(); + iyear = from.year(); + imonth = from.month(); + /* find equivelent day of month for this month */ + dayOfWeek = e.start().date().dayOfWeek(); + weekOfMonth = (e.start().date().day() - 1) / 7; + + /* work out when the next valid month is */ + a = from.year() - e.start().date().year(); + a *= 12; + a = a + (imonth - e.start().date().month()); + /* a is e.start()monthsFrom(from); */ + if(a % freq) { + a = freq - (a % freq); + imonth = from.month() + a; + if (imonth > 12) { + imonth--; + iyear += imonth / 12; + imonth = imonth % 12; + imonth++; + } + } + /* imonth is now the first month after or on + from that matches the frequency given */ + + /* find for this month */ + tmpDate = QDate( iyear, imonth, 1 ); + + iday = 1; + iday += (7 + dayOfWeek - tmpDate.dayOfWeek()) % 7; + iday += 7 * weekOfMonth; + while (iday > tmpDate.daysInMonth()) { + imonth += freq; + if (imonth > 12) { + imonth--; + iyear += imonth / 12; + imonth = imonth % 12; + imonth++; + } + tmpDate = QDate( iyear, imonth, 1 ); + /* these loops could go for a while, check end case now */ + if ((tmpDate > e.repeatPattern().endDate()) && e.repeatPattern().hasEndDate) + return FALSE; + iday = 1; + iday += (7 + dayOfWeek - tmpDate.dayOfWeek()) % 7; + iday += 7 * weekOfMonth; + } + tmpDate = QDate(iyear, imonth, iday); + + if (tmpDate >= from) { + next = QDateTime(tmpDate, e.start().time()); + if ((next.date() > e.repeatPattern().endDate()) && e.repeatPattern().hasEndDate) + return FALSE; + return TRUE; + } + + /* need to find the next iteration */ + do { + imonth += freq; + if (imonth > 12) { + imonth--; + iyear += imonth / 12; + imonth = imonth % 12; + imonth++; + } + tmpDate = QDate( iyear, imonth, 1 ); + /* these loops could go for a while, check end case now */ + if ((tmpDate > e.repeatPattern().endDate()) && e.repeatPattern().hasEndDate) + return FALSE; + iday = 1; + iday += (7 + dayOfWeek - tmpDate.dayOfWeek()) % 7; + iday += 7 * weekOfMonth; + } while (iday > tmpDate.daysInMonth()); + tmpDate = QDate(iyear, imonth, iday); + + next = QDateTime(tmpDate, e.start().time()); + if ((next.date() > e.repeatPattern().endDate()) && e.repeatPattern().hasEndDate) + return FALSE; + return TRUE; + case Event::MonthlyDate: + iday = e.start().date().day(); + iyear = from.year(); + imonth = from.month(); + + a = from.year() - e.start().date().year(); + a *= 12; + a = a + (imonth - e.start().date().month()); + /* a is e.start()monthsFrom(from); */ + if(a % freq) { + a = freq - (a % freq); + imonth = from.month() + a; + if (imonth > 12) { + imonth--; + iyear += imonth / 12; + imonth = imonth % 12; + imonth++; + } + } + /* imonth is now the first month after or on + from that matches the frequencey given */ + + /* this could go for a while, worse case, 4*12 iterations, probably */ + while(!QDate::isValid(iyear, imonth, iday) ) { + imonth += freq; + if (imonth > 12) { + imonth--; + iyear += imonth / 12; + imonth = imonth % 12; + imonth++; + } + /* these loops could go for a while, check end case now */ + if ((QDate(iyear, imonth, 1) > e.repeatPattern().endDate()) && e.repeatPattern().hasEndDate) + return FALSE; + } + + if(QDate(iyear, imonth, iday) >= from) { + /* done */ + next = QDateTime(QDate(iyear, imonth, iday), + e.start().time()); + if ((next.date() > e.repeatPattern().endDate()) && e.repeatPattern().hasEndDate) + return FALSE; + return TRUE; + } + + /* ok, need to cycle */ + imonth += freq; + imonth--; + iyear += imonth / 12; + imonth = imonth % 12; + imonth++; + + while(!QDate::isValid(iyear, imonth, iday) ) { + imonth += freq; + imonth--; + iyear += imonth / 12; + imonth = imonth % 12; + imonth++; + if ((QDate(iyear, imonth, 1) > e.repeatPattern().endDate()) && e.repeatPattern().hasEndDate) + return FALSE; + } + + next = QDateTime(QDate(iyear, imonth, iday), e.start().time()); + if ((next.date() > e.repeatPattern().endDate()) && e.repeatPattern().hasEndDate) + return FALSE; + return TRUE; + case Event::Yearly: + iday = e.start().date().day(); + imonth = e.start().date().month(); + iyear = from.year(); // after all, we want to start in this year + + diff = 1; + if(imonth == 2 && iday > 28) { + /* leap year, and it counts, calculate actual frequency */ + if(freq % 4) + if (freq % 2) + freq = freq * 4; + else + freq = freq * 2; + /* else divides by 4 already, leave freq alone */ + diff = 4; + } + + a = from.year() - e.start().date().year(); + if(a % freq) { + a = freq - (a % freq); + iyear = iyear + a; + } + + /* under the assumption we won't hit one of the special not-leap years twice */ + if(!QDate::isValid(iyear, imonth, iday)) { + /* must have been skipping by leap years and hit one that wasn't, (e.g. 2100) */ + iyear += freq; + } + + if(QDate(iyear, imonth, iday) >= from) { + next = QDateTime(QDate(iyear, imonth, iday), + e.start().time()); + if ((next.date() > e.repeatPattern().endDate()) && e.repeatPattern().hasEndDate) + return FALSE; + return TRUE; + } + /* iyear == from.year(), need to advance again */ + iyear += freq; + /* under the assumption we won't hit one of the special not-leap years twice */ + if(!QDate::isValid(iyear, imonth, iday)) { + /* must have been skipping by leap years and hit one that wasn't, (e.g. 2100) */ + iyear += freq; + } + + next = QDateTime(QDate(iyear, imonth, iday), e.start().time()); + if ((next.date() > e.repeatPattern().endDate()) && e.repeatPattern().hasEndDate) + return FALSE; + return TRUE; + default: + return FALSE; + } +} + +static bool nextAlarm( const Event &ev, QDateTime& when, int& warn) +{ + QDateTime now = QDateTime::currentDateTime(); + if ( ev.hasRepeat() ) { + QDateTime ralarm; + if (nextOccurance(ev, now.date(), ralarm)) { + ralarm = ralarm.addSecs(-ev.alarmTime()*60); + if ( ralarm > now ) { + when = ralarm; + warn = ev.alarmTime(); + } else if ( nextOccurance(ev, now.date().addDays(1), ralarm) ) { + ralarm = ralarm.addSecs( -ev.alarmTime()*60 ); + if ( ralarm > now ) { + when = ralarm; + warn = ev.alarmTime(); + } + } + } + } else { + warn = ev.alarmTime(); + when = ev.start().addSecs( -ev.alarmTime()*60 ); + } + return when > now; +} + +static void addEventAlarm( const Event &ev ) +{ + QDateTime when; + int warn; + if ( nextAlarm(ev,when,warn) ) + AlarmServer::addAlarm( when, + "QPE/Application/datebook", + "alarm(QDateTime,int)", warn ); +} + +static void delEventAlarm( const Event &ev ) +{ + QDateTime when; + int warn; + if ( nextAlarm(ev,when,warn) ) + AlarmServer::deleteAlarm( when, + "QPE/Application/datebook", + "alarm(QDateTime,int)", warn ); +} + + +DateBookDB::DateBookDB() +{ + init(); +} + +DateBookDB::~DateBookDB() +{ + save(); + eventList.clear(); + repeatEvents.clear(); +} + + +//#### Why is this code duplicated in getEffectiveEvents ????? +//#### Addendum. Don't use this function, lets faze it out if we can. +QValueList<Event> DateBookDB::getEvents( const QDate &from, const QDate &to ) +{ + QValueList<Event> tmpList; + tmpList = getNonRepeatingEvents( from, to ); + + // check for repeating events... + for (QValueList<Event>::ConstIterator it = repeatEvents.begin(); + it != repeatEvents.end(); ++it) { + QDate itDate = from; + QDateTime due; + + /* create a false end date, to short circuit on hard + MonthlyDay recurences */ + Event dummy_event = *it; + Event::RepeatPattern r = dummy_event.repeatPattern(); + if ( !r.hasEndDate || r.endDate() > to ) { + r.setEndDate( to ); + r.hasEndDate = TRUE; + } + dummy_event.setRepeat(TRUE, r); + + while (nextOccurance(dummy_event, itDate, due)) { + if (due.date() > to) + break; + Event newEvent = *it; + newEvent.setStart(due); + newEvent.setEnd(due.addSecs((*it).start().secsTo((*it).end()))); + + tmpList.append(newEvent); + itDate = due.date().addDays(1); /* the next event */ + } + } + qHeapSort(tmpList); + return tmpList; +} + +QValueList<Event> DateBookDB::getEvents( const QDateTime &start ) +{ + QValueList<Event> day = getEvents(start.date(),start.date()); + + QValueListConstIterator<Event> it; + QDateTime dtTmp; + QValueList<Event> tmpList; + for (it = day.begin(); it != day.end(); ++it ) { + dtTmp = (*it).start(TRUE); + if ( dtTmp == start ) + tmpList.append( *it ); + } + return tmpList; +} + +//#### Why is this code duplicated in getEvents ????? + +QValueList<EffectiveEvent> DateBookDB::getEffectiveEvents( const QDate &from, + const QDate &to ) +{ + QValueList<EffectiveEvent> tmpList; + QValueListIterator<Event> it; + + EffectiveEvent effEv; + QDateTime dtTmp, + dtEnd; + + for (it = eventList.begin(); it != eventList.end(); ++it ) { + dtTmp = (*it).start(TRUE); + dtEnd = (*it).end(TRUE); + + if ( dtTmp.date() >= from && dtTmp.date() <= to ) { + Event tmpEv = *it; + effEv.setEvent(tmpEv); + effEv.setDate( dtTmp.date() ); + effEv.setStart( dtTmp.time() ); + if ( dtTmp.date() != dtEnd.date() ) + effEv.setEnd( QTime(23, 59, 0) ); + else + effEv.setEnd( dtEnd.time() ); + tmpList.append( effEv ); + } + // we must also check for end date information... + if ( dtEnd.date() != dtTmp.date() && dtEnd.date() >= from ) { + QDateTime dt = dtTmp.addDays( 1 ); + dt.setTime( QTime(0, 0, 0) ); + QDateTime dtStop; + if ( dtEnd > to ) { + dtStop = to; + } else + dtStop = dtEnd; + while ( dt <= dtStop ) { + Event tmpEv = *it; + effEv.setEvent( tmpEv ); + effEv.setDate( dt.date() ); + if ( dt >= from ) { + effEv.setStart( QTime(0, 0, 0) ); + if ( dt.date() == dtEnd.date() ) + effEv.setEnd( dtEnd.time() ); + else + effEv.setEnd( QTime(23, 59, 59) ); + tmpList.append( effEv ); + } + dt = dt.addDays( 1 ); + } + } + } + // check for repeating events... + QDateTime repeat; + for ( it = repeatEvents.begin(); it != repeatEvents.end(); ++it ) { + + /* create a false end date, to short circuit on hard + MonthlyDay recurences */ + Event dummy_event = *it; + int duration = (*it).start().date().daysTo( (*it).end().date() ); + QDate itDate = from.addDays(-duration); + + Event::RepeatPattern r = dummy_event.repeatPattern(); + if ( !r.hasEndDate || r.endDate() > to ) { + r.setEndDate( to ); + r.hasEndDate = TRUE; + } + dummy_event.setRepeat(TRUE, r); + + while (nextOccurance(dummy_event, itDate, repeat)) { + if(repeat.date() > to) + break; + effEv.setDate( repeat.date() ); + if ((*it).type() == Event::AllDay) { + effEv.setStart( QTime(0,0,0) ); + effEv.setEnd( QTime(23,59,59) ); + } else { + /* we only occur by days, not hours/minutes/seconds. Hence + the actual end and start times will be the same for + every repeated event. For multi day events this is + fixed up later if on wronge day span */ + effEv.setStart( (*it).start().time() ); + effEv.setEnd( (*it).end().time() ); + } + if ( duration != 0 ) { + // multi-day repeating events + QDate sub_it = QMAX( repeat.date(), from ); + QDate startDate = repeat.date(); + QDate endDate = startDate.addDays( duration ); + + while ( sub_it <= endDate && sub_it <= to ) { + EffectiveEvent tmpEffEv = effEv; + Event tmpEv = *it; + tmpEffEv.setEvent( tmpEv ); + + if ( sub_it != startDate ) + tmpEffEv.setStart( QTime(0,0,0) ); + if ( sub_it != endDate ) + tmpEffEv.setEnd( QTime(23,59,59) ); + tmpEffEv.setDate( sub_it ); + tmpEffEv.setEffectiveDates( startDate, endDate ); + tmpList.append( tmpEffEv ); + sub_it = sub_it.addDays( 1 ); + } + itDate = endDate; + } else { + Event tmpEv = *it; + effEv.setEvent( tmpEv ); + tmpList.append( effEv ); + itDate = repeat.date().addDays( 1 ); + } + } + } + + qHeapSort( tmpList ); + return tmpList; +} + +QValueList<EffectiveEvent> DateBookDB::getEffectiveEvents( const QDateTime &dt) +{ + QValueList<EffectiveEvent> day = getEffectiveEvents(dt.date(), dt.date()); + QValueListConstIterator<EffectiveEvent> it; + QValueList<EffectiveEvent> tmpList; + QDateTime dtTmp; + + for (it = day.begin(); it != day.end(); ++it ) { + dtTmp = QDateTime( (*it).date(), (*it).start() ); + // at the moment we don't have second granularity, be nice about that.. + if ( QABS(dt.secsTo(dtTmp)) < 60 ) + tmpList.append( *it ); + } + return tmpList; +} + + +void DateBookDB::addEvent( const Event &ev, bool doalarm ) +{ + // write to the journal... + saveJournalEntry( ev, ACTION_ADD, -1, false ); + addJFEvent( ev, doalarm ); + d->clean = false; +} + +void DateBookDB::addJFEvent( const Event &ev, bool doalarm ) +{ + if ( doalarm && ev.hasAlarm() ) + addEventAlarm( ev ); + if ( ev.hasRepeat() ) + repeatEvents.append( ev ); + else + eventList.append( ev ); +} + +void DateBookDB::editEvent( const Event &old, Event &editedEv ) +{ + int oldIndex=0; + bool oldHadRepeat = old.hasRepeat(); + Event orig; + + // write to the journal... + if ( oldHadRepeat ) { + if ( origRepeat( old, orig ) ) // should work always... + oldIndex = repeatEvents.findIndex( orig ); + } else + oldIndex = eventList.findIndex( old ); + saveJournalEntry( editedEv, ACTION_REPLACE, oldIndex, oldHadRepeat ); + + if ( old.hasAlarm() ) + delEventAlarm( old ); + if ( oldHadRepeat ) { + if ( oldHadRepeat && editedEv.hasRepeat() ) { + // assumption, when someone edits a repeating event, they + // want to change them all, maybe not perfect, but it works + // for the moment... + repeatEvents.remove( orig ); + } else + removeRepeat( old ); + } else { + QValueList<Event>::Iterator it = eventList.find( old ); + if ( it != eventList.end() ) + eventList.remove( it ); + } + if ( editedEv.hasAlarm() ) + addEventAlarm( editedEv ); + if ( editedEv.hasRepeat() ) + repeatEvents.append( editedEv ); + else + eventList.append( editedEv ); + d->clean = false; +} + +void DateBookDB::removeEvent( const Event &ev ) +{ + // write to the journal... + saveJournalEntry( ev, ACTION_REMOVE, -1, false ); + removeJFEvent( ev ); + d->clean = false; +} + +void DateBookDB::removeJFEvent( const Event&ev ) +{ + if ( ev.hasAlarm() ) + delEventAlarm( ev ); + if ( ev.hasRepeat() ) { + removeRepeat( ev ); + } else { + QValueList<Event>::Iterator it = eventList.find( ev ); + if ( it != eventList.end() ) + eventList.remove( it ); + } +} + +// also handles journaling... +void DateBookDB::loadFile( const QString &strFile ) +{ + + QFile f( strFile ); + if ( !f.open( IO_ReadOnly ) ) + return; + + enum Attribute { + FDescription = 0, + FLocation, + FCategories, + FUid, + FType, + FAlarm, + FSound, + FRType, + FRWeekdays, + FRPosition, + FRFreq, + FRHasEndDate, + FREndDate, + FRStart, + FREnd, + FNote, + FCreated, + FAction, + FActionKey, + FJournalOrigHadRepeat + }; + + QAsciiDict<int> dict( 97 ); + dict.setAutoDelete( TRUE ); + dict.insert( "description", new int(FDescription) ); + dict.insert( "location", new int(FLocation) ); + dict.insert( "categories", new int(FCategories) ); + dict.insert( "uid", new int(FUid) ); + dict.insert( "type", new int(FType) ); + dict.insert( "alarm", new int(FAlarm) ); + dict.insert( "sound", new int(FSound) ); + dict.insert( "rtype", new int(FRType) ); + dict.insert( "rweekdays", new int(FRWeekdays) ); + dict.insert( "rposition", new int(FRPosition) ); + dict.insert( "rfreq", new int(FRFreq) ); + dict.insert( "rhasenddate", new int(FRHasEndDate) ); + dict.insert( "enddt", new int(FREndDate) ); + dict.insert( "start", new int(FRStart) ); + dict.insert( "end", new int(FREnd) ); + dict.insert( "note", new int(FNote) ); + dict.insert( "created", new int(FCreated) ); + dict.insert( "action", new int(FAction) ); + dict.insert( "actionkey", new int(FActionKey) ); + dict.insert( "actionorig", new int (FJournalOrigHadRepeat) ); + + + QByteArray ba = f.readAll(); + char* dt = ba.data(); + int len = ba.size(); + int currentAction, + journalKey, + origHadRepeat; // should be bool, but we need tri-state(not being used) + + int i = 0; + char *point; + while ( ( point = strstr( dt+i, "<event " ) ) != 0 ) { + i = point - dt; + // if we are reading in events in the general case, + // we are just adding them, so let the actions represent that... + currentAction = ACTION_ADD; + journalKey = -1; + origHadRepeat = -1; + // some temporary variables for dates and times ... + //int startY = 0, startM = 0, startD = 0, starth = 0, startm = 0, starts = 0; + //int endY = 0, endM = 0, endD = 0, endh = 0, endm = 0, ends = 0; + //int enddtY = 0, enddtM = 0, enddtD = 0; + + // ... for the alarm settings ... + int alarmTime = -1; Event::SoundTypeChoice alarmSound = Event::Silent; + // ... and for the recurrence + Event::RepeatPattern rp; + Event e; + + i += 7; + + while( 1 ) { + while ( i < len && (dt[i] == ' ' || dt[i] == '\n' || dt[i] == '\r') ) + ++i; + if ( i >= len-2 || (dt[i] == '/' && dt[i+1] == '>') ) + break; + // we have another attribute, read it. + int j = i; + while ( j < len && dt[j] != '=' ) + ++j; + char *attr = dt+i; + dt[j] = '\0'; + i = ++j; // skip = + while ( i < len && dt[i] != '"' ) + ++i; + j = ++i; + bool haveAmp = FALSE; + bool haveUtf = FALSE; + while ( j < len && dt[j] != '"' ) { + if ( dt[j] == '&' ) + haveAmp = TRUE; + if ( ((unsigned char)dt[j]) > 0x7f ) + haveUtf = TRUE; + ++j; + } + + if ( i == j ) { + // leave out empty attributes + i = j + 1; + continue; + } + + QString value = haveUtf ? QString::fromUtf8( dt+i, j-i ) + : QString::fromLatin1( dt+i, j-i ); + if ( haveAmp ) + value = Qtopia::plainString( value ); + i = j + 1; + + //qDebug("attr='%s' value='%s'", attr.data(), value.latin1() ); + int * find = dict[ attr ]; +#if 1 + if ( !find ) { + // custom field + e.setCustomField(attr, value); + continue; + } + + switch( *find ) { + case FDescription: + e.setDescription( value ); + break; + case FLocation: + e.setLocation( value ); + break; + case FCategories: + e.setCategories( Qtopia::Record::idsFromString( value ) ); + break; + case FUid: + e.setUid( value.toInt() ); + break; + case FType: + if ( value == "AllDay" ) + e.setType( Event::AllDay ); + else + e.setType( Event::Normal ); + break; + case FAlarm: + alarmTime = value.toInt(); + break; + case FSound: + alarmSound = value == "loud" ? Event::Loud : Event::Silent; + break; + // recurrence stuff + case FRType: + if ( value == "Daily" ) + rp.type = Event::Daily; + else if ( value == "Weekly" ) + rp.type = Event::Weekly; + else if ( value == "MonthlyDay" ) + rp.type = Event::MonthlyDay; + else if ( value == "MonthlyDate" ) + rp.type = Event::MonthlyDate; + else if ( value == "Yearly" ) + rp.type = Event::Yearly; + else + rp.type = Event::NoRepeat; + break; + case FRWeekdays: + rp.days = value.toInt(); + break; + case FRPosition: + rp.position = value.toInt(); + break; + case FRFreq: + rp.frequency = value.toInt(); + break; + case FRHasEndDate: + rp.hasEndDate = value.toInt(); + break; + case FREndDate: { + rp.endDateUTC = (time_t) value.toLong(); + break; + } + case FRStart: { + e.setStart( (time_t) value.toLong() ); + break; + } + case FREnd: { + e.setEnd( (time_t) value.toLong() ); + break; + } + case FNote: + e.setNotes( value ); + break; + case FCreated: + rp.createTime = value.toInt(); + break; + case FAction: + currentAction = value.toInt(); + break; + case FActionKey: + journalKey = value.toInt(); + break; + case FJournalOrigHadRepeat: + origHadRepeat = value.toInt(); + break; + default: + qDebug( "huh??? missing enum? -- attr.: %s", attr ); + break; + } +#endif + } + // "post processing" (dates, times, alarm, recurrence) + // start date/time + e.setRepeat( rp.type != Event::NoRepeat, rp ); + + if ( alarmTime != -1 ) + e.setAlarm( TRUE, alarmTime, alarmSound ); + + // now do our action based on the current action... + switch ( currentAction ) { + case ACTION_ADD: + addJFEvent( e ); + break; + case ACTION_REMOVE: + removeJFEvent( e ); + break; + case ACTION_REPLACE: + // be a little bit careful, + // in case of a messed up journal... + if ( journalKey > -1 && origHadRepeat > -1 ) { + // get the original from proper list... + if ( origHadRepeat ) + removeJFEvent( *(repeatEvents.at(journalKey)) ); + else + removeJFEvent( *(eventList.at(journalKey)) ); + addJFEvent( e ); + } + break; + default: + break; + } + } + f.close(); +} + +void DateBookDB::init() +{ + d = new DateBookDBPrivate; + d->clean = false; + QString str = dateBookFilename(); + if ( str.isNull() ) { + QMessageBox::warning( 0, QObject::tr("Out of Space"), + QObject::tr("Unable to create start up files\n" + "Please free up some space\n" + "before entering data") ); + } + // continuing along, we call this datebook filename again, + // because they may fix it before continuing, though it seems + // pretty unlikely... + loadFile( dateBookFilename() ); + + if ( QFile::exists( dateBookJournalFile() ) ) { + // merge the journal + loadFile( dateBookJournalFile() ); + // save in our changes and remove the journal... + save(); + } + d->clean = true; +} + +bool DateBookDB::save() +{ + if ( d->clean == true ) + return true; + QValueListIterator<Event> it; + int total_written; + QString strFileNew = dateBookFilename() + ".new"; + + QFile f( strFileNew ); + if ( !f.open( IO_WriteOnly|IO_Raw ) ) + return FALSE; + + QString buf( "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" ); + buf += "<!DOCTYPE DATEBOOK><DATEBOOK>\n"; + buf += "<events>\n"; + QCString str = buf.utf8(); + total_written = f.writeBlock( str.data(), str.length() ); + if ( total_written != int(str.length()) ) { + f.close(); + QFile::remove( strFileNew ); + return false; + } + + for ( it = eventList.begin(); it != eventList.end(); ++it ) { + buf = "<event"; + (*it).save( buf ); + buf += " />\n"; + str = buf.utf8(); + total_written = f.writeBlock( str.data(), str.length() ); + if ( total_written != int(str.length()) ) { + f.close(); + QFile::remove( strFileNew ); + return false; + } + } + for ( it = repeatEvents.begin(); it != repeatEvents.end(); ++it ) { + buf = "<event"; + (*it).save( buf ); + buf += " />\n"; + str = buf.utf8(); + total_written = f.writeBlock( str.data(), str.length() ); + if ( total_written != int(str.length()) ) { + f.close(); + QFile::remove( strFileNew ); + return false; + } + } + buf = "</events>\n</DATEBOOK>\n"; + str = buf.utf8(); + total_written = f.writeBlock( str.data(), str.length() ); + if ( total_written != int(str.length()) ) { + f.close(); + QFile::remove( strFileNew ); + return false; + } + f.close(); + + // now rename... I like to use the systemcall + if ( ::rename( strFileNew, dateBookFilename() ) < 0 ) { + qWarning( "problem renaming file %s to %s errno %d", + strFileNew.latin1(), dateBookFilename().latin1(), errno ); + // remove the file, otherwise it will just stick around... + QFile::remove( strFileNew ); + } + + // may as well remove the journal file... + QFile::remove( dateBookJournalFile() ); + d->clean = true; + return true; +} + +void DateBookDB::reload() +{ + QValueList<Event>::Iterator it = eventList.begin(); + for ( ; it != eventList.end(); ++it ) { + if ( (*it).hasAlarm() ) + delEventAlarm( *it ); + if ( (*it).hasRepeat() ) + removeRepeat( *it ); + } + eventList.clear(); + repeatEvents.clear(); // should be a NOP + init(); +} + +bool DateBookDB::removeRepeat( const Event &ev ) +{ + time_t removeMe = ev.repeatPattern().createTime; + QValueListIterator<Event> it; + for ( it = repeatEvents.begin(); it != repeatEvents.end(); ++it ) { + if ( removeMe == (*it).repeatPattern().createTime ) { + repeatEvents.remove( *it ); + // best break, or we are going into undefined territory! + return TRUE; + } + } + return FALSE; +} + +bool DateBookDB::origRepeat( const Event &ev, Event &orig ) const +{ + time_t removeMe = ev.repeatPattern().createTime; + QValueListConstIterator<Event> it; + for ( it = repeatEvents.begin(); it != repeatEvents.end(); ++it ) { + if ( removeMe == (*it).repeatPattern().createTime ) { + orig = (*it); + return TRUE; + } + } + return FALSE; +} + +void DateBookDB::saveJournalEntry( const Event &ev, journal_action action ) +{ + saveJournalEntry( ev, action, -1, false ); +} + +bool DateBookDB::saveJournalEntry( const Event &evOld, journal_action action, + int key, bool origHadRepeat ) +{ + bool status = false; + Event ev = evOld; + // write our log based on the action + QFile f( dateBookJournalFile() ); + if ( !f.open( IO_WriteOnly|IO_Append ) ) + return false; + QString buf = "<event"; + ev.save( buf ); + buf += " action="; + buf += "\"" + QString::number(action) + "\""; + buf += " actionkey=\"" + QString::number(key) + "\""; + buf += " actionorig=\"" + QString::number(origHadRepeat) +"\""; + buf += " />\n"; + QString str = buf.utf8(); + status = ( f.writeBlock( str.data(), str.length() ) == int(str.length()) ); + f.close(); + return status; +} + +QValueList<Event> DateBookDB::getRawRepeats() const +{ + return repeatEvents; +} + +QValueList<Event> DateBookDB::getNonRepeatingEvents( const QDate &from, + const QDate &to ) const +{ + QValueListConstIterator<Event> it; + QDateTime dtTmp, dtEnd; + QValueList<Event> tmpList; + for (it = eventList.begin(); it != eventList.end(); ++it ) { + dtTmp = (*it).start(TRUE); + dtEnd = (*it).end(TRUE); + + if ( dtTmp.date() >= from && dtTmp.date() <= to ) { + Event e = *it; + if ( dtTmp.date() != dtEnd.date() ) + e.setEnd( QDateTime(dtTmp.date(), QTime(23, 59, 0)) ); + tmpList.append( e ); + } + // we must also check for end date information... + if ( dtEnd.date() != dtTmp.date() && dtEnd.date() >= from ) { + QDateTime dt = dtTmp.addDays( 1 ); + dt.setTime( QTime(0, 0, 0) ); + QDateTime dtStop; + if ( dtEnd > to ) { + dtStop = to; + } else + dtStop = dtEnd; + while ( dt <= dtStop ) { + Event ev = *it; + if ( dt >= from ) { + ev.setStart( QDateTime(dt.date(), QTime(0, 0, 0)) ); + if ( dt.date() == dtEnd.date() ) + ev.setEnd( QDateTime(dt.date(), dtEnd.time()) ); + else + ev.setEnd( QDateTime(dt.date(), QTime(23, 59, 0)) ); + tmpList.append( ev ); + } + dt = dt.addDays( 1 ); + } + } + } + return tmpList; +} |