author | zecke <zecke> | 2003-02-22 16:11:16 (UTC) |
---|---|---|
committer | zecke <zecke> | 2003-02-22 16:11:16 (UTC) |
commit | 397a7b1635488deda02b36df70692e27f59bb3e0 (patch) (side-by-side diff) | |
tree | 1232b1b7e8ac84c5d48df79b635ba61acbdf0f62 /libopie2 | |
parent | e7981098a5c373748d98158e8a6c59750bb0d7dc (diff) | |
download | opie-397a7b1635488deda02b36df70692e27f59bb3e0.zip opie-397a7b1635488deda02b36df70692e27f59bb3e0.tar.gz opie-397a7b1635488deda02b36df70692e27f59bb3e0.tar.bz2 |
Implement saving of events
Implement the ExceptionList
Add Children and parent to OEvent
Make ORecur honor exceptions
Extend the test app to add and save
-rw-r--r-- | libopie2/opiepim/backend/odatebookaccessbackend_xml.cpp | 184 | ||||
-rw-r--r-- | libopie2/opiepim/core/orecur.cpp | 66 | ||||
-rw-r--r-- | libopie2/opiepim/core/orecur.h | 4 | ||||
-rw-r--r-- | libopie2/opiepim/oevent.cpp | 64 | ||||
-rw-r--r-- | libopie2/opiepim/oevent.h | 10 |
5 files changed, 312 insertions, 16 deletions
diff --git a/libopie2/opiepim/backend/odatebookaccessbackend_xml.cpp b/libopie2/opiepim/backend/odatebookaccessbackend_xml.cpp index a4c514b..5239d84 100644 --- a/libopie2/opiepim/backend/odatebookaccessbackend_xml.cpp +++ b/libopie2/opiepim/backend/odatebookaccessbackend_xml.cpp @@ -1,22 +1,26 @@ #include <errno.h> #include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> + #include <sys/types.h> #include <sys/mman.h> #include <sys/stat.h> #include <unistd.h> #include <qasciidict.h> #include <qfile.h> #include <qtopia/global.h> #include <qtopia/stringutil.h> +#include <qtopia/timeconversion.h> #include "opimnotifymanager.h" #include "orecur.h" #include "otimezone.h" #include "odatebookaccessbackend_xml.h" namespace { time_t start, end, created, rp_end; @@ -25,34 +29,115 @@ namespace { if (!rec) rec = new ORecur; return rec; } int alarmTime; int snd; enum Attribute{ - FDescription = 0, - FLocation, - FCategories, - FUid, - FType, + FDescription = 0, + FLocation, + FCategories, + FUid, + FType, FAlarm, FSound, FRType, FRWeekdays, FRPosition, FRFreq, FRHasEndDate, FREndDate, FRStart, FREnd, FNote, - FCreated + FCreated, + FTimeZone, + FRecParent, + FRecChildren, + FExceptions }; + inline void save( const OEvent& ev, QString& buf ) { + buf += " description=\"" + Qtopia::escapeString(ev.description() ) + "\""; + if (!ev.location().isEmpty() ) + buf += " location=\"" + Qtopia::escapeString(ev.location() ) + "\""; + + buf += " categories=\""+ Qtopia::escapeString( Qtopia::Record::idsToString( ev.categories() ) ) + "\""; + buf += " uid=\"" + QString::number( ev.uid() ) + "\""; + + if (ev.isAllDay() ) + buf += " type=\"AllDay\""; + + if (ev.hasNotifiers() ) { + OPimAlarm alarm = ev.notifiers().alarms()[0]; // take only the first + int minutes = alarm.dateTime().secsTo( ev.startDateTime() ) / 60; + buf += " alarm=\"" + QString::number(minutes) + "\" sound=\""; + if ( alarm.sound() == OPimAlarm::Loud ) + buf += "loud"; + else + buf += "silent"; + buf += "\""; + } + if ( ev.hasRecurrence() ) { + buf += ev.recurrence().toString(); + } + + /* + * fscking timezones :) well, we'll first convert + * the QDateTime to a QDateTime in UTC time + * and then we'll create a nice time_t + */ + OTimeZone zone( ev.timeZone().isEmpty() ? OTimeZone::current() : ev.timeZone() ); + buf += " start=\"" + QString::number( zone.fromUTCDateTime( zone.toDateTime( ev.startDateTime(), OTimeZone::utc() ) ) ) + "\""; + buf += " end=\"" + QString::number( zone.fromUTCDateTime( zone.toDateTime( ev.endDateTime() , OTimeZone::utc() ) ) ) + "\""; + if (!ev.note().isEmpty() ) { + buf += " note=\"" + Qtopia::escapeString( ev.note() ) + "\""; + } + + buf += " timezone=\""; + if ( ev.timeZone().isEmpty() ) + buf += "None"; + else + buf += ev.timeZone(); + + if (ev.parent() != 0 ) { + buf += " recparent=\""+QString::number(ev.parent() )+"\""; + } + + if (ev.children().count() != 0 ) { + QArray<int> children = ev.children(); + buf += " recchildren=\""; + for ( uint i = 0; i < children.count(); i++ ) { + if ( i != 0 ) buf += " "; + buf += QString::number( children[i] ); + } + buf+= "\""; + } + + // skip custom writing + } + + inline bool forAll( const QMap<int, OEvent>& list, QFile& file ) { + QMap<int, OEvent>::ConstIterator it; + QString buf; + QCString str; + int total_written; + for ( it = list.begin(); it != list.end(); ++it ) { + buf = "<event"; + save( it.data(), buf ); + buf += " />\n"; + str = buf.utf8(); + + total_written = file.writeBlock(str.data(), str.length() ); + if ( total_written != int(str.length() ) ) + return false; + } + return true; + } } ODateBookAccessBackend_XML::ODateBookAccessBackend_XML( const QString& , const QString& fileName ) : ODateBookAccessBackend() { m_name = fileName.isEmpty() ? Global::applicationFileName( "datebook", "datebook.xml" ) : fileName; m_changed = false; } @@ -61,19 +146,64 @@ ODateBookAccessBackend_XML::~ODateBookAccessBackend_XML() { bool ODateBookAccessBackend_XML::load() { return loadFile(); } bool ODateBookAccessBackend_XML::reload() { clear(); return load(); } bool ODateBookAccessBackend_XML::save() { - if (!m_changed) return true; - m_changed = false; + qWarning("going to save now"); +// if (!m_changed) return true; + + int total_written; + QString strFileNew = m_name + ".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; + } + if (!forAll( m_raw, f ) ) { + f.close(); + QFile::remove( strFileNew ); + return false; + } + if (!forAll( m_rep, f ) ) { + 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(); + + exit(0); + if ( ::rename( strFileNew, m_name ) < 0 ) { + QFile::remove( strFileNew ); + return false; + } + + m_changed = false; return true; } QArray<int> ODateBookAccessBackend_XML::allRecords()const { QArray<int> ints( m_raw.count()+ m_rep.count() ); uint i = 0; QMap<int, OEvent>::ConstIterator it; for ( it = m_raw.begin(); it != m_raw.end(); ++it ) { @@ -178,17 +308,17 @@ bool ODateBookAccessBackend_XML::loadFile() { if ( map_addr == ( (caddr_t)-1) ) { ::close( fd ); return false; } ::madvise( map_addr, attribute.st_size, MADV_SEQUENTIAL ); ::close( fd ); - QAsciiDict<int> dict(FCreated+1); + QAsciiDict<int> dict(FExceptions+1); 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) ); @@ -197,16 +327,20 @@ bool ODateBookAccessBackend_XML::loadFile() { 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( "recparent", new int(FRecParent) ); + dict.insert( "recchildren", new int(FRecChildren) ); + dict.insert( "exceptions", new int(FExceptions) ); + dict.insert( "timezone", new int(FTimeZone) ); char* dt = (char*)map_addr; int len = attribute.st_size; int i = 0; char* point; const char* collectionString = "<event "; int strLen = ::strlen(collectionString); int *find; @@ -284,20 +418,25 @@ bool ODateBookAccessBackend_XML::loadFile() { return true; } void ODateBookAccessBackend_XML::finalizeRecord( OEvent& ev ) { /* AllDay is alway in UTC */ if ( ev.isAllDay() ) { OTimeZone utc = OTimeZone::utc(); ev.setStartDateTime( utc.fromUTCDateTime( start ) ); ev.setEndDateTime ( utc.fromUTCDateTime( end ) ); + ev.setTimeZone( "UTC"); // make sure it is really utc }else { + /* to current date time */ OTimeZone zone( ev.timeZone().isEmpty() ? OTimeZone::current() : ev.timeZone() ); - ev.setStartDateTime( zone.toDateTime( start ) ); - ev.setEndDateTime ( zone.toDateTime( end ) ); + QDateTime date = zone.toDateTime( start ); + ev.setStartDateTime( zone.toDateTime( date, OTimeZone::current() ) ); + + date = zone.toDateTime( end ); + ev.setEndDateTime ( zone.toDateTime( date, OTimeZone::current() ) ); } if ( rec && rec->doesRecur() ) { OTimeZone utc = OTimeZone::utc(); ORecur recu( *rec ); // call copy c'tor; recu.setEndDate ( utc.fromUTCDateTime( rp_end ).date() ); recu.setCreatedDateTime( utc.fromUTCDateTime( created ) ); recu.setStart( ev.startDateTime().date() ); ev.setRecurrence( recu ); @@ -384,12 +523,35 @@ void ODateBookAccessBackend_XML::setField( OEvent& e, int id, const QString& val break; } case FNote: e.setNote( value ); break; case FCreated: created = value.toInt(); break; + case FRecParent: + e.setParent( value.toInt() ); + break; + case FRecChildren:{ + QStringList list = QStringList::split(' ', value ); + for ( QStringList::Iterator it = list.begin(); it != list.end(); ++it ) { + e.addChild( (*it).toInt() ); + } + } + break; + case FExceptions:{ + QStringList list = QStringList::split(' ', value ); + for (QStringList::Iterator it = list.begin(); it != list.end(); ++it ) { + QDate date( (*it).left(4).toInt(), (*it).mid(4, 2).toInt(), (*it).right(2).toInt() ); + qWarning("adding exception %s", date.toString().latin1() ); + recur()->exceptions().append( date ); + } + } + break; + case FTimeZone: + if ( value != "None" ) + e.setTimeZone( value ); + break; default: break; } } diff --git a/libopie2/opiepim/core/orecur.cpp b/libopie2/opiepim/core/orecur.cpp index e6a4787..e3b45b4 100644 --- a/libopie2/opiepim/core/orecur.cpp +++ b/libopie2/opiepim/core/orecur.cpp @@ -1,12 +1,15 @@ +#include <time.h> + #include <qshared.h> #include <qtopia/timeconversion.h> +#include "otimezone.h" #include "orecur.h" struct ORecur::Data : public QShared { Data() : QShared() { type = ORecur::NoRepeat; freq = -1; days = 0; pos = 0; @@ -76,31 +79,39 @@ bool ORecur::doesRecur( const QDate& date ) { return false; return (recur == date); } // FIXME unuglify! // GPL from Datebookdb.cpp // FIXME exception list! bool ORecur::nextOcurrence( const QDate& from, QDate& next ) { + bool stillLooking; + stillLooking = p_nextOccurrence( from, next ); + while ( stillLooking && data->list.contains(next) ) + stillLooking = p_nextOccurrence( next.addDays(1), next ); + + return stillLooking; +} +bool ORecur::p_nextOccurrence( const QDate& from, QDate& next ) { // easy checks, first are we too far in the future or too far in the past? QDate tmpDate; int freq = frequency(); int diff, diff2, a; int iday, imonth, iyear; int dayOfWeek = 0; int firstOfWeek = 0; int weekOfMonth; if (hasEndDate() && endDate() < from) return FALSE; - if (start() >= from) { + if (start() >= from ) { next = start(); return TRUE; } switch ( type() ) { case Weekly: /* weekly is just daily by 7 */ /* first convert the repeatPattern.Days() mask to the next @@ -438,9 +449,62 @@ void ORecur::checkOrModify() { d2->create = data->create; d2->rep = data->rep; d2->app = data->app; d2->list = data->list; d2->start = data->start; data = d2; } } +QString ORecur::toString()const { + QString buf; + + buf += " rtype=\""; + switch ( data->type ) { + case ORecur::Daily: + buf += "Daily"; + break; + case ORecur::Weekly: + buf += "Weekly"; + break; + case ORecur::MonthlyDay: + buf += "MonthlyDay"; + break; + case ORecur::MonthlyDate: + buf += "MonthlyDate"; + break; + case ORecur::Yearly: + buf += "Yearly"; + break; + default: + buf += "NoRepeat"; + break; + } + buf += "\""; + if (data->days > 0 ) + buf += " rweekdays=\"" + QString::number( static_cast<int>( data->days ) ) + "\""; + if ( data->pos != 0 ) + buf += " rposition=\"" + QString::number(data->pos ) + "\""; + + buf += " rfreq=\"" + QString::number( data->freq ) + "\""; + buf += " rhasenddate=\"" + QString::number( static_cast<int>( data->hasEnd ) ) + "\""; + if ( data->hasEnd ) + buf += " enddt=\"" + + QString::number( OTimeZone::utc().fromUTCDateTime( QDateTime( data->end, QTime(12,0,0) ) ) ) + + "\""; + buf += " created=\"" + QString::number( OTimeZone::utc().fromUTCDateTime( data->create ) ) + "\""; + + if ( data->list.isEmpty() ) return buf; + // save exceptions list here!! + ExceptionList::ConstIterator it; + ExceptionList list = data->list; + buf += " exceptions=\""; + QDate date; + for ( it = list.begin(); it != list.end(); ++it ) { + date = (*it); + if ( it != list.begin() ) buf += " "; + + buf += QCString().sprintf("%04d%02d%02d", date.year(), date.month(), date.day() ); + } + buf += "\""; + return buf; +} diff --git a/libopie2/opiepim/core/orecur.h b/libopie2/opiepim/core/orecur.h index 1e0014b..b214b3f 100644 --- a/libopie2/opiepim/core/orecur.h +++ b/libopie2/opiepim/core/orecur.h @@ -68,17 +68,21 @@ public: void setDays( char c); void setEndDate( const QDate& dt ); void setStart( const QDate& dt ); void setCreatedDateTime( const QDateTime& ); void setHasEndDate( bool b ); void setRepitition(int ); void setService( const QString& ser ); + + /* almost internal */ + QString toString()const; private: + bool p_nextOccurrence( const QDate& from, QDate& next ); void deref(); inline void checkOrModify(); class Data; Data* data; class ORecurPrivate; ORecurPrivate *d; diff --git a/libopie2/opiepim/oevent.cpp b/libopie2/opiepim/oevent.cpp index ada596c..c3eeee2 100644 --- a/libopie2/opiepim/oevent.cpp +++ b/libopie2/opiepim/oevent.cpp @@ -37,34 +37,38 @@ int OCalendarHelper::dayOfWeek( char day ) { } int OCalendarHelper::monthDiff( const QDate& first, const QDate& second ) { return ( second.year() - first.year() ) * 12 + second.month() - first.month(); } struct OEvent::Data : public QShared { Data() : QShared() { + child = 0; recur = 0; manager = 0; isAllDay = false; + parent = 0; } ~Data() { delete manager; delete recur; } QString description; QString location; OPimNotifyManager* manager; ORecur* recur; QString note; QDateTime created; QDateTime start; QDateTime end; bool isAllDay : 1; QString timezone; + QArray<int>* child; + int parent; }; OEvent::OEvent( int uid ) : OPimRecord( uid ) { data = new Data; } OEvent::OEvent( const OEvent& ev) : OPimRecord( ev ), data( ev.data ) @@ -97,27 +101,33 @@ void OEvent::setDescription( const QString& description ) { } void OEvent::setLocation( const QString& loc ) { changeOrModify(); data->location = loc; } QString OEvent::location()const { return data->location; } -OPimNotifyManager &OEvent::notifiers() { +OPimNotifyManager &OEvent::notifiers()const { // I hope we can skip the changeOrModify here // the notifier should take care of it // and OPimNotify is shared too if (!data->manager ) data->manager = new OPimNotifyManager; return *data->manager; } bool OEvent::hasNotifiers()const { - return ( data->manager); + if (!data->manager ) + return false; + if (data->manager->reminders().isEmpty() && + data->manager->alarms().isEmpty() ) + return false; + + return true; } ORecur OEvent::recurrence()const { if (!data->recur) data->recur = new ORecur; return *data->recur; } void OEvent::setRecurrence( const ORecur& rec) { @@ -192,16 +202,17 @@ void OEvent::setAllDay( bool allDay ) { data->isAllDay = allDay; if (allDay ) data->timezone = "UTC"; } void OEvent::setTimeZone( const QString& tz ) { changeOrModify(); data->timezone = tz; } QString OEvent::timeZone()const { + if (data->isAllDay ) return QString::fromLatin1("UTC"); return data->timezone; } bool OEvent::match( const QRegExp& )const { // FIXME return false; } QString OEvent::toRichText()const { // FIXME @@ -234,16 +245,21 @@ void OEvent::changeOrModify() { d2->manager = data->manager; d2->recur = data->recur; d2->note = data->note; d2->created = data->created; d2->start = data->start; d2->end = data->end; d2->isAllDay = data->isAllDay; d2->timezone = data->timezone; + d2->parent = data->parent; + d2->child = data->child; + + if (d2->child ) + d2->child->detach(); data = d2; } } void OEvent::deref() { if ( data->deref() ) { delete data; data = 0; @@ -251,18 +267,60 @@ void OEvent::deref() { } // FIXME QMap<int, QString> OEvent::toMap()const { return QMap<int, QString>(); } QMap<QString, QString> OEvent::toExtraMap()const { return QMap<QString, QString>(); } +int OEvent::parent()const { + return data->parent; +} +void OEvent::setParent( int uid ) { + changeOrModify(); + data->parent = uid; +} +QArray<int> OEvent::children() const{ + if (!data->child) return QArray<int>(); + else + return data->child->copy(); +} +void OEvent::setChildren( const QArray<int>& arr ) { + changeOrModify(); + if (data->child) delete data->child; - + data->child = new QArray<int>( arr ); + data->child->detach(); +} +void OEvent::addChild( int uid ) { + changeOrModify(); + if (!data->child ) { + data->child = new QArray<int>(1); + (*data->child)[0] = uid; + }else{ + int count = data->child->count(); + data->child->resize( count + 1 ); + (*data->child)[count] = uid; + } +} +void OEvent::removeChild( int uid ) { + if (!data->child || !data->child->contains( uid ) ) return; + changeOrModify(); + QArray<int> newAr( data->child->count() - 1 ); + int j = 0; + uint count = data->child->count(); + for ( uint i = 0; i < count; i++ ) { + if ( (*data->child)[i] != uid ) { + newAr[j] = (*data->child)[i]; + j++; + } + } + (*data->child) = newAr; +} struct OEffectiveEvent::Data : public QShared { Data() : QShared() { } OEvent event; QDate date; QTime start, end; QDate startDate, endDate; bool dates : 1; diff --git a/libopie2/opiepim/oevent.h b/libopie2/opiepim/oevent.h index c718e2e..585515c 100644 --- a/libopie2/opiepim/oevent.h +++ b/libopie2/opiepim/oevent.h @@ -56,17 +56,17 @@ public: QString description()const; void setDescription( const QString& description ); QString location()const; void setLocation( const QString& loc ); bool hasNotifiers()const; - OPimNotifyManager ¬ifiers(); + OPimNotifyManager ¬ifiers()const; ORecur recurrence()const; void setRecurrence( const ORecur& ); bool hasRecurrence()const; QString note()const; void setNote( const QString& note ); @@ -94,17 +94,25 @@ public: /* pin this event to a timezone! FIXME */ void setTimeZone( const QString& timeZone ); QString timeZone()const; bool match( const QRegExp& )const; + /** For exception to recurrence here is a list of children... */ + QArray<int> children()const; + void setChildren( const QArray<int>& ); + void addChild( int uid ); + void removeChild( int uid ); + /** return the parent OEvent */ + int parent()const; + void setParent( int uid ); /* needed reimp */ QString toRichText()const; QString toShortText()const; QString type()const; QMap<int, QString> toMap()const; |