00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include "address.h"
00022
00023 #include <krandom.h>
00024 #include <kdebug.h>
00025 #include <klocale.h>
00026 #include <kconfig.h>
00027 #include <kstandarddirs.h>
00028 #include <kconfiggroup.h>
00029
00030 #include <QtCore/QFile>
00031 #include <QtCore/QMap>
00032 #include <QtCore/QTextStream>
00033 #include <QtCore/QSharedData>
00034
00035 using namespace KABC;
00036
00037
00038 #define KABC_FMTTAG_realname QString("%n")
00039 #define KABC_FMTTAG_REALNAME QString("%N")
00040 #define KABC_FMTTAG_company QString("%cm")
00041 #define KABC_FMTTAG_COMPANY QString("%CM")
00042 #define KABC_FMTTAG_pobox QString("%p")
00043 #define KABC_FMTTAG_street QString("%s")
00044 #define KABC_FMTTAG_STREET QString("%S")
00045 #define KABC_FMTTAG_zipcode QString("%z")
00046 #define KABC_FMTTAG_location QString("%l")
00047 #define KABC_FMTTAG_LOCATION QString("%L")
00048 #define KABC_FMTTAG_region QString("%r")
00049 #define KABC_FMTTAG_REGION QString("%R")
00050 #define KABC_FMTTAG_newline QString("\\n")
00051 #define KABC_FMTTAG_condcomma QString("%,")
00052 #define KABC_FMTTAG_condwhite QString("%w")
00053 #define KABC_FMTTAG_purgeempty QString("%0")
00054
00060 static int findBalancedBracket( const QString &tsection, int pos )
00061 {
00062 int balancecounter = 0;
00063 for ( int i = pos + 1; i < tsection.length(); ++i ) {
00064 if ( ')' == tsection[i] && 0 == balancecounter ) {
00065
00066 return i;
00067 } else {
00068 if ( '(' == tsection[i] ) {
00069
00070 balancecounter++;
00071 }
00072 }
00073 }
00074 return -1;
00075 }
00076
00083 static bool parseAddressTemplateSection( const QString &tsection, QString &result,
00084 const QString &realName, const QString &orgaName,
00085 const KABC::Address &address )
00086 {
00087
00088
00089
00090
00091
00092 result = tsection;
00093 int stpos = 0;
00094 bool ret = false;
00095
00096
00097 int fpos = result.indexOf( KABC_FMTTAG_purgeempty, stpos );
00098 while ( -1 != fpos ) {
00099 int bpos1 = fpos + KABC_FMTTAG_purgeempty.length();
00100 int bpos2;
00101
00102
00103 if ( '(' == result[bpos1] ) {
00104 bpos2 = findBalancedBracket( result, bpos1 );
00105 if ( -1 != bpos2 ) {
00106
00107 QString rplstr;
00108 bool purge = !parseAddressTemplateSection( result.mid( bpos1+1,
00109 bpos2-bpos1-1 ), rplstr,
00110 realName, orgaName, address );
00111 if ( purge ) {
00112
00113
00114 result.replace( fpos, bpos2 - fpos + 1, "!_P_!" );
00115
00116 } else {
00117
00118 result.replace( fpos, bpos2 - fpos + 1, rplstr );
00119 ret = true;
00120 stpos = fpos + rplstr.length();
00121 }
00122 } else {
00123
00124
00125 stpos = bpos1;
00126 }
00127 }
00128 fpos = result.indexOf( KABC_FMTTAG_purgeempty, stpos );
00129 }
00130
00131
00132
00133
00134
00135 #define REPLTAG(R_TAG,R_FIELD) \
00136 if ( result.indexOf( R_TAG, false ) != -1 ) { \
00137 QString rpl = R_FIELD.isEmpty() ? QString( "!_P_!" ) : R_FIELD; \
00138 result.replace( R_TAG, rpl ); \
00139 if ( !R_FIELD.isEmpty() ) { \
00140 ret = true; \
00141 } \
00142 }
00143 REPLTAG( KABC_FMTTAG_realname, realName );
00144 REPLTAG( KABC_FMTTAG_REALNAME, realName.toUpper() );
00145 REPLTAG( KABC_FMTTAG_company, orgaName );
00146 REPLTAG( KABC_FMTTAG_COMPANY, orgaName.toUpper() );
00147 REPLTAG( KABC_FMTTAG_pobox, address.postOfficeBox() );
00148 REPLTAG( KABC_FMTTAG_street, address.street() );
00149 REPLTAG( KABC_FMTTAG_STREET, address.street().toUpper() );
00150 REPLTAG( KABC_FMTTAG_zipcode, address.postalCode() );
00151 REPLTAG( KABC_FMTTAG_location, address.locality() );
00152 REPLTAG( KABC_FMTTAG_LOCATION, address.locality().toUpper() );
00153 REPLTAG( KABC_FMTTAG_region, address.region() );
00154 REPLTAG( KABC_FMTTAG_REGION, address.region().toUpper() );
00155 result.replace( KABC_FMTTAG_newline, "\n" );
00156 #undef REPLTAG
00157
00158
00159 fpos = result.indexOf( KABC_FMTTAG_condcomma, 0 );
00160 while ( -1 != fpos ) {
00161 QString str1 = result.mid( fpos - 5, 5 );
00162 QString str2 = result.mid( fpos + 2, 5 );
00163 if ( str1 != "!_P_!" && str2 != "!_P_!" ) {
00164 result.replace( fpos, 2, ", " );
00165 } else {
00166 result.remove( fpos, 2 );
00167 }
00168 fpos = result.indexOf( KABC_FMTTAG_condcomma, fpos );
00169 }
00170
00171 fpos = result.indexOf( KABC_FMTTAG_condwhite, 0 );
00172 while ( -1 != fpos ) {
00173 QString str1 = result.mid( fpos - 5, 5 );
00174 QString str2 = result.mid( fpos + 2, 5 );
00175 if ( str1 != "!_P_!" && str2 != "!_P_!" ) {
00176 result.replace( fpos, 2, " " );
00177 } else {
00178 result.remove( fpos, 2 );
00179 }
00180 fpos = result.indexOf( KABC_FMTTAG_condwhite, fpos );
00181 }
00182
00183
00184 result.remove( "!_P_!" );
00185
00186 return ret;
00187 }
00188
00189 class Address::Private : public QSharedData
00190 {
00191 public:
00192 Private()
00193 : mEmpty( true ), mType( 0 )
00194 {
00195 mId = KRandom::randomString( 10 );
00196 }
00197
00198 Private( const Private &other )
00199 : QSharedData( other )
00200 {
00201 mEmpty = other.mEmpty;
00202 mId = other.mId;
00203 mType = other.mType;
00204
00205 mPostOfficeBox = other.mPostOfficeBox;
00206 mExtended = other.mExtended;
00207 mStreet = other.mStreet;
00208 mLocality = other.mLocality;
00209 mRegion = other.mRegion;
00210 mPostalCode = other.mPostalCode;
00211 mCountry = other.mCountry;
00212 mLabel = other.mLabel;
00213 }
00214
00215 bool mEmpty;
00216 QString mId;
00217 Type mType;
00218
00219 QString mPostOfficeBox;
00220 QString mExtended;
00221 QString mStreet;
00222 QString mLocality;
00223 QString mRegion;
00224 QString mPostalCode;
00225 QString mCountry;
00226 QString mLabel;
00227 };
00228
00229 Address::Address()
00230 : d( new Private )
00231 {
00232 }
00233
00234 Address::Address( Type type )
00235 : d( new Private )
00236 {
00237 d->mType = type;
00238 }
00239
00240 Address::Address( const Address &other )
00241 : d( other.d )
00242 {
00243 }
00244
00245 Address::~Address()
00246 {
00247 }
00248
00249 Address &Address::operator=( const Address &other )
00250 {
00251 if ( this != &other ) {
00252 d = other.d;
00253 }
00254
00255 return *this;
00256 }
00257
00258 bool Address::operator==( const Address &other ) const
00259 {
00260 if ( d->mId != other.d->mId ) {
00261 return false;
00262 }
00263 if ( d->mType != other.d->mType ) {
00264 return false;
00265 }
00266 if ( d->mPostOfficeBox != other.d->mPostOfficeBox ) {
00267 return false;
00268 }
00269 if ( d->mExtended != other.d->mExtended ) {
00270 return false;
00271 }
00272 if ( d->mStreet != other.d->mStreet ) {
00273 return false;
00274 }
00275 if ( d->mLocality != other.d->mLocality ) {
00276 return false;
00277 }
00278 if ( d->mRegion != other.d->mRegion ) {
00279 return false;
00280 }
00281 if ( d->mPostalCode != other.d->mPostalCode ) {
00282 return false;
00283 }
00284 if ( d->mCountry != other.d->mCountry ) {
00285 return false;
00286 }
00287 if ( d->mLabel != other.d->mLabel ) {
00288 return false;
00289 }
00290
00291 return true;
00292 }
00293
00294 bool Address::operator!=( const Address &a ) const
00295 {
00296 return !( a == *this );
00297 }
00298
00299 bool Address::isEmpty() const
00300 {
00301 return d->mEmpty;
00302 }
00303
00304 void Address::clear()
00305 {
00306 *this = Address();
00307 }
00308
00309 void Address::setId( const QString &id )
00310 {
00311 d->mEmpty = false;
00312 d->mId = id;
00313 }
00314
00315 QString Address::id() const
00316 {
00317 return d->mId;
00318 }
00319
00320 void Address::setType( Type type )
00321 {
00322 d->mEmpty = false;
00323 d->mType = type;
00324 }
00325
00326 Address::Type Address::type() const
00327 {
00328 return d->mType;
00329 }
00330
00331 QString Address::typeLabel() const
00332 {
00333 QString label;
00334 bool first = true;
00335
00336 const TypeList list = typeList();
00337
00338 TypeList::ConstIterator it;
00339 for ( it = list.begin(); it != list.end(); ++it ) {
00340 if ( ( type() & (*it) ) && ( (*it) != Pref ) ) {
00341 label.append( ( first ? "" : "/" ) + typeLabel( *it ) );
00342 if ( first ) {
00343 first = false;
00344 }
00345 }
00346 }
00347
00348 return label;
00349 }
00350
00351 void Address::setPostOfficeBox( const QString &postOfficeBox )
00352 {
00353 d->mEmpty = false;
00354 d->mPostOfficeBox = postOfficeBox;
00355 }
00356
00357 QString Address::postOfficeBox() const
00358 {
00359 return d->mPostOfficeBox;
00360 }
00361
00362 QString Address::postOfficeBoxLabel()
00363 {
00364 return i18n( "Post Office Box" );
00365 }
00366
00367 void Address::setExtended( const QString &extended )
00368 {
00369 d->mEmpty = false;
00370 d->mExtended = extended;
00371 }
00372
00373 QString Address::extended() const
00374 {
00375 return d->mExtended;
00376 }
00377
00378 QString Address::extendedLabel()
00379 {
00380 return i18n( "Extended Address Information" );
00381 }
00382
00383 void Address::setStreet( const QString &street )
00384 {
00385 d->mEmpty = false;
00386 d->mStreet = street;
00387 }
00388
00389 QString Address::street() const
00390 {
00391 return d->mStreet;
00392 }
00393
00394 QString Address::streetLabel()
00395 {
00396 return i18n( "Street" );
00397 }
00398
00399 void Address::setLocality( const QString &locality )
00400 {
00401 d->mEmpty = false;
00402 d->mLocality = locality;
00403 }
00404
00405 QString Address::locality() const
00406 {
00407 return d->mLocality;
00408 }
00409
00410 QString Address::localityLabel()
00411 {
00412 return i18n( "Locality" );
00413 }
00414
00415 void Address::setRegion( const QString ®ion )
00416 {
00417 d->mEmpty = false;
00418 d->mRegion = region;
00419 }
00420
00421 QString Address::region() const
00422 {
00423 return d->mRegion;
00424 }
00425
00426 QString Address::regionLabel()
00427 {
00428 return i18n( "Region" );
00429 }
00430
00431 void Address::setPostalCode( const QString &postalCode )
00432 {
00433 d->mEmpty = false;
00434 d->mPostalCode = postalCode;
00435 }
00436
00437 QString Address::postalCode() const
00438 {
00439 return d->mPostalCode;
00440 }
00441
00442 QString Address::postalCodeLabel()
00443 {
00444 return i18n( "Postal Code" );
00445 }
00446
00447 void Address::setCountry( const QString &country )
00448 {
00449 d->mEmpty = false;
00450 d->mCountry = country;
00451 }
00452
00453 QString Address::country() const
00454 {
00455 return d->mCountry;
00456 }
00457
00458 QString Address::countryLabel()
00459 {
00460 return i18n( "Country" );
00461 }
00462
00463 void Address::setLabel( const QString &label )
00464 {
00465 d->mEmpty = false;
00466 d->mLabel = label;
00467 }
00468
00469 QString Address::label() const
00470 {
00471 return d->mLabel;
00472 }
00473
00474 QString Address::labelLabel()
00475 {
00476 return i18n( "Delivery Label" );
00477 }
00478
00479 Address::TypeList Address::typeList()
00480 {
00481 static TypeList list;
00482
00483 if ( list.isEmpty() ) {
00484 list << Dom << Intl << Postal << Parcel << Home << Work << Pref;
00485 }
00486
00487 return list;
00488 }
00489
00490 QString Address::typeLabel( Type type )
00491 {
00492 if ( type & Pref ) {
00493 return i18nc( "Preferred address", "Preferred" );
00494 }
00495
00496 switch ( type ) {
00497 case Dom:
00498 return i18nc( "Address is in home country", "Domestic" );
00499 break;
00500 case Intl:
00501 return i18nc( "Address is not in home country", "International" );
00502 break;
00503 case Postal:
00504 return i18nc( "Address for delivering letters", "Postal" );
00505 break;
00506 case Parcel:
00507 return i18nc( "Address for delivering packages", "Parcel" );
00508 break;
00509 case Home:
00510 return i18nc( "Home Address", "Home" );
00511 break;
00512 case Work:
00513 return i18nc( "Work Address", "Work" );
00514 break;
00515 case Pref:
00516 return i18n( "Preferred Address" );
00517 break;
00518 default:
00519 return i18nc( "another type of address", "Other" );
00520 break;
00521 }
00522 }
00523
00524 QString Address::toString() const
00525 {
00526 QString str;
00527
00528 str += QString( "Address {\n" );
00529 str += QString( " IsEmpty: %1\n" ).arg( d->mEmpty ? "true" : "false" );
00530 str += QString( " Id: %1\n" ).arg( d->mId );
00531 str += QString( " Type: %1\n" ).arg( typeLabel( d->mType ) );
00532 str += QString( " Post Office Box: %1\n" ).arg( d->mPostOfficeBox );
00533 str += QString( " Extended: %1\n" ).arg( d->mExtended );
00534 str += QString( " Street: %1\n" ).arg( d->mStreet );
00535 str += QString( " Locality: %1\n" ).arg( d->mLocality );
00536 str += QString( " Region: %1\n" ).arg( d->mRegion );
00537 str += QString( " Postal Code: %1\n" ).arg( d->mPostalCode );
00538 str += QString( " Country: %1\n" ).arg( d->mCountry );
00539 str += QString( " Label: %1\n" ).arg( d->mLabel );
00540 str += QString( "}\n" );
00541
00542 return str;
00543 }
00544
00545 QString Address::formattedAddress( const QString &realName,
00546 const QString &orgaName ) const
00547 {
00548 QString ciso;
00549 QString addrTemplate;
00550 QString ret;
00551
00552
00553 if ( !country().isEmpty() ) {
00554 ciso = countryToISO( country() );
00555 } else {
00556
00557 ciso = KGlobal::locale()->country();
00558 }
00559 KConfig entry( KStandardDirs::locate( "locale",
00560 QString( "l10n/" ) + ciso + QString( "/entry.desktop" ) ) );
00561
00562 KConfigGroup group = entry.group( "KCM Locale" );
00563
00564 if ( orgaName.isEmpty() ) {
00565 addrTemplate = group.readEntry( "AddressFormat" );
00566 } else {
00567 addrTemplate = group.readEntry( "BusinessAddressFormat" );
00568 if ( addrTemplate.isEmpty() ) {
00569 addrTemplate = group.readEntry( "AddressFormat" );
00570 }
00571 }
00572
00573
00574
00575 if ( addrTemplate.isEmpty() ) {
00576 kWarning(5700) << "address format database incomplete"
00577 << "(no format for locale" << ciso
00578 << "found). Using default address formatting.";
00579 addrTemplate = "%0(%n\\n)%0(%cm\\n)%0(%s\\n)%0(PO BOX %p\\n)%0(%l%w%r)%,%z";
00580 }
00581
00582
00583 parseAddressTemplateSection( addrTemplate, ret, realName, orgaName, *this );
00584
00585
00586
00587 if ( !country().isEmpty() ) {
00588 KConfig entry( KStandardDirs::locate( "locale", QString( "l10n/" ) +
00589 KGlobal::locale()->country() +
00590 QString( "/entry.desktop" ) ) );
00591 KConfigGroup group = entry.group( "KCM Locale" );
00592 QString cpos = group.readEntry( "AddressCountryPosition" );
00593 if ( "BELOW" == cpos || cpos.isEmpty() ) {
00594 ret = ret + "\n\n" + country().toUpper();
00595 } else if ( "below" == cpos ) {
00596 ret = ret + "\n\n" + country();
00597 } else if ( "ABOVE" == cpos ) {
00598 ret = country().toUpper() + "\n\n" + ret;
00599 } else if ( "above" == cpos ) {
00600 ret = country() + "\n\n" + ret;
00601 }
00602 }
00603
00604 return ret;
00605 }
00606
00607 QString Address::countryToISO( const QString &cname )
00608 {
00609
00610
00611
00612 typedef QMap<QString, QString> stringMap;
00613 K_GLOBAL_STATIC( stringMap, sISOMap )
00614
00615 QMap<QString, QString>::ConstIterator it;
00616 it = sISOMap->constFind( cname );
00617 if ( it != sISOMap->constEnd() ) {
00618 return it.value();
00619 }
00620
00621 QString mapfile = KGlobal::dirs()->findResource( "data",
00622 QLatin1String( "kabc/countrytransl.map" ) );
00623
00624 QFile file( mapfile );
00625 if ( file.open( QIODevice::ReadOnly ) ) {
00626 QTextStream s( &file );
00627 QString strbuf = s.readLine();
00628 while ( !strbuf.isEmpty() ) {
00629 QStringList countryInfo = strbuf.split( '\t', QString::KeepEmptyParts );
00630 if ( countryInfo[ 0 ] == cname ) {
00631 file.close();
00632 sISOMap->insert( cname, countryInfo[ 1 ] );
00633 return countryInfo[ 1 ];
00634 }
00635 strbuf = s.readLine();
00636 }
00637 file.close();
00638 }
00639
00640
00641 sISOMap->insert( cname, KGlobal::locale()->country() );
00642 return KGlobal::locale()->country();
00643 }
00644
00645 QString Address::ISOtoCountry( const QString &ISOname )
00646 {
00647
00648 if ( ISOname.simplified().isEmpty() ) {
00649 return QString();
00650 }
00651
00652 QString mapfile = KGlobal::dirs()->findResource( "data",
00653 QLatin1String( "kabc/countrytransl.map" ) );
00654
00655 QFile file( mapfile );
00656 if ( file.open( QIODevice::ReadOnly ) ) {
00657 QTextStream s( &file );
00658 QString searchStr = '\t' + ISOname.simplified().toLower();
00659 QString strbuf = s.readLine();
00660 int pos;
00661 while ( !strbuf.isEmpty() ) {
00662 if ( ( pos = strbuf.indexOf( searchStr ) ) != -1 ) {
00663 file.close();
00664 return i18n( strbuf.left( pos ).toUtf8() );
00665 }
00666 strbuf = s.readLine();
00667 }
00668 file.close();
00669 }
00670
00671 return ISOname;
00672 }
00673
00674 QDataStream &KABC::operator<<( QDataStream &s, const Address &addr )
00675 {
00676 return s << addr.d->mId << (uint)addr.d->mType << addr.d->mPostOfficeBox
00677 << addr.d->mExtended << addr.d->mStreet << addr.d->mLocality
00678 << addr.d->mRegion << addr.d->mPostalCode << addr.d->mCountry
00679 << addr.d->mLabel << addr.d->mEmpty;
00680 }
00681
00682 QDataStream &KABC::operator>>( QDataStream &s, Address &addr )
00683 {
00684 uint type;
00685 s >> addr.d->mId >> type >> addr.d->mPostOfficeBox >> addr.d->mExtended
00686 >> addr.d->mStreet >> addr.d->mLocality >> addr.d->mRegion
00687 >> addr.d->mPostalCode >> addr.d->mCountry >> addr.d->mLabel
00688 >> addr.d->mEmpty;
00689
00690 addr.d->mType = Address::Type( type );
00691
00692 return s;
00693 }