-rw-r--r-- | libopie/pim/ocontactaccess.cpp | 8 | ||||
-rw-r--r-- | libopie/pim/ocontactaccess.h | 11 | ||||
-rw-r--r-- | libopie/pim/ocontactaccessbackend.h | 4 | ||||
-rw-r--r-- | libopie/pim/ocontactaccessbackend_vcard.cpp | 10 | ||||
-rw-r--r-- | libopie/pim/ocontactaccessbackend_vcard.h | 4 | ||||
-rw-r--r-- | libopie/pim/ocontactaccessbackend_xml.h | 32 | ||||
-rw-r--r-- | libopie2/opiepim/backend/ocontactaccessbackend.h | 4 | ||||
-rw-r--r-- | libopie2/opiepim/backend/ocontactaccessbackend_vcard.cpp | 10 | ||||
-rw-r--r-- | libopie2/opiepim/backend/ocontactaccessbackend_vcard.h | 4 | ||||
-rw-r--r-- | libopie2/opiepim/backend/ocontactaccessbackend_xml.h | 32 | ||||
-rw-r--r-- | libopie2/opiepim/core/ocontactaccess.cpp | 8 | ||||
-rw-r--r-- | libopie2/opiepim/core/ocontactaccess.h | 11 |
12 files changed, 138 insertions, 0 deletions
diff --git a/libopie/pim/ocontactaccess.cpp b/libopie/pim/ocontactaccess.cpp index f868b53..9c9338e 100644 --- a/libopie/pim/ocontactaccess.cpp +++ b/libopie/pim/ocontactaccess.cpp @@ -1,161 +1,169 @@ /* * 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.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 <qasciidict.h> #include <qdatetime.h> #include <qfile.h> #include <qregexp.h> #include <qlist.h> #include <qcopchannel_qws.h> //#include <qpe/qcopenvelope_qws.h> #include <qpe/global.h> #include <errno.h> #include <fcntl.h> #include <unistd.h> #include <stdlib.h> #include "ocontactaccessbackend_xml.h" OContactAccess::OContactAccess ( const QString appname, const QString , OContactAccessBackend* end, bool autosync ): OPimAccessTemplate<OContact>( 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<OContactAccessBackend>::Default( "contact", appname ); } // Set backend locally and in template m_backEnd = end; OPimAccessTemplate<OContact>::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<OContact>::wasChangedExternally() ) reload(); bool status = OPimAccessTemplate<OContact>::save(); if ( !status ) return false; /* Now tell everyone that new data is available. */ QCopEnvelope e( "QPE/PIM", "addressbookUpdated()" ); return true; } ORecordList<OContact> OContactAccess::matchRegexp( const QRegExp &r ) const{ QArray<int> matchingContacts = m_backEnd -> matchRegexp( r ); return ( ORecordList<OContact>(matchingContacts, this) ); } const uint OContactAccess::querySettings() { return ( m_backEnd->querySettings() ); } bool OContactAccess::hasQuerySettings ( int querySettings ) const { return ( m_backEnd->hasQuerySettings ( querySettings ) ); } +ORecordList<OContact> OContactAccess::sorted( bool ascending, int sortOrder, int sortFilter, int cat ) const +{ + QArray<int> matchingContacts = m_backEnd -> sorted( ascending, sortOrder, sortFilter, cat ); + return ( ORecordList<OContact>(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/libopie/pim/ocontactaccess.h b/libopie/pim/ocontactaccess.h index b4921d5..961968f 100644 --- a/libopie/pim/ocontactaccess.h +++ b/libopie/pim/ocontactaccess.h @@ -1,139 +1,150 @@ /* * 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.5 2002/11/13 14:14:51 eilers + * Added sorted for Contacts.. + * * Revision 1.4 2002/11/01 15:10:42 eilers * Added regExp-search in database for all fields in a contact. * * Revision 1.3 2002/10/16 10:52:40 eilers * Added some docu to the interface and now using the cache infrastucture by zecke.. :) * * Revision 1.2 2002/10/14 16:21:54 eilers * Some minor interface updates * * Revision 1.1 2002/09/27 17:11:44 eilers * Added API for accessing the Contact-Database ! It is compiling, but * please do not expect that anything is working ! * I will debug that stuff in the next time .. * Please read README_COMPILE for compiling ! * * ===================================================================== */ #ifndef _OCONTACTACCESS_H #define _OCONTACTACCESS_H #include <qobject.h> #include <qpe/qcopenvelope_qws.h> #include <qvaluelist.h> #include <qfileinfo.h> #include "ocontact.h" #include "ocontactaccessbackend.h" #include "opimaccesstemplate.h" /** Class to access the contacts database. * This is just a frontend for the real database handling which is * done by the backend. * @see OPimAccessTemplate */ class OContactAccess: public QObject, public OPimAccessTemplate<OContact> { Q_OBJECT public: /** Create Database with contacts (addressbook). * @param appname Name of application which wants access to the database * (i.e. "todolist") * @param filename The name of the database file. If not set, the default one * is used. * @param backend Pointer to an alternative Backend. If not set, we will use * the default backend. * @param handlesync If <b>true</b> the database stores the current state * automatically if it receives the signals <i>flush()</i> and <i>reload()</i> * which are used before and after synchronisation. If the application wants * to react itself, it should be disabled by setting it to <b>false</b> * @see OContactAccessBackend */ OContactAccess (const QString appname, const QString filename = 0l, OContactAccessBackend* backend = 0l, bool handlesync = true); ~OContactAccess (); /** Constants for query. * Use this constants to set the query parameters. * Note: <i>query_IgnoreCase</i> just make sense with one of the other attributes ! * @see queryByExample() */ enum QuerySettings { WildCards = 0x0001, IgnoreCase = 0x0002, RegExp = 0x0004, ExactMatch = 0x0008, MatchOne = 0x0010 // Only one Entry must match }; ORecordList<OContact> matchRegexp( const QRegExp &r )const; + /** Return all Contacts in a sorted manner. + * @param ascending true: Sorted in acending order. + * @param sortOrder Currently not implemented. Just defined to stay compatible to otodoaccess + * @param sortFilter Currently not implemented. Just defined to stay compatible to otodoaccess + * @param cat Currently not implemented. Just defined to stay compatible to otodoaccess + */ + List sorted( bool ascending, int sortOrder, int sortFilter, int cat ) const; + /** Return all possible settings. * @return All settings provided by the current backend * (i.e.: query_WildCards & query_IgnoreCase) */ const uint querySettings(); /** Check whether settings are correct. * @return <i>true</i> if the given settings are correct and possible. */ bool hasQuerySettings ( int querySettings ) const; /** * if the resource was changed externally. * You should use the signal instead of polling possible changes ! */ bool wasChangedExternally()const; /** Save contacts database. * Save is more a "commit". After calling this function, all changes are public available. * @return true if successful */ bool save(); signals: /* Signal is emitted if the database was changed. Therefore * we may need to reload to stay consistent. * @param which Pointer to the database who created this event. This pointer * is useful if an application has to handle multiple databases at the same time. * @see reload() */ void signalChanged ( const OContactAccess *which ); private: // class OContactAccessPrivate; // OContactAccessPrivate* d; OContactAccessBackend *m_backEnd; bool m_loading:1; private slots: void copMessage( const QCString &msg, const QByteArray &data ); }; #endif diff --git a/libopie/pim/ocontactaccessbackend.h b/libopie/pim/ocontactaccessbackend.h index c898f61..821f5bf 100644 --- a/libopie/pim/ocontactaccessbackend.h +++ b/libopie/pim/ocontactaccessbackend.h @@ -1,81 +1,85 @@ /** * 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.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 "qregexp.h" class OContactAccessBackend: public OPimAccessBackend<OContact> { public: OContactAccessBackend() {} 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 <i>true</i> if the database was changed and if save without reload will * be dangerous. <i>false</i> if the database was not changed or it is save to write * in this situation. */ virtual bool wasChangedExternally() = 0; virtual QArray<int> 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 <i>true</i> if the given settings are correct and possible. */ virtual bool hasQuerySettings (uint querySettings) const = 0; + virtual QArray<int> sorted( bool ascending, int sortOrder, int sortFilter, int cat ) = 0; }; #endif diff --git a/libopie/pim/ocontactaccessbackend_vcard.cpp b/libopie/pim/ocontactaccessbackend_vcard.cpp index faa72b4..09ae37b 100644 --- a/libopie/pim/ocontactaccessbackend_vcard.cpp +++ b/libopie/pim/ocontactaccessbackend_vcard.cpp @@ -1,560 +1,570 @@ /* * 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.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 <qpe/timeconversion.h> #include <qfile.h> OContactAccessBackend_VCard::OContactAccessBackend_VCard ( QString , QString filename ): m_dirty( false ), m_file( filename ) { load(); } bool OContactAccessBackend_VCard::load () { m_map.clear(); m_dirty = false; VObject* obj = 0l; obj = Parse_MIME_FromFileName( QFile::encodeName(m_file).data() ); if ( !obj ) 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<int, OContact>::ConstIterator it=m_map.begin(); it !=m_map.end(); ++it ){ vo = createVObject( *it ); writeVObject( file.directHandle() , vo ); cleanVObject( vo ); } cleanStrTbl(); 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<int> OContactAccessBackend_VCard::allRecords() const { QArray<int> ar( m_map.count() ); QMap<int, OContact>::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<int> OContactAccessBackend_VCard::queryByExample ( const OContact&, int ) { QArray<int> ar(0); return ar; } // Not implemented QArray<int> OContactAccessBackend_VCard::matchRegexp( const QRegExp& ) const { QArray<int> 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<int> OContactAccessBackend_VCard::sorted( bool , int, int, int ) +{ + QArray<int> 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; if ( (type & (VOICE|HOME) ) == (VOICE|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) ) 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( TimeConversion::fromString( 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 ) ); } #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() ); 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() ){ QString birthd_rfc2425 = QString("%1-%2-%3") .arg( c.birthday().year() ) .arg( c.birthday().month(), 2 ) .arg( c.birthday().day(), 2 ); // Now replace spaces with "0"... int pos = 0; while ( ( pos = birthd_rfc2425.find (' ') ) > 0 ) birthd_rfc2425.replace( pos, 1, "0" ); qWarning("Exporting birthday as: %s", birthd_rfc2425.latin1()); safeAddPropValue( vcard, VCBirthDateProp, birthd_rfc2425.latin1() ); } 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() ); safeAddPropValue( vcard, "X-Qtopia-Anniversary", TimeConversion::toString( c.anniversary() ) ); safeAddPropValue( vcard, "X-Qtopia-Nickname", c.nickname() ); safeAddPropValue( vcard, "X-Qtopia-Children", c.children() ); return vcard; } 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/libopie/pim/ocontactaccessbackend_vcard.h b/libopie/pim/ocontactaccessbackend_vcard.h index 177ec24..4437756 100644 --- a/libopie/pim/ocontactaccessbackend_vcard.h +++ b/libopie/pim/ocontactaccessbackend_vcard.h @@ -1,70 +1,74 @@ /* * 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.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 <opie/ocontact.h> #include "ocontactaccessbackend.h" class VObject; class OContactAccessBackend_VCard : public OContactAccessBackend { public: OContactAccessBackend_VCard ( QString appname, QString filename = 0l ); 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<int> allRecords() const; QArray<int> queryByExample ( const OContact &query, int settings ); QArray<int> matchRegexp( const QRegExp &r ) const; const uint querySettings(); bool hasQuerySettings (uint querySettings) const; + QArray<int> sorted( bool ascending, int sortOrder, int sortFilter, int cat ); bool wasChangedExternally(); private: OContact parseVObject( VObject* obj ); VObject* createVObject( const OContact& c ); 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<int, OContact> m_map; }; #endif diff --git a/libopie/pim/ocontactaccessbackend_xml.h b/libopie/pim/ocontactaccessbackend_xml.h index f7e8207..8b95102 100644 --- a/libopie/pim/ocontactaccessbackend_xml.h +++ b/libopie/pim/ocontactaccessbackend_xml.h @@ -1,602 +1,634 @@ /* * 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. * ===================================================================== * ToDo: XML-Backend: Automatic reload if something was changed... * * * ===================================================================== * Version: $Id$ * ===================================================================== * History: * $Log$ + * 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 <qasciidict.h> #include <qdatetime.h> #include <qfile.h> #include <qfileinfo.h> #include <qregexp.h> #include <qarray.h> +#include <qmap.h> #include <qpe/global.h> #include <opie/xmltree.h> #include "ocontactaccessbackend.h" #include "ocontactaccess.h" #include <stdlib.h> #include <errno.h> using namespace Opie; /* the default xml implementation */ class OContactAccessBackend_XML : public OContactAccessBackend { public: OContactAccessBackend_XML ( QString appname, QString filename = 0l ): m_changed( 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 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; QString out; out = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><!DOCTYPE Addressbook ><AddressBook>\n" " <Groups>\n" " </Groups>\n" " <Contacts>\n"; //QValueList<Contact>::iterator it; QValueListConstIterator<OContact> it; for ( it = m_contactList.begin(); it != m_contactList.end(); ++it ) { out += "<Contact "; (*it).save( out ); out += "/>\n"; QCString cstr = out.utf8(); total_written = f.writeBlock( cstr.data(), cstr.length() ); if ( total_written != int(cstr.length()) ) { f.close(); QFile::remove( strNewFile ); return false; } out = ""; } out += " </Contacts>\n</AddressBook>\n"; QCString 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 load () { m_contactList.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 clear () { m_contactList.clear(); m_changed = false; } bool wasChangedExternally() { QFileInfo fi( m_fileName ); QDateTime lastmod = fi.lastModified (); return (lastmod != m_readtime); } QArray<int> allRecords() const { QArray<int> uid_list( m_contactList.count() ); uint counter = 0; QValueListConstIterator<OContact> it; for( it = m_contactList.begin(); it != m_contactList.end(); ++it ){ uid_list[counter++] = (*it).uid(); } return ( uid_list ); } OContact find ( int uid ) const { bool found = false; OContact foundContact; //Create empty contact QValueListConstIterator<OContact> it; for( it = m_contactList.begin(); it != m_contactList.end(); ++it ){ if ((*it).uid() == uid){ found = true; break; } } if ( found ){ foundContact = *it; } return ( foundContact ); } QArray<int> queryByExample ( const OContact &query, int settings ){ QArray<int> m_currentQuery( m_contactList.count() ); QValueListConstIterator<OContact> it; uint arraycounter = 0; for( it = m_contactList.begin(); it != m_contactList.end(); ++it ){ /* Search all fields and compare them with query object. Store them into list * if all fields matches. */ bool allcorrect = true; for ( int i = 0; i < Qtopia::rid; i++ ) { /* Just compare fields which are not empty in the query object */ if ( !query.field(i).isEmpty() ){ switch ( settings & ~OContactAccess::IgnoreCase ){ 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<int> matchRegexp( const QRegExp &r ) const{ QArray<int> m_currentQuery( m_contactList.count() ); QValueListConstIterator<OContact> it; uint arraycounter = 0; for( it = m_contactList.begin(); it != m_contactList.end(); ++it ){ if ( (*it).match( r ) ){ m_currentQuery[arraycounter++] = (*it).uid(); } } // Shrink to fit.. m_currentQuery.resize(arraycounter); return m_currentQuery; } const uint querySettings() { return ( OContactAccess::WildCards & OContactAccess::IgnoreCase & OContactAccess::RegExp & OContactAccess::ExactMatch ); } bool hasQuerySettings (uint querySettings) const { /* OContactAccess::IgnoreCase may be added with one * of the other settings, but never used alone. * The other settings are just valid alone... */ switch ( querySettings & ~OContactAccess::IgnoreCase ){ case OContactAccess::RegExp: return ( true ); case OContactAccess::WildCards: return ( true ); case OContactAccess::ExactMatch: return ( true ); default: return ( false ); } } + // Currently only asc implemented.. + QArray<int> sorted( bool asc, int , int , int ) + { + QMap<QString, int> nameToUid; + QStringList names; + QArray<int> m_currentQuery( m_contactList.count() ); + + // First fill map and StringList with all Names ( better LastNames ? ) + // Afterwards sort namelist and use map to fill array to return.. + QValueListConstIterator<OContact> it; + for( it = m_contactList.begin(); it != m_contactList.end(); ++it ){ + names.append( (*it).lastName() ); + nameToUid.insert( (*it).lastName(), (*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 add ( const OContact &newcontact ) { //qWarning("odefaultbackend: ACTION::ADD"); updateJournal (newcontact, OContact::ACTION_ADD); addContact_p( newcontact ); m_changed = true; return true; } bool replace ( const OContact &contact ) { m_changed = true; bool found = false; QValueListIterator<OContact> it; for( it = m_contactList.begin(); it != m_contactList.end(); ++it ){ if ( (*it).uid() == contact.uid() ){ found = true; break; } } if (found) { updateJournal (contact, OContact::ACTION_REPLACE); m_contactList.remove (it); m_contactList.append (contact); return true; } else return false; } bool remove ( int uid ) { m_changed = true; bool found = false; QValueListIterator<OContact> it; for( it = m_contactList.begin(); it != m_contactList.end(); ++it ){ if ((*it).uid() == uid){ found = true; break; } } if (found) { updateJournal ( *it, OContact::ACTION_REMOVE); m_contactList.remove (it); return true; } else return false; } bool reload(){ /* Reload is the same as load in this implementation */ return ( load() ); } private: void addContact_p( const OContact &newcontact ){ m_contactList.append (newcontact); } /* This function loads the xml-database and the journalfile */ bool 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; OContact::journal_action action = OContact::ACTION_ADD; int journalKey = 0; QMap<int, QString> contactMap; QMap<QString, QString> customMap; QMap<QString, QString>::Iterator customIt; QAsciiDict<int> 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 = OContact::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 OContact::ACTION_ADD: addContact_p (contact); break; case OContact::ACTION_REMOVE: if ( !remove (contact.uid()) ) qWarning ("ODefBack(journal)::Unable to remove uid: %d", contact.uid() ); break; case OContact::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 updateJournal( const OContact& cnt, OContact::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 "<CONTACTS>" 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 = "<Contacts>"; QCString cstr = buf.utf8(); f.writeBlock( cstr.data(), cstr.length() ); } buf = "<Contact "; cnt.save( buf ); buf += " action=\"" + QString::number( (int)action ) + "\" "; buf += "/>\n"; QCString cstr = buf.utf8(); f.writeBlock( cstr.data(), cstr.length() ); } void removeJournal() { QFile f ( m_journalName ); if ( f.exists() ) f.remove(); } protected: bool m_changed; QString m_journalName; QString m_fileName; QString m_appName; QValueList<OContact> m_contactList; QDateTime m_readtime; }; #endif diff --git a/libopie2/opiepim/backend/ocontactaccessbackend.h b/libopie2/opiepim/backend/ocontactaccessbackend.h index c898f61..821f5bf 100644 --- a/libopie2/opiepim/backend/ocontactaccessbackend.h +++ b/libopie2/opiepim/backend/ocontactaccessbackend.h @@ -1,81 +1,85 @@ /** * 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.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 "qregexp.h" class OContactAccessBackend: public OPimAccessBackend<OContact> { public: OContactAccessBackend() {} 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 <i>true</i> if the database was changed and if save without reload will * be dangerous. <i>false</i> if the database was not changed or it is save to write * in this situation. */ virtual bool wasChangedExternally() = 0; virtual QArray<int> 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 <i>true</i> if the given settings are correct and possible. */ virtual bool hasQuerySettings (uint querySettings) const = 0; + virtual QArray<int> sorted( bool ascending, int sortOrder, int sortFilter, int cat ) = 0; }; #endif diff --git a/libopie2/opiepim/backend/ocontactaccessbackend_vcard.cpp b/libopie2/opiepim/backend/ocontactaccessbackend_vcard.cpp index faa72b4..09ae37b 100644 --- a/libopie2/opiepim/backend/ocontactaccessbackend_vcard.cpp +++ b/libopie2/opiepim/backend/ocontactaccessbackend_vcard.cpp @@ -1,560 +1,570 @@ /* * 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.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 <qpe/timeconversion.h> #include <qfile.h> OContactAccessBackend_VCard::OContactAccessBackend_VCard ( QString , QString filename ): m_dirty( false ), m_file( filename ) { load(); } bool OContactAccessBackend_VCard::load () { m_map.clear(); m_dirty = false; VObject* obj = 0l; obj = Parse_MIME_FromFileName( QFile::encodeName(m_file).data() ); if ( !obj ) 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<int, OContact>::ConstIterator it=m_map.begin(); it !=m_map.end(); ++it ){ vo = createVObject( *it ); writeVObject( file.directHandle() , vo ); cleanVObject( vo ); } cleanStrTbl(); 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<int> OContactAccessBackend_VCard::allRecords() const { QArray<int> ar( m_map.count() ); QMap<int, OContact>::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<int> OContactAccessBackend_VCard::queryByExample ( const OContact&, int ) { QArray<int> ar(0); return ar; } // Not implemented QArray<int> OContactAccessBackend_VCard::matchRegexp( const QRegExp& ) const { QArray<int> 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<int> OContactAccessBackend_VCard::sorted( bool , int, int, int ) +{ + QArray<int> 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; if ( (type & (VOICE|HOME) ) == (VOICE|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) ) 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( TimeConversion::fromString( 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 ) ); } #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() ); 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() ){ QString birthd_rfc2425 = QString("%1-%2-%3") .arg( c.birthday().year() ) .arg( c.birthday().month(), 2 ) .arg( c.birthday().day(), 2 ); // Now replace spaces with "0"... int pos = 0; while ( ( pos = birthd_rfc2425.find (' ') ) > 0 ) birthd_rfc2425.replace( pos, 1, "0" ); qWarning("Exporting birthday as: %s", birthd_rfc2425.latin1()); safeAddPropValue( vcard, VCBirthDateProp, birthd_rfc2425.latin1() ); } 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() ); safeAddPropValue( vcard, "X-Qtopia-Anniversary", TimeConversion::toString( c.anniversary() ) ); safeAddPropValue( vcard, "X-Qtopia-Nickname", c.nickname() ); safeAddPropValue( vcard, "X-Qtopia-Children", c.children() ); return vcard; } 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/libopie2/opiepim/backend/ocontactaccessbackend_vcard.h b/libopie2/opiepim/backend/ocontactaccessbackend_vcard.h index 177ec24..4437756 100644 --- a/libopie2/opiepim/backend/ocontactaccessbackend_vcard.h +++ b/libopie2/opiepim/backend/ocontactaccessbackend_vcard.h @@ -1,70 +1,74 @@ /* * 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.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 <opie/ocontact.h> #include "ocontactaccessbackend.h" class VObject; class OContactAccessBackend_VCard : public OContactAccessBackend { public: OContactAccessBackend_VCard ( QString appname, QString filename = 0l ); 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<int> allRecords() const; QArray<int> queryByExample ( const OContact &query, int settings ); QArray<int> matchRegexp( const QRegExp &r ) const; const uint querySettings(); bool hasQuerySettings (uint querySettings) const; + QArray<int> sorted( bool ascending, int sortOrder, int sortFilter, int cat ); bool wasChangedExternally(); private: OContact parseVObject( VObject* obj ); VObject* createVObject( const OContact& c ); 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<int, OContact> m_map; }; #endif diff --git a/libopie2/opiepim/backend/ocontactaccessbackend_xml.h b/libopie2/opiepim/backend/ocontactaccessbackend_xml.h index f7e8207..8b95102 100644 --- a/libopie2/opiepim/backend/ocontactaccessbackend_xml.h +++ b/libopie2/opiepim/backend/ocontactaccessbackend_xml.h @@ -1,602 +1,634 @@ /* * 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. * ===================================================================== * ToDo: XML-Backend: Automatic reload if something was changed... * * * ===================================================================== * Version: $Id$ * ===================================================================== * History: * $Log$ + * 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 <qasciidict.h> #include <qdatetime.h> #include <qfile.h> #include <qfileinfo.h> #include <qregexp.h> #include <qarray.h> +#include <qmap.h> #include <qpe/global.h> #include <opie/xmltree.h> #include "ocontactaccessbackend.h" #include "ocontactaccess.h" #include <stdlib.h> #include <errno.h> using namespace Opie; /* the default xml implementation */ class OContactAccessBackend_XML : public OContactAccessBackend { public: OContactAccessBackend_XML ( QString appname, QString filename = 0l ): m_changed( 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 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; QString out; out = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><!DOCTYPE Addressbook ><AddressBook>\n" " <Groups>\n" " </Groups>\n" " <Contacts>\n"; //QValueList<Contact>::iterator it; QValueListConstIterator<OContact> it; for ( it = m_contactList.begin(); it != m_contactList.end(); ++it ) { out += "<Contact "; (*it).save( out ); out += "/>\n"; QCString cstr = out.utf8(); total_written = f.writeBlock( cstr.data(), cstr.length() ); if ( total_written != int(cstr.length()) ) { f.close(); QFile::remove( strNewFile ); return false; } out = ""; } out += " </Contacts>\n</AddressBook>\n"; QCString 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 load () { m_contactList.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 clear () { m_contactList.clear(); m_changed = false; } bool wasChangedExternally() { QFileInfo fi( m_fileName ); QDateTime lastmod = fi.lastModified (); return (lastmod != m_readtime); } QArray<int> allRecords() const { QArray<int> uid_list( m_contactList.count() ); uint counter = 0; QValueListConstIterator<OContact> it; for( it = m_contactList.begin(); it != m_contactList.end(); ++it ){ uid_list[counter++] = (*it).uid(); } return ( uid_list ); } OContact find ( int uid ) const { bool found = false; OContact foundContact; //Create empty contact QValueListConstIterator<OContact> it; for( it = m_contactList.begin(); it != m_contactList.end(); ++it ){ if ((*it).uid() == uid){ found = true; break; } } if ( found ){ foundContact = *it; } return ( foundContact ); } QArray<int> queryByExample ( const OContact &query, int settings ){ QArray<int> m_currentQuery( m_contactList.count() ); QValueListConstIterator<OContact> it; uint arraycounter = 0; for( it = m_contactList.begin(); it != m_contactList.end(); ++it ){ /* Search all fields and compare them with query object. Store them into list * if all fields matches. */ bool allcorrect = true; for ( int i = 0; i < Qtopia::rid; i++ ) { /* Just compare fields which are not empty in the query object */ if ( !query.field(i).isEmpty() ){ switch ( settings & ~OContactAccess::IgnoreCase ){ 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<int> matchRegexp( const QRegExp &r ) const{ QArray<int> m_currentQuery( m_contactList.count() ); QValueListConstIterator<OContact> it; uint arraycounter = 0; for( it = m_contactList.begin(); it != m_contactList.end(); ++it ){ if ( (*it).match( r ) ){ m_currentQuery[arraycounter++] = (*it).uid(); } } // Shrink to fit.. m_currentQuery.resize(arraycounter); return m_currentQuery; } const uint querySettings() { return ( OContactAccess::WildCards & OContactAccess::IgnoreCase & OContactAccess::RegExp & OContactAccess::ExactMatch ); } bool hasQuerySettings (uint querySettings) const { /* OContactAccess::IgnoreCase may be added with one * of the other settings, but never used alone. * The other settings are just valid alone... */ switch ( querySettings & ~OContactAccess::IgnoreCase ){ case OContactAccess::RegExp: return ( true ); case OContactAccess::WildCards: return ( true ); case OContactAccess::ExactMatch: return ( true ); default: return ( false ); } } + // Currently only asc implemented.. + QArray<int> sorted( bool asc, int , int , int ) + { + QMap<QString, int> nameToUid; + QStringList names; + QArray<int> m_currentQuery( m_contactList.count() ); + + // First fill map and StringList with all Names ( better LastNames ? ) + // Afterwards sort namelist and use map to fill array to return.. + QValueListConstIterator<OContact> it; + for( it = m_contactList.begin(); it != m_contactList.end(); ++it ){ + names.append( (*it).lastName() ); + nameToUid.insert( (*it).lastName(), (*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 add ( const OContact &newcontact ) { //qWarning("odefaultbackend: ACTION::ADD"); updateJournal (newcontact, OContact::ACTION_ADD); addContact_p( newcontact ); m_changed = true; return true; } bool replace ( const OContact &contact ) { m_changed = true; bool found = false; QValueListIterator<OContact> it; for( it = m_contactList.begin(); it != m_contactList.end(); ++it ){ if ( (*it).uid() == contact.uid() ){ found = true; break; } } if (found) { updateJournal (contact, OContact::ACTION_REPLACE); m_contactList.remove (it); m_contactList.append (contact); return true; } else return false; } bool remove ( int uid ) { m_changed = true; bool found = false; QValueListIterator<OContact> it; for( it = m_contactList.begin(); it != m_contactList.end(); ++it ){ if ((*it).uid() == uid){ found = true; break; } } if (found) { updateJournal ( *it, OContact::ACTION_REMOVE); m_contactList.remove (it); return true; } else return false; } bool reload(){ /* Reload is the same as load in this implementation */ return ( load() ); } private: void addContact_p( const OContact &newcontact ){ m_contactList.append (newcontact); } /* This function loads the xml-database and the journalfile */ bool 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; OContact::journal_action action = OContact::ACTION_ADD; int journalKey = 0; QMap<int, QString> contactMap; QMap<QString, QString> customMap; QMap<QString, QString>::Iterator customIt; QAsciiDict<int> 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 = OContact::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 OContact::ACTION_ADD: addContact_p (contact); break; case OContact::ACTION_REMOVE: if ( !remove (contact.uid()) ) qWarning ("ODefBack(journal)::Unable to remove uid: %d", contact.uid() ); break; case OContact::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 updateJournal( const OContact& cnt, OContact::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 "<CONTACTS>" 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 = "<Contacts>"; QCString cstr = buf.utf8(); f.writeBlock( cstr.data(), cstr.length() ); } buf = "<Contact "; cnt.save( buf ); buf += " action=\"" + QString::number( (int)action ) + "\" "; buf += "/>\n"; QCString cstr = buf.utf8(); f.writeBlock( cstr.data(), cstr.length() ); } void removeJournal() { QFile f ( m_journalName ); if ( f.exists() ) f.remove(); } protected: bool m_changed; QString m_journalName; QString m_fileName; QString m_appName; QValueList<OContact> m_contactList; QDateTime m_readtime; }; #endif diff --git a/libopie2/opiepim/core/ocontactaccess.cpp b/libopie2/opiepim/core/ocontactaccess.cpp index f868b53..9c9338e 100644 --- a/libopie2/opiepim/core/ocontactaccess.cpp +++ b/libopie2/opiepim/core/ocontactaccess.cpp @@ -1,161 +1,169 @@ /* * 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.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 <qasciidict.h> #include <qdatetime.h> #include <qfile.h> #include <qregexp.h> #include <qlist.h> #include <qcopchannel_qws.h> //#include <qpe/qcopenvelope_qws.h> #include <qpe/global.h> #include <errno.h> #include <fcntl.h> #include <unistd.h> #include <stdlib.h> #include "ocontactaccessbackend_xml.h" OContactAccess::OContactAccess ( const QString appname, const QString , OContactAccessBackend* end, bool autosync ): OPimAccessTemplate<OContact>( 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<OContactAccessBackend>::Default( "contact", appname ); } // Set backend locally and in template m_backEnd = end; OPimAccessTemplate<OContact>::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<OContact>::wasChangedExternally() ) reload(); bool status = OPimAccessTemplate<OContact>::save(); if ( !status ) return false; /* Now tell everyone that new data is available. */ QCopEnvelope e( "QPE/PIM", "addressbookUpdated()" ); return true; } ORecordList<OContact> OContactAccess::matchRegexp( const QRegExp &r ) const{ QArray<int> matchingContacts = m_backEnd -> matchRegexp( r ); return ( ORecordList<OContact>(matchingContacts, this) ); } const uint OContactAccess::querySettings() { return ( m_backEnd->querySettings() ); } bool OContactAccess::hasQuerySettings ( int querySettings ) const { return ( m_backEnd->hasQuerySettings ( querySettings ) ); } +ORecordList<OContact> OContactAccess::sorted( bool ascending, int sortOrder, int sortFilter, int cat ) const +{ + QArray<int> matchingContacts = m_backEnd -> sorted( ascending, sortOrder, sortFilter, cat ); + return ( ORecordList<OContact>(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/libopie2/opiepim/core/ocontactaccess.h b/libopie2/opiepim/core/ocontactaccess.h index b4921d5..961968f 100644 --- a/libopie2/opiepim/core/ocontactaccess.h +++ b/libopie2/opiepim/core/ocontactaccess.h @@ -1,139 +1,150 @@ /* * 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.5 2002/11/13 14:14:51 eilers + * Added sorted for Contacts.. + * * Revision 1.4 2002/11/01 15:10:42 eilers * Added regExp-search in database for all fields in a contact. * * Revision 1.3 2002/10/16 10:52:40 eilers * Added some docu to the interface and now using the cache infrastucture by zecke.. :) * * Revision 1.2 2002/10/14 16:21:54 eilers * Some minor interface updates * * Revision 1.1 2002/09/27 17:11:44 eilers * Added API for accessing the Contact-Database ! It is compiling, but * please do not expect that anything is working ! * I will debug that stuff in the next time .. * Please read README_COMPILE for compiling ! * * ===================================================================== */ #ifndef _OCONTACTACCESS_H #define _OCONTACTACCESS_H #include <qobject.h> #include <qpe/qcopenvelope_qws.h> #include <qvaluelist.h> #include <qfileinfo.h> #include "ocontact.h" #include "ocontactaccessbackend.h" #include "opimaccesstemplate.h" /** Class to access the contacts database. * This is just a frontend for the real database handling which is * done by the backend. * @see OPimAccessTemplate */ class OContactAccess: public QObject, public OPimAccessTemplate<OContact> { Q_OBJECT public: /** Create Database with contacts (addressbook). * @param appname Name of application which wants access to the database * (i.e. "todolist") * @param filename The name of the database file. If not set, the default one * is used. * @param backend Pointer to an alternative Backend. If not set, we will use * the default backend. * @param handlesync If <b>true</b> the database stores the current state * automatically if it receives the signals <i>flush()</i> and <i>reload()</i> * which are used before and after synchronisation. If the application wants * to react itself, it should be disabled by setting it to <b>false</b> * @see OContactAccessBackend */ OContactAccess (const QString appname, const QString filename = 0l, OContactAccessBackend* backend = 0l, bool handlesync = true); ~OContactAccess (); /** Constants for query. * Use this constants to set the query parameters. * Note: <i>query_IgnoreCase</i> just make sense with one of the other attributes ! * @see queryByExample() */ enum QuerySettings { WildCards = 0x0001, IgnoreCase = 0x0002, RegExp = 0x0004, ExactMatch = 0x0008, MatchOne = 0x0010 // Only one Entry must match }; ORecordList<OContact> matchRegexp( const QRegExp &r )const; + /** Return all Contacts in a sorted manner. + * @param ascending true: Sorted in acending order. + * @param sortOrder Currently not implemented. Just defined to stay compatible to otodoaccess + * @param sortFilter Currently not implemented. Just defined to stay compatible to otodoaccess + * @param cat Currently not implemented. Just defined to stay compatible to otodoaccess + */ + List sorted( bool ascending, int sortOrder, int sortFilter, int cat ) const; + /** Return all possible settings. * @return All settings provided by the current backend * (i.e.: query_WildCards & query_IgnoreCase) */ const uint querySettings(); /** Check whether settings are correct. * @return <i>true</i> if the given settings are correct and possible. */ bool hasQuerySettings ( int querySettings ) const; /** * if the resource was changed externally. * You should use the signal instead of polling possible changes ! */ bool wasChangedExternally()const; /** Save contacts database. * Save is more a "commit". After calling this function, all changes are public available. * @return true if successful */ bool save(); signals: /* Signal is emitted if the database was changed. Therefore * we may need to reload to stay consistent. * @param which Pointer to the database who created this event. This pointer * is useful if an application has to handle multiple databases at the same time. * @see reload() */ void signalChanged ( const OContactAccess *which ); private: // class OContactAccessPrivate; // OContactAccessPrivate* d; OContactAccessBackend *m_backEnd; bool m_loading:1; private slots: void copMessage( const QCString &msg, const QByteArray &data ); }; #endif |