You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
593 lines
14 KiB
593 lines
14 KiB
/*
|
|
This file is part of libkabc.
|
|
Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org>
|
|
|
|
This library 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.
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Library General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Library General Public License
|
|
along with this library; see the file COPYING.LIB. If not, write to
|
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
#include "address.h"
|
|
|
|
#include <kapplication.h>
|
|
#include <kdebug.h>
|
|
#include <klocale.h>
|
|
#include <ksimpleconfig.h>
|
|
#include <kstandarddirs.h>
|
|
#include <kstaticdeleter.h>
|
|
|
|
#include <qfile.h>
|
|
|
|
using namespace KABC;
|
|
|
|
QMap<QString, QString> *Address::mISOMap = 0;
|
|
static KStaticDeleter< QMap<QString, QString> > isoMapDeleter;
|
|
|
|
Address::Address() :
|
|
mEmpty( true ), mType( 0 )
|
|
{
|
|
mId = KApplication::randomString( 10 );
|
|
}
|
|
|
|
Address::Address( int type ) :
|
|
mEmpty( true ), mType( type )
|
|
{
|
|
mId = KApplication::randomString( 10 );
|
|
}
|
|
|
|
bool Address::operator==( const Address &a ) const
|
|
{
|
|
if ( mPostOfficeBox != a.mPostOfficeBox ) return false;
|
|
if ( mExtended != a.mExtended ) return false;
|
|
if ( mStreet != a.mStreet ) return false;
|
|
if ( mLocality != a.mLocality ) return false;
|
|
if ( mRegion != a.mRegion ) return false;
|
|
if ( mPostalCode != a.mPostalCode ) return false;
|
|
if ( mCountry != a.mCountry ) return false;
|
|
if ( mLabel != a.mLabel ) return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool Address::operator!=( const Address &a ) const
|
|
{
|
|
return !( a == *this );
|
|
}
|
|
|
|
bool Address::isEmpty() const
|
|
{
|
|
if ( mPostOfficeBox.isEmpty() &&
|
|
mExtended.isEmpty() &&
|
|
mStreet.isEmpty() &&
|
|
mLocality.isEmpty() &&
|
|
mRegion.isEmpty() &&
|
|
mPostalCode.isEmpty() &&
|
|
mCountry.isEmpty() &&
|
|
mLabel.isEmpty() ) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void Address::clear()
|
|
{
|
|
*this = Address();
|
|
}
|
|
|
|
void Address::setId( const QString &id )
|
|
{
|
|
mEmpty = false;
|
|
|
|
mId = id;
|
|
}
|
|
|
|
QString Address::id() const
|
|
{
|
|
return mId;
|
|
}
|
|
|
|
void Address::setType( int type )
|
|
{
|
|
mEmpty = false;
|
|
|
|
mType = type;
|
|
}
|
|
|
|
int Address::type() const
|
|
{
|
|
return mType;
|
|
}
|
|
|
|
QString Address::typeLabel() const
|
|
{
|
|
QString label;
|
|
bool first = true;
|
|
|
|
const TypeList list = typeList();
|
|
|
|
TypeList::ConstIterator it;
|
|
for ( it = list.begin(); it != list.end(); ++it ) {
|
|
if ( ( type() & (*it) ) && ( (*it) != Pref ) ) {
|
|
label.append( ( first ? "" : "/" ) + typeLabel( *it ) );
|
|
if ( first )
|
|
first = false;
|
|
}
|
|
}
|
|
|
|
return label;
|
|
}
|
|
|
|
void Address::setPostOfficeBox( const QString &s )
|
|
{
|
|
mEmpty = false;
|
|
|
|
mPostOfficeBox = s;
|
|
}
|
|
|
|
QString Address::postOfficeBox() const
|
|
{
|
|
return mPostOfficeBox;
|
|
}
|
|
|
|
QString Address::postOfficeBoxLabel()
|
|
{
|
|
return i18n("Post Office Box");
|
|
}
|
|
|
|
|
|
void Address::setExtended( const QString &s )
|
|
{
|
|
mEmpty = false;
|
|
|
|
mExtended = s;
|
|
}
|
|
|
|
QString Address::extended() const
|
|
{
|
|
return mExtended;
|
|
}
|
|
|
|
QString Address::extendedLabel()
|
|
{
|
|
return i18n("Extended Address Information");
|
|
}
|
|
|
|
|
|
void Address::setStreet( const QString &s )
|
|
{
|
|
mEmpty = false;
|
|
|
|
mStreet = s;
|
|
}
|
|
|
|
QString Address::street() const
|
|
{
|
|
return mStreet;
|
|
}
|
|
|
|
QString Address::streetLabel()
|
|
{
|
|
return i18n("Street");
|
|
}
|
|
|
|
|
|
void Address::setLocality( const QString &s )
|
|
{
|
|
mEmpty = false;
|
|
|
|
mLocality = s;
|
|
}
|
|
|
|
QString Address::locality() const
|
|
{
|
|
return mLocality;
|
|
}
|
|
|
|
QString Address::localityLabel()
|
|
{
|
|
return i18n("Locality");
|
|
}
|
|
|
|
|
|
void Address::setRegion( const QString &s )
|
|
{
|
|
mEmpty = false;
|
|
|
|
mRegion = s;
|
|
}
|
|
|
|
QString Address::region() const
|
|
{
|
|
return mRegion;
|
|
}
|
|
|
|
QString Address::regionLabel()
|
|
{
|
|
return i18n("Region");
|
|
}
|
|
|
|
|
|
void Address::setPostalCode( const QString &s )
|
|
{
|
|
mEmpty = false;
|
|
|
|
mPostalCode = s;
|
|
}
|
|
|
|
QString Address::postalCode() const
|
|
{
|
|
return mPostalCode;
|
|
}
|
|
|
|
QString Address::postalCodeLabel()
|
|
{
|
|
return i18n("Postal Code");
|
|
}
|
|
|
|
|
|
void Address::setCountry( const QString &s )
|
|
{
|
|
mEmpty = false;
|
|
|
|
mCountry = s;
|
|
}
|
|
|
|
QString Address::country() const
|
|
{
|
|
return mCountry;
|
|
}
|
|
|
|
QString Address::countryLabel()
|
|
{
|
|
return i18n("Country");
|
|
}
|
|
|
|
|
|
void Address::setLabel( const QString &s )
|
|
{
|
|
mEmpty = false;
|
|
|
|
mLabel = s;
|
|
}
|
|
|
|
QString Address::label() const
|
|
{
|
|
return mLabel;
|
|
}
|
|
|
|
QString Address::labelLabel()
|
|
{
|
|
return i18n("Delivery Label");
|
|
}
|
|
|
|
Address::TypeList Address::typeList()
|
|
{
|
|
static TypeList list;
|
|
|
|
if ( list.isEmpty() )
|
|
list << Dom << Intl << Postal << Parcel << Home << Work << Pref;
|
|
|
|
return list;
|
|
}
|
|
|
|
QString Address::typeLabel( int type )
|
|
{
|
|
if ( type & Pref )
|
|
return i18n( "Preferred address", "Preferred" );
|
|
|
|
switch ( type ) {
|
|
case Dom:
|
|
return i18n("Domestic");
|
|
break;
|
|
case Intl:
|
|
return i18n("International");
|
|
break;
|
|
case Postal:
|
|
return i18n("Postal");
|
|
break;
|
|
case Parcel:
|
|
return i18n("Parcel");
|
|
break;
|
|
case Home:
|
|
return i18n("Home Address", "Home");
|
|
break;
|
|
case Work:
|
|
return i18n("Work Address", "Work");
|
|
break;
|
|
case Pref:
|
|
return i18n("Preferred Address");
|
|
break;
|
|
default:
|
|
return i18n("Other");
|
|
break;
|
|
}
|
|
}
|
|
|
|
void Address::dump() const
|
|
{
|
|
kdDebug(5700) << " Address {" << endl;
|
|
kdDebug(5700) << " Id: " << id() << endl;
|
|
kdDebug(5700) << " Extended: " << extended() << endl;
|
|
kdDebug(5700) << " Street: " << street() << endl;
|
|
kdDebug(5700) << " Postal Code: " << postalCode() << endl;
|
|
kdDebug(5700) << " Locality: " << locality() << endl;
|
|
kdDebug(5700) << " }" << endl;
|
|
}
|
|
|
|
|
|
QString Address::formattedAddress( const QString &realName,
|
|
const QString &orgaName ) const
|
|
{
|
|
QString ciso;
|
|
QString addrTemplate;
|
|
QString ret;
|
|
|
|
// FIXME: first check for iso-country-field and prefer that one
|
|
if ( !country().isEmpty() ) {
|
|
ciso = countryToISO( country() );
|
|
} else {
|
|
// fall back to our own country
|
|
ciso = KGlobal::locale()->country();
|
|
}
|
|
KSimpleConfig entry( locate( "locale",
|
|
QString( "l10n/" ) + ciso + QString( "/entry.desktop" ) ) );
|
|
entry.setGroup( "KCM Locale" );
|
|
|
|
// decide whether this needs special business address formatting
|
|
if ( orgaName.isEmpty() ) {
|
|
addrTemplate = entry.readEntry( "AddressFormat" );
|
|
} else {
|
|
addrTemplate = entry.readEntry( "BusinessAddressFormat" );
|
|
if ( addrTemplate.isEmpty() )
|
|
addrTemplate = entry.readEntry( "AddressFormat" );
|
|
}
|
|
|
|
// in the case there's no format found at all, default to what we've always
|
|
// used:
|
|
if ( addrTemplate.isEmpty() ) {
|
|
kdWarning(5700) << "address format database incomplete "
|
|
<< "(no format for locale " << ciso
|
|
<< " found). Using default address formatting." << endl;
|
|
addrTemplate = "%0(%n\\n)%0(%cm\\n)%0(%s\\n)%0(PO BOX %p\\n)%0(%l%w%r)%,%z";
|
|
}
|
|
|
|
// scan
|
|
parseAddressTemplateSection( addrTemplate, ret, realName, orgaName );
|
|
|
|
// now add the country line if needed (formatting this time according to
|
|
// the rules of our own system country )
|
|
if ( !country().isEmpty() ) {
|
|
KSimpleConfig entry( locate( "locale", QString( "l10n/" )
|
|
+ KGlobal::locale()->country() + QString( "/entry.desktop" ) ) );
|
|
entry.setGroup( "KCM Locale" );
|
|
QString cpos = entry.readEntry( "AddressCountryPosition" );
|
|
if ( "BELOW" == cpos || cpos.isEmpty() ) {
|
|
ret = ret + "\n\n" + country().upper();
|
|
} else if ( "below" == cpos ) {
|
|
ret = ret + "\n\n" + country();
|
|
} else if ( "ABOVE" == cpos ) {
|
|
ret = country().upper() + "\n\n" + ret;
|
|
} else if ( "above" == cpos ) {
|
|
ret = country() + "\n\n" + ret;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
bool Address::parseAddressTemplateSection( const QString &tsection,
|
|
QString &result, const QString &realName, const QString &orgaName ) const
|
|
{
|
|
// This method first parses and substitutes any bracketed sections and
|
|
// after that replaces any tags with their values. If a bracketed section
|
|
// or a tag evaluate to zero, they are not just removed but replaced
|
|
// with a placeholder. This is because in the last step conditionals are
|
|
// resolved which depend on information about zero-evaluations.
|
|
result = tsection;
|
|
int stpos = 0;
|
|
bool ret = false;
|
|
|
|
// first check for brackets that have to be evaluated first
|
|
int fpos = result.find( KABC_FMTTAG_purgeempty, stpos );
|
|
while ( -1 != fpos ) {
|
|
int bpos1 = fpos + KABC_FMTTAG_purgeempty.length();
|
|
int bpos2;
|
|
// expect opening bracket and find next balanced closing bracket. If
|
|
// next char is no opening bracket, continue parsing (no valid tag)
|
|
if ( '(' == result[bpos1] ) {
|
|
bpos2 = findBalancedBracket( result, bpos1 );
|
|
if ( -1 != bpos2 ) {
|
|
// we have balanced brackets, recursively parse:
|
|
QString rplstr;
|
|
bool purge = !parseAddressTemplateSection( result.mid( bpos1+1,
|
|
bpos2-bpos1-1 ), rplstr,
|
|
realName, orgaName );
|
|
if ( purge ) {
|
|
// purge -> remove all
|
|
// replace with !_P_!, so conditional tags work later
|
|
result.replace( fpos, bpos2 - fpos + 1, "!_P_!" );
|
|
// leave stpos as it is
|
|
} else {
|
|
// no purge -> replace with recursively parsed string
|
|
result.replace( fpos, bpos2 - fpos + 1, rplstr );
|
|
ret = true;
|
|
stpos = fpos + rplstr.length();
|
|
}
|
|
} else {
|
|
// unbalanced brackets: keep on parsing (should not happen
|
|
// and will result in bad formatting)
|
|
stpos = bpos1;
|
|
}
|
|
}
|
|
fpos = result.find( KABC_FMTTAG_purgeempty, stpos );
|
|
}
|
|
|
|
// after sorting out all purge tags, we just search'n'replace the rest,
|
|
// keeping track of whether at least one tag evaluates to something.
|
|
// The following macro needs QString for R_FIELD
|
|
// It substitutes !_P_! for empty fields so conditional tags work later
|
|
#define REPLTAG(R_TAG,R_FIELD) \
|
|
if ( result.find(R_TAG, false) != -1 ) { \
|
|
QString rpl = R_FIELD.isEmpty() ? QString("!_P_!") : R_FIELD; \
|
|
result.replace( R_TAG, rpl ); \
|
|
if ( !R_FIELD.isEmpty() ) { \
|
|
ret = true; \
|
|
} \
|
|
}
|
|
REPLTAG( KABC_FMTTAG_realname, realName );
|
|
REPLTAG( KABC_FMTTAG_REALNAME, realName.upper() );
|
|
REPLTAG( KABC_FMTTAG_company, orgaName );
|
|
REPLTAG( KABC_FMTTAG_COMPANY, orgaName.upper() );
|
|
REPLTAG( KABC_FMTTAG_pobox, postOfficeBox() );
|
|
REPLTAG( KABC_FMTTAG_street, street() );
|
|
REPLTAG( KABC_FMTTAG_STREET, street().upper() );
|
|
REPLTAG( KABC_FMTTAG_zipcode, postalCode() );
|
|
REPLTAG( KABC_FMTTAG_location, locality() );
|
|
REPLTAG( KABC_FMTTAG_LOCATION, locality().upper() );
|
|
REPLTAG( KABC_FMTTAG_region, region() );
|
|
REPLTAG( KABC_FMTTAG_REGION, region().upper() );
|
|
result.replace( KABC_FMTTAG_newline, "\n" );
|
|
#undef REPLTAG
|
|
|
|
// conditional comma
|
|
fpos = result.find( KABC_FMTTAG_condcomma, 0 );
|
|
while ( -1 != fpos ) {
|
|
QString str1 = result.mid( fpos - 5, 5 );
|
|
QString str2 = result.mid( fpos + 2, 5 );
|
|
if ( str1 != "!_P_!" && str2 != "!_P_!" ) {
|
|
result.replace( fpos, 2, ", " );
|
|
} else {
|
|
result.remove( fpos, 2 );
|
|
}
|
|
fpos = result.find( KABC_FMTTAG_condcomma, fpos );
|
|
}
|
|
// conditional whitespace
|
|
fpos = result.find( KABC_FMTTAG_condwhite, 0 );
|
|
while ( -1 != fpos ) {
|
|
QString str1 = result.mid( fpos - 5, 5 );
|
|
QString str2 = result.mid( fpos + 2, 5 );
|
|
if ( str1 != "!_P_!" && str2 != "!_P_!" ) {
|
|
result.replace( fpos, 2, " " );
|
|
} else {
|
|
result.remove( fpos, 2 );
|
|
}
|
|
fpos = result.find( KABC_FMTTAG_condwhite, fpos );
|
|
}
|
|
|
|
// remove purged:
|
|
result.remove( "!_P_!" );
|
|
|
|
return ret;
|
|
}
|
|
|
|
int Address::findBalancedBracket( const QString &tsection, int pos ) const
|
|
{
|
|
int balancecounter = 0;
|
|
for( unsigned int i = pos + 1; i < tsection.length(); i++ ) {
|
|
if ( ')' == tsection[i] && 0 == balancecounter ) {
|
|
// found end of brackets
|
|
return i;
|
|
} else
|
|
if ( '(' == tsection[i] ) {
|
|
// nested brackets
|
|
balancecounter++;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
QString Address::countryToISO( const QString &cname )
|
|
{
|
|
// we search a map file for translations from country names to
|
|
// iso codes, storing caching things in a QMap for faster future
|
|
// access.
|
|
if ( !mISOMap )
|
|
isoMapDeleter.setObject( mISOMap, new QMap<QString, QString>() );
|
|
|
|
QMap<QString, QString>::ConstIterator it;
|
|
it = mISOMap->find( cname );
|
|
if ( it != mISOMap->end() )
|
|
return it.data();
|
|
|
|
QString mapfile = KGlobal::dirs()->findResource( "data",
|
|
QString::fromLatin1( "kabc/countrytransl.map" ) );
|
|
|
|
QFile file( mapfile );
|
|
if ( file.open( IO_ReadOnly ) ) {
|
|
QTextStream s( &file );
|
|
QString strbuf = s.readLine();
|
|
while( !strbuf.isEmpty() ) {
|
|
QStringList countryInfo = QStringList::split( '\t', strbuf, true );
|
|
if ( countryInfo[ 0 ] == cname ) {
|
|
file.close();
|
|
mISOMap->insert( cname, countryInfo[ 1 ] );
|
|
return countryInfo[ 1 ];
|
|
}
|
|
strbuf = s.readLine();
|
|
}
|
|
file.close();
|
|
}
|
|
|
|
// fall back to system country
|
|
mISOMap->insert( cname, KGlobal::locale()->country() );
|
|
return KGlobal::locale()->country();
|
|
}
|
|
|
|
QString Address::ISOtoCountry( const QString &ISOname )
|
|
{
|
|
// get country name from ISO country code (e.g. "no" -> i18n("Norway"))
|
|
if ( ISOname.simplifyWhiteSpace().isEmpty() )
|
|
return QString::null;
|
|
|
|
QString mapfile = KGlobal::dirs()->findResource( "data",
|
|
QString::fromLatin1( "kabc/countrytransl.map" ) );
|
|
|
|
QFile file( mapfile );
|
|
if ( file.open( IO_ReadOnly ) ) {
|
|
QTextStream s( &file );
|
|
QString searchStr = "\t" + ISOname.simplifyWhiteSpace().lower();
|
|
QString strbuf = s.readLine();
|
|
int pos;
|
|
while ( !strbuf.isEmpty() ) {
|
|
if ( (pos = strbuf.find( searchStr )) != -1 ) {
|
|
file.close();
|
|
return i18n( strbuf.left( pos ).utf8() );
|
|
}
|
|
strbuf = s.readLine();
|
|
}
|
|
file.close();
|
|
}
|
|
|
|
return ISOname;
|
|
}
|
|
|
|
QDataStream &KABC::operator<<( QDataStream &s, const Address &addr )
|
|
{
|
|
return s << addr.mId << addr.mType << addr.mPostOfficeBox <<
|
|
addr.mExtended << addr.mStreet << addr.mLocality <<
|
|
addr.mRegion << addr.mPostalCode << addr.mCountry <<
|
|
addr.mLabel;
|
|
}
|
|
|
|
QDataStream &KABC::operator>>( QDataStream &s, Address &addr )
|
|
{
|
|
s >> addr.mId >> addr.mType >> addr.mPostOfficeBox >> addr.mExtended >>
|
|
addr.mStreet >> addr.mLocality >> addr.mRegion >>
|
|
addr.mPostalCode >> addr.mCountry >> addr.mLabel;
|
|
|
|
addr.mEmpty = false;
|
|
|
|
return s;
|
|
}
|