• Skip to content
  • Skip to link menu
KDE 4.2 API Reference
  • KDE API Reference
  • KDE-PIM Libraries
  • Sitemap
  • Contact Us
 

kabc

address.cpp

00001 /*
00002     This file is part of libkabc.
00003     Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org>
00004 
00005     This library is free software; you can redistribute it and/or
00006     modify it under the terms of the GNU Library General Public
00007     License as published by the Free Software Foundation; either
00008     version 2 of the License, or (at your option) any later version.
00009 
00010     This library is distributed in the hope that it will be useful,
00011     but WITHOUT ANY WARRANTY; without even the implied warranty of
00012     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013     Library General Public License for more details.
00014 
00015     You should have received a copy of the GNU Library General Public License
00016     along with this library; see the file COPYING.LIB.  If not, write to
00017     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00018     Boston, MA 02110-1301, USA.
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 // template tags for address formatting localization
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       // found end of brackets
00066       return i;
00067     } else {
00068       if ( '(' == tsection[i] ) {
00069         // nested brackets
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   // This method first parses and substitutes any bracketed sections and
00088   // after that replaces any tags with their values. If a bracketed section
00089   // or a tag evaluate to zero, they are not just removed but replaced
00090   // with a placeholder. This is because in the last step conditionals are
00091   // resolved which depend on information about zero-evaluations.
00092   result = tsection;
00093   int stpos = 0;
00094   bool ret = false;
00095 
00096   // first check for brackets that have to be evaluated first
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     // expect opening bracket and find next balanced closing bracket. If
00102     // next char is no opening bracket, continue parsing (no valid tag)
00103     if ( '(' == result[bpos1] ) {
00104       bpos2 = findBalancedBracket( result, bpos1 );
00105       if ( -1 != bpos2 ) {
00106         // we have balanced brackets, recursively parse:
00107         QString rplstr;
00108         bool purge = !parseAddressTemplateSection( result.mid( bpos1+1,
00109                                                    bpos2-bpos1-1 ), rplstr,
00110                                                    realName, orgaName, address );
00111         if ( purge ) {
00112           // purge -> remove all
00113           // replace with !_P_!, so conditional tags work later
00114           result.replace( fpos, bpos2 - fpos + 1, "!_P_!" );
00115           // leave stpos as it is
00116         } else {
00117           // no purge -> replace with recursively parsed string
00118           result.replace( fpos, bpos2 - fpos + 1, rplstr );
00119           ret = true;
00120           stpos = fpos + rplstr.length();
00121         }
00122       } else {
00123         // unbalanced brackets:  keep on parsing (should not happen
00124         // and will result in bad formatting)
00125         stpos = bpos1;
00126       }
00127     }
00128     fpos = result.indexOf( KABC_FMTTAG_purgeempty, stpos );
00129   }
00130 
00131   // after sorting out all purge tags, we just search'n'replace the rest,
00132   // keeping track of whether at least one tag evaluates to something.
00133   // The following macro needs QString for R_FIELD
00134   // It substitutes !_P_! for empty fields so conditional tags work later
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   // conditional comma
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   // conditional whitespace
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   // remove purged:
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 &region )
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   // FIXME: first check for iso-country-field and prefer that one
00553   if ( !country().isEmpty() ) {
00554     ciso = countryToISO( country() );
00555   } else {
00556     // fall back to our own country
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   // decide whether this needs special business address formatting
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   // in the case there's no format found at all, default to what we've always
00574   // used:
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   // scan
00583   parseAddressTemplateSection( addrTemplate, ret, realName, orgaName, *this );
00584 
00585   // now add the country line if needed (formatting this time according to
00586   // the rules of our own system country )
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   // we search a map file for translations from country names to
00610   // iso codes, storing caching things in a QMap for faster future
00611   // access.
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   // fall back to system country
00641   sISOMap->insert( cname, KGlobal::locale()->country() );
00642   return KGlobal::locale()->country();
00643 }
00644 
00645 QString Address::ISOtoCountry( const QString &ISOname )
00646 {
00647   // get country name from ISO country code (e.g. "no" -> i18n("Norway"))
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 }

kabc

Skip menu "kabc"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

KDE-PIM Libraries

Skip menu "KDE-PIM Libraries"
  • akonadi
  • kabc
  • kblog
  • kcal
  • kimap
  • kioslave
  •   imap4
  •   mbox
  • kldap
  • kmime
  • kpimidentities
  •   richtextbuilders
  • kpimutils
  • kresources
  • ktnef
  • kxmlrpcclient
  • mailtransport
  • qgpgme
  • syndication
  •   atom
  •   rdf
  •   rss2
Generated for KDE-PIM Libraries by doxygen 1.5.8
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal