summaryrefslogtreecommitdiff
Side-by-side diff
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--libopie/libopie.pro1
-rw-r--r--libopie/pim/ocontactaccessbackend_vcard.cpp10
-rw-r--r--libopie/pim/ocontactaccessbackend_vcard.h10
-rw-r--r--libopie/pim/ocontactaccessbackend_xml.cpp166
-rw-r--r--libopie/pim/ocontactaccessbackend_xml.h677
-rw-r--r--libopie/pim/odatebookaccessbackend_xml.cpp2
-rw-r--r--libopie/pim/odatebookaccessbackend_xml.h2
-rw-r--r--libopie/pim/opimaccessbackend.h6
-rw-r--r--libopie/pim/opimaccesstemplate.h6
-rw-r--r--libopie/pim/otodoaccesssql.cpp2
-rw-r--r--libopie/pim/otodoaccesssql.h2
-rw-r--r--libopie/pim/otodoaccessvcal.cpp2
-rw-r--r--libopie/pim/otodoaccessvcal.h2
-rw-r--r--libopie/pim/otodoaccessxml.cpp2
-rw-r--r--libopie/pim/otodoaccessxml.h2
-rw-r--r--libopie2/opiepim/backend/ocontactaccessbackend_vcard.cpp10
-rw-r--r--libopie2/opiepim/backend/ocontactaccessbackend_vcard.h10
-rw-r--r--libopie2/opiepim/backend/ocontactaccessbackend_xml.cpp166
-rw-r--r--libopie2/opiepim/backend/ocontactaccessbackend_xml.h677
-rw-r--r--libopie2/opiepim/backend/odatebookaccessbackend_xml.cpp2
-rw-r--r--libopie2/opiepim/backend/odatebookaccessbackend_xml.h2
-rw-r--r--libopie2/opiepim/backend/opimaccessbackend.h6
-rw-r--r--libopie2/opiepim/backend/otodoaccesssql.cpp2
-rw-r--r--libopie2/opiepim/backend/otodoaccesssql.h2
-rw-r--r--libopie2/opiepim/backend/otodoaccessvcal.cpp2
-rw-r--r--libopie2/opiepim/backend/otodoaccessvcal.h2
-rw-r--r--libopie2/opiepim/backend/otodoaccessxml.cpp2
-rw-r--r--libopie2/opiepim/backend/otodoaccessxml.h2
-rw-r--r--libopie2/opiepim/core/opimaccesstemplate.h6
29 files changed, 347 insertions, 1436 deletions
diff --git a/libopie/libopie.pro b/libopie/libopie.pro
index 391f30e..2bf9985 100644
--- a/libopie/libopie.pro
+++ b/libopie/libopie.pro
@@ -1,118 +1,119 @@
TEMPLATE = lib
CONFIG += qte warn_on release
HEADERS = ofontmenu.h \
ocolorbutton.h \
ofiledialog.h ofileselector.h \
ocheckitem.h \
xmltree.h \
colordialog.h colorpopupmenu.h \
oclickablelabel.h oprocctrl.h \
oprocess.h odevice.h odevicebutton.h \
otimepicker.h otabwidget.h \
otabbar.h otabinfo.h \
ofontselector.h \
pim/opimrecord.h \
pim/otodo.h \
pim/orecordlist.h \
pim/opimaccesstemplate.h \
pim/opimaccessbackend.h \
pim/otodoaccess.h \
pim/otodoaccessbackend.h \
pim/ocontact.h \
pim/ocontactaccess.h \
pim/ocontactaccessbackend.h \
pim/ocontactaccessbackend_xml.h \
pim/ocontactaccessbackend_vcard.h \
pim/obackendfactory.h \
pim/opimcache.h \
pim/otodoaccessvcal.h \
pim/orecur.h \
pim/opimstate.h \
pim/opimxrefpartner.h \
pim/opimxref.h \
pim/opimxrefmanager.h \
pim/opimmaintainer.h \
pim/opimnotify.h \
pim/opimnotifymanager.h \
pim/opimmainwindow.h \
pim/opimresolver.h \
pim/oevent.h \
pim/otimezone.h \
pim/odatebookaccess.h \
pim/odatebookaccessbackend.h \
pim/odatebookaccessbackend_xml.h \
orecurrancewidget.h \
oticker.h
# pim/otodoaccesssql.h \
SOURCES = ofontmenu.cc \
ocolorbutton.cpp \
sharp_compat.cpp \
xmltree.cc \
ofiledialog.cc ofileselector.cc \
ocheckitem.cpp \
colordialog.cpp \
colorpopupmenu.cpp oclickablelabel.cpp \
oprocctrl.cpp oprocess.cpp \
odevice.cpp odevicebutton.cpp otimepicker.cpp \
otabwidget.cpp otabbar.cpp \
ofontselector.cpp \
pim/otodo.cpp \
pim/opimrecord.cpp \
pim/otodoaccess.cpp \
pim/otodoaccessbackend.cpp \
pim/otodoaccessxml.cpp \
pim/ocontact.cpp \
pim/ocontactaccess.cpp \
pim/ocontactaccessbackend_vcard.cpp \
+ pim/ocontactaccessbackend_xml.cpp \
pim/otodoaccessvcal.cpp \
pim/orecur.cpp \
pim/opimstate.cpp \
pim/opimxrefpartner.cpp \
pim/opimxref.cpp \
pim/opimxrefmanager.cpp \
pim/opimmaintainer.cpp \
pim/opimnotify.cpp \
pim/opimnotifymanager.cpp \
pim/opimmainwindow.cpp \
pim/opimresolver.cpp \
pim/oevent.cpp \
pim/otimezone.cpp \
pim/odatebookaccess.cpp \
pim/odatebookaccessbackend.cpp \
pim/odatebookaccessbackend_xml.cpp \
orecurrancewidget.cpp \
oticker.cpp
# pim/otodoaccesssql.cpp \
TARGET = opie
INCLUDEPATH += $(OPIEDIR)/include
DESTDIR = $(OPIEDIR)/lib$(PROJMAK)
#VERSION = 1.0.0
LIBS += -lqpe
# LIBS += -lopiesql
INTERFACES = otimepickerbase.ui orecurrancebase.ui
TRANSLATIONS = ../i18n/de/libopie.ts \
../i18n/xx/libopie.ts \
../i18n/en/libopie.ts \
../i18n/es/libopie.ts \
../i18n/fr/libopie.ts \
../i18n/hu/libopie.ts \
../i18n/ja/libopie.ts \
../i18n/ko/libopie.ts \
../i18n/no/libopie.ts \
../i18n/pl/libopie.ts \
../i18n/pt/libopie.ts \
../i18n/pt_BR/libopie.ts \
../i18n/sl/libopie.ts \
../i18n/zh_CN/libopie.ts \
../i18n/zh_TW/libopie.ts \
../i18n/da/libopie.ts
include ( $(OPIEDIR)/include.pro )
diff --git a/libopie/pim/ocontactaccessbackend_vcard.cpp b/libopie/pim/ocontactaccessbackend_vcard.cpp
index e537269..f24523f 100644
--- a/libopie/pim/ocontactaccessbackend_vcard.cpp
+++ b/libopie/pim/ocontactaccessbackend_vcard.cpp
@@ -1,615 +1,623 @@
/*
* 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.9 2003/03/21 10:33:09 eilers
+ * Merged speed optimized xml backend for contacts to main.
+ * Added QDateTime to querybyexample. For instance, it is now possible to get
+ * all Birthdays/Anniversaries between two dates. This should be used
+ * to show all birthdays in the datebook..
+ * This change is sourcecode backward compatible but you have to upgrade
+ * the binaries for today-addressbook.
+ *
* Revision 1.8 2003/02/21 16:52:49 zecke
* -Remove old Todo classes they're deprecated and today I already using the
* new API
* -Guard against self assignment in OTodo
* -Add test apps for OPIM
* -Opiefied Event classes
* -Added TimeZone handling and pinning of TimeZones to OEvent
* -Adjust ORecur and the widget to better timezone behaviour
*
* Revision 1.7 2003/02/16 22:25:46 zecke
* 0000276 Fix for that bug.. or better temp workaround
* A Preferred Number is HOME|VOICE
* A CellPhone is HOME|VOICE|CELL the type & HOME|VOICE test
* triggers both
* and the cell phone number overrides the other entries..
*
* as a temp I check that it's not equal to HOME|VOICE|CELL before setting the
* number
*
* The right and final fix would be to reorder the if statement to make it
* if else based and the less common thing put to the bottom
*
* OTodoAccessVcal fix the date for beaming
*
* Revision 1.6 2003/01/13 15:49:31 eilers
* Fixing crash when businesscard.vcf is missing..
*
* Revision 1.5 2002/12/07 13:26:22 eilers
* Fixing bug in storing anniversary..
*
* Revision 1.4 2002/11/13 14:14:51 eilers
* Added sorted for Contacts..
*
* Revision 1.3 2002/11/11 16:41:09 kergoth
* no default arguments in implementation
*
* Revision 1.2 2002/11/10 15:41:53 eilers
* Bugfixes..
*
* Revision 1.1 2002/11/09 14:34:52 eilers
* Added VCard Backend.
*
*/
#include "ocontactaccessbackend_vcard.h"
#include "../../library/backend/vobject_p.h"
#include "../../library/backend/qfiledirect_p.h"
#include <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;
if ( QFile::exists(m_file) ){
obj = Parse_MIME_FromFileName( QFile::encodeName(m_file).data() );
if ( !obj )
return false;
}else{
qWarning("File \"%s\" not found !", m_file.latin1() );
return false;
}
while ( obj ) {
OContact con = parseVObject( obj );
/*
* if uid is 0 assign a new one
* this at least happens on
* Nokia6210
*/
if ( con.uid() == 0 ){
con.setUid( 1 );
qWarning("assigned new uid %d",con.uid() );
}
m_map.insert( con.uid(), con );
VObject *t = obj;
obj = nextVObjectInList(obj);
cleanVObject( t );
}
return true;
}
bool OContactAccessBackend_VCard::reload()
{
return load();
}
bool OContactAccessBackend_VCard::save()
{
if (!m_dirty )
return true;
QFileDirect file( m_file );
if (!file.open(IO_WriteOnly ) )
return false;
VObject *obj;
obj = newVObject( VCCalProp );
addPropValue( obj, VCVersionProp, "1.0" );
VObject *vo;
for(QMap<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> OContactAccessBackend_VCard::queryByExample ( const OContact&, int, const QDateTime& )
{
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;
qWarning("value %s %d", value.data(), type );
if ( (type & (VOICE|HOME) ) == (VOICE|HOME) && (type & (CELL|HOME) ) != (CELL|HOME) )
c.setHomePhone( value );
if ( ( type & (FAX|HOME) ) == (FAX|HOME) )
c.setHomeFax( value );
if ( ( type & (CELL|HOME) ) == (CELL|HOME) )
c.setHomeMobile( value );
if ( ( type & (VOICE|WORK) ) == (VOICE|WORK) && (type & (CELL|WORK) ) != (CELL|WORK) )
c.setBusinessPhone( value );
if ( ( type & (FAX|WORK) ) == (FAX|WORK) )
c.setBusinessFax( value );
if ( ( type & (CELL|WORK) ) == (CELL|WORK) )
c.setBusinessMobile( value );
if ( ( type & (PAGER|WORK) ) == (PAGER|WORK) )
c.setBusinessPager( value );
}
}
else if ( name == VCEmailAddressProp ) {
QString email = vObjectStringZValue( o );
bool valid = TRUE;
VObjectIterator nit;
initPropIterator( &nit, o );
while( moreIteration( &nit ) ) {
VObject *o = nextVObject( &nit );
QCString name = vObjectTypeInfo( o );
if ( name != VCInternetProp && name != VCHomeProp &&
name != VCWorkProp &&
name != VCPreferredProp )
// ### preffered should map to default email
valid = FALSE;
}
if ( valid ) {
c.insertEmail( email );
}
}
else if ( name == VCURLProp ) {
VObjectIterator nit;
initPropIterator( &nit, o );
while( moreIteration( &nit ) ) {
VObject *o = nextVObject( &nit );
QCString name = vObjectTypeInfo( o );
if ( name == VCHomeProp )
c.setHomeWebpage( value );
else if ( name == VCWorkProp )
c.setBusinessWebpage( value );
}
}
else if ( name == VCOrgProp ) {
VObjectIterator nit;
initPropIterator( &nit, o );
while( moreIteration( &nit ) ) {
VObject *o = nextVObject( &nit );
QCString name = vObjectName( o );
QString value = vObjectStringZValue( o );
if ( name == VCOrgNameProp )
c.setCompany( value );
else if ( name == VCOrgUnitProp )
c.setDepartment( value );
else if ( name == VCOrgUnit2Prop )
c.setOffice( value );
}
}
else if ( name == VCTitleProp ) {
c.setJobTitle( value );
}
else if ( name == "X-Qtopia-Profession" ) {
c.setProfession( value );
}
else if ( name == "X-Qtopia-Manager" ) {
c.setManager( value );
}
else if ( name == "X-Qtopia-Assistant" ) {
c.setAssistant( value );
}
else if ( name == "X-Qtopia-Spouse" ) {
c.setSpouse( value );
}
else if ( name == "X-Qtopia-Gender" ) {
c.setGender( value );
}
else if ( name == "X-Qtopia-Anniversary" ) {
c.setAnniversary( convVCardDateToDate( value ) );
}
else if ( name == "X-Qtopia-Nickname" ) {
c.setNickname( value );
}
else if ( name == "X-Qtopia-Children" ) {
c.setChildren( value );
}
else if ( name == VCBirthDateProp ) {
// Reading Birthdate regarding RFC 2425 (5.8.4)
c.setBirthday( convVCardDateToDate( value ) );
}
#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() ){
qWarning("Exporting birthday as: %s", convDateToVCardDate( c.birthday() ).latin1() );
safeAddPropValue( vcard, VCBirthDateProp, convDateToVCardDate( c.birthday() ) );
}
if ( !c.company().isEmpty() || !c.department().isEmpty() || !c.office().isEmpty() ) {
VObject *org = safeAddProp( vcard, VCOrgProp );
safeAddPropValue( org, VCOrgNameProp, c.company() );
safeAddPropValue( org, VCOrgUnitProp, c.department() );
safeAddPropValue( org, VCOrgUnit2Prop, c.office() );
}
// some values we have to export as custom fields
safeAddPropValue( vcard, "X-Qtopia-Profession", c.profession() );
safeAddPropValue( vcard, "X-Qtopia-Manager", c.manager() );
safeAddPropValue( vcard, "X-Qtopia-Assistant", c.assistant() );
safeAddPropValue( vcard, "X-Qtopia-Spouse", c.spouse() );
safeAddPropValue( vcard, "X-Qtopia-Gender", c.gender() );
if ( c.anniversary().isValid() ){
qWarning("Exporting anniversary as: %s", convDateToVCardDate( c.anniversary() ).latin1() );
safeAddPropValue( vcard, "X-Qtopia-Anniversary", convDateToVCardDate( c.anniversary() ) );
}
safeAddPropValue( vcard, "X-Qtopia-Nickname", c.nickname() );
safeAddPropValue( vcard, "X-Qtopia-Children", c.children() );
return vcard;
}
QString OContactAccessBackend_VCard::convDateToVCardDate( const QDate& d ) const
{
QString str_rfc2425 = QString("%1-%2-%3")
.arg( d.year() )
.arg( d.month(), 2 )
.arg( d.day(), 2 );
// Now replace spaces with "0"...
int pos = 0;
while ( ( pos = str_rfc2425.find (' ') ) > 0 )
str_rfc2425.replace( pos, 1, "0" );
return str_rfc2425;
}
QDate OContactAccessBackend_VCard::convVCardDateToDate( const QString& datestr )
{
int monthPos = datestr.find('-');
int dayPos = datestr.find('-', monthPos+1 );
int sep_ignore = 1;
if ( monthPos == -1 || dayPos == -1 ) {
qDebug("fromString didn't find - in str = %s; mpos = %d ypos = %d", datestr.latin1(), monthPos, dayPos );
// Ok.. No "-" found, therefore we will try to read other format ( YYYYMMDD )
if ( datestr.length() == 8 ){
monthPos = 4;
dayPos = 6;
sep_ignore = 0;
qDebug("Try with follwing positions str = %s; mpos = %d ypos = %d", datestr.latin1(), monthPos, dayPos );
} else {
return QDate();
}
}
int y = datestr.left( monthPos ).toInt();
int m = datestr.mid( monthPos + sep_ignore, dayPos - monthPos - sep_ignore ).toInt();
int d = datestr.mid( dayPos + sep_ignore ).toInt();
qDebug("TimeConversion::fromString ymd = %s => %d %d %d; mpos = %d ypos = %d", datestr.latin1(), y, m, d, monthPos, dayPos);
QDate date ( y,m,d );
return date;
}
VObject* OContactAccessBackend_VCard::safeAddPropValue( VObject *o, const char *prop, const QString &value )
{
VObject *ret = 0;
if ( o && !value.isEmpty() )
ret = addPropValue( o, prop, value.latin1() );
return ret;
}
VObject* OContactAccessBackend_VCard::safeAddProp( VObject *o, const char *prop)
{
VObject *ret = 0;
if ( o )
ret = addProp( o, prop );
return ret;
}
diff --git a/libopie/pim/ocontactaccessbackend_vcard.h b/libopie/pim/ocontactaccessbackend_vcard.h
index 236da00..93e2da3 100644
--- a/libopie/pim/ocontactaccessbackend_vcard.h
+++ b/libopie/pim/ocontactaccessbackend_vcard.h
@@ -1,78 +1,86 @@
/*
* 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.5 2003/03/21 10:33:09 eilers
+ * Merged speed optimized xml backend for contacts to main.
+ * Added QDateTime to querybyexample. For instance, it is now possible to get
+ * all Birthdays/Anniversaries between two dates. This should be used
+ * to show all birthdays in the datebook..
+ * This change is sourcecode backward compatible but you have to upgrade
+ * the binaries for today-addressbook.
+ *
* Revision 1.4 2002/12/07 13:26:22 eilers
* Fixing bug in storing anniversary..
*
* Revision 1.3 2002/11/13 14:14:51 eilers
* Added sorted for Contacts..
*
* Revision 1.2 2002/11/10 15:41:53 eilers
* Bugfixes..
*
* Revision 1.1 2002/11/09 14:34:52 eilers
* Added VCard Backend.
*
*/
#ifndef __OCONTACTACCESSBACKEND_VCARD_H_
#define __OCONTACTACCESSBACKEND_VCARD_H_
#include <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> queryByExample ( const OContact &query, int settings, const QDateTime& d = QDateTime() );
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 );
QString convDateToVCardDate( const QDate& c ) const;
QDate convVCardDateToDate( const QString& datestr );
VObject *safeAddPropValue( VObject *o, const char* prop, const QString& value );
VObject *safeAddProp( VObject* o, const char* prop);
bool m_dirty : 1;
QString m_file;
QMap<int, OContact> m_map;
};
#endif
diff --git a/libopie/pim/ocontactaccessbackend_xml.cpp b/libopie/pim/ocontactaccessbackend_xml.cpp
index 2df6757..4abf4d9 100644
--- a/libopie/pim/ocontactaccessbackend_xml.cpp
+++ b/libopie/pim/ocontactaccessbackend_xml.cpp
@@ -1,739 +1,777 @@
/*
* 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.2 2003/03/21 10:33:09 eilers
+ * Merged speed optimized xml backend for contacts to main.
+ * Added QDateTime to querybyexample. For instance, it is now possible to get
+ * all Birthdays/Anniversaries between two dates. This should be used
+ * to show all birthdays in the datebook..
+ * This change is sourcecode backward compatible but you have to upgrade
+ * the binaries for today-addressbook.
+ *
+ * Revision 1.1.2.2 2003/02/11 12:17:28 eilers
+ * Speed optimization. Removed the sequential search loops.
+ *
+ * Revision 1.1.2.1 2003/02/10 15:31:38 eilers
+ * Writing offsets to debug output..
+ *
* Revision 1.1 2003/02/09 15:05:01 eilers
* Nothing happened.. Just some cleanup before I will start..
*
* Revision 1.12 2003/01/03 16:58:03 eilers
* Reenable debug output
*
* Revision 1.11 2003/01/03 12:31:28 eilers
* Bugfix for calculating data diffs..
*
* Revision 1.10 2003/01/02 14:27:12 eilers
* Improved query by example: Search by date is possible.. First step
* for a today plugin for birthdays..
*
* Revision 1.9 2002/12/08 12:48:57 eilers
* Moved journal-enum from ocontact into i the xml-backend..
*
* Revision 1.8 2002/11/14 17:04:24 eilers
* Sorting will now work if fullname is identical on some entries
*
* Revision 1.7 2002/11/13 15:02:46 eilers
* Small Bug in sorted fixed
*
* Revision 1.6 2002/11/13 14:14:51 eilers
* Added sorted for Contacts..
*
* Revision 1.5 2002/11/01 15:10:42 eilers
* Added regExp-search in database for all fields in a contact.
*
* Revision 1.4 2002/10/16 10:52:40 eilers
* Added some docu to the interface and now using the cache infrastucture by zecke.. :)
*
* Revision 1.3 2002/10/14 16:21:54 eilers
* Some minor interface updates
*
* Revision 1.2 2002/10/07 17:34:24 eilers
* added OBackendFactory for advanced backend access
*
* Revision 1.1 2002/09/27 17:11:44 eilers
* Added API for accessing the Contact-Database ! It is compiling, but
* please do not expect that anything is working !
* I will debug that stuff in the next time ..
* Please read README_COMPILE for compiling !
*
*
*/
#include "ocontactaccessbackend_xml.h"
#include <qasciidict.h>
#include <qdatetime.h>
#include <qfile.h>
#include <qfileinfo.h>
#include <qregexp.h>
#include <qarray.h>
#include <qmap.h>
#include <qdatetime.h>
#include <qpe/global.h>
#include <opie/xmltree.h>
#include "ocontactaccessbackend.h"
#include "ocontactaccess.h"
#include <stdlib.h>
#include <errno.h>
using namespace Opie;
OContactAccessBackend_XML::OContactAccessBackend_XML ( QString appname, QString filename = 0l ):
m_changed( false )
{
+ // Just m_contactlist should call delete if an entry
+ // is removed.
+ m_contactList.setAutoDelete( true );
+ m_uidToContact.setAutoDelete( false );
+
m_appName = appname;
/* Set journalfile name ... */
m_journalName = getenv("HOME");
m_journalName +="/.abjournal" + appname;
/* Expecting to access the default filename if nothing else is set */
if ( filename.isEmpty() ){
m_fileName = Global::applicationFileName( "addressbook","addressbook.xml" );
} else
m_fileName = filename;
/* Load Database now */
load ();
}
bool OContactAccessBackend_XML::save()
{
if ( !m_changed )
return true;
QString strNewFile = m_fileName + ".new";
QFile f( strNewFile );
if ( !f.open( IO_WriteOnly|IO_Raw ) )
return false;
int total_written;
+ int idx_offset = 0;
QString out;
+
+ // Write Header
out = "<?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 ) {
+ QCString cstr = out.utf8();
+ f.writeBlock( cstr.data(), cstr.length() );
+ idx_offset += cstr.length();
+ out = "";
+
+ // Write all contacts
+ QListIterator<OContact> it( m_contactList );
+ for ( ; it.current(); ++it ) {
+ qWarning(" Uid %d at Offset: %x", (*it)->uid(), idx_offset );
out += "<Contact ";
- (*it).save( out );
+ (*it)->save( out );
out += "/>\n";
- QCString cstr = out.utf8();
+ cstr = out.utf8();
total_written = f.writeBlock( cstr.data(), cstr.length() );
+ idx_offset += cstr.length();
if ( total_written != int(cstr.length()) ) {
f.close();
QFile::remove( strNewFile );
return false;
}
out = "";
}
out += " </Contacts>\n</AddressBook>\n";
- QCString cstr = out.utf8();
+ // Write Footer
+ cstr = out.utf8();
total_written = f.writeBlock( cstr.data(), cstr.length() );
if ( total_written != int( cstr.length() ) ) {
f.close();
QFile::remove( strNewFile );
return false;
}
f.close();
// move the file over, I'm just going to use the system call
// because, I don't feel like using QDir.
if ( ::rename( strNewFile.latin1(), m_fileName.latin1() ) < 0 ) {
qWarning( "problem renaming file %s to %s, errno: %d",
strNewFile.latin1(), m_journalName.latin1(), errno );
// remove the tmp file...
QFile::remove( strNewFile );
}
/* The journalfile should be removed now... */
removeJournal();
m_changed = false;
return true;
}
bool OContactAccessBackend_XML::load ()
{
m_contactList.clear();
+ m_uidToContact.clear();
/* Load XML-File and journal if it exists */
if ( !load ( m_fileName, false ) )
return false;
/* The returncode of the journalfile is ignored due to the
* fact that it does not exist when this class is instantiated !
* But there may such a file exist, if the application crashed.
* Therefore we try to load it to get the changes before the #
* crash happened...
*/
load (m_journalName, true);
return true;
}
void OContactAccessBackend_XML::clear ()
{
m_contactList.clear();
+ m_uidToContact.clear();
+
m_changed = false;
-
}
bool OContactAccessBackend_XML::wasChangedExternally()
{
QFileInfo fi( m_fileName );
QDateTime lastmod = fi.lastModified ();
return (lastmod != m_readtime);
}
QArray<int> OContactAccessBackend_XML::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();
+ QListIterator<OContact> it( m_contactList );
+ for( ; it.current(); ++it ){
+ uid_list[counter++] = (*it)->uid();
}
return ( uid_list );
}
OContact OContactAccessBackend_XML::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;
- }
- }
+ OContact* found = m_uidToContact.find( QString().setNum( uid ) );
+
if ( found ){
- foundContact = *it;
+ foundContact = *found;
}
return ( foundContact );
}
-QArray<int> OContactAccessBackend_XML::queryByExample ( const OContact &query, int settings )
+QArray<int> OContactAccessBackend_XML::queryByExample ( const OContact &query, int settings,
+ const QDateTime& d )
{
QArray<int> m_currentQuery( m_contactList.count() );
- QValueListConstIterator<OContact> it;
+ QListIterator<OContact> it( m_contactList );
uint arraycounter = 0;
- for( it = m_contactList.begin(); it != m_contactList.end(); ++it ){
+ for( ; it.current(); ++it ){
/* Search all fields and compare them with query object. Store them into list
* if all fields matches.
*/
QDate* queryDate = 0l;
QDate* checkDate = 0l;
bool allcorrect = true;
for ( int i = 0; i < Qtopia::Groups; i++ ) {
// Birthday and anniversary are special nonstring fields and should
// be handled specially
switch ( i ){
case Qtopia::Birthday:
queryDate = new QDate( query.birthday() );
- checkDate = new QDate( (*it).birthday() );
+ checkDate = new QDate( (*it)->birthday() );
case Qtopia::Anniversary:
if ( queryDate == 0l ){
queryDate = new QDate( query.anniversary() );
- checkDate = new QDate( (*it).anniversary() );
+ checkDate = new QDate( (*it)->anniversary() );
}
if ( queryDate->isValid() ){
if( checkDate->isValid() ){
if ( settings & OContactAccess::DateYear ){
if ( queryDate->year() != checkDate->year() )
allcorrect = false;
}
if ( settings & OContactAccess::DateMonth ){
if ( queryDate->month() != checkDate->month() )
allcorrect = false;
}
if ( settings & OContactAccess::DateDay ){
if ( queryDate->day() != checkDate->day() )
allcorrect = false;
}
if ( settings & OContactAccess::DateDiff ) {
- QDate current = QDate::currentDate();
+ QDate current;
+ // If we get an additional date, we
+ // will take this date instead of
+ // the current one..
+ if ( !d.date().isValid() )
+ current = QDate::currentDate();
+ else
+ current = d.date();
+
// We have to equalize the year, otherwise
// the search will fail..
checkDate->setYMD( current.year(),
checkDate->month(),
checkDate->day() );
if ( *checkDate < current )
checkDate->setYMD( current.year()+1,
checkDate->month(),
checkDate->day() );
+
+ // Check whether the birthday/anniversary date is between
+ // the current/given date and the maximum date
+ // ( maximum time range ) !
qWarning("Checking if %s is between %s and %s ! ",
checkDate->toString().latin1(),
current.toString().latin1(),
queryDate->toString().latin1() );
if ( current.daysTo( *queryDate ) > 0 ){
if ( !( ( *checkDate >= current ) &&
( *checkDate <= *queryDate ) ) ){
allcorrect = false;
qWarning (" Nope!..");
}
}
}
} else{
- // checkDate is invalid. Therfore this entry is always rejected
+ // checkDate is invalid. Therefore this entry is always rejected
allcorrect = false;
}
}
delete queryDate;
queryDate = 0l;
delete checkDate;
checkDate = 0l;
break;
default:
/* Just compare fields which are not empty in the query object */
if ( !query.field(i).isEmpty() ){
switch ( settings & ~( OContactAccess::IgnoreCase
| OContactAccess::DateDiff
| OContactAccess::DateYear
| OContactAccess::DateMonth
| OContactAccess::DateDay
| OContactAccess::MatchOne
) ){
case OContactAccess::RegExp:{
QRegExp expr ( query.field(i),
!(settings & OContactAccess::IgnoreCase),
false );
- if ( expr.find ( (*it).field(i), 0 ) == -1 )
+ 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 )
+ 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() )
+ (*it)->field(i).upper() )
allcorrect = false;
}else{
- if ( query.field(i) != (*it).field(i) )
+ if ( query.field(i) != (*it)->field(i) )
allcorrect = false;
}
}
break;
}
}
}
}
if ( allcorrect ){
- m_currentQuery[arraycounter++] = (*it).uid();
+ m_currentQuery[arraycounter++] = (*it)->uid();
}
}
// Shrink to fit..
m_currentQuery.resize(arraycounter);
return m_currentQuery;
}
QArray<int> OContactAccessBackend_XML::matchRegexp( const QRegExp &r ) const
{
QArray<int> m_currentQuery( m_contactList.count() );
- QValueListConstIterator<OContact> it;
+ QListIterator<OContact> it( m_contactList );
uint arraycounter = 0;
- for( it = m_contactList.begin(); it != m_contactList.end(); ++it ){
- if ( (*it).match( r ) ){
- m_currentQuery[arraycounter++] = (*it).uid();
+ for( ; it.current(); ++it ){
+ if ( (*it)->match( r ) ){
+ m_currentQuery[arraycounter++] = (*it)->uid();
}
}
// Shrink to fit..
m_currentQuery.resize(arraycounter);
return m_currentQuery;
}
const uint OContactAccessBackend_XML::querySettings()
{
return ( OContactAccess::WildCards
| OContactAccess::IgnoreCase
| OContactAccess::RegExp
| OContactAccess::ExactMatch
| OContactAccess::DateDiff
| OContactAccess::DateYear
| OContactAccess::DateMonth
| OContactAccess::DateDay
);
}
bool OContactAccessBackend_XML::hasQuerySettings (uint querySettings) const
{
/* OContactAccess::IgnoreCase, DateDiff, DateYear, DateMonth, DateDay
* may be added with any of the other settings. IgnoreCase should never used alone.
* Wildcards, RegExp, ExactMatch should never used at the same time...
*/
if ( querySettings == OContactAccess::IgnoreCase )
return false;
switch ( querySettings & ~( OContactAccess::IgnoreCase
| OContactAccess::DateDiff
| OContactAccess::DateYear
| OContactAccess::DateMonth
| OContactAccess::DateDay
)
){
case OContactAccess::RegExp:
return ( true );
case OContactAccess::WildCards:
return ( true );
case OContactAccess::ExactMatch:
return ( true );
default:
return ( false );
}
}
// Currently only asc implemented..
QArray<int> OContactAccessBackend_XML::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
// 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).fileAs() + QString::number( (*it).uid() ) );
- nameToUid.insert( (*it).fileAs() + QString::number( (*it).uid() ), (*it).uid() );
+ QListIterator<OContact> it( m_contactList );
+ for( ; it.current(); ++it ){
+ names.append( (*it)->fileAs() + QString::number( (*it)->uid() ) );
+ nameToUid.insert( (*it)->fileAs() + QString::number( (*it)->uid() ), (*it)->uid() );
}
names.sort();
int i = 0;
if ( asc ){
for ( QStringList::Iterator it = names.begin(); it != names.end(); ++it )
m_currentQuery[i++] = nameToUid[ (*it) ];
}else{
for ( QStringList::Iterator it = names.end(); it != names.begin(); --it )
m_currentQuery[i++] = nameToUid[ (*it) ];
}
return m_currentQuery;
}
bool OContactAccessBackend_XML::add ( const OContact &newcontact )
{
//qWarning("odefaultbackend: ACTION::ADD");
updateJournal (newcontact, ACTION_ADD);
addContact_p( newcontact );
m_changed = true;
return true;
}
bool OContactAccessBackend_XML::replace ( const OContact &contact )
{
m_changed = true;
- 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, ACTION_REPLACE);
- m_contactList.remove (it);
- m_contactList.append (contact);
+ OContact* found = m_uidToContact.find ( QString().setNum( contact.uid() ) );
+
+ if ( found ) {
+ OContact* newCont = new OContact( contact );
+
+ updateJournal ( *newCont, ACTION_REPLACE);
+ m_contactList.removeRef ( found );
+ m_contactList.append ( newCont );
+ m_uidToContact.remove( QString().setNum( contact.uid() ) );
+ m_uidToContact.insert( QString().setNum( newCont->uid() ), newCont );
+
+ qWarning("Nur zur Sicherheit: %d == %d ?",contact.uid(), newCont->uid());
+
return true;
} else
return false;
}
bool OContactAccessBackend_XML::remove ( int uid )
{
m_changed = true;
- 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, ACTION_REMOVE);
- m_contactList.remove (it);
+ OContact* found = m_uidToContact.find ( QString().setNum( uid ) );
+
+ if ( found ) {
+ updateJournal ( *found, ACTION_REMOVE);
+ m_contactList.removeRef ( found );
+ m_uidToContact.remove( QString().setNum( uid ) );
+
return true;
} else
return false;
}
bool OContactAccessBackend_XML::reload(){
/* Reload is the same as load in this implementation */
return ( load() );
}
void OContactAccessBackend_XML::addContact_p( const OContact &newcontact )
{
- m_contactList.append (newcontact);
+ OContact* contRef = new OContact( newcontact );
+
+ m_contactList.append ( contRef );
+ m_uidToContact.insert( QString().setNum( newcontact.uid() ), contRef );
}
/* This function loads the xml-database and the journalfile */
bool OContactAccessBackend_XML::load( const QString filename, bool isJournal )
{
/* We use the time of the last read to check if the file was
* changed externally.
*/
if ( !isJournal ){
QFileInfo fi( filename );
m_readtime = fi.lastModified ();
}
const int JOURNALACTION = Qtopia::Notes + 1;
const int JOURNALROW = JOURNALACTION + 1;
bool foundAction = false;
journal_action action = ACTION_ADD;
int journalKey = 0;
QMap<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 = journal_action(it.data().toInt());
foundAction = true;
qWarning ("ODefBack(journal)::ACTION found: %d", action);
break;
case JOURNALROW:
journalKey = it.data().toInt();
break;
default: // no conversion needed add them to the map
contactMap.insert( *find, it.data() );
break;
}
}
/* now generate the Contact contact */
OContact contact( contactMap );
for (customIt = customMap.begin(); customIt != customMap.end(); ++customIt ) {
contact.setCustomField( customIt.key(), customIt.data() );
}
if (foundAction){
foundAction = false;
switch ( action ) {
case ACTION_ADD:
addContact_p (contact);
break;
case ACTION_REMOVE:
if ( !remove (contact.uid()) )
qWarning ("ODefBack(journal)::Unable to remove uid: %d",
contact.uid() );
break;
case ACTION_REPLACE:
if ( !replace ( contact ) )
qWarning ("ODefBack(journal)::Unable to replace uid: %d",
contact.uid() );
break;
default:
qWarning ("Unknown action: ignored !");
break;
}
}else{
/* Add contact to list */
addContact_p (contact);
}
/* Move to next element */
element = element->nextChild();
}
}else {
qWarning("ODefBack::could not load");
}
delete root;
qWarning("returning from loading" );
return true;
}
void OContactAccessBackend_XML::updateJournal( const OContact& cnt,
journal_action action )
{
QFile f( m_journalName );
bool created = !f.exists();
if ( !f.open(IO_WriteOnly|IO_Append) )
return;
QString buf;
QCString str;
// if the file was created, we have to set the Tag "<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 OContactAccessBackend_XML::removeJournal()
{
QFile f ( m_journalName );
if ( f.exists() )
f.remove();
}
diff --git a/libopie/pim/ocontactaccessbackend_xml.h b/libopie/pim/ocontactaccessbackend_xml.h
index 6477705..4d6a7ef 100644
--- a/libopie/pim/ocontactaccessbackend_xml.h
+++ b/libopie/pim/ocontactaccessbackend_xml.h
@@ -1,743 +1,144 @@
/*
* 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.13 2003/03/21 10:33:09 eilers
+ * Merged speed optimized xml backend for contacts to main.
+ * Added QDateTime to querybyexample. For instance, it is now possible to get
+ * all Birthdays/Anniversaries between two dates. This should be used
+ * to show all birthdays in the datebook..
+ * This change is sourcecode backward compatible but you have to upgrade
+ * the binaries for today-addressbook.
+ *
+ * Revision 1.12.2.2 2003/02/11 12:17:28 eilers
+ * Speed optimization. Removed the sequential search loops.
+ *
+ * Revision 1.12.2.1 2003/02/09 15:05:01 eilers
+ * Nothing happened.. Just some cleanup before I will start..
+ *
* Revision 1.12 2003/01/03 16:58:03 eilers
* Reenable debug output
*
* Revision 1.11 2003/01/03 12:31:28 eilers
* Bugfix for calculating data diffs..
*
* Revision 1.10 2003/01/02 14:27:12 eilers
* Improved query by example: Search by date is possible.. First step
* for a today plugin for birthdays..
*
* Revision 1.9 2002/12/08 12:48:57 eilers
* Moved journal-enum from ocontact into i the xml-backend..
*
* Revision 1.8 2002/11/14 17:04:24 eilers
* Sorting will now work if fullname is identical on some entries
*
* Revision 1.7 2002/11/13 15:02:46 eilers
* Small Bug in sorted fixed
*
* Revision 1.6 2002/11/13 14:14:51 eilers
* Added sorted for Contacts..
*
* Revision 1.5 2002/11/01 15:10:42 eilers
* Added regExp-search in database for all fields in a contact.
*
* Revision 1.4 2002/10/16 10:52:40 eilers
* Added some docu to the interface and now using the cache infrastucture by zecke.. :)
*
* Revision 1.3 2002/10/14 16:21:54 eilers
* Some minor interface updates
*
* Revision 1.2 2002/10/07 17:34:24 eilers
* added OBackendFactory for advanced backend access
*
* Revision 1.1 2002/09/27 17:11:44 eilers
* Added API for accessing the Contact-Database ! It is compiling, but
* please do not expect that anything is working !
* I will debug that stuff in the next time ..
* Please read README_COMPILE for compiling !
*
*
*/
#ifndef _OContactAccessBackend_XML_
#define _OContactAccessBackend_XML_
-#include <qasciidict.h>
-#include <qdatetime.h>
-#include <qfile.h>
-#include <qfileinfo.h>
-#include <qregexp.h>
-#include <qarray.h>
-#include <qmap.h>
-#include <qdatetime.h>
-
-#include <qpe/global.h>
-
-#include <opie/xmltree.h>
#include "ocontactaccessbackend.h"
#include "ocontactaccess.h"
-#include <stdlib.h>
-#include <errno.h>
-
-using namespace Opie;
+#include <qlist.h>
+#include <qdict.h>
/* 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 ();
- }
+ OContactAccessBackend_XML ( QString appname, QString filename = 0l );
- 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 save();
- 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 load ();
- }
+ void clear ();
- bool wasChangedExternally()
- {
- QFileInfo fi( m_fileName );
-
- QDateTime lastmod = fi.lastModified ();
-
- return (lastmod != m_readtime);
- }
+ bool wasChangedExternally();
- 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 );
- }
+ QArray<int> allRecords() const;
- 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 );
- }
+ OContact find ( int uid ) const;
- 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.
- */
- QDate* queryDate = 0l;
- QDate* checkDate = 0l;
- bool allcorrect = true;
- for ( int i = 0; i < Qtopia::Groups; i++ ) {
- // Birthday and anniversary are special nonstring fields and should
- // be handled specially
- switch ( i ){
- case Qtopia::Birthday:
- queryDate = new QDate( query.birthday() );
- checkDate = new QDate( (*it).birthday() );
- case Qtopia::Anniversary:
- if ( queryDate == 0l ){
- queryDate = new QDate( query.anniversary() );
- checkDate = new QDate( (*it).anniversary() );
- }
-
- if ( queryDate->isValid() ){
- if( checkDate->isValid() ){
- if ( settings & OContactAccess::DateYear ){
- if ( queryDate->year() != checkDate->year() )
- allcorrect = false;
- }
- if ( settings & OContactAccess::DateMonth ){
- if ( queryDate->month() != checkDate->month() )
- allcorrect = false;
- }
- if ( settings & OContactAccess::DateDay ){
- if ( queryDate->day() != checkDate->day() )
- allcorrect = false;
- }
- if ( settings & OContactAccess::DateDiff ) {
- QDate current = QDate::currentDate();
- // We have to equalize the year, otherwise
- // the search will fail..
- checkDate->setYMD( current.year(),
- checkDate->month(),
- checkDate->day() );
- if ( *checkDate < current )
- checkDate->setYMD( current.year()+1,
- checkDate->month(),
- checkDate->day() );
- qWarning("Checking if %s is between %s and %s ! ",
- checkDate->toString().latin1(),
- current.toString().latin1(),
- queryDate->toString().latin1() );
- if ( current.daysTo( *queryDate ) > 0 ){
- if ( !( ( *checkDate >= current ) &&
- ( *checkDate <= *queryDate ) ) ){
- allcorrect = false;
- qWarning (" Nope!..");
- }
- }
- }
- } else{
- // checkDate is invalid. Therfore this entry is always rejected
- allcorrect = false;
- }
- }
-
- delete queryDate;
- queryDate = 0l;
- delete checkDate;
- checkDate = 0l;
- break;
- default:
- /* Just compare fields which are not empty in the query object */
- if ( !query.field(i).isEmpty() ){
- switch ( settings & ~( OContactAccess::IgnoreCase
- | OContactAccess::DateDiff
- | OContactAccess::DateYear
- | OContactAccess::DateMonth
- | OContactAccess::DateDay
- | OContactAccess::MatchOne
- ) ){
+ QArray<int> queryByExample ( const OContact &query, int settings, const QDateTime& d = QDateTime() );
- 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);
+ QArray<int> matchRegexp( const QRegExp &r ) const;
- 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
- | OContactAccess::DateDiff
- | OContactAccess::DateYear
- | OContactAccess::DateMonth
- | OContactAccess::DateDay
- );
- }
+ const uint querySettings();
- bool hasQuerySettings (uint querySettings) const
- {
- /* OContactAccess::IgnoreCase, DateDiff, DateYear, DateMonth, DateDay
- * may be added with any of the other settings. IgnoreCase should never used alone.
- * Wildcards, RegExp, ExactMatch should never used at the same time...
- */
-
- if ( querySettings == OContactAccess::IgnoreCase )
- return false;
-
- switch ( querySettings & ~( OContactAccess::IgnoreCase
- | OContactAccess::DateDiff
- | OContactAccess::DateYear
- | OContactAccess::DateMonth
- | OContactAccess::DateDay
- )
- ){
- case OContactAccess::RegExp:
- return ( true );
- case OContactAccess::WildCards:
- return ( true );
- case OContactAccess::ExactMatch:
- return ( true );
- default:
- return ( false );
- }
- }
+ bool hasQuerySettings (uint querySettings) const;
// 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
- // 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).fileAs() + QString::number( (*it).uid() ) );
- nameToUid.insert( (*it).fileAs() + QString::number( (*it).uid() ), (*it).uid() );
- }
- names.sort();
-
- int i = 0;
- if ( asc ){
- for ( QStringList::Iterator it = names.begin(); it != names.end(); ++it )
- m_currentQuery[i++] = nameToUid[ (*it) ];
- }else{
- for ( QStringList::Iterator it = names.end(); it != names.begin(); --it )
- m_currentQuery[i++] = nameToUid[ (*it) ];
- }
-
- return m_currentQuery;
-
- }
- bool add ( const OContact &newcontact )
- {
- //qWarning("odefaultbackend: ACTION::ADD");
- updateJournal (newcontact, 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, ACTION_REPLACE);
- m_contactList.remove (it);
- m_contactList.append (contact);
- return true;
- } else
- return false;
- }
+ QArray<int> sorted( bool asc, int , int , int );
+ bool add ( const OContact &newcontact );
- 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, ACTION_REMOVE);
- m_contactList.remove (it);
- return true;
- } else
- return false;
- }
+ bool replace ( const OContact &contact );
- bool reload(){
- /* Reload is the same as load in this implementation */
- return ( load() );
- }
+ bool remove ( int uid );
+ bool reload();
private:
enum journal_action { ACTION_ADD, ACTION_REMOVE, ACTION_REPLACE };
- void addContact_p( const OContact &newcontact ){
- m_contactList.append (newcontact);
- }
+ void addContact_p( const OContact &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;
- journal_action action = 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 = journal_action(it.data().toInt());
- foundAction = true;
- qWarning ("ODefBack(journal)::ACTION found: %d", action);
- break;
- case JOURNALROW:
- journalKey = it.data().toInt();
- break;
- default: // no conversion needed add them to the map
- contactMap.insert( *find, it.data() );
- break;
- }
- }
- /* now generate the Contact contact */
- OContact contact( contactMap );
-
- for (customIt = customMap.begin(); customIt != customMap.end(); ++customIt ) {
- contact.setCustomField( customIt.key(), customIt.data() );
- }
-
- if (foundAction){
- foundAction = false;
- switch ( action ) {
- case ACTION_ADD:
- addContact_p (contact);
- break;
- case ACTION_REMOVE:
- if ( !remove (contact.uid()) )
- qWarning ("ODefBack(journal)::Unable to remove uid: %d",
- contact.uid() );
- break;
- case ACTION_REPLACE:
- if ( !replace ( contact ) )
- qWarning ("ODefBack(journal)::Unable to replace uid: %d",
- contact.uid() );
- break;
- default:
- qWarning ("Unknown action: ignored !");
- break;
- }
- }else{
- /* Add contact to list */
- addContact_p (contact);
- }
-
- /* Move to next element */
- element = element->nextChild();
- }
- }else {
- qWarning("ODefBack::could not load");
- }
- delete root;
- qWarning("returning from loading" );
- return true;
- }
-
+ bool load( const QString filename, bool isJournal );
- void updateJournal( const OContact& cnt,
- journal_action action ) {
- QFile f( m_journalName );
- bool created = !f.exists();
- if ( !f.open(IO_WriteOnly|IO_Append) )
- return;
-
- QString buf;
- QCString str;
-
- // if the file was created, we have to set the Tag "<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();
- }
+ void updateJournal( const OContact& cnt, journal_action action );
+ void removeJournal();
protected:
bool m_changed;
QString m_journalName;
QString m_fileName;
QString m_appName;
- QValueList<OContact> m_contactList;
+ QList<OContact> m_contactList;
QDateTime m_readtime;
+
+ QDict<OContact> m_uidToContact;
};
#endif
diff --git a/libopie/pim/odatebookaccessbackend_xml.cpp b/libopie/pim/odatebookaccessbackend_xml.cpp
index 4a6b7b8..11e19d9 100644
--- a/libopie/pim/odatebookaccessbackend_xml.cpp
+++ b/libopie/pim/odatebookaccessbackend_xml.cpp
@@ -1,587 +1,587 @@
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#include <qasciidict.h>
#include <qfile.h>
#include <qtopia/global.h>
#include <qtopia/stringutil.h>
#include <qtopia/timeconversion.h>
#include "opimnotifymanager.h"
#include "orecur.h"
#include "otimezone.h"
#include "odatebookaccessbackend_xml.h"
namespace {
// FROM TT again
char *strstrlen(const char *haystack, int hLen, const char* needle, int nLen)
{
char needleChar;
char haystackChar;
if (!needle || !haystack || !hLen || !nLen)
return 0;
const char* hsearch = haystack;
if ((needleChar = *needle++) != 0) {
nLen--; //(to make up for needle++)
do {
do {
if ((haystackChar = *hsearch++) == 0)
return (0);
if (hsearch >= haystack + hLen)
return (0);
} while (haystackChar != needleChar);
} while (strncmp(hsearch, needle, QMIN(hLen - (hsearch - haystack), nLen)) != 0);
hsearch--;
}
return ((char *)hsearch);
}
}
namespace {
time_t start, end, created, rp_end;
ORecur* rec;
ORecur* recur() {
if (!rec)
rec = new ORecur;
return rec;
}
int alarmTime;
int snd;
enum Attribute{
FDescription = 0,
FLocation,
FCategories,
FUid,
FType,
FAlarm,
FSound,
FRType,
FRWeekdays,
FRPosition,
FRFreq,
FRHasEndDate,
FREndDate,
FRStart,
FREnd,
FNote,
FCreated,
FTimeZone,
FRecParent,
FRecChildren,
FExceptions
};
inline void save( const OEvent& ev, QString& buf ) {
qWarning("Saving %d %s", ev.uid(), ev.description().latin1() );
buf += " description=\"" + Qtopia::escapeString(ev.description() ) + "\"";
if (!ev.location().isEmpty() )
buf += " location=\"" + Qtopia::escapeString(ev.location() ) + "\"";
buf += " categories=\""+ Qtopia::escapeString( Qtopia::Record::idsToString( ev.categories() ) ) + "\"";
buf += " uid=\"" + QString::number( ev.uid() ) + "\"";
if (ev.isAllDay() )
buf += " type=\"AllDay\"";
if (ev.hasNotifiers() ) {
OPimAlarm alarm = ev.notifiers().alarms()[0]; // take only the first
int minutes = alarm.dateTime().secsTo( ev.startDateTime() ) / 60;
buf += " alarm=\"" + QString::number(minutes) + "\" sound=\"";
if ( alarm.sound() == OPimAlarm::Loud )
buf += "loud";
else
buf += "silent";
buf += "\"";
}
if ( ev.hasRecurrence() ) {
buf += ev.recurrence().toString();
}
/*
* fscking timezones :) well, we'll first convert
* the QDateTime to a QDateTime in UTC time
* and then we'll create a nice time_t
*/
OTimeZone zone( ev.timeZone().isEmpty() ? OTimeZone::current() : ev.timeZone() );
buf += " start=\"" + QString::number( zone.fromUTCDateTime( zone.toDateTime( ev.startDateTime(), OTimeZone::utc() ) ) ) + "\"";
buf += " end=\"" + QString::number( zone.fromUTCDateTime( zone.toDateTime( ev.endDateTime() , OTimeZone::utc() ) ) ) + "\"";
if (!ev.note().isEmpty() ) {
buf += " note=\"" + Qtopia::escapeString( ev.note() ) + "\"";
}
buf += " timezone=\"";
if ( ev.timeZone().isEmpty() )
buf += "None";
else
buf += ev.timeZone();
buf += "\"";
if (ev.parent() != 0 ) {
buf += " recparent=\""+QString::number(ev.parent() )+"\"";
}
if (ev.children().count() != 0 ) {
QArray<int> children = ev.children();
buf += " recchildren=\"";
for ( uint i = 0; i < children.count(); i++ ) {
if ( i != 0 ) buf += " ";
buf += QString::number( children[i] );
}
buf+= "\"";
}
// skip custom writing
}
inline bool forAll( const QMap<int, OEvent>& list, QFile& file ) {
QMap<int, OEvent>::ConstIterator it;
QString buf;
QCString str;
int total_written;
for ( it = list.begin(); it != list.end(); ++it ) {
buf = "<event";
save( it.data(), buf );
buf += " />\n";
str = buf.utf8();
total_written = file.writeBlock(str.data(), str.length() );
if ( total_written != int(str.length() ) )
return false;
}
return true;
}
}
ODateBookAccessBackend_XML::ODateBookAccessBackend_XML( const QString& ,
const QString& fileName )
: ODateBookAccessBackend() {
m_name = fileName.isEmpty() ? Global::applicationFileName( "datebook", "datebook.xml" ) : fileName;
m_changed = false;
}
ODateBookAccessBackend_XML::~ODateBookAccessBackend_XML() {
}
bool ODateBookAccessBackend_XML::load() {
return loadFile();
}
bool ODateBookAccessBackend_XML::reload() {
clear();
return load();
}
bool ODateBookAccessBackend_XML::save() {
if (!m_changed) return true;
int total_written;
QString strFileNew = m_name + ".new";
QFile f( strFileNew );
if (!f.open( IO_WriteOnly | IO_Raw ) ) return false;
QString buf( "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" );
buf += "<!DOCTYPE DATEBOOK><DATEBOOK>\n";
buf += "<events>\n";
QCString str = buf.utf8();
total_written = f.writeBlock( str.data(), str.length() );
if ( total_written != int(str.length() ) ) {
f.close();
QFile::remove( strFileNew );
return false;
}
if (!forAll( m_raw, f ) ) {
f.close();
QFile::remove( strFileNew );
return false;
}
if (!forAll( m_rep, f ) ) {
f.close();
QFile::remove( strFileNew );
return false;
}
buf = "</events>\n</DATEBOOK>\n";
str = buf.utf8();
total_written = f.writeBlock( str.data(), str.length() );
if ( total_written != int(str.length() ) ) {
f.close();
QFile::remove( strFileNew );
return false;
}
f.close();
if ( ::rename( strFileNew, m_name ) < 0 ) {
QFile::remove( strFileNew );
return false;
}
m_changed = false;
return true;
}
QArray<int> ODateBookAccessBackend_XML::allRecords()const {
QArray<int> ints( m_raw.count()+ m_rep.count() );
uint i = 0;
QMap<int, OEvent>::ConstIterator it;
for ( it = m_raw.begin(); it != m_raw.end(); ++it ) {
ints[i] = it.key();
i++;
}
for ( it = m_rep.begin(); it != m_rep.end(); ++it ) {
ints[i] = it.key();
i++;
}
return ints;
}
-QArray<int> ODateBookAccessBackend_XML::queryByExample(const OEvent&, int ) {
+QArray<int> ODateBookAccessBackend_XML::queryByExample(const OEvent&, int, const QDateTime& ) {
return QArray<int>();
}
void ODateBookAccessBackend_XML::clear() {
m_raw.clear();
m_rep.clear();
}
OEvent ODateBookAccessBackend_XML::find( int uid ) const{
if ( m_raw.contains( uid ) )
return m_raw[uid];
else
return m_rep[uid];
}
bool ODateBookAccessBackend_XML::add( const OEvent& ev ) {
m_changed = true;
if (ev.hasRecurrence() )
m_rep.insert( ev.uid(), ev );
else
m_raw.insert( ev.uid(), ev );
return true;
}
bool ODateBookAccessBackend_XML::remove( int uid ) {
m_changed = true;
m_rep.remove( uid );
m_rep.remove( uid );
return true;
}
bool ODateBookAccessBackend_XML::replace( const OEvent& ev ) {
replace( ev.uid() );
return add( ev );
}
QArray<int> ODateBookAccessBackend_XML::rawEvents()const {
return allRecords();
}
QArray<int> ODateBookAccessBackend_XML::rawRepeats()const {
QArray<int> ints( m_rep.count() );
uint i = 0;
QMap<int, OEvent>::ConstIterator it;
for ( it = m_rep.begin(); it != m_rep.end(); ++it ) {
ints[i] = it.key();
i++;
}
return ints;
}
QArray<int> ODateBookAccessBackend_XML::nonRepeats()const {
QArray<int> ints( m_raw.count() );
uint i = 0;
QMap<int, OEvent>::ConstIterator it;
for ( it = m_raw.begin(); it != m_raw.end(); ++it ) {
ints[i] = it.key();
i++;
}
return ints;
}
OEvent::ValueList ODateBookAccessBackend_XML::directNonRepeats() {
OEvent::ValueList list;
QMap<int, OEvent>::ConstIterator it;
for (it = m_raw.begin(); it != m_raw.end(); ++it )
list.append( it.data() );
return list;
}
OEvent::ValueList ODateBookAccessBackend_XML::directRawRepeats() {
OEvent::ValueList list;
QMap<int, OEvent>::ConstIterator it;
for (it = m_rep.begin(); it != m_rep.end(); ++it )
list.append( it.data() );
return list;
}
bool ODateBookAccessBackend_XML::loadFile() {
m_changed = false;
int fd = ::open( QFile::encodeName(m_name).data(), O_RDONLY );
if ( fd < 0 ) return false;
struct stat attribute;
if ( ::fstat(fd, &attribute ) == -1 ) {
::close( fd );
return false;
}
void* map_addr = ::mmap(NULL, attribute.st_size, PROT_READ, MAP_SHARED, fd, 0 );
if ( map_addr == ( (caddr_t)-1) ) {
::close( fd );
return false;
}
::madvise( map_addr, attribute.st_size, MADV_SEQUENTIAL );
::close( fd );
QAsciiDict<int> dict(FExceptions+1);
dict.setAutoDelete( true );
dict.insert( "description", new int(FDescription) );
dict.insert( "location", new int(FLocation) );
dict.insert( "categories", new int(FCategories) );
dict.insert( "uid", new int(FUid) );
dict.insert( "type", new int(FType) );
dict.insert( "alarm", new int(FAlarm) );
dict.insert( "sound", new int(FSound) );
dict.insert( "rtype", new int(FRType) );
dict.insert( "rweekdays", new int(FRWeekdays) );
dict.insert( "rposition", new int(FRPosition) );
dict.insert( "rfreq", new int(FRFreq) );
dict.insert( "rhasenddate", new int(FRHasEndDate) );
dict.insert( "enddt", new int(FREndDate) );
dict.insert( "start", new int(FRStart) );
dict.insert( "end", new int(FREnd) );
dict.insert( "note", new int(FNote) );
dict.insert( "created", new int(FCreated) );
dict.insert( "recparent", new int(FRecParent) );
dict.insert( "recchildren", new int(FRecChildren) );
dict.insert( "exceptions", new int(FExceptions) );
dict.insert( "timezone", new int(FTimeZone) );
char* dt = (char*)map_addr;
int len = attribute.st_size;
int i = 0;
char* point;
const char* collectionString = "<event ";
int strLen = ::strlen(collectionString);
int *find;
while ( ( point = ::strstrlen( dt+i, len -i, collectionString, strLen ) ) != 0 ) {
i = point -dt;
i+= strLen;
alarmTime = -1;
snd = 0; // silent
OEvent ev;
rec = 0;
while ( TRUE ) {
while ( i < len && (dt[i] == ' ' || dt[i] == '\n' || dt[i] == '\r') )
++i;
if ( i >= len-2 || (dt[i] == '/' && dt[i+1] == '>') )
break;
// we have another attribute, read it.
int j = i;
while ( j < len && dt[j] != '=' )
++j;
QCString attr( dt+i, j-i+1);
i = ++j; // skip =
// find the start of quotes
while ( i < len && dt[i] != '"' )
++i;
j = ++i;
bool haveUtf = FALSE;
bool haveEnt = FALSE;
while ( j < len && dt[j] != '"' ) {
if ( ((unsigned char)dt[j]) > 0x7f )
haveUtf = TRUE;
if ( dt[j] == '&' )
haveEnt = TRUE;
++j;
}
if ( i == j ) {
// empty value
i = j + 1;
continue;
}
QCString value( dt+i, j-i+1 );
i = j + 1;
QString str = (haveUtf ? QString::fromUtf8( value )
: QString::fromLatin1( value ) );
if ( haveEnt )
str = Qtopia::plainString( str );
/*
* add key + value
*/
find = dict[attr.data()];
if (!find)
ev.setCustomField( attr, value );
else {
setField( ev, *find, value );
}
}
/* time to finalize */
finalizeRecord( ev );
delete rec;
}
::munmap(map_addr, attribute.st_size );
m_changed = false; // changed during add
return true;
}
void ODateBookAccessBackend_XML::finalizeRecord( OEvent& ev ) {
/* AllDay is alway in UTC */
if ( ev.isAllDay() ) {
OTimeZone utc = OTimeZone::utc();
ev.setStartDateTime( utc.fromUTCDateTime( start ) );
ev.setEndDateTime ( utc.fromUTCDateTime( end ) );
ev.setTimeZone( "UTC"); // make sure it is really utc
}else {
/* to current date time */
qWarning(" Start is %d", start );
OTimeZone zone( ev.timeZone().isEmpty() ? OTimeZone::current() : ev.timeZone() );
QDateTime date = zone.toDateTime( start );
qWarning(" Start is %s", date.toString().latin1() );
ev.setStartDateTime( zone.toDateTime( date, OTimeZone::current() ) );
date = zone.toDateTime( end );
ev.setEndDateTime ( zone.toDateTime( date, OTimeZone::current() ) );
}
if ( rec && rec->doesRecur() ) {
OTimeZone utc = OTimeZone::utc();
ORecur recu( *rec ); // call copy c'tor;
recu.setEndDate ( utc.fromUTCDateTime( rp_end ).date() );
recu.setCreatedDateTime( utc.fromUTCDateTime( created ) );
recu.setStart( ev.startDateTime().date() );
ev.setRecurrence( recu );
}
if (alarmTime != -1 ) {
QDateTime dt = ev.startDateTime().addSecs( -1*alarmTime*60 );
OPimAlarm al( snd , dt );
ev.notifiers().add( al );
}
if ( m_raw.contains( ev.uid() ) || m_rep.contains( ev.uid() ) ) {
qWarning("already contains assign uid");
ev.setUid( 1 );
}
qWarning("addind %d %s", ev.uid(), ev.description().latin1() );
if ( ev.hasRecurrence() )
m_rep.insert( ev.uid(), ev );
else
m_raw.insert( ev.uid(), ev );
}
void ODateBookAccessBackend_XML::setField( OEvent& e, int id, const QString& value) {
// qWarning(" setting %s", value.latin1() );
switch( id ) {
case FDescription:
e.setDescription( value );
break;
case FLocation:
e.setLocation( value );
break;
case FCategories:
e.setCategories( e.idsFromString( value ) );
break;
case FUid:
e.setUid( value.toInt() );
break;
case FType:
if ( value == "AllDay" ) {
e.setAllDay( true );
e.setTimeZone( "UTC" );
}
break;
case FAlarm:
alarmTime = value.toInt();
break;
case FSound:
snd = value == "loud" ? OPimAlarm::Loud : OPimAlarm::Silent;
break;
// recurrence stuff
case FRType:
if ( value == "Daily" )
recur()->setType( ORecur::Daily );
else if ( value == "Weekly" )
recur()->setType( ORecur::Weekly);
else if ( value == "MonthlyDay" )
recur()->setType( ORecur::MonthlyDay );
else if ( value == "MonthlyDate" )
recur()->setType( ORecur::MonthlyDate );
else if ( value == "Yearly" )
recur()->setType( ORecur::Yearly );
else
recur()->setType( ORecur::NoRepeat );
break;
case FRWeekdays:
recur()->setDays( value.toInt() );
break;
case FRPosition:
recur()->setPosition( value.toInt() );
break;
case FRFreq:
recur()->setFrequency( value.toInt() );
break;
case FRHasEndDate:
recur()->setHasEndDate( value.toInt() );
break;
case FREndDate: {
rp_end = (time_t) value.toLong();
break;
}
case FRStart: {
start = (time_t) value.toLong();
break;
}
case FREnd: {
end = ( (time_t) value.toLong() );
break;
}
case FNote:
e.setNote( value );
break;
case FCreated:
created = value.toInt();
break;
case FRecParent:
e.setParent( value.toInt() );
break;
case FRecChildren:{
QStringList list = QStringList::split(' ', value );
for ( QStringList::Iterator it = list.begin(); it != list.end(); ++it ) {
e.addChild( (*it).toInt() );
}
}
break;
case FExceptions:{
QStringList list = QStringList::split(' ', value );
for (QStringList::Iterator it = list.begin(); it != list.end(); ++it ) {
QDate date( (*it).left(4).toInt(), (*it).mid(4, 2).toInt(), (*it).right(2).toInt() );
qWarning("adding exception %s", date.toString().latin1() );
recur()->exceptions().append( date );
}
}
break;
case FTimeZone:
if ( value != "None" )
e.setTimeZone( value );
break;
default:
break;
}
}
diff --git a/libopie/pim/odatebookaccessbackend_xml.h b/libopie/pim/odatebookaccessbackend_xml.h
index 40f69d8..563c31f 100644
--- a/libopie/pim/odatebookaccessbackend_xml.h
+++ b/libopie/pim/odatebookaccessbackend_xml.h
@@ -1,48 +1,48 @@
#ifndef OPIE_DATE_BOOK_ACCESS_BACKEND_XML__H
#define OPIE_DATE_BOOK_ACCESS_BACKEND_XML__H
#include <qmap.h>
#include "odatebookaccessbackend.h"
class ODateBookAccessBackend_XML : public ODateBookAccessBackend {
public:
ODateBookAccessBackend_XML( const QString& appName,
const QString& fileName = QString::null);
~ODateBookAccessBackend_XML();
bool load();
bool reload();
bool save();
QArray<int> allRecords()const;
- QArray<int> queryByExample( const OEvent&, int );
+ QArray<int> queryByExample( const OEvent&, int, const QDateTime& d = QDateTime() );
OEvent find( int uid )const;
void clear();
bool add( const OEvent& ev );
bool remove( int uid );
bool replace( const OEvent& ev );
QArray<UID> rawEvents()const;
QArray<UID> rawRepeats()const;
QArray<UID> nonRepeats()const;
OEvent::ValueList directNonRepeats();
OEvent::ValueList directRawRepeats();
private:
bool m_changed :1 ;
bool loadFile();
inline void finalizeRecord( OEvent& ev );
inline void setField( OEvent&, int field, const QString& val );
QString m_name;
QMap<int, OEvent> m_raw;
QMap<int, OEvent> m_rep;
struct Data;
Data* data;
class Private;
Private *d;
};
#endif
diff --git a/libopie/pim/opimaccessbackend.h b/libopie/pim/opimaccessbackend.h
index e268f4f..01a0c86 100644
--- a/libopie/pim/opimaccessbackend.h
+++ b/libopie/pim/opimaccessbackend.h
@@ -1,153 +1,153 @@
#ifndef OPIE_PIM_ACCESS_BACKEND
#define OPIE_PIM_ACCESS_BACKEND
#include <qarray.h>
#include <opie/otemplatebase.h>
#include <opie/opimrecord.h>
/**
* OPimAccessBackend is the base class
* for all private backends
* it operates on OPimRecord as the base class
* and it's responsible for fast manipulating
* the resource the implementation takes care
* of
*/
template <class T = OPimRecord>
class OPimAccessBackend {
public:
typedef OTemplateBase<T> Frontend;
/** The access hint from the frontend */
OPimAccessBackend(int access = 0);
virtual ~OPimAccessBackend();
/**
* load the resource
*/
virtual bool load() = 0;
/**
* reload the resource
*/
virtual bool reload() = 0;
/**
* save the resource and
* all it's changes
*/
virtual bool save() = 0;
/**
* return an array of
* all available uids
*/
virtual QArray<int> allRecords()const = 0;
/**
- * queryByExample for T with the SortOrder
- * sort
+ * queryByExample for T with the given Settings
+ *
*/
- virtual QArray<int> queryByExample( const T& t, int sort ) = 0;
+ virtual QArray<int> queryByExample( const T& t, int settings, const QDateTime& d = QDateTime() ) = 0;
/**
* find the OPimRecord with uid @param uid
* returns T and T.isEmpty() if nothing was found
*/
virtual T find(int uid )const = 0;
virtual T find(int uid, const QArray<int>& items,
uint current, typename Frontend::CacheDirection )const ;
/**
* clear the back end
*/
virtual void clear() = 0;
/**
* add T
*/
virtual bool add( const T& t ) = 0;
/**
* remove
*/
virtual bool remove( int uid ) = 0;
/**
* replace a record with T.uid()
*/
virtual bool replace( const T& t ) = 0;
/*
* setTheFrontEnd!!!
*/
void setFrontend( Frontend* front );
/**
* set the read ahead count
*/
void setReadAhead( uint count );
protected:
int access()const;
void cache( const T& t )const;
/**
* use a prime number here!
*/
void setSaneCacheSize( int );
uint readAhead()const;
private:
class Private;
Private* d;
Frontend* m_front;
uint m_read;
int m_acc;
};
template <class T>
OPimAccessBackend<T>::OPimAccessBackend(int acc)
: m_acc( acc )
{
m_front = 0l;
}
template <class T>
OPimAccessBackend<T>::~OPimAccessBackend() {
}
template <class T>
void OPimAccessBackend<T>::setFrontend( Frontend* fr ) {
m_front = fr;
}
template <class T>
void OPimAccessBackend<T>::cache( const T& t )const {
if (m_front )
m_front->cache( t );
}
template <class T>
void OPimAccessBackend<T>::setSaneCacheSize( int size) {
if (m_front )
m_front->setSaneCacheSize( size );
}
template <class T>
T OPimAccessBackend<T>::find( int uid, const QArray<int>&,
uint, typename Frontend::CacheDirection )const {
return find( uid );
}
template <class T>
void OPimAccessBackend<T>::setReadAhead( uint count ) {
m_read = count;
}
template <class T>
uint OPimAccessBackend<T>::readAhead()const {
return m_read;
}
template <class T>
int OPimAccessBackend<T>::access()const {
return m_acc;
}
#endif
diff --git a/libopie/pim/opimaccesstemplate.h b/libopie/pim/opimaccesstemplate.h
index 259e2c1..6a3a0db 100644
--- a/libopie/pim/opimaccesstemplate.h
+++ b/libopie/pim/opimaccesstemplate.h
@@ -1,286 +1,286 @@
#ifndef OPIE_PIM_ACCESS_TEMPLATE_H
#define OPIE_PIM_ACCESS_TEMPLATE_H
#include <qarray.h>
#include <opie/opimrecord.h>
#include <opie/opimaccessbackend.h>
#include <opie/orecordlist.h>
#include "opimcache.h"
#include "otemplatebase.h"
/**
* Thats the frontend to our OPIE PIM
* Library. Either you want to use it's
* interface or you want to implement
* your own Access lib
* Just create a OPimRecord and inherit from
* the plugins
*/
template <class T = OPimRecord >
class OPimAccessTemplate : public OTemplateBase<T> {
public:
enum Access {
Random = 0,
SortedAccess
};
typedef ORecordList<T> List;
typedef OPimAccessBackend<T> BackEnd;
typedef OPimCache<T> Cache;
/**
* c'tor BackEnd
* enum Access a small hint on how to handle the backend
*/
OPimAccessTemplate( BackEnd* end);
virtual ~OPimAccessTemplate();
/**
* load from the backend
*/
bool load();
/** Reload database.
* You should execute this function if the external database
* was changed.
* This function will load the external database and afterwards
* rejoin the local changes. Therefore the local database will be set consistent.
*/
virtual bool reload();
/** Save contacts database.
* Save is more a "commit". After calling this function, all changes are public available.
* @return true if successful
*/
bool save();
/**
* if the resource was changed externally
* You should use the signal handling instead of polling possible changes !
* zecke: Do you implement a signal for otodoaccess ?
*/
bool wasChangedExternally()const;
/**
* return a List of records
* you can iterate over them
*/
virtual List allRecords()const;
/**
* queryByExample.
* @see otodoaccess, ocontactaccess
*/
- virtual List queryByExample( const T& t, int querySettings );
+ virtual List queryByExample( const T& t, int querySettings, const QDateTime& d = QDateTime() );
/**
* find the OPimRecord uid
*/
virtual T find( int uid )const;
/**
* read ahead cache find method ;)
*/
virtual T find( int uid, const QArray<int>&,
uint current, typename OTemplateBase<T>::CacheDirection dir = OTemplateBase<T>::Forward )const;
/* invalidate cache here */
/**
* clears the backend and invalidates the backend
*/
void clear() ;
/**
* add T to the backend
* @param t The item to add.
* @return <i>true</i> if added successfully.
*/
virtual bool add( const T& t ) ;
bool add( const OPimRecord& );
/* only the uid matters */
/**
* remove T from the backend
* @param t The item to remove
* @return <i>true</i> if successful.
*/
virtual bool remove( const T& t );
/**
* remove the OPimRecord with uid
* @param uid The ID of the item to remove
* @return <i>true</i> if successful.
*/
bool remove( int uid );
bool remove( const OPimRecord& );
/**
* replace T from backend
* @param t The item to replace
* @return <i>true</i> if successful.
*/
virtual bool replace( const T& t) ;
void setReadAhead( uint count );
/**
* @internal
*/
void cache( const T& )const;
void setSaneCacheSize( int );
QArray<int> records()const;
protected:
/**
* invalidate the cache
*/
void invalidateCache();
void setBackEnd( BackEnd* end );
/**
* returns the backend
*/
BackEnd* backEnd();
BackEnd* m_backEnd;
Cache m_cache;
};
template <class T>
OPimAccessTemplate<T>::OPimAccessTemplate( BackEnd* end )
: OTemplateBase<T>(), m_backEnd( end )
{
if (end )
end->setFrontend( this );
}
template <class T>
OPimAccessTemplate<T>::~OPimAccessTemplate() {
qWarning("~OPimAccessTemplate<T>");
delete m_backEnd;
}
template <class T>
bool OPimAccessTemplate<T>::load() {
invalidateCache();
return m_backEnd->load();
}
template <class T>
bool OPimAccessTemplate<T>::reload() {
invalidateCache(); // zecke: I think this should be added (se)
return m_backEnd->reload();
}
template <class T>
bool OPimAccessTemplate<T>::save() {
return m_backEnd->save();
}
template <class T>
typename OPimAccessTemplate<T>::List OPimAccessTemplate<T>::allRecords()const {
QArray<int> ints = m_backEnd->allRecords();
List lis(ints, this );
return lis;
}
template <class T>
QArray<int> OPimAccessTemplate<T>::records()const {
return m_backEnd->allRecords();
}
template <class T>
typename OPimAccessTemplate<T>::List
-OPimAccessTemplate<T>::queryByExample( const T& t, int sortOrder ) {
- QArray<int> ints = m_backEnd->queryByExample( t, sortOrder );
+OPimAccessTemplate<T>::queryByExample( const T& t, int settings, const QDateTime& d ) {
+ QArray<int> ints = m_backEnd->queryByExample( t, settings, d );
List lis(ints, this );
return lis;
}
template <class T>
T OPimAccessTemplate<T>::find( int uid ) const{
T t = m_backEnd->find( uid );
cache( t );
return t;
}
template <class T>
T OPimAccessTemplate<T>::find( int uid, const QArray<int>& ar,
uint current, typename OTemplateBase<T>::CacheDirection dir )const {
/*
* better do T.isEmpty()
* after a find this way we would
* avoid two finds in QCache...
*/
// qWarning("find it now %d", uid );
if (m_cache.contains( uid ) ) {
return m_cache.find( uid );
}
T t = m_backEnd->find( uid, ar, current, dir );
cache( t );
return t;
}
template <class T>
void OPimAccessTemplate<T>::clear() {
invalidateCache();
m_backEnd->clear();
}
template <class T>
bool OPimAccessTemplate<T>::add( const T& t ) {
cache( t );
return m_backEnd->add( t );
}
template <class T>
bool OPimAccessTemplate<T>::add( const OPimRecord& rec) {
/* same type */
if ( rec.rtti() == T::rtti() ) {
const T &t = static_cast<const T&>(rec);
return add(t);
}
return false;
}
template <class T>
bool OPimAccessTemplate<T>::remove( const T& t ) {
return remove( t.uid() );
}
template <class T>
bool OPimAccessTemplate<T>::remove( int uid ) {
m_cache.remove( uid );
return m_backEnd->remove( uid );
}
template <class T>
bool OPimAccessTemplate<T>::remove( const OPimRecord& rec) {
return remove( rec.uid() );
}
template <class T>
bool OPimAccessTemplate<T>::replace( const T& t ) {
m_cache.replace( t );
return m_backEnd->replace( t );
}
template <class T>
void OPimAccessTemplate<T>::invalidateCache() {
m_cache.invalidate();
}
template <class T>
typename OPimAccessTemplate<T>::BackEnd* OPimAccessTemplate<T>::backEnd() {
return m_backEnd;
}
template <class T>
bool OPimAccessTemplate<T>::wasChangedExternally()const {
return false;
}
template <class T>
void OPimAccessTemplate<T>::setBackEnd( BackEnd* end ) {
m_backEnd = end;
if (m_backEnd )
m_backEnd->setFrontend( this );
}
template <class T>
void OPimAccessTemplate<T>::cache( const T& t ) const{
/* hacky we need to work around the const*/
((OPimAccessTemplate<T>*)this)->m_cache.add( t );
}
template <class T>
void OPimAccessTemplate<T>::setSaneCacheSize( int size ) {
m_cache.setSize( size );
}
template <class T>
void OPimAccessTemplate<T>::setReadAhead( uint count ) {
m_backEnd->setReadAhead( count );
}
#endif
diff --git a/libopie/pim/otodoaccesssql.cpp b/libopie/pim/otodoaccesssql.cpp
index 761d7d8..ec9c14c 100644
--- a/libopie/pim/otodoaccesssql.cpp
+++ b/libopie/pim/otodoaccesssql.cpp
@@ -1,540 +1,540 @@
#include <qdatetime.h>
#include <qpe/global.h>
#include <opie/osqldriver.h>
#include <opie/osqlresult.h>
#include <opie/osqlmanager.h>
#include <opie/osqlquery.h>
#include "otodoaccesssql.h"
/*
* first some query
* CREATE query
* LOAD query
* INSERT
* REMOVE
* CLEAR
*/
namespace {
/**
* CreateQuery for the Todolist Table
*/
class CreateQuery : public OSQLQuery {
public:
CreateQuery();
~CreateQuery();
QString query()const;
};
/**
* LoadQuery
* this one queries for all uids
*/
class LoadQuery : public OSQLQuery {
public:
LoadQuery();
~LoadQuery();
QString query()const;
};
/**
* inserts/adds a OTodo to the table
*/
class InsertQuery : public OSQLQuery {
public:
InsertQuery(const OTodo& );
~InsertQuery();
QString query()const;
private:
OTodo m_todo;
};
/**
* removes one from the table
*/
class RemoveQuery : public OSQLQuery {
public:
RemoveQuery(int uid );
~RemoveQuery();
QString query()const;
private:
int m_uid;
};
/**
* Clears (delete) a Table
*/
class ClearQuery : public OSQLQuery {
public:
ClearQuery();
~ClearQuery();
QString query()const;
};
/**
* a find query
*/
class FindQuery : public OSQLQuery {
public:
FindQuery(int uid);
FindQuery(const QArray<int>& );
~FindQuery();
QString query()const;
private:
QString single()const;
QString multi()const;
QArray<int> m_uids;
int m_uid;
};
/**
* overdue query
*/
class OverDueQuery : public OSQLQuery {
public:
OverDueQuery();
~OverDueQuery();
QString query()const;
};
class EffQuery : public OSQLQuery {
public:
EffQuery( const QDate&, const QDate&, bool inc );
~EffQuery();
QString query()const;
private:
QString with()const;
QString out()const;
QDate m_start;
QDate m_end;
bool m_inc :1;
};
CreateQuery::CreateQuery() : OSQLQuery() {}
CreateQuery::~CreateQuery() {}
QString CreateQuery::query()const {
QString qu;
qu += "create table todolist( uid, categories, completed, progress, ";
qu += "summary, DueDate, priority, description )";
return qu;
}
LoadQuery::LoadQuery() : OSQLQuery() {}
LoadQuery::~LoadQuery() {}
QString LoadQuery::query()const {
QString qu;
qu += "select distinct uid from todolist";
return qu;
}
InsertQuery::InsertQuery( const OTodo& todo )
: OSQLQuery(), m_todo( todo ) {
}
InsertQuery::~InsertQuery() {
}
/*
* converts from a OTodo to a query
* we leave out X-Ref + Alarms
*/
QString InsertQuery::query()const{
int year, month, day;
year = month = day = 0;
if (m_todo.hasDueDate() ) {
QDate date = m_todo.dueDate();
year = date.year();
month = date.month();
day = date.day();
}
QString qu;
qu = "insert into todolist VALUES(" + QString::number( m_todo.uid() ) + ",'" + m_todo.idsToString( m_todo.categories() ) + "',";
qu += QString::number( m_todo.isCompleted() ) + "," + QString::number( m_todo.progress() ) + ",";
qu += "'"+m_todo.summary()+"','"+QString::number(year)+"-"+QString::number(month)+"-"+QString::number(day)+"',";
qu += QString::number(m_todo.priority() ) +",'" + m_todo.description() + "')";
qWarning("add %s", qu.latin1() );
return qu;
}
RemoveQuery::RemoveQuery(int uid )
: OSQLQuery(), m_uid( uid ) {}
RemoveQuery::~RemoveQuery() {}
QString RemoveQuery::query()const {
QString qu = "DELETE from todolist where uid = " + QString::number(m_uid);
return qu;
}
ClearQuery::ClearQuery()
: OSQLQuery() {}
ClearQuery::~ClearQuery() {}
QString ClearQuery::query()const {
QString qu = "drop table todolist";
return qu;
}
FindQuery::FindQuery(int uid)
: OSQLQuery(), m_uid(uid ) {
}
FindQuery::FindQuery(const QArray<int>& ints)
: OSQLQuery(), m_uids(ints){
}
FindQuery::~FindQuery() {
}
QString FindQuery::query()const{
if (m_uids.count() == 0 )
return single();
else
return multi();
}
QString FindQuery::single()const{
QString qu = "select uid, categories, completed, progress, summary, ";
qu += "DueDate, priority, description from todolist where uid = " + QString::number(m_uid);
return qu;
}
QString FindQuery::multi()const {
QString qu = "select uid, categories, completed, progress, summary, ";
qu += "DueDate, priority, description from todolist where ";
for (uint i = 0; i < m_uids.count(); i++ ) {
qu += " UID = " + QString::number( m_uids[i] ) + " OR";
}
qu.remove( qu.length()-2, 2 );
return qu;
}
OverDueQuery::OverDueQuery(): OSQLQuery() {}
OverDueQuery::~OverDueQuery() {}
QString OverDueQuery::query()const {
QDate date = QDate::currentDate();
QString str;
str = QString("select uid from todolist where DueDate ='%1-%2-%3'").arg(date.year() ).arg(date.month() ).arg(date.day() );
return str;
}
EffQuery::EffQuery( const QDate& start, const QDate& end, bool inc )
: OSQLQuery(), m_start( start ), m_end( end ),m_inc(inc) {}
EffQuery::~EffQuery() {}
QString EffQuery::query()const {
return m_inc ? with() : out();
}
QString EffQuery::with()const {
QString str;
str = QString("select uid from todolist where ( DueDate >= '%1-%2-%3' AND DueDate <= '%4-%5-%6' ) OR DueDate = '0-0-0' ")
.arg( m_start.year() ).arg( m_start.month() ).arg( m_start.day() )
.arg( m_end .year() ).arg( m_end .month() ).arg( m_end .day() );
return str;
}
QString EffQuery::out()const {
QString str;
str = QString("select uid from todolist where DueDate >= '%1-%2-%3' AND DueDate <= '%4-%5-%6'")
.arg(m_start.year() ).arg(m_start.month() ).arg( m_start.day() )
.arg(m_end. year() ).arg(m_end. month() ).arg(m_end.day() );
return str;
}
};
OTodoAccessBackendSQL::OTodoAccessBackendSQL( const QString& file )
: OTodoAccessBackend(), m_dict(15), m_dirty(true)
{
QString fi = file;
if ( fi.isEmpty() )
fi = Global::applicationFileName( "todolist", "todolist.db" );
OSQLManager man;
m_driver = man.standard();
m_driver->setUrl(fi);
fillDict();
}
OTodoAccessBackendSQL::~OTodoAccessBackendSQL(){
}
bool OTodoAccessBackendSQL::load(){
if (!m_driver->open() )
return false;
CreateQuery creat;
OSQLResult res = m_driver->query(&creat );
m_dirty = true;
return true;
}
bool OTodoAccessBackendSQL::reload(){
return load();
}
bool OTodoAccessBackendSQL::save(){
return m_driver->close();
}
QArray<int> OTodoAccessBackendSQL::allRecords()const {
if (m_dirty )
update();
return m_uids;
}
-QArray<int> OTodoAccessBackendSQL::queryByExample( const OTodo& , int ){
+QArray<int> OTodoAccessBackendSQL::queryByExample( const OTodo& , int, const QDateTime& ){
QArray<int> ints(0);
return ints;
}
OTodo OTodoAccessBackendSQL::find(int uid ) const{
FindQuery query( uid );
return todo( m_driver->query(&query) );
}
OTodo OTodoAccessBackendSQL::find( int uid, const QArray<int>& ints,
uint cur, Frontend::CacheDirection dir ) const{
int CACHE = readAhead();
qWarning("searching for %d", uid );
QArray<int> search( CACHE );
uint size =0;
OTodo to;
// we try to cache CACHE items
switch( dir ) {
/* forward */
case 0:
for (uint i = cur; i < ints.count() && size < CACHE; i++ ) {
qWarning("size %d %d", size, ints[i] );
search[size] = ints[i];
size++;
}
break;
/* reverse */
case 1:
for (uint i = cur; i != 0 && size < CACHE; i-- ) {
search[size] = ints[i];
size++;
}
break;
}
search.resize( size );
FindQuery query( search );
OSQLResult res = m_driver->query( &query );
if ( res.state() != OSQLResult::Success )
return to;
return todo( res );
}
void OTodoAccessBackendSQL::clear() {
ClearQuery cle;
OSQLResult res = m_driver->query( &cle );
CreateQuery qu;
res = m_driver->query(&qu);
}
bool OTodoAccessBackendSQL::add( const OTodo& t) {
InsertQuery ins( t );
OSQLResult res = m_driver->query( &ins );
if ( res.state() == OSQLResult::Failure )
return false;
int c = m_uids.count();
m_uids.resize( c+1 );
m_uids[c] = t.uid();
return true;
}
bool OTodoAccessBackendSQL::remove( int uid ) {
RemoveQuery rem( uid );
OSQLResult res = m_driver->query(&rem );
if ( res.state() == OSQLResult::Failure )
return false;
m_dirty = true;
return true;
}
/*
* FIXME better set query
* but we need the cache for that
* now we remove
*/
bool OTodoAccessBackendSQL::replace( const OTodo& t) {
remove( t.uid() );
bool b= add(t);
m_dirty = false; // we changed some stuff but the UID stayed the same
return b;
}
QArray<int> OTodoAccessBackendSQL::overDue() {
OverDueQuery qu;
return uids( m_driver->query(&qu ) );
}
QArray<int> OTodoAccessBackendSQL::effectiveToDos( const QDate& s,
const QDate& t,
bool u) {
EffQuery ef(s, t, u );
return uids (m_driver->query(&ef) );
}
/*
*
*/
QArray<int> OTodoAccessBackendSQL::sorted( bool asc, int sortOrder,
int sortFilter, int cat ) {
qWarning("sorted %d, %d", asc, sortOrder );
QString query;
query = "select uid from todolist WHERE ";
/*
* Sort Filter stuff
* not that straight forward
*
*/
/* Category */
if ( sortFilter & 1 ) {
QString str;
if (cat != 0 ) str = QString::number( cat );
query += " categories like '%" +str+"%' AND";
}
/* Show only overdue */
if ( sortFilter & 2 ) {
QDate date = QDate::currentDate();
QString due;
QString base;
base = QString("DueDate <= '%1-%2-%3' AND completed = 0").arg( date.year() ).arg( date.month() ).arg( date.day() );
query += " " + base + " AND";
}
/* not show completed */
if ( sortFilter & 4 ) {
query += " completed = 0 AND";
}else{
query += " ( completed = 1 OR completed = 0) AND";
}
/* srtip the end */
query = query.remove( query.length()-3, 3 );
/*
* sort order stuff
* quite straight forward
*/
query += "ORDER BY ";
switch( sortOrder ) {
/* completed */
case 0:
query += "completed";
break;
case 1:
query += "priority";
break;
case 2:
query += "summary";
break;
case 3:
query += "DueDate";
break;
}
if ( !asc ) {
qWarning("not ascending!");
query += " DESC";
}
qWarning( query );
OSQLRawQuery raw(query );
return uids( m_driver->query(&raw) );
}
bool OTodoAccessBackendSQL::date( QDate& da, const QString& str ) const{
if ( str == "0-0-0" )
return false;
else{
int day, year, month;
QStringList list = QStringList::split("-", str );
year = list[0].toInt();
month = list[1].toInt();
day = list[2].toInt();
da.setYMD( year, month, day );
return true;
}
}
OTodo OTodoAccessBackendSQL::todo( const OSQLResult& res) const{
if ( res.state() == OSQLResult::Failure ) {
OTodo to;
return to;
}
OSQLResultItem::ValueList list = res.results();
OSQLResultItem::ValueList::Iterator it = list.begin();
qWarning("todo1");
OTodo to = todo( (*it) );
cache( to );
++it;
for ( ; it != list.end(); ++it ) {
qWarning("caching");
cache( todo( (*it) ) );
}
return to;
}
OTodo OTodoAccessBackendSQL::todo( OSQLResultItem& item )const {
qWarning("todo");
bool has = false; QDate da = QDate::currentDate();
has = date( da, item.data("DueDate") );
QStringList cats = QStringList::split(";", item.data("categories") );
OTodo to( (bool)item.data("completed").toInt(), item.data("priority").toInt(),
cats, item.data("summary"), item.data("description"),
item.data("progress").toUShort(), has, da,
item.data("uid").toInt() );
return to;
}
OTodo OTodoAccessBackendSQL::todo( int uid )const {
FindQuery find( uid );
return todo( m_driver->query(&find) );
}
/*
* update the dict
*/
void OTodoAccessBackendSQL::fillDict() {
/* initialize dict */
/*
* UPDATE dict if you change anything!!!
*/
m_dict.setAutoDelete( TRUE );
m_dict.insert("Categories" , new int(OTodo::Category) );
m_dict.insert("Uid" , new int(OTodo::Uid) );
m_dict.insert("HasDate" , new int(OTodo::HasDate) );
m_dict.insert("Completed" , new int(OTodo::Completed) );
m_dict.insert("Description" , new int(OTodo::Description) );
m_dict.insert("Summary" , new int(OTodo::Summary) );
m_dict.insert("Priority" , new int(OTodo::Priority) );
m_dict.insert("DateDay" , new int(OTodo::DateDay) );
m_dict.insert("DateMonth" , new int(OTodo::DateMonth) );
m_dict.insert("DateYear" , new int(OTodo::DateYear) );
m_dict.insert("Progress" , new int(OTodo::Progress) );
m_dict.insert("Completed", new int(OTodo::Completed) );
m_dict.insert("CrossReference", new int(OTodo::CrossReference) );
m_dict.insert("HasAlarmDateTime",new int(OTodo::HasAlarmDateTime) );
m_dict.insert("AlarmDateTime", new int(OTodo::AlarmDateTime) );
}
/*
* need to be const so let's fool the
* compiler :(
*/
void OTodoAccessBackendSQL::update()const {
((OTodoAccessBackendSQL*)this)->m_dirty = false;
LoadQuery lo;
OSQLResult res = m_driver->query(&lo);
if ( res.state() != OSQLResult::Success )
return;
((OTodoAccessBackendSQL*)this)->m_uids = uids( res );
}
QArray<int> OTodoAccessBackendSQL::uids( const OSQLResult& res) const{
OSQLResultItem::ValueList list = res.results();
OSQLResultItem::ValueList::Iterator it;
QArray<int> ints(list.count() );
qWarning(" count = %d", list.count() );
int i = 0;
for (it = list.begin(); it != list.end(); ++it ) {
ints[i] = (*it).data("uid").toInt();
i++;
}
return ints;
}
diff --git a/libopie/pim/otodoaccesssql.h b/libopie/pim/otodoaccesssql.h
index 0f6dd2c..6a4257c 100644
--- a/libopie/pim/otodoaccesssql.h
+++ b/libopie/pim/otodoaccesssql.h
@@ -1,50 +1,50 @@
#ifndef OPIE_PIM_ACCESS_SQL_H
#define OPIE_PIM_ACCESS_SQL_H
#include <qasciidict.h>
#include "otodoaccessbackend.h"
class OSQLDriver;
class OSQLResult;
class OSQLResultItem;
class OTodoAccessBackendSQL : public OTodoAccessBackend {
public:
OTodoAccessBackendSQL( const QString& file );
~OTodoAccessBackendSQL();
bool load();
bool reload();
bool save();
QArray<int> allRecords()const;
- QArray<int> queryByExample( const OTodo& t, int sort );
+ QArray<int> queryByExample( const OTodo& t, int settings, const QDateTime& d = QDateTime() );
OTodo find(int uid)const;
OTodo find(int uid, const QArray<int>&, uint cur, Frontend::CacheDirection )const;
void clear();
bool add( const OTodo& t );
bool remove( int uid );
bool replace( const OTodo& t );
QArray<int> overDue();
QArray<int> effectiveToDos( const QDate& start,
const QDate& end, bool includeNoDates );
QArray<int> sorted(bool asc, int sortOrder, int sortFilter, int cat );
private:
void update()const;
void fillDict();
inline bool date( QDate& date, const QString& )const;
inline OTodo todo( const OSQLResult& )const;
inline OTodo todo( OSQLResultItem& )const;
inline QArray<int> uids( const OSQLResult& )const;
OTodo todo( int uid )const;
QAsciiDict<int> m_dict;
OSQLDriver* m_driver;
QArray<int> m_uids;
bool m_dirty : 1;
};
#endif
diff --git a/libopie/pim/otodoaccessvcal.cpp b/libopie/pim/otodoaccessvcal.cpp
index 309f9e1..2136283 100644
--- a/libopie/pim/otodoaccessvcal.cpp
+++ b/libopie/pim/otodoaccessvcal.cpp
@@ -1,201 +1,201 @@
#include <qfile.h>
#include <qtopia/private/vobject_p.h>
#include <qtopia/timeconversion.h>
#include <qtopia/private/qfiledirect_p.h>
#include "otodoaccessvcal.h"
namespace {
static OTodo eventByVObj( VObject *obj ){
OTodo event;
VObject *ob;
QCString name;
// no uid, attendees, ... and no fun
// description
if( ( ob = isAPropertyOf( obj, VCDescriptionProp )) != 0 ){
name = vObjectStringZValue( ob );
event.setDescription( name );
}
// summary
if ( ( ob = isAPropertyOf( obj, VCSummaryProp ) ) != 0 ) {
name = vObjectStringZValue( ob );
event.setSummary( name );
}
// completed
if( ( ob = isAPropertyOf( obj, VCStatusProp )) != 0 ){
name = vObjectStringZValue( ob );
if( name == "COMPLETED" ){
event.setCompleted( true );
}else{
event.setCompleted( false );
}
}else
event.setCompleted( false );
// priority
if ((ob = isAPropertyOf(obj, VCPriorityProp))) {
name = vObjectStringZValue( ob );
bool ok;
event.setPriority(name.toInt(&ok) );
}
//due date
if((ob = isAPropertyOf(obj, VCDueProp)) ){
event.setHasDueDate( true );
name = vObjectStringZValue( ob );
event.setDueDate( TimeConversion::fromISO8601( name).date() );
}
// categories
if((ob = isAPropertyOf( obj, VCCategoriesProp )) != 0 ){
name = vObjectStringZValue( ob );
qWarning("Categories:%s", name.data() );
}
event.setUid( 1 );
return event;
};
static VObject *vobjByEvent( const OTodo &event ) {
VObject *task = newVObject( VCTodoProp );
if( task == 0 )
return 0l;
if( event.hasDueDate() ) {
QTime time(0, 0, 0);
QDateTime date(event.dueDate(), time );
addPropValue( task, VCDueProp,
TimeConversion::toISO8601( date ) );
}
if( event.isCompleted() )
addPropValue( task, VCStatusProp, "COMPLETED");
QString string = QString::number(event.priority() );
addPropValue( task, VCPriorityProp, string.local8Bit() );
addPropValue( task, VCCategoriesProp,
event.idsToString( event.categories() ).local8Bit() );
addPropValue( task, VCDescriptionProp,
event.description().local8Bit() );
addPropValue( task, VCSummaryProp,
event.summary().local8Bit() );
return task;
};
}
OTodoAccessVCal::OTodoAccessVCal( const QString& path )
: m_dirty(false), m_file( path )
{
}
OTodoAccessVCal::~OTodoAccessVCal() {
}
bool OTodoAccessVCal::load() {
m_map.clear();
m_dirty = false;
VObject* vcal = 0l;
vcal = Parse_MIME_FromFileName( QFile::encodeName(m_file).data() );
if (!vcal )
return false;
// Iterate over the list
VObjectIterator it;
VObject* vobj;
initPropIterator(&it, vcal);
while( moreIteration( &it ) ) {
vobj = ::nextVObject( &it );
QCString name = ::vObjectName( vobj );
if( name == VCTodoProp ){
OTodo to = eventByVObj( vobj );
m_map.insert( to.uid(), to );
}
}
// Should I do a delete vcal?
return true;
}
bool OTodoAccessVCal::reload() {
return load();
}
bool OTodoAccessVCal::save() {
if (!m_dirty )
return true;
QFileDirect file( m_file );
if (!file.open(IO_WriteOnly ) )
return false;
VObject *obj;
obj = newVObject( VCCalProp );
addPropValue( obj, VCVersionProp, "1.0" );
VObject *vo;
for(QMap<int, OTodo>::ConstIterator it=m_map.begin(); it !=m_map.end(); ++it ){
vo = vobjByEvent( it.data() );
addVObjectProp(obj, vo );
}
writeVObject( file.directHandle(), obj );
cleanVObject( obj );
cleanStrTbl();
m_dirty = false;
return true;
}
void OTodoAccessVCal::clear() {
m_map.clear();
m_dirty = true;
}
bool OTodoAccessVCal::add( const OTodo& to ) {
m_map.insert( to.uid(), to );
m_dirty = true;
return true;
}
bool OTodoAccessVCal::remove( int uid ) {
m_map.remove( uid );
m_dirty = true;
return true;
}
void OTodoAccessVCal::removeAllCompleted() {
for ( QMap<int, OTodo>::Iterator it = m_map.begin(); it != m_map.end(); ++it ) {
if ( (*it).isCompleted() )
m_map.remove( it );
}
}
bool OTodoAccessVCal::replace( const OTodo& to ) {
m_map.replace( to.uid(), to );
m_dirty = true;
return true;
}
OTodo OTodoAccessVCal::find(int uid )const {
return m_map[uid];
}
QArray<int> OTodoAccessVCal::sorted( bool, int, int, int ) {
QArray<int> ar(0);
return ar;
}
QArray<int> OTodoAccessVCal::allRecords()const {
QArray<int> ar( m_map.count() );
QMap<int, OTodo>::ConstIterator it;
int i = 0;
for ( it = m_map.begin(); it != m_map.end(); ++it ) {
ar[i] = it.key();
i++;
}
return ar;
}
-QArray<int> OTodoAccessVCal::queryByExample( const OTodo&, int ) {
+QArray<int> OTodoAccessVCal::queryByExample( const OTodo&, int, const QDateTime& ) {
QArray<int> ar(0);
return ar;
}
QArray<int> OTodoAccessVCal::effectiveToDos( const QDate& ,
const QDate& ,
bool ) {
QArray<int> ar(0);
return ar;
}
QArray<int> OTodoAccessVCal::overDue() {
QArray<int> ar(0);
return ar;
}
diff --git a/libopie/pim/otodoaccessvcal.h b/libopie/pim/otodoaccessvcal.h
index 452f602..a90ee9c 100644
--- a/libopie/pim/otodoaccessvcal.h
+++ b/libopie/pim/otodoaccessvcal.h
@@ -1,37 +1,37 @@
#ifndef OPIE_OTODO_ACCESS_VCAL_H
#define OPIE_OTODO_ACCESS_VCAL_H
#include "otodoaccessbackend.h"
class OTodoAccessVCal : public OTodoAccessBackend {
public:
OTodoAccessVCal(const QString& );
~OTodoAccessVCal();
bool load();
bool reload();
bool save();
QArray<int> allRecords()const;
- QArray<int> queryByExample( const OTodo& t, int sort );
+ QArray<int> queryByExample( const OTodo& t, int sort, const QDateTime& d = QDateTime() );
QArray<int> effectiveToDos( const QDate& start,
const QDate& end,
bool includeNoDates );
QArray<int> overDue();
QArray<int> sorted( bool asc, int sortOrder, int sortFilter,
int cat );
OTodo find(int uid)const;
void clear();
bool add( const OTodo& );
bool remove( int uid );
bool replace( const OTodo& );
void removeAllCompleted();
private:
bool m_dirty : 1;
QString m_file;
QMap<int, OTodo> m_map;
};
#endif
diff --git a/libopie/pim/otodoaccessxml.cpp b/libopie/pim/otodoaccessxml.cpp
index cda300b..71e8787 100644
--- a/libopie/pim/otodoaccessxml.cpp
+++ b/libopie/pim/otodoaccessxml.cpp
@@ -1,675 +1,675 @@
#include <errno.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <qfile.h>
#include <qvector.h>
#include <qpe/global.h>
#include <qpe/stringutil.h>
#include <qpe/timeconversion.h>
#include "otodoaccessxml.h"
namespace {
// FROM TT again
char *strstrlen(const char *haystack, int hLen, const char* needle, int nLen)
{
char needleChar;
char haystackChar;
if (!needle || !haystack || !hLen || !nLen)
return 0;
const char* hsearch = haystack;
if ((needleChar = *needle++) != 0) {
nLen--; //(to make up for needle++)
do {
do {
if ((haystackChar = *hsearch++) == 0)
return (0);
if (hsearch >= haystack + hLen)
return (0);
} while (haystackChar != needleChar);
} while (strncmp(hsearch, needle, QMIN(hLen - (hsearch - haystack), nLen)) != 0);
hsearch--;
}
return ((char *)hsearch);
}
}
OTodoAccessXML::OTodoAccessXML( const QString& appName,
const QString& fileName )
: OTodoAccessBackend(), m_app( appName ), m_opened( false ), m_changed( false )
{
if (!fileName.isEmpty() )
m_file = fileName;
else
m_file = Global::applicationFileName( "todolist", "todolist.xml" );
}
OTodoAccessXML::~OTodoAccessXML() {
}
bool OTodoAccessXML::load() {
m_opened = true;
m_changed = false;
/* initialize dict */
/*
* UPDATE dict if you change anything!!!
*/
QAsciiDict<int> dict(21);
dict.setAutoDelete( TRUE );
dict.insert("Categories" , new int(OTodo::Category) );
dict.insert("Uid" , new int(OTodo::Uid) );
dict.insert("HasDate" , new int(OTodo::HasDate) );
dict.insert("Completed" , new int(OTodo::Completed) );
dict.insert("Description" , new int(OTodo::Description) );
dict.insert("Summary" , new int(OTodo::Summary) );
dict.insert("Priority" , new int(OTodo::Priority) );
dict.insert("DateDay" , new int(OTodo::DateDay) );
dict.insert("DateMonth" , new int(OTodo::DateMonth) );
dict.insert("DateYear" , new int(OTodo::DateYear) );
dict.insert("Progress" , new int(OTodo::Progress) );
dict.insert("Completed", new int(OTodo::Completed) );
dict.insert("CrossReference", new int(OTodo::CrossReference) );
dict.insert("State", new int(OTodo::State) );
dict.insert("Recurrence", new int(OTodo::Recurrence) );
dict.insert("Alarms", new int(OTodo::Alarms) );
dict.insert("Reminders", new int(OTodo::Reminders) );
dict.insert("Notifiers", new int(OTodo::Notifiers) );
dict.insert("Maintainer", new int(OTodo::Maintainer) );
// here the custom XML parser from TT it's GPL
// but we want to push OpiePIM... to TT.....
// mmap part from zecke :)
int fd = ::open( QFile::encodeName(m_file).data(), O_RDONLY );
struct stat attribut;
if ( fd < 0 ) return false;
if ( fstat(fd, &attribut ) == -1 ) {
::close( fd );
return false;
}
void* map_addr = ::mmap(NULL, attribut.st_size, PROT_READ, MAP_SHARED, fd, 0 );
if ( map_addr == ( (caddr_t)-1) ) {
::close(fd );
return false;
}
/* advise the kernel who we want to read it */
::madvise( map_addr, attribut.st_size, MADV_SEQUENTIAL );
/* we do not the file any more */
::close( fd );
char* dt = (char*)map_addr;
int len = attribut.st_size;
int i = 0;
char *point;
const char* collectionString = "<Task ";
int strLen = strlen(collectionString);
while ( ( point = strstrlen( dt+i, len -i, collectionString, strLen ) ) != 0l ) {
i = point -dt;
i+= strLen;
qWarning("Found a start at %d %d", i, (point-dt) );
OTodo ev;
m_year = m_month = m_day = 0;
while ( TRUE ) {
while ( i < len && (dt[i] == ' ' || dt[i] == '\n' || dt[i] == '\r') )
++i;
if ( i >= len-2 || (dt[i] == '/' && dt[i+1] == '>') )
break;
// we have another attribute, read it.
int j = i;
while ( j < len && dt[j] != '=' )
++j;
QCString attr( dt+i, j-i+1);
i = ++j; // skip =
// find the start of quotes
while ( i < len && dt[i] != '"' )
++i;
j = ++i;
bool haveUtf = FALSE;
bool haveEnt = FALSE;
while ( j < len && dt[j] != '"' ) {
if ( ((unsigned char)dt[j]) > 0x7f )
haveUtf = TRUE;
if ( dt[j] == '&' )
haveEnt = TRUE;
++j;
}
if ( i == j ) {
// empty value
i = j + 1;
continue;
}
QCString value( dt+i, j-i+1 );
i = j + 1;
QString str = (haveUtf ? QString::fromUtf8( value )
: QString::fromLatin1( value ) );
if ( haveEnt )
str = Qtopia::plainString( str );
/*
* add key + value
*/
todo( &dict, ev, attr, str );
}
/*
* now add it
*/
qWarning("End at %d", i );
if (m_events.contains( ev.uid() ) || ev.uid() == 0) {
ev.setUid( 1 );
m_changed = true;
}
if ( ev.hasDueDate() ) {
ev.setDueDate( QDate(m_year, m_month, m_day) );
}
m_events.insert(ev.uid(), ev );
m_year = m_month = m_day = -1;
}
munmap(map_addr, attribut.st_size );
qWarning("counts %d records loaded!", m_events.count() );
return true;
}
bool OTodoAccessXML::reload() {
m_events.clear();
return load();
}
bool OTodoAccessXML::save() {
// qWarning("saving");
if (!m_opened || !m_changed ) {
// qWarning("not saving");
return true;
}
QString strNewFile = m_file + ".new";
QFile f( strNewFile );
if (!f.open( IO_WriteOnly|IO_Raw ) )
return false;
int written;
QString out;
out = "<!DOCTYPE Tasks>\n<Tasks>\n";
// for all todos
QMap<int, OTodo>::Iterator it;
for (it = m_events.begin(); it != m_events.end(); ++it ) {
out+= "<Task " + toString( (*it) ) + " />\n";
QCString cstr = out.utf8();
written = f.writeBlock( cstr.data(), cstr.length() );
/* less written then we wanted */
if ( written != (int)cstr.length() ) {
f.close();
QFile::remove( strNewFile );
return false;
}
out = QString::null;
}
out += "</Tasks>";
QCString cstr = out.utf8();
written = f.writeBlock( cstr.data(), cstr.length() );
if ( written != (int)cstr.length() ) {
f.close();
QFile::remove( strNewFile );
return false;
}
/* flush before renaming */
f.close();
if( ::rename( strNewFile.latin1(), m_file.latin1() ) < 0 ) {
// qWarning("error renaming");
QFile::remove( strNewFile );
}
m_changed = false;
return true;
}
QArray<int> OTodoAccessXML::allRecords()const {
QArray<int> ids( m_events.count() );
QMap<int, OTodo>::ConstIterator it;
int i = 0;
for ( it = m_events.begin(); it != m_events.end(); ++it ) {
ids[i] = it.key();
i++;
}
return ids;
}
-QArray<int> OTodoAccessXML::queryByExample( const OTodo&, int ) {
+QArray<int> OTodoAccessXML::queryByExample( const OTodo&, int, const QDateTime& ) {
QArray<int> ids(0);
return ids;
}
OTodo OTodoAccessXML::find( int uid )const {
OTodo todo;
todo.setUid( 0 ); // isEmpty()
QMap<int, OTodo>::ConstIterator it = m_events.find( uid );
if ( it != m_events.end() )
todo = it.data();
return todo;
}
void OTodoAccessXML::clear() {
if (m_opened )
m_changed = true;
m_events.clear();
}
bool OTodoAccessXML::add( const OTodo& todo ) {
// qWarning("add");
m_changed = true;
m_events.insert( todo.uid(), todo );
return true;
}
bool OTodoAccessXML::remove( int uid ) {
m_changed = true;
m_events.remove( uid );
return true;
}
bool OTodoAccessXML::replace( const OTodo& todo) {
m_changed = true;
m_events.replace( todo.uid(), todo );
return true;
}
QArray<int> OTodoAccessXML::effectiveToDos( const QDate& start,
const QDate& end,
bool includeNoDates ) {
QArray<int> ids( m_events.count() );
QMap<int, OTodo>::Iterator it;
int i = 0;
for ( it = m_events.begin(); it != m_events.end(); ++it ) {
if ( !it.data().hasDueDate() ) {
if ( includeNoDates ) {
ids[i] = it.key();
i++;
}
}else if ( it.data().dueDate() >= start &&
it.data().dueDate() <= end ) {
ids[i] = it.key();
i++;
}
}
ids.resize( i );
return ids;
}
QArray<int> OTodoAccessXML::overDue() {
QArray<int> ids( m_events.count() );
int i = 0;
QMap<int, OTodo>::Iterator it;
for ( it = m_events.begin(); it != m_events.end(); ++it ) {
if ( it.data().isOverdue() ) {
ids[i] = it.key();
i++;
}
}
ids.resize( i );
return ids;
}
/* private */
void OTodoAccessXML::todo( QAsciiDict<int>* dict, OTodo& ev,
const QCString& attr, const QString& val) {
// qWarning("parse to do from XMLElement" );
int *find=0;
find = (*dict)[ attr.data() ];
if (!find ) {
// qWarning("Unknown option" + it.key() );
ev.setCustomField( attr, val );
return;
}
switch( *find ) {
case OTodo::Uid:
ev.setUid( val.toInt() );
break;
case OTodo::Category:
ev.setCategories( ev.idsFromString( val ) );
break;
case OTodo::HasDate:
ev.setHasDueDate( val.toInt() );
break;
case OTodo::Completed:
ev.setCompleted( val.toInt() );
break;
case OTodo::Description:
ev.setDescription( val );
break;
case OTodo::Summary:
ev.setSummary( val );
break;
case OTodo::Priority:
ev.setPriority( val.toInt() );
break;
case OTodo::DateDay:
m_day = val.toInt();
break;
case OTodo::DateMonth:
m_month = val.toInt();
break;
case OTodo::DateYear:
m_year = val.toInt();
break;
case OTodo::Progress:
ev.setProgress( val.toInt() );
break;
case OTodo::CrossReference:
{
/*
* A cross refernce looks like
* appname,id;appname,id
* we need to split it up
*/
QStringList refs = QStringList::split(';', val );
QStringList::Iterator strIt;
for (strIt = refs.begin(); strIt != refs.end(); ++strIt ) {
int pos = (*strIt).find(',');
if ( pos > -1 )
; // ev.addRelation( (*strIt).left(pos), (*strIt).mid(pos+1).toInt() );
}
break;
}
default:
break;
}
}
QString OTodoAccessXML::toString( const OTodo& ev )const {
QString str;
str += "Completed=\"" + QString::number( ev.isCompleted() ) + "\" ";
str += "HasDate=\"" + QString::number( ev.hasDueDate() ) + "\" ";
str += "Priority=\"" + QString::number( ev.priority() ) + "\" ";
str += "Progress=\"" + QString::number(ev.progress() ) + "\" ";
str += "Categories=\"" + toString( ev.categories() ) + "\" ";
str += "Description=\"" + Qtopia::escapeString( ev.description() ) + "\" ";
str += "Summary=\"" + Qtopia::escapeString( ev.summary() ) + "\" ";
if ( ev.hasDueDate() ) {
str += "DateYear=\"" + QString::number( ev.dueDate().year() ) + "\" ";
str += "DateMonth=\"" + QString::number( ev.dueDate().month() ) + "\" ";
str += "DateDay=\"" + QString::number( ev.dueDate().day() ) + "\" ";
}
// qWarning( "Uid %d", ev.uid() );
str += "Uid=\"" + QString::number( ev.uid() ) + "\" ";
// append the extra options
/* FIXME Qtopia::Record this is currently not
* possible you can set custom fields
* but don' iterate over the list
* I may do #define private protected
* for this case - cough --zecke
*/
/*
QMap<QString, QString> extras = ev.extras();
QMap<QString, QString>::Iterator extIt;
for (extIt = extras.begin(); extIt != extras.end(); ++extIt )
str += extIt.key() + "=\"" + extIt.data() + "\" ";
*/
// cross refernce
return str;
}
QString OTodoAccessXML::toString( const QArray<int>& ints ) const {
return Qtopia::Record::idsToString( ints );
}
/* internal class for sorting
*
* Inspired by todoxmlio.cpp from TT
*/
struct OTodoXMLContainer {
OTodo todo;
};
namespace {
inline QString string( const OTodo& todo) {
return todo.summary().isEmpty() ?
todo.description().left(20 ) :
todo.summary();
}
inline int completed( const OTodo& todo1, const OTodo& todo2) {
int ret = 0;
if ( todo1.isCompleted() ) ret++;
if ( todo2.isCompleted() ) ret--;
return ret;
}
inline int priority( const OTodo& t1, const OTodo& t2) {
return ( t1.priority() - t2.priority() );
}
inline int description( const OTodo& t1, const OTodo& t2) {
return QString::compare( string(t1), string(t2) );
}
inline int deadline( const OTodo& t1, const OTodo& t2) {
int ret = 0;
if ( t1.hasDueDate() &&
t2.hasDueDate() )
ret = t2.dueDate().daysTo( t1.dueDate() );
else if ( t1.hasDueDate() )
ret = -1;
else if ( t2.hasDueDate() )
ret = 1;
else
ret = 0;
return ret;
}
};
/*
* Returns:
* 0 if item1 == item2
*
* non-zero if item1 != item2
*
* This function returns int rather than bool so that reimplementations
* can return one of three values and use it to sort by:
*
* 0 if item1 == item2
*
* > 0 (positive integer) if item1 > item2
*
* < 0 (negative integer) if item1 < item2
*
*/
class OTodoXMLVector : public QVector<OTodoXMLContainer> {
public:
OTodoXMLVector(int size, bool asc, int sort)
: QVector<OTodoXMLContainer>( size )
{
setAutoDelete( true );
m_asc = asc;
m_sort = sort;
}
/* return the summary/description */
QString string( const OTodo& todo) {
return todo.summary().isEmpty() ?
todo.description().left(20 ) :
todo.summary();
}
/**
* we take the sortorder( switch on it )
*
*/
int compareItems( Item d1, Item d2 ) {
bool seComp, sePrio, seDesc, seDeadline;
seComp = sePrio = seDeadline = seDesc = false;
int ret =0;
OTodoXMLContainer* con1 = (OTodoXMLContainer*)d1;
OTodoXMLContainer* con2 = (OTodoXMLContainer*)d2;
/* same item */
if ( con1->todo.uid() == con2->todo.uid() )
return 0;
switch ( m_sort ) {
/* completed */
case 0: {
ret = completed( con1->todo, con2->todo );
seComp = TRUE;
break;
}
/* priority */
case 1: {
ret = priority( con1->todo, con2->todo );
sePrio = TRUE;
break;
}
/* description */
case 2: {
ret = description( con1->todo, con2->todo );
seDesc = TRUE;
break;
}
/* deadline */
case 3: {
ret = deadline( con1->todo, con2->todo );
seDeadline = TRUE;
break;
}
default:
ret = 0;
break;
};
/*
* FIXME do better sorting if the first sort criteria
* ret equals 0 start with complete and so on...
*/
/* twist it we're not ascending*/
if (!m_asc)
ret = ret * -1;
if ( ret )
return ret;
// default did not gave difference let's try it other way around
/*
* General try if already checked if not test
* and return
* 1.Completed
* 2.Priority
* 3.Description
* 4.DueDate
*/
if (!seComp ) {
if ( (ret = completed( con1->todo, con2->todo ) ) ) {
if (!m_asc ) ret *= -1;
return ret;
}
}
if (!sePrio ) {
if ( (ret = priority( con1->todo, con2->todo ) ) ) {
if (!m_asc ) ret *= -1;
return ret;
}
}
if (!seDesc ) {
if ( (ret = description(con1->todo, con2->todo ) ) ) {
if (!m_asc) ret *= -1;
return ret;
}
}
if (!seDeadline) {
if ( (ret = deadline( con1->todo, con2->todo ) ) ) {
if (!m_asc) ret *= -1;
return ret;
}
}
return 0;
}
private:
bool m_asc;
int m_sort;
};
QArray<int> OTodoAccessXML::sorted( bool asc, int sortOrder,
int sortFilter, int cat ) {
qWarning("sorted! %d cat", cat);
OTodoXMLVector vector(m_events.count(), asc,sortOrder );
QMap<int, OTodo>::Iterator it;
int item = 0;
bool bCat = sortFilter & 1 ? true : false;
bool bOnly = sortFilter & 2 ? true : false;
bool comp = sortFilter & 4 ? true : false;
for ( it = m_events.begin(); it != m_events.end(); ++it ) {
/* show category */
if ( bCat && cat != 0)
if (!(*it).categories().contains( cat ) ) {
qWarning("category mis match");
continue;
}
/* isOverdue but we should not show overdue - why?*/
/* if ( (*it).isOverdue() && !bOnly ) {
qWarning("item is overdue but !bOnly");
continue;
}
*/
if ( !(*it).isOverdue() && bOnly ) {
qWarning("item is not overdue but bOnly checked");
continue;
}
if ((*it).isCompleted() && comp ) {
qWarning("completed continue!");
continue;
}
OTodoXMLContainer* con = new OTodoXMLContainer();
con->todo = (*it);
vector.insert(item, con );
item++;
}
qWarning("XXX %d Items added", item);
vector.resize( item );
/* sort it now */
vector.sort();
/* now get the uids */
QArray<int> array( vector.count() );
for (uint i= 0; i < vector.count(); i++ ) {
array[i] = ( vector.at(i) )->todo.uid();
}
qWarning("array count = %d %d", array.count(), vector.count() );
return array;
};
void OTodoAccessXML::removeAllCompleted() {
for ( QMap<int, OTodo>::Iterator it = m_events.begin(); it != m_events.end(); ++it ) {
if ( (*it).isCompleted() )
m_events.remove( it );
}
}
diff --git a/libopie/pim/otodoaccessxml.h b/libopie/pim/otodoaccessxml.h
index 93609fe..1032c92 100644
--- a/libopie/pim/otodoaccessxml.h
+++ b/libopie/pim/otodoaccessxml.h
@@ -1,57 +1,57 @@
#ifndef OPIE_TODO_ACCESS_XML_H
#define OPIE_TODO_ACCESS_XML_H
#include <qasciidict.h>
#include <qmap.h>
#include "otodoaccessbackend.h"
namespace Opie {
class XMLElement;
};
class OTodoAccessXML : public OTodoAccessBackend {
public:
/**
* fileName if Empty we will use the default path
*/
OTodoAccessXML( const QString& appName,
const QString& fileName = QString::null );
~OTodoAccessXML();
bool load();
bool reload();
bool save();
QArray<int> allRecords()const;
- QArray<int> queryByExample( const OTodo&, int querysettings );
+ QArray<int> queryByExample( const OTodo&, int querysettings, const QDateTime& d = QDateTime() );
OTodo find( int uid )const;
void clear();
bool add( const OTodo& );
bool remove( int uid );
void removeAllCompleted();
bool replace( const OTodo& );
/* our functions */
QArray<int> effectiveToDos( const QDate& start,
const QDate& end,
bool includeNoDates );
QArray<int> overDue();
QArray<int> sorted( bool asc, int sortOrder,
int sortFilter, int cat );
private:
void todo( QAsciiDict<int>*, OTodo&,const QCString&,const QString& );
QString toString( const OTodo& )const;
QString toString( const QArray<int>& ints ) const;
QMap<int, OTodo> m_events;
QString m_file;
QString m_app;
bool m_opened : 1;
bool m_changed : 1;
class OTodoAccessXMLPrivate;
OTodoAccessXMLPrivate* d;
int m_year, m_month, m_day;
};
#endif
diff --git a/libopie2/opiepim/backend/ocontactaccessbackend_vcard.cpp b/libopie2/opiepim/backend/ocontactaccessbackend_vcard.cpp
index e537269..f24523f 100644
--- a/libopie2/opiepim/backend/ocontactaccessbackend_vcard.cpp
+++ b/libopie2/opiepim/backend/ocontactaccessbackend_vcard.cpp
@@ -1,615 +1,623 @@
/*
* 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.9 2003/03/21 10:33:09 eilers
+ * Merged speed optimized xml backend for contacts to main.
+ * Added QDateTime to querybyexample. For instance, it is now possible to get
+ * all Birthdays/Anniversaries between two dates. This should be used
+ * to show all birthdays in the datebook..
+ * This change is sourcecode backward compatible but you have to upgrade
+ * the binaries for today-addressbook.
+ *
* Revision 1.8 2003/02/21 16:52:49 zecke
* -Remove old Todo classes they're deprecated and today I already using the
* new API
* -Guard against self assignment in OTodo
* -Add test apps for OPIM
* -Opiefied Event classes
* -Added TimeZone handling and pinning of TimeZones to OEvent
* -Adjust ORecur and the widget to better timezone behaviour
*
* Revision 1.7 2003/02/16 22:25:46 zecke
* 0000276 Fix for that bug.. or better temp workaround
* A Preferred Number is HOME|VOICE
* A CellPhone is HOME|VOICE|CELL the type & HOME|VOICE test
* triggers both
* and the cell phone number overrides the other entries..
*
* as a temp I check that it's not equal to HOME|VOICE|CELL before setting the
* number
*
* The right and final fix would be to reorder the if statement to make it
* if else based and the less common thing put to the bottom
*
* OTodoAccessVcal fix the date for beaming
*
* Revision 1.6 2003/01/13 15:49:31 eilers
* Fixing crash when businesscard.vcf is missing..
*
* Revision 1.5 2002/12/07 13:26:22 eilers
* Fixing bug in storing anniversary..
*
* Revision 1.4 2002/11/13 14:14:51 eilers
* Added sorted for Contacts..
*
* Revision 1.3 2002/11/11 16:41:09 kergoth
* no default arguments in implementation
*
* Revision 1.2 2002/11/10 15:41:53 eilers
* Bugfixes..
*
* Revision 1.1 2002/11/09 14:34:52 eilers
* Added VCard Backend.
*
*/
#include "ocontactaccessbackend_vcard.h"
#include "../../library/backend/vobject_p.h"
#include "../../library/backend/qfiledirect_p.h"
#include <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;
if ( QFile::exists(m_file) ){
obj = Parse_MIME_FromFileName( QFile::encodeName(m_file).data() );
if ( !obj )
return false;
}else{
qWarning("File \"%s\" not found !", m_file.latin1() );
return false;
}
while ( obj ) {
OContact con = parseVObject( obj );
/*
* if uid is 0 assign a new one
* this at least happens on
* Nokia6210
*/
if ( con.uid() == 0 ){
con.setUid( 1 );
qWarning("assigned new uid %d",con.uid() );
}
m_map.insert( con.uid(), con );
VObject *t = obj;
obj = nextVObjectInList(obj);
cleanVObject( t );
}
return true;
}
bool OContactAccessBackend_VCard::reload()
{
return load();
}
bool OContactAccessBackend_VCard::save()
{
if (!m_dirty )
return true;
QFileDirect file( m_file );
if (!file.open(IO_WriteOnly ) )
return false;
VObject *obj;
obj = newVObject( VCCalProp );
addPropValue( obj, VCVersionProp, "1.0" );
VObject *vo;
for(QMap<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> OContactAccessBackend_VCard::queryByExample ( const OContact&, int, const QDateTime& )
{
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;
qWarning("value %s %d", value.data(), type );
if ( (type & (VOICE|HOME) ) == (VOICE|HOME) && (type & (CELL|HOME) ) != (CELL|HOME) )
c.setHomePhone( value );
if ( ( type & (FAX|HOME) ) == (FAX|HOME) )
c.setHomeFax( value );
if ( ( type & (CELL|HOME) ) == (CELL|HOME) )
c.setHomeMobile( value );
if ( ( type & (VOICE|WORK) ) == (VOICE|WORK) && (type & (CELL|WORK) ) != (CELL|WORK) )
c.setBusinessPhone( value );
if ( ( type & (FAX|WORK) ) == (FAX|WORK) )
c.setBusinessFax( value );
if ( ( type & (CELL|WORK) ) == (CELL|WORK) )
c.setBusinessMobile( value );
if ( ( type & (PAGER|WORK) ) == (PAGER|WORK) )
c.setBusinessPager( value );
}
}
else if ( name == VCEmailAddressProp ) {
QString email = vObjectStringZValue( o );
bool valid = TRUE;
VObjectIterator nit;
initPropIterator( &nit, o );
while( moreIteration( &nit ) ) {
VObject *o = nextVObject( &nit );
QCString name = vObjectTypeInfo( o );
if ( name != VCInternetProp && name != VCHomeProp &&
name != VCWorkProp &&
name != VCPreferredProp )
// ### preffered should map to default email
valid = FALSE;
}
if ( valid ) {
c.insertEmail( email );
}
}
else if ( name == VCURLProp ) {
VObjectIterator nit;
initPropIterator( &nit, o );
while( moreIteration( &nit ) ) {
VObject *o = nextVObject( &nit );
QCString name = vObjectTypeInfo( o );
if ( name == VCHomeProp )
c.setHomeWebpage( value );
else if ( name == VCWorkProp )
c.setBusinessWebpage( value );
}
}
else if ( name == VCOrgProp ) {
VObjectIterator nit;
initPropIterator( &nit, o );
while( moreIteration( &nit ) ) {
VObject *o = nextVObject( &nit );
QCString name = vObjectName( o );
QString value = vObjectStringZValue( o );
if ( name == VCOrgNameProp )
c.setCompany( value );
else if ( name == VCOrgUnitProp )
c.setDepartment( value );
else if ( name == VCOrgUnit2Prop )
c.setOffice( value );
}
}
else if ( name == VCTitleProp ) {
c.setJobTitle( value );
}
else if ( name == "X-Qtopia-Profession" ) {
c.setProfession( value );
}
else if ( name == "X-Qtopia-Manager" ) {
c.setManager( value );
}
else if ( name == "X-Qtopia-Assistant" ) {
c.setAssistant( value );
}
else if ( name == "X-Qtopia-Spouse" ) {
c.setSpouse( value );
}
else if ( name == "X-Qtopia-Gender" ) {
c.setGender( value );
}
else if ( name == "X-Qtopia-Anniversary" ) {
c.setAnniversary( convVCardDateToDate( value ) );
}
else if ( name == "X-Qtopia-Nickname" ) {
c.setNickname( value );
}
else if ( name == "X-Qtopia-Children" ) {
c.setChildren( value );
}
else if ( name == VCBirthDateProp ) {
// Reading Birthdate regarding RFC 2425 (5.8.4)
c.setBirthday( convVCardDateToDate( value ) );
}
#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() ){
qWarning("Exporting birthday as: %s", convDateToVCardDate( c.birthday() ).latin1() );
safeAddPropValue( vcard, VCBirthDateProp, convDateToVCardDate( c.birthday() ) );
}
if ( !c.company().isEmpty() || !c.department().isEmpty() || !c.office().isEmpty() ) {
VObject *org = safeAddProp( vcard, VCOrgProp );
safeAddPropValue( org, VCOrgNameProp, c.company() );
safeAddPropValue( org, VCOrgUnitProp, c.department() );
safeAddPropValue( org, VCOrgUnit2Prop, c.office() );
}
// some values we have to export as custom fields
safeAddPropValue( vcard, "X-Qtopia-Profession", c.profession() );
safeAddPropValue( vcard, "X-Qtopia-Manager", c.manager() );
safeAddPropValue( vcard, "X-Qtopia-Assistant", c.assistant() );
safeAddPropValue( vcard, "X-Qtopia-Spouse", c.spouse() );
safeAddPropValue( vcard, "X-Qtopia-Gender", c.gender() );
if ( c.anniversary().isValid() ){
qWarning("Exporting anniversary as: %s", convDateToVCardDate( c.anniversary() ).latin1() );
safeAddPropValue( vcard, "X-Qtopia-Anniversary", convDateToVCardDate( c.anniversary() ) );
}
safeAddPropValue( vcard, "X-Qtopia-Nickname", c.nickname() );
safeAddPropValue( vcard, "X-Qtopia-Children", c.children() );
return vcard;
}
QString OContactAccessBackend_VCard::convDateToVCardDate( const QDate& d ) const
{
QString str_rfc2425 = QString("%1-%2-%3")
.arg( d.year() )
.arg( d.month(), 2 )
.arg( d.day(), 2 );
// Now replace spaces with "0"...
int pos = 0;
while ( ( pos = str_rfc2425.find (' ') ) > 0 )
str_rfc2425.replace( pos, 1, "0" );
return str_rfc2425;
}
QDate OContactAccessBackend_VCard::convVCardDateToDate( const QString& datestr )
{
int monthPos = datestr.find('-');
int dayPos = datestr.find('-', monthPos+1 );
int sep_ignore = 1;
if ( monthPos == -1 || dayPos == -1 ) {
qDebug("fromString didn't find - in str = %s; mpos = %d ypos = %d", datestr.latin1(), monthPos, dayPos );
// Ok.. No "-" found, therefore we will try to read other format ( YYYYMMDD )
if ( datestr.length() == 8 ){
monthPos = 4;
dayPos = 6;
sep_ignore = 0;
qDebug("Try with follwing positions str = %s; mpos = %d ypos = %d", datestr.latin1(), monthPos, dayPos );
} else {
return QDate();
}
}
int y = datestr.left( monthPos ).toInt();
int m = datestr.mid( monthPos + sep_ignore, dayPos - monthPos - sep_ignore ).toInt();
int d = datestr.mid( dayPos + sep_ignore ).toInt();
qDebug("TimeConversion::fromString ymd = %s => %d %d %d; mpos = %d ypos = %d", datestr.latin1(), y, m, d, monthPos, dayPos);
QDate date ( y,m,d );
return date;
}
VObject* OContactAccessBackend_VCard::safeAddPropValue( VObject *o, const char *prop, const QString &value )
{
VObject *ret = 0;
if ( o && !value.isEmpty() )
ret = addPropValue( o, prop, value.latin1() );
return ret;
}
VObject* OContactAccessBackend_VCard::safeAddProp( VObject *o, const char *prop)
{
VObject *ret = 0;
if ( o )
ret = addProp( o, prop );
return ret;
}
diff --git a/libopie2/opiepim/backend/ocontactaccessbackend_vcard.h b/libopie2/opiepim/backend/ocontactaccessbackend_vcard.h
index 236da00..93e2da3 100644
--- a/libopie2/opiepim/backend/ocontactaccessbackend_vcard.h
+++ b/libopie2/opiepim/backend/ocontactaccessbackend_vcard.h
@@ -1,78 +1,86 @@
/*
* 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.5 2003/03/21 10:33:09 eilers
+ * Merged speed optimized xml backend for contacts to main.
+ * Added QDateTime to querybyexample. For instance, it is now possible to get
+ * all Birthdays/Anniversaries between two dates. This should be used
+ * to show all birthdays in the datebook..
+ * This change is sourcecode backward compatible but you have to upgrade
+ * the binaries for today-addressbook.
+ *
* Revision 1.4 2002/12/07 13:26:22 eilers
* Fixing bug in storing anniversary..
*
* Revision 1.3 2002/11/13 14:14:51 eilers
* Added sorted for Contacts..
*
* Revision 1.2 2002/11/10 15:41:53 eilers
* Bugfixes..
*
* Revision 1.1 2002/11/09 14:34:52 eilers
* Added VCard Backend.
*
*/
#ifndef __OCONTACTACCESSBACKEND_VCARD_H_
#define __OCONTACTACCESSBACKEND_VCARD_H_
#include <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> queryByExample ( const OContact &query, int settings, const QDateTime& d = QDateTime() );
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 );
QString convDateToVCardDate( const QDate& c ) const;
QDate convVCardDateToDate( const QString& datestr );
VObject *safeAddPropValue( VObject *o, const char* prop, const QString& value );
VObject *safeAddProp( VObject* o, const char* prop);
bool m_dirty : 1;
QString m_file;
QMap<int, OContact> m_map;
};
#endif
diff --git a/libopie2/opiepim/backend/ocontactaccessbackend_xml.cpp b/libopie2/opiepim/backend/ocontactaccessbackend_xml.cpp
index 2df6757..4abf4d9 100644
--- a/libopie2/opiepim/backend/ocontactaccessbackend_xml.cpp
+++ b/libopie2/opiepim/backend/ocontactaccessbackend_xml.cpp
@@ -1,739 +1,777 @@
/*
* 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.2 2003/03/21 10:33:09 eilers
+ * Merged speed optimized xml backend for contacts to main.
+ * Added QDateTime to querybyexample. For instance, it is now possible to get
+ * all Birthdays/Anniversaries between two dates. This should be used
+ * to show all birthdays in the datebook..
+ * This change is sourcecode backward compatible but you have to upgrade
+ * the binaries for today-addressbook.
+ *
+ * Revision 1.1.2.2 2003/02/11 12:17:28 eilers
+ * Speed optimization. Removed the sequential search loops.
+ *
+ * Revision 1.1.2.1 2003/02/10 15:31:38 eilers
+ * Writing offsets to debug output..
+ *
* Revision 1.1 2003/02/09 15:05:01 eilers
* Nothing happened.. Just some cleanup before I will start..
*
* Revision 1.12 2003/01/03 16:58:03 eilers
* Reenable debug output
*
* Revision 1.11 2003/01/03 12:31:28 eilers
* Bugfix for calculating data diffs..
*
* Revision 1.10 2003/01/02 14:27:12 eilers
* Improved query by example: Search by date is possible.. First step
* for a today plugin for birthdays..
*
* Revision 1.9 2002/12/08 12:48:57 eilers
* Moved journal-enum from ocontact into i the xml-backend..
*
* Revision 1.8 2002/11/14 17:04:24 eilers
* Sorting will now work if fullname is identical on some entries
*
* Revision 1.7 2002/11/13 15:02:46 eilers
* Small Bug in sorted fixed
*
* Revision 1.6 2002/11/13 14:14:51 eilers
* Added sorted for Contacts..
*
* Revision 1.5 2002/11/01 15:10:42 eilers
* Added regExp-search in database for all fields in a contact.
*
* Revision 1.4 2002/10/16 10:52:40 eilers
* Added some docu to the interface and now using the cache infrastucture by zecke.. :)
*
* Revision 1.3 2002/10/14 16:21:54 eilers
* Some minor interface updates
*
* Revision 1.2 2002/10/07 17:34:24 eilers
* added OBackendFactory for advanced backend access
*
* Revision 1.1 2002/09/27 17:11:44 eilers
* Added API for accessing the Contact-Database ! It is compiling, but
* please do not expect that anything is working !
* I will debug that stuff in the next time ..
* Please read README_COMPILE for compiling !
*
*
*/
#include "ocontactaccessbackend_xml.h"
#include <qasciidict.h>
#include <qdatetime.h>
#include <qfile.h>
#include <qfileinfo.h>
#include <qregexp.h>
#include <qarray.h>
#include <qmap.h>
#include <qdatetime.h>
#include <qpe/global.h>
#include <opie/xmltree.h>
#include "ocontactaccessbackend.h"
#include "ocontactaccess.h"
#include <stdlib.h>
#include <errno.h>
using namespace Opie;
OContactAccessBackend_XML::OContactAccessBackend_XML ( QString appname, QString filename = 0l ):
m_changed( false )
{
+ // Just m_contactlist should call delete if an entry
+ // is removed.
+ m_contactList.setAutoDelete( true );
+ m_uidToContact.setAutoDelete( false );
+
m_appName = appname;
/* Set journalfile name ... */
m_journalName = getenv("HOME");
m_journalName +="/.abjournal" + appname;
/* Expecting to access the default filename if nothing else is set */
if ( filename.isEmpty() ){
m_fileName = Global::applicationFileName( "addressbook","addressbook.xml" );
} else
m_fileName = filename;
/* Load Database now */
load ();
}
bool OContactAccessBackend_XML::save()
{
if ( !m_changed )
return true;
QString strNewFile = m_fileName + ".new";
QFile f( strNewFile );
if ( !f.open( IO_WriteOnly|IO_Raw ) )
return false;
int total_written;
+ int idx_offset = 0;
QString out;
+
+ // Write Header
out = "<?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 ) {
+ QCString cstr = out.utf8();
+ f.writeBlock( cstr.data(), cstr.length() );
+ idx_offset += cstr.length();
+ out = "";
+
+ // Write all contacts
+ QListIterator<OContact> it( m_contactList );
+ for ( ; it.current(); ++it ) {
+ qWarning(" Uid %d at Offset: %x", (*it)->uid(), idx_offset );
out += "<Contact ";
- (*it).save( out );
+ (*it)->save( out );
out += "/>\n";
- QCString cstr = out.utf8();
+ cstr = out.utf8();
total_written = f.writeBlock( cstr.data(), cstr.length() );
+ idx_offset += cstr.length();
if ( total_written != int(cstr.length()) ) {
f.close();
QFile::remove( strNewFile );
return false;
}
out = "";
}
out += " </Contacts>\n</AddressBook>\n";
- QCString cstr = out.utf8();
+ // Write Footer
+ cstr = out.utf8();
total_written = f.writeBlock( cstr.data(), cstr.length() );
if ( total_written != int( cstr.length() ) ) {
f.close();
QFile::remove( strNewFile );
return false;
}
f.close();
// move the file over, I'm just going to use the system call
// because, I don't feel like using QDir.
if ( ::rename( strNewFile.latin1(), m_fileName.latin1() ) < 0 ) {
qWarning( "problem renaming file %s to %s, errno: %d",
strNewFile.latin1(), m_journalName.latin1(), errno );
// remove the tmp file...
QFile::remove( strNewFile );
}
/* The journalfile should be removed now... */
removeJournal();
m_changed = false;
return true;
}
bool OContactAccessBackend_XML::load ()
{
m_contactList.clear();
+ m_uidToContact.clear();
/* Load XML-File and journal if it exists */
if ( !load ( m_fileName, false ) )
return false;
/* The returncode of the journalfile is ignored due to the
* fact that it does not exist when this class is instantiated !
* But there may such a file exist, if the application crashed.
* Therefore we try to load it to get the changes before the #
* crash happened...
*/
load (m_journalName, true);
return true;
}
void OContactAccessBackend_XML::clear ()
{
m_contactList.clear();
+ m_uidToContact.clear();
+
m_changed = false;
-
}
bool OContactAccessBackend_XML::wasChangedExternally()
{
QFileInfo fi( m_fileName );
QDateTime lastmod = fi.lastModified ();
return (lastmod != m_readtime);
}
QArray<int> OContactAccessBackend_XML::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();
+ QListIterator<OContact> it( m_contactList );
+ for( ; it.current(); ++it ){
+ uid_list[counter++] = (*it)->uid();
}
return ( uid_list );
}
OContact OContactAccessBackend_XML::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;
- }
- }
+ OContact* found = m_uidToContact.find( QString().setNum( uid ) );
+
if ( found ){
- foundContact = *it;
+ foundContact = *found;
}
return ( foundContact );
}
-QArray<int> OContactAccessBackend_XML::queryByExample ( const OContact &query, int settings )
+QArray<int> OContactAccessBackend_XML::queryByExample ( const OContact &query, int settings,
+ const QDateTime& d )
{
QArray<int> m_currentQuery( m_contactList.count() );
- QValueListConstIterator<OContact> it;
+ QListIterator<OContact> it( m_contactList );
uint arraycounter = 0;
- for( it = m_contactList.begin(); it != m_contactList.end(); ++it ){
+ for( ; it.current(); ++it ){
/* Search all fields and compare them with query object. Store them into list
* if all fields matches.
*/
QDate* queryDate = 0l;
QDate* checkDate = 0l;
bool allcorrect = true;
for ( int i = 0; i < Qtopia::Groups; i++ ) {
// Birthday and anniversary are special nonstring fields and should
// be handled specially
switch ( i ){
case Qtopia::Birthday:
queryDate = new QDate( query.birthday() );
- checkDate = new QDate( (*it).birthday() );
+ checkDate = new QDate( (*it)->birthday() );
case Qtopia::Anniversary:
if ( queryDate == 0l ){
queryDate = new QDate( query.anniversary() );
- checkDate = new QDate( (*it).anniversary() );
+ checkDate = new QDate( (*it)->anniversary() );
}
if ( queryDate->isValid() ){
if( checkDate->isValid() ){
if ( settings & OContactAccess::DateYear ){
if ( queryDate->year() != checkDate->year() )
allcorrect = false;
}
if ( settings & OContactAccess::DateMonth ){
if ( queryDate->month() != checkDate->month() )
allcorrect = false;
}
if ( settings & OContactAccess::DateDay ){
if ( queryDate->day() != checkDate->day() )
allcorrect = false;
}
if ( settings & OContactAccess::DateDiff ) {
- QDate current = QDate::currentDate();
+ QDate current;
+ // If we get an additional date, we
+ // will take this date instead of
+ // the current one..
+ if ( !d.date().isValid() )
+ current = QDate::currentDate();
+ else
+ current = d.date();
+
// We have to equalize the year, otherwise
// the search will fail..
checkDate->setYMD( current.year(),
checkDate->month(),
checkDate->day() );
if ( *checkDate < current )
checkDate->setYMD( current.year()+1,
checkDate->month(),
checkDate->day() );
+
+ // Check whether the birthday/anniversary date is between
+ // the current/given date and the maximum date
+ // ( maximum time range ) !
qWarning("Checking if %s is between %s and %s ! ",
checkDate->toString().latin1(),
current.toString().latin1(),
queryDate->toString().latin1() );
if ( current.daysTo( *queryDate ) > 0 ){
if ( !( ( *checkDate >= current ) &&
( *checkDate <= *queryDate ) ) ){
allcorrect = false;
qWarning (" Nope!..");
}
}
}
} else{
- // checkDate is invalid. Therfore this entry is always rejected
+ // checkDate is invalid. Therefore this entry is always rejected
allcorrect = false;
}
}
delete queryDate;
queryDate = 0l;
delete checkDate;
checkDate = 0l;
break;
default:
/* Just compare fields which are not empty in the query object */
if ( !query.field(i).isEmpty() ){
switch ( settings & ~( OContactAccess::IgnoreCase
| OContactAccess::DateDiff
| OContactAccess::DateYear
| OContactAccess::DateMonth
| OContactAccess::DateDay
| OContactAccess::MatchOne
) ){
case OContactAccess::RegExp:{
QRegExp expr ( query.field(i),
!(settings & OContactAccess::IgnoreCase),
false );
- if ( expr.find ( (*it).field(i), 0 ) == -1 )
+ 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 )
+ 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() )
+ (*it)->field(i).upper() )
allcorrect = false;
}else{
- if ( query.field(i) != (*it).field(i) )
+ if ( query.field(i) != (*it)->field(i) )
allcorrect = false;
}
}
break;
}
}
}
}
if ( allcorrect ){
- m_currentQuery[arraycounter++] = (*it).uid();
+ m_currentQuery[arraycounter++] = (*it)->uid();
}
}
// Shrink to fit..
m_currentQuery.resize(arraycounter);
return m_currentQuery;
}
QArray<int> OContactAccessBackend_XML::matchRegexp( const QRegExp &r ) const
{
QArray<int> m_currentQuery( m_contactList.count() );
- QValueListConstIterator<OContact> it;
+ QListIterator<OContact> it( m_contactList );
uint arraycounter = 0;
- for( it = m_contactList.begin(); it != m_contactList.end(); ++it ){
- if ( (*it).match( r ) ){
- m_currentQuery[arraycounter++] = (*it).uid();
+ for( ; it.current(); ++it ){
+ if ( (*it)->match( r ) ){
+ m_currentQuery[arraycounter++] = (*it)->uid();
}
}
// Shrink to fit..
m_currentQuery.resize(arraycounter);
return m_currentQuery;
}
const uint OContactAccessBackend_XML::querySettings()
{
return ( OContactAccess::WildCards
| OContactAccess::IgnoreCase
| OContactAccess::RegExp
| OContactAccess::ExactMatch
| OContactAccess::DateDiff
| OContactAccess::DateYear
| OContactAccess::DateMonth
| OContactAccess::DateDay
);
}
bool OContactAccessBackend_XML::hasQuerySettings (uint querySettings) const
{
/* OContactAccess::IgnoreCase, DateDiff, DateYear, DateMonth, DateDay
* may be added with any of the other settings. IgnoreCase should never used alone.
* Wildcards, RegExp, ExactMatch should never used at the same time...
*/
if ( querySettings == OContactAccess::IgnoreCase )
return false;
switch ( querySettings & ~( OContactAccess::IgnoreCase
| OContactAccess::DateDiff
| OContactAccess::DateYear
| OContactAccess::DateMonth
| OContactAccess::DateDay
)
){
case OContactAccess::RegExp:
return ( true );
case OContactAccess::WildCards:
return ( true );
case OContactAccess::ExactMatch:
return ( true );
default:
return ( false );
}
}
// Currently only asc implemented..
QArray<int> OContactAccessBackend_XML::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
// 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).fileAs() + QString::number( (*it).uid() ) );
- nameToUid.insert( (*it).fileAs() + QString::number( (*it).uid() ), (*it).uid() );
+ QListIterator<OContact> it( m_contactList );
+ for( ; it.current(); ++it ){
+ names.append( (*it)->fileAs() + QString::number( (*it)->uid() ) );
+ nameToUid.insert( (*it)->fileAs() + QString::number( (*it)->uid() ), (*it)->uid() );
}
names.sort();
int i = 0;
if ( asc ){
for ( QStringList::Iterator it = names.begin(); it != names.end(); ++it )
m_currentQuery[i++] = nameToUid[ (*it) ];
}else{
for ( QStringList::Iterator it = names.end(); it != names.begin(); --it )
m_currentQuery[i++] = nameToUid[ (*it) ];
}
return m_currentQuery;
}
bool OContactAccessBackend_XML::add ( const OContact &newcontact )
{
//qWarning("odefaultbackend: ACTION::ADD");
updateJournal (newcontact, ACTION_ADD);
addContact_p( newcontact );
m_changed = true;
return true;
}
bool OContactAccessBackend_XML::replace ( const OContact &contact )
{
m_changed = true;
- 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, ACTION_REPLACE);
- m_contactList.remove (it);
- m_contactList.append (contact);
+ OContact* found = m_uidToContact.find ( QString().setNum( contact.uid() ) );
+
+ if ( found ) {
+ OContact* newCont = new OContact( contact );
+
+ updateJournal ( *newCont, ACTION_REPLACE);
+ m_contactList.removeRef ( found );
+ m_contactList.append ( newCont );
+ m_uidToContact.remove( QString().setNum( contact.uid() ) );
+ m_uidToContact.insert( QString().setNum( newCont->uid() ), newCont );
+
+ qWarning("Nur zur Sicherheit: %d == %d ?",contact.uid(), newCont->uid());
+
return true;
} else
return false;
}
bool OContactAccessBackend_XML::remove ( int uid )
{
m_changed = true;
- 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, ACTION_REMOVE);
- m_contactList.remove (it);
+ OContact* found = m_uidToContact.find ( QString().setNum( uid ) );
+
+ if ( found ) {
+ updateJournal ( *found, ACTION_REMOVE);
+ m_contactList.removeRef ( found );
+ m_uidToContact.remove( QString().setNum( uid ) );
+
return true;
} else
return false;
}
bool OContactAccessBackend_XML::reload(){
/* Reload is the same as load in this implementation */
return ( load() );
}
void OContactAccessBackend_XML::addContact_p( const OContact &newcontact )
{
- m_contactList.append (newcontact);
+ OContact* contRef = new OContact( newcontact );
+
+ m_contactList.append ( contRef );
+ m_uidToContact.insert( QString().setNum( newcontact.uid() ), contRef );
}
/* This function loads the xml-database and the journalfile */
bool OContactAccessBackend_XML::load( const QString filename, bool isJournal )
{
/* We use the time of the last read to check if the file was
* changed externally.
*/
if ( !isJournal ){
QFileInfo fi( filename );
m_readtime = fi.lastModified ();
}
const int JOURNALACTION = Qtopia::Notes + 1;
const int JOURNALROW = JOURNALACTION + 1;
bool foundAction = false;
journal_action action = ACTION_ADD;
int journalKey = 0;
QMap<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 = journal_action(it.data().toInt());
foundAction = true;
qWarning ("ODefBack(journal)::ACTION found: %d", action);
break;
case JOURNALROW:
journalKey = it.data().toInt();
break;
default: // no conversion needed add them to the map
contactMap.insert( *find, it.data() );
break;
}
}
/* now generate the Contact contact */
OContact contact( contactMap );
for (customIt = customMap.begin(); customIt != customMap.end(); ++customIt ) {
contact.setCustomField( customIt.key(), customIt.data() );
}
if (foundAction){
foundAction = false;
switch ( action ) {
case ACTION_ADD:
addContact_p (contact);
break;
case ACTION_REMOVE:
if ( !remove (contact.uid()) )
qWarning ("ODefBack(journal)::Unable to remove uid: %d",
contact.uid() );
break;
case ACTION_REPLACE:
if ( !replace ( contact ) )
qWarning ("ODefBack(journal)::Unable to replace uid: %d",
contact.uid() );
break;
default:
qWarning ("Unknown action: ignored !");
break;
}
}else{
/* Add contact to list */
addContact_p (contact);
}
/* Move to next element */
element = element->nextChild();
}
}else {
qWarning("ODefBack::could not load");
}
delete root;
qWarning("returning from loading" );
return true;
}
void OContactAccessBackend_XML::updateJournal( const OContact& cnt,
journal_action action )
{
QFile f( m_journalName );
bool created = !f.exists();
if ( !f.open(IO_WriteOnly|IO_Append) )
return;
QString buf;
QCString str;
// if the file was created, we have to set the Tag "<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 OContactAccessBackend_XML::removeJournal()
{
QFile f ( m_journalName );
if ( f.exists() )
f.remove();
}
diff --git a/libopie2/opiepim/backend/ocontactaccessbackend_xml.h b/libopie2/opiepim/backend/ocontactaccessbackend_xml.h
index 6477705..4d6a7ef 100644
--- a/libopie2/opiepim/backend/ocontactaccessbackend_xml.h
+++ b/libopie2/opiepim/backend/ocontactaccessbackend_xml.h
@@ -1,743 +1,144 @@
/*
* 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.13 2003/03/21 10:33:09 eilers
+ * Merged speed optimized xml backend for contacts to main.
+ * Added QDateTime to querybyexample. For instance, it is now possible to get
+ * all Birthdays/Anniversaries between two dates. This should be used
+ * to show all birthdays in the datebook..
+ * This change is sourcecode backward compatible but you have to upgrade
+ * the binaries for today-addressbook.
+ *
+ * Revision 1.12.2.2 2003/02/11 12:17:28 eilers
+ * Speed optimization. Removed the sequential search loops.
+ *
+ * Revision 1.12.2.1 2003/02/09 15:05:01 eilers
+ * Nothing happened.. Just some cleanup before I will start..
+ *
* Revision 1.12 2003/01/03 16:58:03 eilers
* Reenable debug output
*
* Revision 1.11 2003/01/03 12:31:28 eilers
* Bugfix for calculating data diffs..
*
* Revision 1.10 2003/01/02 14:27:12 eilers
* Improved query by example: Search by date is possible.. First step
* for a today plugin for birthdays..
*
* Revision 1.9 2002/12/08 12:48:57 eilers
* Moved journal-enum from ocontact into i the xml-backend..
*
* Revision 1.8 2002/11/14 17:04:24 eilers
* Sorting will now work if fullname is identical on some entries
*
* Revision 1.7 2002/11/13 15:02:46 eilers
* Small Bug in sorted fixed
*
* Revision 1.6 2002/11/13 14:14:51 eilers
* Added sorted for Contacts..
*
* Revision 1.5 2002/11/01 15:10:42 eilers
* Added regExp-search in database for all fields in a contact.
*
* Revision 1.4 2002/10/16 10:52:40 eilers
* Added some docu to the interface and now using the cache infrastucture by zecke.. :)
*
* Revision 1.3 2002/10/14 16:21:54 eilers
* Some minor interface updates
*
* Revision 1.2 2002/10/07 17:34:24 eilers
* added OBackendFactory for advanced backend access
*
* Revision 1.1 2002/09/27 17:11:44 eilers
* Added API for accessing the Contact-Database ! It is compiling, but
* please do not expect that anything is working !
* I will debug that stuff in the next time ..
* Please read README_COMPILE for compiling !
*
*
*/
#ifndef _OContactAccessBackend_XML_
#define _OContactAccessBackend_XML_
-#include <qasciidict.h>
-#include <qdatetime.h>
-#include <qfile.h>
-#include <qfileinfo.h>
-#include <qregexp.h>
-#include <qarray.h>
-#include <qmap.h>
-#include <qdatetime.h>
-
-#include <qpe/global.h>
-
-#include <opie/xmltree.h>
#include "ocontactaccessbackend.h"
#include "ocontactaccess.h"
-#include <stdlib.h>
-#include <errno.h>
-
-using namespace Opie;
+#include <qlist.h>
+#include <qdict.h>
/* 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 ();
- }
+ OContactAccessBackend_XML ( QString appname, QString filename = 0l );
- 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 save();
- 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 load ();
- }
+ void clear ();
- bool wasChangedExternally()
- {
- QFileInfo fi( m_fileName );
-
- QDateTime lastmod = fi.lastModified ();
-
- return (lastmod != m_readtime);
- }
+ bool wasChangedExternally();
- 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 );
- }
+ QArray<int> allRecords() const;
- 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 );
- }
+ OContact find ( int uid ) const;
- 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.
- */
- QDate* queryDate = 0l;
- QDate* checkDate = 0l;
- bool allcorrect = true;
- for ( int i = 0; i < Qtopia::Groups; i++ ) {
- // Birthday and anniversary are special nonstring fields and should
- // be handled specially
- switch ( i ){
- case Qtopia::Birthday:
- queryDate = new QDate( query.birthday() );
- checkDate = new QDate( (*it).birthday() );
- case Qtopia::Anniversary:
- if ( queryDate == 0l ){
- queryDate = new QDate( query.anniversary() );
- checkDate = new QDate( (*it).anniversary() );
- }
-
- if ( queryDate->isValid() ){
- if( checkDate->isValid() ){
- if ( settings & OContactAccess::DateYear ){
- if ( queryDate->year() != checkDate->year() )
- allcorrect = false;
- }
- if ( settings & OContactAccess::DateMonth ){
- if ( queryDate->month() != checkDate->month() )
- allcorrect = false;
- }
- if ( settings & OContactAccess::DateDay ){
- if ( queryDate->day() != checkDate->day() )
- allcorrect = false;
- }
- if ( settings & OContactAccess::DateDiff ) {
- QDate current = QDate::currentDate();
- // We have to equalize the year, otherwise
- // the search will fail..
- checkDate->setYMD( current.year(),
- checkDate->month(),
- checkDate->day() );
- if ( *checkDate < current )
- checkDate->setYMD( current.year()+1,
- checkDate->month(),
- checkDate->day() );
- qWarning("Checking if %s is between %s and %s ! ",
- checkDate->toString().latin1(),
- current.toString().latin1(),
- queryDate->toString().latin1() );
- if ( current.daysTo( *queryDate ) > 0 ){
- if ( !( ( *checkDate >= current ) &&
- ( *checkDate <= *queryDate ) ) ){
- allcorrect = false;
- qWarning (" Nope!..");
- }
- }
- }
- } else{
- // checkDate is invalid. Therfore this entry is always rejected
- allcorrect = false;
- }
- }
-
- delete queryDate;
- queryDate = 0l;
- delete checkDate;
- checkDate = 0l;
- break;
- default:
- /* Just compare fields which are not empty in the query object */
- if ( !query.field(i).isEmpty() ){
- switch ( settings & ~( OContactAccess::IgnoreCase
- | OContactAccess::DateDiff
- | OContactAccess::DateYear
- | OContactAccess::DateMonth
- | OContactAccess::DateDay
- | OContactAccess::MatchOne
- ) ){
+ QArray<int> queryByExample ( const OContact &query, int settings, const QDateTime& d = QDateTime() );
- 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);
+ QArray<int> matchRegexp( const QRegExp &r ) const;
- 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
- | OContactAccess::DateDiff
- | OContactAccess::DateYear
- | OContactAccess::DateMonth
- | OContactAccess::DateDay
- );
- }
+ const uint querySettings();
- bool hasQuerySettings (uint querySettings) const
- {
- /* OContactAccess::IgnoreCase, DateDiff, DateYear, DateMonth, DateDay
- * may be added with any of the other settings. IgnoreCase should never used alone.
- * Wildcards, RegExp, ExactMatch should never used at the same time...
- */
-
- if ( querySettings == OContactAccess::IgnoreCase )
- return false;
-
- switch ( querySettings & ~( OContactAccess::IgnoreCase
- | OContactAccess::DateDiff
- | OContactAccess::DateYear
- | OContactAccess::DateMonth
- | OContactAccess::DateDay
- )
- ){
- case OContactAccess::RegExp:
- return ( true );
- case OContactAccess::WildCards:
- return ( true );
- case OContactAccess::ExactMatch:
- return ( true );
- default:
- return ( false );
- }
- }
+ bool hasQuerySettings (uint querySettings) const;
// 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
- // 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).fileAs() + QString::number( (*it).uid() ) );
- nameToUid.insert( (*it).fileAs() + QString::number( (*it).uid() ), (*it).uid() );
- }
- names.sort();
-
- int i = 0;
- if ( asc ){
- for ( QStringList::Iterator it = names.begin(); it != names.end(); ++it )
- m_currentQuery[i++] = nameToUid[ (*it) ];
- }else{
- for ( QStringList::Iterator it = names.end(); it != names.begin(); --it )
- m_currentQuery[i++] = nameToUid[ (*it) ];
- }
-
- return m_currentQuery;
-
- }
- bool add ( const OContact &newcontact )
- {
- //qWarning("odefaultbackend: ACTION::ADD");
- updateJournal (newcontact, 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, ACTION_REPLACE);
- m_contactList.remove (it);
- m_contactList.append (contact);
- return true;
- } else
- return false;
- }
+ QArray<int> sorted( bool asc, int , int , int );
+ bool add ( const OContact &newcontact );
- 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, ACTION_REMOVE);
- m_contactList.remove (it);
- return true;
- } else
- return false;
- }
+ bool replace ( const OContact &contact );
- bool reload(){
- /* Reload is the same as load in this implementation */
- return ( load() );
- }
+ bool remove ( int uid );
+ bool reload();
private:
enum journal_action { ACTION_ADD, ACTION_REMOVE, ACTION_REPLACE };
- void addContact_p( const OContact &newcontact ){
- m_contactList.append (newcontact);
- }
+ void addContact_p( const OContact &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;
- journal_action action = 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 = journal_action(it.data().toInt());
- foundAction = true;
- qWarning ("ODefBack(journal)::ACTION found: %d", action);
- break;
- case JOURNALROW:
- journalKey = it.data().toInt();
- break;
- default: // no conversion needed add them to the map
- contactMap.insert( *find, it.data() );
- break;
- }
- }
- /* now generate the Contact contact */
- OContact contact( contactMap );
-
- for (customIt = customMap.begin(); customIt != customMap.end(); ++customIt ) {
- contact.setCustomField( customIt.key(), customIt.data() );
- }
-
- if (foundAction){
- foundAction = false;
- switch ( action ) {
- case ACTION_ADD:
- addContact_p (contact);
- break;
- case ACTION_REMOVE:
- if ( !remove (contact.uid()) )
- qWarning ("ODefBack(journal)::Unable to remove uid: %d",
- contact.uid() );
- break;
- case ACTION_REPLACE:
- if ( !replace ( contact ) )
- qWarning ("ODefBack(journal)::Unable to replace uid: %d",
- contact.uid() );
- break;
- default:
- qWarning ("Unknown action: ignored !");
- break;
- }
- }else{
- /* Add contact to list */
- addContact_p (contact);
- }
-
- /* Move to next element */
- element = element->nextChild();
- }
- }else {
- qWarning("ODefBack::could not load");
- }
- delete root;
- qWarning("returning from loading" );
- return true;
- }
-
+ bool load( const QString filename, bool isJournal );
- void updateJournal( const OContact& cnt,
- journal_action action ) {
- QFile f( m_journalName );
- bool created = !f.exists();
- if ( !f.open(IO_WriteOnly|IO_Append) )
- return;
-
- QString buf;
- QCString str;
-
- // if the file was created, we have to set the Tag "<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();
- }
+ void updateJournal( const OContact& cnt, journal_action action );
+ void removeJournal();
protected:
bool m_changed;
QString m_journalName;
QString m_fileName;
QString m_appName;
- QValueList<OContact> m_contactList;
+ QList<OContact> m_contactList;
QDateTime m_readtime;
+
+ QDict<OContact> m_uidToContact;
};
#endif
diff --git a/libopie2/opiepim/backend/odatebookaccessbackend_xml.cpp b/libopie2/opiepim/backend/odatebookaccessbackend_xml.cpp
index 4a6b7b8..11e19d9 100644
--- a/libopie2/opiepim/backend/odatebookaccessbackend_xml.cpp
+++ b/libopie2/opiepim/backend/odatebookaccessbackend_xml.cpp
@@ -1,587 +1,587 @@
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#include <qasciidict.h>
#include <qfile.h>
#include <qtopia/global.h>
#include <qtopia/stringutil.h>
#include <qtopia/timeconversion.h>
#include "opimnotifymanager.h"
#include "orecur.h"
#include "otimezone.h"
#include "odatebookaccessbackend_xml.h"
namespace {
// FROM TT again
char *strstrlen(const char *haystack, int hLen, const char* needle, int nLen)
{
char needleChar;
char haystackChar;
if (!needle || !haystack || !hLen || !nLen)
return 0;
const char* hsearch = haystack;
if ((needleChar = *needle++) != 0) {
nLen--; //(to make up for needle++)
do {
do {
if ((haystackChar = *hsearch++) == 0)
return (0);
if (hsearch >= haystack + hLen)
return (0);
} while (haystackChar != needleChar);
} while (strncmp(hsearch, needle, QMIN(hLen - (hsearch - haystack), nLen)) != 0);
hsearch--;
}
return ((char *)hsearch);
}
}
namespace {
time_t start, end, created, rp_end;
ORecur* rec;
ORecur* recur() {
if (!rec)
rec = new ORecur;
return rec;
}
int alarmTime;
int snd;
enum Attribute{
FDescription = 0,
FLocation,
FCategories,
FUid,
FType,
FAlarm,
FSound,
FRType,
FRWeekdays,
FRPosition,
FRFreq,
FRHasEndDate,
FREndDate,
FRStart,
FREnd,
FNote,
FCreated,
FTimeZone,
FRecParent,
FRecChildren,
FExceptions
};
inline void save( const OEvent& ev, QString& buf ) {
qWarning("Saving %d %s", ev.uid(), ev.description().latin1() );
buf += " description=\"" + Qtopia::escapeString(ev.description() ) + "\"";
if (!ev.location().isEmpty() )
buf += " location=\"" + Qtopia::escapeString(ev.location() ) + "\"";
buf += " categories=\""+ Qtopia::escapeString( Qtopia::Record::idsToString( ev.categories() ) ) + "\"";
buf += " uid=\"" + QString::number( ev.uid() ) + "\"";
if (ev.isAllDay() )
buf += " type=\"AllDay\"";
if (ev.hasNotifiers() ) {
OPimAlarm alarm = ev.notifiers().alarms()[0]; // take only the first
int minutes = alarm.dateTime().secsTo( ev.startDateTime() ) / 60;
buf += " alarm=\"" + QString::number(minutes) + "\" sound=\"";
if ( alarm.sound() == OPimAlarm::Loud )
buf += "loud";
else
buf += "silent";
buf += "\"";
}
if ( ev.hasRecurrence() ) {
buf += ev.recurrence().toString();
}
/*
* fscking timezones :) well, we'll first convert
* the QDateTime to a QDateTime in UTC time
* and then we'll create a nice time_t
*/
OTimeZone zone( ev.timeZone().isEmpty() ? OTimeZone::current() : ev.timeZone() );
buf += " start=\"" + QString::number( zone.fromUTCDateTime( zone.toDateTime( ev.startDateTime(), OTimeZone::utc() ) ) ) + "\"";
buf += " end=\"" + QString::number( zone.fromUTCDateTime( zone.toDateTime( ev.endDateTime() , OTimeZone::utc() ) ) ) + "\"";
if (!ev.note().isEmpty() ) {
buf += " note=\"" + Qtopia::escapeString( ev.note() ) + "\"";
}
buf += " timezone=\"";
if ( ev.timeZone().isEmpty() )
buf += "None";
else
buf += ev.timeZone();
buf += "\"";
if (ev.parent() != 0 ) {
buf += " recparent=\""+QString::number(ev.parent() )+"\"";
}
if (ev.children().count() != 0 ) {
QArray<int> children = ev.children();
buf += " recchildren=\"";
for ( uint i = 0; i < children.count(); i++ ) {
if ( i != 0 ) buf += " ";
buf += QString::number( children[i] );
}
buf+= "\"";
}
// skip custom writing
}
inline bool forAll( const QMap<int, OEvent>& list, QFile& file ) {
QMap<int, OEvent>::ConstIterator it;
QString buf;
QCString str;
int total_written;
for ( it = list.begin(); it != list.end(); ++it ) {
buf = "<event";
save( it.data(), buf );
buf += " />\n";
str = buf.utf8();
total_written = file.writeBlock(str.data(), str.length() );
if ( total_written != int(str.length() ) )
return false;
}
return true;
}
}
ODateBookAccessBackend_XML::ODateBookAccessBackend_XML( const QString& ,
const QString& fileName )
: ODateBookAccessBackend() {
m_name = fileName.isEmpty() ? Global::applicationFileName( "datebook", "datebook.xml" ) : fileName;
m_changed = false;
}
ODateBookAccessBackend_XML::~ODateBookAccessBackend_XML() {
}
bool ODateBookAccessBackend_XML::load() {
return loadFile();
}
bool ODateBookAccessBackend_XML::reload() {
clear();
return load();
}
bool ODateBookAccessBackend_XML::save() {
if (!m_changed) return true;
int total_written;
QString strFileNew = m_name + ".new";
QFile f( strFileNew );
if (!f.open( IO_WriteOnly | IO_Raw ) ) return false;
QString buf( "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" );
buf += "<!DOCTYPE DATEBOOK><DATEBOOK>\n";
buf += "<events>\n";
QCString str = buf.utf8();
total_written = f.writeBlock( str.data(), str.length() );
if ( total_written != int(str.length() ) ) {
f.close();
QFile::remove( strFileNew );
return false;
}
if (!forAll( m_raw, f ) ) {
f.close();
QFile::remove( strFileNew );
return false;
}
if (!forAll( m_rep, f ) ) {
f.close();
QFile::remove( strFileNew );
return false;
}
buf = "</events>\n</DATEBOOK>\n";
str = buf.utf8();
total_written = f.writeBlock( str.data(), str.length() );
if ( total_written != int(str.length() ) ) {
f.close();
QFile::remove( strFileNew );
return false;
}
f.close();
if ( ::rename( strFileNew, m_name ) < 0 ) {
QFile::remove( strFileNew );
return false;
}
m_changed = false;
return true;
}
QArray<int> ODateBookAccessBackend_XML::allRecords()const {
QArray<int> ints( m_raw.count()+ m_rep.count() );
uint i = 0;
QMap<int, OEvent>::ConstIterator it;
for ( it = m_raw.begin(); it != m_raw.end(); ++it ) {
ints[i] = it.key();
i++;
}
for ( it = m_rep.begin(); it != m_rep.end(); ++it ) {
ints[i] = it.key();
i++;
}
return ints;
}
-QArray<int> ODateBookAccessBackend_XML::queryByExample(const OEvent&, int ) {
+QArray<int> ODateBookAccessBackend_XML::queryByExample(const OEvent&, int, const QDateTime& ) {
return QArray<int>();
}
void ODateBookAccessBackend_XML::clear() {
m_raw.clear();
m_rep.clear();
}
OEvent ODateBookAccessBackend_XML::find( int uid ) const{
if ( m_raw.contains( uid ) )
return m_raw[uid];
else
return m_rep[uid];
}
bool ODateBookAccessBackend_XML::add( const OEvent& ev ) {
m_changed = true;
if (ev.hasRecurrence() )
m_rep.insert( ev.uid(), ev );
else
m_raw.insert( ev.uid(), ev );
return true;
}
bool ODateBookAccessBackend_XML::remove( int uid ) {
m_changed = true;
m_rep.remove( uid );
m_rep.remove( uid );
return true;
}
bool ODateBookAccessBackend_XML::replace( const OEvent& ev ) {
replace( ev.uid() );
return add( ev );
}
QArray<int> ODateBookAccessBackend_XML::rawEvents()const {
return allRecords();
}
QArray<int> ODateBookAccessBackend_XML::rawRepeats()const {
QArray<int> ints( m_rep.count() );
uint i = 0;
QMap<int, OEvent>::ConstIterator it;
for ( it = m_rep.begin(); it != m_rep.end(); ++it ) {
ints[i] = it.key();
i++;
}
return ints;
}
QArray<int> ODateBookAccessBackend_XML::nonRepeats()const {
QArray<int> ints( m_raw.count() );
uint i = 0;
QMap<int, OEvent>::ConstIterator it;
for ( it = m_raw.begin(); it != m_raw.end(); ++it ) {
ints[i] = it.key();
i++;
}
return ints;
}
OEvent::ValueList ODateBookAccessBackend_XML::directNonRepeats() {
OEvent::ValueList list;
QMap<int, OEvent>::ConstIterator it;
for (it = m_raw.begin(); it != m_raw.end(); ++it )
list.append( it.data() );
return list;
}
OEvent::ValueList ODateBookAccessBackend_XML::directRawRepeats() {
OEvent::ValueList list;
QMap<int, OEvent>::ConstIterator it;
for (it = m_rep.begin(); it != m_rep.end(); ++it )
list.append( it.data() );
return list;
}
bool ODateBookAccessBackend_XML::loadFile() {
m_changed = false;
int fd = ::open( QFile::encodeName(m_name).data(), O_RDONLY );
if ( fd < 0 ) return false;
struct stat attribute;
if ( ::fstat(fd, &attribute ) == -1 ) {
::close( fd );
return false;
}
void* map_addr = ::mmap(NULL, attribute.st_size, PROT_READ, MAP_SHARED, fd, 0 );
if ( map_addr == ( (caddr_t)-1) ) {
::close( fd );
return false;
}
::madvise( map_addr, attribute.st_size, MADV_SEQUENTIAL );
::close( fd );
QAsciiDict<int> dict(FExceptions+1);
dict.setAutoDelete( true );
dict.insert( "description", new int(FDescription) );
dict.insert( "location", new int(FLocation) );
dict.insert( "categories", new int(FCategories) );
dict.insert( "uid", new int(FUid) );
dict.insert( "type", new int(FType) );
dict.insert( "alarm", new int(FAlarm) );
dict.insert( "sound", new int(FSound) );
dict.insert( "rtype", new int(FRType) );
dict.insert( "rweekdays", new int(FRWeekdays) );
dict.insert( "rposition", new int(FRPosition) );
dict.insert( "rfreq", new int(FRFreq) );
dict.insert( "rhasenddate", new int(FRHasEndDate) );
dict.insert( "enddt", new int(FREndDate) );
dict.insert( "start", new int(FRStart) );
dict.insert( "end", new int(FREnd) );
dict.insert( "note", new int(FNote) );
dict.insert( "created", new int(FCreated) );
dict.insert( "recparent", new int(FRecParent) );
dict.insert( "recchildren", new int(FRecChildren) );
dict.insert( "exceptions", new int(FExceptions) );
dict.insert( "timezone", new int(FTimeZone) );
char* dt = (char*)map_addr;
int len = attribute.st_size;
int i = 0;
char* point;
const char* collectionString = "<event ";
int strLen = ::strlen(collectionString);
int *find;
while ( ( point = ::strstrlen( dt+i, len -i, collectionString, strLen ) ) != 0 ) {
i = point -dt;
i+= strLen;
alarmTime = -1;
snd = 0; // silent
OEvent ev;
rec = 0;
while ( TRUE ) {
while ( i < len && (dt[i] == ' ' || dt[i] == '\n' || dt[i] == '\r') )
++i;
if ( i >= len-2 || (dt[i] == '/' && dt[i+1] == '>') )
break;
// we have another attribute, read it.
int j = i;
while ( j < len && dt[j] != '=' )
++j;
QCString attr( dt+i, j-i+1);
i = ++j; // skip =
// find the start of quotes
while ( i < len && dt[i] != '"' )
++i;
j = ++i;
bool haveUtf = FALSE;
bool haveEnt = FALSE;
while ( j < len && dt[j] != '"' ) {
if ( ((unsigned char)dt[j]) > 0x7f )
haveUtf = TRUE;
if ( dt[j] == '&' )
haveEnt = TRUE;
++j;
}
if ( i == j ) {
// empty value
i = j + 1;
continue;
}
QCString value( dt+i, j-i+1 );
i = j + 1;
QString str = (haveUtf ? QString::fromUtf8( value )
: QString::fromLatin1( value ) );
if ( haveEnt )
str = Qtopia::plainString( str );
/*
* add key + value
*/
find = dict[attr.data()];
if (!find)
ev.setCustomField( attr, value );
else {
setField( ev, *find, value );
}
}
/* time to finalize */
finalizeRecord( ev );
delete rec;
}
::munmap(map_addr, attribute.st_size );
m_changed = false; // changed during add
return true;
}
void ODateBookAccessBackend_XML::finalizeRecord( OEvent& ev ) {
/* AllDay is alway in UTC */
if ( ev.isAllDay() ) {
OTimeZone utc = OTimeZone::utc();
ev.setStartDateTime( utc.fromUTCDateTime( start ) );
ev.setEndDateTime ( utc.fromUTCDateTime( end ) );
ev.setTimeZone( "UTC"); // make sure it is really utc
}else {
/* to current date time */
qWarning(" Start is %d", start );
OTimeZone zone( ev.timeZone().isEmpty() ? OTimeZone::current() : ev.timeZone() );
QDateTime date = zone.toDateTime( start );
qWarning(" Start is %s", date.toString().latin1() );
ev.setStartDateTime( zone.toDateTime( date, OTimeZone::current() ) );
date = zone.toDateTime( end );
ev.setEndDateTime ( zone.toDateTime( date, OTimeZone::current() ) );
}
if ( rec && rec->doesRecur() ) {
OTimeZone utc = OTimeZone::utc();
ORecur recu( *rec ); // call copy c'tor;
recu.setEndDate ( utc.fromUTCDateTime( rp_end ).date() );
recu.setCreatedDateTime( utc.fromUTCDateTime( created ) );
recu.setStart( ev.startDateTime().date() );
ev.setRecurrence( recu );
}
if (alarmTime != -1 ) {
QDateTime dt = ev.startDateTime().addSecs( -1*alarmTime*60 );
OPimAlarm al( snd , dt );
ev.notifiers().add( al );
}
if ( m_raw.contains( ev.uid() ) || m_rep.contains( ev.uid() ) ) {
qWarning("already contains assign uid");
ev.setUid( 1 );
}
qWarning("addind %d %s", ev.uid(), ev.description().latin1() );
if ( ev.hasRecurrence() )
m_rep.insert( ev.uid(), ev );
else
m_raw.insert( ev.uid(), ev );
}
void ODateBookAccessBackend_XML::setField( OEvent& e, int id, const QString& value) {
// qWarning(" setting %s", value.latin1() );
switch( id ) {
case FDescription:
e.setDescription( value );
break;
case FLocation:
e.setLocation( value );
break;
case FCategories:
e.setCategories( e.idsFromString( value ) );
break;
case FUid:
e.setUid( value.toInt() );
break;
case FType:
if ( value == "AllDay" ) {
e.setAllDay( true );
e.setTimeZone( "UTC" );
}
break;
case FAlarm:
alarmTime = value.toInt();
break;
case FSound:
snd = value == "loud" ? OPimAlarm::Loud : OPimAlarm::Silent;
break;
// recurrence stuff
case FRType:
if ( value == "Daily" )
recur()->setType( ORecur::Daily );
else if ( value == "Weekly" )
recur()->setType( ORecur::Weekly);
else if ( value == "MonthlyDay" )
recur()->setType( ORecur::MonthlyDay );
else if ( value == "MonthlyDate" )
recur()->setType( ORecur::MonthlyDate );
else if ( value == "Yearly" )
recur()->setType( ORecur::Yearly );
else
recur()->setType( ORecur::NoRepeat );
break;
case FRWeekdays:
recur()->setDays( value.toInt() );
break;
case FRPosition:
recur()->setPosition( value.toInt() );
break;
case FRFreq:
recur()->setFrequency( value.toInt() );
break;
case FRHasEndDate:
recur()->setHasEndDate( value.toInt() );
break;
case FREndDate: {
rp_end = (time_t) value.toLong();
break;
}
case FRStart: {
start = (time_t) value.toLong();
break;
}
case FREnd: {
end = ( (time_t) value.toLong() );
break;
}
case FNote:
e.setNote( value );
break;
case FCreated:
created = value.toInt();
break;
case FRecParent:
e.setParent( value.toInt() );
break;
case FRecChildren:{
QStringList list = QStringList::split(' ', value );
for ( QStringList::Iterator it = list.begin(); it != list.end(); ++it ) {
e.addChild( (*it).toInt() );
}
}
break;
case FExceptions:{
QStringList list = QStringList::split(' ', value );
for (QStringList::Iterator it = list.begin(); it != list.end(); ++it ) {
QDate date( (*it).left(4).toInt(), (*it).mid(4, 2).toInt(), (*it).right(2).toInt() );
qWarning("adding exception %s", date.toString().latin1() );
recur()->exceptions().append( date );
}
}
break;
case FTimeZone:
if ( value != "None" )
e.setTimeZone( value );
break;
default:
break;
}
}
diff --git a/libopie2/opiepim/backend/odatebookaccessbackend_xml.h b/libopie2/opiepim/backend/odatebookaccessbackend_xml.h
index 40f69d8..563c31f 100644
--- a/libopie2/opiepim/backend/odatebookaccessbackend_xml.h
+++ b/libopie2/opiepim/backend/odatebookaccessbackend_xml.h
@@ -1,48 +1,48 @@
#ifndef OPIE_DATE_BOOK_ACCESS_BACKEND_XML__H
#define OPIE_DATE_BOOK_ACCESS_BACKEND_XML__H
#include <qmap.h>
#include "odatebookaccessbackend.h"
class ODateBookAccessBackend_XML : public ODateBookAccessBackend {
public:
ODateBookAccessBackend_XML( const QString& appName,
const QString& fileName = QString::null);
~ODateBookAccessBackend_XML();
bool load();
bool reload();
bool save();
QArray<int> allRecords()const;
- QArray<int> queryByExample( const OEvent&, int );
+ QArray<int> queryByExample( const OEvent&, int, const QDateTime& d = QDateTime() );
OEvent find( int uid )const;
void clear();
bool add( const OEvent& ev );
bool remove( int uid );
bool replace( const OEvent& ev );
QArray<UID> rawEvents()const;
QArray<UID> rawRepeats()const;
QArray<UID> nonRepeats()const;
OEvent::ValueList directNonRepeats();
OEvent::ValueList directRawRepeats();
private:
bool m_changed :1 ;
bool loadFile();
inline void finalizeRecord( OEvent& ev );
inline void setField( OEvent&, int field, const QString& val );
QString m_name;
QMap<int, OEvent> m_raw;
QMap<int, OEvent> m_rep;
struct Data;
Data* data;
class Private;
Private *d;
};
#endif
diff --git a/libopie2/opiepim/backend/opimaccessbackend.h b/libopie2/opiepim/backend/opimaccessbackend.h
index e268f4f..01a0c86 100644
--- a/libopie2/opiepim/backend/opimaccessbackend.h
+++ b/libopie2/opiepim/backend/opimaccessbackend.h
@@ -1,153 +1,153 @@
#ifndef OPIE_PIM_ACCESS_BACKEND
#define OPIE_PIM_ACCESS_BACKEND
#include <qarray.h>
#include <opie/otemplatebase.h>
#include <opie/opimrecord.h>
/**
* OPimAccessBackend is the base class
* for all private backends
* it operates on OPimRecord as the base class
* and it's responsible for fast manipulating
* the resource the implementation takes care
* of
*/
template <class T = OPimRecord>
class OPimAccessBackend {
public:
typedef OTemplateBase<T> Frontend;
/** The access hint from the frontend */
OPimAccessBackend(int access = 0);
virtual ~OPimAccessBackend();
/**
* load the resource
*/
virtual bool load() = 0;
/**
* reload the resource
*/
virtual bool reload() = 0;
/**
* save the resource and
* all it's changes
*/
virtual bool save() = 0;
/**
* return an array of
* all available uids
*/
virtual QArray<int> allRecords()const = 0;
/**
- * queryByExample for T with the SortOrder
- * sort
+ * queryByExample for T with the given Settings
+ *
*/
- virtual QArray<int> queryByExample( const T& t, int sort ) = 0;
+ virtual QArray<int> queryByExample( const T& t, int settings, const QDateTime& d = QDateTime() ) = 0;
/**
* find the OPimRecord with uid @param uid
* returns T and T.isEmpty() if nothing was found
*/
virtual T find(int uid )const = 0;
virtual T find(int uid, const QArray<int>& items,
uint current, typename Frontend::CacheDirection )const ;
/**
* clear the back end
*/
virtual void clear() = 0;
/**
* add T
*/
virtual bool add( const T& t ) = 0;
/**
* remove
*/
virtual bool remove( int uid ) = 0;
/**
* replace a record with T.uid()
*/
virtual bool replace( const T& t ) = 0;
/*
* setTheFrontEnd!!!
*/
void setFrontend( Frontend* front );
/**
* set the read ahead count
*/
void setReadAhead( uint count );
protected:
int access()const;
void cache( const T& t )const;
/**
* use a prime number here!
*/
void setSaneCacheSize( int );
uint readAhead()const;
private:
class Private;
Private* d;
Frontend* m_front;
uint m_read;
int m_acc;
};
template <class T>
OPimAccessBackend<T>::OPimAccessBackend(int acc)
: m_acc( acc )
{
m_front = 0l;
}
template <class T>
OPimAccessBackend<T>::~OPimAccessBackend() {
}
template <class T>
void OPimAccessBackend<T>::setFrontend( Frontend* fr ) {
m_front = fr;
}
template <class T>
void OPimAccessBackend<T>::cache( const T& t )const {
if (m_front )
m_front->cache( t );
}
template <class T>
void OPimAccessBackend<T>::setSaneCacheSize( int size) {
if (m_front )
m_front->setSaneCacheSize( size );
}
template <class T>
T OPimAccessBackend<T>::find( int uid, const QArray<int>&,
uint, typename Frontend::CacheDirection )const {
return find( uid );
}
template <class T>
void OPimAccessBackend<T>::setReadAhead( uint count ) {
m_read = count;
}
template <class T>
uint OPimAccessBackend<T>::readAhead()const {
return m_read;
}
template <class T>
int OPimAccessBackend<T>::access()const {
return m_acc;
}
#endif
diff --git a/libopie2/opiepim/backend/otodoaccesssql.cpp b/libopie2/opiepim/backend/otodoaccesssql.cpp
index 761d7d8..ec9c14c 100644
--- a/libopie2/opiepim/backend/otodoaccesssql.cpp
+++ b/libopie2/opiepim/backend/otodoaccesssql.cpp
@@ -1,540 +1,540 @@
#include <qdatetime.h>
#include <qpe/global.h>
#include <opie/osqldriver.h>
#include <opie/osqlresult.h>
#include <opie/osqlmanager.h>
#include <opie/osqlquery.h>
#include "otodoaccesssql.h"
/*
* first some query
* CREATE query
* LOAD query
* INSERT
* REMOVE
* CLEAR
*/
namespace {
/**
* CreateQuery for the Todolist Table
*/
class CreateQuery : public OSQLQuery {
public:
CreateQuery();
~CreateQuery();
QString query()const;
};
/**
* LoadQuery
* this one queries for all uids
*/
class LoadQuery : public OSQLQuery {
public:
LoadQuery();
~LoadQuery();
QString query()const;
};
/**
* inserts/adds a OTodo to the table
*/
class InsertQuery : public OSQLQuery {
public:
InsertQuery(const OTodo& );
~InsertQuery();
QString query()const;
private:
OTodo m_todo;
};
/**
* removes one from the table
*/
class RemoveQuery : public OSQLQuery {
public:
RemoveQuery(int uid );
~RemoveQuery();
QString query()const;
private:
int m_uid;
};
/**
* Clears (delete) a Table
*/
class ClearQuery : public OSQLQuery {
public:
ClearQuery();
~ClearQuery();
QString query()const;
};
/**
* a find query
*/
class FindQuery : public OSQLQuery {
public:
FindQuery(int uid);
FindQuery(const QArray<int>& );
~FindQuery();
QString query()const;
private:
QString single()const;
QString multi()const;
QArray<int> m_uids;
int m_uid;
};
/**
* overdue query
*/
class OverDueQuery : public OSQLQuery {
public:
OverDueQuery();
~OverDueQuery();
QString query()const;
};
class EffQuery : public OSQLQuery {
public:
EffQuery( const QDate&, const QDate&, bool inc );
~EffQuery();
QString query()const;
private:
QString with()const;
QString out()const;
QDate m_start;
QDate m_end;
bool m_inc :1;
};
CreateQuery::CreateQuery() : OSQLQuery() {}
CreateQuery::~CreateQuery() {}
QString CreateQuery::query()const {
QString qu;
qu += "create table todolist( uid, categories, completed, progress, ";
qu += "summary, DueDate, priority, description )";
return qu;
}
LoadQuery::LoadQuery() : OSQLQuery() {}
LoadQuery::~LoadQuery() {}
QString LoadQuery::query()const {
QString qu;
qu += "select distinct uid from todolist";
return qu;
}
InsertQuery::InsertQuery( const OTodo& todo )
: OSQLQuery(), m_todo( todo ) {
}
InsertQuery::~InsertQuery() {
}
/*
* converts from a OTodo to a query
* we leave out X-Ref + Alarms
*/
QString InsertQuery::query()const{
int year, month, day;
year = month = day = 0;
if (m_todo.hasDueDate() ) {
QDate date = m_todo.dueDate();
year = date.year();
month = date.month();
day = date.day();
}
QString qu;
qu = "insert into todolist VALUES(" + QString::number( m_todo.uid() ) + ",'" + m_todo.idsToString( m_todo.categories() ) + "',";
qu += QString::number( m_todo.isCompleted() ) + "," + QString::number( m_todo.progress() ) + ",";
qu += "'"+m_todo.summary()+"','"+QString::number(year)+"-"+QString::number(month)+"-"+QString::number(day)+"',";
qu += QString::number(m_todo.priority() ) +",'" + m_todo.description() + "')";
qWarning("add %s", qu.latin1() );
return qu;
}
RemoveQuery::RemoveQuery(int uid )
: OSQLQuery(), m_uid( uid ) {}
RemoveQuery::~RemoveQuery() {}
QString RemoveQuery::query()const {
QString qu = "DELETE from todolist where uid = " + QString::number(m_uid);
return qu;
}
ClearQuery::ClearQuery()
: OSQLQuery() {}
ClearQuery::~ClearQuery() {}
QString ClearQuery::query()const {
QString qu = "drop table todolist";
return qu;
}
FindQuery::FindQuery(int uid)
: OSQLQuery(), m_uid(uid ) {
}
FindQuery::FindQuery(const QArray<int>& ints)
: OSQLQuery(), m_uids(ints){
}
FindQuery::~FindQuery() {
}
QString FindQuery::query()const{
if (m_uids.count() == 0 )
return single();
else
return multi();
}
QString FindQuery::single()const{
QString qu = "select uid, categories, completed, progress, summary, ";
qu += "DueDate, priority, description from todolist where uid = " + QString::number(m_uid);
return qu;
}
QString FindQuery::multi()const {
QString qu = "select uid, categories, completed, progress, summary, ";
qu += "DueDate, priority, description from todolist where ";
for (uint i = 0; i < m_uids.count(); i++ ) {
qu += " UID = " + QString::number( m_uids[i] ) + " OR";
}
qu.remove( qu.length()-2, 2 );
return qu;
}
OverDueQuery::OverDueQuery(): OSQLQuery() {}
OverDueQuery::~OverDueQuery() {}
QString OverDueQuery::query()const {
QDate date = QDate::currentDate();
QString str;
str = QString("select uid from todolist where DueDate ='%1-%2-%3'").arg(date.year() ).arg(date.month() ).arg(date.day() );
return str;
}
EffQuery::EffQuery( const QDate& start, const QDate& end, bool inc )
: OSQLQuery(), m_start( start ), m_end( end ),m_inc(inc) {}
EffQuery::~EffQuery() {}
QString EffQuery::query()const {
return m_inc ? with() : out();
}
QString EffQuery::with()const {
QString str;
str = QString("select uid from todolist where ( DueDate >= '%1-%2-%3' AND DueDate <= '%4-%5-%6' ) OR DueDate = '0-0-0' ")
.arg( m_start.year() ).arg( m_start.month() ).arg( m_start.day() )
.arg( m_end .year() ).arg( m_end .month() ).arg( m_end .day() );
return str;
}
QString EffQuery::out()const {
QString str;
str = QString("select uid from todolist where DueDate >= '%1-%2-%3' AND DueDate <= '%4-%5-%6'")
.arg(m_start.year() ).arg(m_start.month() ).arg( m_start.day() )
.arg(m_end. year() ).arg(m_end. month() ).arg(m_end.day() );
return str;
}
};
OTodoAccessBackendSQL::OTodoAccessBackendSQL( const QString& file )
: OTodoAccessBackend(), m_dict(15), m_dirty(true)
{
QString fi = file;
if ( fi.isEmpty() )
fi = Global::applicationFileName( "todolist", "todolist.db" );
OSQLManager man;
m_driver = man.standard();
m_driver->setUrl(fi);
fillDict();
}
OTodoAccessBackendSQL::~OTodoAccessBackendSQL(){
}
bool OTodoAccessBackendSQL::load(){
if (!m_driver->open() )
return false;
CreateQuery creat;
OSQLResult res = m_driver->query(&creat );
m_dirty = true;
return true;
}
bool OTodoAccessBackendSQL::reload(){
return load();
}
bool OTodoAccessBackendSQL::save(){
return m_driver->close();
}
QArray<int> OTodoAccessBackendSQL::allRecords()const {
if (m_dirty )
update();
return m_uids;
}
-QArray<int> OTodoAccessBackendSQL::queryByExample( const OTodo& , int ){
+QArray<int> OTodoAccessBackendSQL::queryByExample( const OTodo& , int, const QDateTime& ){
QArray<int> ints(0);
return ints;
}
OTodo OTodoAccessBackendSQL::find(int uid ) const{
FindQuery query( uid );
return todo( m_driver->query(&query) );
}
OTodo OTodoAccessBackendSQL::find( int uid, const QArray<int>& ints,
uint cur, Frontend::CacheDirection dir ) const{
int CACHE = readAhead();
qWarning("searching for %d", uid );
QArray<int> search( CACHE );
uint size =0;
OTodo to;
// we try to cache CACHE items
switch( dir ) {
/* forward */
case 0:
for (uint i = cur; i < ints.count() && size < CACHE; i++ ) {
qWarning("size %d %d", size, ints[i] );
search[size] = ints[i];
size++;
}
break;
/* reverse */
case 1:
for (uint i = cur; i != 0 && size < CACHE; i-- ) {
search[size] = ints[i];
size++;
}
break;
}
search.resize( size );
FindQuery query( search );
OSQLResult res = m_driver->query( &query );
if ( res.state() != OSQLResult::Success )
return to;
return todo( res );
}
void OTodoAccessBackendSQL::clear() {
ClearQuery cle;
OSQLResult res = m_driver->query( &cle );
CreateQuery qu;
res = m_driver->query(&qu);
}
bool OTodoAccessBackendSQL::add( const OTodo& t) {
InsertQuery ins( t );
OSQLResult res = m_driver->query( &ins );
if ( res.state() == OSQLResult::Failure )
return false;
int c = m_uids.count();
m_uids.resize( c+1 );
m_uids[c] = t.uid();
return true;
}
bool OTodoAccessBackendSQL::remove( int uid ) {
RemoveQuery rem( uid );
OSQLResult res = m_driver->query(&rem );
if ( res.state() == OSQLResult::Failure )
return false;
m_dirty = true;
return true;
}
/*
* FIXME better set query
* but we need the cache for that
* now we remove
*/
bool OTodoAccessBackendSQL::replace( const OTodo& t) {
remove( t.uid() );
bool b= add(t);
m_dirty = false; // we changed some stuff but the UID stayed the same
return b;
}
QArray<int> OTodoAccessBackendSQL::overDue() {
OverDueQuery qu;
return uids( m_driver->query(&qu ) );
}
QArray<int> OTodoAccessBackendSQL::effectiveToDos( const QDate& s,
const QDate& t,
bool u) {
EffQuery ef(s, t, u );
return uids (m_driver->query(&ef) );
}
/*
*
*/
QArray<int> OTodoAccessBackendSQL::sorted( bool asc, int sortOrder,
int sortFilter, int cat ) {
qWarning("sorted %d, %d", asc, sortOrder );
QString query;
query = "select uid from todolist WHERE ";
/*
* Sort Filter stuff
* not that straight forward
*
*/
/* Category */
if ( sortFilter & 1 ) {
QString str;
if (cat != 0 ) str = QString::number( cat );
query += " categories like '%" +str+"%' AND";
}
/* Show only overdue */
if ( sortFilter & 2 ) {
QDate date = QDate::currentDate();
QString due;
QString base;
base = QString("DueDate <= '%1-%2-%3' AND completed = 0").arg( date.year() ).arg( date.month() ).arg( date.day() );
query += " " + base + " AND";
}
/* not show completed */
if ( sortFilter & 4 ) {
query += " completed = 0 AND";
}else{
query += " ( completed = 1 OR completed = 0) AND";
}
/* srtip the end */
query = query.remove( query.length()-3, 3 );
/*
* sort order stuff
* quite straight forward
*/
query += "ORDER BY ";
switch( sortOrder ) {
/* completed */
case 0:
query += "completed";
break;
case 1:
query += "priority";
break;
case 2:
query += "summary";
break;
case 3:
query += "DueDate";
break;
}
if ( !asc ) {
qWarning("not ascending!");
query += " DESC";
}
qWarning( query );
OSQLRawQuery raw(query );
return uids( m_driver->query(&raw) );
}
bool OTodoAccessBackendSQL::date( QDate& da, const QString& str ) const{
if ( str == "0-0-0" )
return false;
else{
int day, year, month;
QStringList list = QStringList::split("-", str );
year = list[0].toInt();
month = list[1].toInt();
day = list[2].toInt();
da.setYMD( year, month, day );
return true;
}
}
OTodo OTodoAccessBackendSQL::todo( const OSQLResult& res) const{
if ( res.state() == OSQLResult::Failure ) {
OTodo to;
return to;
}
OSQLResultItem::ValueList list = res.results();
OSQLResultItem::ValueList::Iterator it = list.begin();
qWarning("todo1");
OTodo to = todo( (*it) );
cache( to );
++it;
for ( ; it != list.end(); ++it ) {
qWarning("caching");
cache( todo( (*it) ) );
}
return to;
}
OTodo OTodoAccessBackendSQL::todo( OSQLResultItem& item )const {
qWarning("todo");
bool has = false; QDate da = QDate::currentDate();
has = date( da, item.data("DueDate") );
QStringList cats = QStringList::split(";", item.data("categories") );
OTodo to( (bool)item.data("completed").toInt(), item.data("priority").toInt(),
cats, item.data("summary"), item.data("description"),
item.data("progress").toUShort(), has, da,
item.data("uid").toInt() );
return to;
}
OTodo OTodoAccessBackendSQL::todo( int uid )const {
FindQuery find( uid );
return todo( m_driver->query(&find) );
}
/*
* update the dict
*/
void OTodoAccessBackendSQL::fillDict() {
/* initialize dict */
/*
* UPDATE dict if you change anything!!!
*/
m_dict.setAutoDelete( TRUE );
m_dict.insert("Categories" , new int(OTodo::Category) );
m_dict.insert("Uid" , new int(OTodo::Uid) );
m_dict.insert("HasDate" , new int(OTodo::HasDate) );
m_dict.insert("Completed" , new int(OTodo::Completed) );
m_dict.insert("Description" , new int(OTodo::Description) );
m_dict.insert("Summary" , new int(OTodo::Summary) );
m_dict.insert("Priority" , new int(OTodo::Priority) );
m_dict.insert("DateDay" , new int(OTodo::DateDay) );
m_dict.insert("DateMonth" , new int(OTodo::DateMonth) );
m_dict.insert("DateYear" , new int(OTodo::DateYear) );
m_dict.insert("Progress" , new int(OTodo::Progress) );
m_dict.insert("Completed", new int(OTodo::Completed) );
m_dict.insert("CrossReference", new int(OTodo::CrossReference) );
m_dict.insert("HasAlarmDateTime",new int(OTodo::HasAlarmDateTime) );
m_dict.insert("AlarmDateTime", new int(OTodo::AlarmDateTime) );
}
/*
* need to be const so let's fool the
* compiler :(
*/
void OTodoAccessBackendSQL::update()const {
((OTodoAccessBackendSQL*)this)->m_dirty = false;
LoadQuery lo;
OSQLResult res = m_driver->query(&lo);
if ( res.state() != OSQLResult::Success )
return;
((OTodoAccessBackendSQL*)this)->m_uids = uids( res );
}
QArray<int> OTodoAccessBackendSQL::uids( const OSQLResult& res) const{
OSQLResultItem::ValueList list = res.results();
OSQLResultItem::ValueList::Iterator it;
QArray<int> ints(list.count() );
qWarning(" count = %d", list.count() );
int i = 0;
for (it = list.begin(); it != list.end(); ++it ) {
ints[i] = (*it).data("uid").toInt();
i++;
}
return ints;
}
diff --git a/libopie2/opiepim/backend/otodoaccesssql.h b/libopie2/opiepim/backend/otodoaccesssql.h
index 0f6dd2c..6a4257c 100644
--- a/libopie2/opiepim/backend/otodoaccesssql.h
+++ b/libopie2/opiepim/backend/otodoaccesssql.h
@@ -1,50 +1,50 @@
#ifndef OPIE_PIM_ACCESS_SQL_H
#define OPIE_PIM_ACCESS_SQL_H
#include <qasciidict.h>
#include "otodoaccessbackend.h"
class OSQLDriver;
class OSQLResult;
class OSQLResultItem;
class OTodoAccessBackendSQL : public OTodoAccessBackend {
public:
OTodoAccessBackendSQL( const QString& file );
~OTodoAccessBackendSQL();
bool load();
bool reload();
bool save();
QArray<int> allRecords()const;
- QArray<int> queryByExample( const OTodo& t, int sort );
+ QArray<int> queryByExample( const OTodo& t, int settings, const QDateTime& d = QDateTime() );
OTodo find(int uid)const;
OTodo find(int uid, const QArray<int>&, uint cur, Frontend::CacheDirection )const;
void clear();
bool add( const OTodo& t );
bool remove( int uid );
bool replace( const OTodo& t );
QArray<int> overDue();
QArray<int> effectiveToDos( const QDate& start,
const QDate& end, bool includeNoDates );
QArray<int> sorted(bool asc, int sortOrder, int sortFilter, int cat );
private:
void update()const;
void fillDict();
inline bool date( QDate& date, const QString& )const;
inline OTodo todo( const OSQLResult& )const;
inline OTodo todo( OSQLResultItem& )const;
inline QArray<int> uids( const OSQLResult& )const;
OTodo todo( int uid )const;
QAsciiDict<int> m_dict;
OSQLDriver* m_driver;
QArray<int> m_uids;
bool m_dirty : 1;
};
#endif
diff --git a/libopie2/opiepim/backend/otodoaccessvcal.cpp b/libopie2/opiepim/backend/otodoaccessvcal.cpp
index 309f9e1..2136283 100644
--- a/libopie2/opiepim/backend/otodoaccessvcal.cpp
+++ b/libopie2/opiepim/backend/otodoaccessvcal.cpp
@@ -1,201 +1,201 @@
#include <qfile.h>
#include <qtopia/private/vobject_p.h>
#include <qtopia/timeconversion.h>
#include <qtopia/private/qfiledirect_p.h>
#include "otodoaccessvcal.h"
namespace {
static OTodo eventByVObj( VObject *obj ){
OTodo event;
VObject *ob;
QCString name;
// no uid, attendees, ... and no fun
// description
if( ( ob = isAPropertyOf( obj, VCDescriptionProp )) != 0 ){
name = vObjectStringZValue( ob );
event.setDescription( name );
}
// summary
if ( ( ob = isAPropertyOf( obj, VCSummaryProp ) ) != 0 ) {
name = vObjectStringZValue( ob );
event.setSummary( name );
}
// completed
if( ( ob = isAPropertyOf( obj, VCStatusProp )) != 0 ){
name = vObjectStringZValue( ob );
if( name == "COMPLETED" ){
event.setCompleted( true );
}else{
event.setCompleted( false );
}
}else
event.setCompleted( false );
// priority
if ((ob = isAPropertyOf(obj, VCPriorityProp))) {
name = vObjectStringZValue( ob );
bool ok;
event.setPriority(name.toInt(&ok) );
}
//due date
if((ob = isAPropertyOf(obj, VCDueProp)) ){
event.setHasDueDate( true );
name = vObjectStringZValue( ob );
event.setDueDate( TimeConversion::fromISO8601( name).date() );
}
// categories
if((ob = isAPropertyOf( obj, VCCategoriesProp )) != 0 ){
name = vObjectStringZValue( ob );
qWarning("Categories:%s", name.data() );
}
event.setUid( 1 );
return event;
};
static VObject *vobjByEvent( const OTodo &event ) {
VObject *task = newVObject( VCTodoProp );
if( task == 0 )
return 0l;
if( event.hasDueDate() ) {
QTime time(0, 0, 0);
QDateTime date(event.dueDate(), time );
addPropValue( task, VCDueProp,
TimeConversion::toISO8601( date ) );
}
if( event.isCompleted() )
addPropValue( task, VCStatusProp, "COMPLETED");
QString string = QString::number(event.priority() );
addPropValue( task, VCPriorityProp, string.local8Bit() );
addPropValue( task, VCCategoriesProp,
event.idsToString( event.categories() ).local8Bit() );
addPropValue( task, VCDescriptionProp,
event.description().local8Bit() );
addPropValue( task, VCSummaryProp,
event.summary().local8Bit() );
return task;
};
}
OTodoAccessVCal::OTodoAccessVCal( const QString& path )
: m_dirty(false), m_file( path )
{
}
OTodoAccessVCal::~OTodoAccessVCal() {
}
bool OTodoAccessVCal::load() {
m_map.clear();
m_dirty = false;
VObject* vcal = 0l;
vcal = Parse_MIME_FromFileName( QFile::encodeName(m_file).data() );
if (!vcal )
return false;
// Iterate over the list
VObjectIterator it;
VObject* vobj;
initPropIterator(&it, vcal);
while( moreIteration( &it ) ) {
vobj = ::nextVObject( &it );
QCString name = ::vObjectName( vobj );
if( name == VCTodoProp ){
OTodo to = eventByVObj( vobj );
m_map.insert( to.uid(), to );
}
}
// Should I do a delete vcal?
return true;
}
bool OTodoAccessVCal::reload() {
return load();
}
bool OTodoAccessVCal::save() {
if (!m_dirty )
return true;
QFileDirect file( m_file );
if (!file.open(IO_WriteOnly ) )
return false;
VObject *obj;
obj = newVObject( VCCalProp );
addPropValue( obj, VCVersionProp, "1.0" );
VObject *vo;
for(QMap<int, OTodo>::ConstIterator it=m_map.begin(); it !=m_map.end(); ++it ){
vo = vobjByEvent( it.data() );
addVObjectProp(obj, vo );
}
writeVObject( file.directHandle(), obj );
cleanVObject( obj );
cleanStrTbl();
m_dirty = false;
return true;
}
void OTodoAccessVCal::clear() {
m_map.clear();
m_dirty = true;
}
bool OTodoAccessVCal::add( const OTodo& to ) {
m_map.insert( to.uid(), to );
m_dirty = true;
return true;
}
bool OTodoAccessVCal::remove( int uid ) {
m_map.remove( uid );
m_dirty = true;
return true;
}
void OTodoAccessVCal::removeAllCompleted() {
for ( QMap<int, OTodo>::Iterator it = m_map.begin(); it != m_map.end(); ++it ) {
if ( (*it).isCompleted() )
m_map.remove( it );
}
}
bool OTodoAccessVCal::replace( const OTodo& to ) {
m_map.replace( to.uid(), to );
m_dirty = true;
return true;
}
OTodo OTodoAccessVCal::find(int uid )const {
return m_map[uid];
}
QArray<int> OTodoAccessVCal::sorted( bool, int, int, int ) {
QArray<int> ar(0);
return ar;
}
QArray<int> OTodoAccessVCal::allRecords()const {
QArray<int> ar( m_map.count() );
QMap<int, OTodo>::ConstIterator it;
int i = 0;
for ( it = m_map.begin(); it != m_map.end(); ++it ) {
ar[i] = it.key();
i++;
}
return ar;
}
-QArray<int> OTodoAccessVCal::queryByExample( const OTodo&, int ) {
+QArray<int> OTodoAccessVCal::queryByExample( const OTodo&, int, const QDateTime& ) {
QArray<int> ar(0);
return ar;
}
QArray<int> OTodoAccessVCal::effectiveToDos( const QDate& ,
const QDate& ,
bool ) {
QArray<int> ar(0);
return ar;
}
QArray<int> OTodoAccessVCal::overDue() {
QArray<int> ar(0);
return ar;
}
diff --git a/libopie2/opiepim/backend/otodoaccessvcal.h b/libopie2/opiepim/backend/otodoaccessvcal.h
index 452f602..a90ee9c 100644
--- a/libopie2/opiepim/backend/otodoaccessvcal.h
+++ b/libopie2/opiepim/backend/otodoaccessvcal.h
@@ -1,37 +1,37 @@
#ifndef OPIE_OTODO_ACCESS_VCAL_H
#define OPIE_OTODO_ACCESS_VCAL_H
#include "otodoaccessbackend.h"
class OTodoAccessVCal : public OTodoAccessBackend {
public:
OTodoAccessVCal(const QString& );
~OTodoAccessVCal();
bool load();
bool reload();
bool save();
QArray<int> allRecords()const;
- QArray<int> queryByExample( const OTodo& t, int sort );
+ QArray<int> queryByExample( const OTodo& t, int sort, const QDateTime& d = QDateTime() );
QArray<int> effectiveToDos( const QDate& start,
const QDate& end,
bool includeNoDates );
QArray<int> overDue();
QArray<int> sorted( bool asc, int sortOrder, int sortFilter,
int cat );
OTodo find(int uid)const;
void clear();
bool add( const OTodo& );
bool remove( int uid );
bool replace( const OTodo& );
void removeAllCompleted();
private:
bool m_dirty : 1;
QString m_file;
QMap<int, OTodo> m_map;
};
#endif
diff --git a/libopie2/opiepim/backend/otodoaccessxml.cpp b/libopie2/opiepim/backend/otodoaccessxml.cpp
index cda300b..71e8787 100644
--- a/libopie2/opiepim/backend/otodoaccessxml.cpp
+++ b/libopie2/opiepim/backend/otodoaccessxml.cpp
@@ -1,675 +1,675 @@
#include <errno.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <qfile.h>
#include <qvector.h>
#include <qpe/global.h>
#include <qpe/stringutil.h>
#include <qpe/timeconversion.h>
#include "otodoaccessxml.h"
namespace {
// FROM TT again
char *strstrlen(const char *haystack, int hLen, const char* needle, int nLen)
{
char needleChar;
char haystackChar;
if (!needle || !haystack || !hLen || !nLen)
return 0;
const char* hsearch = haystack;
if ((needleChar = *needle++) != 0) {
nLen--; //(to make up for needle++)
do {
do {
if ((haystackChar = *hsearch++) == 0)
return (0);
if (hsearch >= haystack + hLen)
return (0);
} while (haystackChar != needleChar);
} while (strncmp(hsearch, needle, QMIN(hLen - (hsearch - haystack), nLen)) != 0);
hsearch--;
}
return ((char *)hsearch);
}
}
OTodoAccessXML::OTodoAccessXML( const QString& appName,
const QString& fileName )
: OTodoAccessBackend(), m_app( appName ), m_opened( false ), m_changed( false )
{
if (!fileName.isEmpty() )
m_file = fileName;
else
m_file = Global::applicationFileName( "todolist", "todolist.xml" );
}
OTodoAccessXML::~OTodoAccessXML() {
}
bool OTodoAccessXML::load() {
m_opened = true;
m_changed = false;
/* initialize dict */
/*
* UPDATE dict if you change anything!!!
*/
QAsciiDict<int> dict(21);
dict.setAutoDelete( TRUE );
dict.insert("Categories" , new int(OTodo::Category) );
dict.insert("Uid" , new int(OTodo::Uid) );
dict.insert("HasDate" , new int(OTodo::HasDate) );
dict.insert("Completed" , new int(OTodo::Completed) );
dict.insert("Description" , new int(OTodo::Description) );
dict.insert("Summary" , new int(OTodo::Summary) );
dict.insert("Priority" , new int(OTodo::Priority) );
dict.insert("DateDay" , new int(OTodo::DateDay) );
dict.insert("DateMonth" , new int(OTodo::DateMonth) );
dict.insert("DateYear" , new int(OTodo::DateYear) );
dict.insert("Progress" , new int(OTodo::Progress) );
dict.insert("Completed", new int(OTodo::Completed) );
dict.insert("CrossReference", new int(OTodo::CrossReference) );
dict.insert("State", new int(OTodo::State) );
dict.insert("Recurrence", new int(OTodo::Recurrence) );
dict.insert("Alarms", new int(OTodo::Alarms) );
dict.insert("Reminders", new int(OTodo::Reminders) );
dict.insert("Notifiers", new int(OTodo::Notifiers) );
dict.insert("Maintainer", new int(OTodo::Maintainer) );
// here the custom XML parser from TT it's GPL
// but we want to push OpiePIM... to TT.....
// mmap part from zecke :)
int fd = ::open( QFile::encodeName(m_file).data(), O_RDONLY );
struct stat attribut;
if ( fd < 0 ) return false;
if ( fstat(fd, &attribut ) == -1 ) {
::close( fd );
return false;
}
void* map_addr = ::mmap(NULL, attribut.st_size, PROT_READ, MAP_SHARED, fd, 0 );
if ( map_addr == ( (caddr_t)-1) ) {
::close(fd );
return false;
}
/* advise the kernel who we want to read it */
::madvise( map_addr, attribut.st_size, MADV_SEQUENTIAL );
/* we do not the file any more */
::close( fd );
char* dt = (char*)map_addr;
int len = attribut.st_size;
int i = 0;
char *point;
const char* collectionString = "<Task ";
int strLen = strlen(collectionString);
while ( ( point = strstrlen( dt+i, len -i, collectionString, strLen ) ) != 0l ) {
i = point -dt;
i+= strLen;
qWarning("Found a start at %d %d", i, (point-dt) );
OTodo ev;
m_year = m_month = m_day = 0;
while ( TRUE ) {
while ( i < len && (dt[i] == ' ' || dt[i] == '\n' || dt[i] == '\r') )
++i;
if ( i >= len-2 || (dt[i] == '/' && dt[i+1] == '>') )
break;
// we have another attribute, read it.
int j = i;
while ( j < len && dt[j] != '=' )
++j;
QCString attr( dt+i, j-i+1);
i = ++j; // skip =
// find the start of quotes
while ( i < len && dt[i] != '"' )
++i;
j = ++i;
bool haveUtf = FALSE;
bool haveEnt = FALSE;
while ( j < len && dt[j] != '"' ) {
if ( ((unsigned char)dt[j]) > 0x7f )
haveUtf = TRUE;
if ( dt[j] == '&' )
haveEnt = TRUE;
++j;
}
if ( i == j ) {
// empty value
i = j + 1;
continue;
}
QCString value( dt+i, j-i+1 );
i = j + 1;
QString str = (haveUtf ? QString::fromUtf8( value )
: QString::fromLatin1( value ) );
if ( haveEnt )
str = Qtopia::plainString( str );
/*
* add key + value
*/
todo( &dict, ev, attr, str );
}
/*
* now add it
*/
qWarning("End at %d", i );
if (m_events.contains( ev.uid() ) || ev.uid() == 0) {
ev.setUid( 1 );
m_changed = true;
}
if ( ev.hasDueDate() ) {
ev.setDueDate( QDate(m_year, m_month, m_day) );
}
m_events.insert(ev.uid(), ev );
m_year = m_month = m_day = -1;
}
munmap(map_addr, attribut.st_size );
qWarning("counts %d records loaded!", m_events.count() );
return true;
}
bool OTodoAccessXML::reload() {
m_events.clear();
return load();
}
bool OTodoAccessXML::save() {
// qWarning("saving");
if (!m_opened || !m_changed ) {
// qWarning("not saving");
return true;
}
QString strNewFile = m_file + ".new";
QFile f( strNewFile );
if (!f.open( IO_WriteOnly|IO_Raw ) )
return false;
int written;
QString out;
out = "<!DOCTYPE Tasks>\n<Tasks>\n";
// for all todos
QMap<int, OTodo>::Iterator it;
for (it = m_events.begin(); it != m_events.end(); ++it ) {
out+= "<Task " + toString( (*it) ) + " />\n";
QCString cstr = out.utf8();
written = f.writeBlock( cstr.data(), cstr.length() );
/* less written then we wanted */
if ( written != (int)cstr.length() ) {
f.close();
QFile::remove( strNewFile );
return false;
}
out = QString::null;
}
out += "</Tasks>";
QCString cstr = out.utf8();
written = f.writeBlock( cstr.data(), cstr.length() );
if ( written != (int)cstr.length() ) {
f.close();
QFile::remove( strNewFile );
return false;
}
/* flush before renaming */
f.close();
if( ::rename( strNewFile.latin1(), m_file.latin1() ) < 0 ) {
// qWarning("error renaming");
QFile::remove( strNewFile );
}
m_changed = false;
return true;
}
QArray<int> OTodoAccessXML::allRecords()const {
QArray<int> ids( m_events.count() );
QMap<int, OTodo>::ConstIterator it;
int i = 0;
for ( it = m_events.begin(); it != m_events.end(); ++it ) {
ids[i] = it.key();
i++;
}
return ids;
}
-QArray<int> OTodoAccessXML::queryByExample( const OTodo&, int ) {
+QArray<int> OTodoAccessXML::queryByExample( const OTodo&, int, const QDateTime& ) {
QArray<int> ids(0);
return ids;
}
OTodo OTodoAccessXML::find( int uid )const {
OTodo todo;
todo.setUid( 0 ); // isEmpty()
QMap<int, OTodo>::ConstIterator it = m_events.find( uid );
if ( it != m_events.end() )
todo = it.data();
return todo;
}
void OTodoAccessXML::clear() {
if (m_opened )
m_changed = true;
m_events.clear();
}
bool OTodoAccessXML::add( const OTodo& todo ) {
// qWarning("add");
m_changed = true;
m_events.insert( todo.uid(), todo );
return true;
}
bool OTodoAccessXML::remove( int uid ) {
m_changed = true;
m_events.remove( uid );
return true;
}
bool OTodoAccessXML::replace( const OTodo& todo) {
m_changed = true;
m_events.replace( todo.uid(), todo );
return true;
}
QArray<int> OTodoAccessXML::effectiveToDos( const QDate& start,
const QDate& end,
bool includeNoDates ) {
QArray<int> ids( m_events.count() );
QMap<int, OTodo>::Iterator it;
int i = 0;
for ( it = m_events.begin(); it != m_events.end(); ++it ) {
if ( !it.data().hasDueDate() ) {
if ( includeNoDates ) {
ids[i] = it.key();
i++;
}
}else if ( it.data().dueDate() >= start &&
it.data().dueDate() <= end ) {
ids[i] = it.key();
i++;
}
}
ids.resize( i );
return ids;
}
QArray<int> OTodoAccessXML::overDue() {
QArray<int> ids( m_events.count() );
int i = 0;
QMap<int, OTodo>::Iterator it;
for ( it = m_events.begin(); it != m_events.end(); ++it ) {
if ( it.data().isOverdue() ) {
ids[i] = it.key();
i++;
}
}
ids.resize( i );
return ids;
}
/* private */
void OTodoAccessXML::todo( QAsciiDict<int>* dict, OTodo& ev,
const QCString& attr, const QString& val) {
// qWarning("parse to do from XMLElement" );
int *find=0;
find = (*dict)[ attr.data() ];
if (!find ) {
// qWarning("Unknown option" + it.key() );
ev.setCustomField( attr, val );
return;
}
switch( *find ) {
case OTodo::Uid:
ev.setUid( val.toInt() );
break;
case OTodo::Category:
ev.setCategories( ev.idsFromString( val ) );
break;
case OTodo::HasDate:
ev.setHasDueDate( val.toInt() );
break;
case OTodo::Completed:
ev.setCompleted( val.toInt() );
break;
case OTodo::Description:
ev.setDescription( val );
break;
case OTodo::Summary:
ev.setSummary( val );
break;
case OTodo::Priority:
ev.setPriority( val.toInt() );
break;
case OTodo::DateDay:
m_day = val.toInt();
break;
case OTodo::DateMonth:
m_month = val.toInt();
break;
case OTodo::DateYear:
m_year = val.toInt();
break;
case OTodo::Progress:
ev.setProgress( val.toInt() );
break;
case OTodo::CrossReference:
{
/*
* A cross refernce looks like
* appname,id;appname,id
* we need to split it up
*/
QStringList refs = QStringList::split(';', val );
QStringList::Iterator strIt;
for (strIt = refs.begin(); strIt != refs.end(); ++strIt ) {
int pos = (*strIt).find(',');
if ( pos > -1 )
; // ev.addRelation( (*strIt).left(pos), (*strIt).mid(pos+1).toInt() );
}
break;
}
default:
break;
}
}
QString OTodoAccessXML::toString( const OTodo& ev )const {
QString str;
str += "Completed=\"" + QString::number( ev.isCompleted() ) + "\" ";
str += "HasDate=\"" + QString::number( ev.hasDueDate() ) + "\" ";
str += "Priority=\"" + QString::number( ev.priority() ) + "\" ";
str += "Progress=\"" + QString::number(ev.progress() ) + "\" ";
str += "Categories=\"" + toString( ev.categories() ) + "\" ";
str += "Description=\"" + Qtopia::escapeString( ev.description() ) + "\" ";
str += "Summary=\"" + Qtopia::escapeString( ev.summary() ) + "\" ";
if ( ev.hasDueDate() ) {
str += "DateYear=\"" + QString::number( ev.dueDate().year() ) + "\" ";
str += "DateMonth=\"" + QString::number( ev.dueDate().month() ) + "\" ";
str += "DateDay=\"" + QString::number( ev.dueDate().day() ) + "\" ";
}
// qWarning( "Uid %d", ev.uid() );
str += "Uid=\"" + QString::number( ev.uid() ) + "\" ";
// append the extra options
/* FIXME Qtopia::Record this is currently not
* possible you can set custom fields
* but don' iterate over the list
* I may do #define private protected
* for this case - cough --zecke
*/
/*
QMap<QString, QString> extras = ev.extras();
QMap<QString, QString>::Iterator extIt;
for (extIt = extras.begin(); extIt != extras.end(); ++extIt )
str += extIt.key() + "=\"" + extIt.data() + "\" ";
*/
// cross refernce
return str;
}
QString OTodoAccessXML::toString( const QArray<int>& ints ) const {
return Qtopia::Record::idsToString( ints );
}
/* internal class for sorting
*
* Inspired by todoxmlio.cpp from TT
*/
struct OTodoXMLContainer {
OTodo todo;
};
namespace {
inline QString string( const OTodo& todo) {
return todo.summary().isEmpty() ?
todo.description().left(20 ) :
todo.summary();
}
inline int completed( const OTodo& todo1, const OTodo& todo2) {
int ret = 0;
if ( todo1.isCompleted() ) ret++;
if ( todo2.isCompleted() ) ret--;
return ret;
}
inline int priority( const OTodo& t1, const OTodo& t2) {
return ( t1.priority() - t2.priority() );
}
inline int description( const OTodo& t1, const OTodo& t2) {
return QString::compare( string(t1), string(t2) );
}
inline int deadline( const OTodo& t1, const OTodo& t2) {
int ret = 0;
if ( t1.hasDueDate() &&
t2.hasDueDate() )
ret = t2.dueDate().daysTo( t1.dueDate() );
else if ( t1.hasDueDate() )
ret = -1;
else if ( t2.hasDueDate() )
ret = 1;
else
ret = 0;
return ret;
}
};
/*
* Returns:
* 0 if item1 == item2
*
* non-zero if item1 != item2
*
* This function returns int rather than bool so that reimplementations
* can return one of three values and use it to sort by:
*
* 0 if item1 == item2
*
* > 0 (positive integer) if item1 > item2
*
* < 0 (negative integer) if item1 < item2
*
*/
class OTodoXMLVector : public QVector<OTodoXMLContainer> {
public:
OTodoXMLVector(int size, bool asc, int sort)
: QVector<OTodoXMLContainer>( size )
{
setAutoDelete( true );
m_asc = asc;
m_sort = sort;
}
/* return the summary/description */
QString string( const OTodo& todo) {
return todo.summary().isEmpty() ?
todo.description().left(20 ) :
todo.summary();
}
/**
* we take the sortorder( switch on it )
*
*/
int compareItems( Item d1, Item d2 ) {
bool seComp, sePrio, seDesc, seDeadline;
seComp = sePrio = seDeadline = seDesc = false;
int ret =0;
OTodoXMLContainer* con1 = (OTodoXMLContainer*)d1;
OTodoXMLContainer* con2 = (OTodoXMLContainer*)d2;
/* same item */
if ( con1->todo.uid() == con2->todo.uid() )
return 0;
switch ( m_sort ) {
/* completed */
case 0: {
ret = completed( con1->todo, con2->todo );
seComp = TRUE;
break;
}
/* priority */
case 1: {
ret = priority( con1->todo, con2->todo );
sePrio = TRUE;
break;
}
/* description */
case 2: {
ret = description( con1->todo, con2->todo );
seDesc = TRUE;
break;
}
/* deadline */
case 3: {
ret = deadline( con1->todo, con2->todo );
seDeadline = TRUE;
break;
}
default:
ret = 0;
break;
};
/*
* FIXME do better sorting if the first sort criteria
* ret equals 0 start with complete and so on...
*/
/* twist it we're not ascending*/
if (!m_asc)
ret = ret * -1;
if ( ret )
return ret;
// default did not gave difference let's try it other way around
/*
* General try if already checked if not test
* and return
* 1.Completed
* 2.Priority
* 3.Description
* 4.DueDate
*/
if (!seComp ) {
if ( (ret = completed( con1->todo, con2->todo ) ) ) {
if (!m_asc ) ret *= -1;
return ret;
}
}
if (!sePrio ) {
if ( (ret = priority( con1->todo, con2->todo ) ) ) {
if (!m_asc ) ret *= -1;
return ret;
}
}
if (!seDesc ) {
if ( (ret = description(con1->todo, con2->todo ) ) ) {
if (!m_asc) ret *= -1;
return ret;
}
}
if (!seDeadline) {
if ( (ret = deadline( con1->todo, con2->todo ) ) ) {
if (!m_asc) ret *= -1;
return ret;
}
}
return 0;
}
private:
bool m_asc;
int m_sort;
};
QArray<int> OTodoAccessXML::sorted( bool asc, int sortOrder,
int sortFilter, int cat ) {
qWarning("sorted! %d cat", cat);
OTodoXMLVector vector(m_events.count(), asc,sortOrder );
QMap<int, OTodo>::Iterator it;
int item = 0;
bool bCat = sortFilter & 1 ? true : false;
bool bOnly = sortFilter & 2 ? true : false;
bool comp = sortFilter & 4 ? true : false;
for ( it = m_events.begin(); it != m_events.end(); ++it ) {
/* show category */
if ( bCat && cat != 0)
if (!(*it).categories().contains( cat ) ) {
qWarning("category mis match");
continue;
}
/* isOverdue but we should not show overdue - why?*/
/* if ( (*it).isOverdue() && !bOnly ) {
qWarning("item is overdue but !bOnly");
continue;
}
*/
if ( !(*it).isOverdue() && bOnly ) {
qWarning("item is not overdue but bOnly checked");
continue;
}
if ((*it).isCompleted() && comp ) {
qWarning("completed continue!");
continue;
}
OTodoXMLContainer* con = new OTodoXMLContainer();
con->todo = (*it);
vector.insert(item, con );
item++;
}
qWarning("XXX %d Items added", item);
vector.resize( item );
/* sort it now */
vector.sort();
/* now get the uids */
QArray<int> array( vector.count() );
for (uint i= 0; i < vector.count(); i++ ) {
array[i] = ( vector.at(i) )->todo.uid();
}
qWarning("array count = %d %d", array.count(), vector.count() );
return array;
};
void OTodoAccessXML::removeAllCompleted() {
for ( QMap<int, OTodo>::Iterator it = m_events.begin(); it != m_events.end(); ++it ) {
if ( (*it).isCompleted() )
m_events.remove( it );
}
}
diff --git a/libopie2/opiepim/backend/otodoaccessxml.h b/libopie2/opiepim/backend/otodoaccessxml.h
index 93609fe..1032c92 100644
--- a/libopie2/opiepim/backend/otodoaccessxml.h
+++ b/libopie2/opiepim/backend/otodoaccessxml.h
@@ -1,57 +1,57 @@
#ifndef OPIE_TODO_ACCESS_XML_H
#define OPIE_TODO_ACCESS_XML_H
#include <qasciidict.h>
#include <qmap.h>
#include "otodoaccessbackend.h"
namespace Opie {
class XMLElement;
};
class OTodoAccessXML : public OTodoAccessBackend {
public:
/**
* fileName if Empty we will use the default path
*/
OTodoAccessXML( const QString& appName,
const QString& fileName = QString::null );
~OTodoAccessXML();
bool load();
bool reload();
bool save();
QArray<int> allRecords()const;
- QArray<int> queryByExample( const OTodo&, int querysettings );
+ QArray<int> queryByExample( const OTodo&, int querysettings, const QDateTime& d = QDateTime() );
OTodo find( int uid )const;
void clear();
bool add( const OTodo& );
bool remove( int uid );
void removeAllCompleted();
bool replace( const OTodo& );
/* our functions */
QArray<int> effectiveToDos( const QDate& start,
const QDate& end,
bool includeNoDates );
QArray<int> overDue();
QArray<int> sorted( bool asc, int sortOrder,
int sortFilter, int cat );
private:
void todo( QAsciiDict<int>*, OTodo&,const QCString&,const QString& );
QString toString( const OTodo& )const;
QString toString( const QArray<int>& ints ) const;
QMap<int, OTodo> m_events;
QString m_file;
QString m_app;
bool m_opened : 1;
bool m_changed : 1;
class OTodoAccessXMLPrivate;
OTodoAccessXMLPrivate* d;
int m_year, m_month, m_day;
};
#endif
diff --git a/libopie2/opiepim/core/opimaccesstemplate.h b/libopie2/opiepim/core/opimaccesstemplate.h
index 259e2c1..6a3a0db 100644
--- a/libopie2/opiepim/core/opimaccesstemplate.h
+++ b/libopie2/opiepim/core/opimaccesstemplate.h
@@ -1,286 +1,286 @@
#ifndef OPIE_PIM_ACCESS_TEMPLATE_H
#define OPIE_PIM_ACCESS_TEMPLATE_H
#include <qarray.h>
#include <opie/opimrecord.h>
#include <opie/opimaccessbackend.h>
#include <opie/orecordlist.h>
#include "opimcache.h"
#include "otemplatebase.h"
/**
* Thats the frontend to our OPIE PIM
* Library. Either you want to use it's
* interface or you want to implement
* your own Access lib
* Just create a OPimRecord and inherit from
* the plugins
*/
template <class T = OPimRecord >
class OPimAccessTemplate : public OTemplateBase<T> {
public:
enum Access {
Random = 0,
SortedAccess
};
typedef ORecordList<T> List;
typedef OPimAccessBackend<T> BackEnd;
typedef OPimCache<T> Cache;
/**
* c'tor BackEnd
* enum Access a small hint on how to handle the backend
*/
OPimAccessTemplate( BackEnd* end);
virtual ~OPimAccessTemplate();
/**
* load from the backend
*/
bool load();
/** Reload database.
* You should execute this function if the external database
* was changed.
* This function will load the external database and afterwards
* rejoin the local changes. Therefore the local database will be set consistent.
*/
virtual bool reload();
/** Save contacts database.
* Save is more a "commit". After calling this function, all changes are public available.
* @return true if successful
*/
bool save();
/**
* if the resource was changed externally
* You should use the signal handling instead of polling possible changes !
* zecke: Do you implement a signal for otodoaccess ?
*/
bool wasChangedExternally()const;
/**
* return a List of records
* you can iterate over them
*/
virtual List allRecords()const;
/**
* queryByExample.
* @see otodoaccess, ocontactaccess
*/
- virtual List queryByExample( const T& t, int querySettings );
+ virtual List queryByExample( const T& t, int querySettings, const QDateTime& d = QDateTime() );
/**
* find the OPimRecord uid
*/
virtual T find( int uid )const;
/**
* read ahead cache find method ;)
*/
virtual T find( int uid, const QArray<int>&,
uint current, typename OTemplateBase<T>::CacheDirection dir = OTemplateBase<T>::Forward )const;
/* invalidate cache here */
/**
* clears the backend and invalidates the backend
*/
void clear() ;
/**
* add T to the backend
* @param t The item to add.
* @return <i>true</i> if added successfully.
*/
virtual bool add( const T& t ) ;
bool add( const OPimRecord& );
/* only the uid matters */
/**
* remove T from the backend
* @param t The item to remove
* @return <i>true</i> if successful.
*/
virtual bool remove( const T& t );
/**
* remove the OPimRecord with uid
* @param uid The ID of the item to remove
* @return <i>true</i> if successful.
*/
bool remove( int uid );
bool remove( const OPimRecord& );
/**
* replace T from backend
* @param t The item to replace
* @return <i>true</i> if successful.
*/
virtual bool replace( const T& t) ;
void setReadAhead( uint count );
/**
* @internal
*/
void cache( const T& )const;
void setSaneCacheSize( int );
QArray<int> records()const;
protected:
/**
* invalidate the cache
*/
void invalidateCache();
void setBackEnd( BackEnd* end );
/**
* returns the backend
*/
BackEnd* backEnd();
BackEnd* m_backEnd;
Cache m_cache;
};
template <class T>
OPimAccessTemplate<T>::OPimAccessTemplate( BackEnd* end )
: OTemplateBase<T>(), m_backEnd( end )
{
if (end )
end->setFrontend( this );
}
template <class T>
OPimAccessTemplate<T>::~OPimAccessTemplate() {
qWarning("~OPimAccessTemplate<T>");
delete m_backEnd;
}
template <class T>
bool OPimAccessTemplate<T>::load() {
invalidateCache();
return m_backEnd->load();
}
template <class T>
bool OPimAccessTemplate<T>::reload() {
invalidateCache(); // zecke: I think this should be added (se)
return m_backEnd->reload();
}
template <class T>
bool OPimAccessTemplate<T>::save() {
return m_backEnd->save();
}
template <class T>
typename OPimAccessTemplate<T>::List OPimAccessTemplate<T>::allRecords()const {
QArray<int> ints = m_backEnd->allRecords();
List lis(ints, this );
return lis;
}
template <class T>
QArray<int> OPimAccessTemplate<T>::records()const {
return m_backEnd->allRecords();
}
template <class T>
typename OPimAccessTemplate<T>::List
-OPimAccessTemplate<T>::queryByExample( const T& t, int sortOrder ) {
- QArray<int> ints = m_backEnd->queryByExample( t, sortOrder );
+OPimAccessTemplate<T>::queryByExample( const T& t, int settings, const QDateTime& d ) {
+ QArray<int> ints = m_backEnd->queryByExample( t, settings, d );
List lis(ints, this );
return lis;
}
template <class T>
T OPimAccessTemplate<T>::find( int uid ) const{
T t = m_backEnd->find( uid );
cache( t );
return t;
}
template <class T>
T OPimAccessTemplate<T>::find( int uid, const QArray<int>& ar,
uint current, typename OTemplateBase<T>::CacheDirection dir )const {
/*
* better do T.isEmpty()
* after a find this way we would
* avoid two finds in QCache...
*/
// qWarning("find it now %d", uid );
if (m_cache.contains( uid ) ) {
return m_cache.find( uid );
}
T t = m_backEnd->find( uid, ar, current, dir );
cache( t );
return t;
}
template <class T>
void OPimAccessTemplate<T>::clear() {
invalidateCache();
m_backEnd->clear();
}
template <class T>
bool OPimAccessTemplate<T>::add( const T& t ) {
cache( t );
return m_backEnd->add( t );
}
template <class T>
bool OPimAccessTemplate<T>::add( const OPimRecord& rec) {
/* same type */
if ( rec.rtti() == T::rtti() ) {
const T &t = static_cast<const T&>(rec);
return add(t);
}
return false;
}
template <class T>
bool OPimAccessTemplate<T>::remove( const T& t ) {
return remove( t.uid() );
}
template <class T>
bool OPimAccessTemplate<T>::remove( int uid ) {
m_cache.remove( uid );
return m_backEnd->remove( uid );
}
template <class T>
bool OPimAccessTemplate<T>::remove( const OPimRecord& rec) {
return remove( rec.uid() );
}
template <class T>
bool OPimAccessTemplate<T>::replace( const T& t ) {
m_cache.replace( t );
return m_backEnd->replace( t );
}
template <class T>
void OPimAccessTemplate<T>::invalidateCache() {
m_cache.invalidate();
}
template <class T>
typename OPimAccessTemplate<T>::BackEnd* OPimAccessTemplate<T>::backEnd() {
return m_backEnd;
}
template <class T>
bool OPimAccessTemplate<T>::wasChangedExternally()const {
return false;
}
template <class T>
void OPimAccessTemplate<T>::setBackEnd( BackEnd* end ) {
m_backEnd = end;
if (m_backEnd )
m_backEnd->setFrontend( this );
}
template <class T>
void OPimAccessTemplate<T>::cache( const T& t ) const{
/* hacky we need to work around the const*/
((OPimAccessTemplate<T>*)this)->m_cache.add( t );
}
template <class T>
void OPimAccessTemplate<T>::setSaneCacheSize( int size ) {
m_cache.setSize( size );
}
template <class T>
void OPimAccessTemplate<T>::setReadAhead( uint count ) {
m_backEnd->setReadAhead( count );
}
#endif