author | eilers <eilers> | 2003-12-22 10:19:25 (UTC) |
---|---|---|
committer | eilers <eilers> | 2003-12-22 10:19:25 (UTC) |
commit | ae70312b1613e26b4ef89a2c9821d9531b82e987 (patch) (side-by-side diff) | |
tree | 7eb70bc45ee29e94da27a18b0136eb4a14e59fa6 | |
parent | 9e7aafdb7c76d29fee742d53131a73dd60aded2b (diff) | |
download | opie-ae70312b1613e26b4ef89a2c9821d9531b82e987.zip opie-ae70312b1613e26b4ef89a2c9821d9531b82e987.tar.gz opie-ae70312b1613e26b4ef89a2c9821d9531b82e987.tar.bz2 |
Finishing implementation of sql-backend for datebook. But I have to
port the PIM datebook application to use it, before I could debug the
whole stuff.
Thus, PIM-Database backend is finished, but highly experimental. And some
parts are still generic. For instance, the "queryByExample()" methods are
not (or not fully) implemented. Todo: custom-entries not stored.
The big show stopper: matchRegExp() (needed by OpieSearch) needs regular
expression search in the database, which is not supported by sqlite !
Therefore we need either an extended sqlite or a workaround which would
be very slow and memory consuming..
30 files changed, 729 insertions, 144 deletions
diff --git a/libopie/libopie.pro b/libopie/libopie.pro index 8682e84..a3d01ab 100644 --- a/libopie/libopie.pro +++ b/libopie/libopie.pro @@ -1,114 +1,114 @@ TEMPLATE = lib CONFIG += qte warn_on release HEADERS = ofontmenu.h \ ocolorbutton.h \ ofiledialog.h ofileselector.h \ ofileselector_p.h \ ocheckitem.h \ xmltree.h \ colordialog.h colorpopupmenu.h \ oclickablelabel.h oprocctrl.h \ oprocess.h odevice.h odevicebutton.h \ otimepicker.h otabwidget.h \ otabbar.h otabinfo.h \ ofontselector.h \ pim/opimrecord.h \ pim/otodo.h \ pim/orecordlist.h \ pim/opimaccesstemplate.h \ pim/opimaccessbackend.h \ pim/otodoaccess.h \ pim/otodoaccessbackend.h \ pim/oconversion.h \ pim/ocontact.h \ pim/ocontactfields.h \ pim/ocontactaccess.h \ pim/ocontactaccessbackend.h \ pim/ocontactaccessbackend_xml.h \ pim/ocontactaccessbackend_vcard.h \ pim/obackendfactory.h \ pim/opimcache.h \ pim/otodoaccessvcal.h \ pim/orecur.h \ pim/opimstate.h \ pim/opimxrefpartner.h \ pim/opimxref.h \ pim/opimxrefmanager.h \ pim/opimmaintainer.h \ pim/opimnotify.h \ pim/opimnotifymanager.h \ pim/opimmainwindow.h \ pim/opimresolver.h \ pim/oevent.h \ pim/otimezone.h \ pim/odatebookaccess.h \ pim/odatebookaccessbackend.h \ pim/odatebookaccessbackend_xml.h \ orecurrancewidget.h \ oticker.h owait.h SOURCES = ofontmenu.cc \ ocolorbutton.cpp \ sharp_compat.cpp \ xmltree.cc \ ofiledialog.cc ofileselector.cpp \ ocheckitem.cpp \ colordialog.cpp \ colorpopupmenu.cpp oclickablelabel.cpp \ oprocctrl.cpp oprocess.cpp \ odevice.cpp odevicebutton.cpp otimepicker.cpp \ otabwidget.cpp otabbar.cpp \ ofontselector.cpp \ pim/otodo.cpp \ pim/opimrecord.cpp \ pim/otodoaccess.cpp \ pim/otodoaccessbackend.cpp \ pim/otodoaccessxml.cpp \ pim/oconversion.cpp \ pim/ocontact.cpp \ pim/ocontactfields.cpp \ pim/ocontactaccess.cpp \ pim/ocontactaccessbackend_vcard.cpp \ pim/ocontactaccessbackend_xml.cpp \ pim/otodoaccessvcal.cpp \ pim/orecur.cpp \ pim/opimstate.cpp \ pim/opimxrefpartner.cpp \ pim/opimxref.cpp \ pim/opimxrefmanager.cpp \ pim/opimmaintainer.cpp \ pim/opimnotify.cpp \ pim/opimnotifymanager.cpp \ pim/opimmainwindow.cpp \ pim/opimresolver.cpp \ pim/oevent.cpp \ pim/otimezone.cpp \ pim/odatebookaccess.cpp \ pim/odatebookaccessbackend.cpp \ pim/odatebookaccessbackend_xml.cpp \ orecurrancewidget.cpp \ oticker.cpp owait.cpp TARGET = opie INCLUDEPATH += $(OPIEDIR)/include DESTDIR = $(OPIEDIR)/lib$(PROJMAK) LIBS += -lqpe # Add SQL-Support if selected by config (eilers) CONFTEST = $$system( echo $CONFIG_SQL_PIM_BACKEND ) contains( CONFTEST, y ){ DEFINES += __USE_SQL LIBS += -lopiedb2 -HEADERS += pim/otodoaccesssql.h pim/ocontactaccessbackend_sql.h -SOURCES += pim/otodoaccesssql.cpp pim/ocontactaccessbackend_sql.cpp +HEADERS += pim/otodoaccesssql.h pim/ocontactaccessbackend_sql.h pim/odatebookaccessbackend_sql.h +SOURCES += pim/otodoaccesssql.cpp pim/ocontactaccessbackend_sql.cpp pim/odatebookaccessbackend_sql.cpp } INTERFACES = otimepickerbase.ui orecurrancebase.ui TARGET = opie include ( big-screen/big-screen.pro ) include ( $(OPIEDIR)/include.pro ) diff --git a/libopie/pim/obackendfactory.h b/libopie/pim/obackendfactory.h index 3567687..761ab9a 100644 --- a/libopie/pim/obackendfactory.h +++ b/libopie/pim/obackendfactory.h @@ -1,176 +1,194 @@ /* * Class to manage Backends. * * 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. * ===================================================================== * ToDo: Use plugins * ===================================================================== * Version: $Id$ * ===================================================================== * History: * $Log$ + * Revision 1.9 2003/12/22 10:19:26 eilers + * Finishing implementation of sql-backend for datebook. But I have to + * port the PIM datebook application to use it, before I could debug the + * whole stuff. + * Thus, PIM-Database backend is finished, but highly experimental. And some + * parts are still generic. For instance, the "queryByExample()" methods are + * not (or not fully) implemented. Todo: custom-entries not stored. + * The big show stopper: matchRegExp() (needed by OpieSearch) needs regular + * expression search in the database, which is not supported by sqlite ! + * Therefore we need either an extended sqlite or a workaround which would + * be very slow and memory consuming.. + * * Revision 1.8 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... * * Revision 1.7 2003/08/01 12:30:16 eilers * Merging changes from BRANCH_1_0 to HEAD * * Revision 1.6.4.1 2003/06/30 14:34:19 eilers * Patches from Zecke: * Fixing and cleaning up extraMap handling * Adding d_ptr for binary compatibility in the future * * Revision 1.6 2003/04/13 18:07:10 zecke * More API doc * QString -> const QString& * QString = 0l -> QString::null * * Revision 1.5 2003/02/21 23:31:52 zecke * Add XML datebookresource * -clean up todoaccessxml header * -implement some more stuff in the oeven tester * -extend DefaultFactory to not crash and to use datebook * * -reading of OEvents is working nicely.. saving will be added * tomorrow * -fix spelling in ODateBookAcces * * Revision 1.4 2002/10/14 15:55:18 eilers * Redeactivate SQL.. ;) * * Revision 1.3 2002/10/10 17:08:58 zecke * The Cache is finally in place * I tested it with my todolist and it 'works' for 10.000 todos the hits are awesome ;) * The read ahead functionality does not make sense for XMLs backends because most of the stuff is already in memory. While using readahead on SQL makes things a lot faster.... * I still have to fully implement read ahead * This change is bic but sc * * Revision 1.2 2002/10/08 09:27:36 eilers * Fixed libopie.pro to include the new pim-API. * The SQL-Stuff is currently deactivated. Otherwise everyone who wants to * compile itself would need to install libsqlite, libopiesql... * Therefore, the backend currently uses XML only.. * * Revision 1.1 2002/10/07 17:35:01 eilers * added OBackendFactory for advanced backend access * * * ===================================================================== */ #ifndef OPIE_BACKENDFACTORY_H_ #define OPIE_BACKENDFACTORY_H_ #include <qstring.h> #include <qasciidict.h> #include <qpe/config.h> #include "otodoaccessxml.h" #include "ocontactaccessbackend_xml.h" #include "odatebookaccessbackend_xml.h" #ifdef __USE_SQL #include "otodoaccesssql.h" #include "ocontactaccessbackend_sql.h" +#include "odatebookaccessbackend_sql.h" #endif class OBackendPrivate; /** * This class is our factory. It will give us the default implementations * of at least Todolist, Contacts and Datebook. In the future this class will * allow users to switch the backend with ( XML->SQLite ) without the need * to recompile.# * This class as the whole PIM Api is making use of templates * * <pre> * OTodoAccessBackend* backend = OBackEndFactory<OTodoAccessBackend>::Default("todo", QString::null ); * backend->load(); * </pre> * * @author Stefan Eilers * @version 0.1 */ template<class T> class OBackendFactory { public: OBackendFactory() {}; enum BACKENDS { TODO, CONTACT, DATE }; /** * Returns a backend implementation for backendName * @param backendName the type of the backend * @param appName will be passed on to the backend */ static T* Default( const QString backendName, const QString& appName ){ // __asm__("int3"); Config config( "pimaccess" ); config.setGroup ( backendName ); QString backend = config.readEntry( "usebackend" ); qWarning("Selected backend for %s is: %s", backendName.latin1(), backend.latin1() ); QAsciiDict<int> dict ( 3 ); dict.setAutoDelete ( TRUE ); dict.insert( "todo", new int (TODO) ); dict.insert( "contact", new int (CONTACT) ); dict.insert( "datebook", new int(DATE) ); int *find = dict[ backendName ]; if (!find ) return 0; switch ( *find ){ case TODO: #ifdef __USE_SQL if ( backend == "sql" ) return (T*) new OTodoAccessBackendSQL(""); #else if ( backend == "sql" ) - qWarning ("OBackendFactory:: sql Backend not implemented! Using XML instead!"); + qWarning ("OBackendFactory:: sql Backend for TODO not implemented! Using XML instead!"); #endif return (T*) new OTodoAccessXML( appName ); case CONTACT: #ifdef __USE_SQL if ( backend == "sql" ) return (T*) new OContactAccessBackend_SQL(""); #else if ( backend == "sql" ) - qWarning ("OBackendFactory:: sql Backend not implemented! Using XML instead!"); + qWarning ("OBackendFactory:: sql Backend for CONTACT not implemented! Using XML instead!"); #endif return (T*) new OContactAccessBackend_XML( appName ); case DATE: +#ifdef __USE_SQL if ( backend == "sql" ) - qWarning("OBackendFactory:: sql Backend not implemented! Using XML instead!"); + return (T*) new ODateBookAccessBackend_SQL(""); +#else + if ( backend == "sql" ) + qWarning("OBackendFactory:: sql Backend for DATEBOOK not implemented! Using XML instead!"); +#endif return (T*) new ODateBookAccessBackend_XML( appName ); default: return NULL; } } private: OBackendPrivate* d; }; #endif diff --git a/libopie/pim/ocontactaccess.h b/libopie/pim/ocontactaccess.h index 9b0a719..bd6da40 100644 --- a/libopie/pim/ocontactaccess.h +++ b/libopie/pim/ocontactaccess.h @@ -1,181 +1,193 @@ /* * Class to manage the Contacts. * * Copyright (c) 2002 by Stefan Eilers (Eilers.Stefan@epost.de) * Copyright (c) 2002 by Holger Freyther (zecke@handhelds.org) * * ===================================================================== * 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. * ===================================================================== * ToDo: Define enum for query settings * ===================================================================== * Version: $Id$ * ===================================================================== * History: * $Log$ + * Revision 1.10 2003/12/22 10:19:26 eilers + * Finishing implementation of sql-backend for datebook. But I have to + * port the PIM datebook application to use it, before I could debug the + * whole stuff. + * Thus, PIM-Database backend is finished, but highly experimental. And some + * parts are still generic. For instance, the "queryByExample()" methods are + * not (or not fully) implemented. Todo: custom-entries not stored. + * The big show stopper: matchRegExp() (needed by OpieSearch) needs regular + * expression search in the database, which is not supported by sqlite ! + * Therefore we need either an extended sqlite or a workaround which would + * be very slow and memory consuming.. + * * Revision 1.9 2003/08/01 12:30:16 eilers * Merging changes from BRANCH_1_0 to HEAD * * Revision 1.8.2.1 2003/06/30 14:34:19 eilers * Patches from Zecke: * Fixing and cleaning up extraMap handling * Adding d_ptr for binary compatibility in the future * * Revision 1.8 2003/05/08 13:55:09 tille * search stuff * and match, toRichText & toShortText in oevent * * Revision 1.7 2003/04/13 18:07:10 zecke * More API doc * QString -> const QString& * QString = 0l -> QString::null * * Revision 1.6 2003/01/02 14:27:12 eilers * Improved query by example: Search by date is possible.. First step * for a today plugin for birthdays.. * * Revision 1.5 2002/11/13 14:14:51 eilers * Added sorted for Contacts.. * * Revision 1.4 2002/11/01 15:10:42 eilers * Added regExp-search in database for all fields in a contact. * * Revision 1.3 2002/10/16 10:52:40 eilers * Added some docu to the interface and now using the cache infrastucture by zecke.. :) * * Revision 1.2 2002/10/14 16:21:54 eilers * Some minor interface updates * * Revision 1.1 2002/09/27 17:11:44 eilers * Added API for accessing the Contact-Database ! It is compiling, but * please do not expect that anything is working ! * I will debug that stuff in the next time .. * Please read README_COMPILE for compiling ! * * ===================================================================== */ #ifndef _OCONTACTACCESS_H #define _OCONTACTACCESS_H #include <qobject.h> #include <qpe/qcopenvelope_qws.h> #include <qvaluelist.h> #include <qfileinfo.h> #include "ocontact.h" #include "ocontactaccessbackend.h" #include "opimaccesstemplate.h" /** * Class to access the contacts database. * This is just a frontend for the real database handling which is * done by the backend. * This class is used to access the Contacts on a system. This class as any OPIE PIM * class is backend independent. - + * @author Stefan Eilers, Holger Freyther * @see OPimAccessTemplate */ class OContactAccess: public QObject, public OPimAccessTemplate<OContact> { Q_OBJECT public: /** * Create Database with contacts (addressbook). * @param appname Name of application which wants access to the database * (i.e. "todolist") * @param filename The name of the database file. If not set, the default one * is used. * @param backend Pointer to an alternative Backend. If not set, we will use * the default backend. * @param handlesync If <b>true</b> the database stores the current state * automatically if it receives the signals <i>flush()</i> and <i>reload()</i> * which are used before and after synchronisation. If the application wants * to react itself, it should be disabled by setting it to <b>false</b> * @see OContactAccessBackend */ OContactAccess (const QString appname, const QString filename = 0l, OContactAccessBackend* backend = 0l, bool handlesync = true); ~OContactAccess (); /** Constants for query. * Use this constants to set the query parameters. * Note: <i>query_IgnoreCase</i> just make sense with one of the other attributes ! * @see queryByExample() */ enum QuerySettings { WildCards = 0x0001, IgnoreCase = 0x0002, RegExp = 0x0004, ExactMatch = 0x0008, MatchOne = 0x0010, // Only one Entry must match DateDiff = 0x0020, // Find all entries from today until given date DateYear = 0x0040, // The year matches DateMonth = 0x0080, // The month matches DateDay = 0x0100, // The day matches }; /** Return all Contacts in a sorted manner. * @param ascending true: Sorted in acending order. * @param sortOrder Currently not implemented. Just defined to stay compatible to otodoaccess * @param sortFilter Currently not implemented. Just defined to stay compatible to otodoaccess * @param cat Currently not implemented. Just defined to stay compatible to otodoaccess */ List sorted( bool ascending, int sortOrder, int sortFilter, int cat ) const; /** Return all possible settings. * @return All settings provided by the current backend * (i.e.: query_WildCards & query_IgnoreCase) */ const uint querySettings(); /** Check whether settings are correct. * @return <i>true</i> if the given settings are correct and possible. */ bool hasQuerySettings ( int querySettings ) const; /** * if the resource was changed externally. * You should use the signal instead of polling possible changes ! */ bool wasChangedExternally()const; /** Save contacts database. * Save is more a "commit". After calling this function, all changes are public available. * @return true if successful */ bool save(); signals: /* Signal is emitted if the database was changed. Therefore * we may need to reload to stay consistent. * @param which Pointer to the database who created this event. This pointer * is useful if an application has to handle multiple databases at the same time. * @see reload() */ void signalChanged ( const OContactAccess *which ); private: // class OContactAccessPrivate; // OContactAccessPrivate* d; OContactAccessBackend *m_backEnd; bool m_loading:1; private slots: void copMessage( const QCString &msg, const QByteArray &data ); private: class Private; Private *d; }; #endif diff --git a/libopie/pim/ocontactaccessbackend_sql.cpp b/libopie/pim/ocontactaccessbackend_sql.cpp index dd9dbde..a5be4c8 100644 --- a/libopie/pim/ocontactaccessbackend_sql.cpp +++ b/libopie/pim/ocontactaccessbackend_sql.cpp @@ -1,906 +1,918 @@ /* * 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.4 2003/12/22 10:19:26 eilers + * Finishing implementation of sql-backend for datebook. But I have to + * port the PIM datebook application to use it, before I could debug the + * whole stuff. + * Thus, PIM-Database backend is finished, but highly experimental. And some + * parts are still generic. For instance, the "queryByExample()" methods are + * not (or not fully) implemented. Todo: custom-entries not stored. + * The big show stopper: matchRegExp() (needed by OpieSearch) needs regular + * expression search in the database, which is not supported by sqlite ! + * Therefore we need either an extended sqlite or a workaround which would + * be very slow and memory consuming.. + * * 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; }; /** * a find query for custom elements */ class FindCustomQuery : public OSQLQuery { public: FindCustomQuery(int uid); FindCustomQuery(const QArray<int>& ); ~FindCustomQuery(); QString query()const; private: QString single()const; QString multi()const; QArray<int> m_uids; int m_uid; }; // We using three tables to store the information: // 1. addressbook : It contains General information about the contact (non custom) // 2. custom_data : Not official supported entries // All tables are connected by the uid of the contact. // Maybe I should add a table for meta-information ? CreateQuery::CreateQuery() : OSQLQuery() {} CreateQuery::~CreateQuery() {} QString CreateQuery::query()const { QString qu; #ifdef __STORE_HORIZONTAL_ qu += "create table addressbook( uid PRIMARY KEY "; QStringList fieldList = OContactFields::untrfields( false ); for ( QStringList::Iterator it = ++fieldList.begin(); it != fieldList.end(); ++it ){ qu += QString( ",\"%1\" VARCHAR(10)" ).arg( *it ); } qu += " );"; qu += "create table custom_data( uid INTEGER, id INTEGER, type VARCHAR, priority INTEGER, value VARCHAR, PRIMARY KEY /* identifier */ (uid, id) );"; #else qu += "create table addressbook( uid INTEGER, id INTEGER, type VARCHAR, priority INTEGER, value VARCHAR, PRIMARY KEY /* identifier */ (uid, id));"; qu += "create table custom_data( uid INTEGER, id INTEGER, type VARCHAR, priority INTEGER, value VARCHAR, PRIMARY KEY /* identifier */ (uid, id) );"; // qu += "create table dates( uid PRIMARY KEY, type, day, month, year, hour, minute, second );"; #endif // __STORE_HORIZONTAL_ return qu; } ClearQuery::ClearQuery() : OSQLQuery() {} ClearQuery::~ClearQuery() {} QString ClearQuery::query()const { QString qu = "drop table addressbook;"; qu += "drop table custom_data;"; // qu += "drop table dates;"; return qu; } LoadQuery::LoadQuery() : OSQLQuery() {} LoadQuery::~LoadQuery() {} QString LoadQuery::query()const { QString qu; #ifdef __STORE_HORIZONTAL_ qu += "select uid from addressbook"; #else # ifndef __USE_SUPERFAST_LOADQUERY qu += "select distinct uid from addressbook"; # else qu += "select uid from addressbook where type = 'Last Name'"; # endif // __USE_SUPERFAST_LOADQUERY #endif // __STORE_HORIZONTAL_ return qu; } InsertQuery::InsertQuery( const OContact& contact ) : OSQLQuery(), m_contact( contact ) { } InsertQuery::~InsertQuery() { } /* * converts from a OContact to a query */ QString InsertQuery::query()const{ #ifdef __STORE_HORIZONTAL_ QString qu; qu += "insert into addressbook VALUES( " + QString::number( m_contact.uid() ); // Get all information out of the contact-class // Remember: The category is stored in contactMap, too ! QMap<int, QString> contactMap = m_contact.toMap(); QStringList fieldList = OContactFields::untrfields( false ); QMap<QString, int> translate = OContactFields::untrFieldsToId(); for ( QStringList::Iterator it = ++fieldList.begin(); it != fieldList.end(); ++it ){ // Convert Column-String to Id and get value for this id.. // Hmmm.. Maybe not very cute solution.. int id = translate[*it]; switch ( id ){ case Qtopia::Birthday:{ // These entries should stored in a special format // year-month-day QDate day = m_contact.birthday(); if ( day.isValid() ){ qu += QString(",\"%1-%2-%3\"") .arg( day.year() ) .arg( day.month() ) .arg( day.day() ); } else { qu += ",\"\""; } } break; case Qtopia::Anniversary:{ // These entries should stored in a special format // year-month-day QDate day = m_contact.anniversary(); if ( day.isValid() ){ qu += QString(",\"%1-%2-%3\"") .arg( day.year() ) .arg( day.month() ) .arg( day.day() ); } else { qu += ",\"\""; } } break; default: qu += QString( ",\"%1\"" ).arg( contactMap[id] ); } } qu += " );"; #else // Get all information out of the contact-class // Remember: The category is stored in contactMap, too ! QMap<int, QString> contactMap = m_contact.toMap(); QMap<QString, QString> addressbook_db; // Get the translation from the ID to the String QMap<int, QString> transMap = OContactFields::idToUntrFields(); for( QMap<int, QString>::Iterator it = contactMap.begin(); it != contactMap.end(); ++it ){ switch ( it.key() ){ case Qtopia::Birthday:{ // These entries should stored in a special format // year-month-day QDate day = m_contact.birthday(); addressbook_db.insert( transMap[it.key()], QString("%1-%2-%3") .arg( day.year() ) .arg( day.month() ) .arg( day.day() ) ); } break; case Qtopia::Anniversary:{ // These entries should stored in a special format // year-month-day QDate day = m_contact.anniversary(); addressbook_db.insert( transMap[it.key()], QString("%1-%2-%3") .arg( day.year() ) .arg( day.month() ) .arg( day.day() ) ); } break; case Qtopia::AddressUid: // Ignore UID break; default: // Translate id to String addressbook_db.insert( transMap[it.key()], it.data() ); break; } } // 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 ): 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(); + return m_driver->close(); // Shouldn't m_driver->sync be better than close ? (eilers) } void OContactAccessBackend_SQL::clear () { ClearQuery cle; OSQLResult res = m_driver->query( &cle ); - CreateQuery qu; - res = m_driver->query(&qu); + + reload(); } 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 " + "'" + queryStr.replace(QRegExp("\\*"),"%") + "'" + ") AND "; else qu += "(\"" + *it + "\"" + " GLOB " + "'" + queryStr + "'" + ") AND "; } } } // Skip trailing "AND" if ( isAnyFieldSelected ) qu = qu.left( qu.length() - 4 ); qWarning( "queryByExample query: %s", qu.latin1() ); // Execute query and return the received uid's OSQLRawQuery raw( qu ); OSQLResult res = m_driver->query( &raw ); if ( res.state() != OSQLResult::Success ){ QArray<int> empty; return empty; } QArray<int> list = extractUids( res ); return list; } QArray<int> OContactAccessBackend_SQL::matchRegexp( const QRegExp &r ) const { QArray<int> nix(0); return nix; } const uint OContactAccessBackend_SQL::querySettings() { return OContactAccess::IgnoreCase || OContactAccess::WildCards; } bool OContactAccessBackend_SQL::hasQuerySettings (uint querySettings) const { /* OContactAccess::IgnoreCase, DateDiff, DateYear, DateMonth, DateDay * may be added with any of the other settings. IgnoreCase should never used alone. * Wildcards, RegExp, ExactMatch should never used at the same time... */ // Step 1: Check whether the given settings are supported by this backend if ( ( querySettings & ( OContactAccess::IgnoreCase | OContactAccess::WildCards // | OContactAccess::DateDiff // | OContactAccess::DateYear // | OContactAccess::DateMonth // | OContactAccess::DateDay // | OContactAccess::RegExp // | OContactAccess::ExactMatch ) ) != querySettings ) return false; // Step 2: Check whether the given combinations are ok.. // IngoreCase alone is invalid if ( querySettings == OContactAccess::IgnoreCase ) return false; // WildCards, RegExp and ExactMatch should never used at the same time switch ( querySettings & ~( OContactAccess::IgnoreCase | OContactAccess::DateDiff | OContactAccess::DateYear | OContactAccess::DateMonth | OContactAccess::DateDay ) ){ case OContactAccess::RegExp: return ( true ); case OContactAccess::WildCards: return ( true ); case OContactAccess::ExactMatch: return ( true ); case 0: // one of the upper removed bits were set.. return ( true ); default: return ( false ); } } QArray<int> OContactAccessBackend_SQL::sorted( bool asc, int , int , int ) { QTime t; t.start(); #ifdef __STORE_HORIZONTAL_ QString query = "SELECT uid FROM addressbook "; query += "ORDER BY \"Last Name\" "; #else QString query = "SELECT uid FROM addressbook WHERE type = 'Last Name' "; query += "ORDER BY upper( value )"; #endif if ( !asc ) query += "DESC"; // qWarning("sorted query is: %s", query.latin1() ); OSQLRawQuery raw( query ); OSQLResult res = m_driver->query( &raw ); if ( res.state() != OSQLResult::Success ){ QArray<int> empty; return empty; } QArray<int> list = extractUids( res ); qWarning("sorted needed %d ms!", t.elapsed() ); return list; } void OContactAccessBackend_SQL::update() { qWarning("Update starts"); QTime t; t.start(); // Now load the database set and extract the uid's // which will be held locally LoadQuery lo; OSQLResult res = m_driver->query(&lo); if ( res.state() != OSQLResult::Success ) return; m_uids = extractUids( res ); m_changed = false; qWarning("Update ends %d ms", t.elapsed() ); } QArray<int> OContactAccessBackend_SQL::extractUids( OSQLResult& res ) const { qWarning("extractUids"); QTime t; t.start(); 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++; } qWarning("extractUids ready: count2 = %d needs %d ms", i, t.elapsed() ); return ints; } #ifdef __STORE_HORIZONTAL_ QMap<int, QString> OContactAccessBackend_SQL::requestNonCustom( int uid ) const { QTime t; t.start(); QMap<int, QString> nonCustomMap; int t2needed = 0; int t3needed = 0; QTime t2; t2.start(); FindQuery query( uid ); OSQLResult res_noncustom = m_driver->query( &query ); t2needed = t2.elapsed(); OSQLResultItem resItem = res_noncustom.first(); QTime t3; t3.start(); // Now loop through all columns QStringList fieldList = OContactFields::untrfields( false ); QMap<QString, int> translate = OContactFields::untrFieldsToId(); for ( QStringList::Iterator it = ++fieldList.begin(); it != fieldList.end(); ++it ){ // Get data for the selected column and store it with the // corresponding id into the map.. int id = translate[*it]; QString value = resItem.data( (*it) ); // qWarning("Reading %s... found: %s", (*it).latin1(), value.latin1() ); switch( id ){ case Qtopia::Birthday: case Qtopia::Anniversary:{ // Birthday and Anniversary are encoded special ( yyyy-mm-dd ) QStringList list = QStringList::split( '-', value ); QStringList::Iterator lit = list.begin(); int year = (*lit).toInt(); int month = (*(++lit)).toInt(); int day = (*(++lit)).toInt(); if ( ( day != 0 ) && ( month != 0 ) && ( year != 0 ) ){ QDate date( year, month, day ); nonCustomMap.insert( id, OConversion::dateToString( date ) ); } } break; case Qtopia::AddressCategory: qWarning("Category is: %s", value.latin1() ); default: nonCustomMap.insert( id, value ); } } // First insert uid nonCustomMap.insert( Qtopia::AddressUid, resItem.data( "uid" ) ); t3needed = t3.elapsed(); // qWarning("Adding UID: %s", resItem.data( "uid" ).latin1() ); qWarning("RequestNonCustom needed: insg.:%d ms, query: %d ms, mapping: %d ms", t.elapsed(), t2needed, t3needed ); return nonCustomMap; } #else QMap<int, QString> OContactAccessBackend_SQL::requestNonCustom( int uid ) const { QTime t; t.start(); QMap<int, QString> nonCustomMap; int t2needed = 0; QTime t2; t2.start(); FindQuery query( uid ); OSQLResult res_noncustom = m_driver->query( &query ); t2needed = t2.elapsed(); if ( res_noncustom.state() == OSQLResult::Failure ) { qWarning("OSQLResult::Failure in find query !!"); QMap<int, QString> empty; return empty; } int t3needed = 0; QTime t3; t3.start(); QMap<QString, int> translateMap = OContactFields::untrFieldsToId(); OSQLResultItem::ValueList list = res_noncustom.results(); OSQLResultItem::ValueList::Iterator it = list.begin(); for ( ; it != list.end(); ++it ) { if ( (*it).data("type") != "" ){ int typeId = translateMap[(*it).data( "type" )]; switch( typeId ){ case Qtopia::Birthday: case Qtopia::Anniversary:{ // Birthday and Anniversary are encoded special ( yyyy-mm-dd ) QStringList list = QStringList::split( '-', (*it).data( "value" ) ); QStringList::Iterator lit = list.begin(); int year = (*lit).toInt(); qWarning("1. %s", (*lit).latin1()); int month = (*(++lit)).toInt(); qWarning("2. %s", (*lit).latin1()); int day = (*(++lit)).toInt(); qWarning("3. %s", (*lit).latin1()); qWarning( "RequestNonCustom->Converting:%s to Year: %d, Month: %d, Day: %d ", (*it).data( "value" ).latin1(), year, month, day ); QDate date( year, month, day ); nonCustomMap.insert( typeId, OConversion::dateToString( date ) ); } break; default: nonCustomMap.insert( typeId, (*it).data( "value" ) ); } } } // Add UID to Map.. nonCustomMap.insert( Qtopia::AddressUid, QString::number( uid ) ); t3needed = t3.elapsed(); qWarning("RequestNonCustom needed: insg.:%d ms, query: %d ms, mapping: %d ms", t.elapsed(), t2needed, t3needed ); return nonCustomMap; } #endif // __STORE_HORIZONTAL_ QMap<QString, QString> OContactAccessBackend_SQL::requestCustom( int uid ) const diff --git a/libopie/pim/odatebookaccess.cpp b/libopie/pim/odatebookaccess.cpp index a3661a3..82934f9 100644 --- a/libopie/pim/odatebookaccess.cpp +++ b/libopie/pim/odatebookaccess.cpp @@ -1,66 +1,81 @@ #include "obackendfactory.h" #include "odatebookaccess.h" /** * Simple constructor * It takes a ODateBookAccessBackend as parent. If it is 0 the default implementation * will be used! * @param back The backend to be used or 0 for the default backend * @param ac What kind of access is intended */ ODateBookAccess::ODateBookAccess( ODateBookAccessBackend* back, enum Access ac ) : OPimAccessTemplate<OEvent>( back ) { if (!back ) back = OBackendFactory<ODateBookAccessBackend>::Default("datebook", QString::null ); m_backEnd = back; setBackEnd( m_backEnd ); } ODateBookAccess::~ODateBookAccess() { } /** * @return all events available */ ODateBookAccess::List ODateBookAccess::rawEvents()const { QArray<int> ints = m_backEnd->rawEvents(); List lis( ints, this ); return lis; } /** * @return all repeating events */ ODateBookAccess::List ODateBookAccess::rawRepeats()const { QArray<int> ints = m_backEnd->rawRepeats(); List lis( ints, this ); return lis; } /** * @return all non repeating events */ ODateBookAccess::List ODateBookAccess::nonRepeats()const { QArray<int> ints = m_backEnd->nonRepeats(); List lis( ints, this ); return lis; } /** * @return dates in the time span between from and to * @param from Include all events from... * @param to Include all events to... */ OEffectiveEvent::ValueList ODateBookAccess::effectiveEvents( const QDate& from, const QDate& to ) { - return m_backEnd->effecticeEvents( from, to ); + return m_backEnd->effectiveEvents( from, to ); } /** * @return all events at a given datetime */ OEffectiveEvent::ValueList ODateBookAccess::effectiveEvents( const QDateTime& start ) { - return m_backEnd->effecticeEvents( start ); + return m_backEnd->effectiveEvents( start ); +} + +/** + * @return non repeating dates in the time span between from and to + * @param from Include all events from... + * @param to Include all events to... + */ +OEffectiveEvent::ValueList ODateBookAccess::effectiveNonRepeatingEvents( const QDate& from, const QDate& to ) { + return m_backEnd->effectiveNonRepeatingEvents( from, to ); +} +/** + * @return all non repeating events at a given datetime + */ +OEffectiveEvent::ValueList ODateBookAccess::effectiveNonRepeatingEvents( const QDateTime& start ) { + return m_backEnd->effectiveNonRepeatingEvents( start ); } diff --git a/libopie/pim/odatebookaccess.h b/libopie/pim/odatebookaccess.h index 7c7a63f..62196da 100644 --- a/libopie/pim/odatebookaccess.h +++ b/libopie/pim/odatebookaccess.h @@ -1,41 +1,44 @@ #ifndef OPIE_DATE_BOOK_ACCESS_H #define OPIE_DATE_BOOK_ACCESS_H #include "odatebookaccessbackend.h" #include "opimaccesstemplate.h" #include "oevent.h" /** * This is the object orientated datebook database. It'll use OBackendFactory * to query for a backend. * All access to the datebook should be done via this class. * Make sure to load and save the datebook this is not part of * destructing and creating the object * - * @author Holger Freyther + * @author Holger Freyther, Stefan Eilers */ class ODateBookAccess : public OPimAccessTemplate<OEvent> { public: ODateBookAccess( ODateBookAccessBackend* = 0l, enum Access acc = Random ); ~ODateBookAccess(); /* return all events */ List rawEvents()const; /* return repeating events */ List rawRepeats()const; /* return non repeating events */ List nonRepeats()const; - OEffectiveEvent::ValueList effectiveEvents( const QDate& from, const QDate& to ); - OEffectiveEvent::ValueList effectiveEvents( const QDateTime& start ); + /* return non repeating events (from,to) */ + OEffectiveEvent::ValueList effectiveEvents( const QDate& from, const QDate& to ) const; + OEffectiveEvent::ValueList effectiveEvents( const QDateTime& start ) const; + OEffectiveEvent::ValueList effectiveNonRepeatingEvents( const QDate& from, const QDate& to ) const; + OEffectiveEvent::ValueList effectiveNonRepeatingEvents( const QDateTime& start ) const; private: ODateBookAccessBackend* m_backEnd; class Private; Private* d; }; #endif diff --git a/libopie/pim/odatebookaccessbackend.cpp b/libopie/pim/odatebookaccessbackend.cpp index 8fa1a68..f0c5d65 100644 --- a/libopie/pim/odatebookaccessbackend.cpp +++ b/libopie/pim/odatebookaccessbackend.cpp @@ -1,156 +1,182 @@ #include <qtl.h> #include "orecur.h" #include "odatebookaccessbackend.h" namespace { /* a small helper to get all NonRepeating events for a range of time */ void events( OEffectiveEvent::ValueList& tmpList, const OEvent::ValueList& events, const QDate& from, const QDate& to ) { QDateTime dtStart, dtEnd; for ( OEvent::ValueList::ConstIterator it = events.begin(); it != events.end(); ++it ) { dtStart = (*it).startDateTime(); dtEnd = (*it).endDateTime(); /* * If in range */ if (dtStart.date() >= from && dtEnd.date() <= to ) { OEffectiveEvent eff; eff.setEvent( (*it) ); eff.setDate( dtStart.date() ); eff.setStartTime( dtStart.time() ); /* if not on the same day */ if ( dtStart.date() != dtEnd.date() ) eff.setEndTime( QTime(23, 59, 0 ) ); else eff.setEndTime( dtEnd.time() ); tmpList.append( eff ); } /* we must also check for end date information... */ if ( dtEnd.date() != dtStart.date() && dtEnd.date() >= from ) { QDateTime dt = dtStart.addDays( 1 ); dt.setTime( QTime(0, 0, 0 ) ); QDateTime dtStop; if ( dtEnd > to ) dtStop = to; else dtStop = dtEnd; while ( dt <= dtStop ) { OEffectiveEvent eff; eff.setEvent( (*it) ); eff.setDate( dt.date() ); if ( dt >= from ) { eff.setStartTime( QTime(0, 0, 0 ) ); if ( dt.date() == dtEnd.date() ) eff.setEndTime( dtEnd.time() ); else eff.setEndTime( QTime(23, 59, 0 ) ); tmpList.append( eff ); } dt = dt.addDays( 1 ); } } } } void repeat( OEffectiveEvent::ValueList& tmpList, const OEvent::ValueList& list, const QDate& from, const QDate& to ) { QDate repeat; for ( OEvent::ValueList::ConstIterator it = list.begin(); it != list.end(); ++it ) { int dur = (*it).startDateTime().date().daysTo( (*it).endDateTime().date() ); QDate itDate = from.addDays(-dur ); ORecur rec = (*it).recurrence(); if ( !rec.hasEndDate() || rec.endDate() > to ) { rec.setEndDate( to ); rec.setHasEndDate( true ); } while (rec.nextOcurrence(itDate, repeat ) ) { if (repeat > to ) break; OEffectiveEvent eff; eff.setDate( repeat ); if ( (*it).isAllDay() ) { eff.setStartTime( QTime(0, 0, 0 ) ); eff.setEndTime( QTime(23, 59, 59 ) ); }else { /* we only occur by days, not hours/minutes/seconds. Hence * the actual end and start times will be the same for * every repeated event. For multi day events this is * fixed up later if on wronge day span */ eff.setStartTime( (*it).startDateTime().time() ); eff.setEndTime( (*it).endDateTime().time() ); } if ( dur != 0 ) { // multi-day repeating events QDate sub_it = QMAX( repeat, from ); QDate startDate = repeat; QDate endDate = startDate.addDays( dur ); while ( sub_it <= endDate && sub_it <= to ) { OEffectiveEvent tmpEff = eff; tmpEff.setEvent( (*it) ); if ( sub_it != startDate ) tmpEff.setStartTime( QTime(0, 0, 0 ) ); if ( sub_it != endDate ) tmpEff.setEndTime( QTime( 23, 59, 59 ) ); tmpEff.setDate( sub_it ); tmpEff.setEffectiveDates( startDate, endDate ); tmpList.append( tmpEff ); sub_it = sub_it.addDays( 1 ); } itDate = endDate; }else { eff.setEvent( (*it) ); tmpList.append( eff ); itDate = repeat.addDays( 1 ); } } } } } ODateBookAccessBackend::ODateBookAccessBackend() : OPimAccessBackend<OEvent>() { } ODateBookAccessBackend::~ODateBookAccessBackend() { } -OEffectiveEvent::ValueList ODateBookAccessBackend::effecticeEvents( const QDate& from, +OEffectiveEvent::ValueList ODateBookAccessBackend::effectiveEvents( const QDate& from, const QDate& to ) { OEffectiveEvent::ValueList tmpList; OEvent::ValueList list = directNonRepeats(); events( tmpList, list, from, to ); repeat( tmpList, directRawRepeats(),from,to ); - list = directRawRepeats(); + list = directRawRepeats(); // Useless, isn't it ? (eilers) qHeapSort( tmpList ); return tmpList; } -OEffectiveEvent::ValueList ODateBookAccessBackend::effecticeEvents( const QDateTime& dt ) { - OEffectiveEvent::ValueList day = effecticeEvents( dt.date(), dt.date() ); +OEffectiveEvent::ValueList ODateBookAccessBackend::effectiveEvents( const QDateTime& dt ) { + OEffectiveEvent::ValueList day = effectiveEvents( dt.date(), dt.date() ); + OEffectiveEvent::ValueList::Iterator it; + + OEffectiveEvent::ValueList tmpList; + QDateTime dtTmp; + for ( it = day.begin(); it != day.end(); ++it ) { + dtTmp = QDateTime( (*it).date(), (*it).startTime() ); + if ( QABS(dt.secsTo(dtTmp) ) < 60 ) + tmpList.append( (*it) ); + } + + return tmpList; +} + +OEffectiveEvent::ValueList ODateBookAccessBackend::effectiveNonRepeatingEvents( const QDate& from, + const QDate& to ) { + OEffectiveEvent::ValueList tmpList; + OEvent::ValueList list = directNonRepeats(); + + events( tmpList, list, from, to ); + + qHeapSort( tmpList ); + return tmpList; +} + +OEffectiveEvent::ValueList ODateBookAccessBackend::effectiveNonRepeatingEvents( const QDateTime& dt ) { + OEffectiveEvent::ValueList day = effectiveNonRepeatingEvents( dt.date(), dt.date() ); OEffectiveEvent::ValueList::Iterator it; OEffectiveEvent::ValueList tmpList; QDateTime dtTmp; for ( it = day.begin(); it != day.end(); ++it ) { dtTmp = QDateTime( (*it).date(), (*it).startTime() ); if ( QABS(dt.secsTo(dtTmp) ) < 60 ) tmpList.append( (*it) ); } return tmpList; } diff --git a/libopie/pim/odatebookaccessbackend.h b/libopie/pim/odatebookaccessbackend.h index 3c02c42..3472ab3 100644 --- a/libopie/pim/odatebookaccessbackend.h +++ b/libopie/pim/odatebookaccessbackend.h @@ -1,77 +1,90 @@ #ifndef OPIE_DATE_BOOK_ACCESS_BACKEND_H #define OPIE_DATE_BOOK_ACCESS_BACKEND_H #include <qarray.h> #include "opimaccessbackend.h" #include "oevent.h" /** * This class is the interface to the storage of Events. * @see OPimAccessBackend * */ class ODateBookAccessBackend : public OPimAccessBackend<OEvent> { public: typedef int UID; /** * c'tor without parameter */ ODateBookAccessBackend(); ~ODateBookAccessBackend(); /** * This method should return a list of UIDs containing * all events. No filter should be applied * @return list of events */ virtual QArray<UID> rawEvents()const = 0; /** * This method should return a list of UIDs containing * all repeating events. No filter should be applied * @return list of repeating events */ virtual QArray<UID> rawRepeats()const = 0; /** * This mthod should return a list of UIDs containing all non * repeating events. No filter should be applied * @return list of nonrepeating events */ virtual QArray<UID> nonRepeats() const = 0; /** * If you do not want to implement the effectiveEvents methods below * you need to supply it with directNonRepeats. * This method can return empty lists if effectiveEvents is implememted */ virtual OEvent::ValueList directNonRepeats() = 0; /** * Same as above but return raw repeats! */ virtual OEvent::ValueList directRawRepeats() = 0; /* is implemented by default but you can reimplement it*/ /** * Effective Events are special event occuring during a time frame. This method does calcualte * EffectiveEvents bases on the directNonRepeats and directRawRepeats. You may implement this method * yourself */ - virtual OEffectiveEvent::ValueList effecticeEvents( const QDate& from, const QDate& to ); + virtual OEffectiveEvent::ValueList effectiveEvents( const QDate& from, const QDate& to ); /** * this is an overloaded member function - * @see effecticeEvents + * @see effectiveEvents( const QDate& from, const QDate& to ) */ - virtual OEffectiveEvent::ValueList effecticeEvents( const QDateTime& start ); + virtual OEffectiveEvent::ValueList effectiveEvents( const QDateTime& start ); + + /** + * Effective Events are special event occuring during a time frame. This method does calcualte + * EffectiveEvents bases on the directNonRepeats and directRawRepeats. You may implement this method + * yourself + */ + virtual OEffectiveEvent::ValueList effectiveNonRepeatingEvents( const QDate& from, const QDate& to ); + + /** + * this is an overloaded member function + * @see effectiveNonRepeatingEvents( const QDate& from, const QDate& to ) + */ + virtual OEffectiveEvent::ValueList effectiveNonRepeatingEvents( const QDateTime& start ); private: class Private; Private *d; }; #endif diff --git a/libopie/pim/odatebookaccessbackend_sql.cpp b/libopie/pim/odatebookaccessbackend_sql.cpp index 9769bf7..e893b38 100644 --- a/libopie/pim/odatebookaccessbackend_sql.cpp +++ b/libopie/pim/odatebookaccessbackend_sql.cpp @@ -1,221 +1,356 @@ /* * 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.2 2003/12/22 10:19:26 eilers + * Finishing implementation of sql-backend for datebook. But I have to + * port the PIM datebook application to use it, before I could debug the + * whole stuff. + * Thus, PIM-Database backend is finished, but highly experimental. And some + * parts are still generic. For instance, the "queryByExample()" methods are + * not (or not fully) implemented. Todo: custom-entries not stored. + * The big show stopper: matchRegExp() (needed by OpieSearch) needs regular + * expression search in the database, which is not supported by sqlite ! + * Therefore we need either an extended sqlite or a workaround which would + * be very slow and memory consuming.. + * * 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 <qpe/global.h> #include <opie2/osqldriver.h> -#include <opie2/osqlresult.h> #include <opie2/osqlmanager.h> #include <opie2/osqlquery.h> -namespace { - +#include "orecur.h" +#include "odatebookaccessbackend_sql.h" -}; ODateBookAccessBackend_SQL::ODateBookAccessBackend_SQL( const QString& , const QString& fileName ) : ODateBookAccessBackend(), m_driver( NULL ) { - m_fileName = fileName.isEmpty() ? Global::applicationFileName( "datebook", "datebook.db" ) : fileName; + 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() { + if( m_driver ) + delete m_driver; } 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::FRExceptions, "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" ); + + // Create a map that maps the column name to the id + QMapConstIterator<int, QString> it; + for ( it = ++m_fieldMap.begin(); it != m_fieldMap.end(); ++it ){ + m_reverseFieldMap.insert( it.data(), it.key() ); + } + } 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 += 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) );"; + qWarning( "command: %s", qu.latin1() ); + 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(); + // 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(); + return load(); } bool ODateBookAccessBackend_SQL::save() { - return m_driver->close(); + return m_driver->close(); // Shouldn't m_driver->sync be better than close ? (eilers) } QArray<int> ODateBookAccessBackend_SQL::allRecords()const { - return m_uids; + return m_uids; } QArray<int> ODateBookAccessBackend_SQL::queryByExample(const OEvent&, int, const QDateTime& ) { - return QArray<int>(); + 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 ); + reload(); } OEvent ODateBookAccessBackend_SQL::find( int uid ) const{ + QString qu = "select *"; + qu += "from datebook where uid = " + QString::number(uid); + + OSQLRawQuery raw( qu ); + OSQLResult res = m_driver->query( &raw ); + + OSQLResultItem resItem = res.first(); + + // Create Map for date event and insert UID + QMap<int,QString> dateEventMap; + dateEventMap.insert( OEvent::FUid, QString::number( uid ) ); + + // Now insert the data out of the columns into the map. + QMapConstIterator<int, QString> it; + for ( it = ++m_fieldMap.begin(); it != m_fieldMap.end(); ++it ){ + dateEventMap.insert( m_reverseFieldMap[*it], resItem.data( *it ) ); + } + + // Last step: Put map into date event and return it + OEvent retDate( dateEventMap ); + + return retDate; } -bool ODateBookAccessBackend_SQL::add( const OEvent& ev ) { - return true; +bool ODateBookAccessBackend_SQL::add( const OEvent& ev ) +{ + QMap<int,QString> eventMap = ev.toMap(); + + QString qu = "insert into datebook VALUES( " + QString::number( ev.uid() ); + QMap<int, QString>::Iterator it; + for ( it = ++m_fieldMap.begin(); it != m_fieldMap.end(); ++it ){ + if ( !eventMap[it.key()].isEmpty() ) + qu += QString( ",\"%1\"" ).arg( eventMap[it.key()] ); + else + qu += QString( ",\"\"" ); + } + qu += " );"; + + // Add custom entries + int id = 0; + QMap<QString, QString> customMap = ev.toExtraMap(); + for( QMap<QString, QString>::Iterator it = customMap.begin(); + it != customMap.end(); ++it ){ + qu += "insert into custom_data VALUES(" + + QString::number( ev.uid() ) + + "," + + QString::number( id++ ) + + ",'" + + it.key() //.latin1() + + "'," + + "0" // Priority for future enhancements + + ",'" + + it.data() //.latin1() + + "');"; + } + qWarning("add %s", qu.latin1() ); + + OSQLRawQuery raw( qu ); + OSQLResult res = m_driver->query( &raw ); + if ( res.state() != OSQLResult::Success ){ + return false; + } + + return true; } -bool ODateBookAccessBackend_SQL::remove( int uid ) { - return true; +bool ODateBookAccessBackend_SQL::remove( int uid ) +{ + QString qu = "DELETE from datebook where uid = " + + QString::number( uid ) + ";"; + qu += "DELETE from custom_data where uid = " + + QString::number( uid ) + ";"; + + OSQLRawQuery raw( qu ); + OSQLResult res = m_driver->query( &raw ); + if ( res.state() != OSQLResult::Success ){ + return false; + } + + return true; } -bool ODateBookAccessBackend_SQL::replace( const OEvent& ev ) { + +bool ODateBookAccessBackend_SQL::replace( const OEvent& ev ) +{ remove( ev.uid() ); return add( ev ); } -QArray<int> ODateBookAccessBackend_SQL::rawEvents()const { + +QArray<int> ODateBookAccessBackend_SQL::rawEvents()const +{ return allRecords(); } -QArray<int> ODateBookAccessBackend_SQL::rawRepeats()const { - return ints; +QArray<int> ODateBookAccessBackend_SQL::rawRepeats()const +{ + QString qu = "select uid from datebook where RType!=\"\" AND RType!=\"NoRepeat\""; + OSQLRawQuery raw( qu ); + OSQLResult res = m_driver->query( &raw ); + if ( res.state() != OSQLResult::Success ){ + QArray<int> nix; + return nix; + } + + return extractUids( res ); } -QArray<int> ODateBookAccessBackend_SQL::nonRepeats()const { - return ints; +QArray<int> ODateBookAccessBackend_SQL::nonRepeats()const +{ + QString qu = "select uid from datebook where RType=\"\" or RType=\"NoRepeat\""; + OSQLRawQuery raw( qu ); + OSQLResult res = m_driver->query( &raw ); + if ( res.state() != OSQLResult::Success ){ + QArray<int> nix; + return nix; + } + + return extractUids( res ); } -OEvent::ValueList ODateBookAccessBackend_SQL::directNonRepeats() { - return list; +OEvent::ValueList ODateBookAccessBackend_SQL::directNonRepeats() +{ + QArray<int> nonRepUids = nonRepeats(); + OEvent::ValueList list; + + for (uint i = 0; i < nonRepUids.count(); ++i ){ + list.append( find( nonRepUids[i] ) ); + } + + return list; + } -OEvent::ValueList ODateBookAccessBackend_SQL::directRawRepeats() { +OEvent::ValueList ODateBookAccessBackend_SQL::directRawRepeats() +{ + QArray<int> rawRepUids = rawRepeats(); + OEvent::ValueList list; - return list; + for (uint i = 0; i < rawRepUids.count(); ++i ){ + list.append( find( rawRepUids[i] ) ); + } + + return list; } QArray<int> ODateBookAccessBackend_SQL::matchRegexp( const QRegExp &r ) const { + QArray<int> null; + return null; +} + +/* ===== Private Functions ========================================== */ + +QArray<int> ODateBookAccessBackend_SQL::extractUids( OSQLResult& res ) const +{ + qWarning("extractUids"); + QTime t; + t.start(); + 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++; + } + qWarning("extractUids ready: count2 = %d needs %d ms", i, t.elapsed() ); + + return ints; - return m_currentQuery; } diff --git a/libopie/pim/odatebookaccessbackend_sql.h b/libopie/pim/odatebookaccessbackend_sql.h index 85e0d4f..f39e154 100644 --- a/libopie/pim/odatebookaccessbackend_sql.h +++ b/libopie/pim/odatebookaccessbackend_sql.h @@ -1,60 +1,62 @@ #ifndef OPIE_DATE_BOOK_ACCESS_BACKEND_SQL__H #define OPIE_DATE_BOOK_ACCESS_BACKEND_SQL__H #include <qmap.h> +#include <opie2/osqlresult.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; + QMap<QString, int> m_reverseFieldMap; OSQLDriver* m_driver; class Private; Private *d; void initFields(); void update(); QArray<int> extractUids( OSQLResult& res ) const; }; #endif diff --git a/libopie/pim/oevent.cpp b/libopie/pim/oevent.cpp index ec05e77..9b31957 100644 --- a/libopie/pim/oevent.cpp +++ b/libopie/pim/oevent.cpp @@ -1,704 +1,717 @@ #include <qshared.h> #include <qarray.h> #include <qpe/palmtopuidgen.h> #include <qpe/categories.h> #include <qpe/stringutil.h> #include "orecur.h" #include "opimresolver.h" #include "opimnotifymanager.h" #include "oevent.h" int OCalendarHelper::week( const QDate& date) { // Calculates the week this date is in within that // month. Equals the "row" is is in in the month view int week = 1; QDate tmp( date.year(), date.month(), 1 ); if ( date.dayOfWeek() < tmp.dayOfWeek() ) ++week; week += ( date.day() - 1 ) / 7; return week; } int OCalendarHelper::ocurrence( const QDate& date) { // calculates the number of occurrances of this day of the // week till the given date (e.g 3rd Wednesday of the month) return ( date.day() - 1 ) / 7 + 1; } int OCalendarHelper::dayOfWeek( char day ) { int dayOfWeek = 1; char i = ORecur::MON; while ( !( i & day ) && i <= ORecur::SUN ) { i <<= 1; ++dayOfWeek; } return dayOfWeek; } int OCalendarHelper::monthDiff( const QDate& first, const QDate& second ) { return ( second.year() - first.year() ) * 12 + second.month() - first.month(); } struct OEvent::Data : public QShared { Data() : QShared() { child = 0; recur = 0; manager = 0; isAllDay = false; parent = 0; } ~Data() { delete manager; delete recur; } QString description; QString location; OPimNotifyManager* manager; ORecur* recur; QString note; QDateTime created; QDateTime start; QDateTime end; bool isAllDay : 1; QString timezone; QArray<int>* child; int parent; }; OEvent::OEvent( int uid ) : OPimRecord( uid ) { data = new Data; } OEvent::OEvent( const OEvent& ev) : OPimRecord( ev ), data( ev.data ) { data->ref(); } + +OEvent::OEvent( const QMap<int, QString> map ) + : OPimRecord( 0 ) +{ + data = new Data; + + fromMap( map ); +} + OEvent::~OEvent() { if ( data->deref() ) { delete data; data = 0; } } OEvent& OEvent::operator=( const OEvent& ev) { if ( this == &ev ) return *this; OPimRecord::operator=( ev ); ev.data->ref(); deref(); data = ev.data; return *this; } QString OEvent::description()const { return data->description; } void OEvent::setDescription( const QString& description ) { changeOrModify(); data->description = description; } void OEvent::setLocation( const QString& loc ) { changeOrModify(); data->location = loc; } QString OEvent::location()const { return data->location; } OPimNotifyManager &OEvent::notifiers()const { // I hope we can skip the changeOrModify here // the notifier should take care of it // and OPimNotify is shared too if (!data->manager ) data->manager = new OPimNotifyManager; return *data->manager; } bool OEvent::hasNotifiers()const { if (!data->manager ) return false; if (data->manager->reminders().isEmpty() && data->manager->alarms().isEmpty() ) return false; return true; } ORecur OEvent::recurrence()const { if (!data->recur) data->recur = new ORecur; return *data->recur; } void OEvent::setRecurrence( const ORecur& rec) { changeOrModify(); if (data->recur ) (*data->recur) = rec; else data->recur = new ORecur( rec ); } bool OEvent::hasRecurrence()const { if (!data->recur ) return false; return data->recur->doesRecur(); } QString OEvent::note()const { return data->note; } void OEvent::setNote( const QString& note ) { changeOrModify(); data->note = note; } QDateTime OEvent::createdDateTime()const { return data->created; } void OEvent::setCreatedDateTime( const QDateTime& time ) { changeOrModify(); data->created = time; } QDateTime OEvent::startDateTime()const { if ( data->isAllDay ) return QDateTime( data->start.date(), QTime(0, 0, 0 ) ); return data->start; } QDateTime OEvent::startDateTimeInZone()const { /* if no timezone, or all day event or if the current and this timeZone match... */ if (data->timezone.isEmpty() || data->isAllDay || data->timezone == OTimeZone::current().timeZone() ) return startDateTime(); OTimeZone zone(data->timezone ); return zone.toDateTime( data->start, OTimeZone::current() ); } void OEvent::setStartDateTime( const QDateTime& dt ) { changeOrModify(); data->start = dt; } QDateTime OEvent::endDateTime()const { /* * if all Day event the end time needs * to be on the same day as the start */ if ( data->isAllDay ) return QDateTime( data->start.date(), QTime(23, 59, 59 ) ); return data->end; } QDateTime OEvent::endDateTimeInZone()const { /* if no timezone, or all day event or if the current and this timeZone match... */ if (data->timezone.isEmpty() || data->isAllDay || data->timezone == OTimeZone::current().timeZone() ) return endDateTime(); OTimeZone zone(data->timezone ); return zone.toDateTime( data->end, OTimeZone::current() ); } void OEvent::setEndDateTime( const QDateTime& dt ) { changeOrModify(); data->end = dt; } bool OEvent::isMultipleDay()const { return data->end.date().day() - data->start.date().day(); } bool OEvent::isAllDay()const { return data->isAllDay; } void OEvent::setAllDay( bool allDay ) { changeOrModify(); data->isAllDay = allDay; if (allDay ) data->timezone = "UTC"; } void OEvent::setTimeZone( const QString& tz ) { changeOrModify(); data->timezone = tz; } QString OEvent::timeZone()const { if (data->isAllDay ) return QString::fromLatin1("UTC"); return data->timezone; } bool OEvent::match( const QRegExp& re )const { if ( re.match( data->description ) != -1 ){ setLastHitField( Qtopia::DatebookDescription ); return true; } if ( re.match( data->note ) != -1 ){ setLastHitField( Qtopia::Note ); return true; } if ( re.match( data->location ) != -1 ){ setLastHitField( Qtopia::Location ); return true; } if ( re.match( data->start.toString() ) != -1 ){ setLastHitField( Qtopia::StartDateTime ); return true; } if ( re.match( data->end.toString() ) != -1 ){ setLastHitField( Qtopia::EndDateTime ); return true; } return false; } QString OEvent::toRichText()const { QString text, value; // description text += "<b><h3><img src=\"datebook/DateBook\">"; if ( !description().isEmpty() ) { text += Qtopia::escapeString(description() ).replace(QRegExp( "[\n]"), "" ); } text += "</h3></b><br><hr><br>"; // location if ( !(value = location()).isEmpty() ) { text += "<b>" + QObject::tr( "Location:" ) + "</b> "; text += Qtopia::escapeString(value) + "<br>"; } // all day event if ( isAllDay() ) { text += "<b><i>" + QObject::tr( "This is an all day event" ) + "</i></b><br>"; } // multiple day event else if ( isMultipleDay () ) { text += "<b><i>" + QObject::tr( "This is a multiple day event" ) + "</i></b><br>"; } // start & end times else { // start time if ( startDateTime().isValid() ) { text += "<b>" + QObject::tr( "Start:") + "</b> "; text += Qtopia::escapeString(startDateTime().toString() ). replace(QRegExp( "[\n]"), "<br>" ) + "<br>"; } // end time if ( endDateTime().isValid() ) { text += "<b>" + QObject::tr( "End:") + "</b> "; text += Qtopia::escapeString(endDateTime().toString() ). replace(QRegExp( "[\n]"), "<br>" ) + "<br>"; } } // categories if ( categoryNames("Calendar").count() ){ text += "<b>" + QObject::tr( "Category:") + "</b> "; text += categoryNames("Calendar").join(", "); text += "<br>"; } //notes if ( !note().isEmpty() ) { text += "<b>" + QObject::tr( "Note:") + "</b><br>"; text += note(); // text += Qtopia::escapeString(note() ). // replace(QRegExp( "[\n]"), "<br>" ) + "<br>"; } return text; } QString OEvent::toShortText()const { QString text; text += QString::number( startDateTime().date().day() ); text += "."; text += QString::number( startDateTime().date().month() ); text += "."; text += QString::number( startDateTime().date().year() ); text += " "; text += QString::number( startDateTime().time().hour() ); text += ":"; text += QString::number( startDateTime().time().minute() ); text += " - "; text += description(); return text; } QString OEvent::type()const { return QString::fromLatin1("OEvent"); } QString OEvent::recordField( int /*id */ )const { return QString::null; } int OEvent::rtti() { return OPimResolver::DateBook; } bool OEvent::loadFromStream( QDataStream& ) { return true; } bool OEvent::saveToStream( QDataStream& )const { return true; } void OEvent::changeOrModify() { if ( data->count != 1 ) { data->deref(); Data* d2 = new Data; d2->description = data->description; d2->location = data->location; if (data->manager ) d2->manager = new OPimNotifyManager( *data->manager ); if ( data->recur ) d2->recur = new ORecur( *data->recur ); d2->note = data->note; d2->created = data->created; d2->start = data->start; d2->end = data->end; d2->isAllDay = data->isAllDay; d2->timezone = data->timezone; d2->parent = data->parent; if ( data->child ) { d2->child = new QArray<int>( *data->child ); d2->child->detach(); } data = d2; } } void OEvent::deref() { if ( data->deref() ) { delete data; data = 0; } } // Exporting Event data to map. Using the same // encoding as ODateBookAccessBackend_xml does.. // Thus, we could remove the stuff there and use this // for it and for all other places.. // Encoding should happen at one place, only ! (eilers) QMap<int, QString> OEvent::toMap()const { QMap<int, QString> retMap; retMap.insert( OEvent::FUid, QString::number( uid() ) ); retMap.insert( OEvent::FCategories, Qtopia::escapeString( Qtopia::Record::idsToString( categories() ) )); retMap.insert( OEvent::FDescription, Qtopia::escapeString( description() ) ); retMap.insert( OEvent::FLocation, Qtopia::escapeString( location() ) ); retMap.insert( OEvent::FType, isAllDay() ? "AllDay" : "" ); OPimAlarm alarm = notifiers().alarms()[0]; retMap.insert( OEvent::FAlarm, QString::number( alarm.dateTime().secsTo( startDateTime() ) / 60 ) ); retMap.insert( OEvent::FSound, (alarm.sound() == OPimAlarm::Loud) ? "loud" : "silent" ); OTimeZone zone( timeZone().isEmpty() ? OTimeZone::current() : timeZone() ); retMap.insert( OEvent::FStart, QString::number( zone.fromUTCDateTime( zone.toDateTime( startDateTime(), OTimeZone::utc() ) ) ) ); retMap.insert( OEvent::FEnd, QString::number( zone.fromUTCDateTime( zone.toDateTime( endDateTime(), OTimeZone::utc() ) ) ) ); retMap.insert( OEvent::FNote, Qtopia::escapeString( note() ) ); retMap.insert( OEvent::FTimeZone, timeZone().isEmpty() ? QString( "None" ) : timeZone() ); if( parent() ) retMap.insert( OEvent::FRecParent, QString::number( parent() ) ); if( children().count() ){ QArray<int> childr = children(); QString buf; for ( uint i = 0; i < childr.count(); i++ ) { if ( i != 0 ) buf += " "; buf += QString::number( childr[i] ); } retMap.insert( OEvent::FRecChildren, buf ); } // Add recurrence stuff if( hasRecurrence() ){ ORecur recur = recurrence(); QMap<int, QString> recFields = recur.toMap(); retMap.insert( OEvent::FRType, recFields[ORecur::RType] ); retMap.insert( OEvent::FRWeekdays, recFields[ORecur::RWeekdays] ); retMap.insert( OEvent::FRPosition, recFields[ORecur::RPosition] ); retMap.insert( OEvent::FRFreq, recFields[ORecur::RFreq] ); retMap.insert( OEvent::FRHasEndDate, recFields[ORecur::RHasEndDate] ); retMap.insert( OEvent::FREndDate, recFields[ORecur::EndDate] ); retMap.insert( OEvent::FRCreated, recFields[ORecur::Created] ); retMap.insert( OEvent::FRExceptions, recFields[ORecur::Exceptions] ); + } else { + ORecur recur = recurrence(); + QMap<int, QString> recFields = recur.toMap(); + retMap.insert( OEvent::FRType, recFields[ORecur::RType] ); } return retMap; } void OEvent::fromMap( const QMap<int, QString>& map ) { // We just want to set the UID if it is really stored. if ( !map[OEvent::FUid].isEmpty() ) setUid( map[OEvent::FUid].toInt() ); setCategories( idsFromString( map[OEvent::FCategories] ) ); setDescription( map[OEvent::FDescription] ); setLocation( map[OEvent::FLocation] ); if ( map[OEvent::FType] == "AllDay" ) setAllDay( true ); else setAllDay( false ); int alarmTime = -1; if( !map[OEvent::FAlarm].isEmpty() ) alarmTime = map[OEvent::FAlarm].toInt(); int sound = ( ( map[OEvent::FSound] == "loud" ) ? OPimAlarm::Loud : OPimAlarm::Silent ); if ( ( alarmTime != -1 ) ){ QDateTime dt = startDateTime().addSecs( -1*alarmTime*60 ); OPimAlarm al( sound , dt ); notifiers().add( al ); } if ( !map[OEvent::FTimeZone].isEmpty() && ( map[OEvent::FTimeZone] != "None" ) ){ setTimeZone( map[OEvent::FTimeZone] ); } time_t start = (time_t) map[OEvent::FStart].toLong(); time_t end = (time_t) map[OEvent::FEnd].toLong(); /* AllDay is always in UTC */ if ( isAllDay() ) { OTimeZone utc = OTimeZone::utc(); setStartDateTime( utc.fromUTCDateTime( start ) ); setEndDateTime ( utc.fromUTCDateTime( end ) ); setTimeZone( "UTC"); // make sure it is really utc }else { /* to current date time */ // qWarning(" Start is %d", start ); OTimeZone zone( timeZone().isEmpty() ? OTimeZone::current() : timeZone() ); QDateTime date = zone.toDateTime( start ); qWarning(" Start is %s", date.toString().latin1() ); setStartDateTime( zone.toDateTime( date, OTimeZone::current() ) ); date = zone.toDateTime( end ); setEndDateTime ( zone.toDateTime( date, OTimeZone::current() ) ); } if ( !map[OEvent::FRecParent].isEmpty() ) setParent( map[OEvent::FRecParent].toInt() ); if ( !map[OEvent::FRecChildren].isEmpty() ){ QStringList list = QStringList::split(' ', map[OEvent::FRecChildren] ); for ( QStringList::Iterator it = list.begin(); it != list.end(); ++it ) { addChild( (*it).toInt() ); } } // Fill recurrence stuff and put it directly into the ORecur-Object using fromMap.. if( !map[OEvent::FRType].isEmpty() ){ QMap<int, QString> recFields; recFields.insert( ORecur::RType, map[OEvent::FRType] ); recFields.insert( ORecur::RWeekdays, map[OEvent::FRWeekdays] ); recFields.insert( ORecur::RPosition, map[OEvent::FRPosition] ); recFields.insert( ORecur::RFreq, map[OEvent::FRFreq] ); recFields.insert( ORecur::RHasEndDate, map[OEvent::FRHasEndDate] ); recFields.insert( ORecur::EndDate, map[OEvent::FREndDate] ); recFields.insert( ORecur::Created, map[OEvent::FRCreated] ); recFields.insert( ORecur::Exceptions, map[OEvent::FRExceptions] ); ORecur recur( recFields ); setRecurrence( recur ); } } int OEvent::parent()const { return data->parent; } void OEvent::setParent( int uid ) { changeOrModify(); data->parent = uid; } QArray<int> OEvent::children() const{ if (!data->child) return QArray<int>(); else return data->child->copy(); } void OEvent::setChildren( const QArray<int>& arr ) { changeOrModify(); if (data->child) delete data->child; data->child = new QArray<int>( arr ); data->child->detach(); } void OEvent::addChild( int uid ) { changeOrModify(); if (!data->child ) { data->child = new QArray<int>(1); (*data->child)[0] = uid; }else{ int count = data->child->count(); data->child->resize( count + 1 ); (*data->child)[count] = uid; } } void OEvent::removeChild( int uid ) { if (!data->child || !data->child->contains( uid ) ) return; changeOrModify(); QArray<int> newAr( data->child->count() - 1 ); int j = 0; uint count = data->child->count(); for ( uint i = 0; i < count; i++ ) { if ( (*data->child)[i] != uid ) { newAr[j] = (*data->child)[i]; j++; } } (*data->child) = newAr; } struct OEffectiveEvent::Data : public QShared { Data() : QShared() { } OEvent event; QDate date; QTime start, end; QDate startDate, endDate; bool dates : 1; }; OEffectiveEvent::OEffectiveEvent() { data = new Data; data->date = QDate::currentDate(); data->start = data->end = QTime::currentTime(); data->dates = false; } OEffectiveEvent::OEffectiveEvent( const OEvent& ev, const QDate& startDate, Position pos ) { data = new Data; data->event = ev; data->date = startDate; if ( pos & Start ) data->start = ev.startDateTime().time(); else data->start = QTime( 0, 0, 0 ); if ( pos & End ) data->end = ev.endDateTime().time(); else data->end = QTime( 23, 59, 59 ); data->dates = false; } OEffectiveEvent::OEffectiveEvent( const OEffectiveEvent& ev) { data = ev.data; data->ref(); } OEffectiveEvent::~OEffectiveEvent() { if ( data->deref() ) { delete data; data = 0; } } OEffectiveEvent& OEffectiveEvent::operator=( const OEffectiveEvent& ev ) { if ( *this == ev ) return *this; ev.data->ref(); deref(); data = ev.data; return *this; } void OEffectiveEvent::setStartTime( const QTime& ti) { changeOrModify(); data->start = ti; } void OEffectiveEvent::setEndTime( const QTime& en) { changeOrModify(); data->end = en; } void OEffectiveEvent::setEvent( const OEvent& ev) { changeOrModify(); data->event = ev; } void OEffectiveEvent::setDate( const QDate& da) { changeOrModify(); data->date = da; } void OEffectiveEvent::setEffectiveDates( const QDate& from, const QDate& to ) { if (!from.isValid() ) { data->dates = false; return; } data->startDate = from; data->endDate = to; } QString OEffectiveEvent::description()const { return data->event.description(); } QString OEffectiveEvent::location()const { return data->event.location(); } QString OEffectiveEvent::note()const { return data->event.note(); } OEvent OEffectiveEvent::event()const { return data->event; } QTime OEffectiveEvent::startTime()const { return data->start; } QTime OEffectiveEvent::endTime()const { return data->end; } QDate OEffectiveEvent::date()const { return data->date; } int OEffectiveEvent::length()const { return (data->end.hour() * 60 - data->start.hour() * 60) + QABS(data->start.minute() - data->end.minute() ); } int OEffectiveEvent::size()const { return ( data->end.hour() - data->start.hour() ) * 3600 + (data->end.minute() - data->start.minute() * 60 + data->end.second() - data->start.second() ); } QDate OEffectiveEvent::startDate()const { if ( data->dates ) return data->startDate; else if ( data->event.hasRecurrence() ) // single day, since multi-day should have a d pointer return data->date; else return data->event.startDateTime().date(); } QDate OEffectiveEvent::endDate()const { if ( data->dates ) return data->endDate; else if ( data->event.hasRecurrence() ) return data->date; else return data->event.endDateTime().date(); } void OEffectiveEvent::deref() { if ( data->deref() ) { delete data; data = 0; } } void OEffectiveEvent::changeOrModify() { if ( data->count != 1 ) { data->deref(); Data* d2 = new Data; d2->event = data->event; d2->date = data->date; d2->start = data->start; d2->end = data->end; d2->startDate = data->startDate; d2->endDate = data->endDate; d2->dates = data->dates; data = d2; } } bool OEffectiveEvent::operator<( const OEffectiveEvent &e ) const{ if ( data->date < e.date() ) return TRUE; if ( data->date == e.date() ) return ( startTime() < e.startTime() ); else return FALSE; } bool OEffectiveEvent::operator<=( const OEffectiveEvent &e ) const{ return (data->date <= e.date() ); } bool OEffectiveEvent::operator==( const OEffectiveEvent &e ) const { return ( date() == e.date() && startTime() == e.startTime() && endTime()== e.endTime() && event() == e.event() ); } bool OEffectiveEvent::operator!=( const OEffectiveEvent &e ) const { return !(*this == e ); } bool OEffectiveEvent::operator>( const OEffectiveEvent &e ) const { return !(*this <= e ); } bool OEffectiveEvent::operator>= ( const OEffectiveEvent &e ) const { return !(*this < e); } diff --git a/libopie/pim/oevent.h b/libopie/pim/oevent.h index 9218c97..9eb948f 100644 --- a/libopie/pim/oevent.h +++ b/libopie/pim/oevent.h @@ -1,230 +1,236 @@ // CONTAINS GPLed code of TT #ifndef OPIE_PIM_EVENT_H #define OPIE_PIM_EVENT_H #include <qstring.h> #include <qdatetime.h> #include <qvaluelist.h> #include <qpe/recordfields.h> #include <qpe/palmtopuidgen.h> #include "otimezone.h" #include "opimrecord.h" struct OCalendarHelper { /** calculate the week number of the date */ static int week( const QDate& ); /** calculate the occurence of week days since the start of the month */ static int ocurrence( const QDate& ); // returns the dayOfWeek for the *first* day it finds (ignores // any further days!). Returns 1 (Monday) if there isn't any day found static int dayOfWeek( char day ); /** returns the diff of month */ static int monthDiff( const QDate& first, const QDate& second ); }; class OPimNotifyManager; class ORecur; /** * This is the container for all Events. It encapsules all * available information for a single Event * @short container for events. */ class OEvent : public OPimRecord { public: typedef QValueList<OEvent> ValueList; /** * RecordFields contain possible attributes * used in the Results of toMap().. */ enum RecordFields { FUid = Qtopia::UID_ID, FCategories = Qtopia::CATEGORY_ID, FDescription = 0, FLocation, FType, FAlarm, FSound, FRType, FRWeekdays, FRPosition, FRFreq, FRHasEndDate, FREndDate, FRCreated, FRExceptions, FStart, FEnd, FNote, FTimeZone, FRecParent, FRecChildren, }; /** * Start with an Empty OEvent. UID == 0 means that it is empty */ OEvent(int uid = 0); /** * copy c'tor */ OEvent( const OEvent& ); + + /** + * Create OEvent, initialized by map + * @see enum RecordFields + */ + OEvent( const QMap<int, QString> map ); ~OEvent(); OEvent &operator=( const OEvent& ); QString description()const; void setDescription( const QString& description ); QString location()const; void setLocation( const QString& loc ); bool hasNotifiers()const; OPimNotifyManager ¬ifiers()const; ORecur recurrence()const; void setRecurrence( const ORecur& ); bool hasRecurrence()const; QString note()const; void setNote( const QString& note ); QDateTime createdDateTime()const; void setCreatedDateTime( const QDateTime& dt); /** set the date to dt. dt is the QDateTime in localtime */ void setStartDateTime( const QDateTime& ); /** returns the datetime in the local timeZone */ QDateTime startDateTime()const; /** returns the start datetime in the current zone */ QDateTime startDateTimeInZone()const; /** in current timezone */ void setEndDateTime( const QDateTime& ); /** in current timezone */ QDateTime endDateTime()const; QDateTime endDateTimeInZone()const; bool isMultipleDay()const; bool isAllDay()const; void setAllDay( bool isAllDay ); /* pin this event to a timezone! FIXME */ void setTimeZone( const QString& timeZone ); QString timeZone()const; virtual bool match( const QRegExp& )const; /** For exception to recurrence here is a list of children... */ QArray<int> children()const; void setChildren( const QArray<int>& ); void addChild( int uid ); void removeChild( int uid ); /** return the parent OEvent */ int parent()const; void setParent( int uid ); /* needed reimp */ QString toRichText()const; QString toShortText()const; QString type()const; QMap<int, QString> toMap()const; void fromMap( const QMap<int, QString>& map ); QString recordField(int )const; static int rtti(); bool loadFromStream( QDataStream& ); bool saveToStream( QDataStream& )const; /* bool operator==( const OEvent& ); bool operator!=( const OEvent& ); bool operator<( const OEvent& ); bool operator<=( const OEvent& ); bool operator>( const OEvent& ); bool operator>=(const OEvent& ); */ private: inline void changeOrModify(); void deref(); struct Data; Data* data; class Private; Private* priv; }; /** * AN Event can span through multiple days. We split up a multiday eve */ class OEffectiveEvent { public: typedef QValueList<OEffectiveEvent> ValueList; enum Position { MidWay, Start, End, StartEnd }; // If we calculate the effective event of a multi-day event // we have to figure out whether we are at the first day, // at the end, or anywhere else ("middle"). This is important // for the start/end times (00:00/23:59) // MidWay: 00:00 -> 23:59, as we are "in the middle" of a multi- // day event // Start: start time -> 23:59 // End: 00:00 -> end time // Start | End == StartEnd: for single-day events (default) // here we draw start time -> end time OEffectiveEvent(); OEffectiveEvent( const OEvent& event, const QDate& startDate, Position pos = StartEnd ); OEffectiveEvent( const OEffectiveEvent& ); OEffectiveEvent &operator=(const OEffectiveEvent& ); ~OEffectiveEvent(); void setStartTime( const QTime& ); void setEndTime( const QTime& ); void setEvent( const OEvent& ); void setDate( const QDate& ); void setEffectiveDates( const QDate& from, const QDate& to ); QString description()const; QString location()const; QString note()const; OEvent event()const; QTime startTime()const; QTime endTime()const; QDate date()const; /* return the length in hours */ int length()const; int size()const; QDate startDate()const; QDate endDate()const; bool operator<( const OEffectiveEvent &e ) const; bool operator<=( const OEffectiveEvent &e ) const; bool operator==( const OEffectiveEvent &e ) const; bool operator!=( const OEffectiveEvent &e ) const; bool operator>( const OEffectiveEvent &e ) const; bool operator>= ( const OEffectiveEvent &e ) const; private: void deref(); inline void changeOrModify(); class Private; Private* priv; struct Data; Data* data; }; #endif diff --git a/libopie/pim/otimezone.cpp b/libopie/pim/otimezone.cpp index b2bd3aa..34659c3 100644 --- a/libopie/pim/otimezone.cpp +++ b/libopie/pim/otimezone.cpp @@ -1,104 +1,113 @@ #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include "otimezone.h" namespace { QDateTime utcTime( time_t t) { tm* broken = ::gmtime( &t ); QDateTime ret; ret.setDate( QDate( broken->tm_year + 1900, broken->tm_mon +1, broken->tm_mday ) ); ret.setTime( QTime( broken->tm_hour, broken->tm_min, broken->tm_sec ) ); return ret; } QDateTime utcTime( time_t t, const QString& zone) { QCString org = ::getenv( "TZ" ); +#ifndef Q_OS_MACX // Following line causes bus errors on Mac ::setenv( "TZ", zone.latin1(), true ); ::tzset(); tm* broken = ::localtime( &t ); ::setenv( "TZ", org, true ); +#else +#warning "Need a replacement for MacOSX!!" + tm* broken = ::localtime( &t ); +#endif QDateTime ret; ret.setDate( QDate( broken->tm_year + 1900, broken->tm_mon +1, broken->tm_mday ) ); ret.setTime( QTime( broken->tm_hour, broken->tm_min, broken->tm_sec ) ); return ret; } time_t to_Time_t( const QDateTime& utc, const QString& str ) { QDate d = utc.date(); QTime t = utc.time(); tm broken; broken.tm_year = d.year() - 1900; broken.tm_mon = d.month() - 1; broken.tm_mday = d.day(); broken.tm_hour = t.hour(); broken.tm_min = t.minute(); broken.tm_sec = t.second(); QCString org = ::getenv( "TZ" ); +#ifndef Q_OS_MACX // Following line causes bus errors on Mac ::setenv( "TZ", str.latin1(), true ); ::tzset(); time_t ti = ::mktime( &broken ); ::setenv( "TZ", org, true ); - +#else +#warning "Need a replacement for MacOSX!!" + time_t ti = ::mktime( &broken ); +#endif return ti; } } OTimeZone::OTimeZone( const ZoneName& zone ) : m_name(zone) { } OTimeZone::~OTimeZone() { } bool OTimeZone::isValid()const { return !m_name.isEmpty(); } /* * we will get the current timezone * and ask it to convert to the timezone date */ QDateTime OTimeZone::toLocalDateTime( const QDateTime& dt) { return OTimeZone::current().toDateTime( dt, *this ); } QDateTime OTimeZone::toUTCDateTime( const QDateTime& dt ) { return OTimeZone::utc().toDateTime( dt, *this ); } QDateTime OTimeZone::fromUTCDateTime( time_t t) { return utcTime( t ); } QDateTime OTimeZone::toDateTime( time_t t) { return utcTime( t, m_name ); } /* * convert dt to utc using zone.m_name * convert utc -> timeZoneDT using this->m_name */ QDateTime OTimeZone::toDateTime( const QDateTime& dt, const OTimeZone& zone ) { time_t utc = to_Time_t( dt, zone.m_name ); qWarning("%d %s", utc, zone.m_name.latin1() ); return utcTime( utc, m_name ); } time_t OTimeZone::fromDateTime( const QDateTime& time ) { return to_Time_t( time, m_name ); } time_t OTimeZone::fromUTCDateTime( const QDateTime& time ) { return to_Time_t( time, "UTC" ); } OTimeZone OTimeZone::current() { QCString str = ::getenv("TZ"); OTimeZone zone( str ); return zone; } OTimeZone OTimeZone::utc() { return OTimeZone("UTC"); } QString OTimeZone::timeZone()const { return m_name; } diff --git a/libopie/pim/otodoaccesssql.cpp b/libopie/pim/otodoaccesssql.cpp index 75a0860..3764c7e 100644 --- a/libopie/pim/otodoaccesssql.cpp +++ b/libopie/pim/otodoaccesssql.cpp @@ -1,693 +1,693 @@ #include <qdatetime.h> #include <qpe/global.h> #include <opie2/osqldriver.h> #include <opie2/osqlresult.h> #include <opie2/osqlmanager.h> #include <opie2/osqlquery.h> #include "otodoaccesssql.h" #include "opimstate.h" #include "opimnotifymanager.h" #include "orecur.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(const QArray<int>& ); ~FindQuery(); QString query()const; private: QString single()const; QString multi()const; QArray<int> m_uids; 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 PRIMARY KEY, categories, completed, "; qu += "description, summary, priority, DueDate, progress , state, "; // This is the recurrance-stuff .. Exceptions are currently not supported (see ORecur.cpp) ! (eilers) qu += "RType, RWeekdays, RPosition, RFreq, RHasEndDate, EndDate, Created, Exceptions, "; qu += "reminders, alarms, maintainer, startdate, completeddate);"; qu += "create table custom_data( uid INTEGER, id INTEGER, type VARCHAR(10), value VARCHAR(10), PRIMARY KEY /* identifier */ (uid, id) );"; return qu; } LoadQuery::LoadQuery() : OSQLQuery() {} LoadQuery::~LoadQuery() {} QString LoadQuery::query()const { QString qu; // We do not need "distinct" here. The primary key is always unique.. //qu += "select distinct uid from todolist"; qu += "select 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(); } int sYear = 0, sMonth = 0, sDay = 0; if( m_todo.hasStartDate() ){ QDate sDate = m_todo.startDate(); sYear = sDate.year(); sMonth= sDate.month(); sDay = sDate.day(); } int eYear = 0, eMonth = 0, eDay = 0; if( m_todo.hasCompletedDate() ){ QDate eDate = m_todo.completedDate(); 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_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(); + return m_driver->close(); // Shouldn't m_driver->sync be better than close ? (eilers) } 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 * */ /* Category */ if ( sortFilter & 1 ) { QString str; if (cat != 0 ) str = QString::number( cat ); query += " categories like '%" +str+"%' AND"; } /* Show only overdue */ if ( sortFilter & 2 ) { QDate date = QDate::currentDate(); QString due; QString base; base = QString("DueDate <= '%1-%2-%3' AND completed = 0").arg( date.year() ).arg( date.month() ).arg( date.day() ); query += " " + base + " AND"; } /* not show completed */ if ( sortFilter & 4 ) { query += " completed = 0 AND"; }else{ query += " ( completed = 1 OR completed = 0) AND"; } /* srtip the end */ query = query.remove( query.length()-3, 3 ); /* * sort order stuff * quite straight forward */ query += "ORDER BY "; switch( sortOrder ) { /* completed */ case 0: query += "completed"; break; case 1: query += "priority"; break; case 2: query += "summary"; break; case 3: query += "DueDate"; break; } if ( !asc ) { qWarning("not ascending!"); query += " DESC"; } qWarning( query ); OSQLRawQuery raw(query ); return uids( m_driver->query(&raw) ); } 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(); qWarning("todo1"); OTodo to = todo( (*it) ); cache( to ); ++it; for ( ; it != list.end(); ++it ) { qWarning("caching"); cache( todo( (*it) ) ); } return to; } OTodo OTodoAccessBackendSQL::todo( OSQLResultItem& item )const { qWarning("todo"); bool hasDueDate = false; QDate dueDate = QDate::currentDate(); hasDueDate = date( dueDate, item.data("DueDate") ); QStringList cats = QStringList::split(";", item.data("categories") ); qWarning("Item is completed: %d", item.data("completed").toInt() ); OTodo to( (bool)item.data("completed").toInt(), item.data("priority").toInt(), cats, item.data("summary"), item.data("description"), item.data("progress").toUShort(), hasDueDate, dueDate, item.data("uid").toInt() ); bool isOk; int prioInt = QString( item.data("priority") ).toInt( &isOk ); if ( isOk ) to.setPriority( prioInt ); bool hasStartDate = false; QDate startDate = QDate::currentDate(); hasStartDate = date( startDate, item.data("startdate") ); bool hasCompletedDate = false; QDate completedDate = QDate::currentDate(); hasCompletedDate = date( completedDate, item.data("completeddate") ); if ( hasStartDate ) to.setStartDate( startDate ); if ( hasCompletedDate ) to.setCompletedDate( completedDate ); OPimNotifyManager& manager = to.notifiers(); manager.alarmsFromString( item.data("alarms") ); manager.remindersFromString( item.data("reminders") ); OPimState pimState; pimState.setState( QString( item.data("state") ).toInt() ); to.setState( pimState ); QMap<int, QString> recMap; recMap.insert( ORecur::RType , item.data("RType") ); recMap.insert( ORecur::RWeekdays , item.data("RWeekdays") ); recMap.insert( ORecur::RPosition , item.data("RPosition") ); recMap.insert( ORecur::RFreq , item.data("RFreq") ); recMap.insert( ORecur::RHasEndDate, item.data("RHasEndDate") ); recMap.insert( ORecur::EndDate , item.data("EndDate") ); recMap.insert( ORecur::Created , item.data("Created") ); recMap.insert( ORecur::Exceptions , item.data("Exceptions") ); ORecur recur; recur.fromMap( recMap ); to.setRecurrence( recur ); 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!!! * FIXME: Isn't this dict obsolete ? (eilers) */ 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) ); // Why twice ? (eilers) m_dict.insert("CrossReference", new int(OTodo::CrossReference) ); // m_dict.insert("HasAlarmDateTime",new int(OTodo::HasAlarmDateTime) ); // old stuff (eilers) // m_dict.insert("AlarmDateTime", new int(OTodo::AlarmDateTime) ); // old stuff (eilers) } /* * need to be const so let's fool the * compiler :( */ void OTodoAccessBackendSQL::update()const { ((OTodoAccessBackendSQL*)this)->m_dirty = false; LoadQuery lo; OSQLResult res = m_driver->query(&lo); if ( res.state() != OSQLResult::Success ) return; ((OTodoAccessBackendSQL*)this)->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() ); 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; } QArray<int> OTodoAccessBackendSQL::matchRegexp( const QRegExp &r ) const { #warning OTodoAccessBackendSQL::matchRegexp() not implemented !! #if 0 Copied from xml-backend by not adapted to sql (eilers) QArray<int> m_currentQuery( m_events.count() ); uint arraycounter = 0; QMap<int, OTodo>::ConstIterator it; for (it = m_events.begin(); it != m_events.end(); ++it ) { if ( it.data().match( r ) ) m_currentQuery[arraycounter++] = it.data().uid(); } // Shrink to fit.. m_currentQuery.resize(arraycounter); return m_currentQuery; #endif QArray<int> empty; return empty; } QBitArray OTodoAccessBackendSQL::supports()const { return sup(); } QBitArray OTodoAccessBackendSQL::sup() const{ QBitArray ar( OTodo::CompletedDate + 1 ); ar.fill( true ); ar[OTodo::CrossReference] = false; ar[OTodo::State ] = false; ar[OTodo::Reminders] = false; ar[OTodo::Notifiers] = false; ar[OTodo::Maintainer] = false; return ar; } void OTodoAccessBackendSQL::removeAllCompleted(){ #warning OTodoAccessBackendSQL::removeAllCompleted() not implemented !! } diff --git a/libopie/pim/test/converter.cpp b/libopie/pim/test/converter.cpp index 650d119..bfdb605 100644 --- a/libopie/pim/test/converter.cpp +++ b/libopie/pim/test/converter.cpp @@ -1,68 +1,107 @@ #include "converter.h" #include <qdatetime.h> #include <qprogressbar.h> #include <qpe/qpeapplication.h> #include <opie/ocontactaccess.h> #include <opie/ocontactaccessbackend_xml.h> #include <opie/ocontactaccessbackend_sql.h> +#include <opie/odatebookaccess.h> +#include <opie/odatebookaccessbackend_xml.h> +#include <opie/odatebookaccessbackend_sql.h> + +// #define _ADDRESSBOOK_ACCESS + Converter::Converter(){ } void Converter::start_conversion(){ qWarning("Converting Contacts from XML to SQL.."); // Creating backends to the requested databases.. + +#ifdef _ADDRESSBOOK_ACCESS OContactAccessBackend* xmlBackend = new OContactAccessBackend_XML( "Converter", QString::null ); OContactAccessBackend* sqlBackend = new OContactAccessBackend_SQL( QString::null, QString::null ); // Put the created backends into frontends to access them OContactAccess* xmlAccess = new OContactAccess ( "addressbook_xml", QString::null , xmlBackend, true ); OContactAccess* sqlAccess = new OContactAccess ( "addressbook_sql", - QString::null , sqlBackend, true ); + QString::null ); +#else + ODateBookAccessBackend* xmlBackend = new ODateBookAccessBackend_XML( "Converter", + QString::null ); + + ODateBookAccessBackend* sqlBackend = new ODateBookAccessBackend_SQL( QString::null, + QString::null ); + // Put the created backends into frontends to access them + ODateBookAccess* xmlAccess = new ODateBookAccess ( xmlBackend ); + + ODateBookAccess* sqlAccess = new ODateBookAccess ( sqlBackend ); + + xmlAccess->load(); + +#endif + QTime t; t.start(); - // Clean the sql-database.. +// Clean the sql-database.. sqlAccess->clear(); +#ifdef _ADDRESSBOOK_ACCESS // Now trasmit every contact from the xml database to the sql-database OContactAccess::List contactList = xmlAccess->allRecords(); m_progressBar->setTotalSteps( contactList.count() ); int count = 0; if ( sqlAccess && xmlAccess ){ OContactAccess::List::Iterator it; for ( it = contactList.begin(); it != contactList.end(); ++it ){ sqlAccess->add( *it ); m_progressBar->setProgress( ++count ); } } - +#else + // Now transmit every contact from the xml database to the sql-database + ODateBookAccess::List dateList = xmlAccess->allRecords(); + m_progressBar->setTotalSteps( dateList.count() ); + qWarning( "Number of elements to copy: %d", dateList.count() ); + + int count = 0; + if ( sqlAccess && xmlAccess ){ + ODateBookAccess::List::Iterator it; + for ( it = dateList.begin(); it != dateList.end(); ++it ){ + sqlAccess->add( *it ); + m_progressBar->setProgress( ++count ); + } + } + +#endif // Delete the frontends. Backends will be deleted automatically, too ! delete sqlAccess; qWarning("Conversion is finished and needed %d ms !", t.elapsed()); delete xmlAccess; } int main( int argc, char** argv ) { QPEApplication a( argc, argv ); Converter dlg; a.showMainWidget( &dlg ); // dlg. showMaximized ( ); return a.exec(); } diff --git a/libopie/pim/test/converter.h b/libopie/pim/test/converter.h new file mode 100755 index 0000000..1cc2a7c --- a/dev/null +++ b/libopie/pim/test/converter.h @@ -0,0 +1,18 @@ +#ifndef _CONVERTER_H_ +#define _CONVERTER_H_ + + +#include "converter_base.h" + + +class Converter: public converter_base { +public: + Converter(); + + void start_conversion(); +private: + +}; + + +#endif diff --git a/libopie/pim/test/converter.pro b/libopie/pim/test/converter.pro index aa74bff..bd9c7a3 100644 --- a/libopie/pim/test/converter.pro +++ b/libopie/pim/test/converter.pro @@ -1,12 +1,12 @@ -TEMPLATE = app +# TEMPLATE = app CONFIG = qt warn_on debug # CONFIG = qt warn_on release #HEADERS = SOURCES = converter.cpp INTERFACES = converter_base.ui INCLUDEPATH += $(OPIEDIR)/include DEPENDPATH += $(OPIEDIR)/include LIBS += -lqpe -lopie TARGET = converter include ( $(OPIEDIR)/include.pro ) diff --git a/libopie2/opiepim/backend/obackendfactory.h b/libopie2/opiepim/backend/obackendfactory.h index 3567687..761ab9a 100644 --- a/libopie2/opiepim/backend/obackendfactory.h +++ b/libopie2/opiepim/backend/obackendfactory.h @@ -1,176 +1,194 @@ /* * Class to manage Backends. * * 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. * ===================================================================== * ToDo: Use plugins * ===================================================================== * Version: $Id$ * ===================================================================== * History: * $Log$ + * Revision 1.9 2003/12/22 10:19:26 eilers + * Finishing implementation of sql-backend for datebook. But I have to + * port the PIM datebook application to use it, before I could debug the + * whole stuff. + * Thus, PIM-Database backend is finished, but highly experimental. And some + * parts are still generic. For instance, the "queryByExample()" methods are + * not (or not fully) implemented. Todo: custom-entries not stored. + * The big show stopper: matchRegExp() (needed by OpieSearch) needs regular + * expression search in the database, which is not supported by sqlite ! + * Therefore we need either an extended sqlite or a workaround which would + * be very slow and memory consuming.. + * * Revision 1.8 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... * * Revision 1.7 2003/08/01 12:30:16 eilers * Merging changes from BRANCH_1_0 to HEAD * * Revision 1.6.4.1 2003/06/30 14:34:19 eilers * Patches from Zecke: * Fixing and cleaning up extraMap handling * Adding d_ptr for binary compatibility in the future * * Revision 1.6 2003/04/13 18:07:10 zecke * More API doc * QString -> const QString& * QString = 0l -> QString::null * * Revision 1.5 2003/02/21 23:31:52 zecke * Add XML datebookresource * -clean up todoaccessxml header * -implement some more stuff in the oeven tester * -extend DefaultFactory to not crash and to use datebook * * -reading of OEvents is working nicely.. saving will be added * tomorrow * -fix spelling in ODateBookAcces * * Revision 1.4 2002/10/14 15:55:18 eilers * Redeactivate SQL.. ;) * * Revision 1.3 2002/10/10 17:08:58 zecke * The Cache is finally in place * I tested it with my todolist and it 'works' for 10.000 todos the hits are awesome ;) * The read ahead functionality does not make sense for XMLs backends because most of the stuff is already in memory. While using readahead on SQL makes things a lot faster.... * I still have to fully implement read ahead * This change is bic but sc * * Revision 1.2 2002/10/08 09:27:36 eilers * Fixed libopie.pro to include the new pim-API. * The SQL-Stuff is currently deactivated. Otherwise everyone who wants to * compile itself would need to install libsqlite, libopiesql... * Therefore, the backend currently uses XML only.. * * Revision 1.1 2002/10/07 17:35:01 eilers * added OBackendFactory for advanced backend access * * * ===================================================================== */ #ifndef OPIE_BACKENDFACTORY_H_ #define OPIE_BACKENDFACTORY_H_ #include <qstring.h> #include <qasciidict.h> #include <qpe/config.h> #include "otodoaccessxml.h" #include "ocontactaccessbackend_xml.h" #include "odatebookaccessbackend_xml.h" #ifdef __USE_SQL #include "otodoaccesssql.h" #include "ocontactaccessbackend_sql.h" +#include "odatebookaccessbackend_sql.h" #endif class OBackendPrivate; /** * This class is our factory. It will give us the default implementations * of at least Todolist, Contacts and Datebook. In the future this class will * allow users to switch the backend with ( XML->SQLite ) without the need * to recompile.# * This class as the whole PIM Api is making use of templates * * <pre> * OTodoAccessBackend* backend = OBackEndFactory<OTodoAccessBackend>::Default("todo", QString::null ); * backend->load(); * </pre> * * @author Stefan Eilers * @version 0.1 */ template<class T> class OBackendFactory { public: OBackendFactory() {}; enum BACKENDS { TODO, CONTACT, DATE }; /** * Returns a backend implementation for backendName * @param backendName the type of the backend * @param appName will be passed on to the backend */ static T* Default( const QString backendName, const QString& appName ){ // __asm__("int3"); Config config( "pimaccess" ); config.setGroup ( backendName ); QString backend = config.readEntry( "usebackend" ); qWarning("Selected backend for %s is: %s", backendName.latin1(), backend.latin1() ); QAsciiDict<int> dict ( 3 ); dict.setAutoDelete ( TRUE ); dict.insert( "todo", new int (TODO) ); dict.insert( "contact", new int (CONTACT) ); dict.insert( "datebook", new int(DATE) ); int *find = dict[ backendName ]; if (!find ) return 0; switch ( *find ){ case TODO: #ifdef __USE_SQL if ( backend == "sql" ) return (T*) new OTodoAccessBackendSQL(""); #else if ( backend == "sql" ) - qWarning ("OBackendFactory:: sql Backend not implemented! Using XML instead!"); + qWarning ("OBackendFactory:: sql Backend for TODO not implemented! Using XML instead!"); #endif return (T*) new OTodoAccessXML( appName ); case CONTACT: #ifdef __USE_SQL if ( backend == "sql" ) return (T*) new OContactAccessBackend_SQL(""); #else if ( backend == "sql" ) - qWarning ("OBackendFactory:: sql Backend not implemented! Using XML instead!"); + qWarning ("OBackendFactory:: sql Backend for CONTACT not implemented! Using XML instead!"); #endif return (T*) new OContactAccessBackend_XML( appName ); case DATE: +#ifdef __USE_SQL if ( backend == "sql" ) - qWarning("OBackendFactory:: sql Backend not implemented! Using XML instead!"); + return (T*) new ODateBookAccessBackend_SQL(""); +#else + if ( backend == "sql" ) + qWarning("OBackendFactory:: sql Backend for DATEBOOK not implemented! Using XML instead!"); +#endif return (T*) new ODateBookAccessBackend_XML( appName ); default: return NULL; } } private: OBackendPrivate* d; }; #endif diff --git a/libopie2/opiepim/backend/ocontactaccessbackend_sql.cpp b/libopie2/opiepim/backend/ocontactaccessbackend_sql.cpp index dd9dbde..a5be4c8 100644 --- a/libopie2/opiepim/backend/ocontactaccessbackend_sql.cpp +++ b/libopie2/opiepim/backend/ocontactaccessbackend_sql.cpp @@ -1,906 +1,918 @@ /* * 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.4 2003/12/22 10:19:26 eilers + * Finishing implementation of sql-backend for datebook. But I have to + * port the PIM datebook application to use it, before I could debug the + * whole stuff. + * Thus, PIM-Database backend is finished, but highly experimental. And some + * parts are still generic. For instance, the "queryByExample()" methods are + * not (or not fully) implemented. Todo: custom-entries not stored. + * The big show stopper: matchRegExp() (needed by OpieSearch) needs regular + * expression search in the database, which is not supported by sqlite ! + * Therefore we need either an extended sqlite or a workaround which would + * be very slow and memory consuming.. + * * 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; }; /** * a find query for custom elements */ class FindCustomQuery : public OSQLQuery { public: FindCustomQuery(int uid); FindCustomQuery(const QArray<int>& ); ~FindCustomQuery(); QString query()const; private: QString single()const; QString multi()const; QArray<int> m_uids; int m_uid; }; // We using three tables to store the information: // 1. addressbook : It contains General information about the contact (non custom) // 2. custom_data : Not official supported entries // All tables are connected by the uid of the contact. // Maybe I should add a table for meta-information ? CreateQuery::CreateQuery() : OSQLQuery() {} CreateQuery::~CreateQuery() {} QString CreateQuery::query()const { QString qu; #ifdef __STORE_HORIZONTAL_ qu += "create table addressbook( uid PRIMARY KEY "; QStringList fieldList = OContactFields::untrfields( false ); for ( QStringList::Iterator it = ++fieldList.begin(); it != fieldList.end(); ++it ){ qu += QString( ",\"%1\" VARCHAR(10)" ).arg( *it ); } qu += " );"; qu += "create table custom_data( uid INTEGER, id INTEGER, type VARCHAR, priority INTEGER, value VARCHAR, PRIMARY KEY /* identifier */ (uid, id) );"; #else qu += "create table addressbook( uid INTEGER, id INTEGER, type VARCHAR, priority INTEGER, value VARCHAR, PRIMARY KEY /* identifier */ (uid, id));"; qu += "create table custom_data( uid INTEGER, id INTEGER, type VARCHAR, priority INTEGER, value VARCHAR, PRIMARY KEY /* identifier */ (uid, id) );"; // qu += "create table dates( uid PRIMARY KEY, type, day, month, year, hour, minute, second );"; #endif // __STORE_HORIZONTAL_ return qu; } ClearQuery::ClearQuery() : OSQLQuery() {} ClearQuery::~ClearQuery() {} QString ClearQuery::query()const { QString qu = "drop table addressbook;"; qu += "drop table custom_data;"; // qu += "drop table dates;"; return qu; } LoadQuery::LoadQuery() : OSQLQuery() {} LoadQuery::~LoadQuery() {} QString LoadQuery::query()const { QString qu; #ifdef __STORE_HORIZONTAL_ qu += "select uid from addressbook"; #else # ifndef __USE_SUPERFAST_LOADQUERY qu += "select distinct uid from addressbook"; # else qu += "select uid from addressbook where type = 'Last Name'"; # endif // __USE_SUPERFAST_LOADQUERY #endif // __STORE_HORIZONTAL_ return qu; } InsertQuery::InsertQuery( const OContact& contact ) : OSQLQuery(), m_contact( contact ) { } InsertQuery::~InsertQuery() { } /* * converts from a OContact to a query */ QString InsertQuery::query()const{ #ifdef __STORE_HORIZONTAL_ QString qu; qu += "insert into addressbook VALUES( " + QString::number( m_contact.uid() ); // Get all information out of the contact-class // Remember: The category is stored in contactMap, too ! QMap<int, QString> contactMap = m_contact.toMap(); QStringList fieldList = OContactFields::untrfields( false ); QMap<QString, int> translate = OContactFields::untrFieldsToId(); for ( QStringList::Iterator it = ++fieldList.begin(); it != fieldList.end(); ++it ){ // Convert Column-String to Id and get value for this id.. // Hmmm.. Maybe not very cute solution.. int id = translate[*it]; switch ( id ){ case Qtopia::Birthday:{ // These entries should stored in a special format // year-month-day QDate day = m_contact.birthday(); if ( day.isValid() ){ qu += QString(",\"%1-%2-%3\"") .arg( day.year() ) .arg( day.month() ) .arg( day.day() ); } else { qu += ",\"\""; } } break; case Qtopia::Anniversary:{ // These entries should stored in a special format // year-month-day QDate day = m_contact.anniversary(); if ( day.isValid() ){ qu += QString(",\"%1-%2-%3\"") .arg( day.year() ) .arg( day.month() ) .arg( day.day() ); } else { qu += ",\"\""; } } break; default: qu += QString( ",\"%1\"" ).arg( contactMap[id] ); } } qu += " );"; #else // Get all information out of the contact-class // Remember: The category is stored in contactMap, too ! QMap<int, QString> contactMap = m_contact.toMap(); QMap<QString, QString> addressbook_db; // Get the translation from the ID to the String QMap<int, QString> transMap = OContactFields::idToUntrFields(); for( QMap<int, QString>::Iterator it = contactMap.begin(); it != contactMap.end(); ++it ){ switch ( it.key() ){ case Qtopia::Birthday:{ // These entries should stored in a special format // year-month-day QDate day = m_contact.birthday(); addressbook_db.insert( transMap[it.key()], QString("%1-%2-%3") .arg( day.year() ) .arg( day.month() ) .arg( day.day() ) ); } break; case Qtopia::Anniversary:{ // These entries should stored in a special format // year-month-day QDate day = m_contact.anniversary(); addressbook_db.insert( transMap[it.key()], QString("%1-%2-%3") .arg( day.year() ) .arg( day.month() ) .arg( day.day() ) ); } break; case Qtopia::AddressUid: // Ignore UID break; default: // Translate id to String addressbook_db.insert( transMap[it.key()], it.data() ); break; } } // 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 ): 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(); + return m_driver->close(); // Shouldn't m_driver->sync be better than close ? (eilers) } void OContactAccessBackend_SQL::clear () { ClearQuery cle; OSQLResult res = m_driver->query( &cle ); - CreateQuery qu; - res = m_driver->query(&qu); + + reload(); } 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 " + "'" + queryStr.replace(QRegExp("\\*"),"%") + "'" + ") AND "; else qu += "(\"" + *it + "\"" + " GLOB " + "'" + queryStr + "'" + ") AND "; } } } // Skip trailing "AND" if ( isAnyFieldSelected ) qu = qu.left( qu.length() - 4 ); qWarning( "queryByExample query: %s", qu.latin1() ); // Execute query and return the received uid's OSQLRawQuery raw( qu ); OSQLResult res = m_driver->query( &raw ); if ( res.state() != OSQLResult::Success ){ QArray<int> empty; return empty; } QArray<int> list = extractUids( res ); return list; } QArray<int> OContactAccessBackend_SQL::matchRegexp( const QRegExp &r ) const { QArray<int> nix(0); return nix; } const uint OContactAccessBackend_SQL::querySettings() { return OContactAccess::IgnoreCase || OContactAccess::WildCards; } bool OContactAccessBackend_SQL::hasQuerySettings (uint querySettings) const { /* OContactAccess::IgnoreCase, DateDiff, DateYear, DateMonth, DateDay * may be added with any of the other settings. IgnoreCase should never used alone. * Wildcards, RegExp, ExactMatch should never used at the same time... */ // Step 1: Check whether the given settings are supported by this backend if ( ( querySettings & ( OContactAccess::IgnoreCase | OContactAccess::WildCards // | OContactAccess::DateDiff // | OContactAccess::DateYear // | OContactAccess::DateMonth // | OContactAccess::DateDay // | OContactAccess::RegExp // | OContactAccess::ExactMatch ) ) != querySettings ) return false; // Step 2: Check whether the given combinations are ok.. // IngoreCase alone is invalid if ( querySettings == OContactAccess::IgnoreCase ) return false; // WildCards, RegExp and ExactMatch should never used at the same time switch ( querySettings & ~( OContactAccess::IgnoreCase | OContactAccess::DateDiff | OContactAccess::DateYear | OContactAccess::DateMonth | OContactAccess::DateDay ) ){ case OContactAccess::RegExp: return ( true ); case OContactAccess::WildCards: return ( true ); case OContactAccess::ExactMatch: return ( true ); case 0: // one of the upper removed bits were set.. return ( true ); default: return ( false ); } } QArray<int> OContactAccessBackend_SQL::sorted( bool asc, int , int , int ) { QTime t; t.start(); #ifdef __STORE_HORIZONTAL_ QString query = "SELECT uid FROM addressbook "; query += "ORDER BY \"Last Name\" "; #else QString query = "SELECT uid FROM addressbook WHERE type = 'Last Name' "; query += "ORDER BY upper( value )"; #endif if ( !asc ) query += "DESC"; // qWarning("sorted query is: %s", query.latin1() ); OSQLRawQuery raw( query ); OSQLResult res = m_driver->query( &raw ); if ( res.state() != OSQLResult::Success ){ QArray<int> empty; return empty; } QArray<int> list = extractUids( res ); qWarning("sorted needed %d ms!", t.elapsed() ); return list; } void OContactAccessBackend_SQL::update() { qWarning("Update starts"); QTime t; t.start(); // Now load the database set and extract the uid's // which will be held locally LoadQuery lo; OSQLResult res = m_driver->query(&lo); if ( res.state() != OSQLResult::Success ) return; m_uids = extractUids( res ); m_changed = false; qWarning("Update ends %d ms", t.elapsed() ); } QArray<int> OContactAccessBackend_SQL::extractUids( OSQLResult& res ) const { qWarning("extractUids"); QTime t; t.start(); 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++; } qWarning("extractUids ready: count2 = %d needs %d ms", i, t.elapsed() ); return ints; } #ifdef __STORE_HORIZONTAL_ QMap<int, QString> OContactAccessBackend_SQL::requestNonCustom( int uid ) const { QTime t; t.start(); QMap<int, QString> nonCustomMap; int t2needed = 0; int t3needed = 0; QTime t2; t2.start(); FindQuery query( uid ); OSQLResult res_noncustom = m_driver->query( &query ); t2needed = t2.elapsed(); OSQLResultItem resItem = res_noncustom.first(); QTime t3; t3.start(); // Now loop through all columns QStringList fieldList = OContactFields::untrfields( false ); QMap<QString, int> translate = OContactFields::untrFieldsToId(); for ( QStringList::Iterator it = ++fieldList.begin(); it != fieldList.end(); ++it ){ // Get data for the selected column and store it with the // corresponding id into the map.. int id = translate[*it]; QString value = resItem.data( (*it) ); // qWarning("Reading %s... found: %s", (*it).latin1(), value.latin1() ); switch( id ){ case Qtopia::Birthday: case Qtopia::Anniversary:{ // Birthday and Anniversary are encoded special ( yyyy-mm-dd ) QStringList list = QStringList::split( '-', value ); QStringList::Iterator lit = list.begin(); int year = (*lit).toInt(); int month = (*(++lit)).toInt(); int day = (*(++lit)).toInt(); if ( ( day != 0 ) && ( month != 0 ) && ( year != 0 ) ){ QDate date( year, month, day ); nonCustomMap.insert( id, OConversion::dateToString( date ) ); } } break; case Qtopia::AddressCategory: qWarning("Category is: %s", value.latin1() ); default: nonCustomMap.insert( id, value ); } } // First insert uid nonCustomMap.insert( Qtopia::AddressUid, resItem.data( "uid" ) ); t3needed = t3.elapsed(); // qWarning("Adding UID: %s", resItem.data( "uid" ).latin1() ); qWarning("RequestNonCustom needed: insg.:%d ms, query: %d ms, mapping: %d ms", t.elapsed(), t2needed, t3needed ); return nonCustomMap; } #else QMap<int, QString> OContactAccessBackend_SQL::requestNonCustom( int uid ) const { QTime t; t.start(); QMap<int, QString> nonCustomMap; int t2needed = 0; QTime t2; t2.start(); FindQuery query( uid ); OSQLResult res_noncustom = m_driver->query( &query ); t2needed = t2.elapsed(); if ( res_noncustom.state() == OSQLResult::Failure ) { qWarning("OSQLResult::Failure in find query !!"); QMap<int, QString> empty; return empty; } int t3needed = 0; QTime t3; t3.start(); QMap<QString, int> translateMap = OContactFields::untrFieldsToId(); OSQLResultItem::ValueList list = res_noncustom.results(); OSQLResultItem::ValueList::Iterator it = list.begin(); for ( ; it != list.end(); ++it ) { if ( (*it).data("type") != "" ){ int typeId = translateMap[(*it).data( "type" )]; switch( typeId ){ case Qtopia::Birthday: case Qtopia::Anniversary:{ // Birthday and Anniversary are encoded special ( yyyy-mm-dd ) QStringList list = QStringList::split( '-', (*it).data( "value" ) ); QStringList::Iterator lit = list.begin(); int year = (*lit).toInt(); qWarning("1. %s", (*lit).latin1()); int month = (*(++lit)).toInt(); qWarning("2. %s", (*lit).latin1()); int day = (*(++lit)).toInt(); qWarning("3. %s", (*lit).latin1()); qWarning( "RequestNonCustom->Converting:%s to Year: %d, Month: %d, Day: %d ", (*it).data( "value" ).latin1(), year, month, day ); QDate date( year, month, day ); nonCustomMap.insert( typeId, OConversion::dateToString( date ) ); } break; default: nonCustomMap.insert( typeId, (*it).data( "value" ) ); } } } // Add UID to Map.. nonCustomMap.insert( Qtopia::AddressUid, QString::number( uid ) ); t3needed = t3.elapsed(); qWarning("RequestNonCustom needed: insg.:%d ms, query: %d ms, mapping: %d ms", t.elapsed(), t2needed, t3needed ); return nonCustomMap; } #endif // __STORE_HORIZONTAL_ QMap<QString, QString> OContactAccessBackend_SQL::requestCustom( int uid ) const diff --git a/libopie2/opiepim/backend/odatebookaccessbackend.cpp b/libopie2/opiepim/backend/odatebookaccessbackend.cpp index 8fa1a68..f0c5d65 100644 --- a/libopie2/opiepim/backend/odatebookaccessbackend.cpp +++ b/libopie2/opiepim/backend/odatebookaccessbackend.cpp @@ -1,156 +1,182 @@ #include <qtl.h> #include "orecur.h" #include "odatebookaccessbackend.h" namespace { /* a small helper to get all NonRepeating events for a range of time */ void events( OEffectiveEvent::ValueList& tmpList, const OEvent::ValueList& events, const QDate& from, const QDate& to ) { QDateTime dtStart, dtEnd; for ( OEvent::ValueList::ConstIterator it = events.begin(); it != events.end(); ++it ) { dtStart = (*it).startDateTime(); dtEnd = (*it).endDateTime(); /* * If in range */ if (dtStart.date() >= from && dtEnd.date() <= to ) { OEffectiveEvent eff; eff.setEvent( (*it) ); eff.setDate( dtStart.date() ); eff.setStartTime( dtStart.time() ); /* if not on the same day */ if ( dtStart.date() != dtEnd.date() ) eff.setEndTime( QTime(23, 59, 0 ) ); else eff.setEndTime( dtEnd.time() ); tmpList.append( eff ); } /* we must also check for end date information... */ if ( dtEnd.date() != dtStart.date() && dtEnd.date() >= from ) { QDateTime dt = dtStart.addDays( 1 ); dt.setTime( QTime(0, 0, 0 ) ); QDateTime dtStop; if ( dtEnd > to ) dtStop = to; else dtStop = dtEnd; while ( dt <= dtStop ) { OEffectiveEvent eff; eff.setEvent( (*it) ); eff.setDate( dt.date() ); if ( dt >= from ) { eff.setStartTime( QTime(0, 0, 0 ) ); if ( dt.date() == dtEnd.date() ) eff.setEndTime( dtEnd.time() ); else eff.setEndTime( QTime(23, 59, 0 ) ); tmpList.append( eff ); } dt = dt.addDays( 1 ); } } } } void repeat( OEffectiveEvent::ValueList& tmpList, const OEvent::ValueList& list, const QDate& from, const QDate& to ) { QDate repeat; for ( OEvent::ValueList::ConstIterator it = list.begin(); it != list.end(); ++it ) { int dur = (*it).startDateTime().date().daysTo( (*it).endDateTime().date() ); QDate itDate = from.addDays(-dur ); ORecur rec = (*it).recurrence(); if ( !rec.hasEndDate() || rec.endDate() > to ) { rec.setEndDate( to ); rec.setHasEndDate( true ); } while (rec.nextOcurrence(itDate, repeat ) ) { if (repeat > to ) break; OEffectiveEvent eff; eff.setDate( repeat ); if ( (*it).isAllDay() ) { eff.setStartTime( QTime(0, 0, 0 ) ); eff.setEndTime( QTime(23, 59, 59 ) ); }else { /* we only occur by days, not hours/minutes/seconds. Hence * the actual end and start times will be the same for * every repeated event. For multi day events this is * fixed up later if on wronge day span */ eff.setStartTime( (*it).startDateTime().time() ); eff.setEndTime( (*it).endDateTime().time() ); } if ( dur != 0 ) { // multi-day repeating events QDate sub_it = QMAX( repeat, from ); QDate startDate = repeat; QDate endDate = startDate.addDays( dur ); while ( sub_it <= endDate && sub_it <= to ) { OEffectiveEvent tmpEff = eff; tmpEff.setEvent( (*it) ); if ( sub_it != startDate ) tmpEff.setStartTime( QTime(0, 0, 0 ) ); if ( sub_it != endDate ) tmpEff.setEndTime( QTime( 23, 59, 59 ) ); tmpEff.setDate( sub_it ); tmpEff.setEffectiveDates( startDate, endDate ); tmpList.append( tmpEff ); sub_it = sub_it.addDays( 1 ); } itDate = endDate; }else { eff.setEvent( (*it) ); tmpList.append( eff ); itDate = repeat.addDays( 1 ); } } } } } ODateBookAccessBackend::ODateBookAccessBackend() : OPimAccessBackend<OEvent>() { } ODateBookAccessBackend::~ODateBookAccessBackend() { } -OEffectiveEvent::ValueList ODateBookAccessBackend::effecticeEvents( const QDate& from, +OEffectiveEvent::ValueList ODateBookAccessBackend::effectiveEvents( const QDate& from, const QDate& to ) { OEffectiveEvent::ValueList tmpList; OEvent::ValueList list = directNonRepeats(); events( tmpList, list, from, to ); repeat( tmpList, directRawRepeats(),from,to ); - list = directRawRepeats(); + list = directRawRepeats(); // Useless, isn't it ? (eilers) qHeapSort( tmpList ); return tmpList; } -OEffectiveEvent::ValueList ODateBookAccessBackend::effecticeEvents( const QDateTime& dt ) { - OEffectiveEvent::ValueList day = effecticeEvents( dt.date(), dt.date() ); +OEffectiveEvent::ValueList ODateBookAccessBackend::effectiveEvents( const QDateTime& dt ) { + OEffectiveEvent::ValueList day = effectiveEvents( dt.date(), dt.date() ); + OEffectiveEvent::ValueList::Iterator it; + + OEffectiveEvent::ValueList tmpList; + QDateTime dtTmp; + for ( it = day.begin(); it != day.end(); ++it ) { + dtTmp = QDateTime( (*it).date(), (*it).startTime() ); + if ( QABS(dt.secsTo(dtTmp) ) < 60 ) + tmpList.append( (*it) ); + } + + return tmpList; +} + +OEffectiveEvent::ValueList ODateBookAccessBackend::effectiveNonRepeatingEvents( const QDate& from, + const QDate& to ) { + OEffectiveEvent::ValueList tmpList; + OEvent::ValueList list = directNonRepeats(); + + events( tmpList, list, from, to ); + + qHeapSort( tmpList ); + return tmpList; +} + +OEffectiveEvent::ValueList ODateBookAccessBackend::effectiveNonRepeatingEvents( const QDateTime& dt ) { + OEffectiveEvent::ValueList day = effectiveNonRepeatingEvents( dt.date(), dt.date() ); OEffectiveEvent::ValueList::Iterator it; OEffectiveEvent::ValueList tmpList; QDateTime dtTmp; for ( it = day.begin(); it != day.end(); ++it ) { dtTmp = QDateTime( (*it).date(), (*it).startTime() ); if ( QABS(dt.secsTo(dtTmp) ) < 60 ) tmpList.append( (*it) ); } return tmpList; } diff --git a/libopie2/opiepim/backend/odatebookaccessbackend.h b/libopie2/opiepim/backend/odatebookaccessbackend.h index 3c02c42..3472ab3 100644 --- a/libopie2/opiepim/backend/odatebookaccessbackend.h +++ b/libopie2/opiepim/backend/odatebookaccessbackend.h @@ -1,77 +1,90 @@ #ifndef OPIE_DATE_BOOK_ACCESS_BACKEND_H #define OPIE_DATE_BOOK_ACCESS_BACKEND_H #include <qarray.h> #include "opimaccessbackend.h" #include "oevent.h" /** * This class is the interface to the storage of Events. * @see OPimAccessBackend * */ class ODateBookAccessBackend : public OPimAccessBackend<OEvent> { public: typedef int UID; /** * c'tor without parameter */ ODateBookAccessBackend(); ~ODateBookAccessBackend(); /** * This method should return a list of UIDs containing * all events. No filter should be applied * @return list of events */ virtual QArray<UID> rawEvents()const = 0; /** * This method should return a list of UIDs containing * all repeating events. No filter should be applied * @return list of repeating events */ virtual QArray<UID> rawRepeats()const = 0; /** * This mthod should return a list of UIDs containing all non * repeating events. No filter should be applied * @return list of nonrepeating events */ virtual QArray<UID> nonRepeats() const = 0; /** * If you do not want to implement the effectiveEvents methods below * you need to supply it with directNonRepeats. * This method can return empty lists if effectiveEvents is implememted */ virtual OEvent::ValueList directNonRepeats() = 0; /** * Same as above but return raw repeats! */ virtual OEvent::ValueList directRawRepeats() = 0; /* is implemented by default but you can reimplement it*/ /** * Effective Events are special event occuring during a time frame. This method does calcualte * EffectiveEvents bases on the directNonRepeats and directRawRepeats. You may implement this method * yourself */ - virtual OEffectiveEvent::ValueList effecticeEvents( const QDate& from, const QDate& to ); + virtual OEffectiveEvent::ValueList effectiveEvents( const QDate& from, const QDate& to ); /** * this is an overloaded member function - * @see effecticeEvents + * @see effectiveEvents( const QDate& from, const QDate& to ) */ - virtual OEffectiveEvent::ValueList effecticeEvents( const QDateTime& start ); + virtual OEffectiveEvent::ValueList effectiveEvents( const QDateTime& start ); + + /** + * Effective Events are special event occuring during a time frame. This method does calcualte + * EffectiveEvents bases on the directNonRepeats and directRawRepeats. You may implement this method + * yourself + */ + virtual OEffectiveEvent::ValueList effectiveNonRepeatingEvents( const QDate& from, const QDate& to ); + + /** + * this is an overloaded member function + * @see effectiveNonRepeatingEvents( const QDate& from, const QDate& to ) + */ + virtual OEffectiveEvent::ValueList effectiveNonRepeatingEvents( const QDateTime& start ); private: class Private; Private *d; }; #endif diff --git a/libopie2/opiepim/backend/odatebookaccessbackend_sql.cpp b/libopie2/opiepim/backend/odatebookaccessbackend_sql.cpp index 9769bf7..e893b38 100644 --- a/libopie2/opiepim/backend/odatebookaccessbackend_sql.cpp +++ b/libopie2/opiepim/backend/odatebookaccessbackend_sql.cpp @@ -1,221 +1,356 @@ /* * 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.2 2003/12/22 10:19:26 eilers + * Finishing implementation of sql-backend for datebook. But I have to + * port the PIM datebook application to use it, before I could debug the + * whole stuff. + * Thus, PIM-Database backend is finished, but highly experimental. And some + * parts are still generic. For instance, the "queryByExample()" methods are + * not (or not fully) implemented. Todo: custom-entries not stored. + * The big show stopper: matchRegExp() (needed by OpieSearch) needs regular + * expression search in the database, which is not supported by sqlite ! + * Therefore we need either an extended sqlite or a workaround which would + * be very slow and memory consuming.. + * * 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 <qpe/global.h> #include <opie2/osqldriver.h> -#include <opie2/osqlresult.h> #include <opie2/osqlmanager.h> #include <opie2/osqlquery.h> -namespace { - +#include "orecur.h" +#include "odatebookaccessbackend_sql.h" -}; ODateBookAccessBackend_SQL::ODateBookAccessBackend_SQL( const QString& , const QString& fileName ) : ODateBookAccessBackend(), m_driver( NULL ) { - m_fileName = fileName.isEmpty() ? Global::applicationFileName( "datebook", "datebook.db" ) : fileName; + 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() { + if( m_driver ) + delete m_driver; } 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::FRExceptions, "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" ); + + // Create a map that maps the column name to the id + QMapConstIterator<int, QString> it; + for ( it = ++m_fieldMap.begin(); it != m_fieldMap.end(); ++it ){ + m_reverseFieldMap.insert( it.data(), it.key() ); + } + } 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 += 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) );"; + qWarning( "command: %s", qu.latin1() ); + 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(); + // 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(); + return load(); } bool ODateBookAccessBackend_SQL::save() { - return m_driver->close(); + return m_driver->close(); // Shouldn't m_driver->sync be better than close ? (eilers) } QArray<int> ODateBookAccessBackend_SQL::allRecords()const { - return m_uids; + return m_uids; } QArray<int> ODateBookAccessBackend_SQL::queryByExample(const OEvent&, int, const QDateTime& ) { - return QArray<int>(); + 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 ); + reload(); } OEvent ODateBookAccessBackend_SQL::find( int uid ) const{ + QString qu = "select *"; + qu += "from datebook where uid = " + QString::number(uid); + + OSQLRawQuery raw( qu ); + OSQLResult res = m_driver->query( &raw ); + + OSQLResultItem resItem = res.first(); + + // Create Map for date event and insert UID + QMap<int,QString> dateEventMap; + dateEventMap.insert( OEvent::FUid, QString::number( uid ) ); + + // Now insert the data out of the columns into the map. + QMapConstIterator<int, QString> it; + for ( it = ++m_fieldMap.begin(); it != m_fieldMap.end(); ++it ){ + dateEventMap.insert( m_reverseFieldMap[*it], resItem.data( *it ) ); + } + + // Last step: Put map into date event and return it + OEvent retDate( dateEventMap ); + + return retDate; } -bool ODateBookAccessBackend_SQL::add( const OEvent& ev ) { - return true; +bool ODateBookAccessBackend_SQL::add( const OEvent& ev ) +{ + QMap<int,QString> eventMap = ev.toMap(); + + QString qu = "insert into datebook VALUES( " + QString::number( ev.uid() ); + QMap<int, QString>::Iterator it; + for ( it = ++m_fieldMap.begin(); it != m_fieldMap.end(); ++it ){ + if ( !eventMap[it.key()].isEmpty() ) + qu += QString( ",\"%1\"" ).arg( eventMap[it.key()] ); + else + qu += QString( ",\"\"" ); + } + qu += " );"; + + // Add custom entries + int id = 0; + QMap<QString, QString> customMap = ev.toExtraMap(); + for( QMap<QString, QString>::Iterator it = customMap.begin(); + it != customMap.end(); ++it ){ + qu += "insert into custom_data VALUES(" + + QString::number( ev.uid() ) + + "," + + QString::number( id++ ) + + ",'" + + it.key() //.latin1() + + "'," + + "0" // Priority for future enhancements + + ",'" + + it.data() //.latin1() + + "');"; + } + qWarning("add %s", qu.latin1() ); + + OSQLRawQuery raw( qu ); + OSQLResult res = m_driver->query( &raw ); + if ( res.state() != OSQLResult::Success ){ + return false; + } + + return true; } -bool ODateBookAccessBackend_SQL::remove( int uid ) { - return true; +bool ODateBookAccessBackend_SQL::remove( int uid ) +{ + QString qu = "DELETE from datebook where uid = " + + QString::number( uid ) + ";"; + qu += "DELETE from custom_data where uid = " + + QString::number( uid ) + ";"; + + OSQLRawQuery raw( qu ); + OSQLResult res = m_driver->query( &raw ); + if ( res.state() != OSQLResult::Success ){ + return false; + } + + return true; } -bool ODateBookAccessBackend_SQL::replace( const OEvent& ev ) { + +bool ODateBookAccessBackend_SQL::replace( const OEvent& ev ) +{ remove( ev.uid() ); return add( ev ); } -QArray<int> ODateBookAccessBackend_SQL::rawEvents()const { + +QArray<int> ODateBookAccessBackend_SQL::rawEvents()const +{ return allRecords(); } -QArray<int> ODateBookAccessBackend_SQL::rawRepeats()const { - return ints; +QArray<int> ODateBookAccessBackend_SQL::rawRepeats()const +{ + QString qu = "select uid from datebook where RType!=\"\" AND RType!=\"NoRepeat\""; + OSQLRawQuery raw( qu ); + OSQLResult res = m_driver->query( &raw ); + if ( res.state() != OSQLResult::Success ){ + QArray<int> nix; + return nix; + } + + return extractUids( res ); } -QArray<int> ODateBookAccessBackend_SQL::nonRepeats()const { - return ints; +QArray<int> ODateBookAccessBackend_SQL::nonRepeats()const +{ + QString qu = "select uid from datebook where RType=\"\" or RType=\"NoRepeat\""; + OSQLRawQuery raw( qu ); + OSQLResult res = m_driver->query( &raw ); + if ( res.state() != OSQLResult::Success ){ + QArray<int> nix; + return nix; + } + + return extractUids( res ); } -OEvent::ValueList ODateBookAccessBackend_SQL::directNonRepeats() { - return list; +OEvent::ValueList ODateBookAccessBackend_SQL::directNonRepeats() +{ + QArray<int> nonRepUids = nonRepeats(); + OEvent::ValueList list; + + for (uint i = 0; i < nonRepUids.count(); ++i ){ + list.append( find( nonRepUids[i] ) ); + } + + return list; + } -OEvent::ValueList ODateBookAccessBackend_SQL::directRawRepeats() { +OEvent::ValueList ODateBookAccessBackend_SQL::directRawRepeats() +{ + QArray<int> rawRepUids = rawRepeats(); + OEvent::ValueList list; - return list; + for (uint i = 0; i < rawRepUids.count(); ++i ){ + list.append( find( rawRepUids[i] ) ); + } + + return list; } QArray<int> ODateBookAccessBackend_SQL::matchRegexp( const QRegExp &r ) const { + QArray<int> null; + return null; +} + +/* ===== Private Functions ========================================== */ + +QArray<int> ODateBookAccessBackend_SQL::extractUids( OSQLResult& res ) const +{ + qWarning("extractUids"); + QTime t; + t.start(); + 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++; + } + qWarning("extractUids ready: count2 = %d needs %d ms", i, t.elapsed() ); + + return ints; - return m_currentQuery; } diff --git a/libopie2/opiepim/backend/odatebookaccessbackend_sql.h b/libopie2/opiepim/backend/odatebookaccessbackend_sql.h index 85e0d4f..f39e154 100644 --- a/libopie2/opiepim/backend/odatebookaccessbackend_sql.h +++ b/libopie2/opiepim/backend/odatebookaccessbackend_sql.h @@ -1,60 +1,62 @@ #ifndef OPIE_DATE_BOOK_ACCESS_BACKEND_SQL__H #define OPIE_DATE_BOOK_ACCESS_BACKEND_SQL__H #include <qmap.h> +#include <opie2/osqlresult.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; + QMap<QString, int> m_reverseFieldMap; OSQLDriver* m_driver; class Private; Private *d; void initFields(); void update(); QArray<int> extractUids( OSQLResult& res ) const; }; #endif diff --git a/libopie2/opiepim/backend/otodoaccesssql.cpp b/libopie2/opiepim/backend/otodoaccesssql.cpp index 75a0860..3764c7e 100644 --- a/libopie2/opiepim/backend/otodoaccesssql.cpp +++ b/libopie2/opiepim/backend/otodoaccesssql.cpp @@ -1,693 +1,693 @@ #include <qdatetime.h> #include <qpe/global.h> #include <opie2/osqldriver.h> #include <opie2/osqlresult.h> #include <opie2/osqlmanager.h> #include <opie2/osqlquery.h> #include "otodoaccesssql.h" #include "opimstate.h" #include "opimnotifymanager.h" #include "orecur.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(const QArray<int>& ); ~FindQuery(); QString query()const; private: QString single()const; QString multi()const; QArray<int> m_uids; 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 PRIMARY KEY, categories, completed, "; qu += "description, summary, priority, DueDate, progress , state, "; // This is the recurrance-stuff .. Exceptions are currently not supported (see ORecur.cpp) ! (eilers) qu += "RType, RWeekdays, RPosition, RFreq, RHasEndDate, EndDate, Created, Exceptions, "; qu += "reminders, alarms, maintainer, startdate, completeddate);"; qu += "create table custom_data( uid INTEGER, id INTEGER, type VARCHAR(10), value VARCHAR(10), PRIMARY KEY /* identifier */ (uid, id) );"; return qu; } LoadQuery::LoadQuery() : OSQLQuery() {} LoadQuery::~LoadQuery() {} QString LoadQuery::query()const { QString qu; // We do not need "distinct" here. The primary key is always unique.. //qu += "select distinct uid from todolist"; qu += "select 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(); } int sYear = 0, sMonth = 0, sDay = 0; if( m_todo.hasStartDate() ){ QDate sDate = m_todo.startDate(); sYear = sDate.year(); sMonth= sDate.month(); sDay = sDate.day(); } int eYear = 0, eMonth = 0, eDay = 0; if( m_todo.hasCompletedDate() ){ QDate eDate = m_todo.completedDate(); 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_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(); + return m_driver->close(); // Shouldn't m_driver->sync be better than close ? (eilers) } 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 * */ /* Category */ if ( sortFilter & 1 ) { QString str; if (cat != 0 ) str = QString::number( cat ); query += " categories like '%" +str+"%' AND"; } /* Show only overdue */ if ( sortFilter & 2 ) { QDate date = QDate::currentDate(); QString due; QString base; base = QString("DueDate <= '%1-%2-%3' AND completed = 0").arg( date.year() ).arg( date.month() ).arg( date.day() ); query += " " + base + " AND"; } /* not show completed */ if ( sortFilter & 4 ) { query += " completed = 0 AND"; }else{ query += " ( completed = 1 OR completed = 0) AND"; } /* srtip the end */ query = query.remove( query.length()-3, 3 ); /* * sort order stuff * quite straight forward */ query += "ORDER BY "; switch( sortOrder ) { /* completed */ case 0: query += "completed"; break; case 1: query += "priority"; break; case 2: query += "summary"; break; case 3: query += "DueDate"; break; } if ( !asc ) { qWarning("not ascending!"); query += " DESC"; } qWarning( query ); OSQLRawQuery raw(query ); return uids( m_driver->query(&raw) ); } 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(); qWarning("todo1"); OTodo to = todo( (*it) ); cache( to ); ++it; for ( ; it != list.end(); ++it ) { qWarning("caching"); cache( todo( (*it) ) ); } return to; } OTodo OTodoAccessBackendSQL::todo( OSQLResultItem& item )const { qWarning("todo"); bool hasDueDate = false; QDate dueDate = QDate::currentDate(); hasDueDate = date( dueDate, item.data("DueDate") ); QStringList cats = QStringList::split(";", item.data("categories") ); qWarning("Item is completed: %d", item.data("completed").toInt() ); OTodo to( (bool)item.data("completed").toInt(), item.data("priority").toInt(), cats, item.data("summary"), item.data("description"), item.data("progress").toUShort(), hasDueDate, dueDate, item.data("uid").toInt() ); bool isOk; int prioInt = QString( item.data("priority") ).toInt( &isOk ); if ( isOk ) to.setPriority( prioInt ); bool hasStartDate = false; QDate startDate = QDate::currentDate(); hasStartDate = date( startDate, item.data("startdate") ); bool hasCompletedDate = false; QDate completedDate = QDate::currentDate(); hasCompletedDate = date( completedDate, item.data("completeddate") ); if ( hasStartDate ) to.setStartDate( startDate ); if ( hasCompletedDate ) to.setCompletedDate( completedDate ); OPimNotifyManager& manager = to.notifiers(); manager.alarmsFromString( item.data("alarms") ); manager.remindersFromString( item.data("reminders") ); OPimState pimState; pimState.setState( QString( item.data("state") ).toInt() ); to.setState( pimState ); QMap<int, QString> recMap; recMap.insert( ORecur::RType , item.data("RType") ); recMap.insert( ORecur::RWeekdays , item.data("RWeekdays") ); recMap.insert( ORecur::RPosition , item.data("RPosition") ); recMap.insert( ORecur::RFreq , item.data("RFreq") ); recMap.insert( ORecur::RHasEndDate, item.data("RHasEndDate") ); recMap.insert( ORecur::EndDate , item.data("EndDate") ); recMap.insert( ORecur::Created , item.data("Created") ); recMap.insert( ORecur::Exceptions , item.data("Exceptions") ); ORecur recur; recur.fromMap( recMap ); to.setRecurrence( recur ); 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!!! * FIXME: Isn't this dict obsolete ? (eilers) */ 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) ); // Why twice ? (eilers) m_dict.insert("CrossReference", new int(OTodo::CrossReference) ); // m_dict.insert("HasAlarmDateTime",new int(OTodo::HasAlarmDateTime) ); // old stuff (eilers) // m_dict.insert("AlarmDateTime", new int(OTodo::AlarmDateTime) ); // old stuff (eilers) } /* * need to be const so let's fool the * compiler :( */ void OTodoAccessBackendSQL::update()const { ((OTodoAccessBackendSQL*)this)->m_dirty = false; LoadQuery lo; OSQLResult res = m_driver->query(&lo); if ( res.state() != OSQLResult::Success ) return; ((OTodoAccessBackendSQL*)this)->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() ); 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; } QArray<int> OTodoAccessBackendSQL::matchRegexp( const QRegExp &r ) const { #warning OTodoAccessBackendSQL::matchRegexp() not implemented !! #if 0 Copied from xml-backend by not adapted to sql (eilers) QArray<int> m_currentQuery( m_events.count() ); uint arraycounter = 0; QMap<int, OTodo>::ConstIterator it; for (it = m_events.begin(); it != m_events.end(); ++it ) { if ( it.data().match( r ) ) m_currentQuery[arraycounter++] = it.data().uid(); } // Shrink to fit.. m_currentQuery.resize(arraycounter); return m_currentQuery; #endif QArray<int> empty; return empty; } QBitArray OTodoAccessBackendSQL::supports()const { return sup(); } QBitArray OTodoAccessBackendSQL::sup() const{ QBitArray ar( OTodo::CompletedDate + 1 ); ar.fill( true ); ar[OTodo::CrossReference] = false; ar[OTodo::State ] = false; ar[OTodo::Reminders] = false; ar[OTodo::Notifiers] = false; ar[OTodo::Maintainer] = false; return ar; } void OTodoAccessBackendSQL::removeAllCompleted(){ #warning OTodoAccessBackendSQL::removeAllCompleted() not implemented !! } diff --git a/libopie2/opiepim/core/ocontactaccess.h b/libopie2/opiepim/core/ocontactaccess.h index 9b0a719..bd6da40 100644 --- a/libopie2/opiepim/core/ocontactaccess.h +++ b/libopie2/opiepim/core/ocontactaccess.h @@ -1,181 +1,193 @@ /* * Class to manage the Contacts. * * Copyright (c) 2002 by Stefan Eilers (Eilers.Stefan@epost.de) * Copyright (c) 2002 by Holger Freyther (zecke@handhelds.org) * * ===================================================================== * 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. * ===================================================================== * ToDo: Define enum for query settings * ===================================================================== * Version: $Id$ * ===================================================================== * History: * $Log$ + * Revision 1.10 2003/12/22 10:19:26 eilers + * Finishing implementation of sql-backend for datebook. But I have to + * port the PIM datebook application to use it, before I could debug the + * whole stuff. + * Thus, PIM-Database backend is finished, but highly experimental. And some + * parts are still generic. For instance, the "queryByExample()" methods are + * not (or not fully) implemented. Todo: custom-entries not stored. + * The big show stopper: matchRegExp() (needed by OpieSearch) needs regular + * expression search in the database, which is not supported by sqlite ! + * Therefore we need either an extended sqlite or a workaround which would + * be very slow and memory consuming.. + * * Revision 1.9 2003/08/01 12:30:16 eilers * Merging changes from BRANCH_1_0 to HEAD * * Revision 1.8.2.1 2003/06/30 14:34:19 eilers * Patches from Zecke: * Fixing and cleaning up extraMap handling * Adding d_ptr for binary compatibility in the future * * Revision 1.8 2003/05/08 13:55:09 tille * search stuff * and match, toRichText & toShortText in oevent * * Revision 1.7 2003/04/13 18:07:10 zecke * More API doc * QString -> const QString& * QString = 0l -> QString::null * * Revision 1.6 2003/01/02 14:27:12 eilers * Improved query by example: Search by date is possible.. First step * for a today plugin for birthdays.. * * Revision 1.5 2002/11/13 14:14:51 eilers * Added sorted for Contacts.. * * Revision 1.4 2002/11/01 15:10:42 eilers * Added regExp-search in database for all fields in a contact. * * Revision 1.3 2002/10/16 10:52:40 eilers * Added some docu to the interface and now using the cache infrastucture by zecke.. :) * * Revision 1.2 2002/10/14 16:21:54 eilers * Some minor interface updates * * Revision 1.1 2002/09/27 17:11:44 eilers * Added API for accessing the Contact-Database ! It is compiling, but * please do not expect that anything is working ! * I will debug that stuff in the next time .. * Please read README_COMPILE for compiling ! * * ===================================================================== */ #ifndef _OCONTACTACCESS_H #define _OCONTACTACCESS_H #include <qobject.h> #include <qpe/qcopenvelope_qws.h> #include <qvaluelist.h> #include <qfileinfo.h> #include "ocontact.h" #include "ocontactaccessbackend.h" #include "opimaccesstemplate.h" /** * Class to access the contacts database. * This is just a frontend for the real database handling which is * done by the backend. * This class is used to access the Contacts on a system. This class as any OPIE PIM * class is backend independent. - + * @author Stefan Eilers, Holger Freyther * @see OPimAccessTemplate */ class OContactAccess: public QObject, public OPimAccessTemplate<OContact> { Q_OBJECT public: /** * Create Database with contacts (addressbook). * @param appname Name of application which wants access to the database * (i.e. "todolist") * @param filename The name of the database file. If not set, the default one * is used. * @param backend Pointer to an alternative Backend. If not set, we will use * the default backend. * @param handlesync If <b>true</b> the database stores the current state * automatically if it receives the signals <i>flush()</i> and <i>reload()</i> * which are used before and after synchronisation. If the application wants * to react itself, it should be disabled by setting it to <b>false</b> * @see OContactAccessBackend */ OContactAccess (const QString appname, const QString filename = 0l, OContactAccessBackend* backend = 0l, bool handlesync = true); ~OContactAccess (); /** Constants for query. * Use this constants to set the query parameters. * Note: <i>query_IgnoreCase</i> just make sense with one of the other attributes ! * @see queryByExample() */ enum QuerySettings { WildCards = 0x0001, IgnoreCase = 0x0002, RegExp = 0x0004, ExactMatch = 0x0008, MatchOne = 0x0010, // Only one Entry must match DateDiff = 0x0020, // Find all entries from today until given date DateYear = 0x0040, // The year matches DateMonth = 0x0080, // The month matches DateDay = 0x0100, // The day matches }; /** Return all Contacts in a sorted manner. * @param ascending true: Sorted in acending order. * @param sortOrder Currently not implemented. Just defined to stay compatible to otodoaccess * @param sortFilter Currently not implemented. Just defined to stay compatible to otodoaccess * @param cat Currently not implemented. Just defined to stay compatible to otodoaccess */ List sorted( bool ascending, int sortOrder, int sortFilter, int cat ) const; /** Return all possible settings. * @return All settings provided by the current backend * (i.e.: query_WildCards & query_IgnoreCase) */ const uint querySettings(); /** Check whether settings are correct. * @return <i>true</i> if the given settings are correct and possible. */ bool hasQuerySettings ( int querySettings ) const; /** * if the resource was changed externally. * You should use the signal instead of polling possible changes ! */ bool wasChangedExternally()const; /** Save contacts database. * Save is more a "commit". After calling this function, all changes are public available. * @return true if successful */ bool save(); signals: /* Signal is emitted if the database was changed. Therefore * we may need to reload to stay consistent. * @param which Pointer to the database who created this event. This pointer * is useful if an application has to handle multiple databases at the same time. * @see reload() */ void signalChanged ( const OContactAccess *which ); private: // class OContactAccessPrivate; // OContactAccessPrivate* d; OContactAccessBackend *m_backEnd; bool m_loading:1; private slots: void copMessage( const QCString &msg, const QByteArray &data ); private: class Private; Private *d; }; #endif diff --git a/libopie2/opiepim/core/odatebookaccess.cpp b/libopie2/opiepim/core/odatebookaccess.cpp index a3661a3..82934f9 100644 --- a/libopie2/opiepim/core/odatebookaccess.cpp +++ b/libopie2/opiepim/core/odatebookaccess.cpp @@ -1,66 +1,81 @@ #include "obackendfactory.h" #include "odatebookaccess.h" /** * Simple constructor * It takes a ODateBookAccessBackend as parent. If it is 0 the default implementation * will be used! * @param back The backend to be used or 0 for the default backend * @param ac What kind of access is intended */ ODateBookAccess::ODateBookAccess( ODateBookAccessBackend* back, enum Access ac ) : OPimAccessTemplate<OEvent>( back ) { if (!back ) back = OBackendFactory<ODateBookAccessBackend>::Default("datebook", QString::null ); m_backEnd = back; setBackEnd( m_backEnd ); } ODateBookAccess::~ODateBookAccess() { } /** * @return all events available */ ODateBookAccess::List ODateBookAccess::rawEvents()const { QArray<int> ints = m_backEnd->rawEvents(); List lis( ints, this ); return lis; } /** * @return all repeating events */ ODateBookAccess::List ODateBookAccess::rawRepeats()const { QArray<int> ints = m_backEnd->rawRepeats(); List lis( ints, this ); return lis; } /** * @return all non repeating events */ ODateBookAccess::List ODateBookAccess::nonRepeats()const { QArray<int> ints = m_backEnd->nonRepeats(); List lis( ints, this ); return lis; } /** * @return dates in the time span between from and to * @param from Include all events from... * @param to Include all events to... */ OEffectiveEvent::ValueList ODateBookAccess::effectiveEvents( const QDate& from, const QDate& to ) { - return m_backEnd->effecticeEvents( from, to ); + return m_backEnd->effectiveEvents( from, to ); } /** * @return all events at a given datetime */ OEffectiveEvent::ValueList ODateBookAccess::effectiveEvents( const QDateTime& start ) { - return m_backEnd->effecticeEvents( start ); + return m_backEnd->effectiveEvents( start ); +} + +/** + * @return non repeating dates in the time span between from and to + * @param from Include all events from... + * @param to Include all events to... + */ +OEffectiveEvent::ValueList ODateBookAccess::effectiveNonRepeatingEvents( const QDate& from, const QDate& to ) { + return m_backEnd->effectiveNonRepeatingEvents( from, to ); +} +/** + * @return all non repeating events at a given datetime + */ +OEffectiveEvent::ValueList ODateBookAccess::effectiveNonRepeatingEvents( const QDateTime& start ) { + return m_backEnd->effectiveNonRepeatingEvents( start ); } diff --git a/libopie2/opiepim/core/odatebookaccess.h b/libopie2/opiepim/core/odatebookaccess.h index 7c7a63f..62196da 100644 --- a/libopie2/opiepim/core/odatebookaccess.h +++ b/libopie2/opiepim/core/odatebookaccess.h @@ -1,41 +1,44 @@ #ifndef OPIE_DATE_BOOK_ACCESS_H #define OPIE_DATE_BOOK_ACCESS_H #include "odatebookaccessbackend.h" #include "opimaccesstemplate.h" #include "oevent.h" /** * This is the object orientated datebook database. It'll use OBackendFactory * to query for a backend. * All access to the datebook should be done via this class. * Make sure to load and save the datebook this is not part of * destructing and creating the object * - * @author Holger Freyther + * @author Holger Freyther, Stefan Eilers */ class ODateBookAccess : public OPimAccessTemplate<OEvent> { public: ODateBookAccess( ODateBookAccessBackend* = 0l, enum Access acc = Random ); ~ODateBookAccess(); /* return all events */ List rawEvents()const; /* return repeating events */ List rawRepeats()const; /* return non repeating events */ List nonRepeats()const; - OEffectiveEvent::ValueList effectiveEvents( const QDate& from, const QDate& to ); - OEffectiveEvent::ValueList effectiveEvents( const QDateTime& start ); + /* return non repeating events (from,to) */ + OEffectiveEvent::ValueList effectiveEvents( const QDate& from, const QDate& to ) const; + OEffectiveEvent::ValueList effectiveEvents( const QDateTime& start ) const; + OEffectiveEvent::ValueList effectiveNonRepeatingEvents( const QDate& from, const QDate& to ) const; + OEffectiveEvent::ValueList effectiveNonRepeatingEvents( const QDateTime& start ) const; private: ODateBookAccessBackend* m_backEnd; class Private; Private* d; }; #endif diff --git a/libopie2/opiepim/core/otimezone.cpp b/libopie2/opiepim/core/otimezone.cpp index b2bd3aa..34659c3 100644 --- a/libopie2/opiepim/core/otimezone.cpp +++ b/libopie2/opiepim/core/otimezone.cpp @@ -1,104 +1,113 @@ #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include "otimezone.h" namespace { QDateTime utcTime( time_t t) { tm* broken = ::gmtime( &t ); QDateTime ret; ret.setDate( QDate( broken->tm_year + 1900, broken->tm_mon +1, broken->tm_mday ) ); ret.setTime( QTime( broken->tm_hour, broken->tm_min, broken->tm_sec ) ); return ret; } QDateTime utcTime( time_t t, const QString& zone) { QCString org = ::getenv( "TZ" ); +#ifndef Q_OS_MACX // Following line causes bus errors on Mac ::setenv( "TZ", zone.latin1(), true ); ::tzset(); tm* broken = ::localtime( &t ); ::setenv( "TZ", org, true ); +#else +#warning "Need a replacement for MacOSX!!" + tm* broken = ::localtime( &t ); +#endif QDateTime ret; ret.setDate( QDate( broken->tm_year + 1900, broken->tm_mon +1, broken->tm_mday ) ); ret.setTime( QTime( broken->tm_hour, broken->tm_min, broken->tm_sec ) ); return ret; } time_t to_Time_t( const QDateTime& utc, const QString& str ) { QDate d = utc.date(); QTime t = utc.time(); tm broken; broken.tm_year = d.year() - 1900; broken.tm_mon = d.month() - 1; broken.tm_mday = d.day(); broken.tm_hour = t.hour(); broken.tm_min = t.minute(); broken.tm_sec = t.second(); QCString org = ::getenv( "TZ" ); +#ifndef Q_OS_MACX // Following line causes bus errors on Mac ::setenv( "TZ", str.latin1(), true ); ::tzset(); time_t ti = ::mktime( &broken ); ::setenv( "TZ", org, true ); - +#else +#warning "Need a replacement for MacOSX!!" + time_t ti = ::mktime( &broken ); +#endif return ti; } } OTimeZone::OTimeZone( const ZoneName& zone ) : m_name(zone) { } OTimeZone::~OTimeZone() { } bool OTimeZone::isValid()const { return !m_name.isEmpty(); } /* * we will get the current timezone * and ask it to convert to the timezone date */ QDateTime OTimeZone::toLocalDateTime( const QDateTime& dt) { return OTimeZone::current().toDateTime( dt, *this ); } QDateTime OTimeZone::toUTCDateTime( const QDateTime& dt ) { return OTimeZone::utc().toDateTime( dt, *this ); } QDateTime OTimeZone::fromUTCDateTime( time_t t) { return utcTime( t ); } QDateTime OTimeZone::toDateTime( time_t t) { return utcTime( t, m_name ); } /* * convert dt to utc using zone.m_name * convert utc -> timeZoneDT using this->m_name */ QDateTime OTimeZone::toDateTime( const QDateTime& dt, const OTimeZone& zone ) { time_t utc = to_Time_t( dt, zone.m_name ); qWarning("%d %s", utc, zone.m_name.latin1() ); return utcTime( utc, m_name ); } time_t OTimeZone::fromDateTime( const QDateTime& time ) { return to_Time_t( time, m_name ); } time_t OTimeZone::fromUTCDateTime( const QDateTime& time ) { return to_Time_t( time, "UTC" ); } OTimeZone OTimeZone::current() { QCString str = ::getenv("TZ"); OTimeZone zone( str ); return zone; } OTimeZone OTimeZone::utc() { return OTimeZone("UTC"); } QString OTimeZone::timeZone()const { return m_name; } diff --git a/libopie2/opiepim/oevent.cpp b/libopie2/opiepim/oevent.cpp index ec05e77..9b31957 100644 --- a/libopie2/opiepim/oevent.cpp +++ b/libopie2/opiepim/oevent.cpp @@ -1,704 +1,717 @@ #include <qshared.h> #include <qarray.h> #include <qpe/palmtopuidgen.h> #include <qpe/categories.h> #include <qpe/stringutil.h> #include "orecur.h" #include "opimresolver.h" #include "opimnotifymanager.h" #include "oevent.h" int OCalendarHelper::week( const QDate& date) { // Calculates the week this date is in within that // month. Equals the "row" is is in in the month view int week = 1; QDate tmp( date.year(), date.month(), 1 ); if ( date.dayOfWeek() < tmp.dayOfWeek() ) ++week; week += ( date.day() - 1 ) / 7; return week; } int OCalendarHelper::ocurrence( const QDate& date) { // calculates the number of occurrances of this day of the // week till the given date (e.g 3rd Wednesday of the month) return ( date.day() - 1 ) / 7 + 1; } int OCalendarHelper::dayOfWeek( char day ) { int dayOfWeek = 1; char i = ORecur::MON; while ( !( i & day ) && i <= ORecur::SUN ) { i <<= 1; ++dayOfWeek; } return dayOfWeek; } int OCalendarHelper::monthDiff( const QDate& first, const QDate& second ) { return ( second.year() - first.year() ) * 12 + second.month() - first.month(); } struct OEvent::Data : public QShared { Data() : QShared() { child = 0; recur = 0; manager = 0; isAllDay = false; parent = 0; } ~Data() { delete manager; delete recur; } QString description; QString location; OPimNotifyManager* manager; ORecur* recur; QString note; QDateTime created; QDateTime start; QDateTime end; bool isAllDay : 1; QString timezone; QArray<int>* child; int parent; }; OEvent::OEvent( int uid ) : OPimRecord( uid ) { data = new Data; } OEvent::OEvent( const OEvent& ev) : OPimRecord( ev ), data( ev.data ) { data->ref(); } + +OEvent::OEvent( const QMap<int, QString> map ) + : OPimRecord( 0 ) +{ + data = new Data; + + fromMap( map ); +} + OEvent::~OEvent() { if ( data->deref() ) { delete data; data = 0; } } OEvent& OEvent::operator=( const OEvent& ev) { if ( this == &ev ) return *this; OPimRecord::operator=( ev ); ev.data->ref(); deref(); data = ev.data; return *this; } QString OEvent::description()const { return data->description; } void OEvent::setDescription( const QString& description ) { changeOrModify(); data->description = description; } void OEvent::setLocation( const QString& loc ) { changeOrModify(); data->location = loc; } QString OEvent::location()const { return data->location; } OPimNotifyManager &OEvent::notifiers()const { // I hope we can skip the changeOrModify here // the notifier should take care of it // and OPimNotify is shared too if (!data->manager ) data->manager = new OPimNotifyManager; return *data->manager; } bool OEvent::hasNotifiers()const { if (!data->manager ) return false; if (data->manager->reminders().isEmpty() && data->manager->alarms().isEmpty() ) return false; return true; } ORecur OEvent::recurrence()const { if (!data->recur) data->recur = new ORecur; return *data->recur; } void OEvent::setRecurrence( const ORecur& rec) { changeOrModify(); if (data->recur ) (*data->recur) = rec; else data->recur = new ORecur( rec ); } bool OEvent::hasRecurrence()const { if (!data->recur ) return false; return data->recur->doesRecur(); } QString OEvent::note()const { return data->note; } void OEvent::setNote( const QString& note ) { changeOrModify(); data->note = note; } QDateTime OEvent::createdDateTime()const { return data->created; } void OEvent::setCreatedDateTime( const QDateTime& time ) { changeOrModify(); data->created = time; } QDateTime OEvent::startDateTime()const { if ( data->isAllDay ) return QDateTime( data->start.date(), QTime(0, 0, 0 ) ); return data->start; } QDateTime OEvent::startDateTimeInZone()const { /* if no timezone, or all day event or if the current and this timeZone match... */ if (data->timezone.isEmpty() || data->isAllDay || data->timezone == OTimeZone::current().timeZone() ) return startDateTime(); OTimeZone zone(data->timezone ); return zone.toDateTime( data->start, OTimeZone::current() ); } void OEvent::setStartDateTime( const QDateTime& dt ) { changeOrModify(); data->start = dt; } QDateTime OEvent::endDateTime()const { /* * if all Day event the end time needs * to be on the same day as the start */ if ( data->isAllDay ) return QDateTime( data->start.date(), QTime(23, 59, 59 ) ); return data->end; } QDateTime OEvent::endDateTimeInZone()const { /* if no timezone, or all day event or if the current and this timeZone match... */ if (data->timezone.isEmpty() || data->isAllDay || data->timezone == OTimeZone::current().timeZone() ) return endDateTime(); OTimeZone zone(data->timezone ); return zone.toDateTime( data->end, OTimeZone::current() ); } void OEvent::setEndDateTime( const QDateTime& dt ) { changeOrModify(); data->end = dt; } bool OEvent::isMultipleDay()const { return data->end.date().day() - data->start.date().day(); } bool OEvent::isAllDay()const { return data->isAllDay; } void OEvent::setAllDay( bool allDay ) { changeOrModify(); data->isAllDay = allDay; if (allDay ) data->timezone = "UTC"; } void OEvent::setTimeZone( const QString& tz ) { changeOrModify(); data->timezone = tz; } QString OEvent::timeZone()const { if (data->isAllDay ) return QString::fromLatin1("UTC"); return data->timezone; } bool OEvent::match( const QRegExp& re )const { if ( re.match( data->description ) != -1 ){ setLastHitField( Qtopia::DatebookDescription ); return true; } if ( re.match( data->note ) != -1 ){ setLastHitField( Qtopia::Note ); return true; } if ( re.match( data->location ) != -1 ){ setLastHitField( Qtopia::Location ); return true; } if ( re.match( data->start.toString() ) != -1 ){ setLastHitField( Qtopia::StartDateTime ); return true; } if ( re.match( data->end.toString() ) != -1 ){ setLastHitField( Qtopia::EndDateTime ); return true; } return false; } QString OEvent::toRichText()const { QString text, value; // description text += "<b><h3><img src=\"datebook/DateBook\">"; if ( !description().isEmpty() ) { text += Qtopia::escapeString(description() ).replace(QRegExp( "[\n]"), "" ); } text += "</h3></b><br><hr><br>"; // location if ( !(value = location()).isEmpty() ) { text += "<b>" + QObject::tr( "Location:" ) + "</b> "; text += Qtopia::escapeString(value) + "<br>"; } // all day event if ( isAllDay() ) { text += "<b><i>" + QObject::tr( "This is an all day event" ) + "</i></b><br>"; } // multiple day event else if ( isMultipleDay () ) { text += "<b><i>" + QObject::tr( "This is a multiple day event" ) + "</i></b><br>"; } // start & end times else { // start time if ( startDateTime().isValid() ) { text += "<b>" + QObject::tr( "Start:") + "</b> "; text += Qtopia::escapeString(startDateTime().toString() ). replace(QRegExp( "[\n]"), "<br>" ) + "<br>"; } // end time if ( endDateTime().isValid() ) { text += "<b>" + QObject::tr( "End:") + "</b> "; text += Qtopia::escapeString(endDateTime().toString() ). replace(QRegExp( "[\n]"), "<br>" ) + "<br>"; } } // categories if ( categoryNames("Calendar").count() ){ text += "<b>" + QObject::tr( "Category:") + "</b> "; text += categoryNames("Calendar").join(", "); text += "<br>"; } //notes if ( !note().isEmpty() ) { text += "<b>" + QObject::tr( "Note:") + "</b><br>"; text += note(); // text += Qtopia::escapeString(note() ). // replace(QRegExp( "[\n]"), "<br>" ) + "<br>"; } return text; } QString OEvent::toShortText()const { QString text; text += QString::number( startDateTime().date().day() ); text += "."; text += QString::number( startDateTime().date().month() ); text += "."; text += QString::number( startDateTime().date().year() ); text += " "; text += QString::number( startDateTime().time().hour() ); text += ":"; text += QString::number( startDateTime().time().minute() ); text += " - "; text += description(); return text; } QString OEvent::type()const { return QString::fromLatin1("OEvent"); } QString OEvent::recordField( int /*id */ )const { return QString::null; } int OEvent::rtti() { return OPimResolver::DateBook; } bool OEvent::loadFromStream( QDataStream& ) { return true; } bool OEvent::saveToStream( QDataStream& )const { return true; } void OEvent::changeOrModify() { if ( data->count != 1 ) { data->deref(); Data* d2 = new Data; d2->description = data->description; d2->location = data->location; if (data->manager ) d2->manager = new OPimNotifyManager( *data->manager ); if ( data->recur ) d2->recur = new ORecur( *data->recur ); d2->note = data->note; d2->created = data->created; d2->start = data->start; d2->end = data->end; d2->isAllDay = data->isAllDay; d2->timezone = data->timezone; d2->parent = data->parent; if ( data->child ) { d2->child = new QArray<int>( *data->child ); d2->child->detach(); } data = d2; } } void OEvent::deref() { if ( data->deref() ) { delete data; data = 0; } } // Exporting Event data to map. Using the same // encoding as ODateBookAccessBackend_xml does.. // Thus, we could remove the stuff there and use this // for it and for all other places.. // Encoding should happen at one place, only ! (eilers) QMap<int, QString> OEvent::toMap()const { QMap<int, QString> retMap; retMap.insert( OEvent::FUid, QString::number( uid() ) ); retMap.insert( OEvent::FCategories, Qtopia::escapeString( Qtopia::Record::idsToString( categories() ) )); retMap.insert( OEvent::FDescription, Qtopia::escapeString( description() ) ); retMap.insert( OEvent::FLocation, Qtopia::escapeString( location() ) ); retMap.insert( OEvent::FType, isAllDay() ? "AllDay" : "" ); OPimAlarm alarm = notifiers().alarms()[0]; retMap.insert( OEvent::FAlarm, QString::number( alarm.dateTime().secsTo( startDateTime() ) / 60 ) ); retMap.insert( OEvent::FSound, (alarm.sound() == OPimAlarm::Loud) ? "loud" : "silent" ); OTimeZone zone( timeZone().isEmpty() ? OTimeZone::current() : timeZone() ); retMap.insert( OEvent::FStart, QString::number( zone.fromUTCDateTime( zone.toDateTime( startDateTime(), OTimeZone::utc() ) ) ) ); retMap.insert( OEvent::FEnd, QString::number( zone.fromUTCDateTime( zone.toDateTime( endDateTime(), OTimeZone::utc() ) ) ) ); retMap.insert( OEvent::FNote, Qtopia::escapeString( note() ) ); retMap.insert( OEvent::FTimeZone, timeZone().isEmpty() ? QString( "None" ) : timeZone() ); if( parent() ) retMap.insert( OEvent::FRecParent, QString::number( parent() ) ); if( children().count() ){ QArray<int> childr = children(); QString buf; for ( uint i = 0; i < childr.count(); i++ ) { if ( i != 0 ) buf += " "; buf += QString::number( childr[i] ); } retMap.insert( OEvent::FRecChildren, buf ); } // Add recurrence stuff if( hasRecurrence() ){ ORecur recur = recurrence(); QMap<int, QString> recFields = recur.toMap(); retMap.insert( OEvent::FRType, recFields[ORecur::RType] ); retMap.insert( OEvent::FRWeekdays, recFields[ORecur::RWeekdays] ); retMap.insert( OEvent::FRPosition, recFields[ORecur::RPosition] ); retMap.insert( OEvent::FRFreq, recFields[ORecur::RFreq] ); retMap.insert( OEvent::FRHasEndDate, recFields[ORecur::RHasEndDate] ); retMap.insert( OEvent::FREndDate, recFields[ORecur::EndDate] ); retMap.insert( OEvent::FRCreated, recFields[ORecur::Created] ); retMap.insert( OEvent::FRExceptions, recFields[ORecur::Exceptions] ); + } else { + ORecur recur = recurrence(); + QMap<int, QString> recFields = recur.toMap(); + retMap.insert( OEvent::FRType, recFields[ORecur::RType] ); } return retMap; } void OEvent::fromMap( const QMap<int, QString>& map ) { // We just want to set the UID if it is really stored. if ( !map[OEvent::FUid].isEmpty() ) setUid( map[OEvent::FUid].toInt() ); setCategories( idsFromString( map[OEvent::FCategories] ) ); setDescription( map[OEvent::FDescription] ); setLocation( map[OEvent::FLocation] ); if ( map[OEvent::FType] == "AllDay" ) setAllDay( true ); else setAllDay( false ); int alarmTime = -1; if( !map[OEvent::FAlarm].isEmpty() ) alarmTime = map[OEvent::FAlarm].toInt(); int sound = ( ( map[OEvent::FSound] == "loud" ) ? OPimAlarm::Loud : OPimAlarm::Silent ); if ( ( alarmTime != -1 ) ){ QDateTime dt = startDateTime().addSecs( -1*alarmTime*60 ); OPimAlarm al( sound , dt ); notifiers().add( al ); } if ( !map[OEvent::FTimeZone].isEmpty() && ( map[OEvent::FTimeZone] != "None" ) ){ setTimeZone( map[OEvent::FTimeZone] ); } time_t start = (time_t) map[OEvent::FStart].toLong(); time_t end = (time_t) map[OEvent::FEnd].toLong(); /* AllDay is always in UTC */ if ( isAllDay() ) { OTimeZone utc = OTimeZone::utc(); setStartDateTime( utc.fromUTCDateTime( start ) ); setEndDateTime ( utc.fromUTCDateTime( end ) ); setTimeZone( "UTC"); // make sure it is really utc }else { /* to current date time */ // qWarning(" Start is %d", start ); OTimeZone zone( timeZone().isEmpty() ? OTimeZone::current() : timeZone() ); QDateTime date = zone.toDateTime( start ); qWarning(" Start is %s", date.toString().latin1() ); setStartDateTime( zone.toDateTime( date, OTimeZone::current() ) ); date = zone.toDateTime( end ); setEndDateTime ( zone.toDateTime( date, OTimeZone::current() ) ); } if ( !map[OEvent::FRecParent].isEmpty() ) setParent( map[OEvent::FRecParent].toInt() ); if ( !map[OEvent::FRecChildren].isEmpty() ){ QStringList list = QStringList::split(' ', map[OEvent::FRecChildren] ); for ( QStringList::Iterator it = list.begin(); it != list.end(); ++it ) { addChild( (*it).toInt() ); } } // Fill recurrence stuff and put it directly into the ORecur-Object using fromMap.. if( !map[OEvent::FRType].isEmpty() ){ QMap<int, QString> recFields; recFields.insert( ORecur::RType, map[OEvent::FRType] ); recFields.insert( ORecur::RWeekdays, map[OEvent::FRWeekdays] ); recFields.insert( ORecur::RPosition, map[OEvent::FRPosition] ); recFields.insert( ORecur::RFreq, map[OEvent::FRFreq] ); recFields.insert( ORecur::RHasEndDate, map[OEvent::FRHasEndDate] ); recFields.insert( ORecur::EndDate, map[OEvent::FREndDate] ); recFields.insert( ORecur::Created, map[OEvent::FRCreated] ); recFields.insert( ORecur::Exceptions, map[OEvent::FRExceptions] ); ORecur recur( recFields ); setRecurrence( recur ); } } int OEvent::parent()const { return data->parent; } void OEvent::setParent( int uid ) { changeOrModify(); data->parent = uid; } QArray<int> OEvent::children() const{ if (!data->child) return QArray<int>(); else return data->child->copy(); } void OEvent::setChildren( const QArray<int>& arr ) { changeOrModify(); if (data->child) delete data->child; data->child = new QArray<int>( arr ); data->child->detach(); } void OEvent::addChild( int uid ) { changeOrModify(); if (!data->child ) { data->child = new QArray<int>(1); (*data->child)[0] = uid; }else{ int count = data->child->count(); data->child->resize( count + 1 ); (*data->child)[count] = uid; } } void OEvent::removeChild( int uid ) { if (!data->child || !data->child->contains( uid ) ) return; changeOrModify(); QArray<int> newAr( data->child->count() - 1 ); int j = 0; uint count = data->child->count(); for ( uint i = 0; i < count; i++ ) { if ( (*data->child)[i] != uid ) { newAr[j] = (*data->child)[i]; j++; } } (*data->child) = newAr; } struct OEffectiveEvent::Data : public QShared { Data() : QShared() { } OEvent event; QDate date; QTime start, end; QDate startDate, endDate; bool dates : 1; }; OEffectiveEvent::OEffectiveEvent() { data = new Data; data->date = QDate::currentDate(); data->start = data->end = QTime::currentTime(); data->dates = false; } OEffectiveEvent::OEffectiveEvent( const OEvent& ev, const QDate& startDate, Position pos ) { data = new Data; data->event = ev; data->date = startDate; if ( pos & Start ) data->start = ev.startDateTime().time(); else data->start = QTime( 0, 0, 0 ); if ( pos & End ) data->end = ev.endDateTime().time(); else data->end = QTime( 23, 59, 59 ); data->dates = false; } OEffectiveEvent::OEffectiveEvent( const OEffectiveEvent& ev) { data = ev.data; data->ref(); } OEffectiveEvent::~OEffectiveEvent() { if ( data->deref() ) { delete data; data = 0; } } OEffectiveEvent& OEffectiveEvent::operator=( const OEffectiveEvent& ev ) { if ( *this == ev ) return *this; ev.data->ref(); deref(); data = ev.data; return *this; } void OEffectiveEvent::setStartTime( const QTime& ti) { changeOrModify(); data->start = ti; } void OEffectiveEvent::setEndTime( const QTime& en) { changeOrModify(); data->end = en; } void OEffectiveEvent::setEvent( const OEvent& ev) { changeOrModify(); data->event = ev; } void OEffectiveEvent::setDate( const QDate& da) { changeOrModify(); data->date = da; } void OEffectiveEvent::setEffectiveDates( const QDate& from, const QDate& to ) { if (!from.isValid() ) { data->dates = false; return; } data->startDate = from; data->endDate = to; } QString OEffectiveEvent::description()const { return data->event.description(); } QString OEffectiveEvent::location()const { return data->event.location(); } QString OEffectiveEvent::note()const { return data->event.note(); } OEvent OEffectiveEvent::event()const { return data->event; } QTime OEffectiveEvent::startTime()const { return data->start; } QTime OEffectiveEvent::endTime()const { return data->end; } QDate OEffectiveEvent::date()const { return data->date; } int OEffectiveEvent::length()const { return (data->end.hour() * 60 - data->start.hour() * 60) + QABS(data->start.minute() - data->end.minute() ); } int OEffectiveEvent::size()const { return ( data->end.hour() - data->start.hour() ) * 3600 + (data->end.minute() - data->start.minute() * 60 + data->end.second() - data->start.second() ); } QDate OEffectiveEvent::startDate()const { if ( data->dates ) return data->startDate; else if ( data->event.hasRecurrence() ) // single day, since multi-day should have a d pointer return data->date; else return data->event.startDateTime().date(); } QDate OEffectiveEvent::endDate()const { if ( data->dates ) return data->endDate; else if ( data->event.hasRecurrence() ) return data->date; else return data->event.endDateTime().date(); } void OEffectiveEvent::deref() { if ( data->deref() ) { delete data; data = 0; } } void OEffectiveEvent::changeOrModify() { if ( data->count != 1 ) { data->deref(); Data* d2 = new Data; d2->event = data->event; d2->date = data->date; d2->start = data->start; d2->end = data->end; d2->startDate = data->startDate; d2->endDate = data->endDate; d2->dates = data->dates; data = d2; } } bool OEffectiveEvent::operator<( const OEffectiveEvent &e ) const{ if ( data->date < e.date() ) return TRUE; if ( data->date == e.date() ) return ( startTime() < e.startTime() ); else return FALSE; } bool OEffectiveEvent::operator<=( const OEffectiveEvent &e ) const{ return (data->date <= e.date() ); } bool OEffectiveEvent::operator==( const OEffectiveEvent &e ) const { return ( date() == e.date() && startTime() == e.startTime() && endTime()== e.endTime() && event() == e.event() ); } bool OEffectiveEvent::operator!=( const OEffectiveEvent &e ) const { return !(*this == e ); } bool OEffectiveEvent::operator>( const OEffectiveEvent &e ) const { return !(*this <= e ); } bool OEffectiveEvent::operator>= ( const OEffectiveEvent &e ) const { return !(*this < e); } diff --git a/libopie2/opiepim/oevent.h b/libopie2/opiepim/oevent.h index 9218c97..9eb948f 100644 --- a/libopie2/opiepim/oevent.h +++ b/libopie2/opiepim/oevent.h @@ -1,230 +1,236 @@ // CONTAINS GPLed code of TT #ifndef OPIE_PIM_EVENT_H #define OPIE_PIM_EVENT_H #include <qstring.h> #include <qdatetime.h> #include <qvaluelist.h> #include <qpe/recordfields.h> #include <qpe/palmtopuidgen.h> #include "otimezone.h" #include "opimrecord.h" struct OCalendarHelper { /** calculate the week number of the date */ static int week( const QDate& ); /** calculate the occurence of week days since the start of the month */ static int ocurrence( const QDate& ); // returns the dayOfWeek for the *first* day it finds (ignores // any further days!). Returns 1 (Monday) if there isn't any day found static int dayOfWeek( char day ); /** returns the diff of month */ static int monthDiff( const QDate& first, const QDate& second ); }; class OPimNotifyManager; class ORecur; /** * This is the container for all Events. It encapsules all * available information for a single Event * @short container for events. */ class OEvent : public OPimRecord { public: typedef QValueList<OEvent> ValueList; /** * RecordFields contain possible attributes * used in the Results of toMap().. */ enum RecordFields { FUid = Qtopia::UID_ID, FCategories = Qtopia::CATEGORY_ID, FDescription = 0, FLocation, FType, FAlarm, FSound, FRType, FRWeekdays, FRPosition, FRFreq, FRHasEndDate, FREndDate, FRCreated, FRExceptions, FStart, FEnd, FNote, FTimeZone, FRecParent, FRecChildren, }; /** * Start with an Empty OEvent. UID == 0 means that it is empty */ OEvent(int uid = 0); /** * copy c'tor */ OEvent( const OEvent& ); + + /** + * Create OEvent, initialized by map + * @see enum RecordFields + */ + OEvent( const QMap<int, QString> map ); ~OEvent(); OEvent &operator=( const OEvent& ); QString description()const; void setDescription( const QString& description ); QString location()const; void setLocation( const QString& loc ); bool hasNotifiers()const; OPimNotifyManager ¬ifiers()const; ORecur recurrence()const; void setRecurrence( const ORecur& ); bool hasRecurrence()const; QString note()const; void setNote( const QString& note ); QDateTime createdDateTime()const; void setCreatedDateTime( const QDateTime& dt); /** set the date to dt. dt is the QDateTime in localtime */ void setStartDateTime( const QDateTime& ); /** returns the datetime in the local timeZone */ QDateTime startDateTime()const; /** returns the start datetime in the current zone */ QDateTime startDateTimeInZone()const; /** in current timezone */ void setEndDateTime( const QDateTime& ); /** in current timezone */ QDateTime endDateTime()const; QDateTime endDateTimeInZone()const; bool isMultipleDay()const; bool isAllDay()const; void setAllDay( bool isAllDay ); /* pin this event to a timezone! FIXME */ void setTimeZone( const QString& timeZone ); QString timeZone()const; virtual bool match( const QRegExp& )const; /** For exception to recurrence here is a list of children... */ QArray<int> children()const; void setChildren( const QArray<int>& ); void addChild( int uid ); void removeChild( int uid ); /** return the parent OEvent */ int parent()const; void setParent( int uid ); /* needed reimp */ QString toRichText()const; QString toShortText()const; QString type()const; QMap<int, QString> toMap()const; void fromMap( const QMap<int, QString>& map ); QString recordField(int )const; static int rtti(); bool loadFromStream( QDataStream& ); bool saveToStream( QDataStream& )const; /* bool operator==( const OEvent& ); bool operator!=( const OEvent& ); bool operator<( const OEvent& ); bool operator<=( const OEvent& ); bool operator>( const OEvent& ); bool operator>=(const OEvent& ); */ private: inline void changeOrModify(); void deref(); struct Data; Data* data; class Private; Private* priv; }; /** * AN Event can span through multiple days. We split up a multiday eve */ class OEffectiveEvent { public: typedef QValueList<OEffectiveEvent> ValueList; enum Position { MidWay, Start, End, StartEnd }; // If we calculate the effective event of a multi-day event // we have to figure out whether we are at the first day, // at the end, or anywhere else ("middle"). This is important // for the start/end times (00:00/23:59) // MidWay: 00:00 -> 23:59, as we are "in the middle" of a multi- // day event // Start: start time -> 23:59 // End: 00:00 -> end time // Start | End == StartEnd: for single-day events (default) // here we draw start time -> end time OEffectiveEvent(); OEffectiveEvent( const OEvent& event, const QDate& startDate, Position pos = StartEnd ); OEffectiveEvent( const OEffectiveEvent& ); OEffectiveEvent &operator=(const OEffectiveEvent& ); ~OEffectiveEvent(); void setStartTime( const QTime& ); void setEndTime( const QTime& ); void setEvent( const OEvent& ); void setDate( const QDate& ); void setEffectiveDates( const QDate& from, const QDate& to ); QString description()const; QString location()const; QString note()const; OEvent event()const; QTime startTime()const; QTime endTime()const; QDate date()const; /* return the length in hours */ int length()const; int size()const; QDate startDate()const; QDate endDate()const; bool operator<( const OEffectiveEvent &e ) const; bool operator<=( const OEffectiveEvent &e ) const; bool operator==( const OEffectiveEvent &e ) const; bool operator!=( const OEffectiveEvent &e ) const; bool operator>( const OEffectiveEvent &e ) const; bool operator>= ( const OEffectiveEvent &e ) const; private: void deref(); inline void changeOrModify(); class Private; Private* priv; struct Data; Data* data; }; #endif |