From ea3945a9bd8f9830f70b1efa133f9df13b19362f Mon Sep 17 00:00:00 2001 From: mickeyl Date: Tue, 16 Nov 2004 19:14:18 +0000 Subject: libopie1 goes into unsupported --- (limited to 'noncore/unsupported/libopie/pim') diff --git a/noncore/unsupported/libopie/pim/.cvsignore b/noncore/unsupported/libopie/pim/.cvsignore new file mode 100644 index 0000000..aef62c4 --- a/dev/null +++ b/noncore/unsupported/libopie/pim/.cvsignore @@ -0,0 +1,2 @@ +config.in +moc* diff --git a/noncore/unsupported/libopie/pim/config.in b/noncore/unsupported/libopie/pim/config.in new file mode 100644 index 0000000..95d3737 --- a/dev/null +++ b/noncore/unsupported/libopie/pim/config.in @@ -0,0 +1,2 @@ +menu "Pim" +endmenu diff --git a/noncore/unsupported/libopie/pim/libopie.pro b/noncore/unsupported/libopie/pim/libopie.pro new file mode 100644 index 0000000..62f235d --- a/dev/null +++ b/noncore/unsupported/libopie/pim/libopie.pro @@ -0,0 +1,64 @@ +TEMPLATE = lib +CONFIG += qte warn_on release +HEADERS = ofontmenu.h \ + tododb.h \ + todoevent.h todoresource.h \ + todovcalresource.h xmltree.h \ + colordialog.h colorpopupmenu.h \ + oclickablelabel.h oprocctrl.h \ + oprocess.h odevice.h \ + otimepicker.h otabwidget.h \ + otabbar.h otabinfo.h \ + ofileselector/ofiledialog.h \ + ofileselector/ofilelistview.h \ + ofileselector/ofileselector.h \ + ofileselector/ofileselectoritem.h \ + ofileselector/ofileview.h \ + ofileselector/olister.h \ + ofileselector/olocallister.h \ + ofileselector/ofileselectormain.h \ + pim/opimrecord.h \ + pim/otodo.h \ + pim/orecordlist.h \ + pim/opimaccesstemplate.h \ + pim/opimaccessbackend.h \ + pim/otodoaccess.h \ + pim/otodacessbackend.h \ + pim/ocontact.h \ + pim/ocontactaccess.h \ + pim/ocontactaccessbackend.h \ + pim/ocontactaccessbackend_xml.h \ + pim/orecord.h + +SOURCES = ofontmenu.cc \ + xmltree.cc \ + tododb.cpp todoevent.cpp \ + todovcalresource.cpp colordialog.cpp \ + colorpopupmenu.cpp oclickablelabel.cpp \ + oprocctrl.cpp oprocess.cpp \ + odevice.cpp otimepicker.cpp \ + otabwidget.cpp otabbar.cpp \ + ofileselector/ofiledialog.cpp \ + ofileselector/ofilelistview.cpp \ + ofileselector/ofileselector.cpp \ + ofileselector/ofileselectoritem.cpp \ + ofileselector/ofileview.cpp \ + ofileselector/olister.cpp \ + ofileselector/olocallister.cpp \ + ofileselector/ofileselectormain.cpp \ + pim/otodo.cpp \ + pim/opimrecord.cpp \ + pim/otodoaccess.cpp \ + pim/otodoaccessbackend.cpp \ + pim/ocontact.cpp \ + pim/ocontactaccess.cpp \ + pim/orecord.cpp + +TARGET = opie +INCLUDEPATH += $(OPIEDIR)/include +DESTDIR = $(OPIEDIR)/lib$(PROJMAK) +#VERSION = 1.0.0 + +INTERFACES = otimepickerbase.ui + +include ( $(OPIEDIR)/include.pro ) diff --git a/noncore/unsupported/libopie/pim/obackendfactory.h b/noncore/unsupported/libopie/pim/obackendfactory.h new file mode 100644 index 0000000..4cdef8b --- a/dev/null +++ b/noncore/unsupported/libopie/pim/obackendfactory.h @@ -0,0 +1,197 @@ +/* + * 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.1 2004/11/16 21:46:07 mickeyl + * libopie1 goes into unsupported + * + * 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 +#include +#include + +#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 + * + *
+ *   OTodoAccessBackend* backend = OBackEndFactory::Default("todo", QString::null );
+ *   backend->load();
+ * 
+ * + * @author Stefan Eilers + * @version 0.1 + */ +template +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 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 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 for CONTACT not implemented! Using XML instead!"); +#endif + + return (T*) new OContactAccessBackend_XML( appName ); + case DATE: +#ifdef __USE_SQL + if ( backend == "sql" ) + 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/noncore/unsupported/libopie/pim/ocontact.cpp b/noncore/unsupported/libopie/pim/ocontact.cpp new file mode 100644 index 0000000..fcf3b26 --- a/dev/null +++ b/noncore/unsupported/libopie/pim/ocontact.cpp @@ -0,0 +1,1207 @@ +/********************************************************************** +** Copyright (C) 2000-2002 Trolltech AS. All rights reserved. +** Copyright (C) 2002-2003 by Stefan Eilers (eilers.stefan@epost.de) +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** See http://www.trolltech.com/gpl/ for GPL licensing information. +** +** Contact info@trolltech.com if any conditions of this licensing are +** not clear to you. +** +**********************************************************************/ + +#define QTOPIA_INTERNAL_CONTACT_MRE + +#include "ocontact.h" +#include "opimresolver.h" +#include "oconversion.h" + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +/*! + \class Contact contact.h + \brief The Contact class holds the data of an address book entry. + + This data includes information the name of the person, contact + information, and business information such as deparment and job title. + + \ingroup qtopiaemb + \ingroup qtopiadesktop +*/ + + +/*! + Creates a new, empty contact. +*/ +OContact::OContact() + : OPimRecord(), mMap(), d( 0 ) +{ +} + +/*! + \internal + Creates a new contact. The properties of the contact are + set from \a fromMap. +*/ +OContact::OContact( const QMap &fromMap ) : + OPimRecord(), mMap( fromMap ), d( 0 ) +{ + QString cats = mMap[ Qtopia::AddressCategory ]; + if ( !cats.isEmpty() ) + setCategories( idsFromString( cats ) ); + + QString uidStr = find( Qtopia::AddressUid ); + + if ( uidStr.isEmpty() || (uidStr.toInt() == 0) ){ + qWarning( "Invalid UID found. Generate new one.." ); + setUid( uidGen().generate() ); + }else + setUid( uidStr.toInt() ); + +// if ( !uidStr.isEmpty() ) +// setUid( uidStr.toInt() ); +} + +/*! + Destroys a contact. +*/ +OContact::~OContact() +{ +} + +/*! \fn void OContact::setTitle( const QString &str ) + Sets the title of the contact to \a str. +*/ + +/*! \fn void OContact::setFirstName( const QString &str ) + Sets the first name of the contact to \a str. +*/ + +/*! \fn void OContact::setMiddleName( const QString &str ) + Sets the middle name of the contact to \a str. +*/ + +/*! \fn void OContact::setLastName( const QString &str ) + Sets the last name of the contact to \a str. +*/ + +/*! \fn void OContact::setSuffix( const QString &str ) + Sets the suffix of the contact to \a str. +*/ + +/*! \fn void OContact::setFileAs( const QString &str ) + Sets the contact to filed as \a str. +*/ + +/*! \fn void OContact::setDefaultEmail( const QString &str ) + Sets the default email of the contact to \a str. +*/ + +/*! \fn void OContact::setHomeStreet( const QString &str ) + Sets the home street address of the contact to \a str. +*/ + +/*! \fn void OContact::setHomeCity( const QString &str ) + Sets the home city of the contact to \a str. +*/ + +/*! \fn void OContact::setHomeState( const QString &str ) + Sets the home state of the contact to \a str. +*/ + +/*! \fn void OContact::setHomeZip( const QString &str ) + Sets the home zip code of the contact to \a str. +*/ + +/*! \fn void OContact::setHomeCountry( const QString &str ) + Sets the home country of the contact to \a str. +*/ + +/*! \fn void OContact::setHomePhone( const QString &str ) + Sets the home phone number of the contact to \a str. +*/ + +/*! \fn void OContact::setHomeFax( const QString &str ) + Sets the home fax number of the contact to \a str. +*/ + +/*! \fn void OContact::setHomeMobile( const QString &str ) + Sets the home mobile phone number of the contact to \a str. +*/ + +/*! \fn void OContact::setHomeWebpage( const QString &str ) + Sets the home webpage of the contact to \a str. +*/ + +/*! \fn void OContact::setCompany( const QString &str ) + Sets the company for contact to \a str. +*/ + +/*! \fn void OContact::setJobTitle( const QString &str ) + Sets the job title of the contact to \a str. +*/ + +/*! \fn void OContact::setDepartment( const QString &str ) + Sets the department for contact to \a str. +*/ + +/*! \fn void OContact::setOffice( const QString &str ) + Sets the office for contact to \a str. +*/ + +/*! \fn void OContact::setBusinessStreet( const QString &str ) + Sets the business street address of the contact to \a str. +*/ + +/*! \fn void OContact::setBusinessCity( const QString &str ) + Sets the business city of the contact to \a str. +*/ + +/*! \fn void OContact::setBusinessState( const QString &str ) + Sets the business state of the contact to \a str. +*/ + +/*! \fn void OContact::setBusinessZip( const QString &str ) + Sets the business zip code of the contact to \a str. +*/ + +/*! \fn void OContact::setBusinessCountry( const QString &str ) + Sets the business country of the contact to \a str. +*/ + +/*! \fn void OContact::setBusinessPhone( const QString &str ) + Sets the business phone number of the contact to \a str. +*/ + +/*! \fn void OContact::setBusinessFax( const QString &str ) + Sets the business fax number of the contact to \a str. +*/ + +/*! \fn void OContact::setBusinessMobile( const QString &str ) + Sets the business mobile phone number of the contact to \a str. +*/ + +/*! \fn void OContact::setBusinessPager( const QString &str ) + Sets the business pager number of the contact to \a str. +*/ + +/*! \fn void OContact::setBusinessWebpage( const QString &str ) + Sets the business webpage of the contact to \a str. +*/ + +/*! \fn void OContact::setProfession( const QString &str ) + Sets the profession of the contact to \a str. +*/ + +/*! \fn void OContact::setAssistant( const QString &str ) + Sets the assistant of the contact to \a str. +*/ + +/*! \fn void OContact::setManager( const QString &str ) + Sets the manager of the contact to \a str. +*/ + +/*! \fn void OContact::setSpouse( const QString &str ) + Sets the spouse of the contact to \a str. +*/ + +/*! \fn void OContact::setGender( const QString &str ) + Sets the gender of the contact to \a str. +*/ + +/*! \fn void OContact::setNickname( const QString &str ) + Sets the nickname of the contact to \a str. +*/ + +/*! \fn void OContact::setNotes( const QString &str ) + Sets the notes about the contact to \a str. +*/ + +/*! \fn QString OContact::title() const + Returns the title of the contact. +*/ + +/*! \fn QString OContact::firstName() const + Returns the first name of the contact. +*/ + +/*! \fn QString OContact::middleName() const + Returns the middle name of the contact. +*/ + +/*! \fn QString OContact::lastName() const + Returns the last name of the contact. +*/ + +/*! \fn QString OContact::suffix() const + Returns the suffix of the contact. +*/ + +/*! \fn QString OContact::fileAs() const + Returns the string the contact is filed as. +*/ + +/*! \fn QString OContact::defaultEmail() const + Returns the default email address of the contact. +*/ + +/*! \fn QString OContact::emails() const + Returns the list of email address for a contact separated by ';'s in a single + string. +*/ + +/*! \fn QString OContact::homeStreet() const + Returns the home street address of the contact. +*/ + +/*! \fn QString OContact::homeCity() const + Returns the home city of the contact. +*/ + +/*! \fn QString OContact::homeState() const + Returns the home state of the contact. +*/ + +/*! \fn QString OContact::homeZip() const + Returns the home zip of the contact. +*/ + +/*! \fn QString OContact::homeCountry() const + Returns the home country of the contact. +*/ + +/*! \fn QString OContact::homePhone() const + Returns the home phone number of the contact. +*/ + +/*! \fn QString OContact::homeFax() const + Returns the home fax number of the contact. +*/ + +/*! \fn QString OContact::homeMobile() const + Returns the home mobile number of the contact. +*/ + +/*! \fn QString OContact::homeWebpage() const + Returns the home webpage of the contact. +*/ + +/*! \fn QString OContact::company() const + Returns the company for the contact. +*/ + +/*! \fn QString OContact::department() const + Returns the department for the contact. +*/ + +/*! \fn QString OContact::office() const + Returns the office for the contact. +*/ + +/*! \fn QString OContact::jobTitle() const + Returns the job title of the contact. +*/ + +/*! \fn QString OContact::profession() const + Returns the profession of the contact. +*/ + +/*! \fn QString OContact::assistant() const + Returns the assistant of the contact. +*/ + +/*! \fn QString OContact::manager() const + Returns the manager of the contact. +*/ + +/*! \fn QString OContact::businessStreet() const + Returns the business street address of the contact. +*/ + +/*! \fn QString OContact::businessCity() const + Returns the business city of the contact. +*/ + +/*! \fn QString OContact::businessState() const + Returns the business state of the contact. +*/ + +/*! \fn QString OContact::businessZip() const + Returns the business zip of the contact. +*/ + +/*! \fn QString OContact::businessCountry() const + Returns the business country of the contact. +*/ + +/*! \fn QString OContact::businessPhone() const + Returns the business phone number of the contact. +*/ + +/*! \fn QString OContact::businessFax() const + Returns the business fax number of the contact. +*/ + +/*! \fn QString OContact::businessMobile() const + Returns the business mobile number of the contact. +*/ + +/*! \fn QString OContact::businessPager() const + Returns the business pager number of the contact. +*/ + +/*! \fn QString OContact::businessWebpage() const + Returns the business webpage of the contact. +*/ + +/*! \fn QString OContact::spouse() const + Returns the spouse of the contact. +*/ + +/*! \fn QString OContact::gender() const + Returns the gender of the contact. +*/ + +/*! \fn QString OContact::nickname() const + Returns the nickname of the contact. +*/ + +/*! \fn QString OContact::children() const + Returns the children of the contact. +*/ + +/*! \fn QString OContact::notes() const + Returns the notes relating to the the contact. +*/ + +/*! \fn QString OContact::groups() const + \internal + Returns the groups for the contact. +*/ + +/*! \fn QStringList OContact::groupList() const + \internal +*/ + +/*! \fn QString OContact::field(int) const + \internal +*/ + +/*! \fn void OContact::saveJournal( journal_action, const QString & = QString::null ) + \internal +*/ + +/*! \fn void OContact::setUid( int id ) + \internal + Sets the uid for this record to \a id. +*/ + +/*! \enum OContact::journal_action + \internal +*/ + +/*! + \internal +*/ +QMap OContact::toMap() const +{ + QMap map = mMap; + QString cats = idsToString( categories() ); + if ( !cats.isEmpty() ) + map.insert( Qtopia::AddressCategory, cats ); + return map; +} + +/*! + Returns a rich text formatted QString representing the contents the contact. +*/ +QString OContact::toRichText() const +{ + QString text; + QString value, comp, state; + QString str; + bool marker = false; + + Config cfg("qpe"); + cfg.setGroup("Appearance"); + int addressformat = cfg.readNumEntry( "AddressFormat", Zip_City_State ); + + // name, jobtitle and company + if ( !(value = fullName()).isEmpty() ) + text += "

" + Qtopia::escapeString(value) + "

"; + + if ( !(value = jobTitle()).isEmpty() ) + text += Qtopia::escapeString(value) + " "; + + comp = company(); + if ( !(value = department()).isEmpty() ) { + text += Qtopia::escapeString(value); + if ( comp ) + text += ", " + Qtopia::escapeString(comp); + }else if ( comp ) + text += "
" + Qtopia::escapeString(comp); + text += "

"; + + // defailt email + QString defEmail = defaultEmail(); + if ( !defEmail.isEmpty() ){ + text += " " + QObject::tr("Default Email: ") + "" + + Qtopia::escapeString(defEmail); + marker = true; + } + + // business address + if ( !businessStreet().isEmpty() || !businessCity().isEmpty() || + !businessZip().isEmpty() || !businessCountry().isEmpty() ) { + text += QObject::tr( "
Work Address:" ); + marker = true; + } + + if ( !(value = businessStreet()).isEmpty() ){ + text += "
" + Qtopia::escapeString(value); + marker = true; + } + + switch( addressformat ){ + case Zip_City_State:{ // Zip_Code City, State + state = businessState(); + if ( !(value = businessZip()).isEmpty() ){ + text += "
" + Qtopia::escapeString(value) + " "; + marker = true; + + } + if ( !(value = businessCity()).isEmpty() ) { + marker = true; + if ( businessZip().isEmpty() && !businessStreet().isEmpty() ) + text += "
"; + text += Qtopia::escapeString(value); + if ( state ) + text += ", " + Qtopia::escapeString(state); + } else if ( !state.isEmpty() ){ + text += "
" + Qtopia::escapeString(state); + marker = true; + } + break; + } + case City_State_Zip:{ // City, State Zip_Code + state = businessState(); + if ( !(value = businessCity()).isEmpty() ) { + marker = true; + text += "
" + Qtopia::escapeString(value); + if ( state ) + text += ", " + Qtopia::escapeString(state); + } else if ( !state.isEmpty() ){ + text += "
" + Qtopia::escapeString(state); + marker = true; + } + if ( !(value = businessZip()).isEmpty() ){ + text += " " + Qtopia::escapeString(value); + marker = true; + } + break; + } + } + + if ( !(value = businessCountry()).isEmpty() ){ + text += "
" + Qtopia::escapeString(value); + marker = true; + } + + // rest of Business data + str = office(); + if ( !str.isEmpty() ){ + text += "
" + QObject::tr("Office: ") + "" + + Qtopia::escapeString(str); + marker = true; + } + str = businessWebpage(); + if ( !str.isEmpty() ){ + text += "
" + QObject::tr("Business Web Page: ") + "" + + Qtopia::escapeString(str); + marker = true; + } + str = businessPhone(); + if ( !str.isEmpty() ){ + text += "
" + QObject::tr("Business Phone: ") + "" + + Qtopia::escapeString(str); + marker = true; + } + str = businessFax(); + if ( !str.isEmpty() ){ + text += "
" + QObject::tr("Business Fax: ") + "" + + Qtopia::escapeString(str); + marker = true; + } + str = businessMobile(); + if ( !str.isEmpty() ){ + text += "
" + QObject::tr("Business Mobile: ") + "" + + Qtopia::escapeString(str); + marker = true; + } + str = businessPager(); + if ( !str.isEmpty() ){ + text += "
" + QObject::tr("Business Pager: ") + "" + + Qtopia::escapeString(str); + marker = true; + } + + // text += "
"; + + // home address + if ( !homeStreet().isEmpty() || !homeCity().isEmpty() || + !homeZip().isEmpty() || !homeCountry().isEmpty() ) { + text += QObject::tr( "
Home Address:" ); + marker = true; + } + + if ( !(value = homeStreet()).isEmpty() ){ + text += "
" + Qtopia::escapeString(value); + marker = true; + } + + switch( addressformat ){ + case Zip_City_State:{ // Zip_Code City, State + state = homeState(); + if ( !(value = homeZip()).isEmpty() ){ + text += "
" + Qtopia::escapeString(value) + " "; + marker = true; + } + if ( !(value = homeCity()).isEmpty() ) { + marker = true; + if ( homeZip().isEmpty() && !homeStreet().isEmpty() ) + text += "
"; + text += Qtopia::escapeString(value); + if ( !state.isEmpty() ) + text += ", " + Qtopia::escapeString(state); + } else if (!state.isEmpty()) { + text += "
" + Qtopia::escapeString(state); + marker = true; + } + break; + } + case City_State_Zip:{ // City, State Zip_Code + state = homeState(); + if ( !(value = homeCity()).isEmpty() ) { + marker = true; + text += "
" + Qtopia::escapeString(value); + if ( state ) + text += ", " + Qtopia::escapeString(state); + } else if ( !state.isEmpty() ){ + text += "
" + Qtopia::escapeString(state); + marker = true; + } + if ( !(value = homeZip()).isEmpty() ){ + text += " " + Qtopia::escapeString(value); + marker = true; + } + break; + } + } + + if ( !(value = homeCountry()).isEmpty() ){ + text += "
" + Qtopia::escapeString(value); + marker = true; + } + + // rest of Home data + str = homeWebpage(); + if ( !str.isEmpty() ){ + text += "
" + QObject::tr("Home Web Page: ") + "" + + Qtopia::escapeString(str); + marker = true; + } + str = homePhone(); + if ( !str.isEmpty() ){ + text += "
" + QObject::tr("Home Phone: ") + "" + + Qtopia::escapeString(str); + marker = true; + } + str = homeFax(); + if ( !str.isEmpty() ){ + text += "
" + QObject::tr("Home Fax: ") + "" + + Qtopia::escapeString(str); + marker = true; + } + str = homeMobile(); + if ( !str.isEmpty() ){ + text += "
" + QObject::tr("Home Mobile: ") + "" + + Qtopia::escapeString(str); + marker = true; + } + + if ( marker ) + text += "

"; + + // the rest... + str = emails(); + if ( !str.isEmpty() && ( str != defEmail ) ) + text += "
" + QObject::tr("All Emails: ") + "" + + Qtopia::escapeString(str); + str = profession(); + if ( !str.isEmpty() ) + text += "
" + QObject::tr("Profession: ") + "" + + Qtopia::escapeString(str); + str = assistant(); + if ( !str.isEmpty() ) + text += "
" + QObject::tr("Assistant: ") + "" + + Qtopia::escapeString(str); + str = manager(); + if ( !str.isEmpty() ) + text += "
" + QObject::tr("Manager: ") + "" + + Qtopia::escapeString(str); + str = gender(); + if ( !str.isEmpty() && str.toInt() != 0 ) { + text += "
"; + if ( str.toInt() == 1 ) + str = QObject::tr( "Male" ); + else if ( str.toInt() == 2 ) + str = QObject::tr( "Female" ); + text += "" + QObject::tr("Gender: ") + "" + str; + } + str = spouse(); + if ( !str.isEmpty() ) + text += "
" + QObject::tr("Spouse: ") + "" + + Qtopia::escapeString(str); + if ( birthday().isValid() ){ + str = TimeString::numberDateString( birthday() ); + text += "
" + QObject::tr("Birthday: ") + "" + + Qtopia::escapeString(str); + } + if ( anniversary().isValid() ){ + str = TimeString::numberDateString( anniversary() ); + text += "
" + QObject::tr("Anniversary: ") + "" + + Qtopia::escapeString(str); + } + str = children(); + if ( !str.isEmpty() ) + text += "
" + QObject::tr("Children: ") + "" + + Qtopia::escapeString(str); + + str = nickname(); + if ( !str.isEmpty() ) + text += "
" + QObject::tr("Nickname: ") + "" + + Qtopia::escapeString(str); + + // categories + if ( categoryNames("Contacts").count() ){ + text += "
" + QObject::tr( "Category:") + " "; + text += categoryNames("Contacts").join(", "); + } + + // notes last + if ( !(value = notes()).isEmpty() ) { + text += "

" + QObject::tr( "Notes:") + " "; + QRegExp reg("\n"); + + //QString tmp = Qtopia::escapeString(value); + QString tmp = QStyleSheet::convertFromPlainText(value); + //tmp.replace( reg, "
" ); + text += "
" + tmp + "
"; + } + return text; +} + +/*! + \internal +*/ +void OContact::insert( int key, const QString &v ) +{ + QString value = v.stripWhiteSpace(); + if ( value.isEmpty() ) + mMap.remove( key ); + else + mMap.insert( key, value ); +} + +/*! + \internal +*/ +void OContact::replace( int key, const QString & v ) +{ + QString value = v.stripWhiteSpace(); + if ( value.isEmpty() ) + mMap.remove( key ); + else + mMap.replace( key, value ); +} + +/*! + \internal +*/ +QString OContact::find( int key ) const +{ + return mMap[key]; +} + +/*! + \internal +*/ +QString OContact::displayAddress( const QString &street, + const QString &city, + const QString &state, + const QString &zip, + const QString &country ) const +{ + QString s = street; + if ( !street.isEmpty() ) + s+= "\n"; + s += city; + if ( !city.isEmpty() && !state.isEmpty() ) + s += ", "; + s += state; + if ( !state.isEmpty() && !zip.isEmpty() ) + s += " "; + s += zip; + if ( !country.isEmpty() && !s.isEmpty() ) + s += "\n"; + s += country; + return s; +} + +/*! + \internal +*/ +QString OContact::displayBusinessAddress() const +{ + return displayAddress( businessStreet(), businessCity(), + businessState(), businessZip(), + businessCountry() ); +} + +/*! + \internal +*/ +QString OContact::displayHomeAddress() const +{ + return displayAddress( homeStreet(), homeCity(), + homeState(), homeZip(), + homeCountry() ); +} + +/*! + Returns the full name of the contact +*/ +QString OContact::fullName() const +{ + QString title = find( Qtopia::Title ); + QString firstName = find( Qtopia::FirstName ); + QString middleName = find( Qtopia::MiddleName ); + QString lastName = find( Qtopia::LastName ); + QString suffix = find( Qtopia::Suffix ); + + QString name = title; + if ( !firstName.isEmpty() ) { + if ( !name.isEmpty() ) + name += " "; + name += firstName; + } + if ( !middleName.isEmpty() ) { + if ( !name.isEmpty() ) + name += " "; + name += middleName; + } + if ( !lastName.isEmpty() ) { + if ( !name.isEmpty() ) + name += " "; + name += lastName; + } + if ( !suffix.isEmpty() ) { + if ( !name.isEmpty() ) + name += " "; + name += suffix; + } + return name.simplifyWhiteSpace(); +} + +/*! + Returns a list of the names of the children of the contact. +*/ +QStringList OContact::childrenList() const +{ + return QStringList::split( " ", find( Qtopia::Children ) ); +} + +/*! \fn void OContact::insertEmail( const QString &email ) + + Insert \a email into the email list. Ensures \a email can only be added + once. If there is no default email address set, it sets it to the \a email. +*/ + +/*! \fn void OContact::removeEmail( const QString &email ) + + Removes the \a email from the email list. If the default email was \a email, + then the default email address is assigned to the first email in the + email list +*/ + +/*! \fn void OContact::clearEmails() + + Clears the email list. + */ + +/*! \fn void OContact::insertEmails( const QStringList &emailList ) + + Appends the \a emailList to the exiting email list + */ + +/*! + Returns a list of email addresses belonging to the contact, including + the default email address. +*/ +QStringList OContact::emailList() const +{ + QString emailStr = emails(); + + QStringList r; + if ( !emailStr.isEmpty() ) { + qDebug(" emailstr "); + QStringList l = QStringList::split( emailSeparator(), emailStr ); + for ( QStringList::ConstIterator it = l.begin();it != l.end();++it ) + r += (*it).simplifyWhiteSpace(); + } + + return r; +} + +/*! + \overload + + Generates the string for the contact to be filed as from the first, + middle and last name of the contact. +*/ +void OContact::setFileAs() +{ + QString lastName, firstName, middleName, fileas; + + lastName = find( Qtopia::LastName ); + firstName = find( Qtopia::FirstName ); + middleName = find( Qtopia::MiddleName ); + if ( !lastName.isEmpty() && !firstName.isEmpty() + && !middleName.isEmpty() ) + fileas = lastName + ", " + firstName + " " + middleName; + else if ( !lastName.isEmpty() && !firstName.isEmpty() ) + fileas = lastName + ", " + firstName; + else if ( !lastName.isEmpty() || !firstName.isEmpty() || + !middleName.isEmpty() ) + fileas = firstName + ( firstName.isEmpty() ? "" : " " ) + + middleName + ( middleName.isEmpty() ? "" : " " ) + + lastName; + + replace( Qtopia::FileAs, fileas ); +} + +/*! + \internal + Appends the contact information to \a buf. +*/ +void OContact::save( QString &buf ) const +{ + static const QStringList SLFIELDS = fields(); + // I'm expecting "::ConstIterator it = mMap.begin(); + it != mMap.end(); ++it ) { + const QString &value = it.data(); + int key = it.key(); + if ( !value.isEmpty() ) { + if ( key == Qtopia::AddressCategory || key == Qtopia::AddressUid) + continue; + + key -= Qtopia::AddressCategory+1; + buf += SLFIELDS[key]; + buf += "=\"" + Qtopia::escapeString(value) + "\" "; + } + } + buf += customToXml(); + if ( categories().count() > 0 ) + buf += "Categories=\"" + idsToString( categories() ) + "\" "; + buf += "Uid=\"" + QString::number( uid() ) + "\" "; + // You need to close this yourself +} + + +/*! + \internal + Returns the list of fields belonging to a contact + Never change order of this list ! It has to be regarding + enum AddressBookFields !! +*/ +QStringList OContact::fields() +{ + QStringList list; + + list.append( "Title" ); // Not Used! + list.append( "FirstName" ); + list.append( "MiddleName" ); + list.append( "LastName" ); + list.append( "Suffix" ); + list.append( "FileAs" ); + + list.append( "JobTitle" ); + list.append( "Department" ); + list.append( "Company" ); + list.append( "BusinessPhone" ); + list.append( "BusinessFax" ); + list.append( "BusinessMobile" ); + + list.append( "DefaultEmail" ); + list.append( "Emails" ); + + list.append( "HomePhone" ); + list.append( "HomeFax" ); + list.append( "HomeMobile" ); + + list.append( "BusinessStreet" ); + list.append( "BusinessCity" ); + list.append( "BusinessState" ); + list.append( "BusinessZip" ); + list.append( "BusinessCountry" ); + list.append( "BusinessPager" ); + list.append( "BusinessWebPage" ); + + list.append( "Office" ); + list.append( "Profession" ); + list.append( "Assistant" ); + list.append( "Manager" ); + + list.append( "HomeStreet" ); + list.append( "HomeCity" ); + list.append( "HomeState" ); + list.append( "HomeZip" ); + list.append( "HomeCountry" ); + list.append( "HomeWebPage" ); + + list.append( "Spouse" ); + list.append( "Gender" ); + list.append( "Birthday" ); + list.append( "Anniversary" ); + list.append( "Nickname" ); + list.append( "Children" ); + + list.append( "Notes" ); + list.append( "Groups" ); + + return list; +} + + +/*! + Sets the list of email address for contact to those contained in \a str. + Email address should be separated by ';'s. +*/ +void OContact::setEmails( const QString &str ) +{ + replace( Qtopia::Emails, str ); + if ( str.isEmpty() ) + setDefaultEmail( QString::null ); +} + +/*! + Sets the list of children for the contact to those contained in \a str. +*/ +void OContact::setChildren( const QString &str ) +{ + replace( Qtopia::Children, str ); +} + +/*! + \overload + Returns TRUE if the contact matches the regular expression \a regexp. + Otherwise returns FALSE. +*/ +bool OContact::match( const QRegExp &r ) const +{ + setLastHitField( -1 ); + bool match; + match = false; + QMap::ConstIterator it; + for ( it = mMap.begin(); it != mMap.end(); ++it ) { + if ( (*it).find( r ) > -1 ) { + setLastHitField( it.key() ); + match = true; + break; + } + } + return match; +} + + +QString OContact::toShortText() const +{ + return ( fullName() ); +} +QString OContact::type() const +{ + return QString::fromLatin1( "OContact" ); +} + + + +class QString OContact::recordField( int pos ) const +{ + QStringList SLFIELDS = fields(); // ?? why this ? (se) + return SLFIELDS[pos]; +} + +// In future releases, we should store birthday and anniversary +// internally as QDate instead of QString ! +// QString is always too complicate to interprete (DD.MM.YY, DD/MM/YY, MM/DD/YY, etc..)(se) + +/*! \fn void OContact::setBirthday( const QDate& date ) + Sets the birthday for the contact to \a date. If date is null + the current stored date will be removed. +*/ +void OContact::setBirthday( const QDate &v ) +{ + if ( v.isNull() ){ + qWarning( "Remove Birthday"); + replace( Qtopia::Birthday, QString::null ); + return; + } + + if ( v.isValid() ) + replace( Qtopia::Birthday, OConversion::dateToString( v ) ); + +} + + +/*! \fn void OContact::setAnniversary( const QDate &date ) + Sets the anniversary of the contact to \a date. If date is + null, the current stored date will be removed. +*/ +void OContact::setAnniversary( const QDate &v ) +{ + if ( v.isNull() ){ + qWarning( "Remove Anniversary"); + replace( Qtopia::Anniversary, QString::null ); + return; + } + + if ( v.isValid() ) + replace( Qtopia::Anniversary, OConversion::dateToString( v ) ); +} + +/*! \fn QDate OContact::birthday() const + Returns the birthday of the contact. +*/ +QDate OContact::birthday() const +{ + QString str = find( Qtopia::Birthday ); + // qWarning ("Birthday %s", str.latin1() ); + if ( !str.isEmpty() ) + return OConversion::dateFromString ( str ); + else + return QDate(); +} + + +/*! \fn QDate OContact::anniversary() const + Returns the anniversary of the contact. +*/ +QDate OContact::anniversary() const +{ + QDate empty; + QString str = find( Qtopia::Anniversary ); + // qWarning ("Anniversary %s", str.latin1() ); + if ( !str.isEmpty() ) + return OConversion::dateFromString ( str ); + else + return empty; +} + + +void OContact::insertEmail( const QString &v ) +{ + //qDebug("insertEmail %s", v.latin1()); + QString e = v.simplifyWhiteSpace(); + QString def = defaultEmail(); + + // if no default, set it as the default email and don't insert + if ( def.isEmpty() ) { + setDefaultEmail( e ); // will insert into the list for us + return; + } + + // otherwise, insert assuming doesn't already exist + QString emailsStr = find( Qtopia::Emails ); + if ( emailsStr.contains( e )) + return; + if ( !emailsStr.isEmpty() ) + emailsStr += emailSeparator(); + emailsStr += e; + replace( Qtopia::Emails, emailsStr ); +} + +void OContact::removeEmail( const QString &v ) +{ + QString e = v.simplifyWhiteSpace(); + QString def = defaultEmail(); + QString emailsStr = find( Qtopia::Emails ); + QStringList emails = emailList(); + + // otherwise, must first contain it + if ( !emailsStr.contains( e ) ) + return; + + // remove it + //qDebug(" removing email from list %s", e.latin1()); + emails.remove( e ); + // reset the string + emailsStr = emails.join(emailSeparator()); // Sharp's brain dead separator + replace( Qtopia::Emails, emailsStr ); + + // if default, then replace the default email with the first one + if ( def == e ) { + //qDebug("removeEmail is default; setting new default"); + if ( !emails.count() ) + clearEmails(); + else // setDefaultEmail will remove e from the list + setDefaultEmail( emails.first() ); + } +} +void OContact::clearEmails() +{ + mMap.remove( Qtopia::DefaultEmail ); + mMap.remove( Qtopia::Emails ); +} +void OContact::setDefaultEmail( const QString &v ) +{ + QString e = v.simplifyWhiteSpace(); + + //qDebug("OContact::setDefaultEmail %s", e.latin1()); + replace( Qtopia::DefaultEmail, e ); + + if ( !e.isEmpty() ) + insertEmail( e ); + +} + +void OContact::insertEmails( const QStringList &v ) +{ + for ( QStringList::ConstIterator it = v.begin(); it != v.end(); ++it ) + insertEmail( *it ); +} +int OContact::rtti() { + return OPimResolver::AddressBook; +} +void OContact::setUid( int i ) +{ + OPimRecord::setUid(i); + replace( Qtopia::AddressUid , QString::number(i)); +} diff --git a/noncore/unsupported/libopie/pim/ocontact.h b/noncore/unsupported/libopie/pim/ocontact.h new file mode 100644 index 0000000..1d46b81 --- a/dev/null +++ b/noncore/unsupported/libopie/pim/ocontact.h @@ -0,0 +1,240 @@ +/********************************************************************** +** Copyright (C) 2000-2002 Trolltech AS. All rights reserved. +** Copyright (C) 2002-2003 by Stefan Eilers (eilers.stefan@epost.de) +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** See http://www.trolltech.com/gpl/ for GPL licensing information. +** +** Contact info@trolltech.com if any conditions of this licensing are +** not clear to you. +** +**********************************************************************/ + +#ifndef __OCONTACT_H__ +#define __OCONTACT_H__ + +#include +#include + +#include +#include + +#if defined(QPC_TEMPLATEDLL) +// MOC_SKIP_BEGIN +QPC_TEMPLATEEXTERN template class QPC_EXPORT QMap; +// MOC_SKIP_END +#endif + +class OContactPrivate; + +/** + * OContact class represents a specialised PIM Record for contacts. + * It does store all kind of persopn related information. + * + * @short Contact Container + * @author TT, Stefan Eiler, Holger Freyther + */ +class QPC_EXPORT OContact : public OPimRecord +{ + friend class DataSet; +public: + OContact(); + OContact( const QMap &fromMap ); + virtual ~OContact(); + + enum DateFormat{ + Zip_City_State = 0, + City_State_Zip + }; + + /* + * do we need to inline them + * if yes do we need to inline them this way? + * -zecke + */ + void setTitle( const QString &v ) { replace( Qtopia::Title, v ); } + void setFirstName( const QString &v ) { replace( Qtopia::FirstName, v ); } + void setMiddleName( const QString &v ) { replace( Qtopia::MiddleName, v ); } + void setLastName( const QString &v ) { replace( Qtopia::LastName, v ); } + void setSuffix( const QString &v ) { replace( Qtopia::Suffix, v ); } + void setFileAs( const QString &v ) { replace( Qtopia::FileAs, v ); } + void setFileAs(); + + // default email address + void setDefaultEmail( const QString &v ); + // inserts email to list and ensure's doesn't already exist + void insertEmail( const QString &v ); + void removeEmail( const QString &v ); + void clearEmails(); + void insertEmails( const QStringList &v ); + + // home + void setHomeStreet( const QString &v ) { replace( Qtopia::HomeStreet, v ); } + void setHomeCity( const QString &v ) { replace( Qtopia::HomeCity, v ); } + void setHomeState( const QString &v ) { replace( Qtopia::HomeState, v ); } + void setHomeZip( const QString &v ) { replace( Qtopia::HomeZip, v ); } + void setHomeCountry( const QString &v ) { replace( Qtopia::HomeCountry, v ); } + void setHomePhone( const QString &v ) { replace( Qtopia::HomePhone, v ); } + void setHomeFax( const QString &v ) { replace( Qtopia::HomeFax, v ); } + void setHomeMobile( const QString &v ) { replace( Qtopia::HomeMobile, v ); } + void setHomeWebpage( const QString &v ) { replace( Qtopia::HomeWebPage, v ); } + + // business + void setCompany( const QString &v ) { replace( Qtopia::Company, v ); } + void setBusinessStreet( const QString &v ) { replace( Qtopia::BusinessStreet, v ); } + void setBusinessCity( const QString &v ) { replace( Qtopia::BusinessCity, v ); } + void setBusinessState( const QString &v ) { replace( Qtopia::BusinessState, v ); } + void setBusinessZip( const QString &v ) { replace( Qtopia::BusinessZip, v ); } + void setBusinessCountry( const QString &v ) { replace( Qtopia::BusinessCountry, v ); } + void setBusinessWebpage( const QString &v ) { replace( Qtopia::BusinessWebPage, v ); } + void setJobTitle( const QString &v ) { replace( Qtopia::JobTitle, v ); } + void setDepartment( const QString &v ) { replace( Qtopia::Department, v ); } + void setOffice( const QString &v ) { replace( Qtopia::Office, v ); } + void setBusinessPhone( const QString &v ) { replace( Qtopia::BusinessPhone, v ); } + void setBusinessFax( const QString &v ) { replace( Qtopia::BusinessFax, v ); } + void setBusinessMobile( const QString &v ) { replace( Qtopia::BusinessMobile, v ); } + void setBusinessPager( const QString &v ) { replace( Qtopia::BusinessPager, v ); } + void setProfession( const QString &v ) { replace( Qtopia::Profession, v ); } + void setAssistant( const QString &v ) { replace( Qtopia::Assistant, v ); } + void setManager( const QString &v ) { replace( Qtopia::Manager, v ); } + + // personal + void setSpouse( const QString &v ) { replace( Qtopia::Spouse, v ); } + void setGender( const QString &v ) { replace( Qtopia::Gender, v ); } + void setBirthday( const QDate &v ); + void setAnniversary( const QDate &v ); + void setNickname( const QString &v ) { replace( Qtopia::Nickname, v ); } + void setChildren( const QString &v ); + + // other + void setNotes( const QString &v ) { replace( Qtopia::Notes, v); } + + virtual bool match( const QRegExp ®exp ) const; + +// // custom +// void setCustomField( const QString &key, const QString &v ) +// { replace(Custom- + key, v ); } + + // name + QString fullName() const; + QString title() const { return find( Qtopia::Title ); } + QString firstName() const { return find( Qtopia::FirstName ); } + QString middleName() const { return find( Qtopia::MiddleName ); } + QString lastName() const { return find( Qtopia::LastName ); } + QString suffix() const { return find( Qtopia::Suffix ); } + QString fileAs() const { return find( Qtopia::FileAs ); } + + // email + QString defaultEmail() const { return find( Qtopia::DefaultEmail ); } + QStringList emailList() const; + + // home + /* + * OPimAddress address(enum Location)const; + * would be some how nicer... + * -zecke + */ + QString homeStreet() const { return find( Qtopia::HomeStreet ); } + QString homeCity() const { return find( Qtopia::HomeCity ); } + QString homeState() const { return find( Qtopia::HomeState ); } + QString homeZip() const { return find( Qtopia::HomeZip ); } + QString homeCountry() const { return find( Qtopia::HomeCountry ); } + QString homePhone() const { return find( Qtopia::HomePhone ); } + QString homeFax() const { return find( Qtopia::HomeFax ); } + QString homeMobile() const { return find( Qtopia::HomeMobile ); } + QString homeWebpage() const { return find( Qtopia::HomeWebPage ); } + /** Multi line string containing all non-empty address info in the form + * Street + * City, State Zip + * Country + */ + QString displayHomeAddress() const; + + // business + QString company() const { return find( Qtopia::Company ); } + QString businessStreet() const { return find( Qtopia::BusinessStreet ); } + QString businessCity() const { return find( Qtopia::BusinessCity ); } + QString businessState() const { return find( Qtopia::BusinessState ); } + QString businessZip() const { return find( Qtopia::BusinessZip ); } + QString businessCountry() const { return find( Qtopia::BusinessCountry ); } + QString businessWebpage() const { return find( Qtopia::BusinessWebPage ); } + QString jobTitle() const { return find( Qtopia::JobTitle ); } + QString department() const { return find( Qtopia::Department ); } + QString office() const { return find( Qtopia::Office ); } + QString businessPhone() const { return find( Qtopia::BusinessPhone ); } + QString businessFax() const { return find( Qtopia::BusinessFax ); } + QString businessMobile() const { return find( Qtopia::BusinessMobile ); } + QString businessPager() const { return find( Qtopia::BusinessPager ); } + QString profession() const { return find( Qtopia::Profession ); } + QString assistant() const { return find( Qtopia::Assistant ); } + QString manager() const { return find( Qtopia::Manager ); } + /** Multi line string containing all non-empty address info in the form + * Street + * City, State Zip + * Country + */ + QString displayBusinessAddress() const; + + //personal + QString spouse() const { return find( Qtopia::Spouse ); } + QString gender() const { return find( Qtopia::Gender ); } + QDate birthday() const; + QDate anniversary() const; + QString nickname() const { return find( Qtopia::Nickname ); } + QString children() const { return find( Qtopia::Children ); } + QStringList childrenList() const; + + // other + QString notes() const { return find( Qtopia::Notes ); } + QString groups() const { return find( Qtopia::Groups ); } + QStringList groupList() const; + + QString toRichText() const; + QMap toMap() const; + QString field( int key ) const { return find( key ); } + + + void setUid( int i ); + + QString toShortText()const; + QString type()const; + class QString recordField(int) const; + + // Why private ? (eilers,se) + QString emailSeparator() const { return " "; } + + // the emails should be seperated by a comma + void setEmails( const QString &v ); + QString emails() const { return find( Qtopia::Emails ); } + static int rtti(); + +private: + // The XML Backend needs some access to the private functions + friend class OContactAccessBackend_XML; + + void insert( int key, const QString &value ); + void replace( int key, const QString &value ); + QString find( int key ) const; + static QStringList fields(); + + void save( QString &buf ) const; + + QString displayAddress( const QString &street, + const QString &city, + const QString &state, + const QString &zip, + const QString &country ) const; + + QMap mMap; + OContactPrivate *d; +}; + + +#endif diff --git a/noncore/unsupported/libopie/pim/ocontactaccess.cpp b/noncore/unsupported/libopie/pim/ocontactaccess.cpp new file mode 100644 index 0000000..63b93ee --- a/dev/null +++ b/noncore/unsupported/libopie/pim/ocontactaccess.cpp @@ -0,0 +1,176 @@ +/* + * Class to manage the Contacts. + * + * 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. + * ===================================================================== + * Info: This class could just work with a change in the header-file + * of the Contact class ! Therefore our libopie only compiles + * with our version of libqpe + * ===================================================================== + * ToDo: XML-Backend: Automatic reload if something was changed... + * + * + * ===================================================================== + * Version: $Id$ + * ===================================================================== + * History: + * $Log$ + * Revision 1.1 2004/11/16 21:46:07 mickeyl + * libopie1 goes into unsupported + * + * Revision 1.9 2004/03/02 12:14:22 alwin + * run the optimize_connect script + * the whole cvs is tagged with "before_optimize_connect" if there are problems you + * can check the diff (but it had compiled and run here) + * + * Revision 1.8 2003/05/08 13:55:09 tille + * search stuff + * and match, toRichText & toShortText in oevent + * + * Revision 1.7 2002/11/13 14:14:51 eilers + * Added sorted for Contacts.. + * + * Revision 1.6 2002/11/01 15:10:42 eilers + * Added regExp-search in database for all fields in a contact. + * + * Revision 1.5 2002/10/16 10:52:40 eilers + * Added some docu to the interface and now using the cache infrastucture by zecke.. :) + * + * Revision 1.4 2002/10/14 16:21:54 eilers + * Some minor interface updates + * + * Revision 1.3 2002/10/07 17:34:24 eilers + * added OBackendFactory for advanced backend access + * + * Revision 1.2 2002/10/02 16:18:11 eilers + * debugged and seems to work almost perfectly .. + * + * 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 ! + * + * + */ + +#include "ocontactaccess.h" +#include "obackendfactory.h" + +#include +#include +#include +#include +#include +#include + +//#include +#include + +#include +#include +#include +#include + +#include "ocontactaccessbackend_xml.h" + + +OContactAccess::OContactAccess ( const QString appname, const QString , + OContactAccessBackend* end, bool autosync ): + OPimAccessTemplate( end ) +{ + /* take care of the backend. If there is no one defined, we + * will use the XML-Backend as default (until we have a cute SQL-Backend..). + */ + if( end == 0 ) { + qWarning ("Using BackendFactory !"); + end = OBackendFactory::Default( "contact", appname ); + } + // Set backend locally and in template + m_backEnd = end; + OPimAccessTemplate::setBackEnd (end); + + + /* Connect signal of external db change to function */ + QCopChannel *dbchannel = new QCopChannel( "QPE/PIM", this ); + connect( dbchannel, SIGNAL(received(const QCString&,const QByteArray&)), + this, SLOT(copMessage(const QCString&,const QByteArray&)) ); + if ( autosync ){ + QCopChannel *syncchannel = new QCopChannel( "QPE/Sync", this ); + connect( syncchannel, SIGNAL(received(const QCString&,const QByteArray&)), + this, SLOT(copMessage(const QCString&,const QByteArray&)) ); + } + + +} +OContactAccess::~OContactAccess () +{ + /* The user may forget to save the changed database, therefore try to + * do it for him.. + */ + save(); + // delete m_backEnd; is done by template.. +} + + +bool OContactAccess::save () +{ + /* If the database was changed externally, we could not save the + * Data. This will remove added items which is unacceptable ! + * Therefore: Reload database and merge the data... + */ + if ( OPimAccessTemplate::wasChangedExternally() ) + reload(); + + bool status = OPimAccessTemplate::save(); + if ( !status ) return false; + + /* Now tell everyone that new data is available. + */ + QCopEnvelope e( "QPE/PIM", "addressbookUpdated()" ); + + return true; +} + +const uint OContactAccess::querySettings() +{ + return ( m_backEnd->querySettings() ); +} + +bool OContactAccess::hasQuerySettings ( int querySettings ) const +{ + return ( m_backEnd->hasQuerySettings ( querySettings ) ); +} +ORecordList OContactAccess::sorted( bool ascending, int sortOrder, int sortFilter, int cat ) const +{ + QArray matchingContacts = m_backEnd -> sorted( ascending, sortOrder, sortFilter, cat ); + return ( ORecordList(matchingContacts, this) ); +} + + +bool OContactAccess::wasChangedExternally()const +{ + return ( m_backEnd->wasChangedExternally() ); +} + + +void OContactAccess::copMessage( const QCString &msg, const QByteArray & ) +{ + if ( msg == "addressbookUpdated()" ){ + qWarning ("OContactAccess: Received addressbokUpdated()"); + emit signalChanged ( this ); + } else if ( msg == "flush()" ) { + qWarning ("OContactAccess: Received flush()"); + save (); + } else if ( msg == "reload()" ) { + qWarning ("OContactAccess: Received reload()"); + reload (); + emit signalChanged ( this ); + } +} diff --git a/noncore/unsupported/libopie/pim/ocontactaccess.h b/noncore/unsupported/libopie/pim/ocontactaccess.h new file mode 100644 index 0000000..a7a099f --- a/dev/null +++ b/noncore/unsupported/libopie/pim/ocontactaccess.h @@ -0,0 +1,196 @@ +/* + * 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.1 2004/11/16 21:46:07 mickeyl + * libopie1 goes into unsupported + * + * 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 + +#include + +#include +#include + +#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 +{ + 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 true the database stores the current state + * automatically if it receives the signals flush() and reload() + * which are used before and after synchronisation. If the application wants + * to react itself, it should be disabled by setting it to false + * @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: query_IgnoreCase 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 true 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/noncore/unsupported/libopie/pim/ocontactaccessbackend.h b/noncore/unsupported/libopie/pim/ocontactaccessbackend.h new file mode 100644 index 0000000..cfeeff2 --- a/dev/null +++ b/noncore/unsupported/libopie/pim/ocontactaccessbackend.h @@ -0,0 +1,131 @@ +/** + * The class responsible for managing a backend. + * The implementation of this abstract class contains + * the complete database handling. + * + * 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.1 2004/11/16 21:46:07 mickeyl + * libopie1 goes into unsupported + * + * Revision 1.7 2004/02/19 02:05:37 zecke + * Add notes for API fixes and BC stuff + * + * Revision 1.6 2003/08/01 12:30:16 eilers + * Merging changes from BRANCH_1_0 to HEAD + * + * Revision 1.5.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.5 2003/04/13 18:07:10 zecke + * More API doc + * QString -> const QString& + * QString = 0l -> QString::null + * + * Revision 1.4 2002/11/13 14:14:51 eilers + * Added sorted for Contacts.. + * + * Revision 1.3 2002/11/01 15:10:42 eilers + * Added regExp-search in database for all fields in a contact. + * + * Revision 1.2 2002/10/07 17:34:24 eilers + * added OBackendFactory for advanced backend access + * + * 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 _OCONTACTACCESSBACKEND_H_ +#define _OCONTACTACCESSBACKEND_H_ + +#include "ocontact.h" +#include "opimaccessbackend.h" + +#include + +/** + * This class represents the interface of all Contact Backends. + * Derivates of this class will be used to access the contacts. + * As implementation currently XML and vCard exist. This class needs to be implemented + * if you want to provide your own storage. + * In all queries a list of uids is passed on instead of loading the actual record! + * + * @see OContactAccessBackend_VCard + * @see OContactAccessBackend_XML + */ +class OContactAccessBackend: public OPimAccessBackend { + public: + /** + * @todo make non line in regard to BC guide of KDE + */ + OContactAccessBackend() {} + /** + * @todo make non inline in regard to the BC guide of KDE + */ + virtual ~OContactAccessBackend() {} + + + /** + * Return if database was changed externally. + * This may just make sense on file based databases like a XML-File. + * It is used to prevent to overwrite the current database content + * if the file was already changed by something else ! + * If this happens, we have to reload before save our data. + * If we use real databases, this should be handled by the database + * management system themselve, therefore this function should always return false in + * this case. It is not our problem to handle this conflict ... + * @return true if the database was changed and if save without reload will + * be dangerous. false if the database was not changed or it is save to write + * in this situation. + */ + virtual bool wasChangedExternally() = 0; + + virtual QArray matchRegexp( const QRegExp &r ) const = 0; + + /** + * Return all possible settings. + * @return All settings provided by the current backend + * (i.e.: query_WildCards & query_IgnoreCase) + */ + virtual const uint querySettings() = 0; + + /** + * Check whether settings are correct. + * @return true if the given settings are correct and possible. + */ + virtual bool hasQuerySettings (uint querySettings) const = 0; + + /** + * FIXME!!! + * Returns a sorted list of records either ascendinf or descending for a giving criteria and category + */ + virtual QArray sorted( bool ascending, int sortOrder, int sortFilter, int cat ) = 0; + + +private: + class Private; + Private *d; +}; +#endif diff --git a/noncore/unsupported/libopie/pim/ocontactaccessbackend_sql.cpp b/noncore/unsupported/libopie/pim/ocontactaccessbackend_sql.cpp new file mode 100644 index 0000000..669483d --- a/dev/null +++ b/noncore/unsupported/libopie/pim/ocontactaccessbackend_sql.cpp @@ -0,0 +1,948 @@ +/* + * 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.1 2004/11/16 21:46:07 mickeyl + * libopie1 goes into unsupported + * + * Revision 1.5 2004/03/14 13:50:35 alwin + * namespace correction + * + * 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 +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +using namespace Opie::DB; + + +// 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& ); + ~FindQuery(); + QString query()const; + private: + QString single()const; + QString multi()const; + QArray m_uids; + int m_uid; + }; + + /** + * a find query for custom elements + */ + class FindCustomQuery : public OSQLQuery { + public: + FindCustomQuery(int uid); + FindCustomQuery(const QArray& ); + ~FindCustomQuery(); + QString query()const; + private: + QString single()const; + QString multi()const; + QArray 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 contactMap = m_contact.toMap(); + + QStringList fieldList = OContactFields::untrfields( false ); + QMap 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 contactMap = m_contact.toMap(); + + QMap addressbook_db; + + // Get the translation from the ID to the String + QMap transMap = OContactFields::idToUntrFields(); + + for( QMap::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::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 customMap = m_contact.toExtraMap(); + for( QMap::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& 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& 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(); // Shouldn't m_driver->sync be better than close ? (eilers) +} + + +void OContactAccessBackend_SQL::clear () +{ + ClearQuery cle; + OSQLResult res = m_driver->query( &cle ); + + reload(); +} + +bool OContactAccessBackend_SQL::wasChangedExternally() +{ + return false; +} + +QArray 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 OContactAccessBackend_SQL::queryByExample ( const OContact &query, int settings, const QDateTime& d = QDateTime() ) +{ + QString qu = "SELECT uid FROM addressbook WHERE"; + + QMap queryFields = query.toMap(); + QStringList fieldList = OContactFields::untrfields( false ); + QMap 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 empty; + return empty; + } + + QArray list = extractUids( res ); + + return list; +} + +QArray OContactAccessBackend_SQL::matchRegexp( const QRegExp &r ) const +{ + QArray 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 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 empty; + return empty; + } + + QArray 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 OContactAccessBackend_SQL::extractUids( OSQLResult& res ) const +{ + qWarning("extractUids"); + QTime t; + t.start(); + OSQLResultItem::ValueList list = res.results(); + OSQLResultItem::ValueList::Iterator it; + QArray 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 OContactAccessBackend_SQL::requestNonCustom( int uid ) const +{ + QTime t; + t.start(); + + QMap 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 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 OContactAccessBackend_SQL::requestNonCustom( int uid ) const +{ + QTime t; + t.start(); + + QMap 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 empty; + return empty; + } + + int t3needed = 0; + QTime t3; + t3.start(); + QMap 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 OContactAccessBackend_SQL::requestCustom( int uid ) const +{ + QTime t; + t.start(); + + QMap customMap; + + FindCustomQuery query( uid ); + OSQLResult res_custom = m_driver->query( &query ); + + if ( res_custom.state() == OSQLResult::Failure ) { + qWarning("OSQLResult::Failure in find query !!"); + QMap empty; + return empty; + } + + OSQLResultItem::ValueList list = res_custom.results(); + OSQLResultItem::ValueList::Iterator it = list.begin(); + for ( ; it != list.end(); ++it ) { + customMap.insert( (*it).data( "type" ), (*it).data( "value" ) ); + } + + qWarning("RequestCustom needed: %d ms", t.elapsed() ); + return customMap; +} diff --git a/noncore/unsupported/libopie/pim/ocontactaccessbackend_sql.h b/noncore/unsupported/libopie/pim/ocontactaccessbackend_sql.h new file mode 100644 index 0000000..8cd92e8 --- a/dev/null +++ b/noncore/unsupported/libopie/pim/ocontactaccessbackend_sql.h @@ -0,0 +1,110 @@ +/* + * 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.1 2004/11/16 21:46:07 mickeyl + * libopie1 goes into unsupported + * + * Revision 1.3 2004/03/14 13:50:35 alwin + * namespace correction + * + * Revision 1.2 2003/12/08 15:18:11 eilers + * Committing unfinished sql implementation before merging to libopie2 starts.. + * + * Revision 1.1 2003/09/22 14:31:16 eilers + * Added first experimental incarnation of sql-backend for addressbook. + * Some modifications to be able to compile the todo sql-backend. + * A lot of changes fill follow... + * + * + */ + +#ifndef _OContactAccessBackend_SQL_ +#define _OContactAccessBackend_SQL_ + +#include "ocontactaccessbackend.h" +#include "ocontactaccess.h" + +#include +#include + +namespace Opie { namespace DB { +class OSQLDriver; +class OSQLResult; +class OSQLResultItem; + +}} + +/* the default xml implementation */ +/** + * This class is the SQL implementation of a Contact backend + * it does implement everything available for OContact. + * @see OPimAccessBackend for more information of available methods + */ +class OContactAccessBackend_SQL : public OContactAccessBackend { + public: + OContactAccessBackend_SQL ( const QString& appname, const QString& filename = QString::null ); + + ~OContactAccessBackend_SQL (); + + bool save(); + + bool load (); + + void clear (); + + bool wasChangedExternally(); + + QArray allRecords() const; + + OContact find ( int uid ) const; + // FIXME: Add lookahead-cache support ! + //OContact find(int uid, const QArray&, uint cur, Frontend::CacheDirection )const; + + QArray queryByExample ( const OContact &query, int settings, + const QDateTime& d ); + + QArray matchRegexp( const QRegExp &r ) const; + + const uint querySettings(); + + bool hasQuerySettings (uint querySettings) const; + + // Currently only asc implemented.. + QArray sorted( bool asc, int , int , int ); + bool add ( const OContact &newcontact ); + + bool replace ( const OContact &contact ); + + bool remove ( int uid ); + bool reload(); + + private: + QArray extractUids( Opie::DB::OSQLResult& res ) const; + QMap requestNonCustom( int uid ) const; + QMap requestCustom( int uid ) const; + void update(); + + protected: + bool m_changed; + QString m_fileName; + QArray m_uids; + + Opie::DB::OSQLDriver* m_driver; +}; + +#endif diff --git a/noncore/unsupported/libopie/pim/ocontactaccessbackend_vcard.cpp b/noncore/unsupported/libopie/pim/ocontactaccessbackend_vcard.cpp new file mode 100644 index 0000000..a795b56 --- a/dev/null +++ b/noncore/unsupported/libopie/pim/ocontactaccessbackend_vcard.cpp @@ -0,0 +1,649 @@ +/* + * VCard Backend for the OPIE-Contact Database. + * + * Copyright (C) 2000 Trolltech AS. All rights reserved. + * 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: + * + * ===================================================================== + * Version: $Id$ + * ===================================================================== + * History: + * $Log$ + * Revision 1.1 2004/11/16 21:46:07 mickeyl + * libopie1 goes into unsupported + * + * Revision 1.11 2003/08/01 12:30:16 eilers + * Merging changes from BRANCH_1_0 to HEAD + * + * Revision 1.10.4.3 2003/07/23 08:54:37 eilers + * Default email was added to the list of all emails, which already contains + * the default email.. + * This closes bug #1045 + * + * Revision 1.10.4.2 2003/07/23 08:44:45 eilers + * Importing of Notes in vcard files wasn't implemented. + * Closes bug #1044 + * + * Revision 1.10.4.1 2003/06/02 13:37:49 eilers + * Fixing memory leak + * + * Revision 1.10 2003/04/13 18:07:10 zecke + * More API doc + * QString -> const QString& + * QString = 0l -> QString::null + * + * Revision 1.9 2003/03/21 10:33:09 eilers + * Merged speed optimized xml backend for contacts to main. + * Added QDateTime to querybyexample. For instance, it is now possible to get + * all Birthdays/Anniversaries between two dates. This should be used + * to show all birthdays in the datebook.. + * This change is sourcecode backward compatible but you have to upgrade + * the binaries for today-addressbook. + * + * Revision 1.8 2003/02/21 16:52:49 zecke + * -Remove old Todo classes they're deprecated and today I already using the + * new API + * -Guard against self assignment in OTodo + * -Add test apps for OPIM + * -Opiefied Event classes + * -Added TimeZone handling and pinning of TimeZones to OEvent + * -Adjust ORecur and the widget to better timezone behaviour + * + * Revision 1.7 2003/02/16 22:25:46 zecke + * 0000276 Fix for that bug.. or better temp workaround + * A Preferred Number is HOME|VOICE + * A CellPhone is HOME|VOICE|CELL the type & HOME|VOICE test + * triggers both + * and the cell phone number overrides the other entries.. + * + * as a temp I check that it's not equal to HOME|VOICE|CELL before setting the + * number + * + * The right and final fix would be to reorder the if statement to make it + * if else based and the less common thing put to the bottom + * + * OTodoAccessVcal fix the date for beaming + * + * Revision 1.6 2003/01/13 15:49:31 eilers + * Fixing crash when businesscard.vcf is missing.. + * + * Revision 1.5 2002/12/07 13:26:22 eilers + * Fixing bug in storing anniversary.. + * + * Revision 1.4 2002/11/13 14:14:51 eilers + * Added sorted for Contacts.. + * + * Revision 1.3 2002/11/11 16:41:09 kergoth + * no default arguments in implementation + * + * Revision 1.2 2002/11/10 15:41:53 eilers + * Bugfixes.. + * + * Revision 1.1 2002/11/09 14:34:52 eilers + * Added VCard Backend. + * + */ +#include "ocontactaccessbackend_vcard.h" +#include "../../library/backend/vobject_p.h" +#include "../../library/backend/qfiledirect_p.h" + +#include + +#include + +OContactAccessBackend_VCard::OContactAccessBackend_VCard ( const QString& , const QString& filename ): + m_dirty( false ), + m_file( filename ) +{ + load(); +} + + +bool OContactAccessBackend_VCard::load () +{ + m_map.clear(); + m_dirty = false; + + VObject* obj = 0l; + + if ( QFile::exists(m_file) ){ + obj = Parse_MIME_FromFileName( QFile::encodeName(m_file).data() ); + if ( !obj ) + return false; + }else{ + qWarning("File \"%s\" not found !", m_file.latin1() ); + return false; + } + + while ( obj ) { + OContact con = parseVObject( obj ); + /* + * if uid is 0 assign a new one + * this at least happens on + * Nokia6210 + */ + if ( con.uid() == 0 ){ + con.setUid( 1 ); + qWarning("assigned new uid %d",con.uid() ); + } + + m_map.insert( con.uid(), con ); + + VObject *t = obj; + obj = nextVObjectInList(obj); + cleanVObject( t ); + } + + return true; + +} +bool OContactAccessBackend_VCard::reload() +{ + return load(); +} +bool OContactAccessBackend_VCard::save() +{ + if (!m_dirty ) + return true; + + QFileDirect file( m_file ); + if (!file.open(IO_WriteOnly ) ) + return false; + + VObject *obj; + obj = newVObject( VCCalProp ); + addPropValue( obj, VCVersionProp, "1.0" ); + + VObject *vo; + for(QMap::ConstIterator it=m_map.begin(); it !=m_map.end(); ++it ){ + vo = createVObject( *it ); + writeVObject( file.directHandle() , vo ); + cleanVObject( vo ); + } + cleanStrTbl(); + deleteVObject( obj ); + + m_dirty = false; + return true; + + +} +void OContactAccessBackend_VCard::clear () +{ + m_map.clear(); + m_dirty = true; // ??? sure ? (se) +} + +bool OContactAccessBackend_VCard::add ( const OContact& newcontact ) +{ + m_map.insert( newcontact.uid(), newcontact ); + m_dirty = true; + return true; +} + +bool OContactAccessBackend_VCard::remove ( int uid ) +{ + m_map.remove( uid ); + m_dirty = true; + return true; +} + +bool OContactAccessBackend_VCard::replace ( const OContact &contact ) +{ + m_map.replace( contact.uid(), contact ); + m_dirty = true; + return true; +} + +OContact OContactAccessBackend_VCard::find ( int uid ) const +{ + return m_map[uid]; +} + +QArray OContactAccessBackend_VCard::allRecords() const +{ + QArray ar( m_map.count() ); + QMap::ConstIterator it; + int i = 0; + for ( it = m_map.begin(); it != m_map.end(); ++it ) { + ar[i] = it.key(); + i++; + } + return ar; +} + +// Not implemented +QArray OContactAccessBackend_VCard::queryByExample ( const OContact&, int, const QDateTime& ) +{ + QArray ar(0); + return ar; +} + +// Not implemented +QArray OContactAccessBackend_VCard::matchRegexp( const QRegExp& ) const +{ + QArray ar(0); + return ar; +} + +const uint OContactAccessBackend_VCard::querySettings() +{ + return 0; // No search possible +} + +bool OContactAccessBackend_VCard::hasQuerySettings (uint ) const +{ + return false; // No search possible, therefore all settings invalid ;) +} + +bool OContactAccessBackend_VCard::wasChangedExternally() +{ + return false; // Don't expect concurrent access +} + +// Not implemented +QArray OContactAccessBackend_VCard::sorted( bool , int, int, int ) +{ + QArray ar(0); + return ar; +} + +// *** Private stuff *** + + +OContact OContactAccessBackend_VCard::parseVObject( VObject *obj ) +{ + OContact c; + + VObjectIterator it; + initPropIterator( &it, obj ); + while( moreIteration( &it ) ) { + VObject *o = nextVObject( &it ); + QCString name = vObjectName( o ); + QCString value = vObjectStringZValue( o ); + if ( name == VCNameProp ) { + VObjectIterator nit; + initPropIterator( &nit, o ); + while( moreIteration( &nit ) ) { + VObject *o = nextVObject( &nit ); + QCString name = vObjectTypeInfo( o ); + QString value = vObjectStringZValue( o ); + if ( name == VCNamePrefixesProp ) + c.setTitle( value ); + else if ( name == VCNameSuffixesProp ) + c.setSuffix( value ); + else if ( name == VCFamilyNameProp ) + c.setLastName( value ); + else if ( name == VCGivenNameProp ) + c.setFirstName( value ); + else if ( name == VCAdditionalNamesProp ) + c.setMiddleName( value ); + } + } + else if ( name == VCAdrProp ) { + bool work = TRUE; // default address is work address + QString street; + QString city; + QString region; + QString postal; + QString country; + + VObjectIterator nit; + initPropIterator( &nit, o ); + while( moreIteration( &nit ) ) { + VObject *o = nextVObject( &nit ); + QCString name = vObjectName( o ); + QString value = vObjectStringZValue( o ); + if ( name == VCHomeProp ) + work = FALSE; + else if ( name == VCWorkProp ) + work = TRUE; + else if ( name == VCStreetAddressProp ) + street = value; + else if ( name == VCCityProp ) + city = value; + else if ( name == VCRegionProp ) + region = value; + else if ( name == VCPostalCodeProp ) + postal = value; + else if ( name == VCCountryNameProp ) + country = value; + } + if ( work ) { + c.setBusinessStreet( street ); + c.setBusinessCity( city ); + c.setBusinessCountry( country ); + c.setBusinessZip( postal ); + c.setBusinessState( region ); + } else { + c.setHomeStreet( street ); + c.setHomeCity( city ); + c.setHomeCountry( country ); + c.setHomeZip( postal ); + c.setHomeState( region ); + } + } + else if ( name == VCTelephoneProp ) { + enum { + HOME = 0x01, + WORK = 0x02, + VOICE = 0x04, + CELL = 0x08, + FAX = 0x10, + PAGER = 0x20, + UNKNOWN = 0x80 + }; + int type = 0; + + VObjectIterator nit; + initPropIterator( &nit, o ); + while( moreIteration( &nit ) ) { + VObject *o = nextVObject( &nit ); + QCString name = vObjectTypeInfo( o ); + if ( name == VCHomeProp ) + type |= HOME; + else if ( name == VCWorkProp ) + type |= WORK; + else if ( name == VCVoiceProp ) + type |= VOICE; + else if ( name == VCCellularProp ) + type |= CELL; + else if ( name == VCFaxProp ) + type |= FAX; + else if ( name == VCPagerProp ) + type |= PAGER; + else if ( name == VCPreferredProp ) + ; + else + type |= UNKNOWN; + } + if ( (type & UNKNOWN) != UNKNOWN ) { + if ( ( type & (HOME|WORK) ) == 0 ) // default + type |= HOME; + if ( ( type & (VOICE|CELL|FAX|PAGER) ) == 0 ) // default + type |= VOICE; + + qWarning("value %s %d", value.data(), type ); + if ( (type & (VOICE|HOME) ) == (VOICE|HOME) && (type & (CELL|HOME) ) != (CELL|HOME) ) + c.setHomePhone( value ); + if ( ( type & (FAX|HOME) ) == (FAX|HOME) ) + c.setHomeFax( value ); + if ( ( type & (CELL|HOME) ) == (CELL|HOME) ) + c.setHomeMobile( value ); + if ( ( type & (VOICE|WORK) ) == (VOICE|WORK) && (type & (CELL|WORK) ) != (CELL|WORK) ) + c.setBusinessPhone( value ); + if ( ( type & (FAX|WORK) ) == (FAX|WORK) ) + c.setBusinessFax( value ); + if ( ( type & (CELL|WORK) ) == (CELL|WORK) ) + c.setBusinessMobile( value ); + if ( ( type & (PAGER|WORK) ) == (PAGER|WORK) ) + c.setBusinessPager( value ); + } + } + else if ( name == VCEmailAddressProp ) { + QString email = vObjectStringZValue( o ); + bool valid = TRUE; + VObjectIterator nit; + initPropIterator( &nit, o ); + while( moreIteration( &nit ) ) { + VObject *o = nextVObject( &nit ); + QCString name = vObjectTypeInfo( o ); + if ( name != VCInternetProp && name != VCHomeProp && + name != VCWorkProp && + name != VCPreferredProp ) + // ### preffered should map to default email + valid = FALSE; + } + if ( valid ) { + c.insertEmail( email ); + } + } + else if ( name == VCURLProp ) { + VObjectIterator nit; + initPropIterator( &nit, o ); + while( moreIteration( &nit ) ) { + VObject *o = nextVObject( &nit ); + QCString name = vObjectTypeInfo( o ); + if ( name == VCHomeProp ) + c.setHomeWebpage( value ); + else if ( name == VCWorkProp ) + c.setBusinessWebpage( value ); + } + } + else if ( name == VCOrgProp ) { + VObjectIterator nit; + initPropIterator( &nit, o ); + while( moreIteration( &nit ) ) { + VObject *o = nextVObject( &nit ); + QCString name = vObjectName( o ); + QString value = vObjectStringZValue( o ); + if ( name == VCOrgNameProp ) + c.setCompany( value ); + else if ( name == VCOrgUnitProp ) + c.setDepartment( value ); + else if ( name == VCOrgUnit2Prop ) + c.setOffice( value ); + } + } + else if ( name == VCTitleProp ) { + c.setJobTitle( value ); + } + else if ( name == "X-Qtopia-Profession" ) { + c.setProfession( value ); + } + else if ( name == "X-Qtopia-Manager" ) { + c.setManager( value ); + } + else if ( name == "X-Qtopia-Assistant" ) { + c.setAssistant( value ); + } + else if ( name == "X-Qtopia-Spouse" ) { + c.setSpouse( value ); + } + else if ( name == "X-Qtopia-Gender" ) { + c.setGender( value ); + } + else if ( name == "X-Qtopia-Anniversary" ) { + c.setAnniversary( convVCardDateToDate( value ) ); + } + else if ( name == "X-Qtopia-Nickname" ) { + c.setNickname( value ); + } + else if ( name == "X-Qtopia-Children" ) { + c.setChildren( value ); + } + else if ( name == VCBirthDateProp ) { + // Reading Birthdate regarding RFC 2425 (5.8.4) + c.setBirthday( convVCardDateToDate( value ) ); + + } + else if ( name == VCCommentProp ) { + c.setNotes( value ); + } +#if 0 + else { + printf("Name: %s, value=%s\n", name.data(), vObjectStringZValue( o ) ); + VObjectIterator nit; + initPropIterator( &nit, o ); + while( moreIteration( &nit ) ) { + VObject *o = nextVObject( &nit ); + QCString name = vObjectName( o ); + QString value = vObjectStringZValue( o ); + printf(" subprop: %s = %s\n", name.data(), value.latin1() ); + } + } +#endif + } + c.setFileAs(); + return c; +} + + +VObject* OContactAccessBackend_VCard::createVObject( const OContact &c ) +{ + VObject *vcard = newVObject( VCCardProp ); + safeAddPropValue( vcard, VCVersionProp, "2.1" ); + safeAddPropValue( vcard, VCLastRevisedProp, TimeConversion::toISO8601( QDateTime::currentDateTime() ) ); + safeAddPropValue( vcard, VCUniqueStringProp, QString::number(c.uid()) ); + + // full name + safeAddPropValue( vcard, VCFullNameProp, c.fullName() ); + + // name properties + VObject *name = safeAddProp( vcard, VCNameProp ); + safeAddPropValue( name, VCFamilyNameProp, c.lastName() ); + safeAddPropValue( name, VCGivenNameProp, c.firstName() ); + safeAddPropValue( name, VCAdditionalNamesProp, c.middleName() ); + safeAddPropValue( name, VCNamePrefixesProp, c.title() ); + safeAddPropValue( name, VCNameSuffixesProp, c.suffix() ); + + // home properties + VObject *home_adr= safeAddProp( vcard, VCAdrProp ); + safeAddProp( home_adr, VCHomeProp ); + safeAddPropValue( home_adr, VCStreetAddressProp, c.homeStreet() ); + safeAddPropValue( home_adr, VCCityProp, c.homeCity() ); + safeAddPropValue( home_adr, VCRegionProp, c.homeState() ); + safeAddPropValue( home_adr, VCPostalCodeProp, c.homeZip() ); + safeAddPropValue( home_adr, VCCountryNameProp, c.homeCountry() ); + + VObject *home_phone = safeAddPropValue( vcard, VCTelephoneProp, c.homePhone() ); + safeAddProp( home_phone, VCHomeProp ); + home_phone = safeAddPropValue( vcard, VCTelephoneProp, c.homeMobile() ); + safeAddProp( home_phone, VCHomeProp ); + safeAddProp( home_phone, VCCellularProp ); + home_phone = safeAddPropValue( vcard, VCTelephoneProp, c.homeFax() ); + safeAddProp( home_phone, VCHomeProp ); + safeAddProp( home_phone, VCFaxProp ); + + VObject *url = safeAddPropValue( vcard, VCURLProp, c.homeWebpage() ); + safeAddProp( url, VCHomeProp ); + + // work properties + VObject *work_adr= safeAddProp( vcard, VCAdrProp ); + safeAddProp( work_adr, VCWorkProp ); + safeAddPropValue( work_adr, VCStreetAddressProp, c.businessStreet() ); + safeAddPropValue( work_adr, VCCityProp, c.businessCity() ); + safeAddPropValue( work_adr, VCRegionProp, c.businessState() ); + safeAddPropValue( work_adr, VCPostalCodeProp, c.businessZip() ); + safeAddPropValue( work_adr, VCCountryNameProp, c.businessCountry() ); + + VObject *work_phone = safeAddPropValue( vcard, VCTelephoneProp, c.businessPhone() ); + safeAddProp( work_phone, VCWorkProp ); + work_phone = safeAddPropValue( vcard, VCTelephoneProp, c.businessMobile() ); + safeAddProp( work_phone, VCWorkProp ); + safeAddProp( work_phone, VCCellularProp ); + work_phone = safeAddPropValue( vcard, VCTelephoneProp, c.businessFax() ); + safeAddProp( work_phone, VCWorkProp ); + safeAddProp( work_phone, VCFaxProp ); + work_phone = safeAddPropValue( vcard, VCTelephoneProp, c.businessPager() ); + safeAddProp( work_phone, VCWorkProp ); + safeAddProp( work_phone, VCPagerProp ); + + url = safeAddPropValue( vcard, VCURLProp, c.businessWebpage() ); + safeAddProp( url, VCWorkProp ); + + VObject *title = safeAddPropValue( vcard, VCTitleProp, c.jobTitle() ); + safeAddProp( title, VCWorkProp ); + + + QStringList emails = c.emailList(); + // emails.prepend( c.defaultEmail() ); Fix for bugreport #1045 + for( QStringList::Iterator it = emails.begin(); it != emails.end(); ++it ) { + VObject *email = safeAddPropValue( vcard, VCEmailAddressProp, *it ); + safeAddProp( email, VCInternetProp ); + } + + safeAddPropValue( vcard, VCNoteProp, c.notes() ); + + // Exporting Birthday regarding RFC 2425 (5.8.4) + if ( c.birthday().isValid() ){ + qWarning("Exporting birthday as: %s", convDateToVCardDate( c.birthday() ).latin1() ); + safeAddPropValue( vcard, VCBirthDateProp, convDateToVCardDate( c.birthday() ) ); + } + + if ( !c.company().isEmpty() || !c.department().isEmpty() || !c.office().isEmpty() ) { + VObject *org = safeAddProp( vcard, VCOrgProp ); + safeAddPropValue( org, VCOrgNameProp, c.company() ); + safeAddPropValue( org, VCOrgUnitProp, c.department() ); + safeAddPropValue( org, VCOrgUnit2Prop, c.office() ); + } + + // some values we have to export as custom fields + safeAddPropValue( vcard, "X-Qtopia-Profession", c.profession() ); + safeAddPropValue( vcard, "X-Qtopia-Manager", c.manager() ); + safeAddPropValue( vcard, "X-Qtopia-Assistant", c.assistant() ); + + safeAddPropValue( vcard, "X-Qtopia-Spouse", c.spouse() ); + safeAddPropValue( vcard, "X-Qtopia-Gender", c.gender() ); + if ( c.anniversary().isValid() ){ + qWarning("Exporting anniversary as: %s", convDateToVCardDate( c.anniversary() ).latin1() ); + safeAddPropValue( vcard, "X-Qtopia-Anniversary", convDateToVCardDate( c.anniversary() ) ); + } + safeAddPropValue( vcard, "X-Qtopia-Nickname", c.nickname() ); + safeAddPropValue( vcard, "X-Qtopia-Children", c.children() ); + + return vcard; +} + +QString OContactAccessBackend_VCard::convDateToVCardDate( const QDate& d ) const +{ + QString str_rfc2425 = QString("%1-%2-%3") + .arg( d.year() ) + .arg( d.month(), 2 ) + .arg( d.day(), 2 ); + // Now replace spaces with "0"... + int pos = 0; + while ( ( pos = str_rfc2425.find (' ') ) > 0 ) + str_rfc2425.replace( pos, 1, "0" ); + + return str_rfc2425; +} + +QDate OContactAccessBackend_VCard::convVCardDateToDate( const QString& datestr ) +{ + int monthPos = datestr.find('-'); + int dayPos = datestr.find('-', monthPos+1 ); + int sep_ignore = 1; + if ( monthPos == -1 || dayPos == -1 ) { + qDebug("fromString didn't find - in str = %s; mpos = %d ypos = %d", datestr.latin1(), monthPos, dayPos ); + // Ok.. No "-" found, therefore we will try to read other format ( YYYYMMDD ) + if ( datestr.length() == 8 ){ + monthPos = 4; + dayPos = 6; + sep_ignore = 0; + qDebug("Try with follwing positions str = %s; mpos = %d ypos = %d", datestr.latin1(), monthPos, dayPos ); + } else { + return QDate(); + } + } + int y = datestr.left( monthPos ).toInt(); + int m = datestr.mid( monthPos + sep_ignore, dayPos - monthPos - sep_ignore ).toInt(); + int d = datestr.mid( dayPos + sep_ignore ).toInt(); + qDebug("TimeConversion::fromString ymd = %s => %d %d %d; mpos = %d ypos = %d", datestr.latin1(), y, m, d, monthPos, dayPos); + QDate date ( y,m,d ); + return date; +} + +VObject* OContactAccessBackend_VCard::safeAddPropValue( VObject *o, const char *prop, const QString &value ) +{ + VObject *ret = 0; + if ( o && !value.isEmpty() ) + ret = addPropValue( o, prop, value.latin1() ); + return ret; +} + +VObject* OContactAccessBackend_VCard::safeAddProp( VObject *o, const char *prop) +{ + VObject *ret = 0; + if ( o ) + ret = addProp( o, prop ); + return ret; +} diff --git a/noncore/unsupported/libopie/pim/ocontactaccessbackend_vcard.h b/noncore/unsupported/libopie/pim/ocontactaccessbackend_vcard.h new file mode 100644 index 0000000..6dbc718 --- a/dev/null +++ b/noncore/unsupported/libopie/pim/ocontactaccessbackend_vcard.h @@ -0,0 +1,99 @@ +/* + * VCard Backend for the OPIE-Contact Database. + * + * Copyright (C) 2000 Trolltech AS. All rights reserved. + * 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: + * + * ===================================================================== + * Version: $Id$ + * ===================================================================== + * History: + * $Log$ + * Revision 1.1 2004/11/16 21:46:07 mickeyl + * libopie1 goes into unsupported + * + * Revision 1.6 2003/04/13 18:07:10 zecke + * More API doc + * QString -> const QString& + * QString = 0l -> QString::null + * + * Revision 1.5 2003/03/21 10:33:09 eilers + * Merged speed optimized xml backend for contacts to main. + * Added QDateTime to querybyexample. For instance, it is now possible to get + * all Birthdays/Anniversaries between two dates. This should be used + * to show all birthdays in the datebook.. + * This change is sourcecode backward compatible but you have to upgrade + * the binaries for today-addressbook. + * + * Revision 1.4 2002/12/07 13:26:22 eilers + * Fixing bug in storing anniversary.. + * + * Revision 1.3 2002/11/13 14:14:51 eilers + * Added sorted for Contacts.. + * + * Revision 1.2 2002/11/10 15:41:53 eilers + * Bugfixes.. + * + * Revision 1.1 2002/11/09 14:34:52 eilers + * Added VCard Backend. + * + */ +#ifndef __OCONTACTACCESSBACKEND_VCARD_H_ +#define __OCONTACTACCESSBACKEND_VCARD_H_ + +#include + +#include "ocontactaccessbackend.h" + +class VObject; + +/** + * This is the vCard 2.1 implementation of the Contact Storage + * @see OContactAccessBackend_XML + * @see OPimAccessBackend + */ +class OContactAccessBackend_VCard : public OContactAccessBackend { + public: + OContactAccessBackend_VCard ( const QString& appname, const QString& filename = QString::null ); + + bool load (); + bool reload(); + bool save(); + void clear (); + + bool add ( const OContact& newcontact ); + bool remove ( int uid ); + bool replace ( const OContact& contact ); + + OContact find ( int uid ) const; + QArray allRecords() const; + QArray queryByExample ( const OContact &query, int settings, const QDateTime& d = QDateTime() ); + QArray matchRegexp( const QRegExp &r ) const; + + const uint querySettings(); + bool hasQuerySettings (uint querySettings) const; + QArray sorted( bool ascending, int sortOrder, int sortFilter, int cat ); + bool wasChangedExternally(); + +private: + OContact parseVObject( VObject* obj ); + VObject* createVObject( const OContact& c ); + QString convDateToVCardDate( const QDate& c ) const; + QDate convVCardDateToDate( const QString& datestr ); + VObject *safeAddPropValue( VObject *o, const char* prop, const QString& value ); + VObject *safeAddProp( VObject* o, const char* prop); + + bool m_dirty : 1; + QString m_file; + QMap m_map; +}; + +#endif diff --git a/noncore/unsupported/libopie/pim/ocontactaccessbackend_xml.cpp b/noncore/unsupported/libopie/pim/ocontactaccessbackend_xml.cpp new file mode 100644 index 0000000..7ceaf5b --- a/dev/null +++ b/noncore/unsupported/libopie/pim/ocontactaccessbackend_xml.cpp @@ -0,0 +1,824 @@ +/* + * XML 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.1 2004/11/16 21:46:07 mickeyl + * libopie1 goes into unsupported + * + * Revision 1.10 2004/03/01 15:44:36 chicken + * fix includes + * + * Revision 1.9 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.8 2003/08/30 15:28:26 eilers + * Removed some unimportant debug output which causes slow down.. + * + * Revision 1.7 2003/08/01 12:30:16 eilers + * Merging changes from BRANCH_1_0 to HEAD + * + * Revision 1.6 2003/07/07 16:19:47 eilers + * Fixing serious bug in hasQuerySettings() + * + * Revision 1.5 2003/04/13 18:07:10 zecke + * More API doc + * QString -> const QString& + * QString = 0l -> QString::null + * + * Revision 1.4 2003/03/21 14:32:54 mickeyl + * g++ compliance fix: default arguments belong into the declaration, but not the definition + * + * Revision 1.3 2003/03/21 12:26:28 eilers + * Fixing small bug: If we search a birthday from today to today, it returned + * every contact .. + * + * Revision 1.2 2003/03/21 10:33:09 eilers + * Merged speed optimized xml backend for contacts to main. + * Added QDateTime to querybyexample. For instance, it is now possible to get + * all Birthdays/Anniversaries between two dates. This should be used + * to show all birthdays in the datebook.. + * This change is sourcecode backward compatible but you have to upgrade + * the binaries for today-addressbook. + * + * Revision 1.1.2.2 2003/02/11 12:17:28 eilers + * Speed optimization. Removed the sequential search loops. + * + * Revision 1.1.2.1 2003/02/10 15:31:38 eilers + * Writing offsets to debug output.. + * + * Revision 1.1 2003/02/09 15:05:01 eilers + * Nothing happened.. Just some cleanup before I will start.. + * + * Revision 1.12 2003/01/03 16:58:03 eilers + * Reenable debug output + * + * Revision 1.11 2003/01/03 12:31:28 eilers + * Bugfix for calculating data diffs.. + * + * Revision 1.10 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.9 2002/12/08 12:48:57 eilers + * Moved journal-enum from ocontact into i the xml-backend.. + * + * Revision 1.8 2002/11/14 17:04:24 eilers + * Sorting will now work if fullname is identical on some entries + * + * Revision 1.7 2002/11/13 15:02:46 eilers + * Small Bug in sorted fixed + * + * Revision 1.6 2002/11/13 14:14:51 eilers + * Added sorted for Contacts.. + * + * Revision 1.5 2002/11/01 15:10:42 eilers + * Added regExp-search in database for all fields in a contact. + * + * Revision 1.4 2002/10/16 10:52:40 eilers + * Added some docu to the interface and now using the cache infrastucture by zecke.. :) + * + * Revision 1.3 2002/10/14 16:21:54 eilers + * Some minor interface updates + * + * Revision 1.2 2002/10/07 17:34:24 eilers + * added OBackendFactory for advanced backend access + * + * 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 ! + * + * + */ + +#include "ocontactaccessbackend_xml.h" + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include "ocontactaccessbackend.h" +#include "ocontactaccess.h" + +#include +#include + +using namespace Opie; + + +OContactAccessBackend_XML::OContactAccessBackend_XML ( const QString& appname, const QString& filename ): + m_changed( false ) +{ + // Just m_contactlist should call delete if an entry + // is removed. + m_contactList.setAutoDelete( true ); + m_uidToContact.setAutoDelete( false ); + + m_appName = appname; + + /* Set journalfile name ... */ + m_journalName = getenv("HOME"); + m_journalName +="/.abjournal" + appname; + + /* Expecting to access the default filename if nothing else is set */ + if ( filename.isEmpty() ){ + m_fileName = Global::applicationFileName( "addressbook","addressbook.xml" ); + } else + m_fileName = filename; + + /* Load Database now */ + load (); +} + +bool OContactAccessBackend_XML::save() +{ + + if ( !m_changed ) + return true; + + QString strNewFile = m_fileName + ".new"; + QFile f( strNewFile ); + if ( !f.open( IO_WriteOnly|IO_Raw ) ) + return false; + + int total_written; + int idx_offset = 0; + QString out; + + // Write Header + out = "\n" + " \n" + " \n" + " \n"; + QCString cstr = out.utf8(); + f.writeBlock( cstr.data(), cstr.length() ); + idx_offset += cstr.length(); + out = ""; + + // Write all contacts + QListIterator it( m_contactList ); + for ( ; it.current(); ++it ) { + // qWarning(" Uid %d at Offset: %x", (*it)->uid(), idx_offset ); + out += "save( out ); + out += "/>\n"; + cstr = out.utf8(); + total_written = f.writeBlock( cstr.data(), cstr.length() ); + idx_offset += cstr.length(); + if ( total_written != int(cstr.length()) ) { + f.close(); + QFile::remove( strNewFile ); + return false; + } + out = ""; + } + out += " \n\n"; + + // Write Footer + cstr = out.utf8(); + total_written = f.writeBlock( cstr.data(), cstr.length() ); + if ( total_written != int( cstr.length() ) ) { + f.close(); + QFile::remove( strNewFile ); + return false; + } + f.close(); + + // move the file over, I'm just going to use the system call + // because, I don't feel like using QDir. + if ( ::rename( strNewFile.latin1(), m_fileName.latin1() ) < 0 ) { + qWarning( "problem renaming file %s to %s, errno: %d", + strNewFile.latin1(), m_journalName.latin1(), errno ); + // remove the tmp file... + QFile::remove( strNewFile ); + } + + /* The journalfile should be removed now... */ + removeJournal(); + + m_changed = false; + return true; +} + +bool OContactAccessBackend_XML::load () +{ + m_contactList.clear(); + m_uidToContact.clear(); + + /* Load XML-File and journal if it exists */ + if ( !load ( m_fileName, false ) ) + return false; + /* The returncode of the journalfile is ignored due to the + * fact that it does not exist when this class is instantiated ! + * But there may such a file exist, if the application crashed. + * Therefore we try to load it to get the changes before the # + * crash happened... + */ + load (m_journalName, true); + + return true; +} + +void OContactAccessBackend_XML::clear () +{ + m_contactList.clear(); + m_uidToContact.clear(); + + m_changed = false; +} + +bool OContactAccessBackend_XML::wasChangedExternally() +{ + QFileInfo fi( m_fileName ); + + QDateTime lastmod = fi.lastModified (); + + return (lastmod != m_readtime); +} + +QArray OContactAccessBackend_XML::allRecords() const +{ + QArray uid_list( m_contactList.count() ); + + uint counter = 0; + QListIterator it( m_contactList ); + for( ; it.current(); ++it ){ + uid_list[counter++] = (*it)->uid(); + } + + return ( uid_list ); +} + +OContact OContactAccessBackend_XML::find ( int uid ) const +{ + OContact foundContact; //Create empty contact + + OContact* found = m_uidToContact.find( QString().setNum( uid ) ); + + if ( found ){ + foundContact = *found; + } + + return ( foundContact ); +} + +QArray OContactAccessBackend_XML::queryByExample ( const OContact &query, int settings, + const QDateTime& d ) +{ + + QArray m_currentQuery( m_contactList.count() ); + QListIterator it( m_contactList ); + uint arraycounter = 0; + + for( ; it.current(); ++it ){ + /* Search all fields and compare them with query object. Store them into list + * if all fields matches. + */ + QDate* queryDate = 0l; + QDate* checkDate = 0l; + bool allcorrect = true; + for ( int i = 0; i < Qtopia::Groups; i++ ) { + // Birthday and anniversary are special nonstring fields and should + // be handled specially + switch ( i ){ + case Qtopia::Birthday: + queryDate = new QDate( query.birthday() ); + checkDate = new QDate( (*it)->birthday() ); + case Qtopia::Anniversary: + if ( queryDate == 0l ){ + queryDate = new QDate( query.anniversary() ); + checkDate = new QDate( (*it)->anniversary() ); + } + + if ( queryDate->isValid() ){ + if( checkDate->isValid() ){ + if ( settings & OContactAccess::DateYear ){ + if ( queryDate->year() != checkDate->year() ) + allcorrect = false; + } + if ( settings & OContactAccess::DateMonth ){ + if ( queryDate->month() != checkDate->month() ) + allcorrect = false; + } + if ( settings & OContactAccess::DateDay ){ + if ( queryDate->day() != checkDate->day() ) + allcorrect = false; + } + if ( settings & OContactAccess::DateDiff ) { + QDate current; + // If we get an additional date, we + // will take this date instead of + // the current one.. + if ( !d.date().isValid() ) + current = QDate::currentDate(); + else + current = d.date(); + + // We have to equalize the year, otherwise + // the search will fail.. + checkDate->setYMD( current.year(), + checkDate->month(), + checkDate->day() ); + if ( *checkDate < current ) + checkDate->setYMD( current.year()+1, + checkDate->month(), + checkDate->day() ); + + // Check whether the birthday/anniversary date is between + // the current/given date and the maximum date + // ( maximum time range ) ! + qWarning("Checking if %s is between %s and %s ! ", + checkDate->toString().latin1(), + current.toString().latin1(), + queryDate->toString().latin1() ); + if ( current.daysTo( *queryDate ) >= 0 ){ + if ( !( ( *checkDate >= current ) && + ( *checkDate <= *queryDate ) ) ){ + allcorrect = false; + qWarning (" Nope!.."); + } + } + } + } else{ + // checkDate is invalid. Therefore this entry is always rejected + allcorrect = false; + } + } + + delete queryDate; + queryDate = 0l; + delete checkDate; + checkDate = 0l; + break; + default: + /* Just compare fields which are not empty in the query object */ + if ( !query.field(i).isEmpty() ){ + switch ( settings & ~( OContactAccess::IgnoreCase + | OContactAccess::DateDiff + | OContactAccess::DateYear + | OContactAccess::DateMonth + | OContactAccess::DateDay + | OContactAccess::MatchOne + ) ){ + + case OContactAccess::RegExp:{ + QRegExp expr ( query.field(i), + !(settings & OContactAccess::IgnoreCase), + false ); + if ( expr.find ( (*it)->field(i), 0 ) == -1 ) + allcorrect = false; + } + break; + case OContactAccess::WildCards:{ + QRegExp expr ( query.field(i), + !(settings & OContactAccess::IgnoreCase), + true ); + if ( expr.find ( (*it)->field(i), 0 ) == -1 ) + allcorrect = false; + } + break; + case OContactAccess::ExactMatch:{ + if (settings & OContactAccess::IgnoreCase){ + if ( query.field(i).upper() != + (*it)->field(i).upper() ) + allcorrect = false; + }else{ + if ( query.field(i) != (*it)->field(i) ) + allcorrect = false; + } + } + break; + } + } + } + } + if ( allcorrect ){ + m_currentQuery[arraycounter++] = (*it)->uid(); + } + } + + // Shrink to fit.. + m_currentQuery.resize(arraycounter); + + return m_currentQuery; +} + +QArray OContactAccessBackend_XML::matchRegexp( const QRegExp &r ) const +{ + QArray m_currentQuery( m_contactList.count() ); + QListIterator it( m_contactList ); + uint arraycounter = 0; + + for( ; it.current(); ++it ){ + if ( (*it)->match( r ) ){ + m_currentQuery[arraycounter++] = (*it)->uid(); + } + + } + // Shrink to fit.. + m_currentQuery.resize(arraycounter); + + return m_currentQuery; +} + +const uint OContactAccessBackend_XML::querySettings() +{ + return ( OContactAccess::WildCards + | OContactAccess::IgnoreCase + | OContactAccess::RegExp + | OContactAccess::ExactMatch + | OContactAccess::DateDiff + | OContactAccess::DateYear + | OContactAccess::DateMonth + | OContactAccess::DateDay + ); +} + +bool OContactAccessBackend_XML::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 ); + } +} + +// Currently only asc implemented.. +QArray OContactAccessBackend_XML::sorted( bool asc, int , int , int ) +{ + QMap nameToUid; + QStringList names; + QArray m_currentQuery( m_contactList.count() ); + + // First fill map and StringList with all Names + // Afterwards sort namelist and use map to fill array to return.. + QListIterator it( m_contactList ); + for( ; it.current(); ++it ){ + names.append( (*it)->fileAs() + QString::number( (*it)->uid() ) ); + nameToUid.insert( (*it)->fileAs() + QString::number( (*it)->uid() ), (*it)->uid() ); + } + names.sort(); + + int i = 0; + if ( asc ){ + for ( QStringList::Iterator it = names.begin(); it != names.end(); ++it ) + m_currentQuery[i++] = nameToUid[ (*it) ]; + }else{ + for ( QStringList::Iterator it = names.end(); it != names.begin(); --it ) + m_currentQuery[i++] = nameToUid[ (*it) ]; + } + + return m_currentQuery; + +} + +bool OContactAccessBackend_XML::add ( const OContact &newcontact ) +{ + //qWarning("odefaultbackend: ACTION::ADD"); + updateJournal (newcontact, ACTION_ADD); + addContact_p( newcontact ); + + m_changed = true; + + return true; +} + +bool OContactAccessBackend_XML::replace ( const OContact &contact ) +{ + m_changed = true; + + OContact* found = m_uidToContact.find ( QString().setNum( contact.uid() ) ); + + if ( found ) { + OContact* newCont = new OContact( contact ); + + updateJournal ( *newCont, ACTION_REPLACE); + m_contactList.removeRef ( found ); + m_contactList.append ( newCont ); + m_uidToContact.remove( QString().setNum( contact.uid() ) ); + m_uidToContact.insert( QString().setNum( newCont->uid() ), newCont ); + + qWarning("Nur zur Sicherheit: %d == %d ?",contact.uid(), newCont->uid()); + + return true; + } else + return false; +} + +bool OContactAccessBackend_XML::remove ( int uid ) +{ + m_changed = true; + + OContact* found = m_uidToContact.find ( QString().setNum( uid ) ); + + if ( found ) { + updateJournal ( *found, ACTION_REMOVE); + m_contactList.removeRef ( found ); + m_uidToContact.remove( QString().setNum( uid ) ); + + return true; + } else + return false; +} + +bool OContactAccessBackend_XML::reload(){ + /* Reload is the same as load in this implementation */ + return ( load() ); +} + +void OContactAccessBackend_XML::addContact_p( const OContact &newcontact ) +{ + OContact* contRef = new OContact( newcontact ); + + m_contactList.append ( contRef ); + m_uidToContact.insert( QString().setNum( newcontact.uid() ), contRef ); +} + +/* This function loads the xml-database and the journalfile */ +bool OContactAccessBackend_XML::load( const QString filename, bool isJournal ) +{ + + /* We use the time of the last read to check if the file was + * changed externally. + */ + if ( !isJournal ){ + QFileInfo fi( filename ); + m_readtime = fi.lastModified (); + } + + const int JOURNALACTION = Qtopia::Notes + 1; + const int JOURNALROW = JOURNALACTION + 1; + + bool foundAction = false; + journal_action action = ACTION_ADD; + int journalKey = 0; + QMap contactMap; + QMap customMap; + QMap::Iterator customIt; + QAsciiDict dict( 47 ); + + dict.setAutoDelete( TRUE ); + dict.insert( "Uid", new int(Qtopia::AddressUid) ); + dict.insert( "Title", new int(Qtopia::Title) ); + dict.insert( "FirstName", new int(Qtopia::FirstName) ); + dict.insert( "MiddleName", new int(Qtopia::MiddleName) ); + dict.insert( "LastName", new int(Qtopia::LastName) ); + dict.insert( "Suffix", new int(Qtopia::Suffix) ); + dict.insert( "FileAs", new int(Qtopia::FileAs) ); + dict.insert( "Categories", new int(Qtopia::AddressCategory) ); + dict.insert( "DefaultEmail", new int(Qtopia::DefaultEmail) ); + dict.insert( "Emails", new int(Qtopia::Emails) ); + dict.insert( "HomeStreet", new int(Qtopia::HomeStreet) ); + dict.insert( "HomeCity", new int(Qtopia::HomeCity) ); + dict.insert( "HomeState", new int(Qtopia::HomeState) ); + dict.insert( "HomeZip", new int(Qtopia::HomeZip) ); + dict.insert( "HomeCountry", new int(Qtopia::HomeCountry) ); + dict.insert( "HomePhone", new int(Qtopia::HomePhone) ); + dict.insert( "HomeFax", new int(Qtopia::HomeFax) ); + dict.insert( "HomeMobile", new int(Qtopia::HomeMobile) ); + dict.insert( "HomeWebPage", new int(Qtopia::HomeWebPage) ); + dict.insert( "Company", new int(Qtopia::Company) ); + dict.insert( "BusinessStreet", new int(Qtopia::BusinessStreet) ); + dict.insert( "BusinessCity", new int(Qtopia::BusinessCity) ); + dict.insert( "BusinessState", new int(Qtopia::BusinessState) ); + dict.insert( "BusinessZip", new int(Qtopia::BusinessZip) ); + dict.insert( "BusinessCountry", new int(Qtopia::BusinessCountry) ); + dict.insert( "BusinessWebPage", new int(Qtopia::BusinessWebPage) ); + dict.insert( "JobTitle", new int(Qtopia::JobTitle) ); + dict.insert( "Department", new int(Qtopia::Department) ); + dict.insert( "Office", new int(Qtopia::Office) ); + dict.insert( "BusinessPhone", new int(Qtopia::BusinessPhone) ); + dict.insert( "BusinessFax", new int(Qtopia::BusinessFax) ); + dict.insert( "BusinessMobile", new int(Qtopia::BusinessMobile) ); + dict.insert( "BusinessPager", new int(Qtopia::BusinessPager) ); + dict.insert( "Profession", new int(Qtopia::Profession) ); + dict.insert( "Assistant", new int(Qtopia::Assistant) ); + dict.insert( "Manager", new int(Qtopia::Manager) ); + dict.insert( "Spouse", new int(Qtopia::Spouse) ); + dict.insert( "Children", new int(Qtopia::Children) ); + dict.insert( "Gender", new int(Qtopia::Gender) ); + dict.insert( "Birthday", new int(Qtopia::Birthday) ); + dict.insert( "Anniversary", new int(Qtopia::Anniversary) ); + dict.insert( "Nickname", new int(Qtopia::Nickname) ); + dict.insert( "Notes", new int(Qtopia::Notes) ); + dict.insert( "action", new int(JOURNALACTION) ); + dict.insert( "actionrow", new int(JOURNALROW) ); + + //qWarning( "OContactDefaultBackEnd::loading %s", filename.latin1() ); + + XMLElement *root = XMLElement::load( filename ); + if(root != 0l ){ // start parsing + /* Parse all XML-Elements and put the data into the + * Contact-Class + */ + XMLElement *element = root->firstChild(); + //qWarning("OContactAccess::load tagName(): %s", root->tagName().latin1() ); + element = element->firstChild(); + + /* Search Tag "Contacts" which is the parent of all Contacts */ + while( element && !isJournal ){ + if( element->tagName() != QString::fromLatin1("Contacts") ){ + //qWarning ("OContactDefBack::Searching for Tag \"Contacts\"! Found: %s", + // element->tagName().latin1()); + element = element->nextChild(); + } else { + element = element->firstChild(); + break; + } + } + /* Parse all Contacts and ignore unknown tags */ + while( element ){ + if( element->tagName() != QString::fromLatin1("Contact") ){ + //qWarning ("OContactDefBack::Searching for Tag \"Contact\"! Found: %s", + // element->tagName().latin1()); + element = element->nextChild(); + continue; + } + /* Found alement with tagname "contact", now parse and store all + * attributes contained + */ + //qWarning("OContactDefBack::load element tagName() : %s", + // element->tagName().latin1() ); + QString dummy; + foundAction = false; + + XMLElement::AttributeMap aMap = element->attributes(); + XMLElement::AttributeMap::Iterator it; + contactMap.clear(); + customMap.clear(); + for( it = aMap.begin(); it != aMap.end(); ++it ){ + // qWarning ("Read Attribute: %s=%s", it.key().latin1(),it.data().latin1()); + + int *find = dict[ it.key() ]; + /* Unknown attributes will be stored as "Custom" elements */ + if ( !find ) { + // qWarning("Attribute %s not known.", it.key().latin1()); + //contact.setCustomField(it.key(), it.data()); + customMap.insert( it.key(), it.data() ); + continue; + } + + /* Check if special conversion is needed and add attribute + * into Contact class + */ + switch( *find ) { + /* + case Qtopia::AddressUid: + contact.setUid( it.data().toInt() ); + break; + case Qtopia::AddressCategory: + contact.setCategories( Qtopia::Record::idsFromString( it.data( ))); + break; + */ + case JOURNALACTION: + action = journal_action(it.data().toInt()); + foundAction = true; + qWarning ("ODefBack(journal)::ACTION found: %d", action); + break; + case JOURNALROW: + journalKey = it.data().toInt(); + break; + default: // no conversion needed add them to the map + contactMap.insert( *find, it.data() ); + break; + } + } + /* now generate the Contact contact */ + OContact contact( contactMap ); + + for (customIt = customMap.begin(); customIt != customMap.end(); ++customIt ) { + contact.setCustomField( customIt.key(), customIt.data() ); + } + + if (foundAction){ + foundAction = false; + switch ( action ) { + case ACTION_ADD: + addContact_p (contact); + break; + case ACTION_REMOVE: + if ( !remove (contact.uid()) ) + qWarning ("ODefBack(journal)::Unable to remove uid: %d", + contact.uid() ); + break; + case ACTION_REPLACE: + if ( !replace ( contact ) ) + qWarning ("ODefBack(journal)::Unable to replace uid: %d", + contact.uid() ); + break; + default: + qWarning ("Unknown action: ignored !"); + break; + } + }else{ + /* Add contact to list */ + addContact_p (contact); + } + + /* Move to next element */ + element = element->nextChild(); + } + }else { + qWarning("ODefBack::could not load"); + } + delete root; + qWarning("returning from loading" ); + return true; +} + + +void OContactAccessBackend_XML::updateJournal( const OContact& cnt, + journal_action action ) +{ + QFile f( m_journalName ); + bool created = !f.exists(); + if ( !f.open(IO_WriteOnly|IO_Append) ) + return; + + QString buf; + QCString str; + + // if the file was created, we have to set the Tag "" to + // get a XML-File which is readable by our parser. + // This is just a cheat, but better than rewrite the parser. + if ( created ){ + buf = ""; + QCString cstr = buf.utf8(); + f.writeBlock( cstr.data(), cstr.length() ); + } + + buf = " const QString& + * QString = 0l -> QString::null + * + * Revision 1.13 2003/03/21 10:33:09 eilers + * Merged speed optimized xml backend for contacts to main. + * Added QDateTime to querybyexample. For instance, it is now possible to get + * all Birthdays/Anniversaries between two dates. This should be used + * to show all birthdays in the datebook.. + * This change is sourcecode backward compatible but you have to upgrade + * the binaries for today-addressbook. + * + * Revision 1.12.2.2 2003/02/11 12:17:28 eilers + * Speed optimization. Removed the sequential search loops. + * + * Revision 1.12.2.1 2003/02/09 15:05:01 eilers + * Nothing happened.. Just some cleanup before I will start.. + * + * Revision 1.12 2003/01/03 16:58:03 eilers + * Reenable debug output + * + * Revision 1.11 2003/01/03 12:31:28 eilers + * Bugfix for calculating data diffs.. + * + * Revision 1.10 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.9 2002/12/08 12:48:57 eilers + * Moved journal-enum from ocontact into i the xml-backend.. + * + * Revision 1.8 2002/11/14 17:04:24 eilers + * Sorting will now work if fullname is identical on some entries + * + * Revision 1.7 2002/11/13 15:02:46 eilers + * Small Bug in sorted fixed + * + * Revision 1.6 2002/11/13 14:14:51 eilers + * Added sorted for Contacts.. + * + * Revision 1.5 2002/11/01 15:10:42 eilers + * Added regExp-search in database for all fields in a contact. + * + * Revision 1.4 2002/10/16 10:52:40 eilers + * Added some docu to the interface and now using the cache infrastucture by zecke.. :) + * + * Revision 1.3 2002/10/14 16:21:54 eilers + * Some minor interface updates + * + * Revision 1.2 2002/10/07 17:34:24 eilers + * added OBackendFactory for advanced backend access + * + * 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 _OContactAccessBackend_XML_ +#define _OContactAccessBackend_XML_ + +#include "ocontactaccessbackend.h" +#include "ocontactaccess.h" + +#include +#include + +/* the default xml implementation */ +/** + * This class is the XML implementation of a Contact backend + * it does implement everything available for OContact. + * @see OPimAccessBackend for more information of available methods + */ +class OContactAccessBackend_XML : public OContactAccessBackend { + public: + OContactAccessBackend_XML ( const QString& appname, const QString& filename = QString::null ); + + bool save(); + + bool load (); + + void clear (); + + bool wasChangedExternally(); + + QArray allRecords() const; + + OContact find ( int uid ) const; + + QArray queryByExample ( const OContact &query, int settings, const QDateTime& d = QDateTime() ); + + QArray matchRegexp( const QRegExp &r ) const; + + const uint querySettings(); + + bool hasQuerySettings (uint querySettings) const; + + // Currently only asc implemented.. + QArray sorted( bool asc, int , int , int ); + bool add ( const OContact &newcontact ); + + bool replace ( const OContact &contact ); + + bool remove ( int uid ); + bool reload(); + + private: + + enum journal_action { ACTION_ADD, ACTION_REMOVE, ACTION_REPLACE }; + + void addContact_p( const OContact &newcontact ); + + /* This function loads the xml-database and the journalfile */ + bool load( const QString filename, bool isJournal ); + + + void updateJournal( const OContact& cnt, journal_action action ); + void removeJournal(); + + protected: + bool m_changed; + QString m_journalName; + QString m_fileName; + QString m_appName; + QList m_contactList; + QDateTime m_readtime; + + QDict m_uidToContact; +}; + +#endif diff --git a/noncore/unsupported/libopie/pim/ocontactfields.cpp b/noncore/unsupported/libopie/pim/ocontactfields.cpp new file mode 100644 index 0000000..0f08a5a --- a/dev/null +++ b/noncore/unsupported/libopie/pim/ocontactfields.cpp @@ -0,0 +1,477 @@ + +#include "ocontactfields.h" + +#include +#include + +// We should use our own enum in the future .. +#include +#include +#include + +/*! + \internal + Returns a list of personal field names for a contact. +*/ +QStringList OContactFields::personalfields( bool sorted, bool translated ) +{ + QStringList list; + QMap mapIdToStr; + if ( translated ) + mapIdToStr = idToTrFields(); + else + mapIdToStr = idToUntrFields(); + + list.append( mapIdToStr[ Qtopia::AddressUid ] ); + list.append( mapIdToStr[ Qtopia::AddressCategory ] ); + + list.append( mapIdToStr[ Qtopia::Title ] ); + list.append( mapIdToStr[ Qtopia::FirstName ] ); + list.append( mapIdToStr[ Qtopia::MiddleName ] ); + list.append( mapIdToStr[ Qtopia::LastName ] ); + list.append( mapIdToStr[ Qtopia::Suffix ] ); + list.append( mapIdToStr[ Qtopia::FileAs ] ); + + list.append( mapIdToStr[ Qtopia::JobTitle ] ); + list.append( mapIdToStr[ Qtopia::Department ] ); + list.append( mapIdToStr[ Qtopia::Company ] ); + + list.append( mapIdToStr[ Qtopia::Notes ] ); + list.append( mapIdToStr[ Qtopia::Groups ] ); + + if (sorted) list.sort(); + return list; +} + +/*! + \internal + Returns a list of details field names for a contact. +*/ +QStringList OContactFields::detailsfields( bool sorted, bool translated ) +{ + QStringList list; + QMap mapIdToStr; + if ( translated ) + mapIdToStr = idToTrFields(); + else + mapIdToStr = idToUntrFields(); + + list.append( mapIdToStr[ Qtopia::Office ] ); + list.append( mapIdToStr[ Qtopia::Profession ] ); + list.append( mapIdToStr[ Qtopia::Assistant ] ); + list.append( mapIdToStr[ Qtopia::Manager ] ); + + list.append( mapIdToStr[ Qtopia::Spouse ] ); + list.append( mapIdToStr[ Qtopia::Gender ] ); + list.append( mapIdToStr[ Qtopia::Birthday ] ); + list.append( mapIdToStr[ Qtopia::Anniversary ] ); + list.append( mapIdToStr[ Qtopia::Nickname ] ); + list.append( mapIdToStr[ Qtopia::Children ] ); + + if (sorted) list.sort(); + return list; +} + +/*! + \internal + Returns a list of phone field names for a contact. +*/ +QStringList OContactFields::phonefields( bool sorted, bool translated ) +{ + QStringList list; + QMap mapIdToStr; + if ( translated ) + mapIdToStr = idToTrFields(); + else + mapIdToStr = idToUntrFields(); + + list.append( mapIdToStr[Qtopia::BusinessPhone] ); + list.append( mapIdToStr[Qtopia::BusinessFax] ); + list.append( mapIdToStr[Qtopia::BusinessMobile] ); + list.append( mapIdToStr[Qtopia::BusinessPager] ); + list.append( mapIdToStr[Qtopia::BusinessWebPage] ); + + list.append( mapIdToStr[Qtopia::DefaultEmail] ); + list.append( mapIdToStr[Qtopia::Emails] ); + + list.append( mapIdToStr[Qtopia::HomePhone] ); + list.append( mapIdToStr[Qtopia::HomeFax] ); + list.append( mapIdToStr[Qtopia::HomeMobile] ); + // list.append( mapIdToStr[Qtopia::HomePager] ); + list.append( mapIdToStr[Qtopia::HomeWebPage] ); + + if (sorted) list.sort(); + + return list; +} + +/*! + \internal + Returns a list of field names for a contact. +*/ +QStringList OContactFields::fields( bool sorted, bool translated ) +{ + QStringList list; + QMap mapIdToStr; + if ( translated ) + mapIdToStr = idToTrFields(); + else + mapIdToStr = idToUntrFields(); + + list += personalfields( sorted, translated ); + + list += phonefields( sorted, translated ); + + list.append( mapIdToStr[Qtopia::BusinessStreet] ); + list.append( mapIdToStr[Qtopia::BusinessCity] ); + list.append( mapIdToStr[Qtopia::BusinessState] ); + list.append( mapIdToStr[Qtopia::BusinessZip] ); + list.append( mapIdToStr[Qtopia::BusinessCountry] ); + + list.append( mapIdToStr[Qtopia::HomeStreet] ); + list.append( mapIdToStr[Qtopia::HomeCity] ); + list.append( mapIdToStr[Qtopia::HomeState] ); + list.append( mapIdToStr[Qtopia::HomeZip] ); + list.append( mapIdToStr[Qtopia::HomeCountry] ); + + list += detailsfields( sorted, translated ); + + if (sorted) list.sort(); + + return list; +} + + +/*! + \internal + Returns an untranslated list of personal field names for a contact. +*/ +QStringList OContactFields::untrpersonalfields( bool sorted ) +{ + return personalfields( sorted, false ); +} + + +/*! + \internal + Returns a translated list of personal field names for a contact. +*/ +QStringList OContactFields::trpersonalfields( bool sorted ) +{ + return personalfields( sorted, true ); +} + + +/*! + \internal + Returns an untranslated list of details field names for a contact. +*/ +QStringList OContactFields::untrdetailsfields( bool sorted ) +{ + return detailsfields( sorted, false ); +} + + +/*! + \internal + Returns a translated list of details field names for a contact. +*/ +QStringList OContactFields::trdetailsfields( bool sorted ) +{ + return detailsfields( sorted, true ); +} + + +/*! + \internal + Returns a translated list of phone field names for a contact. +*/ +QStringList OContactFields::trphonefields( bool sorted ) +{ + return phonefields( sorted, true ); +} + +/*! + \internal + Returns an untranslated list of phone field names for a contact. +*/ +QStringList OContactFields::untrphonefields( bool sorted ) +{ + return phonefields( sorted, false ); +} + + +/*! + \internal + Returns a translated list of field names for a contact. +*/ +QStringList OContactFields::trfields( bool sorted ) +{ + return fields( sorted, true ); +} + +/*! + \internal + Returns an untranslated list of field names for a contact. +*/ +QStringList OContactFields::untrfields( bool sorted ) +{ + return fields( sorted, false ); +} + +QMap OContactFields::idToTrFields() +{ + QMap ret_map; + + ret_map.insert( Qtopia::AddressUid, QObject::tr( "User Id" ) ); + ret_map.insert( Qtopia::AddressCategory, QObject::tr( "Categories" ) ); + + ret_map.insert( Qtopia::Title, QObject::tr( "Name Title") ); + ret_map.insert( Qtopia::FirstName, QObject::tr( "First Name" ) ); + ret_map.insert( Qtopia::MiddleName, QObject::tr( "Middle Name" ) ); + ret_map.insert( Qtopia::LastName, QObject::tr( "Last Name" ) ); + ret_map.insert( Qtopia::Suffix, QObject::tr( "Suffix" )); + ret_map.insert( Qtopia::FileAs, QObject::tr( "File As" ) ); + + ret_map.insert( Qtopia::JobTitle, QObject::tr( "Job Title" ) ); + ret_map.insert( Qtopia::Department, QObject::tr( "Department" ) ); + ret_map.insert( Qtopia::Company, QObject::tr( "Company" ) ); + ret_map.insert( Qtopia::BusinessPhone, QObject::tr( "Business Phone" ) ); + ret_map.insert( Qtopia::BusinessFax, QObject::tr( "Business Fax" ) ); + ret_map.insert( Qtopia::BusinessMobile, QObject::tr( "Business Mobile" )); + + // email + ret_map.insert( Qtopia::DefaultEmail, QObject::tr( "Default Email" ) ); + ret_map.insert( Qtopia::Emails, QObject::tr( "Emails" ) ); + + ret_map.insert( Qtopia::HomePhone, QObject::tr( "Home Phone" ) ); + ret_map.insert( Qtopia::HomeFax, QObject::tr( "Home Fax" ) ); + ret_map.insert( Qtopia::HomeMobile, QObject::tr( "Home Mobile" ) ); + + // business + ret_map.insert( Qtopia::BusinessStreet, QObject::tr( "Business Street" ) ); + ret_map.insert( Qtopia::BusinessCity, QObject::tr( "Business City" ) ); + ret_map.insert( Qtopia::BusinessState, QObject::tr( "Business State" ) ); + ret_map.insert( Qtopia::BusinessZip, QObject::tr( "Business Zip" ) ); + ret_map.insert( Qtopia::BusinessCountry, QObject::tr( "Business Country" ) ); + ret_map.insert( Qtopia::BusinessPager, QObject::tr( "Business Pager" ) ); + ret_map.insert( Qtopia::BusinessWebPage, QObject::tr( "Business WebPage" ) ); + + ret_map.insert( Qtopia::Office, QObject::tr( "Office" ) ); + ret_map.insert( Qtopia::Profession, QObject::tr( "Profession" ) ); + ret_map.insert( Qtopia::Assistant, QObject::tr( "Assistant" ) ); + ret_map.insert( Qtopia::Manager, QObject::tr( "Manager" ) ); + + // home + ret_map.insert( Qtopia::HomeStreet, QObject::tr( "Home Street" ) ); + ret_map.insert( Qtopia::HomeCity, QObject::tr( "Home City" ) ); + ret_map.insert( Qtopia::HomeState, QObject::tr( "Home State" ) ); + ret_map.insert( Qtopia::HomeZip, QObject::tr( "Home Zip" ) ); + ret_map.insert( Qtopia::HomeCountry, QObject::tr( "Home Country" ) ); + ret_map.insert( Qtopia::HomeWebPage, QObject::tr( "Home Web Page" ) ); + + //personal + ret_map.insert( Qtopia::Spouse, QObject::tr( "Spouse" ) ); + ret_map.insert( Qtopia::Gender, QObject::tr( "Gender" ) ); + ret_map.insert( Qtopia::Birthday, QObject::tr( "Birthday" ) ); + ret_map.insert( Qtopia::Anniversary, QObject::tr( "Anniversary" ) ); + ret_map.insert( Qtopia::Nickname, QObject::tr( "Nickname" ) ); + ret_map.insert( Qtopia::Children, QObject::tr( "Children" ) ); + + // other + ret_map.insert( Qtopia::Notes, QObject::tr( "Notes" ) ); + + + return ret_map; +} + +QMap OContactFields::idToUntrFields() +{ + QMap ret_map; + + ret_map.insert( Qtopia::AddressUid, "User Id" ); + ret_map.insert( Qtopia::AddressCategory, "Categories" ); + + ret_map.insert( Qtopia::Title, "Name Title" ); + ret_map.insert( Qtopia::FirstName, "First Name" ); + ret_map.insert( Qtopia::MiddleName, "Middle Name" ); + ret_map.insert( Qtopia::LastName, "Last Name" ); + ret_map.insert( Qtopia::Suffix, "Suffix" ); + ret_map.insert( Qtopia::FileAs, "File As" ); + + ret_map.insert( Qtopia::JobTitle, "Job Title" ); + ret_map.insert( Qtopia::Department, "Department" ); + ret_map.insert( Qtopia::Company, "Company" ); + ret_map.insert( Qtopia::BusinessPhone, "Business Phone" ); + ret_map.insert( Qtopia::BusinessFax, "Business Fax" ); + ret_map.insert( Qtopia::BusinessMobile, "Business Mobile" ); + + // email + ret_map.insert( Qtopia::DefaultEmail, "Default Email" ); + ret_map.insert( Qtopia::Emails, "Emails" ); + + ret_map.insert( Qtopia::HomePhone, "Home Phone" ); + ret_map.insert( Qtopia::HomeFax, "Home Fax" ); + ret_map.insert( Qtopia::HomeMobile, "Home Mobile" ); + + // business + ret_map.insert( Qtopia::BusinessStreet, "Business Street" ); + ret_map.insert( Qtopia::BusinessCity, "Business City" ); + ret_map.insert( Qtopia::BusinessState, "Business State" ); + ret_map.insert( Qtopia::BusinessZip, "Business Zip" ); + ret_map.insert( Qtopia::BusinessCountry, "Business Country" ); + ret_map.insert( Qtopia::BusinessPager, "Business Pager" ); + ret_map.insert( Qtopia::BusinessWebPage, "Business WebPage" ); + + ret_map.insert( Qtopia::Office, "Office" ); + ret_map.insert( Qtopia::Profession, "Profession" ); + ret_map.insert( Qtopia::Assistant, "Assistant" ); + ret_map.insert( Qtopia::Manager, "Manager" ); + + // home + ret_map.insert( Qtopia::HomeStreet, "Home Street" ); + ret_map.insert( Qtopia::HomeCity, "Home City" ); + ret_map.insert( Qtopia::HomeState, "Home State" ); + ret_map.insert( Qtopia::HomeZip, "Home Zip" ); + ret_map.insert( Qtopia::HomeCountry, "Home Country" ); + ret_map.insert( Qtopia::HomeWebPage, "Home Web Page" ); + + //personal + ret_map.insert( Qtopia::Spouse, "Spouse" ); + ret_map.insert( Qtopia::Gender, "Gender" ); + ret_map.insert( Qtopia::Birthday, "Birthday" ); + ret_map.insert( Qtopia::Anniversary, "Anniversary" ); + ret_map.insert( Qtopia::Nickname, "Nickname" ); + ret_map.insert( Qtopia::Children, "Children" ); + + // other + ret_map.insert( Qtopia::Notes, "Notes" ); + ret_map.insert( Qtopia::Groups, "Groups" ); + + + return ret_map; +} + +QMap OContactFields::trFieldsToId() +{ + QMap idtostr = idToTrFields(); + QMap ret_map; + + + QMap::Iterator it; + for( it = idtostr.begin(); it != idtostr.end(); ++it ) + ret_map.insert( *it, it.key() ); + + + return ret_map; +} + +/* ======================================================================= */ + +QMap OContactFields::untrFieldsToId() +{ + QMap idtostr = idToUntrFields(); + QMap ret_map; + + + QMap::Iterator it; + for( it = idtostr.begin(); it != idtostr.end(); ++it ) + ret_map.insert( *it, it.key() ); + + + return ret_map; +} + + +OContactFields::OContactFields(): + fieldOrder( DEFAULT_FIELD_ORDER ), + changedFieldOrder( false ) +{ + // Get the global field order from the config file and + // use it as a start pattern + Config cfg ( "AddressBook" ); + cfg.setGroup( "ContactFieldOrder" ); + globalFieldOrder = cfg.readEntry( "General", DEFAULT_FIELD_ORDER ); +} + +OContactFields::~OContactFields(){ + + // We will store the fieldorder into the config file + // to reuse it for the future.. + if ( changedFieldOrder ){ + Config cfg ( "AddressBook" ); + cfg.setGroup( "ContactFieldOrder" ); + cfg.writeEntry( "General", globalFieldOrder ); + } +} + + + +void OContactFields::saveToRecord( OContact &cnt ){ + + qDebug("ocontactfields saveToRecord: >%s<",fieldOrder.latin1()); + + // Store fieldorder into this contact. + cnt.setCustomField( CONTACT_FIELD_ORDER_NAME, fieldOrder ); + + globalFieldOrder = fieldOrder; + changedFieldOrder = true; + +} + +void OContactFields::loadFromRecord( const OContact &cnt ){ + qDebug("ocontactfields loadFromRecord"); + qDebug("loading >%s<",cnt.fullName().latin1()); + + // Get fieldorder for this contact. If none is defined, + // we will use the global one from the config file.. + + fieldOrder = cnt.customField( CONTACT_FIELD_ORDER_NAME ); + + qDebug("fieldOrder from contact>%s<",fieldOrder.latin1()); + + if (fieldOrder.isEmpty()){ + fieldOrder = globalFieldOrder; + } + + + qDebug("effective fieldOrder in loadFromRecord >%s<",fieldOrder.latin1()); +} + +void OContactFields::setFieldOrder( int num, int index ){ + qDebug("qcontactfields setfieldorder pos %i -> %i",num,index); + + fieldOrder[num] = QString::number( index, 16 )[0]; + + // We will store this new fieldorder globally to + // remember it for contacts which have none + globalFieldOrder = fieldOrder; + changedFieldOrder = true; + + qDebug("fieldOrder >%s<",fieldOrder.latin1()); +} + +int OContactFields::getFieldOrder( int num, int defIndex ){ + qDebug("ocontactfields getFieldOrder"); + qDebug("fieldOrder >%s<",fieldOrder.latin1()); + + // Get index of combo as char.. + QChar poschar = fieldOrder[num]; + + bool ok; + int ret = 0; + // Convert char to number.. + if ( !( poschar == QChar::null ) ) + ret = QString( poschar ).toInt(&ok, 16); + else + ok = false; + + // Return default value if index for + // num was not set or if anything else happened.. + if ( !ok ) ret = defIndex; + + qDebug("returning >%i<",ret); + + return ret; + +} diff --git a/noncore/unsupported/libopie/pim/ocontactfields.h b/noncore/unsupported/libopie/pim/ocontactfields.h new file mode 100644 index 0000000..f105de7 --- a/dev/null +++ b/noncore/unsupported/libopie/pim/ocontactfields.h @@ -0,0 +1,67 @@ +#ifndef OPIE_CONTACTS_FIELDS +#define OPIE_CONTACTS_FIELDS + +class QStringList; + +#include +#include +#include + +#define CONTACT_FIELD_ORDER_NAME "opie-contactfield-order" +#define DEFAULT_FIELD_ORDER "__________" + +class OContactFields{ + + public: + OContactFields(); + ~OContactFields(); + /** Set the index for combo boxes. + * Sets the index of combo num. + * @param num selects the number of the combo + * @param index sets the index in the combo + */ + void setFieldOrder( int num, int index ); + + /** Get the index for combo boxes. + * Returns the index of combo num or defindex + * if none was defined.. + * @param num Selects the number of the combo + * @param defIndex will be returned if none was defined (either + * globally in the config file, nor by the contact which was used + * by loadFromRecord() ) + */ + int getFieldOrder( int num, int defIndex); + + /** Store fieldorder to contact. */ + void saveToRecord( OContact& ); + /** Get Fieldorder from contact. */ + void loadFromRecord( const OContact& ); + + private: + QString fieldOrder; + QString globalFieldOrder; + bool changedFieldOrder; + + public: + static QStringList personalfields( bool sorted = true, bool translated = false ); + static QStringList phonefields( bool sorted = true, bool translated = false ); + static QStringList detailsfields( bool sorted = true, bool translated = false ); + static QStringList fields( bool sorted = true, bool translated = false ); + + static QStringList trpersonalfields( bool sorted = true ); + static QStringList untrpersonalfields( bool sorted = true ); + static QStringList trphonefields( bool sorted = true ); + static QStringList untrphonefields( bool sorted = true ); + static QStringList trdetailsfields( bool sorted = true ); + static QStringList untrdetailsfields( bool sorted = true ); + static QStringList trfields( bool sorted = true ); + static QStringList untrfields( bool sorted = true ); + + static QMap idToTrFields(); + static QMap trFieldsToId(); + static QMap idToUntrFields(); + static QMap untrFieldsToId(); + +}; + +#endif diff --git a/noncore/unsupported/libopie/pim/oconversion.cpp b/noncore/unsupported/libopie/pim/oconversion.cpp new file mode 100644 index 0000000..0d15414 --- a/dev/null +++ b/noncore/unsupported/libopie/pim/oconversion.cpp @@ -0,0 +1,113 @@ +/********************************************************************** +** Copyright (C) 2003 by Stefan Eilers (eilers.stefan@epost.de) +** +** This file may be distributed and/or modified under the terms of the +** GNU Lesser General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +**********************************************************************/ + +#include "oconversion.h" +#include + + +QString OConversion::dateToString( const QDate &d ) +{ + if ( d.isNull() || !d.isValid() ) + return QString::null; + + // ISO format in year, month, day (YYYYMMDD); e.g. 20021231 + QString year = QString::number( d.year() ); + QString month = QString::number( d.month() ); + month = month.rightJustify( 2, '0' ); + QString day = QString::number( d.day() ); + day = day.rightJustify( 2, '0' ); + + QString str = year + month + day; + //qDebug( "\tPimContact dateToStr = %s", str.latin1() ); + + return str; +} + +QDate OConversion::dateFromString( const QString& s ) +{ + QDate date; + + if ( s.isEmpty() ) + return date; + + // Be backward compatible to old Opie format: + // Try to load old format. If it fails, try new ISO-Format! + date = TimeConversion::fromString ( s ); + if ( date.isValid() ) + return date; + + // Read ISO-Format (YYYYMMDD) + int year = s.mid(0, 4).toInt(); + int month = s.mid(4,2).toInt(); + int day = s.mid(6,2).toInt(); + + // do some quick sanity checking -eilers + // but we isValid() again? -zecke + if ( year < 1900 || year > 3000 ) { + qWarning( "PimContact year is not in range"); + return date; + } + if ( month < 0 || month > 12 ) { + qWarning( "PimContact month is not in range"); + return date; + } + if ( day < 0 || day > 31 ) { + qWarning( "PimContact day is not in range"); + return date; + } + + date.setYMD( year, month, day ); + if ( !date.isValid() ) { + qWarning( "PimContact date is not valid"); + return date; + } + + return date; +} +QString OConversion::dateTimeToString( const QDateTime& dt ) { + if (!dt.isValid() || dt.isNull() ) return QString::null; + + QString year = QString::number( dt.date().year() ); + QString month = QString::number( dt.date().month() ); + QString day = QString::number( dt.date().day() ); + + QString hour = QString::number( dt.time().hour() ); + QString min = QString::number( dt.time().minute() ); + QString sec = QString::number( dt.time().second() ); + + month = month.rightJustify( 2, '0' ); + day = day. rightJustify( 2, '0' ); + hour = hour. rightJustify( 2, '0' ); + min = min. rightJustify( 2, '0' ); + sec = sec. rightJustify( 2, '0' ); + + QString str = day + month + year + hour + min + sec; + + return str; +} +QDateTime OConversion::dateTimeFromString( const QString& str) { + + if ( str.isEmpty() ) return QDateTime(); + int day = str.mid(0, 2).toInt(); + int month = str.mid(2, 2).toInt(); + int year = str.mid(4, 4).toInt(); + int hour = str.mid(8, 2).toInt(); + int min = str.mid(10, 2).toInt(); + int sec = str.mid(12, 2).toInt(); + + QDate date( year, month, day ); + QTime time( hour, min, sec ); + QDateTime dt( date, time ); + return dt; +} + diff --git a/noncore/unsupported/libopie/pim/oconversion.h b/noncore/unsupported/libopie/pim/oconversion.h new file mode 100644 index 0000000..4c0a497 --- a/dev/null +++ b/noncore/unsupported/libopie/pim/oconversion.h @@ -0,0 +1,48 @@ +/********************************************************************** +** Copyright (C) 2000-2002 Trolltech AS. All rights reserved. +** Copyright (C) 2002-2003 by Stefan Eilers (eilers.stefan@epost.de) +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** See http://www.trolltech.com/gpl/ for GPL licensing information. +** +** Contact info@trolltech.com if any conditions of this licensing are +** not clear to you. +**********************************************************************/ + +#ifndef __oconversion_h__ +#define __oconversion_h__ + +/* #include */ +/* #include */ +#include + +/* FIXME namespace? -zecke */ +class OConversion +{ +public: + static QString dateToString( const QDate &d ); + static QDate dateFromString( const QString &datestr ); + + /** + * simple function to store DateTime as string and read from string + * no timezone changing is done + * DDMMYYYYHHMMSS is the simple format + */ + static QString dateTimeToString( const QDateTime& ); + static QDateTime dateTimeFromString( const QString& ); + +private: + class Private; + Private* d; + +}; + +#endif // __oconversion_h__ + diff --git a/noncore/unsupported/libopie/pim/odatebookaccess.cpp b/noncore/unsupported/libopie/pim/odatebookaccess.cpp new file mode 100644 index 0000000..d95fed6 --- a/dev/null +++ b/noncore/unsupported/libopie/pim/odatebookaccess.cpp @@ -0,0 +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( back ) +{ + if (!back ) + back = OBackendFactory::Default("datebook", QString::null ); + + m_backEnd = back; + setBackEnd( m_backEnd ); +} +ODateBookAccess::~ODateBookAccess() { +} + +/** + * @return all events available + */ +ODateBookAccess::List ODateBookAccess::rawEvents()const { + QArray ints = m_backEnd->rawEvents(); + + List lis( ints, this ); + return lis; +} + +/** + * @return all repeating events + */ +ODateBookAccess::List ODateBookAccess::rawRepeats()const { + QArray ints = m_backEnd->rawRepeats(); + + List lis( ints, this ); + return lis; +} + +/** + * @return all non repeating events + */ +ODateBookAccess::List ODateBookAccess::nonRepeats()const { + QArray 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 ) const { + return m_backEnd->effectiveEvents( from, to ); +} +/** + * @return all events at a given datetime + */ +OEffectiveEvent::ValueList ODateBookAccess::effectiveEvents( const QDateTime& start ) const { + 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 ) const { + return m_backEnd->effectiveNonRepeatingEvents( from, to ); +} +/** + * @return all non repeating events at a given datetime + */ +OEffectiveEvent::ValueList ODateBookAccess::effectiveNonRepeatingEvents( const QDateTime& start ) const { + return m_backEnd->effectiveNonRepeatingEvents( start ); +} diff --git a/noncore/unsupported/libopie/pim/odatebookaccess.h b/noncore/unsupported/libopie/pim/odatebookaccess.h new file mode 100644 index 0000000..62196da --- a/dev/null +++ b/noncore/unsupported/libopie/pim/odatebookaccess.h @@ -0,0 +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, Stefan Eilers + */ +class ODateBookAccess : public OPimAccessTemplate { +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; + + /* 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/noncore/unsupported/libopie/pim/odatebookaccessbackend.cpp b/noncore/unsupported/libopie/pim/odatebookaccessbackend.cpp new file mode 100644 index 0000000..f0c5d65 --- a/dev/null +++ b/noncore/unsupported/libopie/pim/odatebookaccessbackend.cpp @@ -0,0 +1,182 @@ +#include + +#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() +{ + +} +ODateBookAccessBackend::~ODateBookAccessBackend() { + +} +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(); // Useless, isn't it ? (eilers) + + qHeapSort( tmpList ); + return tmpList; +} +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/noncore/unsupported/libopie/pim/odatebookaccessbackend.h b/noncore/unsupported/libopie/pim/odatebookaccessbackend.h new file mode 100644 index 0000000..3472ab3 --- a/dev/null +++ b/noncore/unsupported/libopie/pim/odatebookaccessbackend.h @@ -0,0 +1,90 @@ +#ifndef OPIE_DATE_BOOK_ACCESS_BACKEND_H +#define OPIE_DATE_BOOK_ACCESS_BACKEND_H + +#include + +#include "opimaccessbackend.h" +#include "oevent.h" + +/** + * This class is the interface to the storage of Events. + * @see OPimAccessBackend + * + */ +class ODateBookAccessBackend : public OPimAccessBackend { +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 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 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 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 effectiveEvents( const QDate& from, const QDate& to ); + + /** + * this is an overloaded member function + * @see effectiveEvents( const QDate& from, const QDate& to ) + */ + 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/noncore/unsupported/libopie/pim/odatebookaccessbackend_sql.cpp b/noncore/unsupported/libopie/pim/odatebookaccessbackend_sql.cpp new file mode 100644 index 0000000..5f87afe --- a/dev/null +++ b/noncore/unsupported/libopie/pim/odatebookaccessbackend_sql.cpp @@ -0,0 +1,374 @@ +/* + * SQL Backend for the OPIE-Calender Database. + * + * Copyright (c) 2003 by Stefan Eilers (Eilers.Stefan@epost.de) + * + * ===================================================================== + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * ===================================================================== + * ===================================================================== + * Version: $Id$ + * ===================================================================== + * History: + * $Log$ + * Revision 1.1 2004/11/16 21:46:08 mickeyl + * libopie1 goes into unsupported + * + * Revision 1.4 2004/03/14 13:50:35 alwin + * namespace correction + * + * Revision 1.3 2003/12/22 11:41:39 eilers + * Fixing stupid bug, found by sourcode review.. + * + * 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 +#include + +#include +#include + +#include + +#include +#include +#include + +#include "orecur.h" +#include "odatebookaccessbackend_sql.h" + +using namespace Opie::DB; + + +ODateBookAccessBackend_SQL::ODateBookAccessBackend_SQL( const QString& , + const QString& fileName ) + : ODateBookAccessBackend(), m_driver( NULL ) +{ + m_fileName = fileName.isEmpty() ? Global::applicationFileName( "datebook", "datebook.db" ) : fileName; + + // Get the standart sql-driver from the OSQLManager.. + OSQLManager man; + m_driver = man.standard(); + m_driver->setUrl( m_fileName ); + + initFields(); + + load(); +} + +ODateBookAccessBackend_SQL::~ODateBookAccessBackend_SQL() { + 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::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 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::Iterator it; + for ( it = ++m_fieldMap.begin(); it != m_fieldMap.end(); ++it ){ + qu += QString( ",%1 VARCHAR(10)" ).arg( it.data() ); + } + qu += " );"; + + qu += "create table custom_data( uid INTEGER, id INTEGER, type VARCHAR, priority INTEGER, value VARCHAR, PRIMARY KEY /* identifier */ (uid, id) );"; + + 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(); + return; + } + + m_uids = extractUids( res ); + +} + +bool ODateBookAccessBackend_SQL::reload() +{ + return load(); +} + +bool ODateBookAccessBackend_SQL::save() +{ + return m_driver->close(); // Shouldn't m_driver->sync be better than close ? (eilers) +} + +QArray ODateBookAccessBackend_SQL::allRecords()const +{ + return m_uids; +} + +QArray ODateBookAccessBackend_SQL::queryByExample(const OEvent&, int, const QDateTime& ) { + return QArray(); +} + +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 dateEventMap; + dateEventMap.insert( OEvent::FUid, QString::number( uid ) ); + + // Now insert the data out of the columns into the map. + QMapConstIterator 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; +} + +// FIXME: Speed up update of uid's.. +bool ODateBookAccessBackend_SQL::add( const OEvent& ev ) +{ + QMap eventMap = ev.toMap(); + + QString qu = "insert into datebook VALUES( " + QString::number( ev.uid() ); + QMap::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 customMap = ev.toExtraMap(); + for( QMap::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; + } + + // Update list of uid's + update(); + + return true; +} + +// FIXME: Speed up update of uid's.. +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; + } + + // Update list of uid's + update(); + + return true; +} + +bool ODateBookAccessBackend_SQL::replace( const OEvent& ev ) +{ + remove( ev.uid() ); + return add( ev ); +} + +QArray ODateBookAccessBackend_SQL::rawEvents()const +{ + return allRecords(); +} + +QArray 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 nix; + return nix; + } + + return extractUids( res ); +} + +QArray 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 nix; + return nix; + } + + return extractUids( res ); +} + +OEvent::ValueList ODateBookAccessBackend_SQL::directNonRepeats() +{ + QArray 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() +{ + QArray rawRepUids = rawRepeats(); + OEvent::ValueList list; + + for (uint i = 0; i < rawRepUids.count(); ++i ){ + list.append( find( rawRepUids[i] ) ); + } + + return list; +} + + +QArray ODateBookAccessBackend_SQL::matchRegexp( const QRegExp &r ) const +{ + QArray null; + return null; +} + +/* ===== Private Functions ========================================== */ + +QArray ODateBookAccessBackend_SQL::extractUids( OSQLResult& res ) const +{ + qWarning("extractUids"); + QTime t; + t.start(); + OSQLResultItem::ValueList list = res.results(); + OSQLResultItem::ValueList::Iterator it; + QArray 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; + +} diff --git a/noncore/unsupported/libopie/pim/odatebookaccessbackend_sql.h b/noncore/unsupported/libopie/pim/odatebookaccessbackend_sql.h new file mode 100644 index 0000000..ba514bc --- a/dev/null +++ b/noncore/unsupported/libopie/pim/odatebookaccessbackend_sql.h @@ -0,0 +1,65 @@ +#ifndef OPIE_DATE_BOOK_ACCESS_BACKEND_SQL__H +#define OPIE_DATE_BOOK_ACCESS_BACKEND_SQL__H + +#include +#include + +#include "odatebookaccessbackend.h" + +namespace Opie { namespace DB { +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 allRecords()const; + QArray matchRegexp(const QRegExp &r) const; + QArray 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 rawEvents()const; + QArray rawRepeats()const; + QArray nonRepeats()const; + + OEvent::ValueList directNonRepeats(); + OEvent::ValueList directRawRepeats(); + +private: + bool loadFile(); + QString m_fileName; + QArray m_uids; + + QMap m_fieldMap; + QMap m_reverseFieldMap; + + Opie::DB::OSQLDriver* m_driver; + + class Private; + Private *d; + + void initFields(); + void update(); + QArray extractUids( Opie::DB::OSQLResult& res ) const; + +}; + +#endif diff --git a/noncore/unsupported/libopie/pim/odatebookaccessbackend_xml.cpp b/noncore/unsupported/libopie/pim/odatebookaccessbackend_xml.cpp new file mode 100644 index 0000000..929d004 --- a/dev/null +++ b/noncore/unsupported/libopie/pim/odatebookaccessbackend_xml.cpp @@ -0,0 +1,612 @@ +#include +#include + +#include +#include + +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include + +#include "opimnotifymanager.h" +#include "orecur.h" +#include "otimezone.h" +#include "odatebookaccessbackend_xml.h" + +namespace { + // FROM TT again +char *strstrlen(const char *haystack, int hLen, const char* needle, int nLen) +{ + char needleChar; + char haystackChar; + if (!needle || !haystack || !hLen || !nLen) + return 0; + + const char* hsearch = haystack; + + if ((needleChar = *needle++) != 0) { + nLen--; //(to make up for needle++) + do { + do { + if ((haystackChar = *hsearch++) == 0) + return (0); + if (hsearch >= haystack + hLen) + return (0); + } while (haystackChar != needleChar); + } while (strncmp(hsearch, needle, QMIN(hLen - (hsearch - haystack), nLen)) != 0); + hsearch--; + } + return ((char *)hsearch); +} +} + +namespace { + time_t start, end, created, rp_end; + ORecur* rec; + ORecur* recur() { + if (!rec) + rec = new ORecur; + + return rec; + } + int alarmTime; + int snd; + enum Attribute{ + FDescription = 0, + FLocation, + FCategories, + FUid, + FType, + FAlarm, + FSound, + FRType, + FRWeekdays, + FRPosition, + FRFreq, + FRHasEndDate, + FREndDate, + FRStart, + FREnd, + FNote, + FCreated, // Should't this be called FRCreated ? + FTimeZone, + FRecParent, + FRecChildren, + FExceptions + }; + + // FIXME: Use OEvent::toMap() here !! (eilers) + inline void save( const OEvent& ev, QString& buf ) { + qWarning("Saving %d %s", ev.uid(), ev.description().latin1() ); + buf += " description=\"" + Qtopia::escapeString(ev.description() ) + "\""; + if (!ev.location().isEmpty() ) + buf += " location=\"" + Qtopia::escapeString(ev.location() ) + "\""; + + buf += " categories=\""+ Qtopia::escapeString( Qtopia::Record::idsToString( ev.categories() ) ) + "\""; + buf += " uid=\"" + QString::number( ev.uid() ) + "\""; + + if (ev.isAllDay() ) + buf += " type=\"AllDay\""; // is that all ?? (eilers) + + if (ev.hasNotifiers() ) { + OPimAlarm alarm = ev.notifiers().alarms()[0]; // take only the first + int minutes = alarm.dateTime().secsTo( ev.startDateTime() ) / 60; + buf += " alarm=\"" + QString::number(minutes) + "\" sound=\""; + if ( alarm.sound() == OPimAlarm::Loud ) + buf += "loud"; + else + buf += "silent"; + buf += "\""; + } + if ( ev.hasRecurrence() ) { + buf += ev.recurrence().toString(); + } + + /* + * fscking timezones :) well, we'll first convert + * the QDateTime to a QDateTime in UTC time + * and then we'll create a nice time_t + */ + OTimeZone zone( ev.timeZone().isEmpty() ? OTimeZone::current() : ev.timeZone() ); + buf += " start=\"" + QString::number( zone.fromUTCDateTime( zone.toDateTime( ev.startDateTime(), OTimeZone::utc() ) ) ) + "\""; + buf += " end=\"" + QString::number( zone.fromUTCDateTime( zone.toDateTime( ev.endDateTime() , OTimeZone::utc() ) ) ) + "\""; + if (!ev.note().isEmpty() ) { + buf += " note=\"" + Qtopia::escapeString( ev.note() ) + "\""; + } + + buf += " timezone=\""; + if ( ev.timeZone().isEmpty() ) + buf += "None"; + else + buf += ev.timeZone(); + buf += "\""; + + if (ev.parent() != 0 ) { + buf += " recparent=\""+QString::number(ev.parent() )+"\""; + } + + if (ev.children().count() != 0 ) { + QArray children = ev.children(); + buf += " recchildren=\""; + for ( uint i = 0; i < children.count(); i++ ) { + if ( i != 0 ) buf += " "; + buf += QString::number( children[i] ); + } + buf+= "\""; + } + + // skip custom writing + } + + inline bool forAll( const QMap& list, QFile& file ) { + QMap::ConstIterator it; + QString buf; + QCString str; + int total_written; + for ( it = list.begin(); it != list.end(); ++it ) { + buf = "\n" ); + buf += "\n"; + buf += "\n"; + QCString str = buf.utf8(); + total_written = f.writeBlock( str.data(), str.length() ); + if ( total_written != int(str.length() ) ) { + f.close(); + QFile::remove( strFileNew ); + return false; + } + + if (!forAll( m_raw, f ) ) { + f.close(); + QFile::remove( strFileNew ); + return false; + } + if (!forAll( m_rep, f ) ) { + f.close(); + QFile::remove( strFileNew ); + return false; + } + + buf = "\n\n"; + str = buf.utf8(); + total_written = f.writeBlock( str.data(), str.length() ); + if ( total_written != int(str.length() ) ) { + f.close(); + QFile::remove( strFileNew ); + return false; + } + f.close(); + + if ( ::rename( strFileNew, m_name ) < 0 ) { + QFile::remove( strFileNew ); + return false; + } + + m_changed = false; + return true; +} +QArray ODateBookAccessBackend_XML::allRecords()const { + QArray ints( m_raw.count()+ m_rep.count() ); + uint i = 0; + QMap::ConstIterator it; + + for ( it = m_raw.begin(); it != m_raw.end(); ++it ) { + ints[i] = it.key(); + i++; + } + for ( it = m_rep.begin(); it != m_rep.end(); ++it ) { + ints[i] = it.key(); + i++; + } + + return ints; +} +QArray ODateBookAccessBackend_XML::queryByExample(const OEvent&, int, const QDateTime& ) { + return QArray(); +} +void ODateBookAccessBackend_XML::clear() { + m_changed = true; + m_raw.clear(); + m_rep.clear(); +} +OEvent ODateBookAccessBackend_XML::find( int uid ) const{ + if ( m_raw.contains( uid ) ) + return m_raw[uid]; + else + return m_rep[uid]; +} +bool ODateBookAccessBackend_XML::add( const OEvent& ev ) { + m_changed = true; + if (ev.hasRecurrence() ) + m_rep.insert( ev.uid(), ev ); + else + m_raw.insert( ev.uid(), ev ); + + return true; +} +bool ODateBookAccessBackend_XML::remove( int uid ) { + m_changed = true; + m_rep.remove( uid ); + m_rep.remove( uid ); + + return true; +} +bool ODateBookAccessBackend_XML::replace( const OEvent& ev ) { + replace( ev.uid() ); // ??? Shouldn't this be "remove( ev.uid() ) ??? (eilers) + return add( ev ); +} +QArray ODateBookAccessBackend_XML::rawEvents()const { + return allRecords(); +} +QArray ODateBookAccessBackend_XML::rawRepeats()const { + QArray ints( m_rep.count() ); + uint i = 0; + QMap::ConstIterator it; + + for ( it = m_rep.begin(); it != m_rep.end(); ++it ) { + ints[i] = it.key(); + i++; + } + + return ints; +} +QArray ODateBookAccessBackend_XML::nonRepeats()const { + QArray ints( m_raw.count() ); + uint i = 0; + QMap::ConstIterator it; + + for ( it = m_raw.begin(); it != m_raw.end(); ++it ) { + ints[i] = it.key(); + i++; + } + + return ints; +} +OEvent::ValueList ODateBookAccessBackend_XML::directNonRepeats() { + OEvent::ValueList list; + QMap::ConstIterator it; + for (it = m_raw.begin(); it != m_raw.end(); ++it ) + list.append( it.data() ); + + return list; +} +OEvent::ValueList ODateBookAccessBackend_XML::directRawRepeats() { + OEvent::ValueList list; + QMap::ConstIterator it; + for (it = m_rep.begin(); it != m_rep.end(); ++it ) + list.append( it.data() ); + + return list; +} + +// FIXME: Use OEvent::fromMap() (eilers) +bool ODateBookAccessBackend_XML::loadFile() { + m_changed = false; + + int fd = ::open( QFile::encodeName(m_name).data(), O_RDONLY ); + if ( fd < 0 ) return false; + + struct stat attribute; + if ( ::fstat(fd, &attribute ) == -1 ) { + ::close( fd ); + return false; + } + void* map_addr = ::mmap(NULL, attribute.st_size, PROT_READ, MAP_SHARED, fd, 0 ); + if ( map_addr == ( (caddr_t)-1) ) { + ::close( fd ); + return false; + } + + ::madvise( map_addr, attribute.st_size, MADV_SEQUENTIAL ); + ::close( fd ); + + QAsciiDict dict(FExceptions+1); + dict.setAutoDelete( true ); + dict.insert( "description", new int(FDescription) ); + dict.insert( "location", new int(FLocation) ); + dict.insert( "categories", new int(FCategories) ); + dict.insert( "uid", new int(FUid) ); + dict.insert( "type", new int(FType) ); + dict.insert( "alarm", new int(FAlarm) ); + dict.insert( "sound", new int(FSound) ); + dict.insert( "rtype", new int(FRType) ); + dict.insert( "rweekdays", new int(FRWeekdays) ); + dict.insert( "rposition", new int(FRPosition) ); + dict.insert( "rfreq", new int(FRFreq) ); + dict.insert( "rhasenddate", new int(FRHasEndDate) ); + dict.insert( "enddt", new int(FREndDate) ); + dict.insert( "start", new int(FRStart) ); + dict.insert( "end", new int(FREnd) ); + dict.insert( "note", new int(FNote) ); + dict.insert( "created", new int(FCreated) ); // Shouldn't this be FRCreated ?? + dict.insert( "recparent", new int(FRecParent) ); + dict.insert( "recchildren", new int(FRecChildren) ); + dict.insert( "exceptions", new int(FExceptions) ); + dict.insert( "timezone", new int(FTimeZone) ); + + char* dt = (char*)map_addr; + int len = attribute.st_size; + int i = 0; + char* point; + const char* collectionString = "= len-2 || (dt[i] == '/' && dt[i+1] == '>') ) + break; + + + // we have another attribute, read it. + int j = i; + while ( j < len && dt[j] != '=' ) + ++j; + QCString attr( dt+i, j-i+1); + + i = ++j; // skip = + + // find the start of quotes + while ( i < len && dt[i] != '"' ) + ++i; + j = ++i; + + bool haveUtf = FALSE; + bool haveEnt = FALSE; + while ( j < len && dt[j] != '"' ) { + if ( ((unsigned char)dt[j]) > 0x7f ) + haveUtf = TRUE; + if ( dt[j] == '&' ) + haveEnt = TRUE; + ++j; + } + if ( i == j ) { + // empty value + i = j + 1; + continue; + } + + QCString value( dt+i, j-i+1 ); + i = j + 1; + + QString str = (haveUtf ? QString::fromUtf8( value ) + : QString::fromLatin1( value ) ); + if ( haveEnt ) + str = Qtopia::plainString( str ); + + /* + * add key + value + */ + find = dict[attr.data()]; + if (!find) + ev.setCustomField( attr, str ); + else { + setField( ev, *find, str ); + } + } + /* time to finalize */ + finalizeRecord( ev ); + delete rec; + } + ::munmap(map_addr, attribute.st_size ); + m_changed = false; // changed during add + + return true; +} + +// FIXME: Use OEvent::fromMap() which makes this obsolete.. (eilers) +void ODateBookAccessBackend_XML::finalizeRecord( OEvent& ev ) { + /* AllDay is alway in UTC */ + if ( ev.isAllDay() ) { + OTimeZone utc = OTimeZone::utc(); + ev.setStartDateTime( utc.fromUTCDateTime( start ) ); + ev.setEndDateTime ( utc.fromUTCDateTime( end ) ); + ev.setTimeZone( "UTC"); // make sure it is really utc + }else { + /* to current date time */ + // qWarning(" Start is %d", start ); + OTimeZone zone( ev.timeZone().isEmpty() ? OTimeZone::current() : ev.timeZone() ); + QDateTime date = zone.toDateTime( start ); + qWarning(" Start is %s", date.toString().latin1() ); + ev.setStartDateTime( zone.toDateTime( date, OTimeZone::current() ) ); + + date = zone.toDateTime( end ); + ev.setEndDateTime ( zone.toDateTime( date, OTimeZone::current() ) ); + } + if ( rec && rec->doesRecur() ) { + OTimeZone utc = OTimeZone::utc(); + ORecur recu( *rec ); // call copy c'tor; + recu.setEndDate ( utc.fromUTCDateTime( rp_end ).date() ); + recu.setCreatedDateTime( utc.fromUTCDateTime( created ) ); + recu.setStart( ev.startDateTime().date() ); + ev.setRecurrence( recu ); + } + + if (alarmTime != -1 ) { + QDateTime dt = ev.startDateTime().addSecs( -1*alarmTime*60 ); + OPimAlarm al( snd , dt ); + ev.notifiers().add( al ); + } + if ( m_raw.contains( ev.uid() ) || m_rep.contains( ev.uid() ) ) { + qWarning("already contains assign uid"); + ev.setUid( 1 ); + } + qWarning("addind %d %s", ev.uid(), ev.description().latin1() ); + if ( ev.hasRecurrence() ) + m_rep.insert( ev.uid(), ev ); + else + m_raw.insert( ev.uid(), ev ); + +} +void ODateBookAccessBackend_XML::setField( OEvent& e, int id, const QString& value) { +// qWarning(" setting %s", value.latin1() ); + switch( id ) { + case FDescription: + e.setDescription( value ); + break; + case FLocation: + e.setLocation( value ); + break; + case FCategories: + e.setCategories( e.idsFromString( value ) ); + break; + case FUid: + e.setUid( value.toInt() ); + break; + case FType: + if ( value == "AllDay" ) { + e.setAllDay( true ); + e.setTimeZone( "UTC" ); + } + break; + case FAlarm: + alarmTime = value.toInt(); + break; + case FSound: + snd = value == "loud" ? OPimAlarm::Loud : OPimAlarm::Silent; + break; + // recurrence stuff + case FRType: + if ( value == "Daily" ) + recur()->setType( ORecur::Daily ); + else if ( value == "Weekly" ) + recur()->setType( ORecur::Weekly); + else if ( value == "MonthlyDay" ) + recur()->setType( ORecur::MonthlyDay ); + else if ( value == "MonthlyDate" ) + recur()->setType( ORecur::MonthlyDate ); + else if ( value == "Yearly" ) + recur()->setType( ORecur::Yearly ); + else + recur()->setType( ORecur::NoRepeat ); + break; + case FRWeekdays: + recur()->setDays( value.toInt() ); + break; + case FRPosition: + recur()->setPosition( value.toInt() ); + break; + case FRFreq: + recur()->setFrequency( value.toInt() ); + break; + case FRHasEndDate: + recur()->setHasEndDate( value.toInt() ); + break; + case FREndDate: { + rp_end = (time_t) value.toLong(); + break; + } + case FRStart: { + start = (time_t) value.toLong(); + break; + } + case FREnd: { + end = ( (time_t) value.toLong() ); + break; + } + case FNote: + e.setNote( value ); + break; + case FCreated: + created = value.toInt(); + break; + case FRecParent: + e.setParent( value.toInt() ); + break; + case FRecChildren:{ + QStringList list = QStringList::split(' ', value ); + for ( QStringList::Iterator it = list.begin(); it != list.end(); ++it ) { + e.addChild( (*it).toInt() ); + } + } + break; + case FExceptions:{ + QStringList list = QStringList::split(' ', value ); + for (QStringList::Iterator it = list.begin(); it != list.end(); ++it ) { + QDate date( (*it).left(4).toInt(), (*it).mid(4, 2).toInt(), (*it).right(2).toInt() ); + qWarning("adding exception %s", date.toString().latin1() ); + recur()->exceptions().append( date ); + } + } + break; + case FTimeZone: + if ( value != "None" ) + e.setTimeZone( value ); + break; + default: + break; + } +} +QArray ODateBookAccessBackend_XML::matchRegexp( const QRegExp &r ) const +{ + QArray m_currentQuery( m_raw.count()+ m_rep.count() ); + uint arraycounter = 0; + QMap::ConstIterator it; + + for ( it = m_raw.begin(); it != m_raw.end(); ++it ) + if ( it.data().match( r ) ) + m_currentQuery[arraycounter++] = it.data().uid(); + for ( it = m_rep.begin(); it != m_rep.end(); ++it ) + if ( it.data().match( r ) ) + m_currentQuery[arraycounter++] = it.data().uid(); + + // Shrink to fit.. + m_currentQuery.resize(arraycounter); + + return m_currentQuery; +} diff --git a/noncore/unsupported/libopie/pim/odatebookaccessbackend_xml.h b/noncore/unsupported/libopie/pim/odatebookaccessbackend_xml.h new file mode 100644 index 0000000..a5cc0fc --- a/dev/null +++ b/noncore/unsupported/libopie/pim/odatebookaccessbackend_xml.h @@ -0,0 +1,55 @@ +#ifndef OPIE_DATE_BOOK_ACCESS_BACKEND_XML__H +#define OPIE_DATE_BOOK_ACCESS_BACKEND_XML__H + +#include + +#include "odatebookaccessbackend.h" + +/** + * This is the default XML implementation for DateBoook XML storage + * It fully implements the interface + * @see ODateBookAccessBackend + * @see OPimAccessBackend + */ +class ODateBookAccessBackend_XML : public ODateBookAccessBackend { +public: + ODateBookAccessBackend_XML( const QString& appName, + const QString& fileName = QString::null); + ~ODateBookAccessBackend_XML(); + + bool load(); + bool reload(); + bool save(); + + QArray allRecords()const; + QArray matchRegexp(const QRegExp &r) const; + QArray 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 rawEvents()const; + QArray rawRepeats()const; + QArray nonRepeats()const; + + OEvent::ValueList directNonRepeats(); + OEvent::ValueList directRawRepeats(); + +private: + bool m_changed :1 ; + bool loadFile(); + inline void finalizeRecord( OEvent& ev ); + inline void setField( OEvent&, int field, const QString& val ); + QString m_name; + QMap m_raw; + QMap m_rep; + + struct Data; + Data* data; + class Private; + Private *d; +}; + +#endif diff --git a/noncore/unsupported/libopie/pim/oevent.cpp b/noncore/unsupported/libopie/pim/oevent.cpp new file mode 100644 index 0000000..9b31957 --- a/dev/null +++ b/noncore/unsupported/libopie/pim/oevent.cpp @@ -0,0 +1,717 @@ +#include +#include + +#include +#include +#include + +#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* 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 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 += "

"; + if ( !description().isEmpty() ) { + text += Qtopia::escapeString(description() ).replace(QRegExp( "[\n]"), "" ); + } + text += "




"; + + // location + if ( !(value = location()).isEmpty() ) { + text += "" + QObject::tr( "Location:" ) + " "; + text += Qtopia::escapeString(value) + "
"; + } + + // all day event + if ( isAllDay() ) { + text += "" + QObject::tr( "This is an all day event" ) + "
"; + } + // multiple day event + else if ( isMultipleDay () ) { + text += "" + QObject::tr( "This is a multiple day event" ) + "
"; + } + // start & end times + else { + // start time + if ( startDateTime().isValid() ) { + text += "" + QObject::tr( "Start:") + " "; + text += Qtopia::escapeString(startDateTime().toString() ). + replace(QRegExp( "[\n]"), "
" ) + "
"; + } + + // end time + if ( endDateTime().isValid() ) { + text += "" + QObject::tr( "End:") + " "; + text += Qtopia::escapeString(endDateTime().toString() ). + replace(QRegExp( "[\n]"), "
" ) + "
"; + } + } + + // categories + if ( categoryNames("Calendar").count() ){ + text += "" + QObject::tr( "Category:") + " "; + text += categoryNames("Calendar").join(", "); + text += "
"; + } + + //notes + if ( !note().isEmpty() ) { + text += "" + QObject::tr( "Note:") + "
"; + text += note(); +// text += Qtopia::escapeString(note() ). +// replace(QRegExp( "[\n]"), "
" ) + "
"; + } + 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( *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 OEvent::toMap()const { + QMap 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 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 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 recFields = recur.toMap(); + retMap.insert( OEvent::FRType, recFields[ORecur::RType] ); + } + + return retMap; +} + +void OEvent::fromMap( const QMap& 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 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 OEvent::children() const{ + if (!data->child) return QArray(); + else + return data->child->copy(); +} +void OEvent::setChildren( const QArray& arr ) { + changeOrModify(); + if (data->child) delete data->child; + + data->child = new QArray( arr ); + data->child->detach(); +} +void OEvent::addChild( int uid ) { + changeOrModify(); + if (!data->child ) { + data->child = new QArray(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 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/noncore/unsupported/libopie/pim/oevent.h b/noncore/unsupported/libopie/pim/oevent.h new file mode 100644 index 0000000..9eb948f --- a/dev/null +++ b/noncore/unsupported/libopie/pim/oevent.h @@ -0,0 +1,236 @@ +// CONTAINS GPLed code of TT + +#ifndef OPIE_PIM_EVENT_H +#define OPIE_PIM_EVENT_H + +#include +#include +#include + +#include +#include + +#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 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 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 children()const; + void setChildren( const QArray& ); + 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 toMap()const; + void fromMap( const QMap& 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 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/noncore/unsupported/libopie/pim/opimaccessbackend.h b/noncore/unsupported/libopie/pim/opimaccessbackend.h new file mode 100644 index 0000000..fd264fc --- a/dev/null +++ b/noncore/unsupported/libopie/pim/opimaccessbackend.h @@ -0,0 +1,160 @@ +#ifndef OPIE_PIM_ACCESS_BACKEND +#define OPIE_PIM_ACCESS_BACKEND + +#include +#include + +#include +#include + + +class OPimAccessBackendPrivate; +/** + * OPimAccessBackend is the base class + * for all private backends + * it operates on OPimRecord as the base class + * and it's responsible for fast manipulating + * the resource the implementation takes care + * of + */ +template +class OPimAccessBackend { +public: + typedef OTemplateBase Frontend; + + /** The access hint from the frontend */ + OPimAccessBackend(int access = 0); + virtual ~OPimAccessBackend(); + + /** + * load the resource + */ + virtual bool load() = 0; + + /** + * reload the resource + */ + virtual bool reload() = 0; + + /** + * save the resource and + * all it's changes + */ + virtual bool save() = 0; + + /** + * return an array of + * all available uids + */ + virtual QArray allRecords()const = 0; + + /** + * return a List of records + * that match the regex + */ + virtual QArray matchRegexp(const QRegExp &r) const = 0; + + /** + * queryByExample for T with the given Settings + * + */ + virtual QArray queryByExample( const T& t, int settings, const QDateTime& d = QDateTime() ) = 0; + + /** + * find the OPimRecord with uid @param uid + * returns T and T.isEmpty() if nothing was found + */ + virtual T find(int uid )const = 0; + + virtual T find(int uid, const QArray& items, + uint current, typename Frontend::CacheDirection )const ; + /** + * clear the back end + */ + virtual void clear() = 0; + + /** + * add T + */ + virtual bool add( const T& t ) = 0; + + /** + * remove + */ + virtual bool remove( int uid ) = 0; + + /** + * replace a record with T.uid() + */ + virtual bool replace( const T& t ) = 0; + + /* + * setTheFrontEnd!!! + */ + void setFrontend( Frontend* front ); + + /** + * set the read ahead count + */ + void setReadAhead( uint count ); +protected: + int access()const; + void cache( const T& t )const; + + /** + * use a prime number here! + */ + void setSaneCacheSize( int ); + + uint readAhead()const; + +private: + OPimAccessBackendPrivate *d; + Frontend* m_front; + uint m_read; + int m_acc; + +}; + +template +OPimAccessBackend::OPimAccessBackend(int acc) + : m_acc( acc ) +{ + m_front = 0l; +} +template +OPimAccessBackend::~OPimAccessBackend() { + +} +template +void OPimAccessBackend::setFrontend( Frontend* fr ) { + m_front = fr; +} +template +void OPimAccessBackend::cache( const T& t )const { + if (m_front ) + m_front->cache( t ); +} +template +void OPimAccessBackend::setSaneCacheSize( int size) { + if (m_front ) + m_front->setSaneCacheSize( size ); +} +template +T OPimAccessBackend::find( int uid, const QArray&, + uint, typename Frontend::CacheDirection )const { + return find( uid ); +} +template +void OPimAccessBackend::setReadAhead( uint count ) { + m_read = count; +} +template +uint OPimAccessBackend::readAhead()const { + return m_read; +} +template +int OPimAccessBackend::access()const { + return m_acc; +} +#endif diff --git a/noncore/unsupported/libopie/pim/opimaccesstemplate.h b/noncore/unsupported/libopie/pim/opimaccesstemplate.h new file mode 100644 index 0000000..ecbeb68 --- a/dev/null +++ b/noncore/unsupported/libopie/pim/opimaccesstemplate.h @@ -0,0 +1,302 @@ +#ifndef OPIE_PIM_ACCESS_TEMPLATE_H +#define OPIE_PIM_ACCESS_TEMPLATE_H + +#include + +#include +#include +#include + +#include "opimcache.h" +#include "otemplatebase.h" + +class OPimAccessTemplatePrivate; +/** + * Thats the frontend to our OPIE PIM + * Library. Either you want to use it's + * interface or you want to implement + * your own Access lib + * Just create a OPimRecord and inherit from + * the plugins + */ + +template +class OPimAccessTemplate : public OTemplateBase { +public: + enum Access { + Random = 0, + SortedAccess + }; + typedef ORecordList List; + typedef OPimAccessBackend BackEnd; + typedef OPimCache Cache; + + /** + * c'tor BackEnd + * enum Access a small hint on how to handle the backend + */ + OPimAccessTemplate( BackEnd* end); + + virtual ~OPimAccessTemplate(); + + /** + * load from the backend + */ + bool load(); + + /** Reload database. + * You should execute this function if the external database + * was changed. + * This function will load the external database and afterwards + * rejoin the local changes. Therefore the local database will be set consistent. + */ + virtual bool reload(); + + /** Save contacts database. + * Save is more a "commit". After calling this function, all changes are public available. + * @return true if successful + */ + bool save(); + + /** + * if the resource was changed externally + * You should use the signal handling instead of polling possible changes ! + * zecke: Do you implement a signal for otodoaccess ? + */ + bool wasChangedExternally()const; + + /** + * return a List of records + * you can iterate over them + */ + virtual List allRecords()const; + + /** + * return a List of records + * that match the regex + */ + virtual List matchRegexp( const QRegExp &r ) const; + + /** + * queryByExample. + * @see otodoaccess, ocontactaccess + */ + virtual List queryByExample( const T& t, int querySettings, const QDateTime& d = QDateTime() ); + + /** + * find the OPimRecord uid + */ + virtual T find( int uid )const; + + /** + * read ahead cache find method ;) + */ + virtual T find( int uid, const QArray&, + uint current, typename OTemplateBase::CacheDirection dir = OTemplateBase::Forward )const; + + /* invalidate cache here */ + /** + * clears the backend and invalidates the backend + */ + void clear() ; + + /** + * add T to the backend + * @param t The item to add. + * @return true if added successfully. + */ + virtual bool add( const T& t ) ; + bool add( const OPimRecord& ); + + /* only the uid matters */ + /** + * remove T from the backend + * @param t The item to remove + * @return true if successful. + */ + virtual bool remove( const T& t ); + + /** + * remove the OPimRecord with uid + * @param uid The ID of the item to remove + * @return true if successful. + */ + bool remove( int uid ); + bool remove( const OPimRecord& ); + + /** + * replace T from backend + * @param t The item to replace + * @return true if successful. + */ + virtual bool replace( const T& t) ; + + void setReadAhead( uint count ); + /** + * @internal + */ + void cache( const T& )const; + void setSaneCacheSize( int ); + + QArray records()const; +protected: + /** + * invalidate the cache + */ + void invalidateCache(); + + void setBackEnd( BackEnd* end ); + /** + * returns the backend + */ + BackEnd* backEnd(); + BackEnd* m_backEnd; + Cache m_cache; + +private: + OPimAccessTemplatePrivate *d; + +}; + +template +OPimAccessTemplate::OPimAccessTemplate( BackEnd* end ) + : OTemplateBase(), m_backEnd( end ) +{ + if (end ) + end->setFrontend( this ); +} +template +OPimAccessTemplate::~OPimAccessTemplate() { + qWarning("~OPimAccessTemplate"); + delete m_backEnd; +} +template +bool OPimAccessTemplate::load() { + invalidateCache(); + return m_backEnd->load(); +} +template +bool OPimAccessTemplate::reload() { + invalidateCache(); // zecke: I think this should be added (se) + return m_backEnd->reload(); +} +template +bool OPimAccessTemplate::save() { + return m_backEnd->save(); +} +template +typename OPimAccessTemplate::List OPimAccessTemplate::allRecords()const { + QArray ints = m_backEnd->allRecords(); + List lis(ints, this ); + return lis; +} +template +typename OPimAccessTemplate::List OPimAccessTemplate::matchRegexp( const QRegExp &r )const { + QArray ints = m_backEnd->matchRegexp( r ); + List lis(ints, this ); + return lis; +} +template +QArray OPimAccessTemplate::records()const { + return m_backEnd->allRecords(); +} +template +typename OPimAccessTemplate::List +OPimAccessTemplate::queryByExample( const T& t, int settings, const QDateTime& d ) { + QArray ints = m_backEnd->queryByExample( t, settings, d ); + + List lis(ints, this ); + return lis; +} +template +T OPimAccessTemplate::find( int uid ) const{ + T t = m_backEnd->find( uid ); + cache( t ); + return t; +} +template +T OPimAccessTemplate::find( int uid, const QArray& ar, + uint current, typename OTemplateBase::CacheDirection dir )const { + /* + * better do T.isEmpty() + * after a find this way we would + * avoid two finds in QCache... + */ + // qWarning("find it now %d", uid ); + if (m_cache.contains( uid ) ) { + return m_cache.find( uid ); + } + + T t = m_backEnd->find( uid, ar, current, dir ); + cache( t ); + return t; +} +template +void OPimAccessTemplate::clear() { + invalidateCache(); + m_backEnd->clear(); +} +template +bool OPimAccessTemplate::add( const T& t ) { + cache( t ); + return m_backEnd->add( t ); +} +template +bool OPimAccessTemplate::add( const OPimRecord& rec) { + /* same type */ + if ( rec.rtti() == T::rtti() ) { + const T &t = static_cast(rec); + return add(t); + } + return false; +} +template +bool OPimAccessTemplate::remove( const T& t ) { + return remove( t.uid() ); +} +template +bool OPimAccessTemplate::remove( int uid ) { + m_cache.remove( uid ); + return m_backEnd->remove( uid ); +} +template +bool OPimAccessTemplate::remove( const OPimRecord& rec) { + return remove( rec.uid() ); +} +template +bool OPimAccessTemplate::replace( const T& t ) { + m_cache.replace( t ); + return m_backEnd->replace( t ); +} +template +void OPimAccessTemplate::invalidateCache() { + m_cache.invalidate(); +} +template +typename OPimAccessTemplate::BackEnd* OPimAccessTemplate::backEnd() { + return m_backEnd; +} +template +bool OPimAccessTemplate::wasChangedExternally()const { + return false; +} +template +void OPimAccessTemplate::setBackEnd( BackEnd* end ) { + m_backEnd = end; + if (m_backEnd ) + m_backEnd->setFrontend( this ); +} +template +void OPimAccessTemplate::cache( const T& t ) const{ + /* hacky we need to work around the const*/ + ((OPimAccessTemplate*)this)->m_cache.add( t ); +} +template +void OPimAccessTemplate::setSaneCacheSize( int size ) { + m_cache.setSize( size ); +} +template +void OPimAccessTemplate::setReadAhead( uint count ) { + m_backEnd->setReadAhead( count ); +} +#endif diff --git a/noncore/unsupported/libopie/pim/opimcache.h b/noncore/unsupported/libopie/pim/opimcache.h new file mode 100644 index 0000000..7f7cff5 --- a/dev/null +++ b/noncore/unsupported/libopie/pim/opimcache.h @@ -0,0 +1,131 @@ +#ifndef OPIE_PIM_CACHE_H +#define OPIE_PIM_CACHE_H + +#include + +#include "opimrecord.h" + +class OPimCacheItemPrivate; + +template +class OPimCacheItem { +public: + OPimCacheItem( const T& t = T() ); + OPimCacheItem( const OPimCacheItem& ); + ~OPimCacheItem(); + + OPimCacheItem &operator=( const OPimCacheItem& ); + + T record()const; + void setRecord( const T& ); +private: + T m_t; + OPimCacheItemPrivate *d; +}; + + +class OPimCachePrivate; +/** + * OPimCache for caching the items + * We support adding, removing + * and finding + */ +template +class OPimCache { +public: + typedef OPimCacheItem Item; + OPimCache(); + OPimCache( const OPimCache& ); + ~OPimCache(); + + OPimCache &operator=( const OPimCache& ); + + bool contains(int uid)const; + void invalidate(); + void setSize( int size ); + + T find(int uid )const; + void add( const T& ); + void remove( int uid ); + void replace( const T& ); + +private: + QIntCache m_cache; + OPimCachePrivate* d; +}; + +// Implementation +template +OPimCacheItem::OPimCacheItem( const T& t ) + : m_t(t) { +} +template +OPimCacheItem::~OPimCacheItem() { + +} +template +T OPimCacheItem::record()const { + return m_t; +} +template +void OPimCacheItem::setRecord( const T& t ) { + m_t = t; +} +// Cache +template +OPimCache::OPimCache() + : m_cache(100, 53 ) +{ + m_cache.setAutoDelete( TRUE ); +} +template +OPimCache::~OPimCache() { + +} +template +bool OPimCache::contains(int uid )const { + Item* it = m_cache.find( uid, FALSE ); + if (!it) + return false; + return true; +} +template +void OPimCache::invalidate() { + m_cache.clear(); +} +template +void OPimCache::setSize( int size ) { + m_cache.setMaxCost( size ); +} +template +T OPimCache::find(int uid )const { + Item *it = m_cache.find( uid ); + if (it) + return it->record(); + return T(); +} +template +void OPimCache::add( const T& t ) { + Item* it = 0l; + it = m_cache.find(t.uid(), FALSE ); + + if (it ) + it->setRecord( t ); + + it = new Item( t ); + if (!m_cache.insert( t.uid(), it ) ) + delete it; +} +template +void OPimCache::remove( int uid ) { + m_cache.remove( uid ); +} +template +void OPimCache::replace( const T& t) { + Item *it = m_cache.find( t.uid() ); + if ( it ) { + it->setRecord( t ); + } +} + +#endif diff --git a/noncore/unsupported/libopie/pim/opimmaintainer.cpp b/noncore/unsupported/libopie/pim/opimmaintainer.cpp new file mode 100644 index 0000000..92cb25a --- a/dev/null +++ b/noncore/unsupported/libopie/pim/opimmaintainer.cpp @@ -0,0 +1,37 @@ +#include "opimmaintainer.h" + +OPimMaintainer::OPimMaintainer( int mode, int uid ) + : m_mode(mode), m_uid(uid ) +{} +OPimMaintainer::~OPimMaintainer() { +} +OPimMaintainer::OPimMaintainer( const OPimMaintainer& main ) { + *this = main; +} +OPimMaintainer &OPimMaintainer::operator=( const OPimMaintainer& main ) { + m_mode = main.m_mode; + m_uid = main.m_uid; + + return *this; +} +bool OPimMaintainer::operator==( const OPimMaintainer& main ) { + if (m_mode != main.m_mode ) return false; + if (m_uid != main.m_uid ) return false; + + return true; +} +bool OPimMaintainer::operator!=( const OPimMaintainer& main ) { + return !(*this == main ); +} +int OPimMaintainer::mode()const { + return m_mode; +} +int OPimMaintainer::uid()const { + return m_uid; +} +void OPimMaintainer::setMode( int mo) { + m_mode = mo; +} +void OPimMaintainer::setUid( int uid ) { + m_uid = uid; +} diff --git a/noncore/unsupported/libopie/pim/opimmaintainer.h b/noncore/unsupported/libopie/pim/opimmaintainer.h new file mode 100644 index 0000000..793d066 --- a/dev/null +++ b/noncore/unsupported/libopie/pim/opimmaintainer.h @@ -0,0 +1,40 @@ +#ifndef OPIE_PIM_MAINTAINER_H +#define OPIE_PIM_MAINTAINER_H + +#include + +/** + * Who maintains what? + */ +class OPimMaintainer { +public: + enum Mode { Undefined = -1, + Nothing = 0, + Responsible, + DoneBy, + Coordinating, + }; + OPimMaintainer( int mode = Undefined, int uid = 0); + OPimMaintainer( const OPimMaintainer& ); + ~OPimMaintainer(); + + OPimMaintainer &operator=( const OPimMaintainer& ); + bool operator==( const OPimMaintainer& ); + bool operator!=( const OPimMaintainer& ); + + + int mode()const; + int uid()const; + + void setMode( int mode ); + void setUid( int uid ); + +private: + int m_mode; + int m_uid; + class Private; + Private *d; + +}; + +#endif diff --git a/noncore/unsupported/libopie/pim/opimmainwindow.cpp b/noncore/unsupported/libopie/pim/opimmainwindow.cpp new file mode 100644 index 0000000..99a0333 --- a/dev/null +++ b/noncore/unsupported/libopie/pim/opimmainwindow.cpp @@ -0,0 +1,150 @@ +#include +#include +#include + +#include +#include +#include + +#include "opimresolver.h" +#include "opimmainwindow.h" + +OPimMainWindow::OPimMainWindow( const QString& service, QWidget* parent, + const char* name, WFlags flag ) + : QMainWindow( parent, name, flag ), m_rtti(-1), m_service( service ), m_fallBack(0l) { + + /* + * let's generate our QCopChannel + */ + m_str = QString("QPE/"+m_service).local8Bit(); + m_channel= new QCopChannel(m_str, this ); + connect(m_channel, SIGNAL(received(const QCString&,const QByteArray&) ), + this, SLOT( appMessage(const QCString&,const QByteArray&) ) ); + connect(qApp, SIGNAL(appMessage(const QCString&,const QByteArray&) ), + this, SLOT( appMessage(const QCString&,const QByteArray&) ) ); + + /* connect flush and reload */ + connect(qApp, SIGNAL(flush() ), + this, SLOT(flush() ) ); + connect(qApp, SIGNAL(reload() ), + this, SLOT(reload() ) ); +} +OPimMainWindow::~OPimMainWindow() { + delete m_channel; +} +QCopChannel* OPimMainWindow::channel() { + return m_channel; +} +void OPimMainWindow::doSetDocument( const QString& ) { + +} +void OPimMainWindow::appMessage( const QCString& cmd, const QByteArray& array ) { + bool needShow = false; + /* + * create demands to create + * a new record... + */ + QDataStream stream(array, IO_ReadOnly); + if ( cmd == "create()" ) { + raise(); + int uid = create(); + QCopEnvelope e(m_str, "created(int)" ); + e << uid; + needShow = true; + }else if ( cmd == "remove(int)" ) { + int uid; + stream >> uid; + bool rem = remove( uid ); + QCopEnvelope e(m_str, "removed(bool)" ); + e << rem; + needShow = true; + }else if ( cmd == "beam(int)" ) { + int uid; + stream >> uid; + beam( uid); + }else if ( cmd == "show(int)" ) { + raise(); + int uid; + stream >> uid; + show( uid ); + needShow = true; + }else if ( cmd == "edit(int)" ) { + raise(); + int uid; + stream >> uid; + edit( uid ); + }else if ( cmd == "add(int,QByteArray)" ) { + int rtti; + QByteArray array; + stream >> rtti; + stream >> array; + m_fallBack = record(rtti, array ); + if (!m_fallBack) return; + add( *m_fallBack ); + delete m_fallBack; + }else if ( cmd == "alarm(QDateTime,int)" ) { + raise(); + QDateTime dt; int uid; + stream >> dt; + stream >> uid; + qWarning(" Date: %s Uid: %d", dt.toString().latin1(), uid ); + QDateTime current = QDateTime::currentDateTime(); + if ( current.time().hour() != dt.time().hour() && current.time().minute() != dt.time().minute() ) + return; + doAlarm( dt, uid ); + needShow = true; + } + + if (needShow ) + QPEApplication::setKeepRunning(); +} +/* implement the url scripting here */ +void OPimMainWindow::setDocument( const QString& str) { + doSetDocument( str ); +} +/* + * we now try to get the array demarshalled + * check if the rtti matches this one + */ +OPimRecord* OPimMainWindow::record( int rtti, const QByteArray& array ) { + if ( service() != rtti ) + return 0l; + + OPimRecord* record = OPimResolver::self()->record( rtti ); + QDataStream str(array, IO_ReadOnly ); + if ( !record || !record->loadFromStream(str) ) { + delete record; + record = 0l; + } + + return record; +} +/* + * get the rtti for the service + */ +int OPimMainWindow::service() { + if ( m_rtti == -1 ) + m_rtti = OPimResolver::self()->serviceId( m_service ); + + return m_rtti; +} +void OPimMainWindow::doAlarm( const QDateTime&, int ) { + +} +void OPimMainWindow::startAlarm(int count ) { + m_alarmCount = count; + m_playedCount = 0; + Sound::soundAlarm(); + m_timerId = startTimer( 5000 ); +} +void OPimMainWindow::killAlarm() { + killTimer( m_timerId ); +} +void OPimMainWindow::timerEvent( QTimerEvent* e) { + if ( m_playedCount timerId() ); + } +} diff --git a/noncore/unsupported/libopie/pim/opimmainwindow.h b/noncore/unsupported/libopie/pim/opimmainwindow.h new file mode 100644 index 0000000..855d364 --- a/dev/null +++ b/noncore/unsupported/libopie/pim/opimmainwindow.h @@ -0,0 +1,99 @@ +#ifndef OPIE_PIM_MAINWINDOW_H +#define OPIE_PIM_MAINWINDOW_H + +#include + +#include + +/** + * This is a common Opie PIM MainWindow + * it takes care of the QCOP internals + * and implements some functions + * for the URL scripting schema + */ +/* + * due Qt and Templates with signal and slots + * do not work that good :( + * (Ok how to moc a template ;) ) + * We will have the mainwindow which calls a struct which + * is normally reimplemented as a template ;) + */ + +class QCopChannel; +class QDateTime; +class OPimMainWindow : public QMainWindow { + Q_OBJECT +public: + enum TransPort { BlueTooth=0, + IrDa }; + + OPimMainWindow( const QString& service, QWidget *parent = 0, const char* name = 0, + WFlags f = WType_TopLevel); + virtual ~OPimMainWindow(); + + +protected slots: + /* + * called when a setDocument + * couldn't be handled by this window + */ + virtual void doSetDocument( const QString& ); + /* for syncing */ + virtual void flush() = 0; + virtual void reload() = 0; + + /** create a new Records and return the uid */ + virtual int create() = 0; + /** remove a record with UID == uid */ + virtual bool remove( int uid ) = 0; + /** beam the record with UID = uid */ + virtual void beam( int uid ) = 0; + + /** show the record with UID == uid */ + virtual void show( int uid ) = 0; + /** edit the record */ + virtual void edit( int uid ) = 0; + + /** make a copy of it! */ + virtual void add( const OPimRecord& ) = 0; + + virtual void doAlarm( const QDateTime&, int uid ); + + QCopChannel* channel(); + +protected: + /** + * start to play soundAlarm() + * @param count How many times the alarm is played + */ + void startAlarm(int count = 10); + void killAlarm(); + void timerEvent( QTimerEvent* ); + +private slots: + void appMessage( const QCString&, const QByteArray& ); + void setDocument( const QString& ); + + +private: + class Private; + Private* d; + + int m_rtti; + QCopChannel* m_channel; + QString m_service; + QCString m_str; + OPimRecord* m_fallBack; + int m_alarmCount; + int m_playedCount; + int m_timerId; + /* I would love to do this as a template + * but can't think of a right way + * because I need signal and slots -zecke + */ + virtual OPimRecord* record( int rtti, const QByteArray& ) ; + int service(); +}; + + +#endif diff --git a/noncore/unsupported/libopie/pim/opimnotify.cpp b/noncore/unsupported/libopie/pim/opimnotify.cpp new file mode 100644 index 0000000..af5514b --- a/dev/null +++ b/noncore/unsupported/libopie/pim/opimnotify.cpp @@ -0,0 +1,227 @@ +#include + +#include "opimnotify.h" + +struct OPimNotify::Data : public QShared { + Data() : QShared(),dur(-1),parent(0) { + + } + QDateTime start; + int dur; + QString application; + int parent; +}; + +OPimNotify::OPimNotify( const QDateTime& start, int duration, int parent ) { + data = new Data; + data->start = start; + data->dur = duration; + data->parent = parent; +} +OPimNotify::OPimNotify( const OPimNotify& noti) + : data( noti.data ) +{ + data->ref(); +} +OPimNotify::~OPimNotify() { + if ( data->deref() ) { + delete data; + data = 0l; + } +} + +OPimNotify &OPimNotify::operator=( const OPimNotify& noti) { + noti.data->ref(); + deref(); + data = noti.data; + + return *this; +} +bool OPimNotify::operator==( const OPimNotify& noti ) { + if ( data == noti.data ) return true; + if ( data->dur != noti.data->dur ) return false; + if ( data->parent != noti.data->parent ) return false; + if ( data->application != noti.data->application ) return false; + if ( data->start != noti.data->start ) return false; + + return true; +} +QDateTime OPimNotify::dateTime()const { + return data->start; +} +QString OPimNotify::service()const { + return data->application; +} +int OPimNotify::parent()const { + return data->parent; +} +int OPimNotify::duration()const { + return data->dur; +} +QDateTime OPimNotify::endTime()const { + return QDateTime( data->start.date(), data->start.time().addSecs( data->dur) ); +} +void OPimNotify::setDateTime( const QDateTime& time ) { + copyIntern(); + data->start = time; +} +void OPimNotify::setDuration( int dur ) { + copyIntern(); + data->dur = dur; +} +void OPimNotify::setParent( int uid ) { + copyIntern(); + data->parent = uid; +} +void OPimNotify::setService( const QString& str ) { + copyIntern(); + data->application = str; +} +void OPimNotify::copyIntern() { + if ( data->count != 1 ) { + data->deref(); + Data* dat = new Data; + dat->start = data->start; + dat->dur = data->dur; + dat->application = data->application; + dat->parent = data->parent; + data = dat; + } +} +void OPimNotify::deref() { + if ( data->deref() ) { + delete data; + data = 0; + } +} + +/***********************************************************/ +struct OPimAlarm::Data : public QShared { + Data() : QShared() { + sound = 1; + } + int sound; + QString file; +}; +OPimAlarm::OPimAlarm( int sound, const QDateTime& start, int duration, int parent ) + : OPimNotify( start, duration, parent ) +{ + data = new Data; + data->sound = sound; +} +OPimAlarm::OPimAlarm( const OPimAlarm& al) + : OPimNotify(al), data( al.data ) +{ + data->ref(); +} +OPimAlarm::~OPimAlarm() { + if ( data->deref() ) { + delete data; + data = 0l; + } +} +OPimAlarm &OPimAlarm::operator=( const OPimAlarm& al) +{ + OPimNotify::operator=( al ); + deref(); + al.data->ref(); + + data = al.data; + + + return *this; +} +bool OPimAlarm::operator==( const OPimAlarm& al) { + if ( data->sound != al.data->sound ) return false; + else if ( data->sound == Custom && data->file != al.data->file ) + return false; + + return OPimNotify::operator==( al ); +} +QString OPimAlarm::type()const { + return QString::fromLatin1("OPimAlarm"); +} +int OPimAlarm::sound()const { + return data->sound; +} +QString OPimAlarm::file()const { + return data->file; +} +void OPimAlarm::setSound( int snd) { + copyIntern(); + data->sound = snd; +} +void OPimAlarm::setFile( const QString& sound ) { + copyIntern(); + data->file = sound; +} +void OPimAlarm::deref() { + if ( data->deref() ) { + delete data; + data = 0l; + } +} +void OPimAlarm::copyIntern() { + if ( data->count != 1 ) { + data->deref(); + Data *newDat = new Data; + newDat->sound = data->sound; + newDat->file = data->file; + data = newDat; + } +} +/************************/ +struct OPimReminder::Data : public QShared { + Data() : QShared(), record( 0) { + } + int record; + +}; +OPimReminder::OPimReminder( int uid, const QDateTime& start, int dur, int parent ) + : OPimNotify( start, dur, parent ) +{ + data = new Data; + data->record = uid; +} +OPimReminder::OPimReminder( const OPimReminder& rem ) + : OPimNotify( rem ), data( rem.data ) +{ + data->ref(); +} +OPimReminder& OPimReminder::operator=( const OPimReminder& rem) { + OPimNotify::operator=(rem ); + + deref(); + rem.data->ref(); + data = rem.data; + + return *this; +} +bool OPimReminder::operator==( const OPimReminder& rem) { + if ( data->record != rem.data->record ) return false; + + return OPimNotify::operator==( rem ); +} +QString OPimReminder::type()const { + return QString::fromLatin1("OPimReminder"); +} +int OPimReminder::recordUid()const { + return data->record; +} +void OPimReminder::setRecordUid( int uid ) { + copyIntern(); + data->record = uid; +} +void OPimReminder::deref() { + if ( data->deref() ) { + delete data; + data = 0l; + } +} +void OPimReminder::copyIntern() { + if ( data->count != 1 ) { + Data* da = new Data; + da->record = data->record; + data = da; + } +} diff --git a/noncore/unsupported/libopie/pim/opimnotify.h b/noncore/unsupported/libopie/pim/opimnotify.h new file mode 100644 index 0000000..58417db --- a/dev/null +++ b/noncore/unsupported/libopie/pim/opimnotify.h @@ -0,0 +1,144 @@ +#ifndef OPIE_PIM_NOTIFY_H +#define OPIE_PIM_NOTIFY_H + +#include +#include + +/** + * This is the base class of Notifiers. Possible + * notifiers would be Alarms, Reminders + * What they share is that they have + * A DateTime, Type, Duration + * This is what this base class takes care of + * on top of that it's shared + */ +/* + * TALK to eilers: have a class OPimDuration which sets the Duration + * given on the Due/Start Date? -zecke + * discuss: do we need a uid for the notify? -zecke + */ +class OPimNotify { +public: + typedef QValueList ValueList; + OPimNotify( const QDateTime& start = QDateTime(), int duration = 0, int parent = 0 ); + OPimNotify( const OPimNotify& ); + virtual ~OPimNotify(); + + OPimNotify &operator=(const OPimNotify& ); + bool operator==( const OPimNotify& ); + + virtual QString type()const = 0; + + /** start date */ + QDateTime dateTime()const; + QString service()const; + + /** + * RETURN the parent uid + */ + int parent()const; + + /** + * in Seconds + */ + int duration()const; + + /** + * Start Time + Duration + */ + QDateTime endTime()const; + + void setDateTime( const QDateTime& ); + void setDuration( int dur ); + void setParent(int uid ); + void setService( const QString& ); + + +private: + inline void copyIntern(); + void deref(); + struct Data; + Data* data; + + /* d-pointer */ + class NotifyPrivate; + NotifyPrivate* d; + +}; +/** + * An alarm is a sound/mail/buzzer played/send + * at a given time to inform about + * an Event + */ +class OPimAlarm : public OPimNotify { +public: + enum Sound{Loud=1, Silent=0, Custom=2 }; + OPimAlarm( int sound = Silent, const QDateTime& start = QDateTime(), int duration = 0, int parent = 0 ); + OPimAlarm( const OPimAlarm& ); + ~OPimAlarm(); + + OPimAlarm &operator=( const OPimAlarm& ); + bool operator==( const OPimAlarm& ); + QString type()const; + + int sound()const; + QString file()const; + + void setSound( int ); + /* only when sound is custom... */ + void setFile( const QString& sound ); + +private: + void deref(); + void copyIntern(); + struct Data; + Data * data; + + class Private; + Private* d; + +}; + +/** + * A Reminder will be put into the + * datebook + * Note that the returned dateTime() may be not valid. + * In these cases one must resolve the uid and get the OEvent + */ +class OPimReminder : public OPimNotify { +public: + + /** + * c'tor of a reminder + * @param uid The uid of the Record inside the Datebook + * @param start the StartDate invalid for all day... + * @param duration The duration of the event ( -1 for all day ) + * @param parent The 'parent' record of this reminder + */ + OPimReminder( int uid = 0, const QDateTime& start = QDateTime(), + int duration = 0, int parent = 0 ); + OPimReminder( const OPimReminder& ); + OPimReminder &operator=(const OPimReminder& ); + + QString type()const; + + bool operator==( const OPimReminder& ); + + /** + * the uid of the alarm + * inside the 'datebook' application + */ + int recordUid()const; + void setRecordUid( int uid ); + +private: + void deref(); + void copyIntern(); + + struct Data; + Data* data; + class Private; + Private *d; +}; + +#endif diff --git a/noncore/unsupported/libopie/pim/opimnotifymanager.cpp b/noncore/unsupported/libopie/pim/opimnotifymanager.cpp new file mode 100644 index 0000000..d6f0ead --- a/dev/null +++ b/noncore/unsupported/libopie/pim/opimnotifymanager.cpp @@ -0,0 +1,162 @@ +#include "opimnotifymanager.h" + +#include "oconversion.h" + +#include + +OPimNotifyManager::OPimNotifyManager( const Reminders& rem, const Alarms& al) + : m_rem( rem ), m_al( al ) +{} +OPimNotifyManager::~OPimNotifyManager() { +} +/* use static_cast and type instead of dynamic... */ +void OPimNotifyManager::add( const OPimNotify& noti) { + if ( noti.type() == QString::fromLatin1("OPimReminder") ) { + const OPimReminder& rem = static_cast(noti); + m_rem.append( rem ); + }else if ( noti.type() == QString::fromLatin1("OPimAlarm") ) { + const OPimAlarm& al = static_cast(noti); + m_al.append( al ); + } +} +void OPimNotifyManager::remove( const OPimNotify& noti) { + if ( noti.type() == QString::fromLatin1("OPimReminder") ) { + const OPimReminder& rem = static_cast(noti); + m_rem.remove( rem ); + }else if ( noti.type() == QString::fromLatin1("OPimAlarm") ) { + const OPimAlarm& al = static_cast(noti); + m_al.remove( al ); + } +} +void OPimNotifyManager::replace( const OPimNotify& noti) { + if ( noti.type() == QString::fromLatin1("OPimReminder") ) { + const OPimReminder& rem = static_cast(noti); + m_rem.remove( rem ); + m_rem.append( rem ); + }else if ( noti.type() == QString::fromLatin1("OPimAlarm") ) { + const OPimAlarm& al = static_cast(noti); + m_al.remove( al ); + m_al.append( al ); + } +} +OPimNotifyManager::Reminders OPimNotifyManager::reminders()const { + return m_rem; +} +OPimNotifyManager::Alarms OPimNotifyManager::alarms()const { + return m_al; +} +OPimAlarm OPimNotifyManager::alarmAtDateTime( const QDateTime& when, bool& found ) const { + Alarms::ConstIterator it; + found = true; + + for ( it = m_al.begin(); it != m_al.end(); ++it ){ + if ( (*it).dateTime() == when ) + return (*it); + } + + // Fall through if nothing could be found + found = false; + OPimAlarm empty; + return empty; +} + + +void OPimNotifyManager::setAlarms( const Alarms& al) { + m_al = al; +} +void OPimNotifyManager::setReminders( const Reminders& rem) { + m_rem = rem; +} +/* FIXME!!! */ +/** + * The idea is to check if the provider for our service + * is online + * if it is we will use QCOP + * if not the Factory to get the backend... + * Qtopia1.6 services would be kewl to have here.... + */ +void OPimNotifyManager::registerNotify( const OPimNotify& ) { + +} +/* FIXME!!! */ +/** + * same as above... + * Also implement Url model + * have a MainWindow.... + */ +void OPimNotifyManager::deregister( const OPimNotify& ) { + +} + +bool OPimNotifyManager::isEmpty()const { + qWarning("is Empty called on OPimNotifyManager %d %d", m_rem.count(), m_al.count() ); + if ( m_rem.isEmpty() && m_al.isEmpty() ) return true; + else return false; +} + +// Taken from otodoaccessxml.. +QString OPimNotifyManager::alarmsToString() const +{ + QString str; + + OPimNotifyManager::Alarms alarms = m_al; + if ( !alarms.isEmpty() ) { + QStringList als; + OPimNotifyManager::Alarms::Iterator it = alarms.begin(); + for ( ; it != alarms.end(); ++it ) { + /* only if time is valid */ + if ( (*it).dateTime().isValid() ) { + als << OConversion::dateTimeToString( (*it).dateTime() ) + + ":" + QString::number( (*it).duration() ) + + ":" + QString::number( (*it).sound() ) + + ":"; + } + } + // now write the list + qWarning("als: %s", als.join("____________").latin1() ); + str = als.join(";"); + } + + return str; +} +QString OPimNotifyManager::remindersToString() const +{ + QString str; + + OPimNotifyManager::Reminders reminders = m_rem; + if (!reminders.isEmpty() ) { + OPimNotifyManager::Reminders::Iterator it = reminders.begin(); + QStringList records; + for ( ; it != reminders.end(); ++it ) { + records << QString::number( (*it).recordUid() ); + } + str = records.join(";"); + } + + return str; +} + +void OPimNotifyManager::alarmsFromString( const QString& str ) +{ + QStringList als = QStringList::split(";", str ); + for (QStringList::Iterator it = als.begin(); it != als.end(); ++it ) { + QStringList alarm = QStringList::split(":", (*it), TRUE ); // allow empty + qWarning("alarm: %s", alarm.join("___").latin1() ); + qWarning("alarm[0]: %s %s", alarm[0].latin1(), + OConversion::dateTimeFromString( alarm[0] ).toString().latin1() ); + OPimAlarm al( alarm[2].toInt(), OConversion::dateTimeFromString( alarm[0] ), + alarm[1].toInt() ); + add( al ); + } +} + +void OPimNotifyManager::remindersFromString( const QString& str ) +{ + + QStringList rems = QStringList::split(";", str ); + for (QStringList::Iterator it = rems.begin(); it != rems.end(); ++it ) { + OPimReminder rem( (*it).toInt() ); + add( rem ); + } + +} diff --git a/noncore/unsupported/libopie/pim/opimnotifymanager.h b/noncore/unsupported/libopie/pim/opimnotifymanager.h new file mode 100644 index 0000000..f3c22f9 --- a/dev/null +++ b/noncore/unsupported/libopie/pim/opimnotifymanager.h @@ -0,0 +1,91 @@ +#ifndef OPIE_PIM_NOTIFY_MANAGER_H +#define OPIE_PIM_NOTIFY_MANAGER_H + +#include + +#include + +/** + * The notify manager keeps track of the Notifiers.... + */ +class OPimNotifyManager { +public: + typedef QValueList Reminders; + typedef QValueList Alarms; + OPimNotifyManager( const Reminders& rems = Reminders(), const Alarms& alarms = Alarms() ); + ~OPimNotifyManager(); + + /* we will cast it for you ;) */ + void add( const OPimNotify& ); + void remove( const OPimNotify& ); + /* replaces all with this one! */ + void replace( const OPimNotify& ); + + Reminders reminders()const; + + /** + * Return + */ + Alarms alarms()const; + + /** + * Return alarm at DateTime "when". If more than one is registered at this + * DateTime, the first one is returned. + * If none was found, an empty Alarm is returned. + * @param when The date and time of the returned alarm + * @param found Returns true if anything was found. + * @return Returns the found alarm at given DateTime. It is empty if found is false + * (nothing could be found at given date and time) + */ + OPimAlarm alarmAtDateTime( const QDateTime& when, bool& found ) const; + + void setAlarms( const Alarms& ); + void setReminders( const Reminders& ); + + /* register is a Ansi C keyword... */ + /** + * This function will register the Notify to the Alarm Server + * or datebook depending on the type of the notify + */ + void registerNotify( const OPimNotify& ); + + /** + * this will do the opposite.. + */ + void deregister( const OPimNotify& ); + + bool isEmpty()const; + + /** + * Return all alarms as string + */ + QString alarmsToString() const; + /** + * Return all notifiers as string + */ + QString remindersToString() const; + + /** + * Convert string to alarms + * @param str String created by alarmsToString() + */ + void alarmsFromString( const QString& str ); + + /** + * Convert string to reminders + * @param str String created by remindersToString() + */ + void remindersFromString( const QString& str ); + + + +private: + Reminders m_rem; + Alarms m_al; + + class Private; + Private *d; + +}; + +#endif diff --git a/noncore/unsupported/libopie/pim/opimrecord.cpp b/noncore/unsupported/libopie/pim/opimrecord.cpp new file mode 100644 index 0000000..2365748 --- a/dev/null +++ b/noncore/unsupported/libopie/pim/opimrecord.cpp @@ -0,0 +1,182 @@ +#include + +#include +#include + +#include "opimrecord.h" + +Qtopia::UidGen OPimRecord::m_uidGen( Qtopia::UidGen::Qtopia ); + + +OPimRecord::OPimRecord( int uid ) + : Qtopia::Record() { + + m_lastHit = -1; + setUid( uid ); +} +OPimRecord::~OPimRecord() { +} +OPimRecord::OPimRecord( const OPimRecord& rec ) + : Qtopia::Record( rec ) +{ + (*this) = rec; +} + +OPimRecord &OPimRecord::operator=( const OPimRecord& rec) { + if ( this == &rec ) return *this; + + Qtopia::Record::operator=( rec ); + m_xrefman = rec.m_xrefman; + m_lastHit = rec.m_lastHit; + + return *this; +} +/* + * category names + */ +QStringList OPimRecord::categoryNames( const QString& appname ) const { + QStringList list; + QArray cats = categories(); + Categories catDB; + catDB.load( categoryFileName() ); + + for (uint i = 0; i < cats.count(); i++ ) { + list << catDB.label( appname, cats[i] ); + } + + return list; +} +void OPimRecord::setCategoryNames( const QStringList& ) { + +} +void OPimRecord::addCategoryName( const QString& ) { + Categories catDB; + catDB.load( categoryFileName() ); + + +} +bool OPimRecord::isEmpty()const { + return ( uid() == 0 ); +} +/*QString OPimRecord::crossToString()const { + QString str; + QMap >::ConstIterator it; + for (it = m_relations.begin(); it != m_relations.end(); ++it ) { + QArray id = it.data(); + for ( uint i = 0; i < id.size(); ++i ) { + str += it.key() + "," + QString::number( i ) + ";"; + } + } + str = str.remove( str.length()-1, 1); // strip the ; + //qWarning("IDS " + str ); + + return str; + }*/ +/* if uid = 1 assign a new one */ +void OPimRecord::setUid( int uid ) { + if ( uid == 1) + uid = uidGen().generate(); + + Qtopia::Record::setUid( uid ); +}; +Qtopia::UidGen &OPimRecord::uidGen() { + return m_uidGen; +} +OPimXRefManager &OPimRecord::xrefmanager() { + return m_xrefman; +} +int OPimRecord::rtti(){ + return 0; +} + +/** + * now let's put our data into the stream + */ +/* + * First read UID + * Categories + * XRef + */ +bool OPimRecord::loadFromStream( QDataStream& stream ) { + int Int; + uint UInt; + stream >> Int; + setUid(Int); + + /** Categories */ + stream >> UInt; + QArray array(UInt); + for (uint i = 0; i < UInt; i++ ) { + stream >> array[i]; + } + setCategories( array ); + + /* + * now we do the X-Ref stuff + */ + OPimXRef xref; + stream >> UInt; + for ( uint i = 0; i < UInt; i++ ) { + xref.setPartner( OPimXRef::One, partner( stream ) ); + xref.setPartner( OPimXRef::Two, partner( stream ) ); + m_xrefman.add( xref ); + } + + return true; +} +bool OPimRecord::saveToStream( QDataStream& stream )const { + /** UIDs */ + + stream << uid(); + + /** Categories */ + stream << categories().count(); + for ( uint i = 0; i < categories().count(); i++ ) { + stream << categories()[i]; + } + + /* + * first the XRef count + * then the xrefs + */ + stream << m_xrefman.list().count(); + for ( OPimXRef::ValueList::ConstIterator it = m_xrefman.list().begin(); + it != m_xrefman.list().end(); ++it ) { + flush( (*it).partner( OPimXRef::One), stream ); + flush( (*it).partner( OPimXRef::Two), stream ); + } + return true; +} +void OPimRecord::flush( const OPimXRefPartner& par, QDataStream& str ) const{ + str << par.service(); + str << par.uid(); + str << par.field(); +} +OPimXRefPartner OPimRecord::partner( QDataStream& stream ) { + OPimXRefPartner par; + QString str; + int i; + + stream >> str; + par.setService( str ); + + stream >> i; + par.setUid( i ); + + stream >> i ; + par.setField( i ); + + return par; +} +void OPimRecord::setLastHitField( int lastHit )const { + m_lastHit = lastHit; +} +int OPimRecord::lastHitField()const{ + return m_lastHit; +} +QMap OPimRecord::toExtraMap()const { + return customMap; +} +void OPimRecord::setExtraMap( const QMap& map) { + customMap = map; +} diff --git a/noncore/unsupported/libopie/pim/opimrecord.h b/noncore/unsupported/libopie/pim/opimrecord.h new file mode 100644 index 0000000..3d774e2 --- a/dev/null +++ b/noncore/unsupported/libopie/pim/opimrecord.h @@ -0,0 +1,158 @@ +#ifndef OPIE_PIM_RECORD_H +#define OPIE_PIM_RECORD_H + +#include +#include +#include +#include + +/* + * we need to get customMap which is private... + */ +#define private protected +#include +#undef private + +#include + +/** + * This is the base class for + * all PIM Records + * + */ +class OPimRecord : public Qtopia::Record { +public: + /** + * c'tor + * uid of 0 isEmpty + * uid of 1 will be assigned a new one + */ + OPimRecord(int uid = 0); + ~OPimRecord(); + + /** + * copy c'tor + */ + OPimRecord( const OPimRecord& rec ); + + /** + * copy operator + */ + OPimRecord &operator=( const OPimRecord& ); + + /** + * category names resolved + */ + QStringList categoryNames( const QString& appname )const; + + /** + * set category names they will be resolved + */ + void setCategoryNames( const QStringList& ); + + /** + * addCategoryName adds a name + * to the internal category list + */ + void addCategoryName( const QString& ); + + /** + * if a Record isEmpty + * it's empty if it's 0 + */ + virtual bool isEmpty()const; + + /** + * toRichText summary + */ + virtual QString toRichText()const = 0; + + /** + * a small one line summary + */ + virtual QString toShortText()const = 0; + + /** + * the name of the Record + */ + virtual QString type()const = 0; + + /** + * matches the Records the regular expression? + */ + virtual bool match( const QString ®exp ) const + {setLastHitField( -1 ); + return Qtopia::Record::match(QRegExp(regexp));}; + + /** + * if implemented this function returns which item has been + * last hit by the match() function. + * or -1 if not implemented or no hit has occured + */ + int lastHitField()const; + + /** + * converts the internal structure to a map + */ + virtual QMap toMap()const = 0; + // virtual fromMap( const & map ) = 0; // Should be added in the future (eilers) + + /** + * key value representation of extra items + */ + QMap toExtraMap()const; + void setExtraMap( const QMap& ); + + /** + * the name for a recordField + */ + virtual QString recordField(int)const = 0; + + /** + * returns a reference of the + * Cross Reference Manager + * Partner 'One' is THIS PIM RECORD! + * 'Two' is the Partner where we link to + */ + OPimXRefManager& xrefmanager(); + + /** + * set the uid + */ + virtual void setUid( int uid ); + + /* + * used inside the Templates for casting + * REIMPLEMENT in your .... + */ + static int rtti(); + + /** + * some marshalling and de marshalling code + * saves the OPimRecord + * to and from a DataStream + */ + virtual bool loadFromStream(QDataStream& ); + virtual bool saveToStream( QDataStream& stream )const; + +protected: + // need to be const cause it is called from const methods + mutable int m_lastHit; + void setLastHitField( int lastHit )const; + Qtopia::UidGen &uidGen(); +// QString crossToString()const; + +private: + class OPimRecordPrivate; + OPimRecordPrivate *d; + OPimXRefManager m_xrefman; + static Qtopia::UidGen m_uidGen; + +private: + void flush( const OPimXRefPartner&, QDataStream& stream )const; + OPimXRefPartner partner( QDataStream& ); +}; + + + +#endif diff --git a/noncore/unsupported/libopie/pim/opimresolver.cpp b/noncore/unsupported/libopie/pim/opimresolver.cpp new file mode 100644 index 0000000..4ebbd6e --- a/dev/null +++ b/noncore/unsupported/libopie/pim/opimresolver.cpp @@ -0,0 +1,198 @@ +#include + +#include + +#include "otodoaccess.h" +#include "ocontactaccess.h" + +//#include "opimfactory.h" +#include "opimresolver.h" + +OPimResolver* OPimResolver::m_self = 0l; + +OPimResolver::OPimResolver() { + /* the built in channels */ + m_builtIns << "Todolist" << "Addressbook" << "Datebook"; +} +OPimResolver* OPimResolver::self() { + if (!m_self) + m_self = new OPimResolver(); + + return m_self; +} + +/* + * FIXME use a cache here too + */ +OPimRecord* OPimResolver::record( const QString& service, int uid ) { + OPimRecord* rec = 0l; + OPimBase* base = backend( service ); + + if ( base ) + rec = base->record( uid ); + delete base; + + return rec; +} +OPimRecord* OPimResolver::record( const QString& service ) { + return record( serviceId( service ) ); +} +OPimRecord* OPimResolver::record( int rtti ) { + OPimRecord* rec = 0l; + switch( rtti ) { + case 1: /* todolist */ + rec = new OTodo(); + case 2: /* contact */ + rec = new OContact(); + default: + break; + } + /* + * FIXME resolve externally + */ + if (!rec ) { + ; + } + return 0l; +} +bool OPimResolver::isBuiltIn( const QString& str) const{ + return m_builtIns.contains( str ); +} +QCString OPimResolver::qcopChannel( enum BuiltIn& built)const { + QCString str("QPE/"); + switch( built ) { + case TodoList: + str += "Todolist"; + break; + case DateBook: + str += "Datebook"; + break; + case AddressBook: + str += "Addressbook"; + break; + default: + break; + } + + return str; +} +QCString OPimResolver::qcopChannel( const QString& service )const { + QCString str("QPE/"); + str += service.latin1(); + return str; +} +/* + * Implement services!! + * FIXME + */ +QCString OPimResolver::applicationChannel( enum BuiltIn& built)const { + QCString str("QPE/Application/"); + switch( built ) { + case TodoList: + str += "todolist"; + break; + case DateBook: + str += "datebook"; + break; + case AddressBook: + str += "addressbook"; + break; + } + + return str; +} +QCString OPimResolver::applicationChannel( const QString& service )const { + QCString str("QPE/Application/"); + + if ( isBuiltIn( service ) ) { + if ( service == "Todolist" ) + str += "todolist"; + else if ( service == "Datebook" ) + str += "datebook"; + else if ( service == "Addressbook" ) + str += "addressbook"; + }else + ; // FIXME for additional stuff + + return str; +} +QStringList OPimResolver::services()const { + return m_builtIns; +} +QString OPimResolver::serviceName( int rtti ) const{ + QString str; + switch ( rtti ) { + case TodoList: + str = "Todolist"; + break; + case DateBook: + str = "Datebook"; + break; + case AddressBook: + str = "Addressbook"; + break; + default: + break; + } + return str; + // FIXME me for 3rd party +} +int OPimResolver::serviceId( const QString& service ) { + int rtti = 0; + if ( service == "Todolist" ) + rtti = TodoList; + else if ( service == "Datebook" ) + rtti = DateBook; + else if ( service == "Addressbook" ) + rtti = AddressBook; + + return rtti; +} +/** + * check if the 'service' is registered and if so we'll + */ +bool OPimResolver::add( const QString& service, const OPimRecord& rec) { + if ( QCopChannel::isRegistered( applicationChannel( service ) ) ) { + QByteArray data; + QDataStream arg(data, IO_WriteOnly ); + if ( rec.saveToStream( arg ) ) { + QCopEnvelope env( applicationChannel( service ), "add(int,QByteArray)" ); + env << rec.rtti(); + env << data; + }else + return false; + }else{ + OPimBase* base = backend( service ); + if (!base ) return false; + + base->load(); + base->add( rec ); + base->save(); + delete base; + } + + return true; +} +OPimBase* OPimResolver::backend( const QString& service ) { + return backend( serviceId( service ) ); +} +OPimBase* OPimResolver::backend( int rtti ) { + OPimBase* base = 0l; + switch( rtti ) { + case TodoList: + base = new OTodoAccess(); + break; + case DateBook: + break; + case AddressBook: + base = new OContactAccess("Resolver"); + break; + default: + break; + } + // FIXME for 3rd party + if (!base ) + ; + + return base; +} diff --git a/noncore/unsupported/libopie/pim/opimresolver.h b/noncore/unsupported/libopie/pim/opimresolver.h new file mode 100644 index 0000000..1ce1619 --- a/dev/null +++ b/noncore/unsupported/libopie/pim/opimresolver.h @@ -0,0 +1,90 @@ +#ifndef OPIE_PIM_RESOLVER +#define OPIE_PIM_RESOLVER + +#include +#include + +#include + +/** + * OPimResolver is a MetaClass to access + * available backends read only. + * It will be used to resolve uids + app names + * to full informations + * to traverse through a list of alarms, reminders + * to get access to built in PIM functionality + * and to more stuff + * THE PERFORMANCE will depend on THE BACKEND + * USING XML is a waste of memory!!!!! + */ +class OPimResolver { +public: + enum BuiltIn { TodoList = 0, + DateBook, + AddressBook + }; + static OPimResolver* self(); + + + /** + * return a record for a uid + * and an service + * You've THE OWNERSHIP NOW! + */ + OPimRecord *record( const QString& service, int uid ); + + /** + * return the QCopChannel for service + * When we will use Qtopia Services it will be used here + */ + QCString qcopChannel( enum BuiltIn& )const; + QCString qcopChannel( const QString& service )const; + + /** + * The Application channel (QPE/Application/name) + */ + QCString applicationChannel( enum BuiltIn& )const; + QCString applicationChannel( const QString& service )const; + + /** + * return a list of available services + */ + QStringList services()const; + inline QString serviceName(int rrti )const; + int serviceId( const QString& Service); + /** + * add a record to a service... ;) + */ + bool add( const QString& service, const OPimRecord& ); + + + /** + * record returns an empty record for a given service. + * Be sure to delete it!!! + * + */ + OPimRecord* record( const QString& service ); + OPimRecord* record( int rtti ); + + /** + * you can cast to your + */ + OPimBase* backend( const QString& service ); + OPimBase* backend( int rtti ); +private: + OPimResolver(); + void loadData(); + inline bool isBuiltIn( const QString& )const; + OPimRecord* recordExtern( const QString&, int ); + OPimRecord* recordExtern( const QString& ); + + static OPimResolver* m_self; + struct Data; + class Private; + + Data* data; + Private* d; + QStringList m_builtIns; +}; + +#endif diff --git a/noncore/unsupported/libopie/pim/opimstate.cpp b/noncore/unsupported/libopie/pim/opimstate.cpp new file mode 100644 index 0000000..6fb2feb --- a/dev/null +++ b/noncore/unsupported/libopie/pim/opimstate.cpp @@ -0,0 +1,64 @@ +#include + +#include "opimstate.h" + +/* + * for one int this does not make + * much sense but never the less + * we will do it for the future + */ +struct OPimState::Data : public QShared { + Data() : QShared(),state(Undefined) { + } + int state; +}; + +OPimState::OPimState( int state ) { + data = new Data; + data->state = state; +} +OPimState::OPimState( const OPimState& st) : + data( st.data ) { + /* ref up */ + data->ref(); +} +OPimState::~OPimState() { + if ( data->deref() ) { + delete data ; + data = 0; + } +} +bool OPimState::operator==( const OPimState& st) { + if ( data->state == st.data->state ) return true; + + return false; +} +OPimState &OPimState::operator=( const OPimState& st) { + st.data->ref(); + deref(); + data = st.data; + + return *this; +} +void OPimState::setState( int st) { + copyInternally(); + data->state = st; +} +int OPimState::state()const { + return data->state; +} +void OPimState::deref() { + if ( data->deref() ) { + delete data; + data = 0l; + } +} +void OPimState::copyInternally() { + /* we need to change it */ + if ( data->count != 1 ) { + data->deref(); + Data* d2 = new Data; + d2->state = data->state; + data = d2; + } +} diff --git a/noncore/unsupported/libopie/pim/opimstate.h b/noncore/unsupported/libopie/pim/opimstate.h new file mode 100644 index 0000000..cf6af46 --- a/dev/null +++ b/noncore/unsupported/libopie/pim/opimstate.h @@ -0,0 +1,46 @@ +#ifndef OPIE_PIM_STATE_H +#define OPIE_PIM_STATE_H + +#include + +/** + * The State of a Task + * This class encapsules the state of a todo + * and it's shared too + */ +/* + * in c a simple struct would be enough ;) + * g_new_state(); + * g_do_some_thing( state_t* ); + * ;) + */ +class OPimState { +public: + enum State { + Started = 0, + Postponed, + Finished, + NotStarted, + Undefined + }; + OPimState( int state = Undefined ); + OPimState( const OPimState& ); + ~OPimState(); + + bool operator==( const OPimState& ); + OPimState &operator=( const OPimState& ); + void setState( int state); + int state()const; +private: + void deref(); + inline void copyInternally(); + + struct Data; + Data* data; + + class Private; + Private *d; +}; + + +#endif diff --git a/noncore/unsupported/libopie/pim/opimxref.cpp b/noncore/unsupported/libopie/pim/opimxref.cpp new file mode 100644 index 0000000..8eefbd8 --- a/dev/null +++ b/noncore/unsupported/libopie/pim/opimxref.cpp @@ -0,0 +1,47 @@ +#include "opimxref.h" + +OPimXRef::OPimXRef( const OPimXRefPartner& one, const OPimXRefPartner& two ) + : m_partners(2) +{ + m_partners[0] = one; + m_partners[1] = two; +} +OPimXRef::OPimXRef() + : m_partners(2) +{ + +} +OPimXRef::OPimXRef( const OPimXRef& ref) { + *this = ref; +} +OPimXRef::~OPimXRef() { +} +OPimXRef &OPimXRef::operator=( const OPimXRef& ref) { + m_partners = ref.m_partners; + m_partners.detach(); + + return* this; +} +bool OPimXRef::operator==( const OPimXRef& oper ) { + if ( m_partners == oper.m_partners ) return true; + + return false; +} +OPimXRefPartner OPimXRef::partner( enum Partners par) const{ + return m_partners[par]; +} +void OPimXRef::setPartner( enum Partners par, const OPimXRefPartner& part) { + m_partners[par] = part; +} +bool OPimXRef::containsString( const QString& string ) const{ + if ( m_partners[One].service() == string || + m_partners[Two].service() == string ) return true; + + return false; +} +bool OPimXRef::containsUid( int uid ) const{ + if ( m_partners[One].uid() == uid || + m_partners[Two].uid() == uid ) return true; + + return false; +} diff --git a/noncore/unsupported/libopie/pim/opimxref.h b/noncore/unsupported/libopie/pim/opimxref.h new file mode 100644 index 0000000..6852651 --- a/dev/null +++ b/noncore/unsupported/libopie/pim/opimxref.h @@ -0,0 +1,39 @@ +#ifndef OPIM_XREF_H +#define OPIM_XREF_H + +#include +#include + +#include + +/** + * this is a Cross Referecne between + * two Cross Reference Partners + */ +class OPimXRef { +public: + typedef QValueList ValueList; + enum Partners { One, Two }; + OPimXRef( const OPimXRefPartner& ONE, const OPimXRefPartner& ); + OPimXRef(); + OPimXRef( const OPimXRef& ); + ~OPimXRef(); + + OPimXRef &operator=( const OPimXRef& ); + bool operator==( const OPimXRef& ); + + OPimXRefPartner partner( enum Partners )const; + + void setPartner( enum Partners, const OPimXRefPartner& ); + + bool containsString( const QString& service)const; + bool containsUid( int uid )const; + +private: + QArray m_partners; + + class Private; + Private *d; +}; + +#endif diff --git a/noncore/unsupported/libopie/pim/opimxrefmanager.cpp b/noncore/unsupported/libopie/pim/opimxrefmanager.cpp new file mode 100644 index 0000000..58bfd24 --- a/dev/null +++ b/noncore/unsupported/libopie/pim/opimxrefmanager.cpp @@ -0,0 +1,71 @@ +#include "opimxrefmanager.h" + + +OPimXRefManager::OPimXRefManager() { +} +OPimXRefManager::OPimXRefManager( const OPimXRefManager& ref) { + m_list = ref.m_list; +} +OPimXRefManager::~OPimXRefManager() { +} +OPimXRefManager &OPimXRefManager::operator=( const OPimXRefManager& ref) { + m_list = ref.m_list; + return *this; +} +bool OPimXRefManager::operator==( const OPimXRefManager& /*ref*/) { + // if ( m_list == ref.m_list ) return true; + + return false; +} +void OPimXRefManager::add( const OPimXRef& ref) { + m_list.append( ref ); +} +void OPimXRefManager::remove( const OPimXRef& ref) { + m_list.remove( ref ); +} +void OPimXRefManager::replace( const OPimXRef& ref) { + m_list.remove( ref ); + m_list.append( ref ); +} +void OPimXRefManager::clear() { + m_list.clear(); +} +QStringList OPimXRefManager::apps()const { + OPimXRef::ValueList::ConstIterator it; + QStringList list; + + QString str; + for ( it = m_list.begin(); it != m_list.end(); ++it ) { + str = (*it).partner( OPimXRef::One ).service(); + if ( !list.contains( str ) ) list << str; + + str = (*it).partner( OPimXRef::Two ).service(); + if ( !list.contains( str ) ) list << str; + } + return list; +} +OPimXRef::ValueList OPimXRefManager::list()const { + return m_list; +} +OPimXRef::ValueList OPimXRefManager::list( const QString& appName )const{ + OPimXRef::ValueList list; + OPimXRef::ValueList::ConstIterator it; + + for ( it = m_list.begin(); it != m_list.end(); ++it ) { + if ( (*it).containsString( appName ) ) + list.append( (*it) ); + } + + return list; +} +OPimXRef::ValueList OPimXRefManager::list( int uid )const { + OPimXRef::ValueList list; + OPimXRef::ValueList::ConstIterator it; + + for ( it = m_list.begin(); it != m_list.end(); ++it ) { + if ( (*it).containsUid( uid ) ) + list.append( (*it) ); + } + + return list; +} diff --git a/noncore/unsupported/libopie/pim/opimxrefmanager.h b/noncore/unsupported/libopie/pim/opimxrefmanager.h new file mode 100644 index 0000000..c485e98 --- a/dev/null +++ b/noncore/unsupported/libopie/pim/opimxrefmanager.h @@ -0,0 +1,43 @@ +#ifndef OPIM_XREF_MANAGER_H +#define OPIM_XREF_MANAGER_H + +#include + +#include + +/** + * This is a simple manager for + * OPimXRefs. + * It allows addition, removing, replacing + * clearing and 'querying' the XRef... + */ +class OPimXRefManager { +public: + OPimXRefManager(); + OPimXRefManager( const OPimXRefManager& ); + ~OPimXRefManager(); + + OPimXRefManager& operator=( const OPimXRefManager& ); + bool operator==( const OPimXRefManager& ); + + void add( const OPimXRef& ); + void remove( const OPimXRef& ); + void replace( const OPimXRef& ); + + void clear(); + + /** + * apps participating + */ + QStringList apps()const; + OPimXRef::ValueList list()const; + OPimXRef::ValueList list( const QString& service )const; + OPimXRef::ValueList list( int uid )const; + +private: + OPimXRef::ValueList m_list; + class Private; + Private *d; +}; + +#endif diff --git a/noncore/unsupported/libopie/pim/opimxrefpartner.cpp b/noncore/unsupported/libopie/pim/opimxrefpartner.cpp new file mode 100644 index 0000000..6ef3efb --- a/dev/null +++ b/noncore/unsupported/libopie/pim/opimxrefpartner.cpp @@ -0,0 +1,43 @@ +#include "opimxrefpartner.h" + +OPimXRefPartner::OPimXRefPartner( const QString& appName, + int uid, int field ) + : m_app(appName), m_uid(uid), m_field( field ) { +} +OPimXRefPartner::OPimXRefPartner( const OPimXRefPartner& ref ) { + *this = ref; +} +OPimXRefPartner::~OPimXRefPartner() { +} +OPimXRefPartner &OPimXRefPartner::operator=( const OPimXRefPartner& par ) { + m_app = par.m_app; + m_uid = par.m_uid; + m_field = par.m_field; + + return *this; +} +bool OPimXRefPartner::operator==( const OPimXRefPartner& par ) { + if ( m_app != par.m_app ) return false; + if ( m_uid != par.m_uid ) return false; + if ( m_field != par.m_field ) return false; + + return true; +} +QString OPimXRefPartner::service()const { + return m_app; +} +int OPimXRefPartner::uid()const { + return m_uid; +} +int OPimXRefPartner::field()const { + return m_field; +} +void OPimXRefPartner::setService( const QString& appName ) { + m_app = appName; +} +void OPimXRefPartner::setUid( int uid ) { + m_uid = uid; +} +void OPimXRefPartner::setField( int field ) { + m_field = field; +} diff --git a/noncore/unsupported/libopie/pim/opimxrefpartner.h b/noncore/unsupported/libopie/pim/opimxrefpartner.h new file mode 100644 index 0000000..d76e384 --- a/dev/null +++ b/noncore/unsupported/libopie/pim/opimxrefpartner.h @@ -0,0 +1,40 @@ +#ifndef OPIM_XREF_PARTNER_H +#define OPIM_XREF_PARTNER_H + +#include + +/** + * This class represents one partner + * of a Cross Reference. + * In Opie one application + * can link one uid + * with one tableId( fieldId ) to another. + */ +class OPimXRefPartner { +public: + OPimXRefPartner( const QString& service = QString::null, + int uid = 0, int field = -1 ); + OPimXRefPartner( const OPimXRefPartner& ); + OPimXRefPartner& operator=( const OPimXRefPartner& ); + ~OPimXRefPartner(); + + bool operator==(const OPimXRefPartner& ); + + QString service()const; + int uid()const; + int field()const; + + void setService( const QString& service ); + void setUid( int uid ); + void setField( int field ); +private: + QString m_app; + int m_uid; + int m_field; + + class Private; + Private* d; +}; + + +#endif diff --git a/noncore/unsupported/libopie/pim/orecordlist.h b/noncore/unsupported/libopie/pim/orecordlist.h new file mode 100644 index 0000000..5211f57 --- a/dev/null +++ b/noncore/unsupported/libopie/pim/orecordlist.h @@ -0,0 +1,306 @@ + +#ifndef OPIE_RECORD_LIST_H +#define OPIE_RECORD_LIST_H + +#include + +#include "otemplatebase.h" +#include "opimrecord.h" + +class ORecordListIteratorPrivate; +/** + * Our List Iterator + * it behaves like STL or Qt + * + * for(it = list.begin(); it != list.end(); ++it ) + * doSomeCoolStuff( (*it) ); + */ +template class ORecordList; +template +class ORecordListIterator { + friend class ORecordList; +public: + typedef OTemplateBase Base; + + /** + * The c'tor used internally from + * ORecordList + */ + ORecordListIterator( const QArray, const Base* ); + + /** + * The standard c'tor + */ + ORecordListIterator(); + ~ORecordListIterator(); + + ORecordListIterator( const ORecordListIterator& ); + ORecordListIterator &operator=(const ORecordListIterator& ); + + /** + * a * operator ;) + * use it like this T = (*it); + */ + T operator*(); + ORecordListIterator &operator++(); + ORecordListIterator &operator--(); + + bool operator==( const ORecordListIterator& it ); + bool operator!=( const ORecordListIterator& it ); + + /** + * the current item + */ + uint current()const; + + /** + * the number of items + */ + uint count()const; + + /** + * sets the current item + */ + void setCurrent( uint cur ); + +private: + QArray m_uids; + uint m_current; + const Base* m_temp; + bool m_end : 1; + T m_record; + bool m_direction :1; + + /* d pointer for future versions */ + ORecordListIteratorPrivate *d; +}; + +class ORecordListPrivate; +/** + * The recordlist used as a return type + * from OPimAccessTemplate + */ +template +class ORecordList { +public: + typedef OTemplateBase Base; + typedef ORecordListIterator Iterator; + + /** + * c'tor + */ + ORecordList () { + } +ORecordList( const QArray& ids, + const Base* ); + ~ORecordList(); + + /** + * the first iterator + */ + Iterator begin(); + + /** + * the end + */ + Iterator end(); + + /** + * the number of items in the list + */ + uint count()const; + + T operator[]( uint i ); + int uidAt(uint i ); + + /** + * Remove the contact with given uid + */ + bool remove( int uid ); + + /* + ConstIterator begin()const; + ConstIterator end()const; + */ +private: + QArray m_ids; + const Base* m_acc; + ORecordListPrivate *d; +}; + +/* ok now implement it */ +template +ORecordListIterator::ORecordListIterator() { + m_current = 0; + m_temp = 0l; + m_end = true; + m_record = T(); + /* forward */ + m_direction = TRUE; +} +template +ORecordListIterator::~ORecordListIterator() { +/* nothing to delete */ +} + +template +ORecordListIterator::ORecordListIterator( const ORecordListIterator& it) { +// qWarning("ORecordListIterator copy c'tor"); + m_uids = it.m_uids; + m_current = it.m_current; + m_temp = it.m_temp; + m_end = it.m_end; + m_record = it.m_record; + m_direction = it.m_direction; +} + +template +ORecordListIterator &ORecordListIterator::operator=( const ORecordListIterator& it) { + m_uids = it.m_uids; + m_current = it.m_current; + m_temp = it.m_temp; + m_end = it.m_end; + m_record = it.m_record; + + return *this; +} + +template +T ORecordListIterator::operator*() { + //qWarning("operator* %d %d", m_current, m_uids[m_current] ); + if (!m_end ) + m_record = m_temp->find( m_uids[m_current], m_uids, m_current, + m_direction ? Base::Forward : + Base::Reverse ); + else + m_record = T(); + + return m_record; +} + +template +ORecordListIterator &ORecordListIterator::operator++() { + m_direction = true; + if (m_current < m_uids.count() ) { + m_end = false; + ++m_current; + }else + m_end = true; + + return *this; +} +template +ORecordListIterator &ORecordListIterator::operator--() { + m_direction = false; + if ( m_current > 0 ) { + --m_current; + m_end = false; + } else + m_end = true; + + return *this; +} + +template +bool ORecordListIterator::operator==( const ORecordListIterator& it ) { + + /* if both are at we're the same.... */ + if ( m_end == it.m_end ) return true; + + if ( m_uids != it.m_uids ) return false; + if ( m_current != it.m_current ) return false; + if ( m_temp != it.m_temp ) return false; + + return true; +} +template +bool ORecordListIterator::operator!=( const ORecordListIterator& it ) { + return !(*this == it ); +} +template +ORecordListIterator::ORecordListIterator( const QArray uids, + const Base* t ) + : m_uids( uids ), m_current( 0 ), m_temp( t ), m_end( false ), + m_direction( false ) +{ + /* if the list is empty we're already at the end of the list */ + if (uids.count() == 0 ) + m_end = true; +} +template +uint ORecordListIterator::current()const { + return m_current; +} +template +void ORecordListIterator::setCurrent( uint cur ) { + if( cur < m_uids.count() ) { + m_end = false; + m_current= cur; + } +} +template +uint ORecordListIterator::count()const { + return m_uids.count(); +} +template +ORecordList::ORecordList( const QArray& ids, + const Base* acc ) + : m_ids( ids ), m_acc( acc ) +{ +} +template +ORecordList::~ORecordList() { +/* nothing to do here */ +} +template +typename ORecordList::Iterator ORecordList::begin() { + Iterator it( m_ids, m_acc ); + return it; +} +template +typename ORecordList::Iterator ORecordList::end() { + Iterator it( m_ids, m_acc ); + it.m_end = true; + it.m_current = m_ids.count(); + + return it; +} +template +uint ORecordList::count()const { +return m_ids.count(); +} +template +T ORecordList::operator[]( uint i ) { + if ( i >= m_ids.count() ) + return T(); + /* forward */ + return m_acc->find( m_ids[i], m_ids, i ); +} +template +int ORecordList::uidAt( uint i ) { + return m_ids[i]; +} + +template +bool ORecordList::remove( int uid ) { + QArray copy( m_ids.count() ); + int counter = 0; + bool ret_val = false; + + for (uint i = 0; i < m_ids.count(); i++){ + if ( m_ids[i] != uid ){ + copy[counter++] = m_ids[i]; + + }else + ret_val = true; + } + + copy.resize( counter ); + m_ids = copy; + + + return ret_val; +} + + +#endif diff --git a/noncore/unsupported/libopie/pim/orecur.cpp b/noncore/unsupported/libopie/pim/orecur.cpp new file mode 100644 index 0000000..f46f22e --- a/dev/null +++ b/noncore/unsupported/libopie/pim/orecur.cpp @@ -0,0 +1,593 @@ +#include + +#include + +#include + +#include "otimezone.h" +#include "orecur.h" + +struct ORecur::Data : public QShared { + Data() : QShared() { + type = ORecur::NoRepeat; + freq = -1; + days = 0; + pos = 0; + create = QDateTime::currentDateTime(); + hasEnd = FALSE; + end = QDate::currentDate(); + } + char days; // Q_UINT8 for 8 seven days;) + ORecur::RepeatType type; + int freq; + int pos; + bool hasEnd : 1; + QDate end; + QDateTime create; + int rep; + QString app; + ExceptionList list; + QDate start; +}; + + +ORecur::ORecur() { + data = new Data; +} + +ORecur::ORecur( const QMap& map ) +{ + ORecur(); + fromMap( map ); +} + + +ORecur::ORecur( const ORecur& rec) + : data( rec.data ) +{ + data->ref(); +} +ORecur::~ORecur() { + if ( data->deref() ) { + delete data; + data = 0l; + } +} +void ORecur::deref() { + if ( data->deref() ) { + delete data; + data = 0l; + } +} +bool ORecur::operator==( const ORecur& )const { + return false; +} +ORecur &ORecur::operator=( const ORecur& re) { + if ( *this == re ) return *this; + + re.data->ref(); + deref(); + data = re.data; + + return *this; +} +bool ORecur::doesRecur()const { + return !( type() == NoRepeat ); +} +/* + * we try to be smart here + * + */ +bool ORecur::doesRecur( const QDate& date ) { + /* the day before the recurrance */ + QDate da = date.addDays(-1); + + QDate recur; + if (!nextOcurrence( da, recur ) ) + return false; + + return (recur == date); +} +// FIXME unuglify! +// GPL from Datebookdb.cpp +// FIXME exception list! +bool ORecur::nextOcurrence( const QDate& from, QDate& next ) { + bool stillLooking; + stillLooking = p_nextOccurrence( from, next ); + while ( stillLooking && data->list.contains(next) ) + stillLooking = p_nextOccurrence( next.addDays(1), next ); + + return stillLooking; +} +bool ORecur::p_nextOccurrence( const QDate& from, QDate& next ) { + + // easy checks, first are we too far in the future or too far in the past? + QDate tmpDate; + int freq = frequency(); + int diff, diff2, a; + int iday, imonth, iyear; + int dayOfWeek = 0; + int firstOfWeek = 0; + int weekOfMonth; + + + if (hasEndDate() && endDate() < from) + return FALSE; + + if (start() >= from ) { + next = start(); + return TRUE; + } + + switch ( type() ) { + case Weekly: + /* weekly is just daily by 7 */ + /* first convert the repeatPattern.Days() mask to the next + day of week valid after from */ + dayOfWeek = from.dayOfWeek(); + dayOfWeek--; /* we want 0-6, doco for above specs 1-7 */ + + /* this is done in case freq > 1 and from in week not + for this round */ + // firstOfWeek = 0; this is already done at decl. + while(!((1 << firstOfWeek) & days() )) + firstOfWeek++; + + /* there is at least one 'day', or there would be no event */ + while(!((1 << (dayOfWeek % 7)) & days() )) + dayOfWeek++; + + dayOfWeek = dayOfWeek % 7; /* the actual day of week */ + dayOfWeek -= start().dayOfWeek() -1; + + firstOfWeek = firstOfWeek % 7; /* the actual first of week */ + firstOfWeek -= start().dayOfWeek() -1; + + // dayOfWeek may be negitive now + // day of week is number of days to add to start day + + freq *= 7; + // FALL-THROUGH !!!!! + case Daily: + // the add is for the possible fall through from weekly */ + if(start().addDays(dayOfWeek) > from) { + /* first week exception */ + next = QDate(start().addDays(dayOfWeek) ); + if ((next > endDate()) + && hasEndDate() ) + return FALSE; + return TRUE; + } + /* if from is middle of a non-week */ + + diff = start().addDays(dayOfWeek).daysTo(from) % freq; + diff2 = start().addDays(firstOfWeek).daysTo(from) % freq; + + if(diff != 0) + diff = freq - diff; + if(diff2 != 0) + diff2 = freq - diff2; + diff = QMIN(diff, diff2); + + next = QDate(from.addDays(diff)); + if ( (next > endDate()) + && hasEndDate() ) + return FALSE; + return TRUE; + case MonthlyDay: + iday = from.day(); + iyear = from.year(); + imonth = from.month(); + /* find equivelent day of month for this month */ + dayOfWeek = start().dayOfWeek(); + weekOfMonth = (start().day() - 1) / 7; + + /* work out when the next valid month is */ + a = from.year() - start().year(); + a *= 12; + a = a + (imonth - start().month()); + /* a is e.start()monthsFrom(from); */ + if(a % freq) { + a = freq - (a % freq); + imonth = from.month() + a; + if (imonth > 12) { + imonth--; + iyear += imonth / 12; + imonth = imonth % 12; + imonth++; + } + } + /* imonth is now the first month after or on + from that matches the frequency given */ + + /* find for this month */ + tmpDate = QDate( iyear, imonth, 1 ); + + iday = 1; + iday += (7 + dayOfWeek - tmpDate.dayOfWeek()) % 7; + iday += 7 * weekOfMonth; + while (iday > tmpDate.daysInMonth()) { + imonth += freq; + if (imonth > 12) { + imonth--; + iyear += imonth / 12; + imonth = imonth % 12; + imonth++; + } + tmpDate = QDate( iyear, imonth, 1 ); + /* these loops could go for a while, check end case now */ + if ((tmpDate > endDate()) && hasEndDate() ) + return FALSE; + iday = 1; + iday += (7 + dayOfWeek - tmpDate.dayOfWeek()) % 7; + iday += 7 * weekOfMonth; + } + tmpDate = QDate(iyear, imonth, iday); + + if (tmpDate >= from) { + next = tmpDate; + if ((next > endDate() ) && hasEndDate() ) + return FALSE; + return TRUE; + } + + /* need to find the next iteration */ + do { + imonth += freq; + if (imonth > 12) { + imonth--; + iyear += imonth / 12; + imonth = imonth % 12; + imonth++; + } + tmpDate = QDate( iyear, imonth, 1 ); + /* these loops could go for a while, check end case now */ + if ((tmpDate > endDate()) && hasEndDate() ) + return FALSE; + iday = 1; + iday += (7 + dayOfWeek - tmpDate.dayOfWeek()) % 7; + iday += 7 * weekOfMonth; + } while (iday > tmpDate.daysInMonth()); + tmpDate = QDate(iyear, imonth, iday); + + next = tmpDate; + if ((next > endDate()) && hasEndDate() ) + return FALSE; + return TRUE; + case MonthlyDate: + iday = start().day(); + iyear = from.year(); + imonth = from.month(); + + a = from.year() - start().year(); + a *= 12; + a = a + (imonth - start().month()); + /* a is e.start()monthsFrom(from); */ + if(a % freq) { + a = freq - (a % freq); + imonth = from.month() + a; + if (imonth > 12) { + imonth--; + iyear += imonth / 12; + imonth = imonth % 12; + imonth++; + } + } + /* imonth is now the first month after or on + from that matches the frequencey given */ + + /* this could go for a while, worse case, 4*12 iterations, probably */ + while(!QDate::isValid(iyear, imonth, iday) ) { + imonth += freq; + if (imonth > 12) { + imonth--; + iyear += imonth / 12; + imonth = imonth % 12; + imonth++; + } + /* these loops could go for a while, check end case now */ + if ((QDate(iyear, imonth, 1) > endDate()) && hasEndDate() ) + return FALSE; + } + + if(QDate(iyear, imonth, iday) >= from) { + /* done */ + next = QDate(iyear, imonth, iday); + if ((next > endDate()) && hasEndDate() ) + return FALSE; + return TRUE; + } + + /* ok, need to cycle */ + imonth += freq; + imonth--; + iyear += imonth / 12; + imonth = imonth % 12; + imonth++; + + while(!QDate::isValid(iyear, imonth, iday) ) { + imonth += freq; + imonth--; + iyear += imonth / 12; + imonth = imonth % 12; + imonth++; + if ((QDate(iyear, imonth, 1) > endDate()) && hasEndDate() ) + return FALSE; + } + + next = QDate(iyear, imonth, iday); + if ((next > endDate()) && hasEndDate() ) + return FALSE; + return TRUE; + case Yearly: + iday = start().day(); + imonth = start().month(); + iyear = from.year(); // after all, we want to start in this year + + diff = 1; + if(imonth == 2 && iday > 28) { + /* leap year, and it counts, calculate actual frequency */ + if(freq % 4) + if (freq % 2) + freq = freq * 4; + else + freq = freq * 2; + /* else divides by 4 already, leave freq alone */ + diff = 4; + } + + a = from.year() - start().year(); + if(a % freq) { + a = freq - (a % freq); + iyear = iyear + a; + } + + /* under the assumption we won't hit one of the special not-leap years twice */ + if(!QDate::isValid(iyear, imonth, iday)) { + /* must have been skipping by leap years and hit one that wasn't, (e.g. 2100) */ + iyear += freq; + } + + if(QDate(iyear, imonth, iday) >= from) { + next = QDate(iyear, imonth, iday); + + if ((next > endDate()) && hasEndDate() ) + return FALSE; + return TRUE; + } + /* iyear == from.year(), need to advance again */ + iyear += freq; + /* under the assumption we won't hit one of the special not-leap years twice */ + if(!QDate::isValid(iyear, imonth, iday)) { + /* must have been skipping by leap years and hit one that wasn't, (e.g. 2100) */ + iyear += freq; + } + + next = QDate(iyear, imonth, iday); + if ((next > endDate()) && hasEndDate() ) + return FALSE; + return TRUE; + default: + return FALSE; + } +} +ORecur::RepeatType ORecur::type()const{ + return data->type; +} +int ORecur::frequency()const { + return data->freq; +} +int ORecur::position()const { + return data->pos; +} +char ORecur::days() const{ + return data->days; +} +bool ORecur::hasEndDate()const { + return data->hasEnd; +} +QDate ORecur::endDate()const { + return data->end; +} +QDate ORecur::start()const{ + return data->start; +} +QDateTime ORecur::createdDateTime()const { + return data->create; +} +int ORecur::repetition()const { + return data->rep; +} +QString ORecur::service()const { + return data->app; +} +ORecur::ExceptionList& ORecur::exceptions() { + return data->list; +} +void ORecur::setType( const RepeatType& z) { + checkOrModify(); + data->type = z; +} +void ORecur::setFrequency( int freq ) { + checkOrModify(); + data->freq = freq; +} +void ORecur::setPosition( int pos ) { + checkOrModify(); + data->pos = pos; +} +void ORecur::setDays( char c ) { + checkOrModify(); + data->days = c; +} +void ORecur::setEndDate( const QDate& dt) { + checkOrModify(); + data->end = dt; +} +void ORecur::setCreatedDateTime( const QDateTime& t) { + checkOrModify(); + data->create = t; +} +void ORecur::setHasEndDate( bool b) { + checkOrModify(); + data->hasEnd = b; +} +void ORecur::setRepitition( int rep ) { + checkOrModify(); + data->rep = rep; +} +void ORecur::setService( const QString& app ) { + checkOrModify(); + data->app = app; +} +void ORecur::setStart( const QDate& dt ) { + checkOrModify(); + data->start = dt; +} +void ORecur::checkOrModify() { + if ( data->count != 1 ) { + data->deref(); + Data* d2 = new Data; + d2->days = data->days; + d2->type = data->type; + d2->freq = data->freq; + d2->pos = data->pos; + d2->hasEnd = data->hasEnd; + d2->end = data->end; + d2->create = data->create; + d2->rep = data->rep; + d2->app = data->app; + d2->list = data->list; + d2->start = data->start; + data = d2; + } +} +QString ORecur::toString()const { + QString buf; + QMap recMap = toMap(); + + buf += " rtype=\""; + buf += recMap[ORecur::RType]; + buf += "\""; + if (data->days > 0 ) + buf += " rweekdays=\"" + recMap[ORecur::RWeekdays] + "\""; + if ( data->pos != 0 ) + buf += " rposition=\"" + recMap[ORecur::RPosition] + "\""; + + buf += " rfreq=\"" + recMap[ORecur::RFreq] + "\""; + buf += " rhasenddate=\"" + recMap[ORecur::RHasEndDate]+ "\""; + if ( data->hasEnd ) + buf += " enddt=\"" + + recMap[ORecur::EndDate] + + "\""; + buf += " created=\"" + recMap[ORecur::Created] + "\""; + + if ( data->list.isEmpty() ) return buf; + buf += " exceptions=\""; + buf += recMap[ORecur::Exceptions]; + buf += "\" "; + + return buf; +} + +QString ORecur::rTypeString() const +{ + QString retString; + switch ( data->type ) { + case ORecur::Daily: + retString = "Daily"; + break; + case ORecur::Weekly: + retString = "Weekly"; + break; + case ORecur::MonthlyDay: + retString = "MonthlyDay"; + break; + case ORecur::MonthlyDate: + retString = "MonthlyDate"; + break; + case ORecur::Yearly: + retString = "Yearly"; + break; + default: + retString = "NoRepeat"; + break; + + } + + return retString; +} + +QMap ORecur::rTypeValueConvertMap() const +{ + QMap convertMap; + + convertMap.insert( QString( "Daily" ), ORecur::Daily ); + convertMap.insert( QString( "Weekly" ), ORecur::Weekly ); + convertMap.insert( QString( "MonthlyDay" ), ORecur::MonthlyDay ); + convertMap.insert( QString( "MonthlyDate" ), ORecur::MonthlyDate ); + convertMap.insert( QString( "Yearly" ), ORecur::Yearly ); + convertMap.insert( QString( "NoRepeat" ), ORecur::NoRepeat ); + + return convertMap; +} + + +QMap ORecur::toMap() const +{ + QMap retMap; + + retMap.insert( ORecur::RType, rTypeString() ); + retMap.insert( ORecur::RWeekdays, QString::number( static_cast( data->days ) ) ); + retMap.insert( ORecur::RPosition, QString::number(data->pos ) ); + retMap.insert( ORecur::RFreq, QString::number( data->freq ) ); + retMap.insert( ORecur::RHasEndDate, QString::number( static_cast( data->hasEnd ) ) ); + if( data -> hasEnd ) + retMap.insert( ORecur::EndDate, QString::number( OTimeZone::utc().fromUTCDateTime( QDateTime( data->end, QTime(12,0,0) ) ) ) ); + retMap.insert( ORecur::Created, QString::number( OTimeZone::utc().fromUTCDateTime( data->create ) ) ); + + if ( data->list.isEmpty() ) return retMap; + + // save exceptions list here!! + ExceptionList::ConstIterator it; + ExceptionList list = data->list; + QString exceptBuf; + QDate date; + for ( it = list.begin(); it != list.end(); ++it ) { + date = (*it); + if ( it != list.begin() ) exceptBuf += " "; + + exceptBuf += QCString().sprintf("%04d%02d%02d", date.year(), date.month(), date.day() ); + } + + retMap.insert( ORecur::Exceptions, exceptBuf ); + + return retMap; +} + +void ORecur::fromMap( const QMap& map ) +{ + QMap repTypeMap = rTypeValueConvertMap(); + + data -> type = repTypeMap[ map [ORecur::RType] ]; + data -> days = (char) map[ ORecur::RWeekdays ].toInt(); + data -> pos = map[ ORecur::RPosition ].toInt(); + data -> freq = map[ ORecur::RFreq ].toInt(); + data -> hasEnd= map[ ORecur::RHasEndDate ].toInt() ? true : false; + OTimeZone utc = OTimeZone::utc(); + if ( data -> hasEnd ){ + data -> end = utc.fromUTCDateTime( (time_t) map[ ORecur::EndDate ].toLong() ).date(); + } + data -> create = utc.fromUTCDateTime( (time_t) map[ ORecur::Created ].toLong() ).date(); + +#if 0 + // FIXME: Exceptions currently not supported... + // Convert the list of exceptions from QString into ExceptionList + data -> list.clear(); + QString exceptStr = map[ ORecur::Exceptions ]; + QStringList exceptList = QStringList::split( " ", exceptStr ); + ... +#endif + + +} diff --git a/noncore/unsupported/libopie/pim/orecur.h b/noncore/unsupported/libopie/pim/orecur.h new file mode 100644 index 0000000..d7ecd90 --- a/dev/null +++ b/noncore/unsupported/libopie/pim/orecur.h @@ -0,0 +1,107 @@ +/* + * GPL from TT + */ + +#ifndef OPIE_RECUR_H +#define OPIE_RECUR_H + +#include + +#include +#include +#include + + +/** + * Class to handle Recurrencies.. + */ + +class ORecur { +public: + typedef QValueList ExceptionList; + enum RepeatType{ NoRepeat = -1, Daily, Weekly, MonthlyDay, + MonthlyDate, Yearly }; + enum Days { MON = 0x01, TUE = 0x02, WED = 0x04, THU = 0x08, + FRI = 0x10, SAT = 0x20, SUN = 0x40 }; + enum Fields{ RType = 0, RWeekdays, RPosition, RFreq, RHasEndDate, + EndDate, Created, Exceptions }; + + ORecur(); + ORecur( const QMap& map ); + ORecur( const ORecur& ); + ~ORecur(); + + ORecur &operator=( const ORecur& ); + bool operator==(const ORecur& )const; + + bool doesRecur()const; + /* if it recurrs on that day */ + bool doesRecur( const QDate& ); + RepeatType type()const; + int frequency()const; + int position()const; + char days()const; + bool hasEndDate()const; + QDate start()const; + QDate endDate()const; + QDateTime createdDateTime()const; + /** + * starting on monday=0, sunday=6 + * for convience + */ + bool repeatOnWeekDay( int day )const; + + /** + * FromWhereToStart is not included!!! + */ + bool nextOcurrence( const QDate& FromWhereToStart, QDate &recurDate ); + + /** + * The module this ORecur belongs to + */ + QString service()const; + + /* + * reference to the exception list + */ + ExceptionList &exceptions(); + + /** + * the current repetition + */ + int repetition()const; + + void setType( const RepeatType& ); + void setFrequency( int freq ); + void setPosition( int pos ); + void setDays( char c); + void setEndDate( const QDate& dt ); + void setStart( const QDate& dt ); + void setCreatedDateTime( const QDateTime& ); + void setHasEndDate( bool b ); + void setRepitition(int ); + + void setService( const QString& ser ); + + QMap toMap() const; + void fromMap( const QMap& map ); + + /* almost internal */ + QString toString()const; +private: + bool p_nextOccurrence( const QDate& from, QDate& next ); + void deref(); + inline void checkOrModify(); + + /* Converts rType to String */ + QString rTypeString() const; + /* Returns a map to convert Stringname for RType to RepeatType */ + QMap rTypeValueConvertMap() const; + + class Data; + Data* data; + class ORecurPrivate; + ORecurPrivate *d; +}; + +#endif diff --git a/noncore/unsupported/libopie/pim/otemplatebase.h b/noncore/unsupported/libopie/pim/otemplatebase.h new file mode 100644 index 0000000..cadac74 --- a/dev/null +++ b/noncore/unsupported/libopie/pim/otemplatebase.h @@ -0,0 +1,98 @@ +#ifndef OPIE_TEMPLATE_BASE_H +#define OPIE_TEMPLATE_BASE_H + +#include + +#include + + +/** + * Templates do not have a base class, This is why + * we've this class + * this is here to give us the possibility + * to have a common base class + * You may not want to use that interface internaly + * POOR mans interface + */ +class OPimBasePrivate; +struct OPimBase { + /** + * return the rtti + */ + virtual int rtti()= 0; + virtual OPimRecord* record()const = 0; + virtual OPimRecord* record(int uid)const = 0; + virtual bool add( const OPimRecord& ) = 0; + virtual bool remove( int uid ) = 0; + virtual bool remove( const OPimRecord& ) = 0; + virtual void clear() = 0; + virtual bool load() = 0; + virtual bool save() = 0; + virtual QArray records()const = 0; + /* + * ADD editing here? + * -zecke + */ +private: + OPimBasePrivate* d; + +}; +/** + * internal template base + * T needs to implement the copy c'tor!!! + */ +class OTemplateBasePrivate; +template +class OTemplateBase : public OPimBase { +public: + enum CacheDirection { Forward=0, Reverse }; + OTemplateBase() { + }; + virtual ~OTemplateBase() { + } + virtual T find( int uid )const = 0; + + /** + * read ahead find + */ + virtual T find( int uid, const QArray& items, + uint current, CacheDirection dir = Forward )const = 0; + virtual void cache( const T& )const = 0; + virtual void setSaneCacheSize( int ) = 0; + + /* reimplement of OPimBase */ + int rtti(); + OPimRecord* record()const; + OPimRecord* record(int uid )const; + static T* rec(); + +private: + OTemplateBasePrivate *d; +}; + +/* + * implementation + */ +template +int +OTemplateBase::rtti() { + return T::rtti(); +} +template +OPimRecord* OTemplateBase::record()const { + T* t = new T; + return t; +} +template +OPimRecord* OTemplateBase::record(int uid )const { + T t2 = find(uid ); + T* t1 = new T(t2); + + return t1; +}; +template +T* OTemplateBase::rec() { + return new T; +} + +#endif diff --git a/noncore/unsupported/libopie/pim/otimezone.cpp b/noncore/unsupported/libopie/pim/otimezone.cpp new file mode 100644 index 0000000..34659c3 --- a/dev/null +++ b/noncore/unsupported/libopie/pim/otimezone.cpp @@ -0,0 +1,113 @@ +#include +#include + +#include + +#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/noncore/unsupported/libopie/pim/otimezone.h b/noncore/unsupported/libopie/pim/otimezone.h new file mode 100644 index 0000000..bb08349 --- a/dev/null +++ b/noncore/unsupported/libopie/pim/otimezone.h @@ -0,0 +1,71 @@ +#ifndef OPIE_TIME_ZONE_H +#define OPIE_TIME_ZONE_H + +#include +#include + +/** + * A very primitive class to convert time + * from one timezone to another + * and to localtime + * and time_t + */ +class OTimeZone { + public: + typedef QString ZoneName; + OTimeZone( const ZoneName& = ZoneName::null ); + virtual ~OTimeZone(); // just in case. + + bool isValid()const; + + /** + * converts the QDateTime to a DateTime + * in the local timezone + * if QDateTime is 25th Jan and takes place in Europe/Berlin at 12h + * and the current timezone is Europe/London the returned + * time will be 11h. + */ + QDateTime toLocalDateTime( const QDateTime& dt ); + + /** + * converts the QDateTime to UTC time + */ + QDateTime toUTCDateTime( const QDateTime& dt ); + + /** + * reads the time_t into a QDateTime using UTC as timezone! + */ + QDateTime fromUTCDateTime( time_t ); + + /** + * converts the time_t to the time in the timezone + */ + QDateTime toDateTime( time_t ); + + /** + * converts the QDateTime from one timezone to this timeZone + */ + QDateTime toDateTime( const QDateTime&, const OTimeZone& timeZone ); + + /** + * converts the date time into a time_t. It takes the timezone into account + */ + time_t fromDateTime( const QDateTime& ); + + /** + * converts the datetime with timezone UTC + */ + time_t fromUTCDateTime( const QDateTime& ); + + static OTimeZone current(); + static OTimeZone utc(); + + QString timeZone()const; + private: + ZoneName m_name; + class Private; + Private* d; +}; + + +#endif diff --git a/noncore/unsupported/libopie/pim/otodo.cpp b/noncore/unsupported/libopie/pim/otodo.cpp new file mode 100644 index 0000000..b2c76f8 --- a/dev/null +++ b/noncore/unsupported/libopie/pim/otodo.cpp @@ -0,0 +1,519 @@ + +#include +#include + + + +#include +#include +#include +#include +#include + + +#include "opimstate.h" +#include "orecur.h" +#include "opimmaintainer.h" +#include "opimnotifymanager.h" +#include "opimresolver.h" + +#include "otodo.h" + + +struct OTodo::OTodoData : public QShared { + OTodoData() : QShared() { + recur = 0; + state = 0; + maintainer = 0; + notifiers = 0; + }; + ~OTodoData() { + delete recur; + delete maintainer; + delete notifiers; + } + + QDate date; + bool isCompleted:1; + bool hasDate:1; + int priority; + QString desc; + QString sum; + QMap extra; + ushort prog; + OPimState *state; + ORecur *recur; + OPimMaintainer *maintainer; + QDate start; + QDate completed; + OPimNotifyManager *notifiers; +}; + +OTodo::OTodo(const OTodo &event ) + : OPimRecord( event ), data( event.data ) +{ + data->ref(); +// qWarning("ref up"); +} +OTodo::~OTodo() { + +// qWarning("~OTodo " ); + if ( data->deref() ) { +// qWarning("OTodo::dereffing"); + delete data; + data = 0l; + } +} +OTodo::OTodo(bool completed, int priority, + const QArray &category, + const QString& summary, + const QString &description, + ushort progress, + bool hasDate, QDate date, int uid ) + : OPimRecord( uid ) +{ +// qWarning("OTodoData " + summary); + setCategories( category ); + + data = new OTodoData; + + data->date = date; + data->isCompleted = completed; + data->hasDate = hasDate; + data->priority = priority; + data->sum = summary; + data->prog = progress; + data->desc = Qtopia::simplifyMultiLineSpace(description ); +} +OTodo::OTodo(bool completed, int priority, + const QStringList &category, + const QString& summary, + const QString &description, + ushort progress, + bool hasDate, QDate date, int uid ) + : OPimRecord( uid ) +{ +// qWarning("OTodoData" + summary); + setCategories( idsFromString( category.join(";") ) ); + + data = new OTodoData; + + data->date = date; + data->isCompleted = completed; + data->hasDate = hasDate; + data->priority = priority; + data->sum = summary; + data->prog = progress; + data->desc = Qtopia::simplifyMultiLineSpace(description ); +} +bool OTodo::match( const QRegExp ®Exp )const +{ + if( QString::number( data->priority ).find( regExp ) != -1 ){ + setLastHitField( Priority ); + return true; + }else if( data->hasDate && data->date.toString().find( regExp) != -1 ){ + setLastHitField( HasDate ); + return true; + }else if(data->desc.find( regExp ) != -1 ){ + setLastHitField( Description ); + return true; + }else if(data->sum.find( regExp ) != -1 ) { + setLastHitField( Summary ); + return true; + } + return false; +} +bool OTodo::isCompleted() const +{ + return data->isCompleted; +} +bool OTodo::hasDueDate() const +{ + return data->hasDate; +} +bool OTodo::hasStartDate()const { + return data->start.isValid(); +} +bool OTodo::hasCompletedDate()const { + return data->completed.isValid(); +} +int OTodo::priority()const +{ + return data->priority; +} +QString OTodo::summary() const +{ + return data->sum; +} +ushort OTodo::progress() const +{ + return data->prog; +} +QDate OTodo::dueDate()const +{ + return data->date; +} +QDate OTodo::startDate()const { + return data->start; +} +QDate OTodo::completedDate()const { + return data->completed; +} +QString OTodo::description()const +{ + return data->desc; +} +bool OTodo::hasState() const{ + if (!data->state ) return false; + return ( data->state->state() != OPimState::Undefined ); +} +OPimState OTodo::state()const { + if (!data->state ) { + OPimState state; + return state; + } + + return (*data->state); +} +bool OTodo::hasRecurrence()const { + if (!data->recur) return false; + return data->recur->doesRecur(); +} +ORecur OTodo::recurrence()const { + if (!data->recur) return ORecur(); + + return (*data->recur); +} +bool OTodo::hasMaintainer()const { + if (!data->maintainer) return false; + + return (data->maintainer->mode() != OPimMaintainer::Undefined ); +} +OPimMaintainer OTodo::maintainer()const { + if (!data->maintainer) return OPimMaintainer(); + + return (*data->maintainer); +} +void OTodo::setCompleted( bool completed ) +{ + changeOrModify(); + data->isCompleted = completed; +} +void OTodo::setHasDueDate( bool hasDate ) +{ + changeOrModify(); + data->hasDate = hasDate; +} +void OTodo::setDescription(const QString &desc ) +{ +// qWarning( "desc " + desc ); + changeOrModify(); + data->desc = Qtopia::simplifyMultiLineSpace(desc ); +} +void OTodo::setSummary( const QString& sum ) +{ + changeOrModify(); + data->sum = sum; +} +void OTodo::setPriority(int prio ) +{ + changeOrModify(); + data->priority = prio; +} +void OTodo::setDueDate( const QDate& date ) +{ + changeOrModify(); + data->date = date; +} +void OTodo::setStartDate( const QDate& date ) { + changeOrModify(); + data->start = date; +} +void OTodo::setCompletedDate( const QDate& date ) { + changeOrModify(); + data->completed = date; +} +void OTodo::setState( const OPimState& state ) { + changeOrModify(); + if (data->state ) + (*data->state) = state; + else + data->state = new OPimState( state ); +} +void OTodo::setRecurrence( const ORecur& rec) { + changeOrModify(); + if (data->recur ) + (*data->recur) = rec; + else + data->recur = new ORecur( rec ); +} +void OTodo::setMaintainer( const OPimMaintainer& pim ) { + changeOrModify(); + + if (data->maintainer ) + (*data->maintainer) = pim; + else + data->maintainer = new OPimMaintainer( pim ); +} +bool OTodo::isOverdue( ) +{ + if( data->hasDate && !data->isCompleted) + return QDate::currentDate() > data->date; + return false; +} +void OTodo::setProgress(ushort progress ) +{ + changeOrModify(); + data->prog = progress; +} +QString OTodo::toShortText() const { + return summary(); +} +/*! + Returns a richt text string +*/ +QString OTodo::toRichText() const +{ + QString text; + QStringList catlist; + + // summary + text += "

"; + if ( !summary().isEmpty() ) { + text += Qtopia::escapeString(summary() ).replace(QRegExp( "[\n]"), "" ); + } + text += "




"; + + // description + if( !description().isEmpty() ){ + text += "" + QObject::tr( "Description:" ) + "
"; + text += Qtopia::escapeString(description() ).replace(QRegExp( "[\n]"), "
" ) + "
"; + } + + // priority + int priorityval = priority(); + text += "" + QObject::tr( "Priority:") +" "; + + switch ( priorityval ) + { + case 1 : text += QObject::tr( "Very high" ); + break; + case 2 : text += QObject::tr( "High" ); + break; + case 3 : text += QObject::tr( "Normal" ); + break; + case 4 : text += QObject::tr( "Low" ); + break; + case 5 : text += QObject::tr( "Very low" ); + break; + }; + text += "
"; + + // progress + text += "" + QObject::tr( "Progress:") + " " + + QString::number( progress() ) + " %
"; + + // due date + if (hasDueDate() ){ + QDate dd = dueDate(); + int off = QDate::currentDate().daysTo( dd ); + + text += "" + QObject::tr( "Deadline:" ) + " 0 ) + text += "#00FF00"; + + text += "\">" + dd.toString() + "
"; + } + + // categories + text += "" + QObject::tr( "Category:") + " "; + text += categoryNames( "Todo List" ).join(", "); + text += "
"; + + return text; +} +bool OTodo::hasNotifiers()const { + if (!data->notifiers) return false; + return !data->notifiers->isEmpty(); +} +OPimNotifyManager& OTodo::notifiers() { + if (!data->notifiers ) + data->notifiers = new OPimNotifyManager; + return (*data->notifiers); +} +const OPimNotifyManager& OTodo::notifiers()const{ + if (!data->notifiers ) + data->notifiers = new OPimNotifyManager; + + return (*data->notifiers); +} + +bool OTodo::operator<( const OTodo &toDoEvent )const{ + if( !hasDueDate() && !toDoEvent.hasDueDate() ) return true; + if( !hasDueDate() && toDoEvent.hasDueDate() ) return false; + if( hasDueDate() && toDoEvent.hasDueDate() ){ + if( dueDate() == toDoEvent.dueDate() ){ // let's the priority decide + return priority() < toDoEvent.priority(); + }else{ + return dueDate() < toDoEvent.dueDate(); + } + } + return false; +} +bool OTodo::operator<=(const OTodo &toDoEvent )const +{ + if( !hasDueDate() && !toDoEvent.hasDueDate() ) return true; + if( !hasDueDate() && toDoEvent.hasDueDate() ) return true; + if( hasDueDate() && toDoEvent.hasDueDate() ){ + if( dueDate() == toDoEvent.dueDate() ){ // let's the priority decide + return priority() <= toDoEvent.priority(); + }else{ + return dueDate() <= toDoEvent.dueDate(); + } + } + return true; +} +bool OTodo::operator>(const OTodo &toDoEvent )const +{ + if( !hasDueDate() && !toDoEvent.hasDueDate() ) return false; + if( !hasDueDate() && toDoEvent.hasDueDate() ) return false; + if( hasDueDate() && toDoEvent.hasDueDate() ){ + if( dueDate() == toDoEvent.dueDate() ){ // let's the priority decide + return priority() > toDoEvent.priority(); + }else{ + return dueDate() > toDoEvent.dueDate(); + } + } + return false; +} +bool OTodo::operator>=(const OTodo &toDoEvent )const +{ + if( !hasDueDate() && !toDoEvent.hasDueDate() ) return true; + if( !hasDueDate() && toDoEvent.hasDueDate() ) return false; + if( hasDueDate() && toDoEvent.hasDueDate() ){ + if( dueDate() == toDoEvent.dueDate() ){ // let's the priority decide + return priority() > toDoEvent.priority(); + }else{ + return dueDate() > toDoEvent.dueDate(); + } + } + return true; +} +bool OTodo::operator==(const OTodo &toDoEvent )const +{ + if ( data->priority != toDoEvent.data->priority ) return false; + if ( data->priority != toDoEvent.data->prog ) return false; + if ( data->isCompleted != toDoEvent.data->isCompleted ) return false; + if ( data->hasDate != toDoEvent.data->hasDate ) return false; + if ( data->date != toDoEvent.data->date ) return false; + if ( data->sum != toDoEvent.data->sum ) return false; + if ( data->desc != toDoEvent.data->desc ) return false; + if ( data->maintainer != toDoEvent.data->maintainer ) + return false; + + return OPimRecord::operator==( toDoEvent ); +} +void OTodo::deref() { + +// qWarning("deref in ToDoEvent"); + if ( data->deref() ) { +// qWarning("deleting"); + delete data; + data= 0; + } +} +OTodo &OTodo::operator=(const OTodo &item ) +{ + if ( this == &item ) return *this; + + OPimRecord::operator=( item ); + //qWarning("operator= ref "); + item.data->ref(); + deref(); + data = item.data; + + return *this; +} + +QMap OTodo::toMap() const { + QMap map; + + map.insert( Uid, QString::number( uid() ) ); + map.insert( Category, idsToString( categories() ) ); + map.insert( HasDate, QString::number( data->hasDate ) ); + map.insert( Completed, QString::number( data->isCompleted ) ); + map.insert( Description, data->desc ); + map.insert( Summary, data->sum ); + map.insert( Priority, QString::number( data->priority ) ); + map.insert( DateDay, QString::number( data->date.day() ) ); + map.insert( DateMonth, QString::number( data->date.month() ) ); + map.insert( DateYear, QString::number( data->date.year() ) ); + map.insert( Progress, QString::number( data->prog ) ); +// map.insert( CrossReference, crossToString() ); + /* FIXME!!! map.insert( State, ); + map.insert( Recurrence, ); + map.insert( Reminders, ); + map. + */ + return map; +} + +/** + * change or modify looks at the ref count and either + * creates a new QShared Object or it can modify it + * right in place + */ +void OTodo::changeOrModify() { + if ( data->count != 1 ) { + qWarning("changeOrModify"); + data->deref(); + OTodoData* d2 = new OTodoData(); + copy(data, d2 ); + data = d2; + } +} +// WATCHOUT +/* + * if you add something to the Data struct + * be sure to copy it here + */ +void OTodo::copy( OTodoData* src, OTodoData* dest ) { + dest->date = src->date; + dest->isCompleted = src->isCompleted; + dest->hasDate = src->hasDate; + dest->priority = src->priority; + dest->desc = src->desc; + dest->sum = src->sum; + dest->extra = src->extra; + dest->prog = src->prog; + + if (src->state ) + dest->state = new OPimState( *src->state ); + + if (src->recur ) + dest->recur = new ORecur( *src->recur ); + + if (src->maintainer ) + dest->maintainer = new OPimMaintainer( *src->maintainer ) + ; + dest->start = src->start; + dest->completed = src->completed; + + if (src->notifiers ) + dest->notifiers = new OPimNotifyManager( *src->notifiers ); +} +QString OTodo::type() const { + return QString::fromLatin1("OTodo"); +} +QString OTodo::recordField(int /*id*/ )const { + return QString::null; +} + +int OTodo::rtti(){ + return OPimResolver::TodoList; +} diff --git a/noncore/unsupported/libopie/pim/otodo.h b/noncore/unsupported/libopie/pim/otodo.h new file mode 100644 index 0000000..6df98b9 --- a/dev/null +++ b/noncore/unsupported/libopie/pim/otodo.h @@ -0,0 +1,285 @@ + +#ifndef OPIE_TODO_EVENT_H +#define OPIE_TODO_EVENT_H + + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + + +class OPimState; +class ORecur; +class OPimMaintainer; +class OPimNotifyManager; +class OTodo : public OPimRecord { +public: + typedef QValueList ValueList; + enum RecordFields { + Uid = Qtopia::UID_ID, + Category = Qtopia::CATEGORY_ID, + HasDate, + Completed, + Description, + Summary, + Priority, + DateDay, + DateMonth, + DateYear, + Progress, + CrossReference, + State, + Recurrence, + Alarms, + Reminders, + Notifiers, + Maintainer, + StartDate, + CompletedDate + }; + public: + // priorities from Very low to very high + enum TaskPriority { VeryHigh=1, High, Normal, Low, VeryLow }; + + /* Constructs a new ToDoEvent + @param completed Is the TodoEvent completed + @param priority What is the priority of this ToDoEvent + @param category Which category does it belong( uid ) + @param summary A small summary of the todo + @param description What is this ToDoEvent about + @param hasDate Does this Event got a deadline + @param date what is the deadline? + @param uid what is the UUID of this Event + **/ + OTodo( bool completed = false, int priority = Normal, + const QStringList &category = QStringList(), + const QString &summary = QString::null , + const QString &description = QString::null, + ushort progress = 0, + bool hasDate = false, QDate date = QDate::currentDate(), + int uid = 0 /*empty*/ ); + + OTodo( bool completed, int priority, + const QArray& category, + const QString& summary = QString::null, + const QString& description = QString::null, + ushort progress = 0, + bool hasDate = false, QDate date = QDate::currentDate(), + int uid = 0 /* empty */ ); + + /** Copy c'tor + * + */ + OTodo(const OTodo & ); + + /** + *destructor + */ + ~OTodo(); + + /** + * Is this event completed? + */ + bool isCompleted() const; + + /** + * Does this Event have a deadline + */ + bool hasDueDate() const; + bool hasStartDate()const; + bool hasCompletedDate()const; + + /** + * What is the priority? + */ + int priority()const ; + + /** + * progress as ushort 0, 20, 40, 60, 80 or 100% + */ + ushort progress() const; + + /** + * The due Date + */ + QDate dueDate()const; + + /** + * When did it start? + */ + QDate startDate()const; + + /** + * When was it completed? + */ + QDate completedDate()const; + + /** + * does it have a state? + */ + bool hasState()const; + + /** + * What is the state of this OTodo? + */ + OPimState state()const; + + /** + * has recurrence? + */ + bool hasRecurrence()const; + + /** + * the recurrance of this + */ + ORecur recurrence()const; + + /** + * does this OTodo have a maintainer? + */ + bool hasMaintainer()const; + + /** + * the Maintainer of this OTodo + */ + OPimMaintainer maintainer()const; + + /** + * The description of the todo + */ + QString description()const; + + /** + * A small summary of the todo + */ + QString summary() const; + + /** + * @reimplemented + * Return this todoevent in a RichText formatted QString + */ + QString toRichText() const; + + bool hasNotifiers()const; + /* + * FIXME check if the sharing is still fine!! -zecke + * ### CHECK If API is fine + */ + /** + * return a reference to our notifiers... + */ + OPimNotifyManager ¬ifiers(); + + /** + * + */ + const OPimNotifyManager ¬ifiers()const; + + /** + * reimplementations + */ + QString type()const; + QString toShortText()const; + QString recordField(int id )const; + + /** + * toMap puts all data into the map. int relates + * to ToDoEvent RecordFields enum + */ + QMap toMap()const; + + /** + * Set if this Todo is completed + */ + void setCompleted(bool completed ); + + /** + * set if this todo got an end data + */ + void setHasDueDate( bool hasDate ); + // FIXME we do not have these for start, completed + // cause we'll use the isNull() of QDate for figuring + // out if it's has a date... + // decide what to do here? -zecke + + /** + * Set the priority of the Todo + */ + void setPriority(int priority ); + + /** + * Set the progress. + */ + void setProgress( ushort progress ); + + /** + * set the end date + */ + void setDueDate( const QDate& date ); + + /** + * set the start date + */ + void setStartDate( const QDate& date ); + + /** + * set the completed date + */ + void setCompletedDate( const QDate& date ); + + void setRecurrence( const ORecur& ); + + void setDescription(const QString& ); + void setSummary(const QString& ); + + /** + * set the state of a Todo + * @param state State what the todo should take + */ + void setState( const OPimState& state); + + /** + * set the Maintainer Mode + */ + void setMaintainer( const OPimMaintainer& ); + + bool isOverdue(); + + + virtual bool match( const QRegExp &r )const; + + bool operator<(const OTodo &toDoEvent )const; + bool operator<=(const OTodo &toDoEvent )const; + bool operator!=(const OTodo &toDoEvent )const; + bool operator>(const OTodo &toDoEvent )const; + bool operator>=(const OTodo &toDoEvent)const; + bool operator==(const OTodo &toDoEvent )const; + OTodo &operator=(const OTodo &toDoEvent ); + + static int rtti(); + + private: + class OTodoPrivate; + struct OTodoData; + + void deref(); + inline void changeOrModify(); + void copy( OTodoData* src, OTodoData* dest ); + OTodoPrivate *d; + OTodoData *data; + +}; +inline bool OTodo::operator!=(const OTodo &toDoEvent )const { + return !(*this == toDoEvent); +} + + +#endif diff --git a/noncore/unsupported/libopie/pim/otodoaccess.cpp b/noncore/unsupported/libopie/pim/otodoaccess.cpp new file mode 100644 index 0000000..37f6fbc --- a/dev/null +++ b/noncore/unsupported/libopie/pim/otodoaccess.cpp @@ -0,0 +1,62 @@ +#include + +#include + +// #include "otodoaccesssql.h" +#include "otodoaccess.h" +#include "obackendfactory.h" + +OTodoAccess::OTodoAccess( OTodoAccessBackend* end, enum Access ) + : QObject(), OPimAccessTemplate( end ), m_todoBackEnd( end ) +{ +// if (end == 0l ) +// m_todoBackEnd = new OTodoAccessBackendSQL( QString::null); + + // Zecke: Du musst hier noch für das XML-Backend einen Appnamen übergeben ! + if (end == 0l ) + m_todoBackEnd = OBackendFactory::Default ("todo", QString::null); + + setBackEnd( m_todoBackEnd ); +} +OTodoAccess::~OTodoAccess() { +// qWarning("~OTodoAccess"); +} +void OTodoAccess::mergeWith( const QValueList& list ) { + QValueList::ConstIterator it; + for ( it = list.begin(); it != list.end(); ++it ) { + replace( (*it) ); + } +} +OTodoAccess::List OTodoAccess::effectiveToDos( const QDate& start, + const QDate& end, + bool includeNoDates ) { + QArray ints = m_todoBackEnd->effectiveToDos( start, end, includeNoDates ); + + List lis( ints, this ); + return lis; +} +OTodoAccess::List OTodoAccess::effectiveToDos( const QDate& start, + bool includeNoDates ) { + return effectiveToDos( start, QDate::currentDate(), + includeNoDates ); +} +OTodoAccess::List OTodoAccess::overDue() { + List lis( m_todoBackEnd->overDue(), this ); + return lis; +} +/* sort order */ +OTodoAccess::List OTodoAccess::sorted( bool ascending, int sort,int filter, int cat ) { + QArray ints = m_todoBackEnd->sorted( ascending, sort, + filter, cat ); + OTodoAccess::List list( ints, this ); + return list; +} +void OTodoAccess::removeAllCompleted() { + m_todoBackEnd->removeAllCompleted(); +} +QBitArray OTodoAccess::backendSupport( const QString& ) const{ + return m_todoBackEnd->supports(); +} +bool OTodoAccess::backendSupports( int attr, const QString& ar) const{ + return backendSupport(ar).testBit( attr ); +} diff --git a/noncore/unsupported/libopie/pim/otodoaccess.h b/noncore/unsupported/libopie/pim/otodoaccess.h new file mode 100644 index 0000000..916923f --- a/dev/null +++ b/noncore/unsupported/libopie/pim/otodoaccess.h @@ -0,0 +1,105 @@ +#ifndef OPIE_TODO_ACCESS_H +#define OPIE_TODO_ACCESS_H + +#include +#include + +#include "otodo.h" +#include "otodoaccessbackend.h" +#include "opimaccesstemplate.h" + + +/** + * OTodoAccess + * the class to get access to + * the todolist + */ +class OTodoAccess : public QObject, public OPimAccessTemplate { + Q_OBJECT +public: + enum SortOrder { Completed = 0, + Priority, + Description, + Deadline }; + enum SortFilter{ Category =1, + OnlyOverDue= 2, + DoNotShowCompleted =4 }; + /** + * if you use 0l + * the default resource will be + * picked up + */ + OTodoAccess( OTodoAccessBackend* = 0l, enum Access acc = Random ); + ~OTodoAccess(); + + + /* our functions here */ + /** + * include todos from start to end + * includeNoDates whether or not to include + * events with no dates + */ + List effectiveToDos( const QDate& start, + const QDate& end, + bool includeNoDates = true ); + + /** + * start + * end date taken from the currentDate() + */ + List effectiveToDos( const QDate& start, + bool includeNoDates = true ); + + + /** + * return overdue OTodos + */ + List overDue(); + + /** + * + */ + List sorted( bool ascending, int sortOrder, int sortFilter, int cat ); + + /** + * merge a list of OTodos into + * the resource + */ + void mergeWith( const QValueList& ); + + /** + * delete all already completed items + */ + void removeAllCompleted(); + + /** + * request information about what a backend supports. + * Supports in the sense of beeing able to store. + * This is related to the enum in OTodo + * + * @param backend Will be used in the future when we support multiple backend + */ + QBitArray backendSupport( const QString& backend = QString::null )const; + + /** + * see above but for a specefic attribute. This method was added for convience + * @param attr The attribute to be queried for + * @param backend Will be used in the future when we support multiple backends + */ + bool backendSupports( int attr, const QString& backend = QString::null )const; +signals: + /** + * if the OTodoAccess was changed + */ + void changed( const OTodoAccess* ); + void changed( const OTodoAccess*, int uid ); + void added( const OTodoAccess*, int uid ); + void removed( const OTodoAccess*, int uid ); +private: + int m_cat; + OTodoAccessBackend* m_todoBackEnd; + class OTodoAccessPrivate; + OTodoAccessPrivate* d; +}; + +#endif diff --git a/noncore/unsupported/libopie/pim/otodoaccessbackend.cpp b/noncore/unsupported/libopie/pim/otodoaccessbackend.cpp new file mode 100644 index 0000000..baaeecc --- a/dev/null +++ b/noncore/unsupported/libopie/pim/otodoaccessbackend.cpp @@ -0,0 +1,10 @@ + +#include "otodoaccessbackend.h" + +OTodoAccessBackend::OTodoAccessBackend() + : OPimAccessBackend() +{ +} +OTodoAccessBackend::~OTodoAccessBackend() { + +} diff --git a/noncore/unsupported/libopie/pim/otodoaccessbackend.h b/noncore/unsupported/libopie/pim/otodoaccessbackend.h new file mode 100644 index 0000000..6be95bc --- a/dev/null +++ b/noncore/unsupported/libopie/pim/otodoaccessbackend.h @@ -0,0 +1,28 @@ +#ifndef OPIE_TODO_ACCESS_BACKEND_H +#define OPIE_TODO_ACCESS_BACKEND_H + +#include + +#include "otodo.h" +#include "opimaccessbackend.h" + +class OTodoAccessBackend : public OPimAccessBackend { +public: + OTodoAccessBackend(); + ~OTodoAccessBackend(); + virtual QArray effectiveToDos( const QDate& start, + const QDate& end, + bool includeNoDates ) = 0; + virtual QArray overDue() = 0; + virtual QArray sorted( bool asc, int sortOrder, int sortFilter, + int cat ) = 0; + virtual void removeAllCompleted() = 0; + virtual QBitArray supports()const = 0; + +private: + class Private; + Private *d; + +}; + +#endif diff --git a/noncore/unsupported/libopie/pim/otodoaccesssql.cpp b/noncore/unsupported/libopie/pim/otodoaccesssql.cpp new file mode 100644 index 0000000..fd01a42 --- a/dev/null +++ b/noncore/unsupported/libopie/pim/otodoaccesssql.cpp @@ -0,0 +1,694 @@ + +#include + +#include + +#include +#include +#include +#include + +#include "otodoaccesssql.h" +#include "opimstate.h" +#include "opimnotifymanager.h" +#include "orecur.h" + +using namespace Opie::DB; +/* + * 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& ); + ~FindQuery(); + QString query()const; + private: + QString single()const; + QString multi()const; + QArray 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 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& 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(); // Shouldn't m_driver->sync be better than close ? (eilers) +} +QArray OTodoAccessBackendSQL::allRecords()const { + if (m_dirty ) + update(); + + return m_uids; +} +QArray OTodoAccessBackendSQL::queryByExample( const OTodo& , int, const QDateTime& ){ + QArray 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& ints, + uint cur, Frontend::CacheDirection dir ) const{ + uint CACHE = readAhead(); + qWarning("searching for %d", uid ); + QArray 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 OTodoAccessBackendSQL::overDue() { + OverDueQuery qu; + return uids( m_driver->query(&qu ) ); +} +QArray OTodoAccessBackendSQL::effectiveToDos( const QDate& s, + const QDate& t, + bool u) { + EffQuery ef(s, t, u ); + return uids (m_driver->query(&ef) ); +} +/* + * + */ +QArray 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 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 OTodoAccessBackendSQL::uids( const OSQLResult& res) const{ + + OSQLResultItem::ValueList list = res.results(); + OSQLResultItem::ValueList::Iterator it; + QArray 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 OTodoAccessBackendSQL::matchRegexp( const QRegExp &r ) const +{ + +#warning OTodoAccessBackendSQL::matchRegexp() not implemented !! + +#if 0 + + Copied from xml-backend by not adapted to sql (eilers) + + QArray m_currentQuery( m_events.count() ); + uint arraycounter = 0; + + + + QMap::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 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/noncore/unsupported/libopie/pim/otodoaccesssql.h b/noncore/unsupported/libopie/pim/otodoaccesssql.h new file mode 100644 index 0000000..72214de --- a/dev/null +++ b/noncore/unsupported/libopie/pim/otodoaccesssql.h @@ -0,0 +1,61 @@ +#ifndef OPIE_PIM_ACCESS_SQL_H +#define OPIE_PIM_ACCESS_SQL_H + +#include + +#include "otodoaccessbackend.h" + +namespace Opie{ +namespace DB { +class OSQLDriver; +class OSQLResult; +class OSQLResultItem; +} +} + +class OTodoAccessBackendSQL : public OTodoAccessBackend { +public: + OTodoAccessBackendSQL( const QString& file ); + ~OTodoAccessBackendSQL(); + + bool load(); + bool reload(); + bool save(); + QArray allRecords()const; + + QArray queryByExample( const OTodo& t, int settings, const QDateTime& d = QDateTime() ); + OTodo find(int uid)const; + OTodo find(int uid, const QArray&, uint cur, Frontend::CacheDirection )const; + void clear(); + bool add( const OTodo& t ); + bool remove( int uid ); + bool replace( const OTodo& t ); + + QArray overDue(); + QArray effectiveToDos( const QDate& start, + const QDate& end, bool includeNoDates ); + QArray sorted(bool asc, int sortOrder, int sortFilter, int cat ); + + QBitArray supports()const; + QArray matchRegexp( const QRegExp &r ) const; + void removeAllCompleted(); + + +private: + void update()const; + void fillDict(); + inline bool date( QDate& date, const QString& )const; + inline OTodo todo( const Opie::DB::OSQLResult& )const; + inline OTodo todo( Opie::DB::OSQLResultItem& )const; + inline QArray uids( const Opie::DB::OSQLResult& )const; + OTodo todo( int uid )const; + QBitArray sup() const; + + QAsciiDict m_dict; + Opie::DB::OSQLDriver* m_driver; + QArray m_uids; + bool m_dirty : 1; +}; + + +#endif diff --git a/noncore/unsupported/libopie/pim/otodoaccessvcal.cpp b/noncore/unsupported/libopie/pim/otodoaccessvcal.cpp new file mode 100644 index 0000000..6415952 --- a/dev/null +++ b/noncore/unsupported/libopie/pim/otodoaccessvcal.cpp @@ -0,0 +1,249 @@ +#include + +#include +#include +#include + +#include "otodoaccessvcal.h" + +namespace { + static OTodo eventByVObj( VObject *obj ){ + OTodo event; + VObject *ob; + QCString name; + // no uid, attendees, ... and no fun + // description + if( ( ob = isAPropertyOf( obj, VCDescriptionProp )) != 0 ){ + name = vObjectStringZValue( ob ); +#if 0 + event.setDescription( name ); +#else + event.setSummary( name ); +#endif + } + // summary + if ( ( ob = isAPropertyOf( obj, VCSummaryProp ) ) != 0 ) { + name = vObjectStringZValue( ob ); +#if 0 + event.setSummary( name ); +#else + event.setDescription( name ); +#endif + } + // completed + if( ( ob = isAPropertyOf( obj, VCStatusProp )) != 0 ){ + name = vObjectStringZValue( ob ); + if( name == "COMPLETED" ){ + event.setCompleted( true ); + }else{ + event.setCompleted( false ); + } + }else + event.setCompleted( false ); + // priority + if ((ob = isAPropertyOf(obj, VCPriorityProp))) { + name = vObjectStringZValue( ob ); + bool ok; + event.setPriority(name.toInt(&ok) ); + } + //due date + if((ob = isAPropertyOf(obj, VCDueProp)) ){ + event.setHasDueDate( true ); + name = vObjectStringZValue( ob ); + event.setDueDate( TimeConversion::fromISO8601( name).date() ); + } + // categories + if((ob = isAPropertyOf( obj, VCCategoriesProp )) != 0 ){ + name = vObjectStringZValue( ob ); + qWarning("Categories:%s", name.data() ); + } + + event.setUid( 1 ); + return event; + }; + static VObject *vobjByEvent( const OTodo &event ) { + VObject *task = newVObject( VCTodoProp ); + if( task == 0 ) + return 0l; + + if( event.hasDueDate() ) { + QTime time(0, 0, 0); + QDateTime date(event.dueDate(), time ); + addPropValue( task, VCDueProp, + TimeConversion::toISO8601( date ) ); + } + + if( event.isCompleted() ) + addPropValue( task, VCStatusProp, "COMPLETED"); + + QString string = QString::number(event.priority() ); + addPropValue( task, VCPriorityProp, string.local8Bit() ); + + addPropValue( task, VCCategoriesProp, + event.idsToString( event.categories() ).local8Bit() ); + +#if 0 + + // There seems a misrepresentation between summary in otodoevent + // and summary in vcard. + // The same with description.. + // Description is summary and vice versa.. Argh.. (eilers) + + + addPropValue( task, VCDescriptionProp, + event.description().local8Bit() ); + + addPropValue( task, VCSummaryProp, + event.summary().local8Bit() ); + +#else + addPropValue( task, VCDescriptionProp, + event.summary().local8Bit() ); + + addPropValue( task, VCSummaryProp, + event.description().local8Bit() ); +#endif + return task; +}; +} + +OTodoAccessVCal::OTodoAccessVCal( const QString& path ) + : m_dirty(false), m_file( path ) +{ +} +OTodoAccessVCal::~OTodoAccessVCal() { +} +bool OTodoAccessVCal::load() { + m_map.clear(); + m_dirty = false; + + VObject* vcal = 0l; + vcal = Parse_MIME_FromFileName( QFile::encodeName(m_file).data() ); + if (!vcal ) + return false; + + // Iterate over the list + VObjectIterator it; + VObject* vobj; + + initPropIterator(&it, vcal); + + while( moreIteration( &it ) ) { + vobj = ::nextVObject( &it ); + QCString name = ::vObjectName( vobj ); + if( name == VCTodoProp ){ + OTodo to = eventByVObj( vobj ); + m_map.insert( to.uid(), to ); + } + } + + // Should I do a delete vcal? + + return true; +} +bool OTodoAccessVCal::reload() { + return load(); +} +bool OTodoAccessVCal::save() { + if (!m_dirty ) + return true; + + QFileDirect file( m_file ); + if (!file.open(IO_WriteOnly ) ) + return false; + + VObject *obj; + obj = newVObject( VCCalProp ); + addPropValue( obj, VCVersionProp, "1.0" ); + VObject *vo; + for(QMap::ConstIterator it=m_map.begin(); it !=m_map.end(); ++it ){ + vo = vobjByEvent( it.data() ); + addVObjectProp(obj, vo ); + } + writeVObject( file.directHandle(), obj ); + cleanVObject( obj ); + cleanStrTbl(); + + m_dirty = false; + return true; +} +void OTodoAccessVCal::clear() { + m_map.clear(); + m_dirty = true; +} +bool OTodoAccessVCal::add( const OTodo& to ) { + m_map.insert( to.uid(), to ); + m_dirty = true; + return true; +} +bool OTodoAccessVCal::remove( int uid ) { + m_map.remove( uid ); + m_dirty = true; + return true; +} +void OTodoAccessVCal::removeAllCompleted() { + for ( QMap::Iterator it = m_map.begin(); it != m_map.end(); ++it ) { + if ( (*it).isCompleted() ) + m_map.remove( it ); + } +} +bool OTodoAccessVCal::replace( const OTodo& to ) { + m_map.replace( to.uid(), to ); + m_dirty = true; + return true; +} +OTodo OTodoAccessVCal::find(int uid )const { + return m_map[uid]; +} +QArray OTodoAccessVCal::sorted( bool, int, int, int ) { + QArray ar(0); + return ar; +} +QArray OTodoAccessVCal::allRecords()const { + QArray ar( m_map.count() ); + QMap::ConstIterator it; + int i = 0; + for ( it = m_map.begin(); it != m_map.end(); ++it ) { + ar[i] = it.key(); + i++; + } + return ar; +} +QArray OTodoAccessVCal::matchRegexp(const QRegExp& /* r */)const { + QArray ar(0); + return ar; +} +QArray OTodoAccessVCal::queryByExample( const OTodo&, int, const QDateTime& ) { + QArray ar(0); + return ar; +} +QArray OTodoAccessVCal::effectiveToDos( const QDate& , + const QDate& , + bool ) { + QArray ar(0); + return ar; +} +QArray OTodoAccessVCal::overDue() { + QArray ar(0); + return ar; +} +QBitArray OTodoAccessVCal::supports()const { + static QBitArray ar = sup(); + + return ar; +} +QBitArray OTodoAccessVCal::sup() { + 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; + ar[OTodo::Progress] = false; + ar[OTodo::Alarms ] = false; + ar[OTodo::Recurrence] = false; + + return ar; +} diff --git a/noncore/unsupported/libopie/pim/otodoaccessvcal.h b/noncore/unsupported/libopie/pim/otodoaccessvcal.h new file mode 100644 index 0000000..2b17147 --- a/dev/null +++ b/noncore/unsupported/libopie/pim/otodoaccessvcal.h @@ -0,0 +1,40 @@ +#ifndef OPIE_OTODO_ACCESS_VCAL_H +#define OPIE_OTODO_ACCESS_VCAL_H + +#include "otodoaccessbackend.h" + +class OTodoAccessVCal : public OTodoAccessBackend { +public: + OTodoAccessVCal(const QString& ); + ~OTodoAccessVCal(); + + bool load(); + bool reload(); + bool save(); + + QArray allRecords()const; + QArray matchRegexp(const QRegExp &r) const; + QArray queryByExample( const OTodo& t, int sort, const QDateTime& d = QDateTime() ); + QArray effectiveToDos( const QDate& start, + const QDate& end, + bool includeNoDates ); + QArray overDue(); + QArray sorted( bool asc, int sortOrder, int sortFilter, + int cat ); + OTodo find(int uid)const; + void clear(); + bool add( const OTodo& ); + bool remove( int uid ); + bool replace( const OTodo& ); + + void removeAllCompleted(); + virtual QBitArray supports()const; + +private: + static QBitArray sup(); + bool m_dirty : 1; + QString m_file; + QMap m_map; +}; + +#endif diff --git a/noncore/unsupported/libopie/pim/otodoaccessxml.cpp b/noncore/unsupported/libopie/pim/otodoaccessxml.cpp new file mode 100644 index 0000000..4a5cb33 --- a/dev/null +++ b/noncore/unsupported/libopie/pim/otodoaccessxml.cpp @@ -0,0 +1,876 @@ +#include +#include + +#include +#include +#include + +#include + + +#include +#include + +#include +#include +#include + +#include "oconversion.h" +#include "opimstate.h" +#include "otimezone.h" +#include "opimnotifymanager.h" +#include "orecur.h" +#include "otodoaccessxml.h" + +namespace { + time_t rp_end; + ORecur* rec; + ORecur *recur() { + if (!rec ) rec = new ORecur; + return rec; + } + int snd; + enum MoreAttributes { + FRType = OTodo::CompletedDate + 2, + FRWeekdays, + FRPosition, + FRFreq, + FRHasEndDate, + FREndDate, + FRStart, + FREnd + }; + // FROM TT again +char *strstrlen(const char *haystack, int hLen, const char* needle, int nLen) +{ + char needleChar; + char haystackChar; + if (!needle || !haystack || !hLen || !nLen) + return 0; + + const char* hsearch = haystack; + + if ((needleChar = *needle++) != 0) { + nLen--; //(to make up for needle++) + do { + do { + if ((haystackChar = *hsearch++) == 0) + return (0); + if (hsearch >= haystack + hLen) + return (0); + } while (haystackChar != needleChar); + } while (strncmp(hsearch, needle, QMIN(hLen - (hsearch - haystack), nLen)) != 0); + hsearch--; + } + return ((char *)hsearch); +} +} + + +OTodoAccessXML::OTodoAccessXML( const QString& appName, + const QString& fileName ) + : OTodoAccessBackend(), m_app( appName ), m_opened( false ), m_changed( false ) +{ + if (!fileName.isEmpty() ) + m_file = fileName; + else + m_file = Global::applicationFileName( "todolist", "todolist.xml" ); +} +OTodoAccessXML::~OTodoAccessXML() { + +} +bool OTodoAccessXML::load() { + rec = 0; + m_opened = true; + m_changed = false; + /* initialize dict */ + /* + * UPDATE dict if you change anything!!! + */ + QAsciiDict dict(26); + dict.setAutoDelete( TRUE ); + dict.insert("Categories" , new int(OTodo::Category) ); + dict.insert("Uid" , new int(OTodo::Uid) ); + dict.insert("HasDate" , new int(OTodo::HasDate) ); + dict.insert("Completed" , new int(OTodo::Completed) ); + dict.insert("Description" , new int(OTodo::Description) ); + dict.insert("Summary" , new int(OTodo::Summary) ); + dict.insert("Priority" , new int(OTodo::Priority) ); + dict.insert("DateDay" , new int(OTodo::DateDay) ); + dict.insert("DateMonth" , new int(OTodo::DateMonth) ); + dict.insert("DateYear" , new int(OTodo::DateYear) ); + dict.insert("Progress" , new int(OTodo::Progress) ); + dict.insert("CompletedDate", new int(OTodo::CompletedDate) ); + dict.insert("StartDate", new int(OTodo::StartDate) ); + dict.insert("CrossReference", new int(OTodo::CrossReference) ); + dict.insert("State", new int(OTodo::State) ); + dict.insert("Alarms", new int(OTodo::Alarms) ); + dict.insert("Reminders", new int(OTodo::Reminders) ); + dict.insert("Notifiers", new int(OTodo::Notifiers) ); + dict.insert("Maintainer", new int(OTodo::Maintainer) ); + dict.insert("rtype", new int(FRType) ); + dict.insert("rweekdays", new int(FRWeekdays) ); + dict.insert("rposition", new int(FRPosition) ); + dict.insert("rfreq", new int(FRFreq) ); + dict.insert("start", new int(FRStart) ); + dict.insert("rhasenddate", new int(FRHasEndDate) ); + dict.insert("enddt", new int(FREndDate) ); + + // here the custom XML parser from TT it's GPL + // but we want to push OpiePIM... to TT..... + // mmap part from zecke :) + int fd = ::open( QFile::encodeName(m_file).data(), O_RDONLY ); + struct stat attribut; + if ( fd < 0 ) return false; + + if ( fstat(fd, &attribut ) == -1 ) { + ::close( fd ); + return false; + } + void* map_addr = ::mmap(NULL, attribut.st_size, PROT_READ, MAP_SHARED, fd, 0 ); + if ( map_addr == ( (caddr_t)-1) ) { + ::close(fd ); + return false; + } + /* advise the kernel who we want to read it */ + ::madvise( map_addr, attribut.st_size, MADV_SEQUENTIAL ); + /* we do not the file any more */ + ::close( fd ); + + char* dt = (char*)map_addr; + int len = attribut.st_size; + int i = 0; + char *point; + const char* collectionString = "= len-2 || (dt[i] == '/' && dt[i+1] == '>') ) + break; + + // we have another attribute, read it. + int j = i; + while ( j < len && dt[j] != '=' ) + ++j; + QCString attr( dt+i, j-i+1); + + i = ++j; // skip = + + // find the start of quotes + while ( i < len && dt[i] != '"' ) + ++i; + j = ++i; + + bool haveUtf = FALSE; + bool haveEnt = FALSE; + while ( j < len && dt[j] != '"' ) { + if ( ((unsigned char)dt[j]) > 0x7f ) + haveUtf = TRUE; + if ( dt[j] == '&' ) + haveEnt = TRUE; + ++j; + } + if ( i == j ) { + // empty value + i = j + 1; + continue; + } + + QCString value( dt+i, j-i+1 ); + i = j + 1; + + QString str = (haveUtf ? QString::fromUtf8( value ) + : QString::fromLatin1( value ) ); + if ( haveEnt ) + str = Qtopia::plainString( str ); + + /* + * add key + value + */ + todo( &dict, ev, attr, str ); + + } + /* + * now add it + */ + qWarning("End at %d", i ); + if (m_events.contains( ev.uid() ) || ev.uid() == 0) { + ev.setUid( 1 ); + m_changed = true; + } + if ( ev.hasDueDate() ) { + ev.setDueDate( QDate(m_year, m_month, m_day) ); + } + if ( rec && rec->doesRecur() ) { + OTimeZone utc = OTimeZone::utc(); + ORecur recu( *rec ); // call copy c'tor + recu.setEndDate( utc.fromUTCDateTime( rp_end ).date() ); + recu.setStart( ev.dueDate() ); + ev.setRecurrence( recu ); + } + m_events.insert(ev.uid(), ev ); + m_year = m_month = m_day = -1; + delete rec; + rec = 0; + } + + munmap(map_addr, attribut.st_size ); + + qWarning("counts %d records loaded!", m_events.count() ); + return true; +} +bool OTodoAccessXML::reload() { + m_events.clear(); + return load(); +} +bool OTodoAccessXML::save() { +// qWarning("saving"); + if (!m_opened || !m_changed ) { +// qWarning("not saving"); + return true; + } + QString strNewFile = m_file + ".new"; + QFile f( strNewFile ); + if (!f.open( IO_WriteOnly|IO_Raw ) ) + return false; + + int written; + QString out; + out = "\n\n"; + + // for all todos + QMap::Iterator it; + for (it = m_events.begin(); it != m_events.end(); ++it ) { + out+= "\n"; + QCString cstr = out.utf8(); + written = f.writeBlock( cstr.data(), cstr.length() ); + + /* less written then we wanted */ + if ( written != (int)cstr.length() ) { + f.close(); + QFile::remove( strNewFile ); + return false; + } + out = QString::null; + } + + out += ""; + QCString cstr = out.utf8(); + written = f.writeBlock( cstr.data(), cstr.length() ); + + if ( written != (int)cstr.length() ) { + f.close(); + QFile::remove( strNewFile ); + return false; + } + /* flush before renaming */ + f.close(); + + if( ::rename( strNewFile.latin1(), m_file.latin1() ) < 0 ) { +// qWarning("error renaming"); + QFile::remove( strNewFile ); + } + + m_changed = false; + return true; +} +QArray OTodoAccessXML::allRecords()const { + QArray ids( m_events.count() ); + QMap::ConstIterator it; + int i = 0; + + for ( it = m_events.begin(); it != m_events.end(); ++it ) { + ids[i] = it.key(); + i++; + } + return ids; +} +QArray OTodoAccessXML::queryByExample( const OTodo&, int, const QDateTime& ) { + QArray ids(0); + return ids; +} +OTodo OTodoAccessXML::find( int uid )const { + OTodo todo; + todo.setUid( 0 ); // isEmpty() + QMap::ConstIterator it = m_events.find( uid ); + if ( it != m_events.end() ) + todo = it.data(); + + return todo; +} +void OTodoAccessXML::clear() { + if (m_opened ) + m_changed = true; + + m_events.clear(); +} +bool OTodoAccessXML::add( const OTodo& todo ) { +// qWarning("add"); + m_changed = true; + m_events.insert( todo.uid(), todo ); + + return true; +} +bool OTodoAccessXML::remove( int uid ) { + m_changed = true; + m_events.remove( uid ); + + return true; +} +bool OTodoAccessXML::replace( const OTodo& todo) { + m_changed = true; + m_events.replace( todo.uid(), todo ); + + return true; +} +QArray OTodoAccessXML::effectiveToDos( const QDate& start, + const QDate& end, + bool includeNoDates ) { + QArray ids( m_events.count() ); + QMap::Iterator it; + + int i = 0; + for ( it = m_events.begin(); it != m_events.end(); ++it ) { + if ( !it.data().hasDueDate() ) { + if ( includeNoDates ) { + ids[i] = it.key(); + i++; + } + }else if ( it.data().dueDate() >= start && + it.data().dueDate() <= end ) { + ids[i] = it.key(); + i++; + } + } + ids.resize( i ); + return ids; +} +QArray OTodoAccessXML::overDue() { + QArray ids( m_events.count() ); + int i = 0; + + QMap::Iterator it; + for ( it = m_events.begin(); it != m_events.end(); ++it ) { + if ( it.data().isOverdue() ) { + ids[i] = it.key(); + i++; + } + } + ids.resize( i ); + return ids; +} + + +/* private */ +void OTodoAccessXML::todo( QAsciiDict* dict, OTodo& ev, + const QCString& attr, const QString& val) { +// qWarning("parse to do from XMLElement" ); + + int *find=0; + + find = (*dict)[ attr.data() ]; + if (!find ) { +// qWarning("Unknown option" + it.key() ); + ev.setCustomField( attr, val ); + return; + } + + switch( *find ) { + case OTodo::Uid: + ev.setUid( val.toInt() ); + break; + case OTodo::Category: + ev.setCategories( ev.idsFromString( val ) ); + break; + case OTodo::HasDate: + ev.setHasDueDate( val.toInt() ); + break; + case OTodo::Completed: + ev.setCompleted( val.toInt() ); + break; + case OTodo::Description: + ev.setDescription( val ); + break; + case OTodo::Summary: + ev.setSummary( val ); + break; + case OTodo::Priority: + ev.setPriority( val.toInt() ); + break; + case OTodo::DateDay: + m_day = val.toInt(); + break; + case OTodo::DateMonth: + m_month = val.toInt(); + break; + case OTodo::DateYear: + m_year = val.toInt(); + break; + case OTodo::Progress: + ev.setProgress( val.toInt() ); + break; + case OTodo::CompletedDate: + ev.setCompletedDate( OConversion::dateFromString( val ) ); + break; + case OTodo::StartDate: + ev.setStartDate( OConversion::dateFromString( val ) ); + break; + case OTodo::State: + ev.setState( val.toInt() ); + break; + case OTodo::Alarms:{ + OPimNotifyManager &manager = ev.notifiers(); + QStringList als = QStringList::split(";", val ); + for (QStringList::Iterator it = als.begin(); it != als.end(); ++it ) { + QStringList alarm = QStringList::split(":", (*it), TRUE ); // allow empty + qWarning("alarm: %s", alarm.join("___").latin1() ); + qWarning("alarm[0]: %s %s", alarm[0].latin1(), OConversion::dateTimeFromString( alarm[0] ).toString().latin1() ); + OPimAlarm al( alarm[2].toInt(), OConversion::dateTimeFromString( alarm[0] ), alarm[1].toInt() ); + manager.add( al ); + } + } + break; + case OTodo::Reminders:{ + OPimNotifyManager &manager = ev.notifiers(); + QStringList rems = QStringList::split(";", val ); + for (QStringList::Iterator it = rems.begin(); it != rems.end(); ++it ) { + OPimReminder rem( (*it).toInt() ); + manager.add( rem ); + } + } + break; + case OTodo::CrossReference: + { + /* + * A cross refernce looks like + * appname,id;appname,id + * we need to split it up + */ + QStringList refs = QStringList::split(';', val ); + QStringList::Iterator strIt; + for (strIt = refs.begin(); strIt != refs.end(); ++strIt ) { + int pos = (*strIt).find(','); + if ( pos > -1 ) + ; // ev.addRelation( (*strIt).left(pos), (*strIt).mid(pos+1).toInt() ); + + } + break; + } + /* Recurrence stuff below + post processing later */ + case FRType: + if ( val == "Daily" ) + recur()->setType( ORecur::Daily ); + else if ( val == "Weekly" ) + recur()->setType( ORecur::Weekly); + else if ( val == "MonthlyDay" ) + recur()->setType( ORecur::MonthlyDay ); + else if ( val == "MonthlyDate" ) + recur()->setType( ORecur::MonthlyDate ); + else if ( val == "Yearly" ) + recur()->setType( ORecur::Yearly ); + else + recur()->setType( ORecur::NoRepeat ); + break; + case FRWeekdays: + recur()->setDays( val.toInt() ); + break; + case FRPosition: + recur()->setPosition( val.toInt() ); + break; + case FRFreq: + recur()->setFrequency( val.toInt() ); + break; + case FRHasEndDate: + recur()->setHasEndDate( val.toInt() ); + break; + case FREndDate: { + rp_end = (time_t) val.toLong(); + break; + } + default: + ev.setCustomField( attr, val ); + break; + } +} + +// from PalmtopRecord... GPL ### FIXME +namespace { +QString customToXml(const QMap& customMap ) +{ + //qWarning(QString("writing custom %1").arg(customMap.count())); + QString buf(" "); + for ( QMap::ConstIterator cit = customMap.begin(); + cit != customMap.end(); ++cit) { +// qWarning(".ITEM."); + buf += cit.key(); + buf += "=\""; + buf += Qtopia::escapeString(cit.data()); + buf += "\" "; + } + return buf; +} + + +} + +QString OTodoAccessXML::toString( const OTodo& ev )const { + QString str; + + str += "Completed=\"" + QString::number( ev.isCompleted() ) + "\" "; + str += "HasDate=\"" + QString::number( ev.hasDueDate() ) + "\" "; + str += "Priority=\"" + QString::number( ev.priority() ) + "\" "; + str += "Progress=\"" + QString::number(ev.progress() ) + "\" "; + + str += "Categories=\"" + toString( ev.categories() ) + "\" "; + str += "Description=\"" + Qtopia::escapeString( ev.description() ) + "\" "; + str += "Summary=\"" + Qtopia::escapeString( ev.summary() ) + "\" "; + + if ( ev.hasDueDate() ) { + str += "DateYear=\"" + QString::number( ev.dueDate().year() ) + "\" "; + str += "DateMonth=\"" + QString::number( ev.dueDate().month() ) + "\" "; + str += "DateDay=\"" + QString::number( ev.dueDate().day() ) + "\" "; + } +// qWarning( "Uid %d", ev.uid() ); + str += "Uid=\"" + QString::number( ev.uid() ) + "\" "; + +// append the extra options + /* FIXME Qtopia::Record this is currently not + * possible you can set custom fields + * but don' iterate over the list + * I may do #define private protected + * for this case - cough --zecke + */ + /* + QMap extras = ev.extras(); + QMap::Iterator extIt; + for (extIt = extras.begin(); extIt != extras.end(); ++extIt ) + str += extIt.key() + "=\"" + extIt.data() + "\" "; + */ + // cross refernce + if ( ev.hasRecurrence() ) { + str += ev.recurrence().toString(); + } + if ( ev.hasStartDate() ) + str += "StartDate=\""+ OConversion::dateToString( ev.startDate() ) +"\" "; + if ( ev.hasCompletedDate() ) + str += "CompletedDate=\""+ OConversion::dateToString( ev.completedDate() ) +"\" "; + if ( ev.hasState() ) + str += "State=\""+QString::number( ev.state().state() )+"\" "; + + /* + * save reminders and notifiers! + * DATE_TIME:DURATION:SOUND:NOT_USED_YET;OTHER_DATE_TIME:OTHER_DURATION:SOUND:.... + */ + if ( ev.hasNotifiers() ) { + OPimNotifyManager manager = ev.notifiers(); + OPimNotifyManager::Alarms alarms = manager.alarms(); + if (!alarms.isEmpty() ) { + QStringList als; + OPimNotifyManager::Alarms::Iterator it = alarms.begin(); + for ( ; it != alarms.end(); ++it ) { + /* only if time is valid */ + if ( (*it).dateTime().isValid() ) { + als << OConversion::dateTimeToString( (*it).dateTime() ) + + ":" + QString::number( (*it).duration() ) + + ":" + QString::number( (*it).sound() ) + + ":"; + } + } + // now write the list + qWarning("als: %s", als.join("____________").latin1() ); + str += "Alarms=\""+als.join(";") +"\" "; + } + + /* + * now the same for reminders but more easy. We just save the uid of the OEvent. + */ + OPimNotifyManager::Reminders reminders = manager.reminders(); + if (!reminders.isEmpty() ) { + OPimNotifyManager::Reminders::Iterator it = reminders.begin(); + QStringList records; + for ( ; it != reminders.end(); ++it ) { + records << QString::number( (*it).recordUid() ); + } + str += "Reminders=\""+ records.join(";") +"\" "; + } + } + str += customToXml( ev.toExtraMap() ); + + + return str; +} +QString OTodoAccessXML::toString( const QArray& ints ) const { + return Qtopia::Record::idsToString( ints ); +} + +/* internal class for sorting + * + * Inspired by todoxmlio.cpp from TT + */ + +struct OTodoXMLContainer { + OTodo todo; +}; + +namespace { + inline QString string( const OTodo& todo) { + return todo.summary().isEmpty() ? + todo.description().left(20 ) : + todo.summary(); + } + inline int completed( const OTodo& todo1, const OTodo& todo2) { + int ret = 0; + if ( todo1.isCompleted() ) ret++; + if ( todo2.isCompleted() ) ret--; + return ret; + } + inline int priority( const OTodo& t1, const OTodo& t2) { + return ( t1.priority() - t2.priority() ); + } + inline int description( const OTodo& t1, const OTodo& t2) { + return QString::compare( string(t1), string(t2) ); + } + inline int deadline( const OTodo& t1, const OTodo& t2) { + int ret = 0; + if ( t1.hasDueDate() && + t2.hasDueDate() ) + ret = t2.dueDate().daysTo( t1.dueDate() ); + else if ( t1.hasDueDate() ) + ret = -1; + else if ( t2.hasDueDate() ) + ret = 1; + else + ret = 0; + + return ret; + } + +}; + +/* + * Returns: + * 0 if item1 == item2 + * + * non-zero if item1 != item2 + * + * This function returns int rather than bool so that reimplementations + * can return one of three values and use it to sort by: + * + * 0 if item1 == item2 + * + * > 0 (positive integer) if item1 > item2 + * + * < 0 (negative integer) if item1 < item2 + * + */ +class OTodoXMLVector : public QVector { +public: + OTodoXMLVector(int size, bool asc, int sort) + : QVector( size ) + { + setAutoDelete( true ); + m_asc = asc; + m_sort = sort; + } + /* return the summary/description */ + QString string( const OTodo& todo) { + return todo.summary().isEmpty() ? + todo.description().left(20 ) : + todo.summary(); + } + /** + * we take the sortorder( switch on it ) + * + */ + int compareItems( Item d1, Item d2 ) { + bool seComp, sePrio, seDesc, seDeadline; + seComp = sePrio = seDeadline = seDesc = false; + int ret =0; + OTodoXMLContainer* con1 = (OTodoXMLContainer*)d1; + OTodoXMLContainer* con2 = (OTodoXMLContainer*)d2; + + /* same item */ + if ( con1->todo.uid() == con2->todo.uid() ) + return 0; + + switch ( m_sort ) { + /* completed */ + case 0: { + ret = completed( con1->todo, con2->todo ); + seComp = TRUE; + break; + } + /* priority */ + case 1: { + ret = priority( con1->todo, con2->todo ); + sePrio = TRUE; + break; + } + /* description */ + case 2: { + ret = description( con1->todo, con2->todo ); + seDesc = TRUE; + break; + } + /* deadline */ + case 3: { + ret = deadline( con1->todo, con2->todo ); + seDeadline = TRUE; + break; + } + default: + ret = 0; + break; + }; + /* + * FIXME do better sorting if the first sort criteria + * ret equals 0 start with complete and so on... + */ + + /* twist it we're not ascending*/ + if (!m_asc) + ret = ret * -1; + + if ( ret ) + return ret; + + // default did not gave difference let's try it other way around + /* + * General try if already checked if not test + * and return + * 1.Completed + * 2.Priority + * 3.Description + * 4.DueDate + */ + if (!seComp ) { + if ( (ret = completed( con1->todo, con2->todo ) ) ) { + if (!m_asc ) ret *= -1; + return ret; + } + } + if (!sePrio ) { + if ( (ret = priority( con1->todo, con2->todo ) ) ) { + if (!m_asc ) ret *= -1; + return ret; + } + } + if (!seDesc ) { + if ( (ret = description(con1->todo, con2->todo ) ) ) { + if (!m_asc) ret *= -1; + return ret; + } + } + if (!seDeadline) { + if ( (ret = deadline( con1->todo, con2->todo ) ) ) { + if (!m_asc) ret *= -1; + return ret; + } + } + + return 0; + } + private: + bool m_asc; + int m_sort; + +}; + +QArray OTodoAccessXML::sorted( bool asc, int sortOrder, + int sortFilter, int cat ) { + OTodoXMLVector vector(m_events.count(), asc,sortOrder ); + QMap::Iterator it; + int item = 0; + + bool bCat = sortFilter & 1 ? true : false; + bool bOnly = sortFilter & 2 ? true : false; + bool comp = sortFilter & 4 ? true : false; + for ( it = m_events.begin(); it != m_events.end(); ++it ) { + + /* show category */ + /* -1 == unfiled */ + if ( bCat && cat == -1 ) { + if(!(*it).categories().isEmpty() ) + continue; + }else if ( bCat && cat != 0) + if (!(*it).categories().contains( cat ) ) { + continue; + } + /* isOverdue but we should not show overdue - why?*/ +/* if ( (*it).isOverdue() && !bOnly ) { + qWarning("item is overdue but !bOnly"); + continue; + } +*/ + if ( !(*it).isOverdue() && bOnly ) { + continue; + } + + if ((*it).isCompleted() && comp ) { + continue; + } + + + OTodoXMLContainer* con = new OTodoXMLContainer(); + con->todo = (*it); + vector.insert(item, con ); + item++; + } + vector.resize( item ); + /* sort it now */ + vector.sort(); + /* now get the uids */ + QArray array( vector.count() ); + for (uint i= 0; i < vector.count(); i++ ) { + array[i] = ( vector.at(i) )->todo.uid(); + } + return array; +}; +void OTodoAccessXML::removeAllCompleted() { + QMap events = m_events; + for ( QMap::Iterator it = m_events.begin(); it != m_events.end(); ++it ) { + if ( (*it).isCompleted() ) + events.remove( it.key() ); + } + m_events = events; +} +QBitArray OTodoAccessXML::supports()const { + static QBitArray ar = sup(); + return ar; +} +QBitArray OTodoAccessXML::sup() { + 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; +} +QArray OTodoAccessXML::matchRegexp( const QRegExp &r ) const +{ + QArray m_currentQuery( m_events.count() ); + uint arraycounter = 0; + + QMap::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; +} diff --git a/noncore/unsupported/libopie/pim/otodoaccessxml.h b/noncore/unsupported/libopie/pim/otodoaccessxml.h new file mode 100644 index 0000000..e4850a1 --- a/dev/null +++ b/noncore/unsupported/libopie/pim/otodoaccessxml.h @@ -0,0 +1,60 @@ +#ifndef OPIE_TODO_ACCESS_XML_H +#define OPIE_TODO_ACCESS_XML_H + +#include +#include + +#include "otodoaccessbackend.h" + +namespace Opie { + class XMLElement; +}; + +class OTodoAccessXML : public OTodoAccessBackend { +public: + /** + * fileName if Empty we will use the default path + */ + OTodoAccessXML( const QString& appName, + const QString& fileName = QString::null ); + ~OTodoAccessXML(); + + bool load(); + bool reload(); + bool save(); + + QArray allRecords()const; + QArray matchRegexp(const QRegExp &r) const; + QArray queryByExample( const OTodo&, int querysettings, const QDateTime& d = QDateTime() ); + OTodo find( int uid )const; + void clear(); + bool add( const OTodo& ); + bool remove( int uid ); + void removeAllCompleted(); + bool replace( const OTodo& ); + + /* our functions */ + QArray effectiveToDos( const QDate& start, + const QDate& end, + bool includeNoDates ); + QArray overDue(); + QArray sorted( bool asc, int sortOrder, + int sortFilter, int cat ); + QBitArray supports()const; +private: + static QBitArray sup(); + void todo( QAsciiDict*, OTodo&,const QCString&,const QString& ); + QString toString( const OTodo& )const; + QString toString( const QArray& ints ) const; + QMap m_events; + QString m_file; + QString m_app; + bool m_opened : 1; + bool m_changed : 1; + class OTodoAccessXMLPrivate; + OTodoAccessXMLPrivate* d; + int m_year, m_month, m_day; + +}; + +#endif -- cgit v0.9.0.2