From 397a7b1635488deda02b36df70692e27f59bb3e0 Mon Sep 17 00:00:00 2001
From: zecke <zecke>
Date: Sat, 22 Feb 2003 16:11:16 +0000
Subject: 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
---
diff --git a/libopie/pim/odatebookaccessbackend_xml.cpp b/libopie/pim/odatebookaccessbackend_xml.cpp
index a4c514b..5239d84 100644
--- a/libopie/pim/odatebookaccessbackend_xml.cpp
+++ b/libopie/pim/odatebookaccessbackend_xml.cpp
@@ -1,6 +1,9 @@
 #include <errno.h>
 #include <fcntl.h>
 
+#include <stdio.h>
+#include <stdlib.h>
+
 #include <sys/types.h>
 #include <sys/mman.h>
 #include <sys/stat.h>
@@ -12,6 +15,7 @@
 
 #include <qtopia/global.h>
 #include <qtopia/stringutil.h>
+#include <qtopia/timeconversion.h>
 
 #include "opimnotifymanager.h"
 #include "orecur.h"
@@ -30,11 +34,11 @@ namespace {
     int alarmTime;
     int snd;
     enum Attribute{
-        	FDescription = 0,
-	FLocation,
-	FCategories,
-	FUid,
-	FType,
+        FDescription = 0,
+        FLocation,
+        FCategories,
+        FUid,
+        FType,
 	FAlarm,
 	FSound,
 	FRType,
@@ -46,8 +50,89 @@ namespace {
 	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& ,
@@ -66,9 +151,54 @@ bool ODateBookAccessBackend_XML::reload() {
     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 {
@@ -183,7 +313,7 @@ bool ODateBookAccessBackend_XML::loadFile() {
     ::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) );
@@ -202,6 +332,10 @@ bool ODateBookAccessBackend_XML::loadFile() {
     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;
@@ -289,10 +423,15 @@ void ODateBookAccessBackend_XML::finalizeRecord( OEvent& ev ) {
         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();
@@ -389,6 +528,29 @@ void ODateBookAccessBackend_XML::setField( OEvent& e, int id, const QString& val
     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/libopie/pim/oevent.cpp b/libopie/pim/oevent.cpp
index ada596c..c3eeee2 100644
--- a/libopie/pim/oevent.cpp
+++ b/libopie/pim/oevent.cpp
@@ -42,9 +42,11 @@ int OCalendarHelper::monthDiff( const QDate& first, const QDate& second ) {
 
 struct OEvent::Data : public QShared {
     Data() : QShared() {
+        child = 0;
         recur = 0;
         manager = 0;
         isAllDay = false;
+        parent = 0;
     }
     ~Data() {
         delete manager;
@@ -60,6 +62,8 @@ struct OEvent::Data : public QShared {
     QDateTime end;
     bool isAllDay : 1;
     QString timezone;
+    QArray<int>* child;
+    int parent;
 };
 
 OEvent::OEvent( int uid )
@@ -102,7 +106,7 @@ void OEvent::setLocation( const QString& 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
@@ -112,7 +116,13 @@ OPimNotifyManager &OEvent::notifiers() {
     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)
@@ -197,6 +207,7 @@ void OEvent::setTimeZone( const QString& tz ) {
     data->timezone = tz;
 }
 QString OEvent::timeZone()const {
+    if (data->isAllDay ) return QString::fromLatin1("UTC");
     return data->timezone;
 }
 bool OEvent::match( const QRegExp& )const {
@@ -239,6 +250,11 @@ void OEvent::changeOrModify() {
         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;
     }
@@ -256,8 +272,50 @@ QMap<int, QString> OEvent::toMap()const {
 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() {
     }
diff --git a/libopie/pim/oevent.h b/libopie/pim/oevent.h
index c718e2e..585515c 100644
--- a/libopie/pim/oevent.h
+++ b/libopie/pim/oevent.h
@@ -61,7 +61,7 @@ public:
     void setLocation( const QString& loc );
 
     bool hasNotifiers()const;
-    OPimNotifyManager &notifiers();
+    OPimNotifyManager &notifiers()const;
 
     ORecur recurrence()const;
     void setRecurrence( const ORecur& );
@@ -99,7 +99,15 @@ public:
 
     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 */
diff --git a/libopie/pim/orecur.cpp b/libopie/pim/orecur.cpp
index e6a4787..e3b45b4 100644
--- a/libopie/pim/orecur.cpp
+++ b/libopie/pim/orecur.cpp
@@ -1,7 +1,10 @@
+#include <time.h>
+
 #include <qshared.h>
 
 #include <qtopia/timeconversion.h>
 
+#include "otimezone.h"
 #include "orecur.h"
 
 struct ORecur::Data : public QShared {
@@ -81,6 +84,14 @@ bool ORecur::doesRecur( const QDate& date ) {
 // 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;
@@ -95,7 +106,7 @@ bool ORecur::nextOcurrence( const QDate& from, QDate& next ) {
     if (hasEndDate() && endDate() < from)
 	return FALSE;
 
-    if (start() >= from) {
+    if (start() >= from  ) {
 	next = start();
 	return TRUE;
     }
@@ -443,4 +454,57 @@ void ORecur::checkOrModify() {
         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/libopie/pim/orecur.h b/libopie/pim/orecur.h
index 1e0014b..b214b3f 100644
--- a/libopie/pim/orecur.h
+++ b/libopie/pim/orecur.h
@@ -73,7 +73,11 @@ public:
     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();
 
diff --git a/libopie/pim/test/oevent_test.cpp b/libopie/pim/test/oevent_test.cpp
index 6f04995..247b83b 100644
--- a/libopie/pim/test/oevent_test.cpp
+++ b/libopie/pim/test/oevent_test.cpp
@@ -5,12 +5,12 @@
 
 int main(int argc, char* argv ) {
     OEvent ev;
-    ev.setUid( 20 );
+//    ev.setUid( 20 );
 
-    ev.setDescription( "Foo" );
+    ev.setDescription( "Foo Descsfewrf" );
 
     OEvent ev2 = ev;
-    ev2.setDescription("Foo2");
+    ev2.setDescription("Foo3");
     qWarning("%s", ev2.description().latin1() );
     qWarning("%s", ev.description().latin1() );
 
@@ -20,6 +20,7 @@ int main(int argc, char* argv ) {
 
     qWarning("%s", ev2.startDateTime().toString().latin1() );
     qWarning("%s", ev2.startDateTimeInZone().toString().latin1() );
+    qWarning("%d %d", ev.isAllDay(), ev2.isAllDay() );
 
     ODateBookAccess acc;
     if(!acc.load() ) qWarning("could not load");
@@ -45,6 +46,9 @@ int main(int argc, char* argv ) {
 	qWarning("Summary: %s", ef.description().latin1() );
 	qWarning("Date: %s", ef.date().toString().latin1() );
     }
+    ev.setUid( 1 );
+    acc.add( ev );
+    acc.save();
 
     return 0;
 }
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,6 +1,9 @@
 #include <errno.h>
 #include <fcntl.h>
 
+#include <stdio.h>
+#include <stdlib.h>
+
 #include <sys/types.h>
 #include <sys/mman.h>
 #include <sys/stat.h>
@@ -12,6 +15,7 @@
 
 #include <qtopia/global.h>
 #include <qtopia/stringutil.h>
+#include <qtopia/timeconversion.h>
 
 #include "opimnotifymanager.h"
 #include "orecur.h"
@@ -30,11 +34,11 @@ namespace {
     int alarmTime;
     int snd;
     enum Attribute{
-        	FDescription = 0,
-	FLocation,
-	FCategories,
-	FUid,
-	FType,
+        FDescription = 0,
+        FLocation,
+        FCategories,
+        FUid,
+        FType,
 	FAlarm,
 	FSound,
 	FRType,
@@ -46,8 +50,89 @@ namespace {
 	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& ,
@@ -66,9 +151,54 @@ bool ODateBookAccessBackend_XML::reload() {
     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 {
@@ -183,7 +313,7 @@ bool ODateBookAccessBackend_XML::loadFile() {
     ::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) );
@@ -202,6 +332,10 @@ bool ODateBookAccessBackend_XML::loadFile() {
     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;
@@ -289,10 +423,15 @@ void ODateBookAccessBackend_XML::finalizeRecord( OEvent& ev ) {
         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();
@@ -389,6 +528,29 @@ void ODateBookAccessBackend_XML::setField( OEvent& e, int id, const QString& val
     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,7 +1,10 @@
+#include <time.h>
+
 #include <qshared.h>
 
 #include <qtopia/timeconversion.h>
 
+#include "otimezone.h"
 #include "orecur.h"
 
 struct ORecur::Data : public QShared {
@@ -81,6 +84,14 @@ bool ORecur::doesRecur( const QDate& date ) {
 // 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;
@@ -95,7 +106,7 @@ bool ORecur::nextOcurrence( const QDate& from, QDate& next ) {
     if (hasEndDate() && endDate() < from)
 	return FALSE;
 
-    if (start() >= from) {
+    if (start() >= from  ) {
 	next = start();
 	return TRUE;
     }
@@ -443,4 +454,57 @@ void ORecur::checkOrModify() {
         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
@@ -73,7 +73,11 @@ public:
     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();
 
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
@@ -42,9 +42,11 @@ int OCalendarHelper::monthDiff( const QDate& first, const QDate& second ) {
 
 struct OEvent::Data : public QShared {
     Data() : QShared() {
+        child = 0;
         recur = 0;
         manager = 0;
         isAllDay = false;
+        parent = 0;
     }
     ~Data() {
         delete manager;
@@ -60,6 +62,8 @@ struct OEvent::Data : public QShared {
     QDateTime end;
     bool isAllDay : 1;
     QString timezone;
+    QArray<int>* child;
+    int parent;
 };
 
 OEvent::OEvent( int uid )
@@ -102,7 +106,7 @@ void OEvent::setLocation( const QString& 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
@@ -112,7 +116,13 @@ OPimNotifyManager &OEvent::notifiers() {
     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)
@@ -197,6 +207,7 @@ void OEvent::setTimeZone( const QString& tz ) {
     data->timezone = tz;
 }
 QString OEvent::timeZone()const {
+    if (data->isAllDay ) return QString::fromLatin1("UTC");
     return data->timezone;
 }
 bool OEvent::match( const QRegExp& )const {
@@ -239,6 +250,11 @@ void OEvent::changeOrModify() {
         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;
     }
@@ -256,8 +272,50 @@ QMap<int, QString> OEvent::toMap()const {
 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() {
     }
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
@@ -61,7 +61,7 @@ public:
     void setLocation( const QString& loc );
 
     bool hasNotifiers()const;
-    OPimNotifyManager &notifiers();
+    OPimNotifyManager &notifiers()const;
 
     ORecur recurrence()const;
     void setRecurrence( const ORecur& );
@@ -99,7 +99,15 @@ public:
 
     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 */
--
cgit v0.9.0.2