| 1 | /* |
| 2 | This file is part of the KContacts framework. |
| 3 | SPDX-FileCopyrightText: 2001 Cornelius Schumacher <schumacher@kde.org> |
| 4 | |
| 5 | SPDX-License-Identifier: LGPL-2.0-or-later |
| 6 | */ |
| 7 | |
| 8 | #include "address.h" |
| 9 | #include "addressformat.h" |
| 10 | #include "addressformatter_p.h" |
| 11 | |
| 12 | #include "kcontacts_debug.h" |
| 13 | #include <KConfig> |
| 14 | #include <KCountry> |
| 15 | #include <KLocalizedString> |
| 16 | #include <krandom.h> |
| 17 | |
| 18 | #include <KConfigGroup> |
| 19 | |
| 20 | #include <QDataStream> |
| 21 | #include <QSharedData> |
| 22 | #include <QUrlQuery> |
| 23 | |
| 24 | using namespace KContacts; |
| 25 | |
| 26 | class Q_DECL_HIDDEN Address::Private : public QSharedData |
| 27 | { |
| 28 | public: |
| 29 | Private() |
| 30 | : mEmpty(true) |
| 31 | { |
| 32 | mId = KRandom::randomString(length: 10); |
| 33 | } |
| 34 | |
| 35 | Private(const Private &other) |
| 36 | : QSharedData(other) |
| 37 | { |
| 38 | mEmpty = other.mEmpty; |
| 39 | mId = other.mId; |
| 40 | mType = other.mType; |
| 41 | |
| 42 | mPostOfficeBox = other.mPostOfficeBox; |
| 43 | mExtended = other.mExtended; |
| 44 | mStreet = other.mStreet; |
| 45 | mLocality = other.mLocality; |
| 46 | mRegion = other.mRegion; |
| 47 | mPostalCode = other.mPostalCode; |
| 48 | mCountry = other.mCountry; |
| 49 | mLabel = other.mLabel; |
| 50 | } |
| 51 | |
| 52 | bool mEmpty; |
| 53 | QString mId; |
| 54 | Type mType; |
| 55 | Geo mGeo; |
| 56 | |
| 57 | QString mPostOfficeBox; |
| 58 | QString mExtended; |
| 59 | QString mStreet; |
| 60 | QString mLocality; |
| 61 | QString mRegion; |
| 62 | QString mPostalCode; |
| 63 | QString mCountry; |
| 64 | QString mLabel; |
| 65 | }; |
| 66 | |
| 67 | Address::Address() |
| 68 | : d(new Private) |
| 69 | { |
| 70 | } |
| 71 | |
| 72 | Address::Address(Type type) |
| 73 | : d(new Private) |
| 74 | { |
| 75 | d->mType = type; |
| 76 | } |
| 77 | |
| 78 | Address::Address(const Address &other) |
| 79 | : d(other.d) |
| 80 | { |
| 81 | } |
| 82 | |
| 83 | Address::~Address() |
| 84 | { |
| 85 | } |
| 86 | |
| 87 | Address &Address::operator=(const Address &other) |
| 88 | { |
| 89 | if (this != &other) { |
| 90 | d = other.d; |
| 91 | } |
| 92 | |
| 93 | return *this; |
| 94 | } |
| 95 | |
| 96 | bool Address::operator==(const Address &other) const |
| 97 | { |
| 98 | if (d->mId != other.d->mId) { |
| 99 | return false; |
| 100 | } |
| 101 | if (d->mType != other.d->mType) { |
| 102 | return false; |
| 103 | } |
| 104 | if (d->mPostOfficeBox != other.d->mPostOfficeBox) { |
| 105 | return false; |
| 106 | } |
| 107 | if (d->mExtended != other.d->mExtended) { |
| 108 | return false; |
| 109 | } |
| 110 | if (d->mStreet != other.d->mStreet) { |
| 111 | return false; |
| 112 | } |
| 113 | if (d->mLocality != other.d->mLocality) { |
| 114 | return false; |
| 115 | } |
| 116 | if (d->mRegion != other.d->mRegion) { |
| 117 | return false; |
| 118 | } |
| 119 | if (d->mPostalCode != other.d->mPostalCode) { |
| 120 | return false; |
| 121 | } |
| 122 | if (d->mCountry != other.d->mCountry) { |
| 123 | return false; |
| 124 | } |
| 125 | if (d->mLabel != other.d->mLabel) { |
| 126 | return false; |
| 127 | } |
| 128 | |
| 129 | if (d->mGeo != other.d->mGeo) { |
| 130 | return false; |
| 131 | } |
| 132 | |
| 133 | return true; |
| 134 | } |
| 135 | |
| 136 | bool Address::operator!=(const Address &a) const |
| 137 | { |
| 138 | return !(a == *this); |
| 139 | } |
| 140 | |
| 141 | bool Address::isEmpty() const |
| 142 | { |
| 143 | return d->mEmpty; |
| 144 | } |
| 145 | |
| 146 | void Address::clear() |
| 147 | { |
| 148 | *this = Address(); |
| 149 | } |
| 150 | |
| 151 | void Address::setId(const QString &id) |
| 152 | { |
| 153 | d->mEmpty = false; |
| 154 | d->mId = id; |
| 155 | } |
| 156 | |
| 157 | QString Address::id() const |
| 158 | { |
| 159 | return d->mId; |
| 160 | } |
| 161 | |
| 162 | void Address::setType(Type type) |
| 163 | { |
| 164 | d->mEmpty = false; |
| 165 | d->mType = type; |
| 166 | } |
| 167 | |
| 168 | Address::Type Address::type() const |
| 169 | { |
| 170 | return d->mType; |
| 171 | } |
| 172 | |
| 173 | QString Address::typeLabel(Type type) |
| 174 | { |
| 175 | QString label; |
| 176 | const TypeList list = typeList(); |
| 177 | |
| 178 | for (const auto typeFlag : list) { |
| 179 | // these are actually flags |
| 180 | const TypeFlag flag = static_cast<TypeFlag>(static_cast<int>(typeFlag)); |
| 181 | if (type & flag) { |
| 182 | label.append(s: QLatin1Char('/') + typeFlagLabel(type: flag)); |
| 183 | } |
| 184 | } |
| 185 | |
| 186 | // Remove the first '/' |
| 187 | if (!label.isEmpty()) { |
| 188 | label.remove(i: 0, len: 1); |
| 189 | } |
| 190 | |
| 191 | return label; |
| 192 | } |
| 193 | |
| 194 | QString Address::typeLabel() const |
| 195 | { |
| 196 | QString label; |
| 197 | const TypeList list = typeList(); |
| 198 | |
| 199 | for (const auto f : list) { |
| 200 | if ((type() & f) && (f != Pref)) { |
| 201 | label.append(s: QLatin1Char('/') + typeLabel(type: f)); |
| 202 | } |
| 203 | } |
| 204 | // Remove the first '/' |
| 205 | if (!label.isEmpty()) { |
| 206 | label.remove(i: 0, len: 1); |
| 207 | } |
| 208 | return label; |
| 209 | } |
| 210 | |
| 211 | void Address::setPostOfficeBox(const QString &postOfficeBox) |
| 212 | { |
| 213 | d->mEmpty = false; |
| 214 | d->mPostOfficeBox = postOfficeBox; |
| 215 | } |
| 216 | |
| 217 | QString Address::postOfficeBox() const |
| 218 | { |
| 219 | return d->mPostOfficeBox; |
| 220 | } |
| 221 | |
| 222 | QString Address::postOfficeBoxLabel() |
| 223 | { |
| 224 | return i18n("Post Office Box" ); |
| 225 | } |
| 226 | |
| 227 | void Address::setExtended(const QString &extended) |
| 228 | { |
| 229 | d->mEmpty = false; |
| 230 | d->mExtended = extended; |
| 231 | } |
| 232 | |
| 233 | QString Address::extended() const |
| 234 | { |
| 235 | return d->mExtended; |
| 236 | } |
| 237 | |
| 238 | QString Address::extendedLabel() |
| 239 | { |
| 240 | return i18n("Extended Address Information" ); |
| 241 | } |
| 242 | |
| 243 | void Address::setStreet(const QString &street) |
| 244 | { |
| 245 | d->mEmpty = false; |
| 246 | d->mStreet = street; |
| 247 | } |
| 248 | |
| 249 | QString Address::street() const |
| 250 | { |
| 251 | return d->mStreet; |
| 252 | } |
| 253 | |
| 254 | QString Address::streetLabel() |
| 255 | { |
| 256 | return i18n("Street" ); |
| 257 | } |
| 258 | |
| 259 | void Address::setLocality(const QString &locality) |
| 260 | { |
| 261 | d->mEmpty = false; |
| 262 | d->mLocality = locality; |
| 263 | } |
| 264 | |
| 265 | QString Address::locality() const |
| 266 | { |
| 267 | return d->mLocality; |
| 268 | } |
| 269 | |
| 270 | QString Address::localityLabel() |
| 271 | { |
| 272 | return i18n("Locality" ); |
| 273 | } |
| 274 | |
| 275 | void Address::setRegion(const QString ®ion) |
| 276 | { |
| 277 | d->mEmpty = false; |
| 278 | d->mRegion = region; |
| 279 | } |
| 280 | |
| 281 | QString Address::region() const |
| 282 | { |
| 283 | return d->mRegion; |
| 284 | } |
| 285 | |
| 286 | QString Address::regionLabel() |
| 287 | { |
| 288 | return i18n("Region" ); |
| 289 | } |
| 290 | |
| 291 | void Address::setPostalCode(const QString &postalCode) |
| 292 | { |
| 293 | d->mEmpty = false; |
| 294 | d->mPostalCode = postalCode; |
| 295 | } |
| 296 | |
| 297 | QString Address::postalCode() const |
| 298 | { |
| 299 | return d->mPostalCode; |
| 300 | } |
| 301 | |
| 302 | QString Address::postalCodeLabel() |
| 303 | { |
| 304 | return i18n("Postal Code" ); |
| 305 | } |
| 306 | |
| 307 | void Address::setCountry(const QString &country) |
| 308 | { |
| 309 | d->mEmpty = false; |
| 310 | d->mCountry = country; |
| 311 | } |
| 312 | |
| 313 | QString Address::country() const |
| 314 | { |
| 315 | return d->mCountry; |
| 316 | } |
| 317 | |
| 318 | QString Address::countryLabel() |
| 319 | { |
| 320 | return i18n("Country" ); |
| 321 | } |
| 322 | |
| 323 | void Address::setLabel(const QString &label) |
| 324 | { |
| 325 | d->mEmpty = false; |
| 326 | d->mLabel = label; |
| 327 | } |
| 328 | |
| 329 | QString Address::label() const |
| 330 | { |
| 331 | return d->mLabel; |
| 332 | } |
| 333 | |
| 334 | QString Address::labelLabel() |
| 335 | { |
| 336 | return i18n("Delivery Label" ); |
| 337 | } |
| 338 | |
| 339 | Address::TypeList Address::typeList() |
| 340 | { |
| 341 | static TypeList list; |
| 342 | |
| 343 | if (list.isEmpty()) { |
| 344 | list << Dom << Intl << Postal << Parcel << Home << Work << Pref; |
| 345 | } |
| 346 | |
| 347 | return list; |
| 348 | } |
| 349 | |
| 350 | QString Address::typeFlagLabel(TypeFlag type) |
| 351 | { |
| 352 | switch (type) { |
| 353 | case Dom: |
| 354 | return i18nc("Address is in home country" , "Domestic" ); |
| 355 | case Intl: |
| 356 | return i18nc("Address is not in home country" , "International" ); |
| 357 | case Postal: |
| 358 | return i18nc("Address for delivering letters" , "Postal" ); |
| 359 | case Parcel: |
| 360 | return i18nc("Address for delivering packages" , "Parcel" ); |
| 361 | case Home: |
| 362 | return i18nc("Home Address" , "Home" ); |
| 363 | case Work: |
| 364 | return i18nc("Work Address" , "Work" ); |
| 365 | case Pref: |
| 366 | return i18n("Preferred Address" ); |
| 367 | } |
| 368 | return i18nc("another type of address" , "Other" ); |
| 369 | } |
| 370 | |
| 371 | void Address::setGeo(const Geo &geo) |
| 372 | { |
| 373 | d->mEmpty = false; |
| 374 | d->mGeo = geo; |
| 375 | } |
| 376 | |
| 377 | Geo Address::geo() const |
| 378 | { |
| 379 | return d->mGeo; |
| 380 | } |
| 381 | |
| 382 | QString Address::toString() const |
| 383 | { |
| 384 | QString str = QLatin1String("Address {\n" ); |
| 385 | str += QStringLiteral(" IsEmpty: %1\n" ).arg(a: d->mEmpty ? QStringLiteral("true" ) : QStringLiteral("false" )); |
| 386 | str += QStringLiteral(" Id: %1\n" ).arg(a: d->mId); |
| 387 | str += QStringLiteral(" Type: %1\n" ).arg(a: typeLabel(type: d->mType)); |
| 388 | str += QStringLiteral(" Post office box: %1\n" ).arg(a: d->mPostOfficeBox); |
| 389 | str += QStringLiteral(" Extended: %1\n" ).arg(a: d->mExtended); |
| 390 | str += QStringLiteral(" Street: %1\n" ).arg(a: d->mStreet); |
| 391 | str += QStringLiteral(" Locality: %1\n" ).arg(a: d->mLocality); |
| 392 | str += QStringLiteral(" Region: %1\n" ).arg(a: d->mRegion); |
| 393 | str += QStringLiteral(" Postal code: %1\n" ).arg(a: d->mPostalCode); |
| 394 | str += QStringLiteral(" Country: %1\n" ).arg(a: d->mCountry); |
| 395 | str += QStringLiteral(" Label: %1\n" ).arg(a: d->mLabel); |
| 396 | str += QStringLiteral(" Geo: %1\n" ).arg(a: d->mGeo.toString()); |
| 397 | str += QLatin1String("}\n" ); |
| 398 | |
| 399 | return str; |
| 400 | } |
| 401 | |
| 402 | QString Address::formatted(AddressFormatStyle style, const QString &realName, const QString &orgaName) const |
| 403 | { |
| 404 | const auto formatPref = (orgaName.isEmpty() || style != AddressFormatStyle::Postal) ? AddressFormatPreference::Generic : AddressFormatPreference::Business; |
| 405 | const auto format = AddressFormatRepository::formatForAddress(address: *this, formatPref); |
| 406 | return AddressFormatter::format(address: *this, name: realName, organization: orgaName, format, style); |
| 407 | } |
| 408 | |
| 409 | QString Address::formattedPostalAddress() const |
| 410 | { |
| 411 | return formatted(style: AddressFormatStyle::Postal); |
| 412 | } |
| 413 | |
| 414 | QUrl Address::geoUri() const |
| 415 | { |
| 416 | QUrl url; |
| 417 | url.setScheme(QStringLiteral("geo" )); |
| 418 | |
| 419 | if (geo().isValid()) { |
| 420 | url.setPath(path: QString::number(geo().latitude()) + QLatin1Char(',') + QString::number(geo().longitude())); |
| 421 | return url; |
| 422 | } |
| 423 | |
| 424 | if (!isEmpty()) { |
| 425 | url.setPath(QStringLiteral("0,0" )); |
| 426 | QUrlQuery query; |
| 427 | query.addQueryItem(QStringLiteral("q" ), value: formatted(style: KContacts::AddressFormatStyle::GeoUriQuery)); |
| 428 | url.setQuery(query); |
| 429 | return url; |
| 430 | } |
| 431 | |
| 432 | return {}; |
| 433 | } |
| 434 | |
| 435 | // clang-format off |
| 436 | QDataStream &KContacts::operator<<(QDataStream &s, const Address &addr) |
| 437 | { |
| 438 | return s << addr.d->mId << (uint)addr.d->mType << addr.d->mPostOfficeBox |
| 439 | << addr.d->mExtended << addr.d->mStreet << addr.d->mLocality |
| 440 | << addr.d->mRegion << addr.d->mPostalCode << addr.d->mCountry |
| 441 | << addr.d->mLabel << addr.d->mEmpty << addr.d->mGeo; |
| 442 | } |
| 443 | |
| 444 | QDataStream &KContacts::operator>>(QDataStream &s, Address &addr) |
| 445 | { |
| 446 | uint type; |
| 447 | s >> addr.d->mId >> type >> addr.d->mPostOfficeBox >> addr.d->mExtended |
| 448 | >> addr.d->mStreet >> addr.d->mLocality >> addr.d->mRegion |
| 449 | >> addr.d->mPostalCode >> addr.d->mCountry >> addr.d->mLabel |
| 450 | >> addr.d->mEmpty >> addr.d->mGeo; |
| 451 | |
| 452 | addr.d->mType = Address::Type(type); |
| 453 | |
| 454 | return s; |
| 455 | } |
| 456 | // clang-format on |
| 457 | |
| 458 | #include "moc_address.cpp" |
| 459 | |