diff --git a/autotests/addresstest.cpp b/autotests/addresstest.cpp index 62fd6b3a..d0e8e264 100644 --- a/autotests/addresstest.cpp +++ b/autotests/addresstest.cpp @@ -1,428 +1,438 @@ /* This file is part of the KContacts framework. Copyright (c) 2007 Tobias Koenig This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "addresstest.h" #include "addresseelist.h" #include "vcardtool.h" #include "kcontacts/address.h" #include #include #include #include #include QTEST_MAIN(AddressTest) #ifndef Q_OS_WIN void initLocale() { qputenv("LC_ALL", "en_US.utf-8"); } Q_CONSTRUCTOR_FUNCTION(initLocale) #endif void AddressTest::initTestCase() { } void AddressTest::emptyTest() { KContacts::Address address; QVERIFY(address.isEmpty()); } void AddressTest::storeTest() { KContacts::Address address; address.setId(QStringLiteral("My Id")); address.setType(KContacts::Address::Home); address.setPostOfficeBox(QStringLiteral("1234")); address.setExtended(QStringLiteral("My Extended Label")); address.setStreet(QStringLiteral("My Street")); address.setLocality(QStringLiteral("My Locality")); address.setRegion(QStringLiteral("My Region")); address.setPostalCode(QStringLiteral("My PostalCode")); address.setCountry(QStringLiteral("My Country")); address.setLabel(QStringLiteral("My Label")); QCOMPARE(address.id(), QStringLiteral("My Id")); QCOMPARE(address.type(), KContacts::Address::Home); QCOMPARE(address.postOfficeBox(), QStringLiteral("1234")); QCOMPARE(address.extended(), QStringLiteral("My Extended Label")); QCOMPARE(address.street(), QStringLiteral("My Street")); QCOMPARE(address.locality(), QStringLiteral("My Locality")); QCOMPARE(address.region(), QStringLiteral("My Region")); QCOMPARE(address.postalCode(), QStringLiteral("My PostalCode")); QCOMPARE(address.country(), QStringLiteral("My Country")); QCOMPARE(address.label(), QStringLiteral("My Label")); QVERIFY(!address.geo().isValid()); } void AddressTest::equalsTest() { KContacts::Address address1, address2; address1.setId(QStringLiteral("My Id")); address1.setType(KContacts::Address::Home); address1.setPostOfficeBox(QStringLiteral("1234")); address1.setExtended(QStringLiteral("My Extended Label")); address1.setStreet(QStringLiteral("My Street")); address1.setLocality(QStringLiteral("My Locality")); address1.setRegion(QStringLiteral("My Region")); address1.setPostalCode(QStringLiteral("My Postalcode")); address1.setCountry(QStringLiteral("My country")); address1.setLabel(QStringLiteral("My Label")); address2.setId(QStringLiteral("My Id")); address2.setType(KContacts::Address::Home); address2.setPostOfficeBox(QStringLiteral("1234")); address2.setExtended(QStringLiteral("My Extended Label")); address2.setStreet(QStringLiteral("My Street")); address2.setLocality(QStringLiteral("My Locality")); address2.setRegion(QStringLiteral("My Region")); address2.setPostalCode(QStringLiteral("My Postalcode")); address2.setCountry(QStringLiteral("My country")); address2.setLabel(QStringLiteral("My Label")); QVERIFY(address1 == address2); } void AddressTest::differsTest() { KContacts::Address address1(KContacts::Address::Home); KContacts::Address address2(KContacts::Address::Work); QVERIFY(address1 != address2); } void AddressTest::assignmentTest() { KContacts::Address address1, address2; address1.setId(QStringLiteral("My Id")); address1.setType(KContacts::Address::Home); address1.setPostOfficeBox(QStringLiteral("1234")); address1.setExtended(QStringLiteral("My Extended Label")); address1.setStreet(QStringLiteral("My Street")); address1.setLocality(QStringLiteral("My Locality")); address1.setRegion(QStringLiteral("My Region")); address1.setPostalCode(QStringLiteral("My Postalcode")); address1.setCountry(QStringLiteral("My country")); address1.setLabel(QStringLiteral("My Label")); address2 = address1; QVERIFY(address1 == address2); } void AddressTest::serializeTest() { KContacts::Address address1, address2; address1.setType(KContacts::Address::Work); address1.setPostOfficeBox(QStringLiteral("1234")); address1.setExtended(QStringLiteral("My Extended Label")); address1.setStreet(QStringLiteral("My Street")); address1.setLocality(QStringLiteral("My Locality")); address1.setRegion(QStringLiteral("My Region")); address1.setPostalCode(QStringLiteral("My Postalcode")); address1.setCountry(QStringLiteral("My country")); address1.setLabel(QStringLiteral("My Label")); QByteArray data; QDataStream s(&data, QIODevice::WriteOnly); s << address1; QDataStream t(&data, QIODevice::ReadOnly); t >> address2; QVERIFY(address1 == address2); } void AddressTest::formatTest() { { // check availability of country to ISO code mapping data file const QString mapfile = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("kf5/kcontacts/countrytransl.map")); QVERIFY2(!mapfile.isEmpty(), "Country to ISO code mapping data file does not exist"); QFileInfo fileInfo(mapfile); QVERIFY2(fileInfo.exists(), "Country to ISO code mapping data file does not exist"); QVERIFY2(fileInfo.isReadable(), "Country to ISO code mapping data file is not readable"); } { const QString templateFile = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("kf5/locale/countries/de/country.desktop")); QVERIFY(!templateFile.isEmpty()); const KConfig templateConfig(templateFile); QVERIFY2(templateConfig.hasGroup("KCM Locale"), "Address format config for 'de' is missing expected 'KCM Locale' group"); const KConfigGroup templateGroup = templateConfig.group("KCM Locale"); QVERIFY2(templateGroup.hasKey("AddressFormat"), "Address format config is missing expected key 'AddressFormat'"); const QString addressTemplate = templateGroup.readEntry("AddressFormat"); QVERIFY2(!addressTemplate.isEmpty(), "Address format template for 'de' is empty"); KContacts::Address address; address.setStreet(QStringLiteral("Lummerlandstr. 1")); address.setPostalCode(QStringLiteral("12345")); address.setLocality(QStringLiteral("Lummerstadt")); address.setCountry(QStringLiteral("Germany")); const QString result(QStringLiteral("Jim Knopf\nLummerlandstr. 1\n" "12345 Lummerstadt\n\nGERMANY")); QCOMPARE(address.formattedAddress(QStringLiteral("Jim Knopf")), result); } { KContacts::Address address; address.setStreet(QStringLiteral("457 Foobar Ave")); address.setPostalCode(QStringLiteral("1A2B3C")); address.setLocality(QStringLiteral("Nervousbreaktown")); address.setRegion(QStringLiteral("DC")); address.setCountry(QStringLiteral("United States of America")); const QString result( QStringLiteral("Huck Finn\n457 Foobar Ave\nNervousbreaktown," " DC 1A2B3C\n\nUNITED STATES OF AMERICA")); QCOMPARE(address.formattedAddress(QStringLiteral("Huck Finn")), result); } { KContacts::Address address; address.setStreet(QStringLiteral("Lummerlandstr. 1")); address.setPostalCode(QStringLiteral("12345")); address.setLocality(QStringLiteral("Lummerstadt")); address.setCountry(QStringLiteral("Deutschland")); const QString result(QStringLiteral("Jim Knopf\nLummerlandstr. 1\n" "12345 Lummerstadt\n\nDEUTSCHLAND")); QCOMPARE(address.formattedAddress(QStringLiteral("Jim Knopf")), result); } { KContacts::Address address; address.setStreet(QStringLiteral("Lummerlandstr. 1")); address.setPostalCode(QStringLiteral("12345")); address.setLocality(QStringLiteral("Lummerstadt")); address.setCountry(QString()); const QString result(QStringLiteral("Jim Knopf\nLummerlandstr. 1\nLummerstadt, 12345")); QCOMPARE(address.formattedAddress(QStringLiteral("Jim Knopf")), result); } } void AddressTest::shouldExportVcard3() { KContacts::Address address; address.setId(QStringLiteral("My Id")); address.setType(KContacts::Address::Home); address.setPostOfficeBox(QStringLiteral("1234")); address.setExtended(QStringLiteral("My Extended Label")); address.setStreet(QStringLiteral("My Street")); address.setLocality(QStringLiteral("My Locality")); address.setRegion(QStringLiteral("My Region")); address.setPostalCode(QStringLiteral("My Postalcode")); address.setCountry(QStringLiteral("My country")); address.setLabel(QStringLiteral("My Label")); KContacts::AddresseeList lst; KContacts::Addressee addr; addr.setEmails(QStringList() << QStringLiteral("foo@kde.org")); addr.setUid(QStringLiteral("testuid")); addr.insertAddress(address); lst << addr; KContacts::VCardTool vcard; const QByteArray ba = vcard.exportVCards(lst, KContacts::VCard::v3_0); QByteArray expected("BEGIN:VCARD\r\n" "VERSION:3.0\r\n" "ADR;TYPE=home:1234;My Extended Label;My Street;My Locality;My Region;My Pos\r\n" " talcode;My country\r\n" "EMAIL:foo@kde.org\r\n" "LABEL;TYPE=home:My Label\r\n" "N:;;;;\r\n" "UID:testuid\r\n" "END:VCARD\r\n\r\n"); QCOMPARE(ba, expected); } void AddressTest::shouldParseAddressVCard3() { QByteArray vcarddata("BEGIN:VCARD\n" "VERSION:3.0\n" "N:LastName;FirstName;;;\n" "ADR;TYPE=home:1234;My Extended Label;My Street;My Locality;My Region;My Pos\r\n" " talcode;My country\r\n" "UID:c80cf296-0825-4eb0-ab16-1fac1d522a33@xxxxxx.xx\n" "LABEL;TYPE=home:My Label\r\n" "REV:2015-03-14T09:24:45+00:00\n" "FN:FirstName LastName\n" "END:VCARD\n"); KContacts::VCardTool vcard; const KContacts::AddresseeList lst = vcard.parseVCards(vcarddata); QCOMPARE(lst.count(), 1); QCOMPARE(lst.at(0).addresses().count(), 1); KContacts::Address address = lst.at(0).addresses().at(0); QCOMPARE(address.type(), KContacts::Address::Home); QCOMPARE(address.postOfficeBox(), QStringLiteral("1234")); QCOMPARE(address.extended(), QStringLiteral("My Extended Label")); QCOMPARE(address.region(), QStringLiteral("My Region")); QCOMPARE(address.street(), QStringLiteral("My Street")); QCOMPARE(address.country(), QStringLiteral("My country")); QCOMPARE(address.postalCode(), QStringLiteral("My Postalcode")); QCOMPARE(address.label(), QStringLiteral("My Label")); } void AddressTest::shouldParseAddressVCard4() { QByteArray vcarddata("BEGIN:VCARD\r\n" "VERSION:4.0\r\n" "ADR;LABEL=\"My Label\";TYPE=home:1234;My Extended Label;My Street;My Locality\r\n" " ;My Region;My Postalcode;My country\r\nEMAIL:foo@kde.org\r\n" "N:;;;;\r\n" "UID:testuid\r\n" "END:VCARD\r\n\r\n"); KContacts::VCardTool vcard; const KContacts::AddresseeList lst = vcard.parseVCards(vcarddata); QCOMPARE(lst.count(), 1); QCOMPARE(lst.at(0).addresses().count(), 1); KContacts::Address address = lst.at(0).addresses().at(0); QCOMPARE(address.type(), KContacts::Address::Home); QCOMPARE(address.postOfficeBox(), QStringLiteral("1234")); QCOMPARE(address.extended(), QStringLiteral("My Extended Label")); QCOMPARE(address.region(), QStringLiteral("My Region")); QCOMPARE(address.street(), QStringLiteral("My Street")); QCOMPARE(address.country(), QStringLiteral("My country")); QCOMPARE(address.postalCode(), QStringLiteral("My Postalcode")); QCOMPARE(address.label(), QStringLiteral("My Label")); } void AddressTest::shouldParseAddressVCard4WithGeoPosition() { QByteArray vcarddata("BEGIN:VCARD\r\n" "VERSION:4.0\r\n" "ADR;GEO=\"geo:22.500000,45.099998\";LABEL=\"My Label\";TYPE=home:1234;My Extend\r\n" " ed Label;My Street;My Locality;My Region;My Postalcode;My country\r\n" "EMAIL:foo@kde.org\r\n" "N:;;;;\r\n" "UID:testuid\r\n" "END:VCARD\r\n\r\n"); KContacts::VCardTool vcard; const KContacts::AddresseeList lst = vcard.parseVCards(vcarddata); QCOMPARE(lst.count(), 1); QCOMPARE(lst.at(0).addresses().count(), 1); KContacts::Address address = lst.at(0).addresses().at(0); QCOMPARE(address.type(), KContacts::Address::Home); QCOMPARE(address.postOfficeBox(), QStringLiteral("1234")); QCOMPARE(address.extended(), QStringLiteral("My Extended Label")); QCOMPARE(address.region(), QStringLiteral("My Region")); QCOMPARE(address.street(), QStringLiteral("My Street")); QCOMPARE(address.country(), QStringLiteral("My country")); QCOMPARE(address.postalCode(), QStringLiteral("My Postalcode")); QCOMPARE(address.label(), QStringLiteral("My Label")); KContacts::Geo geo = address.geo(); QVERIFY(geo.isValid()); QCOMPARE(geo.latitude(), 22.5f); QCOMPARE(geo.longitude(), 45.1f); } void AddressTest::shouldExportVCard4WithGeoPosition() { KContacts::Address address; address.setId(QStringLiteral("My Id")); address.setType(KContacts::Address::Home); address.setPostOfficeBox(QStringLiteral("1234")); address.setExtended(QStringLiteral("My Extended Label")); address.setStreet(QStringLiteral("My Street")); address.setLocality(QStringLiteral("My Locality")); address.setRegion(QStringLiteral("My Region")); address.setPostalCode(QStringLiteral("My Postalcode")); address.setCountry(QStringLiteral("My country")); address.setLabel(QStringLiteral("My Label")); KContacts::Geo geo; geo.setLatitude(22.5f); geo.setLongitude(45.1f); address.setGeo(geo); KContacts::AddresseeList lst; KContacts::Addressee addr; addr.setEmails(QStringList() << QStringLiteral("foo@kde.org")); addr.setUid(QStringLiteral("testuid")); addr.insertAddress(address); lst << addr; KContacts::VCardTool vcard; const QByteArray ba = vcard.exportVCards(lst, KContacts::VCard::v4_0); QByteArray expected("BEGIN:VCARD\r\n" "VERSION:4.0\r\n" "ADR;GEO=\"geo:22.500000,45.099998\";LABEL=\"My Label\";TYPE=home:1234;My Extend\r\n" " ed Label;My Street;My Locality;My Region;My Postalcode;My country\r\n" "EMAIL:foo@kde.org\r\n" "N:;;;;\r\n" "UID:testuid\r\n" "END:VCARD\r\n\r\n"); QCOMPARE(ba, expected); } void AddressTest::shouldExportVcard4() { KContacts::Address address; address.setId(QStringLiteral("My Id")); address.setType(KContacts::Address::Home); address.setPostOfficeBox(QStringLiteral("1234")); address.setExtended(QStringLiteral("My Extended Label")); address.setStreet(QStringLiteral("My Street")); address.setLocality(QStringLiteral("My Locality")); address.setRegion(QStringLiteral("My Region")); address.setPostalCode(QStringLiteral("My Postalcode")); address.setCountry(QStringLiteral("My country")); address.setLabel(QStringLiteral("My Label")); KContacts::AddresseeList lst; KContacts::Addressee addr; addr.setEmails(QStringList() << QStringLiteral("foo@kde.org")); addr.setUid(QStringLiteral("testuid")); addr.insertAddress(address); lst << addr; KContacts::VCardTool vcard; const QByteArray ba = vcard.exportVCards(lst, KContacts::VCard::v4_0); QByteArray expected("BEGIN:VCARD\r\n" "VERSION:4.0\r\n" "ADR;LABEL=\"My Label\";TYPE=home:1234;My Extended Label;My Street;My Locality\r\n" " ;My Region;My Postalcode;My country\r\nEMAIL:foo@kde.org\r\n" "N:;;;;\r\n" "UID:testuid\r\n" "END:VCARD\r\n\r\n"); QCOMPARE(ba, expected); } + +void AddressTest::countryToISOTest() +{ + using namespace KContacts; + QCOMPARE(Address::countryToISO(QStringLiteral("France")), QLatin1String("fr")); + QCOMPARE(Address::countryToISO(QStringLiteral("Frankreich")), QLatin1String("fr")); + QCOMPARE(Address::countryToISO(QStringLiteral("Germany")), QLatin1String("de")); + QCOMPARE(Address::countryToISO(QStringLiteral("Österreich")), QLatin1String("at")); + QCOMPARE(Address::countryToISO(QStringLiteral("Disneyland")), QString()); +} diff --git a/autotests/addresstest.h b/autotests/addresstest.h index e6d5d26d..efcadc49 100644 --- a/autotests/addresstest.h +++ b/autotests/addresstest.h @@ -1,47 +1,48 @@ /* This file is part of the KContacts framework. Copyright (c) 2007 Tobias Koenig This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef ADDRESS_TEST_H #define ADDRESS_TEST_H #include class AddressTest : public QObject { Q_OBJECT private Q_SLOTS: void initTestCase(); void emptyTest(); void storeTest(); void equalsTest(); void differsTest(); void assignmentTest(); void serializeTest(); void formatTest(); void shouldExportVcard4(); void shouldExportVcard3(); void shouldParseAddressVCard3(); void shouldParseAddressVCard4(); void shouldExportVCard4WithGeoPosition(); void shouldParseAddressVCard4WithGeoPosition(); + void countryToISOTest(); }; #endif diff --git a/src/address.cpp b/src/address.cpp index 8fecae52..4103d2b7 100644 --- a/src/address.cpp +++ b/src/address.cpp @@ -1,728 +1,725 @@ /* This file is part of the KContacts framework. Copyright (c) 2001 Cornelius Schumacher This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "address.h" #include "geo.h" #include "kcontacts_debug.h" #include #include #include #include #include #include #include #include #include #include #include using namespace KContacts; // template tags for address formatting localization #define KCONTACTS_FMTTAG_realname QStringLiteral("%n") #define KCONTACTS_FMTTAG_REALNAME QStringLiteral("%N") #define KCONTACTS_FMTTAG_company QStringLiteral("%cm") #define KCONTACTS_FMTTAG_COMPANY QStringLiteral("%CM") #define KCONTACTS_FMTTAG_pobox QStringLiteral("%p") #define KCONTACTS_FMTTAG_street QStringLiteral("%s") #define KCONTACTS_FMTTAG_STREET QStringLiteral("%S") #define KCONTACTS_FMTTAG_zipcode QStringLiteral("%z") #define KCONTACTS_FMTTAG_location QStringLiteral("%l") #define KCONTACTS_FMTTAG_LOCATION QStringLiteral("%L") #define KCONTACTS_FMTTAG_region QStringLiteral("%r") #define KCONTACTS_FMTTAG_REGION QStringLiteral("%R") #define KCONTACTS_FMTTAG_newline QStringLiteral("\\n") #define KCONTACTS_FMTTAG_condcomma QStringLiteral("%,") #define KCONTACTS_FMTTAG_condwhite QStringLiteral("%w") #define KCONTACTS_FMTTAG_purgeempty QStringLiteral("%0") /** Finds the balanced closing bracket starting from the opening bracket at pos in tsection. @return position of closing bracket, -1 for unbalanced brackets */ static int findBalancedBracket(const QString &tsection, int pos) { int balancecounter = 0; for (int i = pos + 1; i < tsection.length(); ++i) { if (QLatin1Char(')') == tsection[i] && 0 == balancecounter) { // found end of brackets return i; } else { if (QLatin1Char('(') == tsection[i]) { // nested brackets balancecounter++; } } } return -1; } /** Parses a snippet of an address template @param tsection the template string to be parsed @param result QString reference in which the result will be stored @return true if at least one tag evaluated positively, else false */ static bool parseAddressTemplateSection(const QString &tsection, QString &result, const QString &realName, const QString &orgaName, const KContacts::Address &address) { // This method first parses and substitutes any bracketed sections and // after that replaces any tags with their values. If a bracketed section // or a tag evaluate to zero, they are not just removed but replaced // with a placeholder. This is because in the last step conditionals are // resolved which depend on information about zero-evaluations. result = tsection; int stpos = 0; bool ret = false; // first check for brackets that have to be evaluated first int fpos = result.indexOf(KCONTACTS_FMTTAG_purgeempty, stpos); while (-1 != fpos) { int bpos1 = fpos + KCONTACTS_FMTTAG_purgeempty.length(); // expect opening bracket and find next balanced closing bracket. If // next char is no opening bracket, continue parsing (no valid tag) if (QLatin1Char('(') == result[bpos1]) { int bpos2 = findBalancedBracket(result, bpos1); if (-1 != bpos2) { // we have balanced brackets, recursively parse: QString rplstr; bool purge = !parseAddressTemplateSection(result.mid(bpos1 + 1, bpos2 - bpos1 - 1), rplstr, realName, orgaName, address); if (purge) { // purge -> remove all // replace with !_P_!, so conditional tags work later result.replace(fpos, bpos2 - fpos + 1, QStringLiteral("!_P_!")); // leave stpos as it is } else { // no purge -> replace with recursively parsed string result.replace(fpos, bpos2 - fpos + 1, rplstr); ret = true; stpos = fpos + rplstr.length(); } } else { // unbalanced brackets: keep on parsing (should not happen // and will result in bad formatting) stpos = bpos1; } } fpos = result.indexOf(KCONTACTS_FMTTAG_purgeempty, stpos); } // after sorting out all purge tags, we just search'n'replace the rest, // keeping track of whether at least one tag evaluates to something. // The following macro needs QString for R_FIELD // It substitutes !_P_! for empty fields so conditional tags work later #define REPLTAG(R_TAG, R_FIELD) \ if (result.contains(R_TAG)) { \ QString rpl = R_FIELD.isEmpty() ? QStringLiteral("!_P_!") : R_FIELD; \ result.replace(R_TAG, rpl); \ if (!R_FIELD.isEmpty()) { \ ret = true; \ } \ } REPLTAG(KCONTACTS_FMTTAG_realname, realName); REPLTAG(KCONTACTS_FMTTAG_REALNAME, realName.toUpper()); REPLTAG(KCONTACTS_FMTTAG_company, orgaName); REPLTAG(KCONTACTS_FMTTAG_COMPANY, orgaName.toUpper()); REPLTAG(KCONTACTS_FMTTAG_pobox, address.postOfficeBox()); REPLTAG(KCONTACTS_FMTTAG_street, address.street()); REPLTAG(KCONTACTS_FMTTAG_STREET, address.street().toUpper()); REPLTAG(KCONTACTS_FMTTAG_zipcode, address.postalCode()); REPLTAG(KCONTACTS_FMTTAG_location, address.locality()); REPLTAG(KCONTACTS_FMTTAG_LOCATION, address.locality().toUpper()); REPLTAG(KCONTACTS_FMTTAG_region, address.region()); REPLTAG(KCONTACTS_FMTTAG_REGION, address.region().toUpper()); result.replace(KCONTACTS_FMTTAG_newline, QLatin1String("\n")); #undef REPLTAG // conditional comma fpos = result.indexOf(KCONTACTS_FMTTAG_condcomma, 0); while (-1 != fpos) { const QString str1 = result.mid(fpos - 5, 5); const QString str2 = result.mid(fpos + 2, 5); if (str1 != QLatin1String("!_P_!") && str2 != QLatin1String("!_P_!")) { result.replace(fpos, 2, QStringLiteral(", ")); } else { result.remove(fpos, 2); } fpos = result.indexOf(KCONTACTS_FMTTAG_condcomma, fpos); } // conditional whitespace fpos = result.indexOf(KCONTACTS_FMTTAG_condwhite, 0); while (-1 != fpos) { const QString str1 = result.mid(fpos - 5, 5); const QString str2 = result.mid(fpos + 2, 5); if (str1 != QLatin1String("!_P_!") && str2 != QLatin1String("!_P_!")) { result.replace(fpos, 2, QLatin1Char(' ')); } else { result.remove(fpos, 2); } fpos = result.indexOf(KCONTACTS_FMTTAG_condwhite, fpos); } // remove purged: result.remove(QStringLiteral("!_P_!")); return ret; } class Q_DECL_HIDDEN Address::Private : public QSharedData { public: Private() : mEmpty(true) , mType(nullptr) { mId = KRandom::randomString(10); } Private(const Private &other) : QSharedData(other) { mEmpty = other.mEmpty; mId = other.mId; mType = other.mType; mPostOfficeBox = other.mPostOfficeBox; mExtended = other.mExtended; mStreet = other.mStreet; mLocality = other.mLocality; mRegion = other.mRegion; mPostalCode = other.mPostalCode; mCountry = other.mCountry; mLabel = other.mLabel; } bool mEmpty; QString mId; Type mType; Geo mGeo; QString mPostOfficeBox; QString mExtended; QString mStreet; QString mLocality; QString mRegion; QString mPostalCode; QString mCountry; QString mLabel; }; Address::Address() : d(new Private) { } Address::Address(Type type) : d(new Private) { d->mType = type; } Address::Address(const Address &other) : d(other.d) { } Address::~Address() { } Address &Address::operator=(const Address &other) { if (this != &other) { d = other.d; } return *this; } bool Address::operator==(const Address &other) const { if (d->mId != other.d->mId) { return false; } if (d->mType != other.d->mType) { return false; } if (d->mPostOfficeBox != other.d->mPostOfficeBox) { return false; } if (d->mExtended != other.d->mExtended) { return false; } if (d->mStreet != other.d->mStreet) { return false; } if (d->mLocality != other.d->mLocality) { return false; } if (d->mRegion != other.d->mRegion) { return false; } if (d->mPostalCode != other.d->mPostalCode) { return false; } if (d->mCountry != other.d->mCountry) { return false; } if (d->mLabel != other.d->mLabel) { return false; } if (d->mGeo != other.d->mGeo) { return false; } return true; } bool Address::operator!=(const Address &a) const { return !(a == *this); } bool Address::isEmpty() const { return d->mEmpty; } void Address::clear() { *this = Address(); } void Address::setId(const QString &id) { d->mEmpty = false; d->mId = id; } QString Address::id() const { return d->mId; } void Address::setType(Type type) { d->mEmpty = false; d->mType = type; } Address::Type Address::type() const { return d->mType; } QString Address::typeLabel(Type type) { QString label; bool first = true; const TypeList list = typeList(); TypeList::ConstIterator it; TypeList::ConstIterator end(list.end()); for (it = list.begin(); it != end; ++it) { // these are actually flags const TypeFlag flag = static_cast(static_cast(*it)); if (type & flag) { if (!first) { label.append(QLatin1Char('/')); } label.append(typeFlagLabel(flag)); if (first) { first = false; } } } return label; } QString Address::typeLabel() const { QString label; bool first = true; const TypeList list = typeList(); TypeList::ConstIterator it; for (it = list.begin(); it != list.end(); ++it) { if ((type() & (*it)) && ((*it) != Pref)) { if (!first) { label.append(QLatin1Char('/')); } label.append(typeLabel(*it)); if (first) { first = false; } } } return label; } void Address::setPostOfficeBox(const QString &postOfficeBox) { d->mEmpty = false; d->mPostOfficeBox = postOfficeBox; } QString Address::postOfficeBox() const { return d->mPostOfficeBox; } QString Address::postOfficeBoxLabel() { return i18n("Post Office Box"); } void Address::setExtended(const QString &extended) { d->mEmpty = false; d->mExtended = extended; } QString Address::extended() const { return d->mExtended; } QString Address::extendedLabel() { return i18n("Extended Address Information"); } void Address::setStreet(const QString &street) { d->mEmpty = false; d->mStreet = street; } QString Address::street() const { return d->mStreet; } QString Address::streetLabel() { return i18n("Street"); } void Address::setLocality(const QString &locality) { d->mEmpty = false; d->mLocality = locality; } QString Address::locality() const { return d->mLocality; } QString Address::localityLabel() { return i18n("Locality"); } void Address::setRegion(const QString ®ion) { d->mEmpty = false; d->mRegion = region; } QString Address::region() const { return d->mRegion; } QString Address::regionLabel() { return i18n("Region"); } void Address::setPostalCode(const QString &postalCode) { d->mEmpty = false; d->mPostalCode = postalCode; } QString Address::postalCode() const { return d->mPostalCode; } QString Address::postalCodeLabel() { return i18n("Postal Code"); } void Address::setCountry(const QString &country) { d->mEmpty = false; d->mCountry = country; } QString Address::country() const { return d->mCountry; } QString Address::countryLabel() { return i18n("Country"); } void Address::setLabel(const QString &label) { d->mEmpty = false; d->mLabel = label; } QString Address::label() const { return d->mLabel; } QString Address::labelLabel() { return i18n("Delivery Label"); } Address::TypeList Address::typeList() { static TypeList list; if (list.isEmpty()) { list << Dom << Intl << Postal << Parcel << Home << Work << Pref; } return list; } QString Address::typeFlagLabel(TypeFlag type) { switch (type) { case Dom: return i18nc("Address is in home country", "Domestic"); case Intl: return i18nc("Address is not in home country", "International"); case Postal: return i18nc("Address for delivering letters", "Postal"); case Parcel: return i18nc("Address for delivering packages", "Parcel"); case Home: return i18nc("Home Address", "Home"); case Work: return i18nc("Work Address", "Work"); case Pref: return i18n("Preferred Address"); } return i18nc("another type of address", "Other"); } void Address::setGeo(const Geo &geo) { d->mEmpty = false; d->mGeo = geo; } Geo Address::geo() const { return d->mGeo; } QString Address::toString() const { QString str = QLatin1String("Address {\n"); str += QStringLiteral(" IsEmpty: %1\n"). arg(d->mEmpty ? QStringLiteral("true") : QStringLiteral("false")); str += QStringLiteral(" Id: %1\n").arg(d->mId); str += QStringLiteral(" Type: %1\n").arg(typeLabel(d->mType)); str += QStringLiteral(" Post office box: %1\n").arg(d->mPostOfficeBox); str += QStringLiteral(" Extended: %1\n").arg(d->mExtended); str += QStringLiteral(" Street: %1\n").arg(d->mStreet); str += QStringLiteral(" Locality: %1\n").arg(d->mLocality); str += QStringLiteral(" Region: %1\n").arg(d->mRegion); str += QStringLiteral(" Postal code: %1\n").arg(d->mPostalCode); str += QStringLiteral(" Country: %1\n").arg(d->mCountry); str += QStringLiteral(" Label: %1\n").arg(d->mLabel); str += QStringLiteral(" Geo: %1\n").arg(d->mGeo.toString()); str += QLatin1String("}\n"); return str; } QString Address::formattedAddress(const QString &realName, const QString &orgaName) const { QString ciso; QString addrTemplate; QString ret; // FIXME: first check for iso-country-field and prefer that one if (!country().isEmpty()) { ciso = countryToISO(country()); } else { // fall back to our own country ciso = QLocale().bcp47Name(); } KConfig entry(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QLatin1String("kf5/locale/countries/") + ciso + QLatin1String("/country.desktop"))); KConfigGroup group = entry.group("KCM Locale"); // decide whether this needs special business address formatting if (orgaName.isEmpty()) { addrTemplate = group.readEntry("AddressFormat"); } else { addrTemplate = group.readEntry("BusinessAddressFormat"); if (addrTemplate.isEmpty()) { addrTemplate = group.readEntry("AddressFormat"); } } // in the case there's no format found at all, default to what we've always // used: if (addrTemplate.isEmpty()) { qCWarning(KCONTACTS_LOG) << "address format database incomplete" << "(no format for locale" << ciso << "found). Using default address formatting."; addrTemplate = QStringLiteral("%0(%n\\n)%0(%cm\\n)%0(%s\\n)%0(PO BOX %p\\n)%0(%l%w%r)%,%z"); } // scan parseAddressTemplateSection(addrTemplate, ret, realName, orgaName, *this); // now add the country line if needed (formatting this time according to // the rules of our own system country ) if (!country().isEmpty()) { KConfig entry(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QLatin1String("kf5/locale/countries/") +QLocale().name() +QLatin1String("/country.desktop"))); KConfigGroup group = entry.group("KCM Locale"); QString cpos = group.readEntry("AddressCountryPosition"); if (QLatin1String("BELOW") == cpos || cpos.isEmpty()) { ret = ret + QLatin1String("\n\n") + country().toUpper(); } else if (QLatin1String("below") == cpos) { ret = ret + QLatin1String("\n\n") + country(); } else if (QLatin1String("ABOVE") == cpos) { ret = country().toUpper() + QLatin1String("\n\n") + ret; } else if (QLatin1String("above") == cpos) { ret = country() + QLatin1String("\n\n") + ret; } } return ret; } typedef QMap StringMap; Q_GLOBAL_STATIC(StringMap, sISOMap) QString Address::countryToISO(const QString &cname) { // we search a map file for translations from country names to // iso codes, storing caching things in a QMap for faster future // access. QMap::ConstIterator it; it = sISOMap->constFind(cname); if (it != sISOMap->constEnd()) { return it.value(); } QString mapfile = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("kf5/kcontacts/countrytransl.map")); if (mapfile.isEmpty()) { qWarning() << "Installation error, couldn't find the countrytransl.map file"; } QFile file(mapfile); if (file.open(QIODevice::ReadOnly)) { QTextStream s(&file); QString strbuf = s.readLine(); while (!strbuf.isEmpty()) { QStringList countryInfo = strbuf.split(QLatin1Char('\t'), QString::KeepEmptyParts); if (countryInfo[0] == cname) { file.close(); sISOMap->insert(cname, countryInfo[1]); return countryInfo[1]; } strbuf = s.readLine(); } file.close(); } - // fall back to system country - QString systemCountry = QLocale().bcp47Name(); - sISOMap->insert(cname, systemCountry); - return systemCountry; + return {}; } QString Address::ISOtoCountry(const QString &ISOname) { // get country name from ISO country code (e.g. "no" -> i18n("Norway")) if (ISOname.simplified().isEmpty()) { return QString(); } QString mapfile = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("kf5/kcontacts/countrytransl.map")); QFile file(mapfile); if (file.open(QIODevice::ReadOnly)) { QTextStream s(&file); QString searchStr = QLatin1Char('\t') + ISOname.simplified().toLower(); QString strbuf = s.readLine(); while (!strbuf.isEmpty()) { int pos; if ((pos = strbuf.indexOf(searchStr)) != -1) { file.close(); return i18n(strbuf.left(pos).toUtf8().constData()); } strbuf = s.readLine(); } file.close(); } return ISOname; } QDataStream &KContacts::operator<<(QDataStream &s, const Address &addr) { return s << addr.d->mId << (uint)addr.d->mType << addr.d->mPostOfficeBox << addr.d->mExtended << addr.d->mStreet << addr.d->mLocality << addr.d->mRegion << addr.d->mPostalCode << addr.d->mCountry << addr.d->mLabel << addr.d->mEmpty << addr.d->mGeo; } QDataStream &KContacts::operator>>(QDataStream &s, Address &addr) { uint type; s >> addr.d->mId >> type >> addr.d->mPostOfficeBox >> addr.d->mExtended >> addr.d->mStreet >> addr.d->mLocality >> addr.d->mRegion >> addr.d->mPostalCode >> addr.d->mCountry >> addr.d->mLabel >> addr.d->mEmpty >> addr.d->mGeo; addr.d->mType = Address::Type(type); return s; } diff --git a/src/address.h b/src/address.h index ad0ef15d..8f0e92b4 100644 --- a/src/address.h +++ b/src/address.h @@ -1,351 +1,351 @@ /* This file is part of the KContacts framework. Copyright (c) 2001 Cornelius Schumacher This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KCONTACTS_ADDRESS_H #define KCONTACTS_ADDRESS_H #include #include #include #include "kcontacts_export.h" namespace KContacts { class Geo; /** @brief Postal address information. This class represents information about a postal address. */ class KCONTACTS_EXPORT Address { friend KCONTACTS_EXPORT QDataStream &operator<<(QDataStream &s, const Address &addr); friend KCONTACTS_EXPORT QDataStream &operator>>(QDataStream &s, Address &addr); public: /** List of addresses. */ typedef QVector
List; /** Address types: */ enum TypeFlag { Dom = 1, /**< domestic */ Intl = 2, /**< international */ Postal = 4, /**< postal */ Parcel = 8, /**< parcel */ Home = 16, /**< home address */ Work = 32, /**< address at work */ Pref = 64 /**< preferred address */ }; Q_DECLARE_FLAGS(Type, TypeFlag) /** List of address types. */ typedef QList TypeList; /** Creates an empty address. */ Address(); /** Creates an address of the given @p type. */ Address(Type type); /** Copy constructor. */ Address(const Address &address); /** Destroys the address. */ ~Address(); /** Equality operator. @param addr the address to compare to @return @c true if @c this and @p addr are equal, otherwise @c false */ bool operator==(const Address &other) const; /** Not-equal operator. @param addr the address to compare to @return @c true if @c this and @p addr are not equal, otherwise @c false */ bool operator!=(const Address &other) const; /** Assignment operator. @param addr the address data to assign to @c this @return a reference to @c this */ Address &operator=(const Address &other); /** Returns true, if the address is empty. */ bool isEmpty() const; /** Clears all entries of the address. */ void clear(); /** Sets the unique @p identifier. */ void setId(const QString &identifier); /** Returns the unique identifier. */ QString id() const; /** Sets the type of address. See enum for definiton of types. @param type type, can be a bitwise or of multiple types. */ void setType(Type type); /** Returns the type of address. Can be a bitwise or of multiple types. */ Type type() const; /** Returns a translated string of all types the address has. */ QString typeLabel() const; /** Sets the post office box. */ void setPostOfficeBox(const QString &postOfficeBox); /** Returns the post office box. */ QString postOfficeBox() const; /** Returns the translated label for post office box field. */ static QString postOfficeBoxLabel(); /** Sets the @p extended address information. */ void setExtended(const QString &extended); /** Returns the extended address information. */ QString extended() const; /** Returns the translated label for extended field. */ static QString extendedLabel(); /** Sets the @p street (including house number). */ void setStreet(const QString &street); /** Returns the street. */ QString street() const; /** Returns the translated label for street field. */ static QString streetLabel(); /** Sets the @p locality, e.g. city. @param locality the locality of the address, e.g. city */ void setLocality(const QString &locality); /** Returns the locality. */ QString locality() const; /** Returns the translated label for locality field. */ static QString localityLabel(); /** Sets the @p region, e.g. state. @param region the region the address falls into, e.g. state */ void setRegion(const QString ®ion); /** Returns the region. */ QString region() const; /** Returns the translated label for region field. */ static QString regionLabel(); /** Sets the postal @p code. */ void setPostalCode(const QString &code); /** Returns the postal code. */ QString postalCode() const; /** Returns the translated label for postal code field. */ static QString postalCodeLabel(); /** Sets the @p country. */ void setCountry(const QString &country); /** Returns the country. */ QString country() const; /** Returns the translated label for country field. */ static QString countryLabel(); /** Sets the delivery @p label. This is the literal text to be used as label. @param label the string to use for delivery labels */ void setLabel(const QString &label); /** Returns the delivery label. */ QString label() const; /** Returns the translated label for delivery label field. */ static QString labelLabel(); /** Returns the list of available types. */ static TypeList typeList(); /** Returns the translated label for the given @p type. */ static QString typeLabel(Type type); /** Returns a string representation of the address. */ QString toString() const; /** Returns this address formatted according to the country-specific address formatting rules. The formatting rules applied depend on either the addresses {@link #country country} field, or (if the latter is empty) on the system country setting. If companyName is provided, an available business address format will be preferred. @param realName the formatted name of the contact @param orgaName the name of the organization or company @return the formatted address (containing newline characters) */ QString formattedAddress(const QString &realName = QString(), const QString &orgaName = QString()) const; /** Returns ISO code for a localized country name. Only localized country - names will be understood. This might be replaced by a KLocale method in - the future. + names will be understood. @param cname name of the country - @return two digit ISO code + @return two digit ISO code, empty string if the country was not + recognized */ static QString countryToISO(const QString &cname); /** Returns a localized country name for a ISO code. This might be replaced by a KLocale method in the future. @param ISOname two digit ISO code @return localized name of the country */ static QString ISOtoCountry(const QString &ISOname); static QString typeFlagLabel(TypeFlag type); /** Set geographic position. */ void setGeo(const Geo &geo); /** Return geographic position. */ Geo geo() const; private: class Private; QSharedDataPointer d; }; Q_DECLARE_OPERATORS_FOR_FLAGS(Address::Type) /** Serializes the @p address object into the @p stream. */ KCONTACTS_EXPORT QDataStream &operator<<(QDataStream &stream, const Address &address); /** Initializes the @p address object from the @p stream. */ KCONTACTS_EXPORT QDataStream &operator>>(QDataStream &stream, Address &address); } #endif