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::fromLatin1("%n")
00039 #define KABC_FMTTAG_REALNAME QString::fromLatin1("%N")
00040 #define KABC_FMTTAG_company QString::fromLatin1("%cm")
00041 #define KABC_FMTTAG_COMPANY QString::fromLatin1("%CM")
00042 #define KABC_FMTTAG_pobox QString::fromLatin1("%p")
00043 #define KABC_FMTTAG_street QString::fromLatin1("%s")
00044 #define KABC_FMTTAG_STREET QString::fromLatin1("%S")
00045 #define KABC_FMTTAG_zipcode QString::fromLatin1("%z")
00046 #define KABC_FMTTAG_location QString::fromLatin1("%l")
00047 #define KABC_FMTTAG_LOCATION QString::fromLatin1("%L")
00048 #define KABC_FMTTAG_region QString::fromLatin1("%r")
00049 #define KABC_FMTTAG_REGION QString::fromLatin1("%R")
00050 #define KABC_FMTTAG_newline QString::fromLatin1("\\n")
00051 #define KABC_FMTTAG_condcomma QString::fromLatin1("%,")
00052 #define KABC_FMTTAG_condwhite QString::fromLatin1("%w")
00053 #define KABC_FMTTAG_purgeempty QString::fromLatin1("%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 ( QLatin1Char( ')' ) == tsection[i] && 0 == balancecounter ) {
00065
00066 return i;
00067 } else {
00068 if ( QLatin1Char( '(' ) == 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 ( QLatin1Char( '(' ) == 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, QLatin1String( "!_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() ? QLatin1String( "!_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, QLatin1String( "\n" ) );
00156 #undef REPLTAG
00157
00158
00159 fpos = result.indexOf( KABC_FMTTAG_condcomma, 0 );
00160 while ( -1 != fpos ) {
00161 const QString str1 = result.mid( fpos - 5, 5 );
00162 const QString str2 = result.mid( fpos + 2, 5 );
00163 if ( str1 != QLatin1String( "!_P_!" ) && str2 != QLatin1String( "!_P_!" ) ) {
00164 result.replace( fpos, 2, QLatin1String( ", " ) );
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 const QString str1 = result.mid( fpos - 5, 5 );
00174 const QString str2 = result.mid( fpos + 2, 5 );
00175 if ( str1 != QLatin1String( "!_P_!" ) && str2 != QLatin1String( "!_P_!" ) ) {
00176 result.replace( fpos, 2, QLatin1String( " " ) );
00177 } else {
00178 result.remove( fpos, 2 );
00179 }
00180 fpos = result.indexOf( KABC_FMTTAG_condwhite, fpos );
00181 }
00182
00183
00184 result.remove( QLatin1String( "!_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 if ( !first ) {
00342 label.append( QLatin1Char( '/' ) );
00343 }
00344 label.append( typeLabel( *it ) );
00345 if ( first ) {
00346 first = false;
00347 }
00348 }
00349 }
00350
00351 return label;
00352 }
00353
00354 void Address::setPostOfficeBox( const QString &postOfficeBox )
00355 {
00356 d->mEmpty = false;
00357 d->mPostOfficeBox = postOfficeBox;
00358 }
00359
00360 QString Address::postOfficeBox() const
00361 {
00362 return d->mPostOfficeBox;
00363 }
00364
00365 QString Address::postOfficeBoxLabel()
00366 {
00367 return i18n( "Post Office Box" );
00368 }
00369
00370 void Address::setExtended( const QString &extended )
00371 {
00372 d->mEmpty = false;
00373 d->mExtended = extended;
00374 }
00375
00376 QString Address::extended() const
00377 {
00378 return d->mExtended;
00379 }
00380
00381 QString Address::extendedLabel()
00382 {
00383 return i18n( "Extended Address Information" );
00384 }
00385
00386 void Address::setStreet( const QString &street )
00387 {
00388 d->mEmpty = false;
00389 d->mStreet = street;
00390 }
00391
00392 QString Address::street() const
00393 {
00394 return d->mStreet;
00395 }
00396
00397 QString Address::streetLabel()
00398 {
00399 return i18n( "Street" );
00400 }
00401
00402 void Address::setLocality( const QString &locality )
00403 {
00404 d->mEmpty = false;
00405 d->mLocality = locality;
00406 }
00407
00408 QString Address::locality() const
00409 {
00410 return d->mLocality;
00411 }
00412
00413 QString Address::localityLabel()
00414 {
00415 return i18n( "Locality" );
00416 }
00417
00418 void Address::setRegion( const QString ®ion )
00419 {
00420 d->mEmpty = false;
00421 d->mRegion = region;
00422 }
00423
00424 QString Address::region() const
00425 {
00426 return d->mRegion;
00427 }
00428
00429 QString Address::regionLabel()
00430 {
00431 return i18n( "Region" );
00432 }
00433
00434 void Address::setPostalCode( const QString &postalCode )
00435 {
00436 d->mEmpty = false;
00437 d->mPostalCode = postalCode;
00438 }
00439
00440 QString Address::postalCode() const
00441 {
00442 return d->mPostalCode;
00443 }
00444
00445 QString Address::postalCodeLabel()
00446 {
00447 return i18n( "Postal Code" );
00448 }
00449
00450 void Address::setCountry( const QString &country )
00451 {
00452 d->mEmpty = false;
00453 d->mCountry = country;
00454 }
00455
00456 QString Address::country() const
00457 {
00458 return d->mCountry;
00459 }
00460
00461 QString Address::countryLabel()
00462 {
00463 return i18n( "Country" );
00464 }
00465
00466 void Address::setLabel( const QString &label )
00467 {
00468 d->mEmpty = false;
00469 d->mLabel = label;
00470 }
00471
00472 QString Address::label() const
00473 {
00474 return d->mLabel;
00475 }
00476
00477 QString Address::labelLabel()
00478 {
00479 return i18n( "Delivery Label" );
00480 }
00481
00482 Address::TypeList Address::typeList()
00483 {
00484 static TypeList list;
00485
00486 if ( list.isEmpty() ) {
00487 list << Dom << Intl << Postal << Parcel << Home << Work << Pref;
00488 }
00489
00490 return list;
00491 }
00492
00493 QString Address::typeLabel( Type type )
00494 {
00495 if ( type & Pref ) {
00496 return i18nc( "Preferred address", "Preferred" );
00497 }
00498
00499 switch ( type ) {
00500 case Dom:
00501 return i18nc( "Address is in home country", "Domestic" );
00502 break;
00503 case Intl:
00504 return i18nc( "Address is not in home country", "International" );
00505 break;
00506 case Postal:
00507 return i18nc( "Address for delivering letters", "Postal" );
00508 break;
00509 case Parcel:
00510 return i18nc( "Address for delivering packages", "Parcel" );
00511 break;
00512 case Home:
00513 return i18nc( "Home Address", "Home" );
00514 break;
00515 case Work:
00516 return i18nc( "Work Address", "Work" );
00517 break;
00518 case Pref:
00519 return i18n( "Preferred Address" );
00520 break;
00521 default:
00522 return i18nc( "another type of address", "Other" );
00523 break;
00524 }
00525 }
00526
00527 QString Address::toString() const
00528 {
00529 QString str;
00530
00531 str += QLatin1String( "Address {\n" );
00532 str += QString::fromLatin1( " IsEmpty: %1\n" ).
00533 arg( d->mEmpty ? QLatin1String( "true" ) : QLatin1String( "false" ) );
00534 str += QString::fromLatin1( " Id: %1\n" ).arg( d->mId );
00535 str += QString::fromLatin1( " Type: %1\n" ).arg( typeLabel( d->mType ) );
00536 str += QString::fromLatin1( " Post office box: %1\n" ).arg( d->mPostOfficeBox );
00537 str += QString::fromLatin1( " Extended: %1\n" ).arg( d->mExtended );
00538 str += QString::fromLatin1( " Street: %1\n" ).arg( d->mStreet );
00539 str += QString::fromLatin1( " Locality: %1\n" ).arg( d->mLocality );
00540 str += QString::fromLatin1( " Region: %1\n" ).arg( d->mRegion );
00541 str += QString::fromLatin1( " Postal code: %1\n" ).arg( d->mPostalCode );
00542 str += QString::fromLatin1( " Country: %1\n" ).arg( d->mCountry );
00543 str += QString::fromLatin1( " Label: %1\n" ).arg( d->mLabel );
00544 str += QLatin1String( "}\n" );
00545
00546 return str;
00547 }
00548
00549 QString Address::formattedAddress( const QString &realName,
00550 const QString &orgaName ) const
00551 {
00552 QString ciso;
00553 QString addrTemplate;
00554 QString ret;
00555
00556
00557 if ( !country().isEmpty() ) {
00558 ciso = countryToISO( country() );
00559 } else {
00560
00561 ciso = KGlobal::locale()->country();
00562 }
00563 KConfig entry( KStandardDirs::locate( "locale",
00564 QLatin1String( "l10n/" ) + ciso + QLatin1String( "/entry.desktop" ) ) );
00565
00566 KConfigGroup group = entry.group( "KCM Locale" );
00567
00568 if ( orgaName.isEmpty() ) {
00569 addrTemplate = group.readEntry( "AddressFormat" );
00570 } else {
00571 addrTemplate = group.readEntry( "BusinessAddressFormat" );
00572 if ( addrTemplate.isEmpty() ) {
00573 addrTemplate = group.readEntry( "AddressFormat" );
00574 }
00575 }
00576
00577
00578
00579 if ( addrTemplate.isEmpty() ) {
00580 kWarning(5700) << "address format database incomplete"
00581 << "(no format for locale" << ciso
00582 << "found). Using default address formatting.";
00583 addrTemplate = QLatin1String( "%0(%n\\n)%0(%cm\\n)%0(%s\\n)%0(PO BOX %p\\n)%0(%l%w%r)%,%z" );
00584 }
00585
00586
00587 parseAddressTemplateSection( addrTemplate, ret, realName, orgaName, *this );
00588
00589
00590
00591 if ( !country().isEmpty() ) {
00592 KConfig entry( KStandardDirs::locate( "locale", QLatin1String( "l10n/" ) +
00593 KGlobal::locale()->country() +
00594 QLatin1String( "/entry.desktop" ) ) );
00595 KConfigGroup group = entry.group( "KCM Locale" );
00596 QString cpos = group.readEntry( "AddressCountryPosition" );
00597 if ( QLatin1String( "BELOW" ) == cpos || cpos.isEmpty() ) {
00598 ret = ret + QLatin1String( "\n\n" ) + country().toUpper();
00599 } else if ( QLatin1String( "below" ) == cpos ) {
00600 ret = ret + QLatin1String( "\n\n" ) + country();
00601 } else if ( QLatin1String( "ABOVE" ) == cpos ) {
00602 ret = country().toUpper() + QLatin1String( "\n\n" ) + ret;
00603 } else if ( QLatin1String( "above" ) == cpos ) {
00604 ret = country() + QLatin1String( "\n\n" ) + ret;
00605 }
00606 }
00607
00608 return ret;
00609 }
00610
00611 QString Address::countryToISO( const QString &cname )
00612 {
00613
00614
00615
00616 typedef QMap<QString, QString> stringMap;
00617 K_GLOBAL_STATIC( stringMap, sISOMap )
00618
00619 QMap<QString, QString>::ConstIterator it;
00620 it = sISOMap->constFind( cname );
00621 if ( it != sISOMap->constEnd() ) {
00622 return it.value();
00623 }
00624
00625 QString mapfile = KGlobal::dirs()->findResource( "data",
00626 QLatin1String( "kabc/countrytransl.map" ) );
00627
00628 QFile file( mapfile );
00629 if ( file.open( QIODevice::ReadOnly ) ) {
00630 QTextStream s( &file );
00631 QString strbuf = s.readLine();
00632 while ( !strbuf.isEmpty() ) {
00633 QStringList countryInfo = strbuf.split( QLatin1Char( '\t' ), QString::KeepEmptyParts );
00634 if ( countryInfo[ 0 ] == cname ) {
00635 file.close();
00636 sISOMap->insert( cname, countryInfo[ 1 ] );
00637 return countryInfo[ 1 ];
00638 }
00639 strbuf = s.readLine();
00640 }
00641 file.close();
00642 }
00643
00644
00645 sISOMap->insert( cname, KGlobal::locale()->country() );
00646 return KGlobal::locale()->country();
00647 }
00648
00649 QString Address::ISOtoCountry( const QString &ISOname )
00650 {
00651
00652 if ( ISOname.simplified().isEmpty() ) {
00653 return QString();
00654 }
00655
00656 QString mapfile = KGlobal::dirs()->findResource( "data",
00657 QLatin1String( "kabc/countrytransl.map" ) );
00658
00659 QFile file( mapfile );
00660 if ( file.open( QIODevice::ReadOnly ) ) {
00661 QTextStream s( &file );
00662 QString searchStr = QLatin1Char( '\t' ) + ISOname.simplified().toLower();
00663 QString strbuf = s.readLine();
00664 int pos;
00665 while ( !strbuf.isEmpty() ) {
00666 if ( ( pos = strbuf.indexOf( searchStr ) ) != -1 ) {
00667 file.close();
00668 return i18n( strbuf.left( pos ).toUtf8() );
00669 }
00670 strbuf = s.readLine();
00671 }
00672 file.close();
00673 }
00674
00675 return ISOname;
00676 }
00677
00678 QDataStream &KABC::operator<<( QDataStream &s, const Address &addr )
00679 {
00680 return s << addr.d->mId << (uint)addr.d->mType << addr.d->mPostOfficeBox
00681 << addr.d->mExtended << addr.d->mStreet << addr.d->mLocality
00682 << addr.d->mRegion << addr.d->mPostalCode << addr.d->mCountry
00683 << addr.d->mLabel << addr.d->mEmpty;
00684 }
00685
00686 QDataStream &KABC::operator>>( QDataStream &s, Address &addr )
00687 {
00688 uint type;
00689 s >> addr.d->mId >> type >> addr.d->mPostOfficeBox >> addr.d->mExtended
00690 >> addr.d->mStreet >> addr.d->mLocality >> addr.d->mRegion
00691 >> addr.d->mPostalCode >> addr.d->mCountry >> addr.d->mLabel
00692 >> addr.d->mEmpty;
00693
00694 addr.d->mType = Address::Type( type );
00695
00696 return s;
00697 }