6 files changed, 309 insertions, 5 deletions
diff --git a/libopie2/opiepim/backend/ocontactaccessbackend_sql.cpp b/libopie2/opiepim/backend/ocontactaccessbackend_sql.cpp index 132c9fc..dd9dbde 100644 --- a/libopie2/opiepim/backend/ocontactaccessbackend_sql.cpp +++ b/libopie2/opiepim/backend/ocontactaccessbackend_sql.cpp @@ -1,144 +1,147 @@ /* * SQL Backend for the OPIE-Contact Database. * * Copyright (c) 2002 by Stefan Eilers (Eilers.Stefan@epost.de) * * ===================================================================== * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * ===================================================================== * ===================================================================== * Version: $Id$ * ===================================================================== * History: * $Log$ + * Revision 1.3 2003/12/08 15:18:10 eilers + * Committing unfinished sql implementation before merging to libopie2 starts.. + * * Revision 1.2 2003/09/29 07:44:26 eilers * Improvement of PIM-SQL Databases, but search queries are still limited. * Addressbook: Changed table layout. Now, we just need 1/3 of disk-space. * Todo: Started to add new attributes. Some type conversions missing. * * Revision 1.1 2003/09/22 14:31:16 eilers * Added first experimental incarnation of sql-backend for addressbook. * Some modifications to be able to compile the todo sql-backend. * A lot of changes fill follow... * */ #include "ocontactaccessbackend_sql.h" #include <qarray.h> #include <qdatetime.h> #include <qstringlist.h> #include <qpe/global.h> #include <qpe/recordfields.h> #include <opie/ocontactfields.h> #include <opie/oconversion.h> #include <opie2/osqldriver.h> #include <opie2/osqlresult.h> #include <opie2/osqlmanager.h> #include <opie2/osqlquery.h> // If defined, we use a horizontal table ( uid, attr1, attr2, attr3, ..., attrn ) instead // vertical like "uid, type, value". // DON'T DEACTIVATE THIS DEFINE IN PRODUCTIVE ENVIRONMENTS !! #define __STORE_HORIZONTAL_ // Distinct loading is not very fast. If I expect that every person has just // one (and always one) 'Last Name', I can request all uid's for existing lastnames, // which is faster.. // But this may not be true for all entries, like company contacts.. // The current AddressBook application handles this problem, but other may not.. (eilers) #define __USE_SUPERFAST_LOADQUERY /* * Implementation of used query types * CREATE query * LOAD query * INSERT * REMOVE * CLEAR */ namespace { /** * CreateQuery for the Todolist Table */ class CreateQuery : public OSQLQuery { public: CreateQuery(); ~CreateQuery(); QString query()const; }; /** * Clears (delete) a Table */ class ClearQuery : public OSQLQuery { public: ClearQuery(); ~ClearQuery(); QString query()const; }; /** * LoadQuery * this one queries for all uids */ class LoadQuery : public OSQLQuery { public: LoadQuery(); ~LoadQuery(); QString query()const; }; /** * inserts/adds a OContact to the table */ class InsertQuery : public OSQLQuery { public: InsertQuery(const OContact& ); ~InsertQuery(); QString query()const; private: OContact m_contact; }; /** * removes one from the table */ class RemoveQuery : public OSQLQuery { public: RemoveQuery(int uid ); ~RemoveQuery(); QString query()const; private: int m_uid; }; /** * a find query for noncustom elements */ class FindQuery : public OSQLQuery { public: FindQuery(int uid); FindQuery(const QArray<int>& ); ~FindQuery(); QString query()const; private: QString single()const; QString multi()const; QArray<int> m_uids; int m_uid; }; /** @@ -329,278 +332,284 @@ namespace { // Now convert this whole stuff into a SQL String, beginning with // the addressbook table.. QString qu; // qu += "begin transaction;"; int id = 0; for( QMap<QString, QString>::Iterator it = addressbook_db.begin(); it != addressbook_db.end(); ++it ){ qu += "insert into addressbook VALUES(" + QString::number( m_contact.uid() ) + "," + QString::number( id++ ) + ",'" + it.key() //.latin1() + "'," + "0" // Priority for future enhancements + ",'" + it.data() //.latin1() + "');"; } #endif //__STORE_HORIZONTAL_ // Now add custom data.. #ifdef __STORE_HORIZONTAL_ int id = 0; #endif id = 0; QMap<QString, QString> customMap = m_contact.toExtraMap(); for( QMap<QString, QString>::Iterator it = customMap.begin(); it != customMap.end(); ++it ){ qu += "insert into custom_data VALUES(" + QString::number( m_contact.uid() ) + "," + QString::number( id++ ) + ",'" + it.key() //.latin1() + "'," + "0" // Priority for future enhancements + ",'" + it.data() //.latin1() + "');"; } // qu += "commit;"; 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 addressbook where uid = " + QString::number(m_uid) + ";"; qu += "DELETE from custom_data where uid = " + QString::number(m_uid) + ";"; return qu; } FindQuery::FindQuery(int uid) : OSQLQuery(), m_uid( uid ) { } FindQuery::FindQuery(const QArray<int>& ints) : OSQLQuery(), m_uids( ints ){ } FindQuery::~FindQuery() { } QString FindQuery::query()const{ // if ( m_uids.count() == 0 ) return single(); } /* else return multi(); } QString FindQuery::multi()const { QString qu = "select uid, type, value from addressbook where"; for (uint i = 0; i < m_uids.count(); i++ ) { qu += " UID = " + QString::number( m_uids[i] ) + " OR"; } qu.remove( qu.length()-2, 2 ); // Hmmmm.. return qu; } */ #ifdef __STORE_HORIZONTAL_ QString FindQuery::single()const{ QString qu = "select *"; qu += " from addressbook where uid = " + QString::number(m_uid); // qWarning("find query: %s", qu.latin1() ); return qu; } #else QString FindQuery::single()const{ QString qu = "select uid, type, value from addressbook where uid = "; qu += QString::number(m_uid); return qu; } #endif FindCustomQuery::FindCustomQuery(int uid) : OSQLQuery(), m_uid( uid ) { } FindCustomQuery::FindCustomQuery(const QArray<int>& ints) : OSQLQuery(), m_uids( ints ){ } FindCustomQuery::~FindCustomQuery() { } QString FindCustomQuery::query()const{ // if ( m_uids.count() == 0 ) return single(); } QString FindCustomQuery::single()const{ QString qu = "select uid, type, value from custom_data where uid = "; qu += QString::number(m_uid); return qu; } }; /* --------------------------------------------------------------------------- */ OContactAccessBackend_SQL::OContactAccessBackend_SQL ( const QString& /* appname */, - const QString& filename ): m_changed(false) + const QString& filename ): + OContactAccessBackend(), m_changed(false), m_driver( NULL ) { qWarning("C'tor OContactAccessBackend_SQL starts"); QTime t; t.start(); /* Expecting to access the default filename if nothing else is set */ if ( filename.isEmpty() ){ m_fileName = Global::applicationFileName( "addressbook","addressbook.db" ); } else m_fileName = filename; // Get the standart sql-driver from the OSQLManager.. OSQLManager man; m_driver = man.standard(); m_driver->setUrl( m_fileName ); load(); qWarning("C'tor OContactAccessBackend_SQL ends: %d ms", t.elapsed() ); } +OContactAccessBackend_SQL::~OContactAccessBackend_SQL () +{ + if( m_driver ) + delete m_driver; +} bool OContactAccessBackend_SQL::load () { if (!m_driver->open() ) return false; // Don't expect that the database exists. // It is save here to create the table, even if it // do exist. ( Is that correct for all databases ?? ) CreateQuery creat; OSQLResult res = m_driver->query( &creat ); update(); return true; } bool OContactAccessBackend_SQL::reload() { return load(); } bool OContactAccessBackend_SQL::save() { return m_driver->close(); } void OContactAccessBackend_SQL::clear () { ClearQuery cle; OSQLResult res = m_driver->query( &cle ); CreateQuery qu; res = m_driver->query(&qu); } bool OContactAccessBackend_SQL::wasChangedExternally() { return false; } QArray<int> OContactAccessBackend_SQL::allRecords() const { // FIXME: Think about cute handling of changed tables.. // Thus, we don't have to call update here... if ( m_changed ) ((OContactAccessBackend_SQL*)this)->update(); return m_uids; } bool OContactAccessBackend_SQL::add ( const OContact &newcontact ) { InsertQuery ins( newcontact ); 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] = newcontact.uid(); return true; } bool OContactAccessBackend_SQL::remove ( int uid ) { RemoveQuery rem( uid ); OSQLResult res = m_driver->query(&rem ); if ( res.state() == OSQLResult::Failure ) return false; m_changed = true; return true; } bool OContactAccessBackend_SQL::replace ( const OContact &contact ) { if ( !remove( contact.uid() ) ) return false; return add( contact ); } OContact OContactAccessBackend_SQL::find ( int uid ) const { qWarning("OContactAccessBackend_SQL::find()"); QTime t; t.start(); OContact retContact( requestNonCustom( uid ) ); retContact.setExtraMap( requestCustom( uid ) ); qWarning("OContactAccessBackend_SQL::find() needed: %d ms", t.elapsed() ); return retContact; } QArray<int> OContactAccessBackend_SQL::queryByExample ( const OContact &query, int settings, const QDateTime& d = QDateTime() ) { QString qu = "SELECT uid FROM addressbook WHERE"; QMap<int, QString> queryFields = query.toMap(); QStringList fieldList = OContactFields::untrfields( false ); QMap<QString, int> translate = OContactFields::untrFieldsToId(); // Convert every filled field to a SQL-Query bool isAnyFieldSelected = false; for ( QStringList::Iterator it = ++fieldList.begin(); it != fieldList.end(); ++it ){ int id = translate[*it]; QString queryStr = queryFields[id]; if ( !queryStr.isEmpty() ){ isAnyFieldSelected = true; switch( id ){ default: // Switching between case sensitive and insensitive... // LIKE is not case sensitive, GLOB is case sensitive // Do exist a better solution to switch this ? if ( settings & OContactAccess::IgnoreCase ) qu += "(\"" + *it + "\"" + " LIKE " + "'" diff --git a/libopie2/opiepim/backend/ocontactaccessbackend_sql.h b/libopie2/opiepim/backend/ocontactaccessbackend_sql.h index bb22551..b8f1d8d 100644 --- a/libopie2/opiepim/backend/ocontactaccessbackend_sql.h +++ b/libopie2/opiepim/backend/ocontactaccessbackend_sql.h @@ -1,96 +1,101 @@ /* * SQL Backend for the OPIE-Contact Database. * * Copyright (c) 2002 by Stefan Eilers (Eilers.Stefan@epost.de) * * ===================================================================== * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * ===================================================================== * * * ===================================================================== * Version: $Id$ * ===================================================================== * History: * $Log$ + * Revision 1.2 2003/12/08 15:18:11 eilers + * Committing unfinished sql implementation before merging to libopie2 starts.. + * * Revision 1.1 2003/09/22 14:31:16 eilers * Added first experimental incarnation of sql-backend for addressbook. * Some modifications to be able to compile the todo sql-backend. * A lot of changes fill follow... * * */ #ifndef _OContactAccessBackend_SQL_ #define _OContactAccessBackend_SQL_ #include "ocontactaccessbackend.h" #include "ocontactaccess.h" #include <qlist.h> #include <qdict.h> class OSQLDriver; class OSQLResult; class OSQLResultItem; /* the default xml implementation */ /** * This class is the SQL implementation of a Contact backend * it does implement everything available for OContact. * @see OPimAccessBackend for more information of available methods */ class OContactAccessBackend_SQL : public OContactAccessBackend { public: OContactAccessBackend_SQL ( const QString& appname, const QString& filename = QString::null ); + ~OContactAccessBackend_SQL (); + bool save(); bool load (); void clear (); bool wasChangedExternally(); QArray<int> allRecords() const; OContact find ( int uid ) const; // FIXME: Add lookahead-cache support ! //OContact find(int uid, const QArray<int>&, uint cur, Frontend::CacheDirection )const; QArray<int> queryByExample ( const OContact &query, int settings, const QDateTime& d ); QArray<int> matchRegexp( const QRegExp &r ) const; const uint querySettings(); bool hasQuerySettings (uint querySettings) const; // Currently only asc implemented.. QArray<int> sorted( bool asc, int , int , int ); bool add ( const OContact &newcontact ); bool replace ( const OContact &contact ); bool remove ( int uid ); bool reload(); private: QArray<int> extractUids( OSQLResult& res ) const; QMap<int, QString> requestNonCustom( int uid ) const; QMap<QString, QString> requestCustom( int uid ) const; void update(); protected: bool m_changed; QString m_fileName; QArray<int> m_uids; OSQLDriver* m_driver; }; #endif diff --git a/libopie2/opiepim/backend/odatebookaccessbackend_sql.cpp b/libopie2/opiepim/backend/odatebookaccessbackend_sql.cpp new file mode 100644 index 0000000..9769bf7 --- a/dev/null +++ b/libopie2/opiepim/backend/odatebookaccessbackend_sql.cpp @@ -0,0 +1,221 @@ +/* + * SQL Backend for the OPIE-Calender Database. + * + * Copyright (c) 2003 by Stefan Eilers (Eilers.Stefan@epost.de) + * + * ===================================================================== + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * ===================================================================== + * ===================================================================== + * Version: $Id$ + * ===================================================================== + * History: + * $Log$ + * Revision 1.1 2003/12/08 15:18:12 eilers + * Committing unfinished sql implementation before merging to libopie2 starts.. + * + * + */ + +#include <stdio.h> +#include <stdlib.h> + +#include <qarray.h> +#include <qstringlist.h> + +#include "orecur.h" +#include "odatebookaccessbackend_sql.h" + +#include <opie2/osqldriver.h> +#include <opie2/osqlresult.h> +#include <opie2/osqlmanager.h> +#include <opie2/osqlquery.h> + +namespace { + + + +}; + +ODateBookAccessBackend_SQL::ODateBookAccessBackend_SQL( const QString& , + const QString& fileName ) + : ODateBookAccessBackend(), m_driver( NULL ) +{ + m_fileName = fileName.isEmpty() ? Global::applicationFileName( "datebook", "datebook.db" ) : fileName; + + // Get the standart sql-driver from the OSQLManager.. + OSQLManager man; + m_driver = man.standard(); + m_driver->setUrl( m_fileName ); + + initFields(); + + load(); +} + +ODateBookAccessBackend_SQL::~ODateBookAccessBackend_SQL() { +} + +void ODateBookAccessBackend_SQL::initFields() +{ + + // This map contains the translation of the fieldtype id's to + // the names of the table columns + m_fieldMap.insert( OEvent::FUid, "uid" ); + m_fieldMap.insert( OEvent::FCategories, "Categories" ); + m_fieldMap.insert( OEvent::FDescription, "Description" ); + m_fieldMap.insert( OEvent::FLocation, "Location" ); + m_fieldMap.insert( OEvent::FType, "Type" ); + m_fieldMap.insert( OEvent::FAlarm, "Alarm" ); + m_fieldMap.insert( OEvent::FSound, "Sound" ); + m_fieldMap.insert( OEvent::FRType, "RType" ); + m_fieldMap.insert( OEvent::FRWeekdays, "RWeekdays" ); + m_fieldMap.insert( OEvent::FRPosition, "RPosition" ); + m_fieldMap.insert( OEvent::FRFreq, "RFreq" ); + m_fieldMap.insert( OEvent::FRHasEndDate, "RHasEndDate" ); + m_fieldMap.insert( OEvent::FREndDate, "REndDate" ); + m_fieldMap.insert( OEvent::FRCreated, "RCreated" ); + m_fieldMap.insert( OEvent::FRExeptions, "RExceptions" ); + m_fieldMap.insert( OEvent::FStart, "Start" ); + m_fieldMap.insert( OEvent::FEnd, "End" ); + m_fieldMap.insert( OEvent::FNote, "Note" ); + m_fieldMap.insert( OEvent::FTimeZone, "TimeZone" ); + m_fieldMap.insert( OEvent::FRecParent, "RecParent" ); + m_fieldMap.insert( OEvent::FRecChildren, "Recchildren" ); +} + +bool ODateBookAccessBackend_SQL::load() +{ + if (!m_driver->open() ) + return false; + + // Don't expect that the database exists. + // It is save here to create the table, even if it + // do exist. ( Is that correct for all databases ?? ) + QString qu = "create table datebook( uid INTEGER PRIMARY KEY "; + + QMap<int, QString>::Iterator it; + for ( it = ++m_fieldMap.begin(); it != m_fieldMap.end(); ++it ){ + qu += QString( ",\"%1\" VARCHAR(10)" ).arg( it.data() ); + } + qu += " );"; + + qu += "create table custom_data( uid INTEGER, id INTEGER, type VARCHAR, priority INTEGER, value VARCHAR, PRIMARY KEY /* identifier */ (uid, id) );"; + + OSQLRawQuery raw( qu ); + OSQLResult res = m_driver->query( &raw ); + if ( res.state() != OSQLResult::Success ) + return false; + + update(); + + return true; +} + +void ODateBookAccessBackend_SQL::update() +{ + + QString qu = "select uid from datebook"; + OSQLRawQuery raw( qu ); + OSQLResult res = m_driver->query( &raw ); + if ( res.state() != OSQLResult::Success ){ + m_uids.clear(); + return; + } + + m_uids = extractUids( res ); + +} + +QArray<int> ODateBookAccessBackend_SQL::extractUids( OSQLResult& res ) const +{ + qWarning("extractUids"); + + OSQLResultItem::ValueList list = res.results(); + OSQLResultItem::ValueList::Iterator it; + QArray<int> ints(list.count() ); + qWarning(" count = %d", list.count() ); + + int i = 0; + for (it = list.begin(); it != list.end(); ++it ) { + ints[i] = (*it).data("uid").toInt(); + i++; + } + + return ints; + +} + +bool ODateBookAccessBackend_SQL::reload() +{ + return load(); +} + +bool ODateBookAccessBackend_SQL::save() +{ + return m_driver->close(); +} + +QArray<int> ODateBookAccessBackend_SQL::allRecords()const +{ + return m_uids; +} + +QArray<int> ODateBookAccessBackend_SQL::queryByExample(const OEvent&, int, const QDateTime& ) { + return QArray<int>(); +} + +void ODateBookAccessBackend_SQL::clear() +{ + QString qu = "drop table datebook;"; + qu += "drop table custom_data;"; + + OSQLRawQuery raw( qu ); + OSQLResult res = m_driver->query( &raw ); + +} + + +OEvent ODateBookAccessBackend_SQL::find( int uid ) const{ +} + +bool ODateBookAccessBackend_SQL::add( const OEvent& ev ) { + return true; +} +bool ODateBookAccessBackend_SQL::remove( int uid ) { + + return true; +} +bool ODateBookAccessBackend_SQL::replace( const OEvent& ev ) { + remove( ev.uid() ); + return add( ev ); +} +QArray<int> ODateBookAccessBackend_SQL::rawEvents()const { + return allRecords(); +} +QArray<int> ODateBookAccessBackend_SQL::rawRepeats()const { + + return ints; +} +QArray<int> ODateBookAccessBackend_SQL::nonRepeats()const { + + return ints; +} +OEvent::ValueList ODateBookAccessBackend_SQL::directNonRepeats() { + + return list; +} +OEvent::ValueList ODateBookAccessBackend_SQL::directRawRepeats() { + + return list; +} + + +QArray<int> ODateBookAccessBackend_SQL::matchRegexp( const QRegExp &r ) const +{ + + return m_currentQuery; +} diff --git a/libopie2/opiepim/backend/odatebookaccessbackend_sql.h b/libopie2/opiepim/backend/odatebookaccessbackend_sql.h new file mode 100644 index 0000000..85e0d4f --- a/dev/null +++ b/libopie2/opiepim/backend/odatebookaccessbackend_sql.h @@ -0,0 +1,60 @@ +#ifndef OPIE_DATE_BOOK_ACCESS_BACKEND_SQL__H +#define OPIE_DATE_BOOK_ACCESS_BACKEND_SQL__H + +#include <qmap.h> + +#include "odatebookaccessbackend.h" + +class OSQLDriver; + +/** + * This is the default SQL implementation for DateBoook SQL storage + * It fully implements the interface + * @see ODateBookAccessBackend + * @see OPimAccessBackend + */ +class ODateBookAccessBackend_SQL : public ODateBookAccessBackend { +public: + ODateBookAccessBackend_SQL( const QString& appName, + const QString& fileName = QString::null); + ~ODateBookAccessBackend_SQL(); + + bool load(); + bool reload(); + bool save(); + + QArray<int> allRecords()const; + QArray<int> matchRegexp(const QRegExp &r) const; + QArray<int> queryByExample( const OEvent&, int, const QDateTime& d = QDateTime() ); + OEvent find( int uid )const; + void clear(); + bool add( const OEvent& ev ); + bool remove( int uid ); + bool replace( const OEvent& ev ); + + QArray<UID> rawEvents()const; + QArray<UID> rawRepeats()const; + QArray<UID> nonRepeats()const; + + OEvent::ValueList directNonRepeats(); + OEvent::ValueList directRawRepeats(); + +private: + bool loadFile(); + QString m_fileName; + QArray<int> m_uids; + + QMap<int, QString> m_fieldMap; + + OSQLDriver* m_driver; + + class Private; + Private *d; + + void initFields(); + void update(); + QArray<int> extractUids( OSQLResult& res ) const; + +}; + +#endif diff --git a/libopie2/opiepim/backend/odatebookaccessbackend_xml.cpp b/libopie2/opiepim/backend/odatebookaccessbackend_xml.cpp index 39c43c5..929d004 100644 --- a/libopie2/opiepim/backend/odatebookaccessbackend_xml.cpp +++ b/libopie2/opiepim/backend/odatebookaccessbackend_xml.cpp @@ -1,574 +1,580 @@ #include <errno.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/mman.h> #include <sys/stat.h> #include <unistd.h> #include <qasciidict.h> #include <qfile.h> #include <qtopia/global.h> #include <qtopia/stringutil.h> #include <qtopia/timeconversion.h> #include "opimnotifymanager.h" #include "orecur.h" #include "otimezone.h" #include "odatebookaccessbackend_xml.h" namespace { // FROM TT again char *strstrlen(const char *haystack, int hLen, const char* needle, int nLen) { char needleChar; char haystackChar; if (!needle || !haystack || !hLen || !nLen) return 0; const char* hsearch = haystack; if ((needleChar = *needle++) != 0) { nLen--; //(to make up for needle++) do { do { if ((haystackChar = *hsearch++) == 0) return (0); if (hsearch >= haystack + hLen) return (0); } while (haystackChar != needleChar); } while (strncmp(hsearch, needle, QMIN(hLen - (hsearch - haystack), nLen)) != 0); hsearch--; } return ((char *)hsearch); } } namespace { time_t start, end, created, rp_end; ORecur* rec; ORecur* recur() { if (!rec) rec = new ORecur; return rec; } int alarmTime; int snd; enum Attribute{ FDescription = 0, FLocation, FCategories, FUid, FType, FAlarm, FSound, FRType, FRWeekdays, FRPosition, FRFreq, FRHasEndDate, FREndDate, FRStart, FREnd, FNote, - FCreated, + FCreated, // Should't this be called FRCreated ? FTimeZone, FRecParent, FRecChildren, FExceptions }; + + // FIXME: Use OEvent::toMap() here !! (eilers) inline void save( const OEvent& ev, QString& buf ) { qWarning("Saving %d %s", ev.uid(), ev.description().latin1() ); 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\""; // is that all ?? (eilers) 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(); buf += "\""; if (ev.parent() != 0 ) { buf += " recparent=\""+QString::number(ev.parent() )+"\""; } if (ev.children().count() != 0 ) { QArray<int> children = ev.children(); buf += " recchildren=\""; for ( uint i = 0; i < children.count(); i++ ) { if ( i != 0 ) buf += " "; buf += QString::number( children[i] ); } buf+= "\""; } // skip custom writing } inline bool forAll( const QMap<int, OEvent>& list, QFile& file ) { QMap<int, OEvent>::ConstIterator it; QString buf; QCString str; int total_written; for ( it = list.begin(); it != list.end(); ++it ) { buf = "<event"; save( it.data(), buf ); buf += " />\n"; str = buf.utf8(); total_written = file.writeBlock(str.data(), str.length() ); if ( total_written != int(str.length() ) ) return false; } return true; } } ODateBookAccessBackend_XML::ODateBookAccessBackend_XML( const QString& , const QString& fileName ) : ODateBookAccessBackend() { m_name = fileName.isEmpty() ? Global::applicationFileName( "datebook", "datebook.xml" ) : fileName; m_changed = false; } ODateBookAccessBackend_XML::~ODateBookAccessBackend_XML() { } bool ODateBookAccessBackend_XML::load() { return loadFile(); } bool ODateBookAccessBackend_XML::reload() { clear(); return load(); } bool ODateBookAccessBackend_XML::save() { if (!m_changed) return true; 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(); if ( ::rename( strFileNew, m_name ) < 0 ) { QFile::remove( strFileNew ); return false; } m_changed = false; return true; } QArray<int> ODateBookAccessBackend_XML::allRecords()const { QArray<int> ints( m_raw.count()+ m_rep.count() ); uint i = 0; QMap<int, OEvent>::ConstIterator it; for ( it = m_raw.begin(); it != m_raw.end(); ++it ) { ints[i] = it.key(); i++; } for ( it = m_rep.begin(); it != m_rep.end(); ++it ) { ints[i] = it.key(); i++; } return ints; } QArray<int> ODateBookAccessBackend_XML::queryByExample(const OEvent&, int, const QDateTime& ) { return QArray<int>(); } void ODateBookAccessBackend_XML::clear() { m_changed = true; m_raw.clear(); m_rep.clear(); } OEvent ODateBookAccessBackend_XML::find( int uid ) const{ if ( m_raw.contains( uid ) ) return m_raw[uid]; else return m_rep[uid]; } bool ODateBookAccessBackend_XML::add( const OEvent& ev ) { m_changed = true; if (ev.hasRecurrence() ) m_rep.insert( ev.uid(), ev ); else m_raw.insert( ev.uid(), ev ); return true; } bool ODateBookAccessBackend_XML::remove( int uid ) { m_changed = true; m_rep.remove( uid ); m_rep.remove( uid ); return true; } bool ODateBookAccessBackend_XML::replace( const OEvent& ev ) { - replace( ev.uid() ); + replace( ev.uid() ); // ??? Shouldn't this be "remove( ev.uid() ) ??? (eilers) return add( ev ); } QArray<int> ODateBookAccessBackend_XML::rawEvents()const { return allRecords(); } QArray<int> ODateBookAccessBackend_XML::rawRepeats()const { QArray<int> ints( m_rep.count() ); uint i = 0; QMap<int, OEvent>::ConstIterator it; for ( it = m_rep.begin(); it != m_rep.end(); ++it ) { ints[i] = it.key(); i++; } return ints; } QArray<int> ODateBookAccessBackend_XML::nonRepeats()const { QArray<int> ints( m_raw.count() ); uint i = 0; QMap<int, OEvent>::ConstIterator it; for ( it = m_raw.begin(); it != m_raw.end(); ++it ) { ints[i] = it.key(); i++; } return ints; } OEvent::ValueList ODateBookAccessBackend_XML::directNonRepeats() { OEvent::ValueList list; QMap<int, OEvent>::ConstIterator it; for (it = m_raw.begin(); it != m_raw.end(); ++it ) list.append( it.data() ); return list; } OEvent::ValueList ODateBookAccessBackend_XML::directRawRepeats() { OEvent::ValueList list; QMap<int, OEvent>::ConstIterator it; for (it = m_rep.begin(); it != m_rep.end(); ++it ) list.append( it.data() ); return list; } + +// FIXME: Use OEvent::fromMap() (eilers) bool ODateBookAccessBackend_XML::loadFile() { m_changed = false; int fd = ::open( QFile::encodeName(m_name).data(), O_RDONLY ); if ( fd < 0 ) return false; struct stat attribute; if ( ::fstat(fd, &attribute ) == -1 ) { ::close( fd ); return false; } void* map_addr = ::mmap(NULL, attribute.st_size, PROT_READ, MAP_SHARED, fd, 0 ); if ( map_addr == ( (caddr_t)-1) ) { ::close( fd ); return false; } ::madvise( map_addr, attribute.st_size, MADV_SEQUENTIAL ); ::close( fd ); QAsciiDict<int> dict(FExceptions+1); dict.setAutoDelete( true ); dict.insert( "description", new int(FDescription) ); dict.insert( "location", new int(FLocation) ); dict.insert( "categories", new int(FCategories) ); dict.insert( "uid", new int(FUid) ); dict.insert( "type", new int(FType) ); dict.insert( "alarm", new int(FAlarm) ); dict.insert( "sound", new int(FSound) ); dict.insert( "rtype", new int(FRType) ); dict.insert( "rweekdays", new int(FRWeekdays) ); dict.insert( "rposition", new int(FRPosition) ); dict.insert( "rfreq", new int(FRFreq) ); dict.insert( "rhasenddate", new int(FRHasEndDate) ); dict.insert( "enddt", new int(FREndDate) ); dict.insert( "start", new int(FRStart) ); dict.insert( "end", new int(FREnd) ); dict.insert( "note", new int(FNote) ); - dict.insert( "created", new int(FCreated) ); + dict.insert( "created", new int(FCreated) ); // Shouldn't this be FRCreated ?? dict.insert( "recparent", new int(FRecParent) ); dict.insert( "recchildren", new int(FRecChildren) ); dict.insert( "exceptions", new int(FExceptions) ); dict.insert( "timezone", new int(FTimeZone) ); char* dt = (char*)map_addr; int len = attribute.st_size; int i = 0; char* point; const char* collectionString = "<event "; int strLen = ::strlen(collectionString); int *find; while ( ( point = ::strstrlen( dt+i, len -i, collectionString, strLen ) ) != 0 ) { i = point -dt; i+= strLen; alarmTime = -1; snd = 0; // silent OEvent ev; rec = 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 */ find = dict[attr.data()]; if (!find) ev.setCustomField( attr, str ); else { setField( ev, *find, str ); } } /* time to finalize */ finalizeRecord( ev ); delete rec; } ::munmap(map_addr, attribute.st_size ); m_changed = false; // changed during add return true; } + +// FIXME: Use OEvent::fromMap() which makes this obsolete.. (eilers) void ODateBookAccessBackend_XML::finalizeRecord( OEvent& ev ) { /* AllDay is alway in UTC */ if ( ev.isAllDay() ) { OTimeZone utc = OTimeZone::utc(); ev.setStartDateTime( utc.fromUTCDateTime( start ) ); ev.setEndDateTime ( utc.fromUTCDateTime( end ) ); ev.setTimeZone( "UTC"); // make sure it is really utc }else { /* to current date time */ // qWarning(" Start is %d", start ); OTimeZone zone( ev.timeZone().isEmpty() ? OTimeZone::current() : ev.timeZone() ); QDateTime date = zone.toDateTime( start ); qWarning(" Start is %s", date.toString().latin1() ); ev.setStartDateTime( zone.toDateTime( date, OTimeZone::current() ) ); date = zone.toDateTime( end ); ev.setEndDateTime ( zone.toDateTime( date, OTimeZone::current() ) ); } if ( rec && rec->doesRecur() ) { OTimeZone utc = OTimeZone::utc(); ORecur recu( *rec ); // call copy c'tor; recu.setEndDate ( utc.fromUTCDateTime( rp_end ).date() ); recu.setCreatedDateTime( utc.fromUTCDateTime( created ) ); recu.setStart( ev.startDateTime().date() ); ev.setRecurrence( recu ); } if (alarmTime != -1 ) { QDateTime dt = ev.startDateTime().addSecs( -1*alarmTime*60 ); OPimAlarm al( snd , dt ); ev.notifiers().add( al ); } if ( m_raw.contains( ev.uid() ) || m_rep.contains( ev.uid() ) ) { qWarning("already contains assign uid"); ev.setUid( 1 ); } qWarning("addind %d %s", ev.uid(), ev.description().latin1() ); if ( ev.hasRecurrence() ) m_rep.insert( ev.uid(), ev ); else m_raw.insert( ev.uid(), ev ); } void ODateBookAccessBackend_XML::setField( OEvent& e, int id, const QString& value) { // qWarning(" setting %s", value.latin1() ); switch( id ) { case FDescription: e.setDescription( value ); break; case FLocation: e.setLocation( value ); break; case FCategories: e.setCategories( e.idsFromString( value ) ); break; case FUid: e.setUid( value.toInt() ); break; case FType: if ( value == "AllDay" ) { e.setAllDay( true ); e.setTimeZone( "UTC" ); } break; case FAlarm: alarmTime = value.toInt(); break; case FSound: snd = value == "loud" ? OPimAlarm::Loud : OPimAlarm::Silent; break; // recurrence stuff case FRType: if ( value == "Daily" ) recur()->setType( ORecur::Daily ); else if ( value == "Weekly" ) recur()->setType( ORecur::Weekly); else if ( value == "MonthlyDay" ) recur()->setType( ORecur::MonthlyDay ); else if ( value == "MonthlyDate" ) recur()->setType( ORecur::MonthlyDate ); else if ( value == "Yearly" ) recur()->setType( ORecur::Yearly ); else recur()->setType( ORecur::NoRepeat ); break; case FRWeekdays: recur()->setDays( value.toInt() ); break; case FRPosition: recur()->setPosition( value.toInt() ); break; case FRFreq: recur()->setFrequency( value.toInt() ); break; case FRHasEndDate: recur()->setHasEndDate( value.toInt() ); break; case FREndDate: { rp_end = (time_t) value.toLong(); break; } case FRStart: { start = (time_t) value.toLong(); break; } case FREnd: { end = ( (time_t) value.toLong() ); break; } case FNote: e.setNote( value ); break; case FCreated: created = value.toInt(); break; case FRecParent: e.setParent( value.toInt() ); break; case FRecChildren:{ QStringList list = QStringList::split(' ', value ); for ( QStringList::Iterator it = list.begin(); it != list.end(); ++it ) { e.addChild( (*it).toInt() ); } } break; case FExceptions:{ QStringList list = QStringList::split(' ', value ); for (QStringList::Iterator it = list.begin(); it != list.end(); ++it ) { diff --git a/libopie2/opiepim/backend/otodoaccesssql.cpp b/libopie2/opiepim/backend/otodoaccesssql.cpp index 3913661..75a0860 100644 --- a/libopie2/opiepim/backend/otodoaccesssql.cpp +++ b/libopie2/opiepim/backend/otodoaccesssql.cpp @@ -174,269 +174,272 @@ namespace { eYear = eDate.year(); eMonth= eDate.month(); eDay = eDate.day(); } QString qu; QMap<int, QString> recMap = m_todo.recurrence().toMap(); qu = "insert into todolist VALUES(" + QString::number( m_todo.uid() ) + "," + "'" + m_todo.idsToString( m_todo.categories() ) + "'" + "," + QString::number( m_todo.isCompleted() ) + "," + "'" + m_todo.description() + "'" + "," + "'" + m_todo.summary() + "'" + "," + QString::number(m_todo.priority() ) + "," + "'" + QString::number(year) + "-" + QString::number(month) + "-" + QString::number( day ) + "'" + "," + QString::number( m_todo.progress() ) + "," + QString::number( m_todo.state().state() ) + "," + "'" + recMap[ ORecur::RType ] + "'" + "," + "'" + recMap[ ORecur::RWeekdays ] + "'" + "," + "'" + recMap[ ORecur::RPosition ] + "'" + "," + "'" + recMap[ ORecur::RFreq ] + "'" + "," + "'" + recMap[ ORecur::RHasEndDate ] + "'" + "," + "'" + recMap[ ORecur::EndDate ] + "'" + "," + "'" + recMap[ ORecur::Created ] + "'" + "," + "'" + recMap[ ORecur::Exceptions ] + "'" + ","; if ( m_todo.hasNotifiers() ) { OPimNotifyManager manager = m_todo.notifiers(); qu += "'" + manager.remindersToString() + "'" + "," + "'" + manager.alarmsToString() + "'" + ","; } else{ qu += QString( "''" ) + "," + "''" + ","; } qu += QString( "''" ) + QString( "," ) // Maintainers (cur. not supported !) + "'" + QString::number(sYear) + "-" + QString::number(sMonth) + "-" + QString::number(sDay) + "'" + "," + "'" + QString::number(eYear) + "-" + QString::number(eMonth) + "-"+QString::number(eDay) + "'" + ")"; 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(const QArray<int>& ints) : OSQLQuery(), m_uids(ints){ } FindQuery::~FindQuery() { } QString FindQuery::query()const{ if (m_uids.count() == 0 ) return single(); else return multi(); } QString FindQuery::single()const{ QString qu = "select * from todolist where uid = " + QString::number(m_uid); return qu; } QString FindQuery::multi()const { QString qu = "select * from todolist where "; for (uint i = 0; i < m_uids.count(); i++ ) { qu += " UID = " + QString::number( m_uids[i] ) + " OR"; } qu.remove( qu.length()-2, 2 ); 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), m_dirty(true) + : OTodoAccessBackend(), m_dict(15), m_driver(NULL), m_dirty(true) { 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(){ + if( m_driver ) + delete m_driver; } + bool OTodoAccessBackendSQL::load(){ if (!m_driver->open() ) return false; CreateQuery creat; OSQLResult res = m_driver->query(&creat ); m_dirty = true; return true; } bool OTodoAccessBackendSQL::reload(){ return load(); } bool OTodoAccessBackendSQL::save(){ return m_driver->close(); } QArray<int> OTodoAccessBackendSQL::allRecords()const { if (m_dirty ) update(); return m_uids; } QArray<int> OTodoAccessBackendSQL::queryByExample( const OTodo& , int, const QDateTime& ){ QArray<int> ints(0); return ints; } OTodo OTodoAccessBackendSQL::find(int uid ) const{ FindQuery query( uid ); return todo( m_driver->query(&query) ); } OTodo OTodoAccessBackendSQL::find( int uid, const QArray<int>& ints, uint cur, Frontend::CacheDirection dir ) const{ uint CACHE = readAhead(); qWarning("searching for %d", uid ); QArray<int> search( CACHE ); uint size =0; OTodo to; // we try to cache CACHE items switch( dir ) { /* forward */ case 0: // FIXME: Not a good style to use magic numbers here (eilers) for (uint i = cur; i < ints.count() && size < CACHE; i++ ) { qWarning("size %d %d", size, ints[i] ); search[size] = ints[i]; size++; } break; /* reverse */ case 1: // FIXME: Not a good style to use magic numbers here (eilers) for (uint i = cur; i != 0 && size < CACHE; i-- ) { search[size] = ints[i]; size++; } break; } search.resize( size ); FindQuery query( search ); OSQLResult res = m_driver->query( &query ); if ( res.state() != OSQLResult::Success ) return to; return todo( res ); } 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; m_dirty = true; 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() ); bool b= add(t); m_dirty = false; // we changed some stuff but the UID stayed the same return b; } 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 ) { qWarning("sorted %d, %d", asc, sortOrder ); QString query; query = "select uid from todolist WHERE "; /* * Sort Filter stuff * not that straight forward * FIXME: Replace magic numbers |