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