author | zecke <zecke> | 2002-10-07 09:10:05 (UTC) |
---|---|---|
committer | zecke <zecke> | 2002-10-07 09:10:05 (UTC) |
commit | acd2d0062dd87635feb03927663b1f700305d67b (patch) (side-by-side diff) | |
tree | 179217749ec7ba3217ca71f80a6e1064f249d1b0 | |
parent | de56dc7c5a21cbd70e9d1e66ab68ca95a277c45c (diff) | |
download | opie-acd2d0062dd87635feb03927663b1f700305d67b.zip opie-acd2d0062dd87635feb03927663b1f700305d67b.tar.gz opie-acd2d0062dd87635feb03927663b1f700305d67b.tar.bz2 |
First SQL backend Resource
The DB layout is not fully what we've in mind
but for example I do lack the Categories infrastructure
for that
I've to implement sorted lists and then I'll make
Todolist fast
-rw-r--r-- | libopie/pim/orecordlist.h | 3 | ||||
-rw-r--r-- | libopie/pim/otodoaccess.cpp | 4 | ||||
-rw-r--r-- | libopie/pim/otodoaccesssql.cpp | 397 | ||||
-rw-r--r-- | libopie/pim/otodoaccesssql.h | 46 | ||||
-rw-r--r-- | libopie/pim/otodoaccessxml.cpp | 1 | ||||
-rw-r--r-- | libopie2/opiepim/backend/otodoaccesssql.cpp | 397 | ||||
-rw-r--r-- | libopie2/opiepim/backend/otodoaccesssql.h | 46 | ||||
-rw-r--r-- | libopie2/opiepim/backend/otodoaccessxml.cpp | 1 | ||||
-rw-r--r-- | libopie2/opiepim/core/otodoaccess.cpp | 4 | ||||
-rw-r--r-- | libopie2/opiepim/orecordlist.h | 3 |
10 files changed, 894 insertions, 8 deletions
diff --git a/libopie/pim/orecordlist.h b/libopie/pim/orecordlist.h index 1fd0741..b6fa7fa 100644 --- a/libopie/pim/orecordlist.h +++ b/libopie/pim/orecordlist.h @@ -1,260 +1,259 @@ #ifndef OPIE_RECORD_LIST_H #define OPIE_RECORD_LIST_H #include <qarray.h> #include "otemplatebase.h" #include "opimrecord.h" /** * Our List Iterator * it behaves like STL or Qt * * for(it = list.begin(); it != list.end(); ++it ) * doSomeCoolStuff( (*it) ); */ template <class T> class ORecordList; template <class T = OPimRecord> class ORecordListIterator { friend class ORecordList<T>; public: typedef OTemplateBase<T> Base; /** * The c'tor used internally from * ORecordList */ ORecordListIterator( const QArray<int>, const Base* ); /** * The standard c'tor */ ORecordListIterator(); ~ORecordListIterator(); ORecordListIterator( const ORecordListIterator& ); ORecordListIterator &operator=(const ORecordListIterator& ); /** * a * operator ;) * use it like this T = (*it); */ T operator*(); ORecordListIterator &operator++(); ORecordListIterator &operator--(); bool operator==( const ORecordListIterator& it ); bool operator!=( const ORecordListIterator& it ); /** * the current item */ uint current()const; /** * the number of items */ uint count()const; /** * sets the current item */ void setCurrent( uint cur ); private: QArray<int> m_uids; uint m_current; const Base* m_temp; bool m_end : 1; T m_record; /* d pointer for future versions */ class IteratorPrivate; IteratorPrivate *d; }; /** * The recordlist used as a return type * from OPimAccessTemplate */ template <class T = OPimRecord > class ORecordList { public: typedef OTemplateBase<T> Base; typedef ORecordListIterator<T> Iterator; /** * c'tor */ ORecordList () { } ORecordList( const QArray<int>& ids, const Base* ); ~ORecordList(); /** * the first iterator */ Iterator begin(); /** * the end */ Iterator end(); /** * the number of items in the list */ uint count()const; T operator[]( uint i ); // FIXME implemenent remove /* ConstIterator begin()const; ConstIterator end()const; */ private: QArray<int> m_ids; const Base* m_acc; }; /* ok now implement it */ template <class T> ORecordListIterator<T>::ORecordListIterator() { m_current = 0; m_temp = 0l; m_end = true; m_record = T(); } template <class T> ORecordListIterator<T>::~ORecordListIterator() { /* nothing to delete */ } template <class T> ORecordListIterator<T>::ORecordListIterator( const ORecordListIterator<T>& it) { // qWarning("ORecordListIterator copy c'tor"); m_uids = it.m_uids; m_current = it.m_current; m_temp = it.m_temp; m_end = it.m_end; m_record = it.m_record; } template <class T> ORecordListIterator<T> &ORecordListIterator<T>::operator=( const ORecordListIterator<T>& it) { m_uids = it.m_uids; m_current = it.m_current; m_temp = it.m_temp; m_end = it.m_end; m_record = it.m_record; return *this; } template <class T> T ORecordListIterator<T>::operator*() { -// qWarning("operator* %d %d", m_current, m_uids[m_current] ); + qWarning("operator* %d %d", m_current, m_uids[m_current] ); if (!m_end ) /* FIXME * until the cache is in place * we do the uid match uid check */ - if(m_record.uid() != m_uids[m_current] ) m_record = m_temp->find( m_uids[m_current] ); else m_record = T(); return m_record; } template <class T> ORecordListIterator<T> &ORecordListIterator<T>::operator++() { if (m_current < m_uids.count() ) { m_end = false; ++m_current; }else m_end = true; return *this; } template <class T> ORecordListIterator<T> &ORecordListIterator<T>::operator--() { if ( m_current > 0 ) { --m_current; m_end = false; } else m_end = true; return *this; } template <class T> bool ORecordListIterator<T>::operator==( const ORecordListIterator<T>& it ) { /* if both are at we're the same.... */ if ( m_end == it.m_end ) return true; if ( m_uids != it.m_uids ) return false; if ( m_current != it.m_current ) return false; if ( m_temp != it.m_temp ) return false; return true; } template <class T> bool ORecordListIterator<T>::operator!=( const ORecordListIterator<T>& it ) { return !(*this == it ); } template <class T> ORecordListIterator<T>::ORecordListIterator( const QArray<int> uids, const Base* t ) : m_uids( uids ), m_current( 0 ), m_temp( t ), m_end( false ) { } template <class T> uint ORecordListIterator<T>::current()const { return m_current; } template <class T> void ORecordListIterator<T>::setCurrent( uint cur ) { if( cur < m_uids.count() ) { m_end = false; m_current= cur; } } template <class T> uint ORecordListIterator<T>::count()const { return m_uids.count(); } template <class T> ORecordList<T>::ORecordList( const QArray<int>& ids, const Base* acc ) : m_ids( ids ), m_acc( acc ) { } template <class T> ORecordList<T>::~ORecordList() { /* nothing to do here */ } template <class T> ORecordList<T>::Iterator ORecordList<T>::begin() { Iterator it( m_ids, m_acc ); return it; } template <class T> ORecordList<T>::Iterator ORecordList<T>::end() { Iterator it( m_ids, m_acc ); it.m_end = true; it.m_current = m_ids.count(); return it; } template <class T> uint ORecordList<T>::count()const { return m_ids.count(); } template <class T> T ORecordList<T>::operator[]( uint i ) { return m_acc->find( m_ids[i] ); } #endif diff --git a/libopie/pim/otodoaccess.cpp b/libopie/pim/otodoaccess.cpp index f51da29..5990841 100644 --- a/libopie/pim/otodoaccess.cpp +++ b/libopie/pim/otodoaccess.cpp @@ -1,77 +1,77 @@ #include <qdatetime.h> #include <qpe/alarmserver.h> -#include "otodoaccessxml.h" +#include "otodoaccesssql.h" #include "otodoaccess.h" OTodoAccess::OTodoAccess( OTodoAccessBackend* end ) : QObject(), OPimAccessTemplate<OTodo>( end ), m_todoBackEnd( end ) { if (end == 0l ) - m_todoBackEnd = new OTodoAccessXML( "Todolist" ); + m_todoBackEnd = new OTodoAccessBackendSQL( QString::null); setBackEnd( m_todoBackEnd ); } OTodoAccess::~OTodoAccess() { // qWarning("~OTodoAccess"); } void OTodoAccess::mergeWith( const QValueList<OTodo>& list ) { QValueList<OTodo>::ConstIterator it; for ( it = list.begin(); it != list.end(); ++it ) { replace( (*it) ); } } OTodoAccess::List OTodoAccess::effectiveToDos( const QDate& start, const QDate& end, bool includeNoDates ) { QArray<int> ints = m_todoBackEnd->effectiveToDos( start, end, includeNoDates ); List lis( ints, this ); return lis; } OTodoAccess::List OTodoAccess::effectiveToDos( const QDate& start, bool includeNoDates ) { return effectiveToDos( start, QDate::currentDate(), includeNoDates ); } OTodoAccess::List OTodoAccess::overDue() { List lis( m_todoBackEnd->overDue(), this ); return lis; } void OTodoAccess::addAlarm( const OTodo& event) { if (!event.hasAlarmDateTime() ) return; QDateTime now = QDateTime::currentDateTime(); QDateTime schedule = event.alarmDateTime(); if ( schedule > now ){ AlarmServer::addAlarm( schedule, "QPE/Application/todolist", "alarm(QDateTime,int)", event.uid() ); } } void OTodoAccess::delAlarm( int uid) { QDateTime schedule; // Create null DateTime // I hope this will remove all scheduled alarms // with the given uid !? // If not: I have to rethink how to remove already // scheduled events... (se) // it should be fine -zecke // qWarning("Removing alarm for event with uid %d", uid ); AlarmServer::deleteAlarm( schedule , "QPE/Application/todolist", "alarm(QDateTime,int)", uid ); } /* sort order */ OTodoAccess::List OTodoAccess::sorted( bool ascending, int sort,int filter, int cat ) { QArray<int> ints = m_todoBackEnd->sorted( ascending, sort, filter, cat ); OTodoAccess::List list( ints, this ); return list; } diff --git a/libopie/pim/otodoaccesssql.cpp b/libopie/pim/otodoaccesssql.cpp new file mode 100644 index 0000000..209e714 --- a/dev/null +++ b/libopie/pim/otodoaccesssql.cpp @@ -0,0 +1,397 @@ + +#include <qdatetime.h> + +#include <qpe/global.h> + +#include <opie/osqldriver.h> +#include <opie/osqlresult.h> +#include <opie/osqlmanager.h> +#include <opie/osqlquery.h> + +#include "otodoaccesssql.h" + +/* + * first some query + * CREATE query + * LOAD query + * INSERT + * REMOVE + * CLEAR + */ +namespace { + /** + * CreateQuery for the Todolist Table + */ + class CreateQuery : public OSQLQuery { + public: + CreateQuery(); + ~CreateQuery(); + QString query()const; + }; + + /** + * LoadQuery + * this one queries for all uids + */ + class LoadQuery : public OSQLQuery { + public: + LoadQuery(); + ~LoadQuery(); + QString query()const; + }; + + /** + * inserts/adds a OTodo to the table + */ + class InsertQuery : public OSQLQuery { + public: + InsertQuery(const OTodo& ); + ~InsertQuery(); + QString query()const; + private: + OTodo m_todo; + }; + + /** + * removes one from the table + */ + class RemoveQuery : public OSQLQuery { + public: + RemoveQuery(int uid ); + ~RemoveQuery(); + QString query()const; + private: + int m_uid; + }; + + /** + * Clears (delete) a Table + */ + class ClearQuery : public OSQLQuery { + public: + ClearQuery(); + ~ClearQuery(); + QString query()const; + + }; + + /** + * a find query + */ + class FindQuery : public OSQLQuery { + public: + FindQuery(int uid); + ~FindQuery(); + QString query()const; + private: + int m_uid; + }; + + /** + * overdue query + */ + class OverDueQuery : public OSQLQuery { + public: + OverDueQuery(); + ~OverDueQuery(); + QString query()const; + }; + class EffQuery : public OSQLQuery { + public: + EffQuery( const QDate&, const QDate&, bool inc ); + ~EffQuery(); + QString query()const; + private: + QString with()const; + QString out()const; + QDate m_start; + QDate m_end; + bool m_inc :1; + }; + + + CreateQuery::CreateQuery() : OSQLQuery() {} + CreateQuery::~CreateQuery() {} + QString CreateQuery::query()const { + QString qu; + qu += "create table todolist( uid, categories, completed, progress, "; + qu += "summary, DueDate, priority, description )"; + return qu; + } + + LoadQuery::LoadQuery() : OSQLQuery() {} + LoadQuery::~LoadQuery() {} + QString LoadQuery::query()const { + QString qu; + qu += "select distinct uid from todolist"; + + return qu; + } + + InsertQuery::InsertQuery( const OTodo& todo ) + : OSQLQuery(), m_todo( todo ) { + } + InsertQuery::~InsertQuery() { + } + /* + * converts from a OTodo to a query + * we leave out X-Ref + Alarms + */ + QString InsertQuery::query()const{ + int year, month, day; + year = month = day = 0; + if (m_todo.hasDueDate() ) { + QDate date = m_todo.dueDate(); + year = date.year(); + month = date.month(); + day = date.day(); + } + QString qu; + qu = "insert into todolist VALUES(" + QString::number( m_todo.uid() ) + ",'" + m_todo.idsToString( m_todo.categories() ) + "',"; + qu += QString::number( m_todo.isCompleted() ) + "," + QString::number( m_todo.progress() ) + ","; + qu += "'"+m_todo.summary()+"','"+QString::number(year)+"-"+QString::number(month)+"-"+QString::number(day)+"',"; + qu += QString::number(m_todo.priority() ) +",'" + m_todo.description() + "')"; + + qWarning("add %s", qu.latin1() ); + return qu; + } + + RemoveQuery::RemoveQuery(int uid ) + : OSQLQuery(), m_uid( uid ) {} + RemoveQuery::~RemoveQuery() {} + QString RemoveQuery::query()const { + QString qu = "DELETE from todolist where uid = " + QString::number(m_uid); + return qu; + } + + + ClearQuery::ClearQuery() + : OSQLQuery() {} + ClearQuery::~ClearQuery() {} + QString ClearQuery::query()const { + QString qu = "drop table todolist"; + return qu; + } + FindQuery::FindQuery(int uid) + : OSQLQuery(), m_uid(uid ) { + } + FindQuery::~FindQuery() { + } + QString FindQuery::query()const{ + QString qu = "select uid, categories, completed, progress, summary, "; + qu += "DueDate, priority, description from todolist where uid = " + QString::number(m_uid); + return qu; + } + + OverDueQuery::OverDueQuery(): OSQLQuery() {} + OverDueQuery::~OverDueQuery() {} + QString OverDueQuery::query()const { + QDate date = QDate::currentDate(); + QString str; + str = QString("select uid from todolist where DueDate ='%1-%2-%3'").arg(date.year() ).arg(date.month() ).arg(date.day() ); + + return str; + } + + + EffQuery::EffQuery( const QDate& start, const QDate& end, bool inc ) + : OSQLQuery(), m_start( start ), m_end( end ),m_inc(inc) {} + EffQuery::~EffQuery() {} + QString EffQuery::query()const { + return m_inc ? with() : out(); + } + QString EffQuery::with()const { + QString str; + str = QString("select uid from todolist where ( DueDate >= '%1-%2-%3' AND DueDate <= '%4-%5-%6' ) OR DueDate = '0-0-0' ") + .arg( m_start.year() ).arg( m_start.month() ).arg( m_start.day() ) + .arg( m_end .year() ).arg( m_end .month() ).arg( m_end .day() ); + return str; + } + QString EffQuery::out()const { + QString str; + str = QString("select uid from todolist where DueDate >= '%1-%2-%3' AND DueDate <= '%4-%5-%6'") + .arg(m_start.year() ).arg(m_start.month() ).arg( m_start.day() ) + .arg(m_end. year() ).arg(m_end. month() ).arg(m_end.day() ); + + return str; + } +}; + +OTodoAccessBackendSQL::OTodoAccessBackendSQL( const QString& file ) + : OTodoAccessBackend(), m_dict(15) +{ + QString fi = file; + if ( fi.isEmpty() ) + fi = Global::applicationFileName( "todolist", "todolist.db" ); + OSQLManager man; + m_driver = man.standard(); + m_driver->setUrl(fi); + fillDict(); +} + +OTodoAccessBackendSQL::~OTodoAccessBackendSQL(){ +} +bool OTodoAccessBackendSQL::load(){ + if (!m_driver->open() ) + return false; + + CreateQuery creat; + OSQLResult res = m_driver->query(&creat ); + + update(); + qWarning("loaded %d", m_uids.count() ); + return true; +} +bool OTodoAccessBackendSQL::reload(){ + return load(); +} + +bool OTodoAccessBackendSQL::save(){ + return m_driver->close(); +} +QArray<int> OTodoAccessBackendSQL::allRecords()const { + return m_uids; +} +QArray<int> OTodoAccessBackendSQL::queryByExample( const OTodo& , int ){ + QArray<int> ints(0); + return ints; +} +OTodo OTodoAccessBackendSQL::find(int uid ) const{ + FindQuery query( uid ); + return todo( m_driver->query(&query) ); + +} +void OTodoAccessBackendSQL::clear() { + ClearQuery cle; + OSQLResult res = m_driver->query( &cle ); + CreateQuery qu; + res = m_driver->query(&qu); +} +bool OTodoAccessBackendSQL::add( const OTodo& t) { + InsertQuery ins( t ); + OSQLResult res = m_driver->query( &ins ); + + if ( res.state() == OSQLResult::Failure ) + return false; + int c = m_uids.count(); + m_uids.resize( c+1 ); + m_uids[c] = t.uid(); + + return true; +} +bool OTodoAccessBackendSQL::remove( int uid ) { + RemoveQuery rem( uid ); + OSQLResult res = m_driver->query(&rem ); + + if ( res.state() == OSQLResult::Failure ) + return false; + + update(); + return true; +} +/* + * FIXME better set query + * but we need the cache for that + * now we remove + */ +bool OTodoAccessBackendSQL::replace( const OTodo& t) { + remove( t.uid() ); + return add(t); +} +QArray<int> OTodoAccessBackendSQL::overDue() { + OverDueQuery qu; + return uids( m_driver->query(&qu ) ); +} +QArray<int> OTodoAccessBackendSQL::effectiveToDos( const QDate& s, + const QDate& t, + bool u) { + EffQuery ef(s, t, u ); + return uids (m_driver->query(&ef) ); +} +QArray<int> OTodoAccessBackendSQL::sorted( bool asc, int sortOrder, + int sortFilter, int cat ) { + QArray<int> ints(0); + return ints; +} +bool OTodoAccessBackendSQL::date( QDate& da, const QString& str ) const{ + if ( str == "0-0-0" ) + return false; + else{ + int day, year, month; + QStringList list = QStringList::split("-", str ); + year = list[0].toInt(); + month = list[1].toInt(); + day = list[2].toInt(); + da.setYMD( year, month, day ); + return true; + } +} +OTodo OTodoAccessBackendSQL::todo( const OSQLResult& res) const{ + if ( res.state() == OSQLResult::Failure ) { + OTodo to; + return to; + } + + OSQLResultItem::ValueList list = res.results(); + OSQLResultItem::ValueList::Iterator it = list.begin(); + + bool has = false; QDate da = QDate::currentDate(); + has = date( da, (*it).data("DueDate") ); + QStringList cats = QStringList::split(";", (*it).data("categories") ); + + OTodo to( (bool)(*it).data("completed").toInt(), (*it).data("priority").toInt(), + cats, (*it).data("summary"), (*it).data("description"), + (*it).data("progress").toUShort(), has, da, (*it).data("uid").toInt() ); + return to; +} +OTodo OTodoAccessBackendSQL::todo( int uid )const { + FindQuery find( uid ); + return todo( m_driver->query(&find) ); +} +/* + * update the dict + */ +void OTodoAccessBackendSQL::fillDict() { + /* initialize dict */ + /* + * UPDATE dict if you change anything!!! + */ + m_dict.setAutoDelete( TRUE ); + m_dict.insert("Categories" , new int(OTodo::Category) ); + m_dict.insert("Uid" , new int(OTodo::Uid) ); + m_dict.insert("HasDate" , new int(OTodo::HasDate) ); + m_dict.insert("Completed" , new int(OTodo::Completed) ); + m_dict.insert("Description" , new int(OTodo::Description) ); + m_dict.insert("Summary" , new int(OTodo::Summary) ); + m_dict.insert("Priority" , new int(OTodo::Priority) ); + m_dict.insert("DateDay" , new int(OTodo::DateDay) ); + m_dict.insert("DateMonth" , new int(OTodo::DateMonth) ); + m_dict.insert("DateYear" , new int(OTodo::DateYear) ); + m_dict.insert("Progress" , new int(OTodo::Progress) ); + m_dict.insert("Completed", new int(OTodo::Completed) ); + m_dict.insert("CrossReference", new int(OTodo::CrossReference) ); + m_dict.insert("HasAlarmDateTime",new int(OTodo::HasAlarmDateTime) ); + m_dict.insert("AlarmDateTime", new int(OTodo::AlarmDateTime) ); +} +void OTodoAccessBackendSQL::update() { + LoadQuery lo; + OSQLResult res = m_driver->query(&lo); + if ( res.state() != OSQLResult::Success ) + return; + + m_uids = uids( res ); +} +QArray<int> OTodoAccessBackendSQL::uids( const OSQLResult& res) const{ + + OSQLResultItem::ValueList list = res.results(); + OSQLResultItem::ValueList::Iterator it; + QArray<int> ints(list.count() ); + + int i = 0; + for (it = list.begin(); it != list.end(); ++it ) { + ints[i] = (*it).data("uid").toInt(); + i++; + } + return ints; +} + diff --git a/libopie/pim/otodoaccesssql.h b/libopie/pim/otodoaccesssql.h new file mode 100644 index 0000000..966628d --- a/dev/null +++ b/libopie/pim/otodoaccesssql.h @@ -0,0 +1,46 @@ +#ifndef OPIE_PIM_ACCESS_SQL_H +#define OPIE_PIM_ACCESS_SQL_H + +#include <qasciidict.h> + +#include "otodoaccessbackend.h" + +class OSQLDriver; +class OSQLResult; +class OTodoAccessBackendSQL : public OTodoAccessBackend { +public: + OTodoAccessBackendSQL( const QString& file ); + ~OTodoAccessBackendSQL(); + + bool load(); + bool reload(); + bool save(); + QArray<int> allRecords()const; + + QArray<int> queryByExample( const OTodo& t, int sort ); + OTodo find(int uid)const; + void clear(); + bool add( const OTodo& t ); + bool remove( int uid ); + bool replace( const OTodo& t ); + + QArray<int> overDue(); + QArray<int> effectiveToDos( const QDate& start, + const QDate& end, bool includeNoDates ); + QArray<int> sorted(bool asc, int sortOrder, int sortFilter, int cat ); + +private: + void update(); + void fillDict(); + bool date( QDate& date, const QString& )const; + OTodo todo( const OSQLResult& )const; + QArray<int> uids( const OSQLResult& )const; + OTodo todo( int uid )const; + + QAsciiDict<int> m_dict; + OSQLDriver* m_driver; + QArray<int> m_uids; +}; + + +#endif diff --git a/libopie/pim/otodoaccessxml.cpp b/libopie/pim/otodoaccessxml.cpp index 31822d4..80b8599 100644 --- a/libopie/pim/otodoaccessxml.cpp +++ b/libopie/pim/otodoaccessxml.cpp @@ -1,547 +1,548 @@ #include <qfile.h> #include <qvector.h> #include <qpe/global.h> #include <qpe/stringutil.h> #include <qpe/timeconversion.h> #include <opie/xmltree.h> #include "otodoaccessxml.h" OTodoAccessXML::OTodoAccessXML( const QString& appName, const QString& fileName ) : OTodoAccessBackend(), m_app( appName ), m_opened( false ), m_changed( false ) { if (!fileName.isEmpty() ) m_file = fileName; else m_file = Global::applicationFileName( "todolist", "todolist.xml" ); } OTodoAccessXML::~OTodoAccessXML() { } bool OTodoAccessXML::load() { m_opened = true; m_changed = false; /* initialize dict */ /* * UPDATE dict if you change anything!!! */ QAsciiDict<int> dict(15); dict.setAutoDelete( TRUE ); dict.insert("Categories" , new int(OTodo::Category) ); dict.insert("Uid" , new int(OTodo::Uid) ); dict.insert("HasDate" , new int(OTodo::HasDate) ); dict.insert("Completed" , new int(OTodo::Completed) ); dict.insert("Description" , new int(OTodo::Description) ); dict.insert("Summary" , new int(OTodo::Summary) ); dict.insert("Priority" , new int(OTodo::Priority) ); dict.insert("DateDay" , new int(OTodo::DateDay) ); dict.insert("DateMonth" , new int(OTodo::DateMonth) ); dict.insert("DateYear" , new int(OTodo::DateYear) ); dict.insert("Progress" , new int(OTodo::Progress) ); dict.insert("Completed", new int(OTodo::Completed) ); dict.insert("CrossReference", new int(OTodo::CrossReference) ); dict.insert("HasAlarmDateTime",new int(OTodo::HasAlarmDateTime) ); dict.insert("AlarmDateTime", new int(OTodo::AlarmDateTime) ); // here the custom XML parser from TT it's GPL // but we want to push that to TT..... QFile f(m_file ); if (!f.open(IO_ReadOnly) ) return false; QByteArray ba = f.readAll(); f.close(); char* dt = ba.data(); int len = ba.size(); int i = 0; char *point; const char* collectionString = "<Task "; while ( dt+i != 0 && ( point = strstr( dt+i, collectionString ) ) != 0l ) { i = point -dt; i+= strlen(collectionString); OTodo ev; m_year = m_month = m_day = 0; while ( TRUE ) { 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; QCString attr( dt+i, j-i+1); i = ++j; // skip = // find the start of quotes while ( i < len && dt[i] != '"' ) ++i; j = ++i; bool haveUtf = FALSE; bool haveEnt = FALSE; while ( j < len && dt[j] != '"' ) { if ( ((unsigned char)dt[j]) > 0x7f ) haveUtf = TRUE; if ( dt[j] == '&' ) haveEnt = TRUE; ++j; } if ( i == j ) { // empty value i = j + 1; continue; } QCString value( dt+i, j-i+1 ); i = j + 1; QString str = (haveUtf ? QString::fromUtf8( value ) : QString::fromLatin1( value ) ); if ( haveEnt ) str = Qtopia::plainString( str ); /* * add key + value */ todo( &dict, ev, attr, str ); } /* * now add it */ m_events.insert(ev.uid(), ev ); } + qWarning("counts %d", m_events.count() ); return true; } bool OTodoAccessXML::reload() { return load(); } bool OTodoAccessXML::save() { // qWarning("saving"); if (!m_opened || !m_changed ) { // qWarning("not saving"); return true; } QString strNewFile = m_file + ".new"; QFile f( strNewFile ); if (!f.open( IO_WriteOnly|IO_Raw ) ) return false; int written; QString out; out = "<!DOCTYPE Tasks>\n<Tasks>\n"; // for all todos QMap<int, OTodo>::Iterator it; for (it = m_events.begin(); it != m_events.end(); ++it ) { out+= "<Task " + toString( (*it) ) + " />\n"; QCString cstr = out.utf8(); written = f.writeBlock( cstr.data(), cstr.length() ); /* less written then we wanted */ if ( written != (int)cstr.length() ) { f.close(); QFile::remove( strNewFile ); return false; } out = QString::null; } out += "</Tasks>"; QCString cstr = out.utf8(); written = f.writeBlock( cstr.data(), cstr.length() ); if ( written != (int)cstr.length() ) { f.close(); QFile::remove( strNewFile ); return false; } /* flush before renaming */ f.close(); if( ::rename( strNewFile.latin1(), m_file.latin1() ) < 0 ) { // qWarning("error renaming"); QFile::remove( strNewFile ); } m_changed = false; return true; } QArray<int> OTodoAccessXML::allRecords()const { QArray<int> ids( m_events.count() ); QMap<int, OTodo>::ConstIterator it; int i = 0; for ( it = m_events.begin(); it != m_events.end(); ++it ) { ids[i] = it.key(); i++; } return ids; } QArray<int> OTodoAccessXML::queryByExample( const OTodo&, int sort ) { QArray<int> ids(0); return ids; } OTodo OTodoAccessXML::find( int uid )const { OTodo todo; todo.setUid( 0 ); // isEmpty() QMap<int, OTodo>::ConstIterator it = m_events.find( uid ); if ( it != m_events.end() ) todo = it.data(); return todo; } void OTodoAccessXML::clear() { if (m_opened ) m_changed = true; m_events.clear(); } bool OTodoAccessXML::add( const OTodo& todo ) { // qWarning("add"); m_changed = true; m_events.insert( todo.uid(), todo ); return true; } bool OTodoAccessXML::remove( int uid ) { m_changed = true; m_events.remove( uid ); return true; } bool OTodoAccessXML::replace( const OTodo& todo) { m_changed = true; m_events.replace( todo.uid(), todo ); return true; } QArray<int> OTodoAccessXML::effectiveToDos( const QDate& start, const QDate& end, bool includeNoDates ) { QArray<int> ids( m_events.count() ); QMap<int, OTodo>::Iterator it; int i = 0; for ( it = m_events.begin(); it != m_events.end(); ++it ) { if ( !it.data().hasDueDate() ) { if ( includeNoDates ) { ids[i] = it.key(); i++; } }else if ( it.data().dueDate() >= start && it.data().dueDate() <= end ) { ids[i] = it.key(); i++; } } ids.resize( i ); return ids; } QArray<int> OTodoAccessXML::overDue() { QArray<int> ids( m_events.count() ); int i = 0; QMap<int, OTodo>::Iterator it; for ( it = m_events.begin(); it != m_events.end(); ++it ) { if ( it.data().isOverdue() ) { ids[i] = it.key(); i++; } } ids.resize( i ); return ids; } /* private */ void OTodoAccessXML::todo( QAsciiDict<int>* dict, OTodo& ev, const QCString& attr, const QString& val) { // qWarning("parse to do from XMLElement" ); int *find=0; find = (*dict)[ attr.data() ]; if (!find ) { // qWarning("Unknown option" + it.key() ); ev.setCustomField( attr, val ); return; } switch( *find ) { case OTodo::Uid: ev.setUid( val.toInt() ); break; case OTodo::Category: ev.setCategories( ev.idsFromString( val ) ); break; case OTodo::HasDate: ev.setHasDueDate( val.toInt() ); break; case OTodo::Completed: ev.setCompleted( val.toInt() ); break; case OTodo::Description: ev.setDescription( val ); break; case OTodo::Summary: ev.setSummary( val ); break; case OTodo::Priority: ev.setPriority( val.toInt() ); break; case OTodo::DateDay: m_day = val.toInt(); break; case OTodo::DateMonth: m_month = val.toInt(); break; case OTodo::DateYear: m_year = val.toInt(); break; case OTodo::Progress: ev.setProgress( val.toInt() ); break; case OTodo::CrossReference: { /* * A cross refernce looks like * appname,id;appname,id * we need to split it up */ QStringList refs = QStringList::split(';', val ); QStringList::Iterator strIt; for (strIt = refs.begin(); strIt != refs.end(); ++strIt ) { int pos = (*strIt).find(','); if ( pos > -1 ) ev.addRelation( (*strIt).left(pos), (*strIt).mid(pos+1).toInt() ); } break; } case OTodo::HasAlarmDateTime: ev.setHasAlarmDateTime( val.toInt() ); break; case OTodo::AlarmDateTime: { /* this sounds better ;) zecke */ ev.setAlarmDateTime( TimeConversion::fromISO8601( val.local8Bit() ) ); break; } default: break; } if ( ev.hasDueDate() ) { QDate date( m_year, m_month, m_day ); ev.setDueDate( date ); } } QString OTodoAccessXML::toString( const OTodo& ev )const { QString str; str += "Completed=\"" + QString::number( ev.isCompleted() ) + "\" "; str += "HasDate=\"" + QString::number( ev.hasDueDate() ) + "\" "; str += "Priority=\"" + QString::number( ev.priority() ) + "\" "; str += "Progress=\"" + QString::number(ev.progress() ) + "\" "; str += "Categories=\"" + toString( ev.categories() ) + "\" "; str += "Description=\"" + Qtopia::escapeString( ev.description() ) + "\" "; str += "Summary=\"" + Qtopia::escapeString( ev.summary() ) + "\" "; if ( ev.hasDueDate() ) { str += "DateYear=\"" + QString::number( ev.dueDate().year() ) + "\" "; str += "DateMonth=\"" + QString::number( ev.dueDate().month() ) + "\" "; str += "DateDay=\"" + QString::number( ev.dueDate().day() ) + "\" "; } // qWarning( "Uid %d", ev.uid() ); str += "Uid=\"" + QString::number( ev.uid() ) + "\" "; // append the extra options /* FIXME Qtopia::Record this is currently not * possible you can set custom fields * but don' iterate over the list * I may do #define private protected * for this case - cough --zecke */ /* QMap<QString, QString> extras = ev.extras(); QMap<QString, QString>::Iterator extIt; for (extIt = extras.begin(); extIt != extras.end(); ++extIt ) str += extIt.key() + "=\"" + extIt.data() + "\" "; */ // cross refernce QStringList list = ev.relatedApps(); QStringList::Iterator listIt; QString refs; str += "CrossReference=\""; bool added = false; for ( listIt = list.begin(); listIt != list.end(); ++listIt ) { added = true; QArray<int> ints = ev.relations( (*listIt) ); for ( uint i = 0; i< ints.count(); i++ ) { str += (*listIt) + "," + QString::number( i ) + ";"; } } if ( added ) str = str.remove( str.length()-1, 1 ); str += "\" "; str += "AlarmDateTime=\"" + TimeConversion::toISO8601( ev.alarmDateTime() ) + "\" "; return str; } QString OTodoAccessXML::toString( const QArray<int>& ints ) const { return Qtopia::Record::idsToString( ints ); } /* internal class for sorting */ struct OTodoXMLContainer { OTodo todo; }; /* * Returns: * 0 if item1 == item2 * * non-zero if item1 != item2 * * This function returns int rather than bool so that reimplementations * can return one of three values and use it to sort by: * * 0 if item1 == item2 * * > 0 (positive integer) if item1 > item2 * * < 0 (negative integer) if item1 < item2 * */ class OTodoXMLVector : public QVector<OTodoXMLContainer> { public: OTodoXMLVector(int size, bool asc, int sort) : QVector<OTodoXMLContainer>( size ) { setAutoDelete( true ); m_asc = asc; m_sort = sort; } /* return the summary/description */ QString string( const OTodo& todo) { return todo.summary().isEmpty() ? todo.description().left(20 ) : todo.summary(); } /** * we take the sortorder( switch on it ) * */ int compareItems( Item d1, Item d2 ) { qWarning("compare items"); int ret =0; OTodoXMLContainer* con1 = (OTodoXMLContainer*)d1; OTodoXMLContainer* con2 = (OTodoXMLContainer*)d2; /* same item */ if ( con1->todo.uid() == con2->todo.uid() ) return 0; qWarning("m_sort %d", m_sort ); switch ( m_sort ) { /* completed */ case 0: { ret = 0; if ( con1->todo.isCompleted() ) ret++; if ( con2->todo.isCompleted() ) ret--; break; } /* priority */ case 1: { ret = con1->todo.priority() - con2->todo.priority(); qWarning(" priority %d %d %d", ret, con1->todo.priority(), con2->todo.priority() ); break; } /* description */ case 2: { QString str1 = string( con1->todo ); QString str2 = string( con2->todo ); ret = QString::compare( str1, str2 ); break; } /* deadline */ case 3: { /* either bot got a dueDate * or one of them got one */ if ( con1->todo.hasDueDate() && con2->todo.hasDueDate() ) ret = con1->todo.dueDate().daysTo( con2->todo.dueDate() ); else if ( con1->todo.hasDueDate() ) ret = -1; else if ( con2->todo.hasDueDate() ) ret = 0; break; } default: ret = 0; break; }; /* twist it we're not ascending*/ if (!m_asc) ret = ret * -1; return ret; } private: bool m_asc; int m_sort; }; QArray<int> OTodoAccessXML::sorted( bool asc, int sortOrder, int sortFilter, int cat ) { OTodoXMLVector vector(m_events.count(), asc,sortOrder ); QMap<int, OTodo>::Iterator it; int item = 0; bool bCat = sortFilter & 1 ? true : false; bool bOver = sortFilter & 0 ? true : false; bool bOnly = sortFilter & 2 ? true : false; for ( it = m_events.begin(); it != m_events.end(); ++it ) { /* show category */ if ( bCat ) if (!(*it).categories().contains( cat ) ) continue; /* isOverdue but we should not show overdue */ if ( (*it).isOverdue() && ( !bOver || !bOnly ) ) continue; if ( !(*it).isOverdue() && bOnly ) continue; OTodoXMLContainer* con = new OTodoXMLContainer(); con->todo = (*it); vector.insert(item, con ); item++; } vector.resize( item ); /* sort it now */ vector.sort(); /* now get the uids */ QArray<int> array( vector.count() ); for (uint i= 0; i < vector.count(); i++ ) { array[i] = ( vector.at(i) )->todo.uid(); } return array; }; diff --git a/libopie2/opiepim/backend/otodoaccesssql.cpp b/libopie2/opiepim/backend/otodoaccesssql.cpp new file mode 100644 index 0000000..209e714 --- a/dev/null +++ b/libopie2/opiepim/backend/otodoaccesssql.cpp @@ -0,0 +1,397 @@ + +#include <qdatetime.h> + +#include <qpe/global.h> + +#include <opie/osqldriver.h> +#include <opie/osqlresult.h> +#include <opie/osqlmanager.h> +#include <opie/osqlquery.h> + +#include "otodoaccesssql.h" + +/* + * first some query + * CREATE query + * LOAD query + * INSERT + * REMOVE + * CLEAR + */ +namespace { + /** + * CreateQuery for the Todolist Table + */ + class CreateQuery : public OSQLQuery { + public: + CreateQuery(); + ~CreateQuery(); + QString query()const; + }; + + /** + * LoadQuery + * this one queries for all uids + */ + class LoadQuery : public OSQLQuery { + public: + LoadQuery(); + ~LoadQuery(); + QString query()const; + }; + + /** + * inserts/adds a OTodo to the table + */ + class InsertQuery : public OSQLQuery { + public: + InsertQuery(const OTodo& ); + ~InsertQuery(); + QString query()const; + private: + OTodo m_todo; + }; + + /** + * removes one from the table + */ + class RemoveQuery : public OSQLQuery { + public: + RemoveQuery(int uid ); + ~RemoveQuery(); + QString query()const; + private: + int m_uid; + }; + + /** + * Clears (delete) a Table + */ + class ClearQuery : public OSQLQuery { + public: + ClearQuery(); + ~ClearQuery(); + QString query()const; + + }; + + /** + * a find query + */ + class FindQuery : public OSQLQuery { + public: + FindQuery(int uid); + ~FindQuery(); + QString query()const; + private: + int m_uid; + }; + + /** + * overdue query + */ + class OverDueQuery : public OSQLQuery { + public: + OverDueQuery(); + ~OverDueQuery(); + QString query()const; + }; + class EffQuery : public OSQLQuery { + public: + EffQuery( const QDate&, const QDate&, bool inc ); + ~EffQuery(); + QString query()const; + private: + QString with()const; + QString out()const; + QDate m_start; + QDate m_end; + bool m_inc :1; + }; + + + CreateQuery::CreateQuery() : OSQLQuery() {} + CreateQuery::~CreateQuery() {} + QString CreateQuery::query()const { + QString qu; + qu += "create table todolist( uid, categories, completed, progress, "; + qu += "summary, DueDate, priority, description )"; + return qu; + } + + LoadQuery::LoadQuery() : OSQLQuery() {} + LoadQuery::~LoadQuery() {} + QString LoadQuery::query()const { + QString qu; + qu += "select distinct uid from todolist"; + + return qu; + } + + InsertQuery::InsertQuery( const OTodo& todo ) + : OSQLQuery(), m_todo( todo ) { + } + InsertQuery::~InsertQuery() { + } + /* + * converts from a OTodo to a query + * we leave out X-Ref + Alarms + */ + QString InsertQuery::query()const{ + int year, month, day; + year = month = day = 0; + if (m_todo.hasDueDate() ) { + QDate date = m_todo.dueDate(); + year = date.year(); + month = date.month(); + day = date.day(); + } + QString qu; + qu = "insert into todolist VALUES(" + QString::number( m_todo.uid() ) + ",'" + m_todo.idsToString( m_todo.categories() ) + "',"; + qu += QString::number( m_todo.isCompleted() ) + "," + QString::number( m_todo.progress() ) + ","; + qu += "'"+m_todo.summary()+"','"+QString::number(year)+"-"+QString::number(month)+"-"+QString::number(day)+"',"; + qu += QString::number(m_todo.priority() ) +",'" + m_todo.description() + "')"; + + qWarning("add %s", qu.latin1() ); + return qu; + } + + RemoveQuery::RemoveQuery(int uid ) + : OSQLQuery(), m_uid( uid ) {} + RemoveQuery::~RemoveQuery() {} + QString RemoveQuery::query()const { + QString qu = "DELETE from todolist where uid = " + QString::number(m_uid); + return qu; + } + + + ClearQuery::ClearQuery() + : OSQLQuery() {} + ClearQuery::~ClearQuery() {} + QString ClearQuery::query()const { + QString qu = "drop table todolist"; + return qu; + } + FindQuery::FindQuery(int uid) + : OSQLQuery(), m_uid(uid ) { + } + FindQuery::~FindQuery() { + } + QString FindQuery::query()const{ + QString qu = "select uid, categories, completed, progress, summary, "; + qu += "DueDate, priority, description from todolist where uid = " + QString::number(m_uid); + return qu; + } + + OverDueQuery::OverDueQuery(): OSQLQuery() {} + OverDueQuery::~OverDueQuery() {} + QString OverDueQuery::query()const { + QDate date = QDate::currentDate(); + QString str; + str = QString("select uid from todolist where DueDate ='%1-%2-%3'").arg(date.year() ).arg(date.month() ).arg(date.day() ); + + return str; + } + + + EffQuery::EffQuery( const QDate& start, const QDate& end, bool inc ) + : OSQLQuery(), m_start( start ), m_end( end ),m_inc(inc) {} + EffQuery::~EffQuery() {} + QString EffQuery::query()const { + return m_inc ? with() : out(); + } + QString EffQuery::with()const { + QString str; + str = QString("select uid from todolist where ( DueDate >= '%1-%2-%3' AND DueDate <= '%4-%5-%6' ) OR DueDate = '0-0-0' ") + .arg( m_start.year() ).arg( m_start.month() ).arg( m_start.day() ) + .arg( m_end .year() ).arg( m_end .month() ).arg( m_end .day() ); + return str; + } + QString EffQuery::out()const { + QString str; + str = QString("select uid from todolist where DueDate >= '%1-%2-%3' AND DueDate <= '%4-%5-%6'") + .arg(m_start.year() ).arg(m_start.month() ).arg( m_start.day() ) + .arg(m_end. year() ).arg(m_end. month() ).arg(m_end.day() ); + + return str; + } +}; + +OTodoAccessBackendSQL::OTodoAccessBackendSQL( const QString& file ) + : OTodoAccessBackend(), m_dict(15) +{ + QString fi = file; + if ( fi.isEmpty() ) + fi = Global::applicationFileName( "todolist", "todolist.db" ); + OSQLManager man; + m_driver = man.standard(); + m_driver->setUrl(fi); + fillDict(); +} + +OTodoAccessBackendSQL::~OTodoAccessBackendSQL(){ +} +bool OTodoAccessBackendSQL::load(){ + if (!m_driver->open() ) + return false; + + CreateQuery creat; + OSQLResult res = m_driver->query(&creat ); + + update(); + qWarning("loaded %d", m_uids.count() ); + return true; +} +bool OTodoAccessBackendSQL::reload(){ + return load(); +} + +bool OTodoAccessBackendSQL::save(){ + return m_driver->close(); +} +QArray<int> OTodoAccessBackendSQL::allRecords()const { + return m_uids; +} +QArray<int> OTodoAccessBackendSQL::queryByExample( const OTodo& , int ){ + QArray<int> ints(0); + return ints; +} +OTodo OTodoAccessBackendSQL::find(int uid ) const{ + FindQuery query( uid ); + return todo( m_driver->query(&query) ); + +} +void OTodoAccessBackendSQL::clear() { + ClearQuery cle; + OSQLResult res = m_driver->query( &cle ); + CreateQuery qu; + res = m_driver->query(&qu); +} +bool OTodoAccessBackendSQL::add( const OTodo& t) { + InsertQuery ins( t ); + OSQLResult res = m_driver->query( &ins ); + + if ( res.state() == OSQLResult::Failure ) + return false; + int c = m_uids.count(); + m_uids.resize( c+1 ); + m_uids[c] = t.uid(); + + return true; +} +bool OTodoAccessBackendSQL::remove( int uid ) { + RemoveQuery rem( uid ); + OSQLResult res = m_driver->query(&rem ); + + if ( res.state() == OSQLResult::Failure ) + return false; + + update(); + return true; +} +/* + * FIXME better set query + * but we need the cache for that + * now we remove + */ +bool OTodoAccessBackendSQL::replace( const OTodo& t) { + remove( t.uid() ); + return add(t); +} +QArray<int> OTodoAccessBackendSQL::overDue() { + OverDueQuery qu; + return uids( m_driver->query(&qu ) ); +} +QArray<int> OTodoAccessBackendSQL::effectiveToDos( const QDate& s, + const QDate& t, + bool u) { + EffQuery ef(s, t, u ); + return uids (m_driver->query(&ef) ); +} +QArray<int> OTodoAccessBackendSQL::sorted( bool asc, int sortOrder, + int sortFilter, int cat ) { + QArray<int> ints(0); + return ints; +} +bool OTodoAccessBackendSQL::date( QDate& da, const QString& str ) const{ + if ( str == "0-0-0" ) + return false; + else{ + int day, year, month; + QStringList list = QStringList::split("-", str ); + year = list[0].toInt(); + month = list[1].toInt(); + day = list[2].toInt(); + da.setYMD( year, month, day ); + return true; + } +} +OTodo OTodoAccessBackendSQL::todo( const OSQLResult& res) const{ + if ( res.state() == OSQLResult::Failure ) { + OTodo to; + return to; + } + + OSQLResultItem::ValueList list = res.results(); + OSQLResultItem::ValueList::Iterator it = list.begin(); + + bool has = false; QDate da = QDate::currentDate(); + has = date( da, (*it).data("DueDate") ); + QStringList cats = QStringList::split(";", (*it).data("categories") ); + + OTodo to( (bool)(*it).data("completed").toInt(), (*it).data("priority").toInt(), + cats, (*it).data("summary"), (*it).data("description"), + (*it).data("progress").toUShort(), has, da, (*it).data("uid").toInt() ); + return to; +} +OTodo OTodoAccessBackendSQL::todo( int uid )const { + FindQuery find( uid ); + return todo( m_driver->query(&find) ); +} +/* + * update the dict + */ +void OTodoAccessBackendSQL::fillDict() { + /* initialize dict */ + /* + * UPDATE dict if you change anything!!! + */ + m_dict.setAutoDelete( TRUE ); + m_dict.insert("Categories" , new int(OTodo::Category) ); + m_dict.insert("Uid" , new int(OTodo::Uid) ); + m_dict.insert("HasDate" , new int(OTodo::HasDate) ); + m_dict.insert("Completed" , new int(OTodo::Completed) ); + m_dict.insert("Description" , new int(OTodo::Description) ); + m_dict.insert("Summary" , new int(OTodo::Summary) ); + m_dict.insert("Priority" , new int(OTodo::Priority) ); + m_dict.insert("DateDay" , new int(OTodo::DateDay) ); + m_dict.insert("DateMonth" , new int(OTodo::DateMonth) ); + m_dict.insert("DateYear" , new int(OTodo::DateYear) ); + m_dict.insert("Progress" , new int(OTodo::Progress) ); + m_dict.insert("Completed", new int(OTodo::Completed) ); + m_dict.insert("CrossReference", new int(OTodo::CrossReference) ); + m_dict.insert("HasAlarmDateTime",new int(OTodo::HasAlarmDateTime) ); + m_dict.insert("AlarmDateTime", new int(OTodo::AlarmDateTime) ); +} +void OTodoAccessBackendSQL::update() { + LoadQuery lo; + OSQLResult res = m_driver->query(&lo); + if ( res.state() != OSQLResult::Success ) + return; + + m_uids = uids( res ); +} +QArray<int> OTodoAccessBackendSQL::uids( const OSQLResult& res) const{ + + OSQLResultItem::ValueList list = res.results(); + OSQLResultItem::ValueList::Iterator it; + QArray<int> ints(list.count() ); + + int i = 0; + for (it = list.begin(); it != list.end(); ++it ) { + ints[i] = (*it).data("uid").toInt(); + i++; + } + return ints; +} + diff --git a/libopie2/opiepim/backend/otodoaccesssql.h b/libopie2/opiepim/backend/otodoaccesssql.h new file mode 100644 index 0000000..966628d --- a/dev/null +++ b/libopie2/opiepim/backend/otodoaccesssql.h @@ -0,0 +1,46 @@ +#ifndef OPIE_PIM_ACCESS_SQL_H +#define OPIE_PIM_ACCESS_SQL_H + +#include <qasciidict.h> + +#include "otodoaccessbackend.h" + +class OSQLDriver; +class OSQLResult; +class OTodoAccessBackendSQL : public OTodoAccessBackend { +public: + OTodoAccessBackendSQL( const QString& file ); + ~OTodoAccessBackendSQL(); + + bool load(); + bool reload(); + bool save(); + QArray<int> allRecords()const; + + QArray<int> queryByExample( const OTodo& t, int sort ); + OTodo find(int uid)const; + void clear(); + bool add( const OTodo& t ); + bool remove( int uid ); + bool replace( const OTodo& t ); + + QArray<int> overDue(); + QArray<int> effectiveToDos( const QDate& start, + const QDate& end, bool includeNoDates ); + QArray<int> sorted(bool asc, int sortOrder, int sortFilter, int cat ); + +private: + void update(); + void fillDict(); + bool date( QDate& date, const QString& )const; + OTodo todo( const OSQLResult& )const; + QArray<int> uids( const OSQLResult& )const; + OTodo todo( int uid )const; + + QAsciiDict<int> m_dict; + OSQLDriver* m_driver; + QArray<int> m_uids; +}; + + +#endif diff --git a/libopie2/opiepim/backend/otodoaccessxml.cpp b/libopie2/opiepim/backend/otodoaccessxml.cpp index 31822d4..80b8599 100644 --- a/libopie2/opiepim/backend/otodoaccessxml.cpp +++ b/libopie2/opiepim/backend/otodoaccessxml.cpp @@ -1,547 +1,548 @@ #include <qfile.h> #include <qvector.h> #include <qpe/global.h> #include <qpe/stringutil.h> #include <qpe/timeconversion.h> #include <opie/xmltree.h> #include "otodoaccessxml.h" OTodoAccessXML::OTodoAccessXML( const QString& appName, const QString& fileName ) : OTodoAccessBackend(), m_app( appName ), m_opened( false ), m_changed( false ) { if (!fileName.isEmpty() ) m_file = fileName; else m_file = Global::applicationFileName( "todolist", "todolist.xml" ); } OTodoAccessXML::~OTodoAccessXML() { } bool OTodoAccessXML::load() { m_opened = true; m_changed = false; /* initialize dict */ /* * UPDATE dict if you change anything!!! */ QAsciiDict<int> dict(15); dict.setAutoDelete( TRUE ); dict.insert("Categories" , new int(OTodo::Category) ); dict.insert("Uid" , new int(OTodo::Uid) ); dict.insert("HasDate" , new int(OTodo::HasDate) ); dict.insert("Completed" , new int(OTodo::Completed) ); dict.insert("Description" , new int(OTodo::Description) ); dict.insert("Summary" , new int(OTodo::Summary) ); dict.insert("Priority" , new int(OTodo::Priority) ); dict.insert("DateDay" , new int(OTodo::DateDay) ); dict.insert("DateMonth" , new int(OTodo::DateMonth) ); dict.insert("DateYear" , new int(OTodo::DateYear) ); dict.insert("Progress" , new int(OTodo::Progress) ); dict.insert("Completed", new int(OTodo::Completed) ); dict.insert("CrossReference", new int(OTodo::CrossReference) ); dict.insert("HasAlarmDateTime",new int(OTodo::HasAlarmDateTime) ); dict.insert("AlarmDateTime", new int(OTodo::AlarmDateTime) ); // here the custom XML parser from TT it's GPL // but we want to push that to TT..... QFile f(m_file ); if (!f.open(IO_ReadOnly) ) return false; QByteArray ba = f.readAll(); f.close(); char* dt = ba.data(); int len = ba.size(); int i = 0; char *point; const char* collectionString = "<Task "; while ( dt+i != 0 && ( point = strstr( dt+i, collectionString ) ) != 0l ) { i = point -dt; i+= strlen(collectionString); OTodo ev; m_year = m_month = m_day = 0; while ( TRUE ) { 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; QCString attr( dt+i, j-i+1); i = ++j; // skip = // find the start of quotes while ( i < len && dt[i] != '"' ) ++i; j = ++i; bool haveUtf = FALSE; bool haveEnt = FALSE; while ( j < len && dt[j] != '"' ) { if ( ((unsigned char)dt[j]) > 0x7f ) haveUtf = TRUE; if ( dt[j] == '&' ) haveEnt = TRUE; ++j; } if ( i == j ) { // empty value i = j + 1; continue; } QCString value( dt+i, j-i+1 ); i = j + 1; QString str = (haveUtf ? QString::fromUtf8( value ) : QString::fromLatin1( value ) ); if ( haveEnt ) str = Qtopia::plainString( str ); /* * add key + value */ todo( &dict, ev, attr, str ); } /* * now add it */ m_events.insert(ev.uid(), ev ); } + qWarning("counts %d", m_events.count() ); return true; } bool OTodoAccessXML::reload() { return load(); } bool OTodoAccessXML::save() { // qWarning("saving"); if (!m_opened || !m_changed ) { // qWarning("not saving"); return true; } QString strNewFile = m_file + ".new"; QFile f( strNewFile ); if (!f.open( IO_WriteOnly|IO_Raw ) ) return false; int written; QString out; out = "<!DOCTYPE Tasks>\n<Tasks>\n"; // for all todos QMap<int, OTodo>::Iterator it; for (it = m_events.begin(); it != m_events.end(); ++it ) { out+= "<Task " + toString( (*it) ) + " />\n"; QCString cstr = out.utf8(); written = f.writeBlock( cstr.data(), cstr.length() ); /* less written then we wanted */ if ( written != (int)cstr.length() ) { f.close(); QFile::remove( strNewFile ); return false; } out = QString::null; } out += "</Tasks>"; QCString cstr = out.utf8(); written = f.writeBlock( cstr.data(), cstr.length() ); if ( written != (int)cstr.length() ) { f.close(); QFile::remove( strNewFile ); return false; } /* flush before renaming */ f.close(); if( ::rename( strNewFile.latin1(), m_file.latin1() ) < 0 ) { // qWarning("error renaming"); QFile::remove( strNewFile ); } m_changed = false; return true; } QArray<int> OTodoAccessXML::allRecords()const { QArray<int> ids( m_events.count() ); QMap<int, OTodo>::ConstIterator it; int i = 0; for ( it = m_events.begin(); it != m_events.end(); ++it ) { ids[i] = it.key(); i++; } return ids; } QArray<int> OTodoAccessXML::queryByExample( const OTodo&, int sort ) { QArray<int> ids(0); return ids; } OTodo OTodoAccessXML::find( int uid )const { OTodo todo; todo.setUid( 0 ); // isEmpty() QMap<int, OTodo>::ConstIterator it = m_events.find( uid ); if ( it != m_events.end() ) todo = it.data(); return todo; } void OTodoAccessXML::clear() { if (m_opened ) m_changed = true; m_events.clear(); } bool OTodoAccessXML::add( const OTodo& todo ) { // qWarning("add"); m_changed = true; m_events.insert( todo.uid(), todo ); return true; } bool OTodoAccessXML::remove( int uid ) { m_changed = true; m_events.remove( uid ); return true; } bool OTodoAccessXML::replace( const OTodo& todo) { m_changed = true; m_events.replace( todo.uid(), todo ); return true; } QArray<int> OTodoAccessXML::effectiveToDos( const QDate& start, const QDate& end, bool includeNoDates ) { QArray<int> ids( m_events.count() ); QMap<int, OTodo>::Iterator it; int i = 0; for ( it = m_events.begin(); it != m_events.end(); ++it ) { if ( !it.data().hasDueDate() ) { if ( includeNoDates ) { ids[i] = it.key(); i++; } }else if ( it.data().dueDate() >= start && it.data().dueDate() <= end ) { ids[i] = it.key(); i++; } } ids.resize( i ); return ids; } QArray<int> OTodoAccessXML::overDue() { QArray<int> ids( m_events.count() ); int i = 0; QMap<int, OTodo>::Iterator it; for ( it = m_events.begin(); it != m_events.end(); ++it ) { if ( it.data().isOverdue() ) { ids[i] = it.key(); i++; } } ids.resize( i ); return ids; } /* private */ void OTodoAccessXML::todo( QAsciiDict<int>* dict, OTodo& ev, const QCString& attr, const QString& val) { // qWarning("parse to do from XMLElement" ); int *find=0; find = (*dict)[ attr.data() ]; if (!find ) { // qWarning("Unknown option" + it.key() ); ev.setCustomField( attr, val ); return; } switch( *find ) { case OTodo::Uid: ev.setUid( val.toInt() ); break; case OTodo::Category: ev.setCategories( ev.idsFromString( val ) ); break; case OTodo::HasDate: ev.setHasDueDate( val.toInt() ); break; case OTodo::Completed: ev.setCompleted( val.toInt() ); break; case OTodo::Description: ev.setDescription( val ); break; case OTodo::Summary: ev.setSummary( val ); break; case OTodo::Priority: ev.setPriority( val.toInt() ); break; case OTodo::DateDay: m_day = val.toInt(); break; case OTodo::DateMonth: m_month = val.toInt(); break; case OTodo::DateYear: m_year = val.toInt(); break; case OTodo::Progress: ev.setProgress( val.toInt() ); break; case OTodo::CrossReference: { /* * A cross refernce looks like * appname,id;appname,id * we need to split it up */ QStringList refs = QStringList::split(';', val ); QStringList::Iterator strIt; for (strIt = refs.begin(); strIt != refs.end(); ++strIt ) { int pos = (*strIt).find(','); if ( pos > -1 ) ev.addRelation( (*strIt).left(pos), (*strIt).mid(pos+1).toInt() ); } break; } case OTodo::HasAlarmDateTime: ev.setHasAlarmDateTime( val.toInt() ); break; case OTodo::AlarmDateTime: { /* this sounds better ;) zecke */ ev.setAlarmDateTime( TimeConversion::fromISO8601( val.local8Bit() ) ); break; } default: break; } if ( ev.hasDueDate() ) { QDate date( m_year, m_month, m_day ); ev.setDueDate( date ); } } QString OTodoAccessXML::toString( const OTodo& ev )const { QString str; str += "Completed=\"" + QString::number( ev.isCompleted() ) + "\" "; str += "HasDate=\"" + QString::number( ev.hasDueDate() ) + "\" "; str += "Priority=\"" + QString::number( ev.priority() ) + "\" "; str += "Progress=\"" + QString::number(ev.progress() ) + "\" "; str += "Categories=\"" + toString( ev.categories() ) + "\" "; str += "Description=\"" + Qtopia::escapeString( ev.description() ) + "\" "; str += "Summary=\"" + Qtopia::escapeString( ev.summary() ) + "\" "; if ( ev.hasDueDate() ) { str += "DateYear=\"" + QString::number( ev.dueDate().year() ) + "\" "; str += "DateMonth=\"" + QString::number( ev.dueDate().month() ) + "\" "; str += "DateDay=\"" + QString::number( ev.dueDate().day() ) + "\" "; } // qWarning( "Uid %d", ev.uid() ); str += "Uid=\"" + QString::number( ev.uid() ) + "\" "; // append the extra options /* FIXME Qtopia::Record this is currently not * possible you can set custom fields * but don' iterate over the list * I may do #define private protected * for this case - cough --zecke */ /* QMap<QString, QString> extras = ev.extras(); QMap<QString, QString>::Iterator extIt; for (extIt = extras.begin(); extIt != extras.end(); ++extIt ) str += extIt.key() + "=\"" + extIt.data() + "\" "; */ // cross refernce QStringList list = ev.relatedApps(); QStringList::Iterator listIt; QString refs; str += "CrossReference=\""; bool added = false; for ( listIt = list.begin(); listIt != list.end(); ++listIt ) { added = true; QArray<int> ints = ev.relations( (*listIt) ); for ( uint i = 0; i< ints.count(); i++ ) { str += (*listIt) + "," + QString::number( i ) + ";"; } } if ( added ) str = str.remove( str.length()-1, 1 ); str += "\" "; str += "AlarmDateTime=\"" + TimeConversion::toISO8601( ev.alarmDateTime() ) + "\" "; return str; } QString OTodoAccessXML::toString( const QArray<int>& ints ) const { return Qtopia::Record::idsToString( ints ); } /* internal class for sorting */ struct OTodoXMLContainer { OTodo todo; }; /* * Returns: * 0 if item1 == item2 * * non-zero if item1 != item2 * * This function returns int rather than bool so that reimplementations * can return one of three values and use it to sort by: * * 0 if item1 == item2 * * > 0 (positive integer) if item1 > item2 * * < 0 (negative integer) if item1 < item2 * */ class OTodoXMLVector : public QVector<OTodoXMLContainer> { public: OTodoXMLVector(int size, bool asc, int sort) : QVector<OTodoXMLContainer>( size ) { setAutoDelete( true ); m_asc = asc; m_sort = sort; } /* return the summary/description */ QString string( const OTodo& todo) { return todo.summary().isEmpty() ? todo.description().left(20 ) : todo.summary(); } /** * we take the sortorder( switch on it ) * */ int compareItems( Item d1, Item d2 ) { qWarning("compare items"); int ret =0; OTodoXMLContainer* con1 = (OTodoXMLContainer*)d1; OTodoXMLContainer* con2 = (OTodoXMLContainer*)d2; /* same item */ if ( con1->todo.uid() == con2->todo.uid() ) return 0; qWarning("m_sort %d", m_sort ); switch ( m_sort ) { /* completed */ case 0: { ret = 0; if ( con1->todo.isCompleted() ) ret++; if ( con2->todo.isCompleted() ) ret--; break; } /* priority */ case 1: { ret = con1->todo.priority() - con2->todo.priority(); qWarning(" priority %d %d %d", ret, con1->todo.priority(), con2->todo.priority() ); break; } /* description */ case 2: { QString str1 = string( con1->todo ); QString str2 = string( con2->todo ); ret = QString::compare( str1, str2 ); break; } /* deadline */ case 3: { /* either bot got a dueDate * or one of them got one */ if ( con1->todo.hasDueDate() && con2->todo.hasDueDate() ) ret = con1->todo.dueDate().daysTo( con2->todo.dueDate() ); else if ( con1->todo.hasDueDate() ) ret = -1; else if ( con2->todo.hasDueDate() ) ret = 0; break; } default: ret = 0; break; }; /* twist it we're not ascending*/ if (!m_asc) ret = ret * -1; return ret; } private: bool m_asc; int m_sort; }; QArray<int> OTodoAccessXML::sorted( bool asc, int sortOrder, int sortFilter, int cat ) { OTodoXMLVector vector(m_events.count(), asc,sortOrder ); QMap<int, OTodo>::Iterator it; int item = 0; bool bCat = sortFilter & 1 ? true : false; bool bOver = sortFilter & 0 ? true : false; bool bOnly = sortFilter & 2 ? true : false; for ( it = m_events.begin(); it != m_events.end(); ++it ) { /* show category */ if ( bCat ) if (!(*it).categories().contains( cat ) ) continue; /* isOverdue but we should not show overdue */ if ( (*it).isOverdue() && ( !bOver || !bOnly ) ) continue; if ( !(*it).isOverdue() && bOnly ) continue; OTodoXMLContainer* con = new OTodoXMLContainer(); con->todo = (*it); vector.insert(item, con ); item++; } vector.resize( item ); /* sort it now */ vector.sort(); /* now get the uids */ QArray<int> array( vector.count() ); for (uint i= 0; i < vector.count(); i++ ) { array[i] = ( vector.at(i) )->todo.uid(); } return array; }; diff --git a/libopie2/opiepim/core/otodoaccess.cpp b/libopie2/opiepim/core/otodoaccess.cpp index f51da29..5990841 100644 --- a/libopie2/opiepim/core/otodoaccess.cpp +++ b/libopie2/opiepim/core/otodoaccess.cpp @@ -1,77 +1,77 @@ #include <qdatetime.h> #include <qpe/alarmserver.h> -#include "otodoaccessxml.h" +#include "otodoaccesssql.h" #include "otodoaccess.h" OTodoAccess::OTodoAccess( OTodoAccessBackend* end ) : QObject(), OPimAccessTemplate<OTodo>( end ), m_todoBackEnd( end ) { if (end == 0l ) - m_todoBackEnd = new OTodoAccessXML( "Todolist" ); + m_todoBackEnd = new OTodoAccessBackendSQL( QString::null); setBackEnd( m_todoBackEnd ); } OTodoAccess::~OTodoAccess() { // qWarning("~OTodoAccess"); } void OTodoAccess::mergeWith( const QValueList<OTodo>& list ) { QValueList<OTodo>::ConstIterator it; for ( it = list.begin(); it != list.end(); ++it ) { replace( (*it) ); } } OTodoAccess::List OTodoAccess::effectiveToDos( const QDate& start, const QDate& end, bool includeNoDates ) { QArray<int> ints = m_todoBackEnd->effectiveToDos( start, end, includeNoDates ); List lis( ints, this ); return lis; } OTodoAccess::List OTodoAccess::effectiveToDos( const QDate& start, bool includeNoDates ) { return effectiveToDos( start, QDate::currentDate(), includeNoDates ); } OTodoAccess::List OTodoAccess::overDue() { List lis( m_todoBackEnd->overDue(), this ); return lis; } void OTodoAccess::addAlarm( const OTodo& event) { if (!event.hasAlarmDateTime() ) return; QDateTime now = QDateTime::currentDateTime(); QDateTime schedule = event.alarmDateTime(); if ( schedule > now ){ AlarmServer::addAlarm( schedule, "QPE/Application/todolist", "alarm(QDateTime,int)", event.uid() ); } } void OTodoAccess::delAlarm( int uid) { QDateTime schedule; // Create null DateTime // I hope this will remove all scheduled alarms // with the given uid !? // If not: I have to rethink how to remove already // scheduled events... (se) // it should be fine -zecke // qWarning("Removing alarm for event with uid %d", uid ); AlarmServer::deleteAlarm( schedule , "QPE/Application/todolist", "alarm(QDateTime,int)", uid ); } /* sort order */ OTodoAccess::List OTodoAccess::sorted( bool ascending, int sort,int filter, int cat ) { QArray<int> ints = m_todoBackEnd->sorted( ascending, sort, filter, cat ); OTodoAccess::List list( ints, this ); return list; } diff --git a/libopie2/opiepim/orecordlist.h b/libopie2/opiepim/orecordlist.h index 1fd0741..b6fa7fa 100644 --- a/libopie2/opiepim/orecordlist.h +++ b/libopie2/opiepim/orecordlist.h @@ -1,260 +1,259 @@ #ifndef OPIE_RECORD_LIST_H #define OPIE_RECORD_LIST_H #include <qarray.h> #include "otemplatebase.h" #include "opimrecord.h" /** * Our List Iterator * it behaves like STL or Qt * * for(it = list.begin(); it != list.end(); ++it ) * doSomeCoolStuff( (*it) ); */ template <class T> class ORecordList; template <class T = OPimRecord> class ORecordListIterator { friend class ORecordList<T>; public: typedef OTemplateBase<T> Base; /** * The c'tor used internally from * ORecordList */ ORecordListIterator( const QArray<int>, const Base* ); /** * The standard c'tor */ ORecordListIterator(); ~ORecordListIterator(); ORecordListIterator( const ORecordListIterator& ); ORecordListIterator &operator=(const ORecordListIterator& ); /** * a * operator ;) * use it like this T = (*it); */ T operator*(); ORecordListIterator &operator++(); ORecordListIterator &operator--(); bool operator==( const ORecordListIterator& it ); bool operator!=( const ORecordListIterator& it ); /** * the current item */ uint current()const; /** * the number of items */ uint count()const; /** * sets the current item */ void setCurrent( uint cur ); private: QArray<int> m_uids; uint m_current; const Base* m_temp; bool m_end : 1; T m_record; /* d pointer for future versions */ class IteratorPrivate; IteratorPrivate *d; }; /** * The recordlist used as a return type * from OPimAccessTemplate */ template <class T = OPimRecord > class ORecordList { public: typedef OTemplateBase<T> Base; typedef ORecordListIterator<T> Iterator; /** * c'tor */ ORecordList () { } ORecordList( const QArray<int>& ids, const Base* ); ~ORecordList(); /** * the first iterator */ Iterator begin(); /** * the end */ Iterator end(); /** * the number of items in the list */ uint count()const; T operator[]( uint i ); // FIXME implemenent remove /* ConstIterator begin()const; ConstIterator end()const; */ private: QArray<int> m_ids; const Base* m_acc; }; /* ok now implement it */ template <class T> ORecordListIterator<T>::ORecordListIterator() { m_current = 0; m_temp = 0l; m_end = true; m_record = T(); } template <class T> ORecordListIterator<T>::~ORecordListIterator() { /* nothing to delete */ } template <class T> ORecordListIterator<T>::ORecordListIterator( const ORecordListIterator<T>& it) { // qWarning("ORecordListIterator copy c'tor"); m_uids = it.m_uids; m_current = it.m_current; m_temp = it.m_temp; m_end = it.m_end; m_record = it.m_record; } template <class T> ORecordListIterator<T> &ORecordListIterator<T>::operator=( const ORecordListIterator<T>& it) { m_uids = it.m_uids; m_current = it.m_current; m_temp = it.m_temp; m_end = it.m_end; m_record = it.m_record; return *this; } template <class T> T ORecordListIterator<T>::operator*() { -// qWarning("operator* %d %d", m_current, m_uids[m_current] ); + qWarning("operator* %d %d", m_current, m_uids[m_current] ); if (!m_end ) /* FIXME * until the cache is in place * we do the uid match uid check */ - if(m_record.uid() != m_uids[m_current] ) m_record = m_temp->find( m_uids[m_current] ); else m_record = T(); return m_record; } template <class T> ORecordListIterator<T> &ORecordListIterator<T>::operator++() { if (m_current < m_uids.count() ) { m_end = false; ++m_current; }else m_end = true; return *this; } template <class T> ORecordListIterator<T> &ORecordListIterator<T>::operator--() { if ( m_current > 0 ) { --m_current; m_end = false; } else m_end = true; return *this; } template <class T> bool ORecordListIterator<T>::operator==( const ORecordListIterator<T>& it ) { /* if both are at we're the same.... */ if ( m_end == it.m_end ) return true; if ( m_uids != it.m_uids ) return false; if ( m_current != it.m_current ) return false; if ( m_temp != it.m_temp ) return false; return true; } template <class T> bool ORecordListIterator<T>::operator!=( const ORecordListIterator<T>& it ) { return !(*this == it ); } template <class T> ORecordListIterator<T>::ORecordListIterator( const QArray<int> uids, const Base* t ) : m_uids( uids ), m_current( 0 ), m_temp( t ), m_end( false ) { } template <class T> uint ORecordListIterator<T>::current()const { return m_current; } template <class T> void ORecordListIterator<T>::setCurrent( uint cur ) { if( cur < m_uids.count() ) { m_end = false; m_current= cur; } } template <class T> uint ORecordListIterator<T>::count()const { return m_uids.count(); } template <class T> ORecordList<T>::ORecordList( const QArray<int>& ids, const Base* acc ) : m_ids( ids ), m_acc( acc ) { } template <class T> ORecordList<T>::~ORecordList() { /* nothing to do here */ } template <class T> ORecordList<T>::Iterator ORecordList<T>::begin() { Iterator it( m_ids, m_acc ); return it; } template <class T> ORecordList<T>::Iterator ORecordList<T>::end() { Iterator it( m_ids, m_acc ); it.m_end = true; it.m_current = m_ids.count(); return it; } template <class T> uint ORecordList<T>::count()const { return m_ids.count(); } template <class T> T ORecordList<T>::operator[]( uint i ) { return m_acc->find( m_ids[i] ); } #endif |