summaryrefslogtreecommitdiff
path: root/library/datebookdb.cpp
Side-by-side diff
Diffstat (limited to 'library/datebookdb.cpp') (more/less context) (ignore whitespace changes)
-rw-r--r--library/datebookdb.cpp3
1 files changed, 2 insertions, 1 deletions
diff --git a/library/datebookdb.cpp b/library/datebookdb.cpp
index 2ac9a0c..a26fe8f 100644
--- a/library/datebookdb.cpp
+++ b/library/datebookdb.cpp
@@ -352,769 +352,770 @@ static bool nextAlarm( const Event &ev, QDateTime& when, int& warn)
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 ) {
if (!(*it).isValidUid())
(*it).assignUid(); // FIXME: Hack to restore cleared uids
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 ) {
if (!(*it).isValidUid())
(*it).assignUid(); // FIXME: Hack to restore cleared uids
/* 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 );
// Delete old event
if ( old.hasAlarm() )
delEventAlarm( old );
if ( oldHadRepeat ) {
if ( editedEv.hasRepeat() ) { // This mean that origRepeat was run above and
// orig is initialized
// 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 );
}
// Add new event
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 ) {
+ // hack to get rid of segfaults after reading </DATEBOOK>
+ while ( (dt+i != 0) && (( 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()) );