File indexing completed on 2025-01-05 04:49:19
0001 /* 0002 SPDX-FileCopyrightText: 2016-2024 Laurent Montel <montel@kde.org> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "gmximportexportplugininterface.h" 0008 #include "kaddressbook_importexport_gmx_debug.h" 0009 #include <KActionCollection> 0010 #include <KJobWidgets> 0011 #include <KLocalizedString> 0012 #include <KMessageBox> 0013 #include <PimCommon/RenameFileDialog> 0014 #include <QAction> 0015 #include <QFileDialog> 0016 #include <QPointer> 0017 #include <QTemporaryFile> 0018 #include <QTextStream> 0019 #include <QUrl> 0020 #include <importexportengine.h> 0021 #include <kio/filecopyjob.h> 0022 0023 #include <KAddressBookImportExport/ContactSelectionDialog> 0024 0025 #define GMX_FILESELECTION_STRING i18n("GMX address book file (*.gmxa)") 0026 const int typeHome = 0; 0027 const int typeWork = 1; 0028 const int typeOther = 2; 0029 0030 GMXImportExportPluginInterface::GMXImportExportPluginInterface(QObject *parent) 0031 : KAddressBookImportExport::PluginInterface(parent) 0032 { 0033 } 0034 0035 GMXImportExportPluginInterface::~GMXImportExportPluginInterface() = default; 0036 0037 void GMXImportExportPluginInterface::createAction(KActionCollection *ac) 0038 { 0039 QAction *action = ac->addAction(QStringLiteral("file_import_gmx")); 0040 action->setText(i18n("Import GMX file...")); 0041 action->setWhatsThis(i18n("Import contacts from a GMX address book file.")); 0042 setImportActions(QList<QAction *>() << action); 0043 connect(action, &QAction::triggered, this, &GMXImportExportPluginInterface::slotImportGmx); 0044 0045 action = ac->addAction(QStringLiteral("file_export_gmx")); 0046 action->setText(i18n("Export GMX file...")); 0047 action->setWhatsThis(i18n("Export contacts to a GMX address book file.")); 0048 setExportActions(QList<QAction *>() << action); 0049 connect(action, &QAction::triggered, this, &GMXImportExportPluginInterface::slotExportGmx); 0050 } 0051 0052 void GMXImportExportPluginInterface::exec() 0053 { 0054 switch (mImportExportAction) { 0055 case Import: 0056 importGMX(); 0057 break; 0058 case Export: 0059 exportGMX(); 0060 break; 0061 } 0062 } 0063 0064 void GMXImportExportPluginInterface::slotImportGmx() 0065 { 0066 mImportExportAction = Import; 0067 Q_EMIT emitPluginActivated(this); 0068 } 0069 0070 void GMXImportExportPluginInterface::slotExportGmx() 0071 { 0072 mImportExportAction = Export; 0073 Q_EMIT emitPluginActivated(this); 0074 } 0075 0076 void GMXImportExportPluginInterface::exportGMX() 0077 { 0078 QPointer<KAddressBookImportExport::ContactSelectionDialog> dlg = 0079 new KAddressBookImportExport::ContactSelectionDialog(itemSelectionModel(), false, parentWidget()); 0080 dlg->setMessageText(i18n("Which contact do you want to export?")); 0081 dlg->setDefaultAddressBook(defaultCollection()); 0082 if (!dlg->exec()) { 0083 delete dlg; 0084 return; 0085 } 0086 const KContacts::AddresseeList contacts = dlg->selectedContacts().addressList(); 0087 delete dlg; 0088 0089 if (contacts.isEmpty()) { 0090 KMessageBox::error(nullptr, i18n("You have not selected any contacts to export.")); 0091 return; 0092 } 0093 0094 KAddressBookImportExport::ContactList contactLists; 0095 contactLists.setAddressList(contacts); 0096 QFileDialog::Options options = QFileDialog::DontConfirmOverwrite; 0097 QUrl url = QFileDialog::getSaveFileUrl(parentWidget(), 0098 QString(), 0099 QUrl::fromLocalFile(QDir::homePath() + QStringLiteral("/addressbook.gmx")), 0100 GMX_FILESELECTION_STRING, 0101 nullptr, 0102 options); 0103 if (url.isEmpty()) { 0104 return; 0105 } 0106 0107 if (QFileInfo::exists(url.isLocalFile() ? url.toLocalFile() : url.path())) { 0108 if (url.isLocalFile() && QFileInfo::exists(url.toLocalFile())) { 0109 auto dialog = new PimCommon::RenameFileDialog(url, false, parentWidget()); 0110 const auto result = static_cast<PimCommon::RenameFileDialog::RenameFileDialogResult>(dialog->exec()); 0111 if (result == PimCommon::RenameFileDialog::RENAMEFILE_RENAME) { 0112 url = dialog->newName(); 0113 } else if (result == PimCommon::RenameFileDialog::RENAMEFILE_IGNORE) { 0114 delete dialog; 0115 return; 0116 } 0117 delete dialog; 0118 } 0119 } 0120 0121 if (!url.isLocalFile()) { 0122 QTemporaryFile tmpFile; 0123 if (!tmpFile.open()) { 0124 const QString txt = i18n("<qt>Unable to open file <b>%1</b></qt>", url.url()); 0125 KMessageBox::error(parentWidget(), txt); 0126 return; 0127 } 0128 0129 doExport(&tmpFile, contactLists.addressList()); 0130 tmpFile.flush(); 0131 auto job = KIO::file_copy(QUrl::fromLocalFile(tmpFile.fileName()), url, -1, KIO::Overwrite); 0132 KJobWidgets::setWindow(job, parentWidget()); 0133 job->exec(); 0134 } else { 0135 QString fileName = url.toLocalFile(); 0136 QFile file(fileName); 0137 0138 if (!file.open(QIODevice::WriteOnly)) { 0139 const QString txt = i18n("<qt>Unable to open file <b>%1</b>.</qt>", fileName); 0140 KMessageBox::error(parentWidget(), txt); 0141 return; 0142 } 0143 0144 doExport(&file, contactLists.addressList()); 0145 file.close(); 0146 } 0147 } 0148 0149 static bool checkDateTime(const QString &dateStr, QDateTime &dt) 0150 { 0151 if (dateStr.isEmpty()) { 0152 return false; 0153 } 0154 0155 dt = QDateTime::fromString(dateStr, Qt::ISODate); 0156 if (dt.isValid() && dt.date().year() > 1901) { 0157 return true; 0158 } 0159 dt.setDate(QDate()); 0160 0161 return false; 0162 } 0163 0164 static const QString dateString(const QDateTime &dt) 0165 { 0166 if (!dt.isValid()) { 0167 return QStringLiteral("1000-01-01 00:00:00"); 0168 } 0169 QString d(dt.toString(Qt::ISODate)); 0170 d[10] = QLatin1Char(' '); // remove the "T" in the middle of the string 0171 return d; 0172 } 0173 0174 static const QStringList assignedCategoriesSorted(const KContacts::AddresseeList &list) 0175 { 0176 // Walk through the addressees and return a unique list of up to 31 0177 // categories, alphabetically sorted 0178 QStringList categoryList; 0179 const KContacts::Addressee *addressee = nullptr; 0180 for (KContacts::AddresseeList::ConstIterator addresseeIt = list.begin(); addresseeIt != list.end() && categoryList.count() < 32; ++addresseeIt) { 0181 addressee = &(*addresseeIt); 0182 if (addressee->isEmpty()) { 0183 continue; 0184 } 0185 const QStringList categories = addressee->categories(); 0186 for (int i = 0; i < categories.count() && categoryList.count() < 32; ++i) { 0187 if (!categoryList.contains(categories[i])) { 0188 categoryList.append(categories[i]); 0189 } 0190 } 0191 } 0192 categoryList.sort(); 0193 return categoryList; 0194 } 0195 0196 void GMXImportExportPluginInterface::doExport(QFile *fp, const KContacts::AddresseeList &list) const 0197 { 0198 if (!fp || list.isEmpty()) { 0199 return; 0200 } 0201 0202 QTextStream t(fp); 0203 t.setEncoding(QStringConverter::Latin1); 0204 0205 using AddresseeMap = QMap<int, const KContacts::Addressee *>; 0206 AddresseeMap addresseeMap; 0207 const KContacts::Addressee *addressee = nullptr; 0208 0209 t << "AB_ADDRESSES:\n"; 0210 t << "Address_id,Nickname,Firstname,Lastname,Title,Birthday,Comments," 0211 "Change_date,Status,Address_link_id,Categories\n"; 0212 0213 QStringList categoryMap; 0214 categoryMap.append(assignedCategoriesSorted(list)); 0215 0216 int addresseeId = 0; 0217 const QChar DELIM(QLatin1Char('#')); 0218 for (KContacts::AddresseeList::ConstIterator it = list.begin(); it != list.end(); ++it) { 0219 addressee = &(*it); 0220 if (addressee->isEmpty()) { 0221 continue; 0222 } 0223 addresseeMap[++addresseeId] = addressee; 0224 0225 // Assign categories as bitfield 0226 const QStringList categories = addressee->categories(); 0227 long int category = 0; 0228 if (!categories.isEmpty()) { 0229 const int categoriesCount(categories.count()); 0230 for (int i = 0; i < categoriesCount; ++i) { 0231 const QString cat = categories[i]; 0232 if (categoryMap.contains(cat)) { 0233 category |= 1 << categoryMap.indexOf(cat, 0); 0234 } 0235 } 0236 } 0237 0238 // GMX sorts by nickname by default - don't leave empty 0239 QString nickName = addressee->nickName(); 0240 if (nickName.isEmpty()) { 0241 nickName = addressee->formattedName(); 0242 } 0243 0244 t << addresseeId << DELIM // Address_id 0245 << nickName << DELIM // Nickname 0246 << addressee->givenName() << DELIM // Firstname 0247 << addressee->familyName() << DELIM // Lastname 0248 << addressee->prefix() << DELIM // Title - Note: ->title() 0249 // refers to the professional title 0250 << dateString(addressee->birthday()) << DELIM // Birthday 0251 << addressee->note() /*.replace('\n',"\r\n")*/ << DELIM // Comments 0252 << dateString(addressee->revision()) << DELIM // Change_date 0253 << "1" << DELIM // Status 0254 << DELIM // Address_link_id 0255 << category << QLatin1Char('\n'); // Categories 0256 } 0257 0258 t << "####\n"; 0259 t << "AB_ADDRESS_RECORDS:\n"; 0260 t << "Address_id,Record_id,Street,Country,Zipcode,City,Phone,Fax,Mobile," 0261 "Mobile_type,Email,Homepage,Position,Comments,Record_type_id,Record_type," 0262 "Company,Department,Change_date,Preferred,Status\n"; 0263 0264 addresseeId = 1; 0265 while ((addressee = addresseeMap[addresseeId]) != nullptr) { 0266 const KContacts::PhoneNumber::List cellPhones = addressee->phoneNumbers(KContacts::PhoneNumber::Cell); 0267 0268 const QStringList emails = addressee->emails(); 0269 0270 for (int recId = 0; recId < 3; ++recId) { 0271 KContacts::Address address; 0272 KContacts::PhoneNumber phone; 0273 KContacts::PhoneNumber fax; 0274 KContacts::PhoneNumber cell; 0275 0276 // address preference flag: 0277 // & 1: preferred email address 0278 // & 4: preferred cell phone 0279 int prefFlag = 0; 0280 0281 switch (recId) { 0282 // Assign address, phone and cellphone, fax if applicable 0283 case typeHome: 0284 address = addressee->address(KContacts::Address::Home); 0285 phone = addressee->phoneNumber(KContacts::PhoneNumber::Home); 0286 if (!cellPhones.isEmpty()) { 0287 cell = cellPhones.at(0); 0288 if (!cell.isEmpty()) { 0289 prefFlag |= 4; 0290 } 0291 } 0292 break; 0293 case typeWork: 0294 address = addressee->address(KContacts::Address::Work); 0295 phone = addressee->phoneNumber(KContacts::PhoneNumber::Work); 0296 if (cellPhones.count() >= 2) { 0297 cell = cellPhones.at(1); 0298 } 0299 fax = addressee->phoneNumber(KContacts::PhoneNumber::Fax); 0300 break; 0301 case typeOther: 0302 default: 0303 if (addressee->addresses(KContacts::Address::Home).count() > 1) { 0304 address = addressee->addresses(KContacts::Address::Home).at(1); 0305 } 0306 if ((address.isEmpty()) && (addressee->addresses(KContacts::Address::Work).count() > 1)) { 0307 address = addressee->addresses(KContacts::Address::Work).at(1); 0308 } 0309 if (address.isEmpty()) { 0310 address = addressee->address(KContacts::Address::Dom); 0311 } 0312 if (address.isEmpty()) { 0313 address = addressee->address(KContacts::Address::Intl); 0314 } 0315 if (address.isEmpty()) { 0316 address = addressee->address(KContacts::Address::Postal); 0317 } 0318 if (address.isEmpty()) { 0319 address = addressee->address(KContacts::Address::Parcel); 0320 } 0321 0322 if (addressee->phoneNumbers(KContacts::PhoneNumber::Home).count() > 1) { 0323 phone = addressee->phoneNumbers(KContacts::PhoneNumber::Home).at(1); 0324 } 0325 if ((phone.isEmpty()) && (addressee->phoneNumbers(KContacts::PhoneNumber::Work).count() > 1)) { 0326 phone = addressee->phoneNumbers(KContacts::PhoneNumber::Work).at(1); 0327 } 0328 if (phone.isEmpty()) { 0329 phone = addressee->phoneNumber(KContacts::PhoneNumber::Voice); 0330 } 0331 if (phone.isEmpty()) { 0332 phone = addressee->phoneNumber(KContacts::PhoneNumber::Msg); 0333 } 0334 if (phone.isEmpty()) { 0335 phone = addressee->phoneNumber(KContacts::PhoneNumber::Isdn); 0336 } 0337 if (phone.isEmpty()) { 0338 phone = addressee->phoneNumber(KContacts::PhoneNumber::Car); 0339 } 0340 if (phone.isEmpty()) { 0341 phone = addressee->phoneNumber(KContacts::PhoneNumber::Pager); 0342 } 0343 0344 switch (cellPhones.count()) { 0345 case 0: 0346 break; 0347 case 1: 0348 case 2: 0349 if (!address.isEmpty()) { 0350 cell = cellPhones.at(0); 0351 } 0352 break; 0353 default: 0354 cell = cellPhones.at(2); 0355 break; 0356 } 0357 break; 0358 } 0359 0360 QString email; 0361 if (emails.count() > recId) { 0362 email = emails[recId]; 0363 if (email == addressee->preferredEmail()) { 0364 prefFlag |= 1; 0365 } 0366 } 0367 0368 if (!address.isEmpty() || !phone.isEmpty() || !cell.isEmpty() || !email.isEmpty()) { 0369 t << addresseeId << DELIM // Address_id 0370 << recId << DELIM // Record_id 0371 << address.street() << DELIM // Street 0372 << address.country() << DELIM // Country 0373 << address.postalCode() << DELIM // Zipcode 0374 << address.locality() << DELIM // City 0375 << phone.number() << DELIM // Phone 0376 << fax.number() << DELIM // Fax 0377 << cell.number() << DELIM // Mobile 0378 0379 << ((recId == typeWork) ? 0 : 1) << DELIM // Mobile_type 0380 0381 << email << DELIM // Email 0382 0383 << ((recId == typeWork) ? addressee->url().url().url() : QString()) << DELIM // Homepage 0384 0385 << ((recId == typeWork) ? addressee->role() : QString()) << DELIM // Position 0386 0387 << ((recId == typeHome) ? addressee->custom(QStringLiteral("KADDRESSBOOK"), QStringLiteral("X-SpousesName")) : QString()) << DELIM // Comments 0388 0389 << recId << DELIM // Record_type_id (0,1,2) 0390 0391 << DELIM // Record_type 0392 0393 << ((recId == typeWork) ? addressee->organization() : QString()) << DELIM // Company 0394 0395 << ((recId == typeWork) ? addressee->custom(QStringLiteral("KADDRESSBOOK"), QStringLiteral("X-Department")) : QString()) 0396 << DELIM // Department 0397 0398 << dateString(addressee->revision()) << DELIM // Change_date 0399 0400 << prefFlag << DELIM // Preferred: 0401 // ( & 1: preferred email, 0402 // & 4: preferred cell phone ) 0403 << 1 << QLatin1Char('\n'); // Status (should always be "1") 0404 } 0405 } 0406 0407 ++addresseeId; 0408 } 0409 0410 t << "####" << QLatin1Char('\n'); 0411 t << "AB_CATEGORIES:" << QLatin1Char('\n'); 0412 t << "Category_id,Name,Icon_id" << QLatin1Char('\n'); 0413 0414 // Write Category List (beware: Category_ID 0 is reserved for none 0415 // Interestingly: The index here is an int sequence and does not 0416 // correspond to the bit reference used above. 0417 const int categoryCount(categoryMap.size()); 0418 for (int i = 0; i < categoryCount; ++i) { 0419 t << (i + 1) << DELIM << categoryMap.at(i) << DELIM << 0 << QLatin1Char('\n'); 0420 } 0421 t << "####" << QLatin1Char('\n'); 0422 } 0423 0424 void GMXImportExportPluginInterface::importGMX() 0425 { 0426 const QString fileName = QFileDialog::getOpenFileName(parentWidget(), QString(), QDir::homePath(), GMX_FILESELECTION_STRING); 0427 0428 if (fileName.isEmpty()) { 0429 return; 0430 } 0431 0432 QFile file(fileName); 0433 if (!file.open(QIODevice::ReadOnly)) { 0434 const QString msg = i18n("<qt>Unable to open <b>%1</b> for reading.</qt>", fileName); 0435 KMessageBox::error(parentWidget(), msg); 0436 return; 0437 } 0438 0439 QDateTime dt; 0440 QTextStream gmxStream(&file); 0441 gmxStream.setEncoding(QStringConverter::Latin1); 0442 QString line; 0443 QString line2; 0444 line = gmxStream.readLine(); 0445 line2 = gmxStream.readLine(); 0446 if (!line.startsWith(QLatin1StringView("AB_ADDRESSES:")) || !line2.startsWith(QLatin1StringView("Address_id"))) { 0447 KMessageBox::error(parentWidget(), i18n("%1 is not a GMX address book file.", fileName)); 0448 return; 0449 } 0450 0451 QStringList itemList; 0452 QMap<QString, QString> categoriesOfAddressee; 0453 using AddresseeMap = QMap<QString, KContacts::Addressee *>; 0454 AddresseeMap addresseeMap; 0455 0456 // "Address_id,Nickname,Firstname,Lastname,Title,Birthday,Comments, 0457 // Change_date,Status,Address_link_id,Categories" 0458 line = gmxStream.readLine(); 0459 while ((line != QLatin1StringView("####")) && !gmxStream.atEnd()) { 0460 // an addressee entry may spread over several lines in the file 0461 while (true) { 0462 itemList = line.split(QLatin1Char('#'), Qt::KeepEmptyParts); 0463 if (itemList.count() >= 11) { 0464 break; 0465 } 0466 line.append(QLatin1Char('\n')); 0467 line.append(gmxStream.readLine()); 0468 } 0469 0470 // populate the addressee 0471 auto addressee = new KContacts::Addressee; 0472 addressee->setNickName(itemList.at(1)); 0473 addressee->setGivenName(itemList.at(2)); 0474 addressee->setFamilyName(itemList.at(3)); 0475 addressee->setFormattedName(itemList.at(3) + QStringLiteral(", ") + itemList.at(2)); 0476 addressee->setPrefix(itemList.at(4)); 0477 if (checkDateTime(itemList.at(5), dt)) { 0478 addressee->setBirthday(dt); 0479 } 0480 addressee->setNote(itemList.at(6)); 0481 if (checkDateTime(itemList.at(7), dt)) { 0482 addressee->setRevision(dt); 0483 } 0484 // addressee->setStatus( itemList[8] ); Status 0485 // addressee->xxx( itemList[9] ); Address_link_id 0486 categoriesOfAddressee[itemList[0]] = itemList[10]; 0487 addresseeMap[itemList[0]] = addressee; 0488 0489 line = gmxStream.readLine(); 0490 } 0491 0492 // now read the address records 0493 line = gmxStream.readLine(); 0494 if (!line.startsWith(QLatin1StringView("AB_ADDRESS_RECORDS:"))) { 0495 qCWarning(KADDRESSBOOK_IMPORTEXPORT_GMX_LOG) << "Could not find address records!"; 0496 return; 0497 } 0498 // Address_id,Record_id,Street,Country,Zipcode,City,Phone,Fax,Mobile, 0499 // Mobile_type,Email,Homepage,Position,Comments,Record_type_id,Record_type, 0500 // Company,Department,Change_date,Preferred,Status 0501 line = gmxStream.readLine(); 0502 line = gmxStream.readLine(); 0503 0504 while (!line.startsWith(QLatin1StringView("####")) && !gmxStream.atEnd()) { 0505 // an address entry may spread over several lines in the file 0506 while (true) { 0507 itemList = line.split(QLatin1Char('#'), Qt::KeepEmptyParts); 0508 if (itemList.count() >= 21) { 0509 break; 0510 } 0511 line.append(QLatin1Char('\n')); 0512 line.append(gmxStream.readLine()); 0513 } 0514 0515 KContacts::Addressee *addressee = addresseeMap[itemList[0]]; 0516 if (addressee) { 0517 // itemList[1] = Record_id (numbered item, ignore here) 0518 int recordTypeId = itemList[14].toInt(); 0519 KContacts::Address::Type addressType; 0520 KContacts::PhoneNumber::Type phoneType; 0521 switch (recordTypeId) { 0522 case typeHome: 0523 addressType = KContacts::Address::Home; 0524 phoneType = KContacts::PhoneNumber::Home; 0525 break; 0526 case typeWork: 0527 addressType = KContacts::Address::Work; 0528 phoneType = KContacts::PhoneNumber::Work; 0529 break; 0530 case typeOther: 0531 default: 0532 addressType = KContacts::Address::Intl; 0533 phoneType = KContacts::PhoneNumber::Voice; 0534 break; 0535 } 0536 KContacts::Address address = addressee->address(addressType); 0537 address.setStreet(itemList[2]); 0538 address.setCountry(itemList[3]); 0539 address.setPostalCode(itemList[4]); 0540 address.setLocality(itemList[5]); 0541 if (!itemList[6].isEmpty()) { 0542 addressee->insertPhoneNumber(KContacts::PhoneNumber(itemList[6], phoneType)); 0543 } 0544 if (!itemList[7].isEmpty()) { 0545 addressee->insertPhoneNumber(KContacts::PhoneNumber(itemList[7], KContacts::PhoneNumber::Fax)); 0546 } 0547 KContacts::PhoneNumber::Type cellType = KContacts::PhoneNumber::Cell; 0548 // itemList[9]=Mobile_type // always 0 or -1(default phone). 0549 // if ( itemList[19].toInt() & 4 ) cellType |= KContacts::PhoneNumber::Pref; 0550 // don't do the above to avoid duplicate mobile numbers 0551 if (!itemList[8].isEmpty()) { 0552 addressee->insertPhoneNumber(KContacts::PhoneNumber(itemList[8], cellType)); 0553 } 0554 KContacts::Email email(itemList[10]); 0555 if (itemList[19].toInt() & 1) { 0556 email.setPreferred(true); 0557 } 0558 addressee->addEmail(email); 0559 if (!itemList[11].isEmpty()) { 0560 KContacts::ResourceLocatorUrl url; 0561 url.setUrl(QUrl(itemList[11])); 0562 addressee->setUrl(url); 0563 } 0564 if (!itemList[12].isEmpty()) { 0565 addressee->setRole(itemList[12]); 0566 } 0567 // itemList[13]=Comments 0568 // itemList[14]=Record_type_id (0,1,2) - see above 0569 // itemList[15]=Record_type (name of this additional record entry) 0570 if (!itemList[16].isEmpty()) { 0571 addressee->setOrganization(itemList[16]); // Company 0572 } 0573 if (!itemList[17].isEmpty()) { 0574 addressee->insertCustom(QStringLiteral("KADDRESSBOOK"), QStringLiteral("X-Department"), itemList[17]); // Department 0575 } 0576 if (checkDateTime(itemList[18], dt)) { 0577 addressee->setRevision(dt); // Change_date 0578 } 0579 // itemList[19]=Preferred (see above) 0580 // itemList[20]=Status (should always be "1") 0581 addressee->insertAddress(address); 0582 } else { 0583 qCWarning(KADDRESSBOOK_IMPORTEXPORT_GMX_LOG) << "unresolved line:" << line; 0584 } 0585 line = gmxStream.readLine(); 0586 } 0587 0588 // extract the categories from the list of addressees of the file to import 0589 QStringList usedCategoryList; 0590 line = gmxStream.readLine(); 0591 line2 = gmxStream.readLine(); 0592 if (!line.startsWith(QLatin1StringView("AB_CATEGORIES:")) || !line2.startsWith(QLatin1StringView("Category_id"))) { 0593 qCWarning(KADDRESSBOOK_IMPORTEXPORT_GMX_LOG) << "Could not find category records!"; 0594 } else { 0595 while (!line.startsWith(QLatin1StringView("####")) && !gmxStream.atEnd()) { 0596 // a category should not spread over multiple lines, but just in case 0597 while (true) { 0598 itemList = line.split(QLatin1Char('#'), Qt::KeepEmptyParts); 0599 if (itemList.count() >= 3) { 0600 break; 0601 } 0602 line.append(QLatin1Char('\n')); 0603 line.append(gmxStream.readLine()); 0604 } 0605 usedCategoryList.append(itemList[1]); 0606 line = gmxStream.readLine(); 0607 } 0608 } 0609 KContacts::Addressee::List addresseeList; 0610 0611 // now add the addresses to addresseeList 0612 for (AddresseeMap::Iterator addresseeIt = addresseeMap.begin(); addresseeIt != addresseeMap.end(); ++addresseeIt) { 0613 KContacts::Addressee *addressee = addresseeIt.value(); 0614 // Add categories 0615 // categories is a bitfield with max 31 defined categories 0616 int categories = categoriesOfAddressee[addresseeIt.key()].toInt(); 0617 for (int i = 32; i >= 0; --i) { 0618 // convert category index to bitfield value for comparison 0619 int catBit = 1 << i; 0620 if (catBit > categories) { 0621 continue; // current index unassigned 0622 } 0623 if (catBit & categories && usedCategoryList.count() > i) { 0624 addressee->insertCategory(usedCategoryList[i]); 0625 } 0626 } 0627 addresseeList.append(*addressee); 0628 delete addressee; 0629 } 0630 0631 file.close(); 0632 KAddressBookImportExport::ContactList contactList; 0633 contactList.setAddressList(addresseeList); 0634 0635 auto engine = new KAddressBookImportExport::ImportExportEngine(this); 0636 engine->setContactList(contactList); 0637 engine->setDefaultAddressBook(defaultCollection()); 0638 engine->importContacts(); 0639 } 0640 0641 bool GMXImportExportPluginInterface::canImportFileType(const QUrl &url) 0642 { 0643 return url.path().endsWith(QLatin1StringView(".gmx")); 0644 } 0645 0646 #include "moc_gmximportexportplugininterface.cpp"