kabc Library API Documentation

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., 59 Temple Place - Suite 330,
00018     Boston, MA 02111-1307, USA.
00019 */
00020 
00021 #include <kapplication.h>
00022 #include <kdebug.h>
00023 #include <klocale.h>
00024 #include <ksimpleconfig.h>
00025 #include <kstandarddirs.h>
00026 #include <kstaticdeleter.h>
00027 
00028 #include <qfile.h>
00029 
00030 #include "address.h"
00031 
00032 using namespace KABC;
00033 
00034 QMap<QString, QString> *Address::mISOMap = 0;
00035 static KStaticDeleter< QMap<QString, QString> > isoMapDeleter;
00036 
00037 Address::Address() :
00038   mEmpty( true ), mType( 0 )
00039 {
00040   mId = KApplication::randomString( 10 );
00041 }
00042 
00043 Address::Address( int type ) :
00044   mEmpty( true ), mType( type )
00045 {
00046   mId = KApplication::randomString( 10 );
00047 }
00048 
00049 bool Address::operator==( const Address &a ) const
00050 {
00051   if ( mPostOfficeBox != a.mPostOfficeBox ) return false;
00052   if ( mExtended != a.mExtended ) return false;
00053   if ( mStreet != a.mStreet ) return false;
00054   if ( mLocality != a.mLocality ) return false;
00055   if ( mRegion != a.mRegion ) return false;
00056   if ( mPostalCode != a.mPostalCode ) return false;
00057   if ( mCountry != a.mCountry ) return false;
00058   if ( mLabel != a.mLabel ) return false;
00059   
00060   return true;
00061 }
00062 
00063 bool Address::operator!=( const Address &a ) const
00064 {
00065   return !( a == *this );
00066 }
00067 
00068 bool Address::isEmpty() const
00069 {
00070   if ( mPostOfficeBox.isEmpty() &&
00071        mExtended.isEmpty() &&
00072        mStreet.isEmpty() &&
00073        mLocality.isEmpty() &&
00074        mRegion.isEmpty() &&
00075        mPostalCode.isEmpty() &&
00076        mCountry.isEmpty() &&
00077        mLabel.isEmpty() ) {
00078     return true;
00079   }
00080   return false;
00081 }
00082 
00083 void Address::clear()
00084 {
00085   *this = Address();
00086 }
00087 
00088 void Address::setId( const QString &id )
00089 {
00090   mEmpty = false;
00091 
00092   mId = id;
00093 }
00094 
00095 QString Address::id() const
00096 {
00097   return mId;
00098 }
00099 
00100 void Address::setType( int type )
00101 {
00102   mEmpty = false;
00103 
00104   mType = type;
00105 }
00106 
00107 int Address::type() const
00108 {
00109   return mType;
00110 }
00111 
00112 QString Address::typeLabel() const
00113 {
00114   QString label;
00115   bool first = true;
00116 
00117   TypeList list = typeList();
00118 
00119   TypeList::Iterator it;
00120   for ( it = list.begin(); it != list.end(); ++it ) {
00121     if ( ( type() & (*it) ) && ( (*it) != Pref ) ) {
00122       label.append( ( first ? "" : "/" ) + typeLabel( *it ) );
00123       if ( first )
00124         first = false;
00125     }
00126   }
00127 
00128   return label;
00129 }
00130 
00131 void Address::setPostOfficeBox( const QString &s )
00132 {
00133   mEmpty = false;
00134 
00135   mPostOfficeBox = s;
00136 }
00137 
00138 QString Address::postOfficeBox() const
00139 {
00140   return mPostOfficeBox;
00141 }
00142 
00143 QString Address::postOfficeBoxLabel()
00144 {
00145   return i18n("Post Office Box");
00146 }
00147 
00148 
00149 void Address::setExtended( const QString &s )
00150 {
00151   mEmpty = false;
00152 
00153   mExtended = s;
00154 }
00155 
00156 QString Address::extended() const
00157 {
00158   return mExtended;
00159 }
00160 
00161 QString Address::extendedLabel()
00162 {
00163   return i18n("Extended Address Information");
00164 }
00165 
00166 
00167 void Address::setStreet( const QString &s )
00168 {
00169   mEmpty = false;
00170 
00171   mStreet = s;
00172 }
00173 
00174 QString Address::street() const
00175 {
00176   return mStreet;
00177 }
00178 
00179 QString Address::streetLabel()
00180 {
00181   return i18n("Street");
00182 }
00183 
00184 
00185 void Address::setLocality( const QString &s )
00186 {
00187   mEmpty = false;
00188 
00189   mLocality = s;
00190 }
00191 
00192 QString Address::locality() const
00193 {
00194   return mLocality;
00195 }
00196 
00197 QString Address::localityLabel()
00198 {
00199   return i18n("Locality");
00200 }
00201 
00202 
00203 void Address::setRegion( const QString &s )
00204 {
00205   mEmpty = false;
00206 
00207   mRegion = s;
00208 }
00209 
00210 QString Address::region() const
00211 {
00212   return mRegion;
00213 }
00214 
00215 QString Address::regionLabel()
00216 {
00217   return i18n("Region");
00218 }
00219 
00220 
00221 void Address::setPostalCode( const QString &s )
00222 {
00223   mEmpty = false;
00224 
00225   mPostalCode = s;
00226 }
00227 
00228 QString Address::postalCode() const
00229 {
00230   return mPostalCode;
00231 }
00232 
00233 QString Address::postalCodeLabel()
00234 {
00235   return i18n("Postal Code");
00236 }
00237 
00238 
00239 void Address::setCountry( const QString &s )
00240 {
00241   mEmpty = false;
00242 
00243   mCountry = s;
00244 }
00245 
00246 QString Address::country() const
00247 {
00248   return mCountry;
00249 }
00250 
00251 QString Address::countryLabel()
00252 {
00253   return i18n("Country");
00254 }
00255 
00256 
00257 void Address::setLabel( const QString &s )
00258 {
00259   mEmpty = false;
00260 
00261   mLabel = s;
00262 }
00263 
00264 QString Address::label() const
00265 {
00266   return mLabel;
00267 }
00268 
00269 QString Address::labelLabel()
00270 {
00271   return i18n("Delivery Label");
00272 }
00273 
00274 Address::TypeList Address::typeList()
00275 {
00276   TypeList list;
00277 
00278   list << Dom << Intl << Postal << Parcel << Home << Work << Pref;
00279 
00280   return list;
00281 }
00282 
00283 QString Address::typeLabel( int type )
00284 {
00285   if ( type & Pref )
00286     return i18n( "Preferred address", "Preferred" );
00287 
00288   switch ( type ) {
00289     case Dom:
00290       return i18n("Domestic");
00291       break;
00292     case Intl:
00293       return i18n("International");
00294       break;
00295     case Postal:
00296       return i18n("Postal");
00297       break;
00298     case Parcel:
00299       return i18n("Parcel");
00300       break;
00301     case Home:
00302       return i18n("Home Address", "Home");
00303       break;
00304     case Work:
00305       return i18n("Work Address", "Work");
00306       break;
00307     case Pref:
00308       return i18n("Preferred Address");
00309       break;
00310     default:
00311       return i18n("Other");
00312       break;
00313   }
00314 }
00315 
00316 void Address::dump() const
00317 {
00318   kdDebug(5700) << "  Address {" << endl;
00319   kdDebug(5700) << "    Id: " << id() << endl;
00320   kdDebug(5700) << "    Extended: " << extended() << endl;
00321   kdDebug(5700) << "    Street: " << street() << endl;
00322   kdDebug(5700) << "    Postal Code: " << postalCode() << endl;
00323   kdDebug(5700) << "    Locality: " << locality() << endl;
00324   kdDebug(5700) << "  }" << endl;
00325 }
00326 
00327 
00328 QString Address::formattedAddress( const QString &realName
00329                                  , const QString &orgaName ) const
00330 {
00331   QString ciso;
00332   QString addrTemplate;
00333   QString ret;
00334 
00335   // FIXME: first check for iso-country-field and prefer that one
00336   if ( !country().isEmpty() ) {
00337     ciso = countryToISO( country() );
00338   } else {
00339     // fall back to our own country
00340     ciso = KGlobal::locale()->country();
00341   }
00342   KSimpleConfig entry( locate( "locale", 
00343         QString( "l10n/" ) + ciso + QString( "/entry.desktop" ) ) );
00344   entry.setGroup( "KCM Locale" );
00345   
00346   // decide whether this needs special business address formatting
00347   if ( orgaName.isNull() ) {
00348     addrTemplate = entry.readEntry( "AddressFormat" );
00349   } else {
00350     addrTemplate = entry.readEntry( "BusinessAddressFormat" );
00351     if ( addrTemplate.isEmpty() )
00352       addrTemplate = entry.readEntry( "AddressFormat" );
00353   }
00354   
00355   // in the case there's no format found at all, default to what we've always
00356   // used:
00357   if ( addrTemplate.isEmpty() ) {
00358     kdWarning(5700) << "address format database incomplete "
00359         << "(no format for locale " << ciso 
00360         << " found). Using default address formatting." << endl;
00361     addrTemplate = "%0(%n\\n)%0(%cm\\n)%0(%s\\n)%0(PO BOX %p\\n)%0(%l%w%r)%,%z";
00362   }
00363   
00364   // scan
00365   parseAddressTemplateSection( addrTemplate, ret, realName, orgaName );
00366 
00367   // now add the country line if needed (formatting this time according to
00368   // the rules of our own system country )
00369   if ( !country().isEmpty() ) {
00370     KSimpleConfig entry( locate( "locale", QString( "l10n/" )
00371           + KGlobal::locale()->country() + QString( "/entry.desktop" ) ) );
00372     entry.setGroup( "KCM Locale" );
00373     QString cpos = entry.readEntry( "AddressCountryPosition" );
00374     if ( "BELOW" == cpos || cpos.isEmpty() ) {
00375       ret = ret + "\n\n" + country().upper();
00376     } else if ( "below" == cpos ) {
00377       ret = ret + "\n\n" + country();
00378     } else if ( "ABOVE" == cpos ) {
00379       ret = country().upper() + "\n\n" + ret;
00380     } else if ( "above" == cpos ) {
00381       ret = country() + "\n\n" + ret;
00382     }
00383   }
00384   
00385   return ret;
00386 }
00387 
00388 bool Address::parseAddressTemplateSection( const QString &tsection, 
00389     QString &result, const QString &realName, const QString &orgaName ) const
00390 {
00391   // This method first parses and substitutes any bracketed sections and
00392   // after that replaces any tags with their values. If a bracketed section
00393   // or a tag evaluate to zero, they are not just removed but replaced
00394   // with a placeholder. This is because in the last step conditionals are
00395   // resolved which depend on information about zero-evaluations.
00396   result = tsection;
00397   int stpos = 0;
00398   bool ret = false;
00399   
00400   // first check for brackets that have to be evaluated first 
00401   int fpos = result.find( KABC_FMTTAG_purgeempty, stpos );
00402   while ( -1 != fpos ) {
00403     int bpos1 = fpos + KABC_FMTTAG_purgeempty.length();
00404     int bpos2;
00405     // expect opening bracket and find next balanced closing bracket. If 
00406     // next char is no opening bracket, continue parsing (no valid tag)
00407     if ( '(' == result[bpos1] ) {
00408       bpos2 = findBalancedBracket( result, bpos1 );
00409       if ( -1 != bpos2 ) {
00410         // we have balanced brackets, recursively parse:
00411         QString rplstr;
00412         bool purge = !parseAddressTemplateSection( result.mid( bpos1+1,
00413                                                    bpos2-bpos1-1 ), rplstr,
00414                                                    realName, orgaName );
00415         if ( purge ) {
00416           // purge -> remove all
00417           // replace with !_P_!, so conditional tags work later
00418           result.replace( fpos, bpos2 - fpos + 1, "!_P_!" );
00419           // leave stpos as it is
00420         } else {
00421           // no purge -> replace with recursively parsed string
00422           result.replace( fpos, bpos2 - fpos + 1, rplstr );
00423           ret = true;
00424           stpos = fpos + rplstr.length();
00425         }
00426       } else {
00427         // unbalanced brackets:  keep on parsing (should not happen 
00428         // and will result in bad formatting)
00429         stpos = bpos1; 
00430       }
00431     }
00432     fpos = result.find( KABC_FMTTAG_purgeempty, stpos );
00433   }
00434 
00435   // after sorting out all purge tags, we just search'n'replace the rest,
00436   // keeping track of whether at least one tag evaluates to something.
00437   // The following macro needs QString for R_FIELD
00438   // It substitutes !_P_! for empty fields so conditional tags work later
00439 #define REPLTAG(R_TAG,R_FIELD) \
00440   if ( result.find(R_TAG, false) != -1 ) { \
00441     QString rpl = R_FIELD.isEmpty() ? QString("!_P_!") : R_FIELD; \
00442     result.replace( R_TAG, rpl ); \
00443     if ( !R_FIELD.isEmpty() ) { \
00444       ret = true; \
00445     } \
00446   }
00447   REPLTAG( KABC_FMTTAG_realname, realName );
00448   REPLTAG( KABC_FMTTAG_REALNAME, realName.upper() );
00449   REPLTAG( KABC_FMTTAG_company, orgaName );
00450   REPLTAG( KABC_FMTTAG_COMPANY, orgaName.upper() );
00451   REPLTAG( KABC_FMTTAG_pobox, postOfficeBox() );
00452   REPLTAG( KABC_FMTTAG_street, street() );
00453   REPLTAG( KABC_FMTTAG_STREET, street().upper() );
00454   REPLTAG( KABC_FMTTAG_zipcode, postalCode() );
00455   REPLTAG( KABC_FMTTAG_location, locality() );
00456   REPLTAG( KABC_FMTTAG_LOCATION, locality().upper() );
00457   REPLTAG( KABC_FMTTAG_region, region() );
00458   REPLTAG( KABC_FMTTAG_REGION, region().upper() );
00459   result.replace( KABC_FMTTAG_newline, "\n" );
00460 #undef REPLTAG
00461  
00462   // conditional comma 
00463   fpos = result.find( KABC_FMTTAG_condcomma, 0 );
00464   while ( -1 != fpos ) {
00465     QString str1 = result.mid( fpos - 5, 5 );
00466     QString str2 = result.mid( fpos + 2, 5 );
00467     if ( str1 != "!_P_!" && str2 != "!_P_!" ) {
00468       result.replace( fpos, 2, ", " );
00469     } else {
00470       result.remove( fpos, 2 );
00471     }
00472     fpos = result.find( KABC_FMTTAG_condcomma, fpos );
00473   }
00474   // conditional whitespace
00475   fpos = result.find( KABC_FMTTAG_condwhite, 0 );
00476   while ( -1 != fpos ) {
00477     QString str1 = result.mid( fpos - 5, 5 );
00478     QString str2 = result.mid( fpos + 2, 5 );
00479     if ( str1 != "!_P_!" && str2 != "!_P_!" ) {
00480       result.replace( fpos, 2, " " );
00481     } else {
00482       result.remove( fpos, 2 );
00483     }
00484     fpos = result.find( KABC_FMTTAG_condwhite, fpos );
00485   }
00486 
00487   // remove purged:
00488   result.remove( "!_P_!" );
00489 
00490   return ret;
00491 }
00492 
00493 int Address::findBalancedBracket( const QString &tsection, int pos ) const
00494 {
00495   int balancecounter = 0;
00496   for( unsigned int i = pos + 1; i < tsection.length(); i++ ) {
00497     if ( ')' == tsection[i] && 0 == balancecounter ) {
00498       // found end of brackets
00499       return i;
00500     } else
00501     if ( '(' == tsection[i] ) {
00502       // nested brackets
00503       balancecounter++;
00504     }
00505   }
00506   return -1;
00507 }
00508 
00509 QString Address::countryToISO( const QString &cname )
00510 {
00511   // we search a map file for translations from country names to
00512   // iso codes, storing caching things in a QMap for faster future 
00513   // access.
00514   if ( !mISOMap )
00515     isoMapDeleter.setObject( mISOMap, new QMap<QString, QString>() );
00516 
00517   QMap<QString, QString>::ConstIterator it;
00518   it = mISOMap->find( cname );
00519   if ( it != mISOMap->end() )
00520     return it.data();
00521 
00522   QString mapfile = KGlobal::dirs()->findResource( "data", 
00523           QString::fromLatin1( "kabc/countrytransl.map" ) );
00524 
00525   QFile file( mapfile );
00526   if ( file.open( IO_ReadOnly ) ) {
00527     QTextStream s( &file );
00528     QString strbuf = s.readLine();
00529     while( !strbuf.isNull() ) {
00530       if ( strbuf.startsWith( cname ) ) {
00531         int index = strbuf.findRev('\t');
00532         strbuf = strbuf.mid(index+1, 2);
00533         file.close();
00534         mISOMap->insert( cname, strbuf );
00535         return strbuf;
00536       }
00537       strbuf = s.readLine();
00538     }
00539     file.close();
00540   }
00541   
00542   // fall back to system country
00543   mISOMap->insert( cname, KGlobal::locale()->country() );
00544   return KGlobal::locale()->country();
00545 }
00546 
00547 QString Address::ISOtoCountry( const QString &ISOname )
00548 {
00549   // get country name from ISO country code (e.g. "no" -> i18n("Norway"))
00550   if (ISOname.simplifyWhiteSpace().isEmpty())
00551     return QString::null;
00552 
00553   QString mapfile = KGlobal::dirs()->findResource( "data", 
00554           QString::fromLatin1( "kabc/countrytransl.map" ) );
00555 
00556   QFile file( mapfile );
00557   if ( file.open( IO_ReadOnly ) ) {
00558     QTextStream s( &file );
00559     QString searchStr = "\t" + ISOname.simplifyWhiteSpace().lower();
00560     QString strbuf = s.readLine();
00561     int pos;
00562     while( !strbuf.isNull() ) {
00563       if ( (pos=strbuf.find( searchStr )) != -1 ) {
00564         file.close();
00565         return i18n(strbuf.left(pos).utf8());
00566       }
00567       strbuf = s.readLine();
00568     }
00569     file.close();
00570   }
00571 
00572   return ISOname;
00573 }
00574 
00575 QDataStream &KABC::operator<<( QDataStream &s, const Address &addr )
00576 {
00577     return s << addr.mId << addr.mType << addr.mPostOfficeBox <<
00578         addr.mExtended << addr.mStreet << addr.mLocality <<
00579         addr.mRegion << addr.mPostalCode << addr.mCountry <<
00580         addr.mLabel;
00581 }
00582 
00583 QDataStream &KABC::operator>>( QDataStream &s, Address &addr )
00584 {
00585     s >> addr.mId >> addr.mType >> addr.mPostOfficeBox >> addr.mExtended >>
00586         addr.mStreet >> addr.mLocality >> addr.mRegion >>
00587         addr.mPostalCode >> addr.mCountry >> addr.mLabel;
00588 
00589     addr.mEmpty = false;
00590 
00591     return s;
00592 }
KDE Logo
This file is part of the documentation for kabc Library Version 3.3.1.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Fri Feb 18 15:11:25 2005 by doxygen 1.3.9.1 written by Dimitri van Heesch, © 1997-2003