diff --git a/CMakeLists.txt b/CMakeLists.txt index 042e400..bd43d0f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,87 +1,88 @@ cmake_minimum_required(VERSION 3.5) set(KF5_VERSION "5.57.0") # handled by release scripts set(KF5_DEP_VERSION "5.56.0") # handled by release scripts project (KPeople VERSION ${KF5_VERSION}) include(FeatureSummary) find_package(ECM 5.56.0 NO_MODULE) set_package_properties(ECM PROPERTIES TYPE REQUIRED DESCRIPTION "Extra CMake Modules." URL "https://projects.kde.org/projects/kdesupport/extra-cmake-modules") feature_summary(WHAT REQUIRED_PACKAGES_NOT_FOUND FATAL_ON_MISSING_REQUIRED_PACKAGES) set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR}) set(REQUIRED_QT_VERSION 5.10.0) find_package(Qt5 ${REQUIRED_QT_VERSION} CONFIG REQUIRED Gui Sql DBus Widgets Qml) find_package(KF5CoreAddons ${KF5_DEP_VERSION} CONFIG REQUIRED) find_package(KF5WidgetsAddons ${KF5_DEP_VERSION} CONFIG REQUIRED) find_package(KF5I18n ${KF5_DEP_VERSION} CONFIG REQUIRED) find_package(KF5ItemViews ${KF5_DEP_VERSION} CONFIG REQUIRED) find_package(KF5Service ${KF5_DEP_VERSION} CONFIG REQUIRED) include(ECMSetupVersion) include(ECMGenerateHeaders) include(ECMSetupVersion) include(KDEFrameworkCompilerSettings NO_POLICY_SCOPE) include(ECMAddTests) include(ECMAddQch) include(CMakePackageConfigHelpers) include(ECMGeneratePriFile) include(GenerateExportHeader) include(ECMQtDeclareLoggingCategory) include(KDEInstallDirs) include(KDECMakeSettings) option(BUILD_QCH "Build API documentation in QCH format (for e.g. Qt Assistant, Qt Creator & KDevelop)" OFF) add_feature_info(QCH ${BUILD_QCH} "API documentation in QCH format (for e.g. Qt Assistant, Qt Creator & KDevelop)") add_definitions(-DTRANSLATION_DOMAIN=\"kpeople5\") set(CMAKECONFIG_INSTALL_DIR "${CMAKECONFIG_INSTALL_PREFIX}/KF5People") ecm_setup_version(PROJECT VARIABLE_PREFIX KPEOPLE VERSION_HEADER "${CMAKE_CURRENT_BINARY_DIR}/kpeople_version.h" PACKAGE_VERSION_FILE "${CMAKE_CURRENT_BINARY_DIR}/KF5PeopleConfigVersion.cmake" SOVERSION 5) option(ENABLE_EXAMPLES "Build the examples" "ON") if (IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/po") ki18n_install(po) endif () +add_definitions(-DQT_NO_FOREACH) add_subdirectory (src) add_subdirectory (pixmaps) if (ENABLE_EXAMPLES) add_subdirectory(examples) endif() if (BUILD_TESTING) add_subdirectory(autotests) endif() if (BUILD_QCH) ecm_install_qch_export( TARGETS KF5People_QCH FILE KF5PeopleQchTargets.cmake DESTINATION "${CMAKECONFIG_INSTALL_DIR}" COMPONENT Devel ) set(PACKAGE_INCLUDE_QCHTARGETS "include(\"\${CMAKE_CURRENT_LIST_DIR}/KF5PeopleQchTargets.cmake\")") endif() configure_package_config_file( "${CMAKE_CURRENT_SOURCE_DIR}/KF5PeopleConfig.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/KF5PeopleConfig.cmake" INSTALL_DESTINATION ${CMAKECONFIG_INSTALL_DIR} ) install(EXPORT KPeopleTargets DESTINATION "${CMAKECONFIG_INSTALL_DIR}" FILE KPeopleTargets.cmake NAMESPACE KF5::) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/KF5PeopleConfig.cmake" "${CMAKE_CURRENT_BINARY_DIR}/KF5PeopleConfigVersion.cmake" DESTINATION "${CMAKECONFIG_INSTALL_DIR}" COMPONENT Devel ) install( FILES kpeople.categories DESTINATION ${KDE_INSTALL_CONFDIR} ) feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/examples/contactlistwidgets.cpp b/examples/contactlistwidgets.cpp index bddbb3a..7489036 100644 --- a/examples/contactlistwidgets.cpp +++ b/examples/contactlistwidgets.cpp @@ -1,167 +1,167 @@ /* Persons Model Example Copyright (C) 2012 Aleix Pol Gonzalez This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include using namespace KPeople; const int SPACING = 8; const int PHOTO_SIZE = 32; class PersonsDelegate : public QStyledItemDelegate { public: PersonsDelegate(QObject *parent = nullptr); ~PersonsDelegate(); void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override; QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override; }; PersonsDelegate::PersonsDelegate(QObject *parent) : QStyledItemDelegate(parent) { } PersonsDelegate::~PersonsDelegate() { } void PersonsDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { QStyleOptionViewItemV4 optV4 = option; initStyleOption(&optV4, index); painter->save(); painter->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform | QPainter::HighQualityAntialiasing); painter->setClipRect(optV4.rect); QStyle *style = QApplication::style(); style->drawPrimitive(QStyle::PE_PanelItemViewItem, &option, painter); QRect contactPhotoRect = optV4.rect; contactPhotoRect.adjust(SPACING, SPACING, SPACING, SPACING); contactPhotoRect.setWidth(PHOTO_SIZE); contactPhotoRect.setHeight(PHOTO_SIZE); QImage avatar = index.data(Qt::DecorationRole).value(); painter->drawImage(contactPhotoRect, avatar); painter->drawRect(contactPhotoRect); QRect nameRect = optV4.rect; nameRect.adjust(SPACING + PHOTO_SIZE + SPACING, SPACING, 0, 0); painter->drawText(nameRect, index.data(Qt::DisplayRole).toString()); QRect idRect = optV4.rect; idRect.adjust(SPACING + PHOTO_SIZE + SPACING, SPACING + 15, 0, 0); painter->drawText(idRect, index.data(PersonsModel::PersonUriRole).toString()); painter->restore(); } QSize PersonsDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const { Q_UNUSED(option) Q_UNUSED(index) return QSize(128, 48); } class ContactListApp : public QWidget { Q_OBJECT public: ContactListApp(); private Q_SLOTS: void onMergeClicked(); void onUnmergeClicked(); private: PersonsModel *m_model; QTreeView *m_view; }; ContactListApp::ContactListApp() { m_view = new QTreeView(this); m_model = new PersonsModel(this); QVBoxLayout *layout = new QVBoxLayout(this); QSortFilterProxyModel *sortFilter = new QSortFilterProxyModel(m_view); sortFilter->setDynamicSortFilter(true); sortFilter->setSourceModel(m_model); sortFilter->sort(0); m_view->setRootIsDecorated(false); m_view->setModel(sortFilter); m_view->setItemDelegate(new PersonsDelegate(this)); m_view->setSelectionMode(QAbstractItemView::ExtendedSelection); layout->addWidget(m_view); QPushButton *mergeButton = new QPushButton(QStringLiteral("Merge"), this); connect(mergeButton, SIGNAL(released()), SLOT(onMergeClicked())); layout->addWidget(mergeButton); QPushButton *unmergeButton = new QPushButton(QStringLiteral("Unmerge"), this); connect(unmergeButton, SIGNAL(released()), SLOT(onUnmergeClicked())); layout->addWidget(unmergeButton); } void ContactListApp::onMergeClicked() { - QModelIndexList indexes = m_view->selectionModel()->selectedIndexes(); + const QModelIndexList indexes = m_view->selectionModel()->selectedIndexes(); QStringList ids; - Q_FOREACH (const QModelIndex &index, indexes) { + for (const QModelIndex &index : indexes) { ids << index.data(PersonsModel::PersonUriRole).toString(); } if (!ids.isEmpty()) { KPeople::mergeContacts(ids); } } void ContactListApp::onUnmergeClicked() { QModelIndexList indexes = m_view->selectionModel()->selectedIndexes(); if (!indexes.isEmpty()) { QString id = indexes.first().data(PersonsModel::PersonUriRole).toString(); KPeople::unmergeContact(id); } } int main(int argc, char **argv) { QApplication app(argc, argv); ContactListApp widget; widget.show(); app.exec(); } #include "contactlistwidgets.moc" diff --git a/examples/duplicates.cpp b/examples/duplicates.cpp index 36a60b1..2a2883c 100644 --- a/examples/duplicates.cpp +++ b/examples/duplicates.cpp @@ -1,127 +1,127 @@ /* Duplicates Example Copyright (C) 2012 Aleix Pol Gonzalez This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #include using namespace KPeople; class ResultPrinter : public QObject { Q_OBJECT public Q_SLOTS: void print(KJob *j) { QList res = ((DuplicatesFinder *) j)->results(); std::cout << "Results: " << res.count() << std::endl; for (QList::iterator it = res.begin(); it != res.end();) { QStringList roles = it->matchReasons(); QStringList rA, rB; AbstractContact::Ptr aA = it->indexA.data(PersonsModel::PersonVCardRole).value(); AbstractContact::Ptr aB = it->indexB.data(PersonsModel::PersonVCardRole).value(); Q_ASSERT(!it->reasons.isEmpty()); - Q_FOREACH (Match::MatchReason i, it->reasons) { + for (Match::MatchReason i : qAsConst(it->reasons)) { rA += it->matchValue(i, aA); rB += it->matchValue(i, aB); } std::cout << "\t- " << qPrintable(roles.join(QStringLiteral(", "))) << ": " << it->indexA.row() << " " << it->indexB.row() << " because: " << qPrintable(rA.join(QStringLiteral(", "))) << " // " << qPrintable(rB.join(QStringLiteral(", "))) << '.' << std::endl; bool remove = false; if (m_action == Ask) { for (char ans = ' '; ans != 'y' && ans != 'n';) { std::cout << "apply? (y/n) "; std::cin >> ans; remove = ans == 'n'; } } if (remove) { it = res.erase(it); } else { ++it; } } if ((m_action == Apply || m_action == Ask) && !res.isEmpty()) { MatchesSolver *s = new MatchesSolver(res, m_model, this); connect(s, SIGNAL(finished(KJob*)), this, SLOT(matchesSolverDone(KJob*))); s->start(); } else { QCoreApplication::instance()->quit(); } } void matchesSolverDone(KJob *job) { if (job->error() == 0) { std::cout << "Matching successfully finished" << std::endl; } else { std::cout << "Matching failed with error: " << job->error() << std::endl; } QCoreApplication::instance()->quit(); } public: enum MatchAction { Apply, NotApply, Ask }; MatchAction m_action; PersonsModel *m_model; }; int main(int argc, char **argv) { QCoreApplication app(argc, argv); PersonsModel model; ResultPrinter r; r.m_model = &model; { QCommandLineParser parser; parser.addOption(QCommandLineOption(QStringLiteral("ask"), QStringLiteral("Ask whether to actually do the merge"))); parser.addOption(QCommandLineOption(QStringLiteral("apply"), QStringLiteral("Actually apply all merges. (!!!)"))); parser.addHelpOption(); parser.process(app); r.m_action = parser.isSet(QStringLiteral("apply")) ? ResultPrinter::Apply : parser.isSet(QStringLiteral("ask")) ? ResultPrinter::Ask : ResultPrinter::NotApply; } DuplicatesFinder *f = new DuplicatesFinder(&model); QObject::connect(f, SIGNAL(finished(KJob*)), &r, SLOT(print(KJob*))); QTimer *t = new QTimer(&app); t->setInterval(500); t->setSingleShot(true); QObject::connect(&model, SIGNAL(modelInitialized(bool)), t, SLOT(start())); QObject::connect(&model, SIGNAL(rowsInserted(QModelIndex,int,int)), t, SLOT(start())); QObject::connect(t, SIGNAL(timeout()), f, SLOT(start())); app.exec(); } #include "duplicates.moc" diff --git a/src/duplicatesfinder.cpp b/src/duplicatesfinder.cpp index 494b0bb..a82589f 100644 --- a/src/duplicatesfinder.cpp +++ b/src/duplicatesfinder.cpp @@ -1,112 +1,112 @@ /* Copyright (C) 2012 Aleix Pol Gonzalez This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "duplicatesfinder_p.h" #include "personsmodel.h" #include #include "kpeople_debug.h" using namespace KPeople; DuplicatesFinder::DuplicatesFinder(PersonsModel *model, QObject *parent) : KJob(parent) , m_model(model) { } void DuplicatesFinder::setSpecificPerson(const QString &personUri) { m_personUri = personUri; } void DuplicatesFinder::start() { if (m_personUri.isEmpty()) { QMetaObject::invokeMethod(this, "doSearch", Qt::QueuedConnection); } else { QMetaObject::invokeMethod(this, "doSpecificSearch", Qt::QueuedConnection); } } //TODO: start providing partial results so that we can start processing matches while it's not done void DuplicatesFinder::doSearch() { //NOTE: This can probably optimized. I'm just trying to get the semantics right at the moment //maybe using nepomuk for the matching would help? QVector collectedValues; m_matches.clear(); if (m_model->rowCount() == 0) { qCWarning(KPEOPLE_LOG) << "finding duplicates on empty model!"; } for (int i = 0, rows = m_model->rowCount(); i < rows; i++) { QModelIndex idx = m_model->index(i, 0); //we gather the values AbstractContact::Ptr values = idx.data(PersonsModel::PersonVCardRole).value(); //we check if it matches int j = 0; - Q_FOREACH (const AbstractContact::Ptr &valueToCompare, collectedValues) { + for (const AbstractContact::Ptr &valueToCompare : qAsConst(collectedValues)) { QList matchedRoles = Match::matchAt(values, valueToCompare); if (!matchedRoles.isEmpty()) { QPersistentModelIndex i2(m_model->index(j, 0)); m_matches.append(Match(matchedRoles, idx, i2)); } j++; } //we add our data for comparing later collectedValues.append(values); } emitResult(); } void DuplicatesFinder::doSpecificSearch() { m_matches.clear(); QModelIndex idx = m_model->indexForPersonUri(m_personUri); AbstractContact::Ptr values = idx.data(PersonsModel::PersonVCardRole).value(); for (int i = 0, rows = m_model->rowCount(); i < rows; i++) { QModelIndex idx2 = m_model->index(i, 0); if (idx2.data(PersonsModel::PersonUriRole) == m_personUri) { continue; } AbstractContact::Ptr values2 = idx2.data(PersonsModel::PersonVCardRole).value(); QList matchedRoles = Match::matchAt(values, values2); if (!matchedRoles.isEmpty()) { m_matches.append(Match(matchedRoles, idx, idx2)); } } emitResult(); } QList DuplicatesFinder::results() const { return m_matches; } diff --git a/src/matchessolver.cpp b/src/matchessolver.cpp index 87d007f..669e453 100644 --- a/src/matchessolver.cpp +++ b/src/matchessolver.cpp @@ -1,93 +1,95 @@ /* Copyright (C) 2012 Aleix Pol Gonzalez Copyright (C) 2013 David Edmundson This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "matchessolver_p.h" #include "match_p.h" #include "personsmodel.h" #include "personmanager_p.h" #include #include "kpeople_debug.h" using namespace KPeople; MatchesSolver::MatchesSolver(const QList &matches, PersonsModel *model, QObject *parent) : KJob(parent) , m_matches(matches) , m_model(model) { } void MatchesSolver::start() { QMetaObject::invokeMethod(this, "startMatching", Qt::QueuedConnection); } void MatchesSolver::startMatching() { //Will contain all the sets to be merged QHash > jobsData; // has a relation of each person, to know where it is QHash destinationResolver; - Q_FOREACH (const Match &m, m_matches) { + for (const Match &m : qAsConst(m_matches)) { QString urlA = m.indexA.data(PersonsModel::PersonUriRole).toString(); QString urlB = m.indexB.data(PersonsModel::PersonUriRole).toString(); Q_ASSERT(urlA != urlB); const bool inA = destinationResolver.contains(urlA); const bool inB = destinationResolver.contains(urlB); if (inA && inB) { //Both are in already, so we have to merge both sets destinationResolver[urlB] = urlA; jobsData[urlA] = jobsData.take(urlB); //we've put all items pointed to by urlA, to the B set //now we re-assign anything pointing to B as pointing to A //because they are the same - QList keys = destinationResolver.keys(urlB); - foreach (const QString &key, keys) { - destinationResolver[key] = urlA; + const auto keys = destinationResolver.keys(urlB); + auto it = keys.constBegin(); + const auto end = keys.constEnd(); + for (; it != end; ++it) { + destinationResolver[*it] = urlA; } } else { //if A is in but not B, we want B wherever A is if (inA) { qSwap(urlB, urlA); } //in case B is anywhere, add A to that set, otherwise just insert B const QString mergeUrl = destinationResolver.value(urlB, urlB); QSet &jobs = jobsData[mergeUrl]; jobs.insert(urlB); jobs.insert(urlA); //remember where urlA and urlB are Q_ASSERT(urlA != mergeUrl); destinationResolver.insert(urlA, mergeUrl); if (urlB != mergeUrl) { destinationResolver.insert(urlB, mergeUrl); } } } - Q_FOREACH (const QSet &uris, jobsData) { + for (const QSet &uris : qAsConst(jobsData)) { if (PersonManager::instance()->mergeContacts(uris.toList()).isEmpty()) { qCWarning(KPEOPLE_LOG) << "error: failing to merge contacts: " << uris; } } emitResult(); } diff --git a/src/metacontact.cpp b/src/metacontact.cpp index e7a05d4..b08414d 100644 --- a/src/metacontact.cpp +++ b/src/metacontact.cpp @@ -1,212 +1,212 @@ /* Copyright (C) 2013 David Edmundson This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "metacontact_p.h" #include "global.h" #include #include "kpeople_debug.h" #include namespace KPeople { class MetaContactData : public QSharedData { public: QString personUri; QStringList contactUris; AbstractContact::List contacts; AbstractContact::Ptr personAddressee; }; } // TODO: It feels like MetaContact and MetaContactProxy should be merged, // still, not today. class MetaContactProxy : public KPeople::AbstractContact { public: MetaContactProxy(const AbstractContact::List &contacts) : m_contacts(contacts) {} QVariant customProperty(const QString &key) const override { if (key.startsWith(QLatin1String("all-"))) { QVariantList ret; - Q_FOREACH (const AbstractContact::Ptr &contact, m_contacts) { + for (const AbstractContact::Ptr &contact : qAsConst(m_contacts)) { QVariant val = contact->customProperty(key); Q_ASSERT(val.canConvert() || val.isNull()); if (!val.isNull()) { ret.append(val.toList()); } } return ret; } else { - Q_FOREACH (const AbstractContact::Ptr &contact, m_contacts) { + for (const AbstractContact::Ptr &contact : qAsConst(m_contacts)) { QVariant val = contact->customProperty(key); if (val.isValid()) { return val; } } return QVariant(); } } AbstractContact::List m_contacts; }; using namespace KPeople; MetaContact::MetaContact(): d(new MetaContactData) { reload(); } MetaContact::MetaContact(const QString &personUri, const QMap &contacts): d(new MetaContactData) { d->personUri = personUri; QMap::const_iterator it = contacts.constBegin(); while (it != contacts.constEnd()) { insertContactInternal(it.key(), it.value()); it++; } reload(); } MetaContact::MetaContact(const QString &contactUri, const AbstractContact::Ptr &contact): d(new MetaContactData) { d->personUri = contactUri; insertContactInternal(contactUri, contact); reload(); } MetaContact::MetaContact(const MetaContact &other) : d(other.d) { } MetaContact &MetaContact::operator=(const MetaContact &other) { if (this != &other) { d = other.d; } return *this; } MetaContact::~MetaContact() { } QString MetaContact::id() const { return d->personUri; } bool MetaContact::isValid() const { return !d->contacts.isEmpty(); } QStringList MetaContact::contactUris() const { return d->contactUris; } AbstractContact::Ptr MetaContact::contact(const QString &contactUri) { int index = d->contactUris.indexOf(contactUri); if (index >= 0) { return d->contacts[index]; } else { return AbstractContact::Ptr(); } } AbstractContact::List MetaContact::contacts() const { return d->contacts; } const AbstractContact::Ptr &MetaContact::personAddressee() const { Q_ASSERT(d->personAddressee); return d->personAddressee; } int MetaContact::insertContact(const QString &contactUri, const AbstractContact::Ptr &contact) { int index = insertContactInternal(contactUri, contact); if (index >= 0) { reload(); } else { qCWarning(KPEOPLE_LOG) << "Inserting an already-present contact" << contactUri; } return index; } int MetaContact::insertContactInternal(const QString &contactUri, const AbstractContact::Ptr &contact) { if (d->contactUris.contains(contactUri)) { //if item is already listed, do nothing. return -1; } else { //TODO if from the local address book - prepend to give higher priority. int index = d->contacts.size(); d->contacts.append(contact); d->contactUris.append(contactUri); return index; } } int MetaContact::updateContact(const QString &contactUri, const AbstractContact::Ptr &contact) { const int index = d->contactUris.indexOf(contactUri); Q_ASSERT(index < 0 || d->contacts[index] == contact); if (index < 0) { qCWarning(KPEOPLE_LOG) << "contact not part of the metacontact"; } return index; } int MetaContact::removeContact(const QString &contactUri) { const int index = d->contactUris.indexOf(contactUri); if (index >= 0) { d->contacts.removeAt(index); d->contactUris.removeAt(index); reload(); } return index; } void MetaContact::reload() { //always favour the first item //Optimization, if only one contact re-use that one d->personAddressee = (d->contacts.size() == 1) ? d->contacts.first() : AbstractContact::Ptr(new MetaContactProxy(d->contacts)); Q_ASSERT(d->personAddressee); } diff --git a/src/persondata.cpp b/src/persondata.cpp index a054249..6e36c24 100644 --- a/src/persondata.cpp +++ b/src/persondata.cpp @@ -1,209 +1,209 @@ /* Copyright (C) 2013 David Edmundson (davidedmundson@kde.org) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "persondata.h" #include "metacontact_p.h" #include "personmanager_p.h" #include "personpluginmanager.h" #include "backends/basepersonsdatasource.h" #include "backends/contactmonitor.h" #include "backends/abstractcontact.h" #include #include "kpeople_debug.h" #include namespace KPeople { class PersonDataPrivate { public: QStringList contactUris; MetaContact metaContact; QList watchers; }; } using namespace KPeople; KPeople::PersonData::PersonData(const QString &id, QObject *parent): QObject(parent), d_ptr(new PersonDataPrivate) { Q_D(PersonData); if (id.isEmpty()) { return; } QString personUri; //query DB if (id.startsWith(QLatin1String("kpeople://"))) { personUri = id; } else { personUri = PersonManager::instance()->personUriForContact(id); } if (personUri.isEmpty()) { d->contactUris = QStringList() << id; } else { d->contactUris = PersonManager::instance()->contactsForPersonUri(personUri); } QMap contacts; - Q_FOREACH (const QString &contactUri, d->contactUris) { + for (const QString &contactUri : qAsConst(d->contactUris)) { //load the correct data source for this contact ID const QString sourceId = contactUri.left(contactUri.indexOf(QStringLiteral("://"))); Q_ASSERT(!sourceId.isEmpty()); BasePersonsDataSource *dataSource = PersonPluginManager::dataSource(sourceId); if (dataSource) { ContactMonitorPtr cw = dataSource->contactMonitor(contactUri); d->watchers << cw; //if the data source already has the contact set it already //if not it will be loaded when the contactChanged signal is emitted if (cw->contact()) { contacts[contactUri] = cw->contact(); } connect(cw.data(), SIGNAL(contactChanged()), SLOT(onContactChanged())); } else qCWarning(KPEOPLE_LOG) << "error: creating PersonData for unknown contact" << contactUri << id; } if (personUri.isEmpty() && contacts.size() == 1) { d->metaContact = MetaContact(id, contacts.first()); } else { d->metaContact = MetaContact(personUri, contacts); } } PersonData::~PersonData() { delete d_ptr; } bool PersonData::isValid() const { Q_D(const PersonData); return !d->metaContact.id().isEmpty(); } QString PersonData::personUri() const { Q_D(const PersonData); return d->metaContact.id(); } QStringList PersonData::contactUris() const { Q_D(const PersonData); return d->metaContact.contactUris(); } void PersonData::onContactChanged() { Q_D(PersonData); ContactMonitor *watcher = qobject_cast(sender()); if (d->metaContact.contactUris().contains(watcher->contactUri())) { #ifdef __GNUC__ #warning probably not needed anymore #endif d->metaContact.updateContact(watcher->contactUri(), watcher->contact()); } else { d->metaContact.insertContact(watcher->contactUri(), watcher->contact()); } Q_EMIT dataChanged(); } QPixmap PersonData::photo() const { QPixmap avatar; QVariant pic = contactCustomProperty(AbstractContact::PictureProperty); if (pic.canConvert()) { avatar = QPixmap::fromImage(pic.value()); } else if (pic.canConvert()) { avatar = QPixmap(pic.toUrl().toLocalFile()); } if (avatar.isNull()) { static QString defaultAvatar = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("kf5/kpeople/dummy_avatar.png")); avatar = QPixmap(defaultAvatar); } return avatar; } QVariant PersonData::contactCustomProperty(const QString &key) const { Q_D(const PersonData); return d->metaContact.personAddressee()->customProperty(key); } QString PersonData::presenceIconName() const { QString contactPresence = contactCustomProperty(QStringLiteral("telepathy-presence")).toString(); return KPeople::iconNameForPresenceString(contactPresence); } QString PersonData::name() const { return contactCustomProperty(AbstractContact::NameProperty).toString(); } QString PersonData::presence() const { return contactCustomProperty(AbstractContact::PresenceProperty).toString(); } QUrl PersonData::pictureUrl() const { return contactCustomProperty(AbstractContact::PictureProperty).toUrl(); } QString PersonData::email() const { return contactCustomProperty(AbstractContact::EmailProperty).toString(); } QStringList PersonData::groups() const { // We might want to cache it eventually? - QVariantList groups = contactCustomProperty(AbstractContact::GroupsProperty).toList(); + const QVariantList groups = contactCustomProperty(AbstractContact::GroupsProperty).toList(); QStringList ret; - Q_FOREACH (const QVariant &g, groups) { + for (const QVariant &g : groups) { Q_ASSERT(g.canConvert()); ret += g.toString(); } ret.removeDuplicates(); return ret; } QStringList PersonData::allEmails() const { - QVariantList emails = contactCustomProperty(AbstractContact::AllEmailsProperty).toList(); + const QVariantList emails = contactCustomProperty(AbstractContact::AllEmailsProperty).toList(); QStringList ret; - Q_FOREACH (const QVariant &e, emails) { + for (const QVariant &e : emails) { Q_ASSERT(e.canConvert()); ret += e.toString(); } ret.removeDuplicates(); return ret; } diff --git a/src/personmanager.cpp b/src/personmanager.cpp index 97bd66f..7a5c785 100644 --- a/src/personmanager.cpp +++ b/src/personmanager.cpp @@ -1,293 +1,293 @@ /* Copyright (C) 2013 David Edmundson Copyright 2013 Martin Klapetek This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "personmanager_p.h" #include #include "kpeople_debug.h" #include #include #include #include #include class Transaction { public: Transaction(const QSqlDatabase &db); void cancel(); ~Transaction(); Transaction(const Transaction &) = delete; Transaction& operator=(const Transaction &) = delete; private: QSqlDatabase m_db; bool m_cancelled = false; }; Transaction::Transaction(const QSqlDatabase &db) : m_db(db), m_cancelled(false) { m_db.transaction(); } void Transaction::cancel() { m_db.rollback(); m_cancelled = true; } Transaction::~Transaction() { if (!m_cancelled) { m_db.commit(); } } PersonManager::PersonManager(const QString &databasePath, QObject *parent): QObject(parent), m_db(QSqlDatabase::addDatabase(QStringLiteral("QSQLITE"), QStringLiteral("kpeoplePersonsManager"))) { m_db.setDatabaseName(databasePath); if (!m_db.open()) { qCWarning(KPEOPLE_LOG) << "Couldn't open the database at" << databasePath; } m_db.exec(QStringLiteral("CREATE TABLE IF NOT EXISTS persons (contactID VARCHAR UNIQUE NOT NULL, personID INT NOT NULL)")); m_db.exec(QStringLiteral("CREATE INDEX IF NOT EXISTS contactIdIndex ON persons (contactId)")); m_db.exec(QStringLiteral("CREATE INDEX IF NOT EXISTS personIdIndex ON persons (personId)")); QDBusConnection::sessionBus().connect(QString(), QStringLiteral("/KPeople"), QStringLiteral("org.kde.KPeople"), QStringLiteral("ContactAddedToPerson"), this, SIGNAL(contactAddedToPerson(QString,QString))); QDBusConnection::sessionBus().connect(QString(), QStringLiteral("/KPeople"), QStringLiteral("org.kde.KPeople"), QStringLiteral("ContactRemovedFromPerson"), this, SIGNAL(contactRemovedFromPerson(QString))); } PersonManager::~PersonManager() { } QMultiHash< QString, QString > PersonManager::allPersons() const { QMultiHash contactMapping; QSqlQuery query = m_db.exec(QStringLiteral("SELECT personID, contactID FROM persons")); while (query.next()) { const QString personUri = QLatin1String("kpeople://") + query.value(0).toString(); // we store as ints internally, convert it to a string here const QString contactID = query.value(1).toString(); contactMapping.insertMulti(personUri, contactID); } return contactMapping; } QStringList PersonManager::contactsForPersonUri(const QString &personUri) const { if (!personUri.startsWith(QLatin1String("kpeople://"))) { return QStringList(); } QStringList contactUris; //TODO port to the proper qsql method for args QSqlQuery query(m_db); query.prepare(QStringLiteral("SELECT contactID FROM persons WHERE personId = ?")); query.bindValue(0, personUri.mid(strlen("kpeople://"))); query.exec(); while (query.next()) { contactUris << query.value(0).toString(); } return contactUris; } QString PersonManager::personUriForContact(const QString &contactUri) const { QSqlQuery query(m_db); query.prepare(QStringLiteral("SELECT personId FROM persons WHERE contactId = ?")); query.bindValue(0, contactUri); query.exec(); if (query.next()) { return QLatin1String("kpeople://") + query.value(0).toString(); } return QString(); } QString PersonManager::mergeContacts(const QStringList &ids) { // no merging if we have only 0 || 1 ids if (ids.size() < 2) { return QString(); } QStringList metacontacts; QStringList contacts; bool rc = true; QList pendingMessages; // separate the passed ids to metacontacts and simple contacts - Q_FOREACH (const QString &id, ids) { + for (const QString &id : ids) { if (id.startsWith(QLatin1String("kpeople://"))) { metacontacts << id; } else { contacts << id; } } // create new personUriString // - if we're merging two simple contacts, create completely new id // - if we're merging an existing metacontact, take the first id and use it QString personUriString; if (metacontacts.count() == 0) { // query for the highest existing ID in the database and +1 it int personUri = 0; QSqlQuery query = m_db.exec(QStringLiteral("SELECT MAX(personID) FROM persons")); if (query.next()) { personUri = query.value(0).toInt(); personUri++; } personUriString = QLatin1String("kpeople://") + QString::number(personUri); } else { personUriString = metacontacts.first(); } // start a db transaction (ends automatically on destruction) Transaction t(m_db); // processed passed metacontacts if (metacontacts.count() > 1) { // collect all the contacts from other persons QStringList personContacts; - Q_FOREACH (const QString &id, metacontacts) { + for (const QString &id : qAsConst(metacontacts)) { if (id != personUriString) { personContacts << contactsForPersonUri(id); } } // iterate over all of the contacts and change their personID to the new personUriString - Q_FOREACH (const QString &id, personContacts) { + for (const QString &id : qAsConst(personContacts)) { QSqlQuery updateQuery(m_db); updateQuery.prepare(QStringLiteral("UPDATE persons SET personID = ? WHERE contactID = ?")); updateQuery.bindValue(0, personUriString.mid(strlen("kpeople://"))); updateQuery.bindValue(1, id); if (!updateQuery.exec()) { rc = false; } QDBusMessage message = QDBusMessage::createSignal(QStringLiteral("/KPeople"), QStringLiteral("org.kde.KPeople"), QStringLiteral("ContactRemovedFromPerson")); message.setArguments(QVariantList() << id); pendingMessages << message; message = QDBusMessage::createSignal(QStringLiteral("/KPeople"), QStringLiteral("org.kde.KPeople"), QStringLiteral("ContactAddedToPerson")); message.setArguments(QVariantList() << id << personUriString); pendingMessages << message; } } // process passed contacts if (!contacts.isEmpty()) { - Q_FOREACH (const QString &id, contacts) { + for (const QString &id : qAsConst(contacts)) { QSqlQuery insertQuery(m_db); insertQuery.prepare(QStringLiteral("INSERT INTO persons VALUES (?, ?)")); insertQuery.bindValue(0, id); insertQuery.bindValue(1, personUriString.mid(strlen("kpeople://"))); //strip kpeople:// if (!insertQuery.exec()) { rc = false; } //FUTURE OPTIMIZATION - this would be best as one signal, but arguments become complex QDBusMessage message = QDBusMessage::createSignal(QStringLiteral("/KPeople"), QStringLiteral("org.kde.KPeople"), QStringLiteral("ContactAddedToPerson")); message.setArguments(QVariantList() << id << personUriString); pendingMessages << message; } } //if success send all messages to other clients //otherwise roll back our database changes and return an empty string if (rc) { - Q_FOREACH (const QDBusMessage &message, pendingMessages) { + for (const QDBusMessage &message : qAsConst(pendingMessages)) { QDBusConnection::sessionBus().send(message); } } else { t.cancel(); personUriString.clear(); } return personUriString; } bool PersonManager::unmergeContact(const QString &id) { //remove rows from DB if (id.startsWith(QLatin1String("kpeople://"))) { QSqlQuery query(m_db); const QStringList contactUris = contactsForPersonUri(id); query.prepare(QStringLiteral("DELETE FROM persons WHERE personId = ?")); query.bindValue(0, id.mid(strlen("kpeople://"))); query.exec(); - Q_FOREACH (const QString &contactUri, contactUris) { + for (const QString &contactUri : contactUris) { //FUTURE OPTIMIZATION - this would be best as one signal, but arguments become complex QDBusMessage message = QDBusMessage::createSignal(QStringLiteral("/KPeople"), QStringLiteral("org.kde.KPeople"), QStringLiteral("ContactRemovedFromPerson")); message.setArguments(QVariantList() << contactUri); QDBusConnection::sessionBus().send(message); } } else { QSqlQuery query(m_db); query.prepare(QStringLiteral("DELETE FROM persons WHERE contactId = ?")); query.bindValue(0, id); query.exec(); //emit signal(dbus) Q_EMIT contactRemovedFromPerson(id); } //TODO return if removing rows worked return true; } PersonManager *PersonManager::instance(const QString &databasePath) { static PersonManager *s_instance = nullptr; if (!s_instance) { QString path = databasePath; if (path.isEmpty()) { path = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QStringLiteral("/kpeople/"); QDir().mkpath(path); path += QLatin1String("persondb"); } s_instance = new PersonManager(path); } return s_instance; } diff --git a/src/personpluginmanager.cpp b/src/personpluginmanager.cpp index 1cf893c..02b6a43 100644 --- a/src/personpluginmanager.cpp +++ b/src/personpluginmanager.cpp @@ -1,137 +1,137 @@ /* Copyright (C) 2013 David Edmundson This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "personpluginmanager.h" #include "backends/basepersonsdatasource.h" #include #include #include #include #include #include #include #include "kpeople_debug.h" using namespace KPeople; class PersonPluginManagerPrivate { public: PersonPluginManagerPrivate(); ~PersonPluginManagerPrivate(); QHash dataSourcePlugins; void loadDataSourcePlugins(); bool m_autoloadDataSourcePlugins; bool m_loadedDataSourcePlugins; QMutex m_mutex; }; Q_GLOBAL_STATIC(PersonPluginManagerPrivate, s_instance) PersonPluginManagerPrivate::PersonPluginManagerPrivate() : m_autoloadDataSourcePlugins(true) , m_loadedDataSourcePlugins(false) { } PersonPluginManagerPrivate::~PersonPluginManagerPrivate() { qDeleteAll(dataSourcePlugins); } void PersonPluginManagerPrivate::loadDataSourcePlugins() { - QVector pluginList = KPluginLoader::findPlugins(QStringLiteral("kpeople/datasource")); - Q_FOREACH (const KPluginMetaData &service, pluginList) { + const QVector pluginList = KPluginLoader::findPlugins(QStringLiteral("kpeople/datasource")); + for (const KPluginMetaData &service : pluginList) { KPluginLoader loader(service.fileName()); KPluginFactory *factory = loader.factory(); BasePersonsDataSource *dataSource = factory->create(); if (dataSource) { const QString pluginId = dataSource->sourcePluginId(); if (!dataSourcePlugins.contains(pluginId)) { dataSourcePlugins[pluginId] = dataSource; } else { dataSource->deleteLater(); qCDebug(KPEOPLE_LOG) << "Plugin" << pluginId << "was already loaded manually, ignoring..."; } } else { qCWarning(KPEOPLE_LOG) << "Failed to create data source " << service.name() << service.fileName(); } } //TODO: Remove as soon as KTp sources are released with the new plugin system - KService::List servicesList = KServiceTypeTrader::self()->query(QStringLiteral("KPeople/DataSource")); - Q_FOREACH (const KService::Ptr &service, servicesList) { + const KService::List servicesList = KServiceTypeTrader::self()->query(QStringLiteral("KPeople/DataSource")); + for (const KService::Ptr &service : servicesList) { BasePersonsDataSource *dataSource = service->createInstance(nullptr); if (dataSource) { dataSourcePlugins[dataSource->sourcePluginId()] = dataSource; } else { qCWarning(KPEOPLE_LOG) << "Failed to create data source " << service->name() << service->path(); } } m_loadedDataSourcePlugins = true; } void PersonPluginManager::setAutoloadDataSourcePlugins(bool autoloadDataSourcePlugins) { s_instance->m_autoloadDataSourcePlugins = autoloadDataSourcePlugins; } void PersonPluginManager::addDataSource(const QString &sourceId, BasePersonsDataSource *source) { QMutexLocker(&s_instance->m_mutex); if (s_instance->dataSourcePlugins.contains(sourceId)) { qCWarning(KPEOPLE_LOG) << "Attempting to load data source that is already loaded, overriding!"; s_instance->dataSourcePlugins[sourceId]->deleteLater(); } s_instance->dataSourcePlugins.insert(sourceId, source); } void PersonPluginManager::setDataSourcePlugins(const QHash &dataSources) { QMutexLocker(&s_instance->m_mutex); qDeleteAll(s_instance->dataSourcePlugins); s_instance->dataSourcePlugins = dataSources; s_instance->m_loadedDataSourcePlugins = true; } QList PersonPluginManager::dataSourcePlugins() { QMutexLocker(&s_instance->m_mutex); if (!s_instance->m_loadedDataSourcePlugins && s_instance->m_autoloadDataSourcePlugins) { s_instance->loadDataSourcePlugins(); } return s_instance->dataSourcePlugins.values(); } BasePersonsDataSource *PersonPluginManager::dataSource(const QString &sourceId) { QMutexLocker(&s_instance->m_mutex); if (!s_instance->m_loadedDataSourcePlugins && s_instance->m_autoloadDataSourcePlugins) { s_instance->loadDataSourcePlugins(); } return s_instance->dataSourcePlugins.value(sourceId); } diff --git a/src/personsmodel.cpp b/src/personsmodel.cpp index f638b24..d3f8574 100644 --- a/src/personsmodel.cpp +++ b/src/personsmodel.cpp @@ -1,498 +1,498 @@ /* Persons Model Copyright (C) 2012 Martin Klapetek Copyright (C) 2012 Aleix Pol Gonzalez Copyright (C) 2013 David Edmundson This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "personsmodel.h" #include "personpluginmanager.h" #include "metacontact_p.h" #include "backends/basepersonsdatasource.h" #include "personmanager_p.h" #include "backends/abstractcontact.h" #include #include #include #include #include "kpeople_debug.h" namespace KPeople { class PersonsModelPrivate : public QObject { Q_OBJECT public: PersonsModelPrivate(PersonsModel *q) : q(q) , genericAvatarImagePath(QStandardPaths::locate(QStandardPaths::QStandardPaths::GenericDataLocation, QStringLiteral("kf5/kpeople/dummy_avatar.png"))) , initialFetchesDoneCount(0) , isInitialized(false) , hasError(false) { } PersonsModel *const q; //NOTE This is the opposite way round to the return value from contactMapping() for easier lookups QHash contactToPersons; //hash of person objects indexed by ID QHash personIndex; //a list so we have an order in the model QVector metacontacts; QString genericAvatarImagePath; QVector m_sourceMonitors; int initialFetchesDoneCount; bool isInitialized; bool hasError; //methods that manipulate the model void addPerson(const MetaContact &mc); void removePerson(const QString &id); void personChanged(const QString &personUri); QString personUriForContact(const QString &contactUri) const; QVariant dataForContact(const QString &personUri, const AbstractContact::Ptr &contact, int role) const; // SLOTS void onContactsFetched(); //update when a resource signals a contact has changed void onContactAdded(const QString &contactUri, const AbstractContact::Ptr &contact); void onContactChanged(const QString &contactUri, const AbstractContact::Ptr &contact); void onContactRemoved(const QString &contactUri); //update on metadata changes void onAddContactToPerson(const QString &contactUri, const QString &newPersonUri); void onRemoveContactsFromPerson(const QString &contactUri); public Q_SLOTS: void onMonitorInitialFetchComplete(bool success = true); }; } using namespace KPeople; PersonsModel::PersonsModel(QObject *parent): QAbstractItemModel(parent), d_ptr(new PersonsModelPrivate(this)) { Q_D(PersonsModel); - - Q_FOREACH (BasePersonsDataSource *dataSource, PersonPluginManager::dataSourcePlugins()) { + const auto listPlugins = PersonPluginManager::dataSourcePlugins(); + for (BasePersonsDataSource *dataSource : listPlugins) { const AllContactsMonitorPtr monitor = dataSource->allContactsMonitor(); if (monitor->isInitialFetchComplete()) { QMetaObject::invokeMethod(d, "onMonitorInitialFetchComplete", Qt::QueuedConnection, Q_ARG(bool, monitor->initialFetchSuccess())); } else { connect(monitor.data(), &AllContactsMonitor::initialFetchComplete, d, &PersonsModelPrivate::onMonitorInitialFetchComplete); } d->m_sourceMonitors << monitor; } d->onContactsFetched(); connect(PersonManager::instance(), &PersonManager::contactAddedToPerson, d, &PersonsModelPrivate::onAddContactToPerson); connect(PersonManager::instance(), &PersonManager::contactRemovedFromPerson, d, &PersonsModelPrivate::onRemoveContactsFromPerson); } PersonsModel::~PersonsModel() { } QHash PersonsModel::roleNames() const { QHash roles = QAbstractItemModel::roleNames(); roles.insert(PersonUriRole, "personUri"); roles.insert(PersonVCardRole, "personVCard"); roles.insert(ContactsVCardRole, "contactsVCard"); return roles; } QVariant PersonsModel::data(const QModelIndex &index, int role) const { Q_D(const PersonsModel); //optimization - if we don't cover this role, ignore it if (role < Qt::UserRole && role != Qt::DisplayRole && role != Qt::DecorationRole) { return QVariant(); } if (index.row() < 0 || index.row() >= rowCount(index.parent())) { return QVariant(); } if (index.parent().isValid()) { if (role == ContactsVCardRole) { return QVariant::fromValue(AbstractContact::List()); } const MetaContact &mc = d->metacontacts.at(index.parent().row()); return d->dataForContact(mc.id(), mc.contacts().at(index.row()), role); } else { const MetaContact &mc = d->metacontacts.at(index.row()); return d->dataForContact(mc.id(), mc.personAddressee(), role); } } QVariant PersonsModelPrivate::dataForContact(const QString &personUri, const AbstractContact::Ptr &person, int role) const { switch (role) { case PersonsModel::FormattedNameRole: return person->customProperty(AbstractContact::NameProperty); case PersonsModel::PhotoRole: { QVariant pic = person->customProperty(AbstractContact::PictureProperty); if (pic.canConvert()) { QImage avatar = pic.value(); if (!avatar.isNull()) { return avatar; } } else if (pic.canConvert()) { QPixmap avatar = pic.value(); if (!avatar.isNull()) { return avatar; } } else if (pic.canConvert() && pic.toUrl().isLocalFile()) { QPixmap avatar = QPixmap(pic.toUrl().toLocalFile()); if (!avatar.isNull()) { return avatar; } } // If none of the above were valid images, // return the generic one return QPixmap(genericAvatarImagePath); } case PersonsModel::PersonUriRole: return personUri; case PersonsModel::PersonVCardRole: return QVariant::fromValue(person); case PersonsModel::ContactsVCardRole: return QVariant::fromValue(metacontacts[personIndex[personUri].row()].contacts()); case PersonsModel::GroupsRole: return person->customProperty(QStringLiteral("all-groups")); } return QVariant(); } int PersonsModel::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent); return 1; } int PersonsModel::rowCount(const QModelIndex &parent) const { Q_D(const PersonsModel); if (!parent.isValid()) { return d->metacontacts.size(); } if (parent.isValid() && !parent.parent().isValid()) { return d->metacontacts.at(parent.row()).contacts().count(); } return 0; } bool PersonsModel::isInitialized() const { Q_D(const PersonsModel); return d->isInitialized; } QModelIndex PersonsModel::index(int row, int column, const QModelIndex &parent) const { if (row < 0 || column < 0 || row >= rowCount(parent)) { return QModelIndex(); } //top level items have internalId -1. Anything >=0 is the row of the top level item if (!parent.isValid()) { return createIndex(row, column, -1); } return createIndex(row, column, parent.row()); } QModelIndex PersonsModel::parent(const QModelIndex &childIndex) const { if (childIndex.internalId() == -1 || !childIndex.isValid()) { return QModelIndex(); } return index(childIndex.internalId(), 0, QModelIndex()); } void PersonsModelPrivate::onMonitorInitialFetchComplete(bool success) { initialFetchesDoneCount++; if (!success) { hasError = true; } Q_ASSERT(initialFetchesDoneCount <= m_sourceMonitors.count()); if (initialFetchesDoneCount == m_sourceMonitors.count()) { isInitialized = true; Q_EMIT q->modelInitialized(!hasError); } } void PersonsModelPrivate::onContactsFetched() { QMap addresseeMap; //fetch all already loaded contacts from plugins - Q_FOREACH (const AllContactsMonitorPtr &contactWatcher, m_sourceMonitors) { + for (const AllContactsMonitorPtr &contactWatcher : qAsConst(m_sourceMonitors)) { addresseeMap.unite(contactWatcher->contacts()); } //add metacontacts const QMultiHash contactMapping = PersonManager::instance()->allPersons(); - Q_FOREACH (const QString &key, contactMapping.uniqueKeys()) { + for (const QString &key : contactMapping.uniqueKeys()) { QMap contacts; - Q_FOREACH (const QString &contact, contactMapping.values(key)) { + for (const QString &contact : contactMapping.values(key)) { contactToPersons[contact] = key; AbstractContact::Ptr ptr = addresseeMap.take(contact); if (ptr) { contacts[contact] = ptr; } } if (!contacts.isEmpty()) { addPerson(MetaContact(key, contacts)); } } //add remaining contacts QMap::const_iterator i; for (i = addresseeMap.constBegin(); i != addresseeMap.constEnd(); ++i) { addPerson(MetaContact(i.key(), i.value())); } - Q_FOREACH (const AllContactsMonitorPtr monitor, m_sourceMonitors) { + for (const AllContactsMonitorPtr monitor : qAsConst(m_sourceMonitors)) { connect(monitor.data(), &AllContactsMonitor::contactAdded, this, &PersonsModelPrivate::onContactAdded); connect(monitor.data(), &AllContactsMonitor::contactChanged, this, &PersonsModelPrivate::onContactChanged); connect(monitor.data(), &AllContactsMonitor::contactRemoved, this, &PersonsModelPrivate::onContactRemoved); } } void PersonsModelPrivate::onContactAdded(const QString &contactUri, const AbstractContact::Ptr &contact) { const QString &personUri = personUriForContact(contactUri); QHash::const_iterator pidx = personIndex.constFind(personUri); if (pidx != personIndex.constEnd()) { int personRow = pidx->row(); MetaContact &mc = metacontacts[personRow]; //if the MC object already contains this object, we want to update the row, not do an insert if (mc.contactUris().contains(contactUri)) { qCWarning(KPEOPLE_LOG) << "Source emitted contactAdded for a contact we already know about " << contactUri; onContactChanged(contactUri, contact); } else { int newContactPos = mc.contacts().size(); q->beginInsertRows(q->index(personRow), newContactPos, newContactPos); mc.insertContact(contactUri, contact); q->endInsertRows(); personChanged(personUri); } } else { //new contact -> new person QMap map; map[contactUri] = contact; addPerson(MetaContact(personUri, map)); } } void PersonsModelPrivate::onContactChanged(const QString &contactUri, const AbstractContact::Ptr &contact) { const QString &personUri = personUriForContact(contactUri); int personRow = personIndex[personUri].row(); int contactRow = metacontacts[personRow].updateContact(contactUri, contact); const QModelIndex contactIndex = q->index(contactRow, 0, q->index(personRow)); Q_EMIT q->dataChanged(contactIndex, contactIndex); personChanged(personUri); } void PersonsModelPrivate::onContactRemoved(const QString &contactUri) { const QString &personUri = personUriForContact(contactUri); int personRow = personIndex[personUri].row(); MetaContact &mc = metacontacts[personRow]; int contactPosition = mc.contactUris().indexOf(contactUri); q->beginRemoveRows(q->index(personRow, 0), contactPosition, contactPosition); mc.removeContact(contactUri); q->endRemoveRows(); //if MC object is now invalid remove the person from the list if (!mc.isValid()) { removePerson(personUri); } else { personChanged(personUri); } } void PersonsModelPrivate::onAddContactToPerson(const QString &contactUri, const QString &newPersonUri) { const QString oldPersonUri = personUriForContact(contactUri); contactToPersons.insert(contactUri, newPersonUri); int oldPersonRow = personIndex[oldPersonUri].row(); if (oldPersonRow < 0) { return; } MetaContact &oldMc = metacontacts[oldPersonRow]; //get contact already in the model, remove it from the previous contact int contactPosition = oldMc.contactUris().indexOf(contactUri); const AbstractContact::Ptr contact = oldMc.contacts().at(contactPosition); q->beginRemoveRows(q->index(oldPersonRow), contactPosition, contactPosition); oldMc.removeContact(contactUri); q->endRemoveRows(); if (!oldMc.isValid()) { removePerson(oldPersonUri); } else { personChanged(oldPersonUri); } //if the new person is already in the model, add the contact to it QHash::const_iterator pidx = personIndex.constFind(newPersonUri); if (pidx != personIndex.constEnd()) { int newPersonRow = pidx->row(); MetaContact &newMc = metacontacts[newPersonRow]; int newContactPos = newMc.contacts().size(); q->beginInsertRows(q->index(newPersonRow), newContactPos, newContactPos); newMc.insertContact(contactUri, contact); q->endInsertRows(); personChanged(newPersonUri); } else { //if the person is not in the model, create a new person and insert it QMap contacts; contacts[contactUri] = contact; addPerson(MetaContact(newPersonUri, contacts)); } } void PersonsModelPrivate::onRemoveContactsFromPerson(const QString &contactUri) { const QString personUri = personUriForContact(contactUri); int personRow = personIndex[personUri].row(); MetaContact &mc = metacontacts[personRow]; const AbstractContact::Ptr &contact = mc.contact(contactUri); const int index = mc.contactUris().indexOf(contactUri); q->beginRemoveRows(personIndex[personUri], index, index); mc.removeContact(contactUri); q->endRemoveRows(); contactToPersons.remove(contactUri); //if we don't want the person object anymore if (!mc.isValid()) { removePerson(personUri); } else { personChanged(personUri); } //now re-insert as a new contact //we know it's not part of a metacontact anymore so reinsert as a contact addPerson(MetaContact(contactUri, contact)); } void PersonsModelPrivate::addPerson(const KPeople::MetaContact &mc) { const QString &id = mc.id(); int row = metacontacts.size(); q->beginInsertRows(QModelIndex(), row, row); metacontacts.append(mc); personIndex[id] = q->index(row); q->endInsertRows(); } void PersonsModelPrivate::removePerson(const QString &id) { QPersistentModelIndex index = personIndex.value(id); if (!index.isValid()) { //item not found return; } q->beginRemoveRows(QModelIndex(), index.row(), index.row()); personIndex.remove(id); metacontacts.removeAt(index.row()); q->endRemoveRows(); } void PersonsModelPrivate::personChanged(const QString &personUri) { int row = personIndex[personUri].row(); if (row >= 0) { const QModelIndex personIndex = q->index(row); Q_EMIT q->dataChanged(personIndex, personIndex); } } QString PersonsModelPrivate::personUriForContact(const QString &contactUri) const { QHash::const_iterator it = contactToPersons.constFind(contactUri); if (it != contactToPersons.constEnd()) { return *it; } else { return contactUri; } } QModelIndex PersonsModel::indexForPersonUri(const QString &personUri) const { Q_D(const PersonsModel); return d->personIndex.value(personUri); } QVariant PersonsModel::get(int row, int role) { return index(row, 0).data(role); } QVariant PersonsModel::contactCustomProperty(const QModelIndex &index, const QString &key) const { Q_D(const PersonsModel); if (index.parent().isValid()) { const MetaContact &mc = d->metacontacts.at(index.parent().row()); return mc.contacts().at(index.row())->customProperty(key); } else { const MetaContact &mc = d->metacontacts.at(index.row()); return mc.personAddressee()->customProperty(key); } } #include "personsmodel.moc" diff --git a/src/personssortfilterproxymodel.cpp b/src/personssortfilterproxymodel.cpp index 5bed8f6..230665d 100644 --- a/src/personssortfilterproxymodel.cpp +++ b/src/personssortfilterproxymodel.cpp @@ -1,79 +1,79 @@ /* Copyright (C) 2015 Aleix Pol i Gonzalez This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "personssortfilterproxymodel.h" #include "backends/abstractcontact.h" #include "personsmodel.h" namespace KPeople { class PersonsSortFilterProxyModelPrivate { public: QStringList m_keys; }; PersonsSortFilterProxyModel::PersonsSortFilterProxyModel(QObject *parent) : QSortFilterProxyModel(parent) , d_ptr(new PersonsSortFilterProxyModelPrivate) {} PersonsSortFilterProxyModel::~PersonsSortFilterProxyModel() {} QStringList PersonsSortFilterProxyModel::requiredProperties() const { Q_D(const PersonsSortFilterProxyModel); return d->m_keys; } void PersonsSortFilterProxyModel::setRequiredProperties(const QStringList &props) { Q_D(PersonsSortFilterProxyModel); d->m_keys = props; invalidate(); } bool PersonsSortFilterProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const { Q_D(const PersonsSortFilterProxyModel); const QModelIndex idx = sourceModel()->index(source_row, 0, source_parent); Q_ASSERT(idx.isValid()); if (!QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent)) return false; const AbstractContact::Ptr contact = idx.data(KPeople::PersonsModel::PersonVCardRole).value(); Q_ASSERT(contact); // Don't filter if no keys are set if (d->m_keys.isEmpty()) { return true; } - foreach(const QString &key, d->m_keys) { + for(const QString &key : qAsConst(d->m_keys)) { if (!contact->customProperty(key).isNull()) { return true; } } return false; } } diff --git a/src/widgets/actions.cpp b/src/widgets/actions.cpp index 7beae52..a92d29d 100644 --- a/src/widgets/actions.cpp +++ b/src/widgets/actions.cpp @@ -1,77 +1,78 @@ /* Copyright (C) 2015 Aleix Pol i Gonzalez This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "actions.h" #include #include "kpeople_widgets_debug.h" #include #include #include #include #include #include "../backends/abstractpersonaction.h" namespace KPeople { static QList actionsPlugins() { QList actionPlugins; - QVector personPluginList = KPluginLoader::findPlugins(QStringLiteral("kpeople/actions")); - Q_FOREACH (const KPluginMetaData &service, personPluginList) { + const QVector personPluginList = KPluginLoader::findPlugins(QStringLiteral("kpeople/actions")); + for (const KPluginMetaData &service : personPluginList) { KPluginLoader loader(service.fileName()); KPluginFactory *factory = loader.factory(); if (!factory) { qCWarning(KPEOPLE_WIDGETS_LOG) << "Couldn't create the factory for" << service.name() << "at" << service.fileName(); continue; } AbstractPersonAction *plugin = factory->create(); if (plugin) { // qCDebug(KPEOPLE_WIDGETS_LOG) << "found plugin" << service->name(); actionPlugins << plugin; } } //TODO: Remove as soon as KTp sources are released with the new plugin system - KService::List personServicesList = KServiceTypeTrader::self()->query(QStringLiteral("KPeople/Plugin")); - Q_FOREACH (const KService::Ptr &service, personServicesList) { + const KService::List personServicesList = KServiceTypeTrader::self()->query(QStringLiteral("KPeople/Plugin")); + for (const KService::Ptr &service : personServicesList) { AbstractPersonAction *plugin = service->createInstance(nullptr); if (plugin) { // qCDebug(KPEOPLE_WIDGETS_LOG) << "found plugin" << service->name(); actionPlugins << plugin; } } return actionPlugins; } QList actionsForPerson(const QString &contactUri, QObject *parent) { PersonData person(contactUri); QList actions; - Q_FOREACH (KPeople::AbstractPersonAction *plugin, actionsPlugins()) { + const auto lst = actionsPlugins(); + for (KPeople::AbstractPersonAction *plugin : lst) { actions << plugin->actionsForPerson(person, parent); } return actions; } } diff --git a/src/widgets/mergedialog.cpp b/src/widgets/mergedialog.cpp index d862bef..fea1498 100644 --- a/src/widgets/mergedialog.cpp +++ b/src/widgets/mergedialog.cpp @@ -1,228 +1,228 @@ /* KPeople - Duplicates Copyright (C) 2013 Franck Arrecot This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "mergedialog.h" #include "mergedelegate.h" #include "duplicatesfinder_p.h" #include "persondata.h" #include "personsmodel.h" #include "matchessolver_p.h" #include #include #include #include #include #include #include "kpeople_widgets_debug.h" #include #include #include #include using namespace KPeople; class MergeDialogPrivate { public: PersonsModel *personsModel; QListView *view; MergeDelegate *delegate; QStandardItemModel *model; DuplicatesFinder *duplicatesFinder; KPixmapSequenceWidget *sequence; }; MergeDialog::MergeDialog(QWidget *parent) : QDialog(parent) , d_ptr(new MergeDialogPrivate) { Q_D(MergeDialog); d_ptr->personsModel = nullptr; d_ptr->delegate = nullptr; d_ptr->duplicatesFinder = nullptr; setWindowTitle(i18n("Duplicates Manager")); setLayout(new QVBoxLayout()); setMinimumSize(450, 350); d->model = new QStandardItemModel(this); d->view = new QListView(this); d->view->setModel(d->model); d->view->setEditTriggers(QAbstractItemView::NoEditTriggers); QLabel *topLabel = new QLabel(i18n("Select contacts to be merged")); QDialogButtonBox *buttons = new QDialogButtonBox(this); buttons->addButton(QDialogButtonBox::Ok); buttons->addButton(QDialogButtonBox::Cancel); connect(buttons, SIGNAL(accepted()), SLOT(onMergeButtonClicked())); connect(buttons, SIGNAL(rejected()), SLOT(reject())); d->sequence = new KPixmapSequenceWidget(this); d->sequence->setSequence(KPixmapSequence(QStringLiteral("process-working"), 22)); d->sequence->setInterval(100); d->sequence->setVisible(false); layout()->addWidget(topLabel); layout()->addWidget(d->view); layout()->addWidget(d->sequence); layout()->addWidget(buttons); } MergeDialog::~MergeDialog() { delete d_ptr; } void MergeDialog::setPersonsModel(PersonsModel *model) { Q_D(MergeDialog); d->personsModel = model; if (d->personsModel) { searchForDuplicates(); connect(d->personsModel, SIGNAL(modelInitialized(bool)), SLOT(searchForDuplicates())); } } void MergeDialog::searchForDuplicates() { Q_D(MergeDialog); if (!d->personsModel || !d->personsModel->rowCount() || d->duplicatesFinder) { qCWarning(KPEOPLE_WIDGETS_LOG) << "MergeDialog failed to launch the duplicates research"; return; } d->duplicatesFinder = new DuplicatesFinder(d->personsModel); connect(d->duplicatesFinder, SIGNAL(result(KJob*)), SLOT(searchForDuplicatesFinished(KJob*))); d->duplicatesFinder->start(); } void MergeDialog::onMergeButtonClicked() { Q_D(MergeDialog); QList matches; for (int i = 0, rows = d->model->rowCount(); i < rows; i++) { QStandardItem *item = d->model->item(i, 0); if (item->checkState() == Qt::Checked) { for (int j = 0, contactsCount = item->rowCount(); j < contactsCount; ++j) { QStandardItem *matchItem = item->child(j); matches << matchItem->data(MergeDialog::MergeReasonRole).value(); } } } MatchesSolver *solverJob = new MatchesSolver(matches, d->personsModel, this); solverJob->start(); d->sequence->setVisible(true); d->view->setEnabled(false); connect(solverJob, SIGNAL(finished(KJob*)), this, SLOT(accept())); } void MergeDialog::searchForDuplicatesFinished(KJob *) { Q_D(MergeDialog); feedDuplicateModelFromMatches(d->duplicatesFinder->results()); d->delegate = new MergeDelegate(d->view); d->view->setItemDelegate(d->delegate); // To extend the selected item connect(d->view->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), d->delegate, SLOT(onSelectedContactsChanged(QItemSelection,QItemSelection))); // To contract an already selected item connect(d->view, SIGNAL(doubleClicked(QModelIndex)), d->delegate, SLOT(onClickContactParent(QModelIndex))); } void MergeDialog::feedDuplicateModelFromMatches(const QList &matches) { Q_D(MergeDialog); QHash > compareTable; QHash doneIndexes; - Q_FOREACH (const Match &match, matches) { + for (const Match &match : matches) { QPersistentModelIndex destination = doneIndexes.value(match.indexA, match.indexA); QHash >::iterator currentValue = compareTable.find(destination); if (currentValue == compareTable.end()) { // new parent, create it compareTable[match.indexA] = QList() << match; } else { //know parent, add child currentValue->append(match); } doneIndexes[match.indexB] = destination; } // now build the model : 1st dimension = person candidate, 2nd dimension = match QStandardItem *rootItem = d->model->invisibleRootItem(); QHash >::const_iterator i; for (i = compareTable.constBegin(); i != compareTable.constEnd(); ++i) { // Build the merge Contact in the model QStandardItem *parent = itemMergeContactFromMatch(true, i->first()); rootItem->appendRow(parent); - Q_FOREACH (const Match &matchChild, *i) { + for (const Match &matchChild : qAsConst(*i)) { parent->appendRow(itemMergeContactFromMatch(false, matchChild)); } } rootItem->sortChildren(0); } QStandardItem *MergeDialog::itemMergeContactFromMatch(bool isParent, const Match &match) { QStandardItem *item = new QStandardItem; item->setCheckable(true); item->setCheckState(Qt::Unchecked); item->setSizeHint(MergeDelegate::pictureSize()); item->setData(true, KExtendableItemDelegate::ShowExtensionIndicatorRole); QVariant deco; if (!isParent) { // child QString uri = match.indexB.data(PersonsModel::PersonUriRole).toString(); item->setData(uri, UriRole); item->setData(qVariantFromValue(match), MergeReasonRole); item->setText(match.indexB.data(Qt::DisplayRole).toString()); deco = match.indexB.data(Qt::DecorationRole); } else { // parent QString uri = match.indexA.data(PersonsModel::PersonUriRole).toString(); item->setData(uri, UriRole); item->setText(match.indexA.data(Qt::DisplayRole).toString()); deco = match.indexA.data(Qt::DecorationRole); } QIcon icon; if (deco.type() == (QVariant::Icon)) { icon = deco.value(); } else if (deco.type() == (QVariant::Pixmap)) { icon = QIcon(deco.value()); } else if (deco.type() == (QVariant::Image)) { icon = QIcon(QPixmap::fromImage(deco.value())); } else { qCWarning(KPEOPLE_WIDGETS_LOG) << "unknown decoration type" << deco.typeName(); } item->setIcon(icon); return item; } diff --git a/src/widgets/persondetailsview.cpp b/src/widgets/persondetailsview.cpp index 8ee86f4..4d07c81 100644 --- a/src/widgets/persondetailsview.cpp +++ b/src/widgets/persondetailsview.cpp @@ -1,221 +1,221 @@ /* Copyright (C) 2011 Martin Klapetek Copyright (C) 2013 David Edmundson This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "persondetailsview.h" #include #include #include #include "kpeople_widgets_debug.h" #include #include #include #include #include #include #include #include #include "abstractfieldwidgetfactory.h" #include "plugins/emaildetailswidget.h" #include "global.h" #include "ui_person-details-presentation.h" namespace KPeople { class PersonDetailsViewPrivate { public: PersonData *m_person; Ui::PersonDetailsPresentation *m_personDetailsPresentation; QWidget *m_mainWidget; QList m_plugins; }; } using namespace KPeople; class CoreFieldsPlugin : public AbstractFieldWidgetFactory { public: CoreFieldsPlugin(const QString &field); ~CoreFieldsPlugin() override; QString label() const override; int sortWeight() const override; QWidget *createDetailsWidget(const PersonData &person, QWidget *parent) const override; private: QString m_field; }; CoreFieldsPlugin::CoreFieldsPlugin(const QString &field): m_field(field) { } CoreFieldsPlugin::~CoreFieldsPlugin() { } QString CoreFieldsPlugin::label() const { #ifdef __GNUC__ #warning fixme, should be made user-visible somehow #endif return m_field; } int CoreFieldsPlugin::sortWeight() const { return 1; } QWidget *CoreFieldsPlugin::createDetailsWidget(const PersonData &person, QWidget *parent) const { // we have a plugin specific for e-mails. if (m_field == QLatin1String("email")) { return nullptr; } QString text = person.contactCustomProperty(m_field).toString(); if (text.isEmpty()) { return nullptr; } return new QLabel(text, parent); } PersonDetailsView::PersonDetailsView(QWidget *parent) : QWidget(parent), d_ptr(new PersonDetailsViewPrivate()) { Q_D(PersonDetailsView); setLayout(new QVBoxLayout(this)); d->m_mainWidget = new QWidget(this); d->m_person = nullptr; QWidget *details = new QWidget(); d->m_personDetailsPresentation = new Ui::PersonDetailsPresentation(); d->m_personDetailsPresentation->setupUi(details); layout()->addWidget(details); layout()->addWidget(d->m_mainWidget); layout()->addItem(new QSpacerItem(1, 1, QSizePolicy::Fixed, QSizePolicy::Expanding)); //create plugins #ifdef __GNUC__ #warning figure out a way to list properties #endif - QStringList fields { QStringLiteral("name"), QStringLiteral("all-email") }; - Q_FOREACH (const QString &field, fields) { + const QStringList fields { QStringLiteral("name"), QStringLiteral("all-email") }; + for (const QString &field : fields) { d->m_plugins << new CoreFieldsPlugin(field); } d->m_plugins << new EmailFieldsPlugin(); // load every KPeopleWidgets Plugin - QVector personPluginList = KPluginLoader::findPlugins(QStringLiteral("kpeople/widgets")); + const QVector personPluginList = KPluginLoader::findPlugins(QStringLiteral("kpeople/widgets")); - Q_FOREACH (const KPluginMetaData &service, personPluginList) { + for (const KPluginMetaData &service : personPluginList) { KPluginLoader loader(service.fileName()); KPluginFactory *factory = loader.factory(); AbstractFieldWidgetFactory *f = qobject_cast(factory->create()); Q_ASSERT(f); if (f) { d->m_plugins << f; } } //TODO: Remove as soon as KTp sources are released with the new plugin system KService::List pluginList = KServiceTypeTrader::self()->query(QStringLiteral("KPeopleWidgets/Plugin")); - QList plugins = KPluginInfo::fromServices(pluginList); + const QList plugins = KPluginInfo::fromServices(pluginList); - Q_FOREACH (const KPluginInfo &p, plugins) { + for (const KPluginInfo &p : plugins) { QString error; AbstractFieldWidgetFactory *f = p.service()->createInstance(this, QVariantList(), &error); if (f) { d->m_plugins << f; } } //TODO Sort plugins } PersonDetailsView::~PersonDetailsView() { delete d_ptr; } void PersonDetailsView::setPerson(PersonData *person) { Q_D(PersonDetailsView); if (d->m_person) { disconnect(d->m_person, SIGNAL(dataChanged()), this, SLOT(reload())); } d->m_person = person; connect(d->m_person, SIGNAL(dataChanged()), this, SLOT(reload())); reload(); } // void PersonDetailsView::setPersonsModel(PersonsModel *model) // { // Q_D(PersonDetailsView); // Q_FOREACH (AbstractPersonDetailsWidget *detailsWidget, d->m_detailWidgets) { // detailsWidget->setPersonsModel(model); // } // } void PersonDetailsView::reload() { Q_D(PersonDetailsView); //replace the entire main widget int layoutIndex = layout()->indexOf(d->m_mainWidget); layout()->takeAt(layoutIndex); d->m_mainWidget->deleteLater(); d->m_mainWidget = new QWidget(this); dynamic_cast(layout())->insertWidget(layoutIndex, d->m_mainWidget); QFormLayout *layout = new QFormLayout(d->m_mainWidget); layout->setSpacing(4); //update header information //FIXME - possibly split this out into a new class with a nice setPerson method QPixmap avatar = d->m_person->photo(); d->m_personDetailsPresentation->avatarPixmapLabel->setPixmap(avatar.scaled(96, 96, Qt::KeepAspectRatio)); //FIXME d->m_personDetailsPresentation->presencePixmapLabel->setPixmap(QIcon::fromTheme(d->m_person->presenceIconName()).pixmap(32, 32)); //FIXME d->m_personDetailsPresentation->nameLabel->setText(d->m_person->name()); - Q_FOREACH (AbstractFieldWidgetFactory *widgetFactory, d->m_plugins) { + for (AbstractFieldWidgetFactory *widgetFactory : qAsConst(d->m_plugins)) { const QString label = widgetFactory->label() + QLatin1Char(':'); QWidget *widget = widgetFactory->createDetailsWidget(d->m_person->personUri(), this); if (widget) { QFont font = widget->font(); font.setBold(true); widget->setFont(font); QLabel *widgetLabel = new QLabel(label, this); layout->addRow(widgetLabel, widget); } } } diff --git a/src/widgets/plugins/emaildetailswidget.cpp b/src/widgets/plugins/emaildetailswidget.cpp index 936dc8f..ea2753f 100644 --- a/src/widgets/plugins/emaildetailswidget.cpp +++ b/src/widgets/plugins/emaildetailswidget.cpp @@ -1,57 +1,58 @@ /* Copyright (C) 2013 David Edmundson This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "emaildetailswidget.h" #include #include #include #include #include // K_PLUGIN_FACTORY( EmailDetailsWidgetFactory, registerPlugin(); ) // K_EXPORT_PLUGIN( EmailDetailsWidgetFactory("emaildetailswidgetplugin") ) using namespace KPeople; QWidget *EmailFieldsPlugin::createDetailsWidget(const PersonData &person, QWidget *parent) const { if (person.allEmails().isEmpty()) { return nullptr; } QWidget *widget = new QWidget(parent); QVBoxLayout *layout = new QVBoxLayout(widget); layout->setContentsMargins(0, 0, 0, 0); - Q_FOREACH (const QString &email, person.allEmails()) { + const auto lstEmails = person.allEmails(); + for (const QString &email : lstEmails) { layout->addWidget(new QLabel(email)); } widget->setLayout(layout); return widget; } QString EmailFieldsPlugin::label() const { return i18nc("E-mail field label", "E-mail"); } int EmailFieldsPlugin::sortWeight() const { return 50; } diff --git a/src/widgets/plugins/mergecontactswidget.cpp b/src/widgets/plugins/mergecontactswidget.cpp index 80283bd..80714cd 100644 --- a/src/widgets/plugins/mergecontactswidget.cpp +++ b/src/widgets/plugins/mergecontactswidget.cpp @@ -1,192 +1,192 @@ /* Copyright (C) 2013 Franck Arrecot This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "mergecontactswidget.h" #include "personpresentationwidget.h" #include "persondata.h" #include "personsmodel.h" #include "duplicatesfinder_p.h" #include #include #include #include #include #include #include "kpeople_debug.h" #include K_PLUGIN_FACTORY(MergeContactsWidgetFactory, registerPlugin();) K_EXPORT_PLUGIN(MergeContactsWidgetFactory("mergecontactswidgetplugin")) using namespace KPeople; MergeContactsWidget::MergeContactsWidget(QWidget *parent, const QVariantList &args) : AbstractPersonDetailsWidget(parent) , m_person(nullptr) , m_model(nullptr) , m_containerListDetails(nullptr) , m_duplicatesBuster(nullptr) { Q_UNUSED(args); setLayout(new QVBoxLayout()); m_mergeButton = new QPushButton(this); m_mergeButton->setText(i18n("Show Merge Suggestions...")); m_mergeButton->setVisible(false); connect(m_mergeButton, SIGNAL(clicked(bool)), this, SLOT(onMergePossibilitiesButtonPressed())); layout()->addWidget(m_mergeButton); } void MergeContactsWidget::setPerson(PersonData *person) { m_person = person; searchForDuplicates(); } void MergeContactsWidget::setPersonsModel(PersonsModel *model) { m_model = model; searchForDuplicates(); } void MergeContactsWidget::fillDuplicatesWidget(const QList &duplicates) { // clean the previous list delete m_containerListDetails; m_listMergeContacts.clear(); m_mergeButton->setVisible(!duplicates.isEmpty()); // 1- Vertical list of person-presentation-widget : one contact, one checkbox m_containerListDetails = new QWidget(this); m_containerListDetails->setLayout(new QVBoxLayout()); layout()->addWidget(m_containerListDetails); m_containerListDetails->setVisible(false); if (duplicates.isEmpty()) { return ; } // building the new button QPushButton *triggerButton = new QPushButton(m_containerListDetails); triggerButton->setText(i18n("Merge with Selected Contacts")); connect(triggerButton, SIGNAL(clicked(bool)), this, SLOT(onMergeButtonPressed())); m_containerListDetails->layout()->addWidget(triggerButton); // building personPresentationWidget to fill up the list Q_FOREACH (const QPersistentModelIndex &duplicate, duplicates) { // displaying contact in a user friendly way qCDebug(KPEOPLE_LOG) << "Name retireved form the duplicate :" << duplicate.data(Qt::DisplayRole).toString(); QIcon avatar ; QString name = duplicate.data(Qt::DisplayRole).toString(); QVariant decoration = duplicate.data(Qt::DecorationRole); if (decoration.type() == (QVariant::Icon)) { avatar = decoration.value(); } else if (decoration.type() == (QVariant::Pixmap)) { avatar = QIcon(decoration.value()); } // memorise the link between checkbox widget and model index PersonPresentationWidget *myMergeContactWidget = new PersonPresentationWidget(name, avatar, m_containerListDetails); m_containerListDetails->layout()->addWidget(myMergeContactWidget); m_listMergeContacts.append(qMakePair(duplicate, myMergeContactWidget)); } } QList MergeContactsWidget::duplicateBusterFromPerson(const QUrl &uri) const { Q_ASSERT(m_duplicatesBuster); - QList wrongFormatResults = m_duplicatesBuster->results(); + const QList wrongFormatResults = m_duplicatesBuster->results(); QList duplicateMatching; - Q_FOREACH (const Match &match, wrongFormatResults) { + for (const Match &match : wrongFormatResults) { // pick up only the couple with match with our parameter index QUrl uriA = match.indexA.data(PersonsModel::UriRole).toUrl(); // Tested with URI because QModelIndex isn't reliable if (uriA == uri) { duplicateMatching.append(match.indexB); } QUrl uriB = match.indexB.data(PersonsModel::UriRole).toUrl(); if (uriB == uri) { duplicateMatching.append(match.indexA); } } qCDebug(KPEOPLE_LOG) << "Result of the duplicates Buster :" << duplicateMatching.size(); return duplicateMatching; } void MergeContactsWidget::searchForDuplicates() { m_mergeButton->setVisible(false); if (m_duplicatesBuster || !m_person || !m_person->isValid() || !m_model) { qCDebug(KPEOPLE_LOG) << "Merge Widget failed to launch the duplicates search"; return; } m_duplicatesBuster = new DuplicatesFinder(m_model , this); connect(m_duplicatesBuster, SIGNAL(result(KJob*)), SLOT(searchForDuplicatesFinished())); m_duplicatesBuster->setSpecificPerson(m_person->uri()); m_duplicatesBuster->start(); } void MergeContactsWidget::searchForDuplicatesFinished() { QList duplicates = duplicateBusterFromPerson(m_person->uri()); fillDuplicatesWidget(duplicates); m_duplicatesBuster = 0; } void MergeContactsWidget::onMergePossibilitiesButtonPressed() { m_mergeButton->setVisible(false); m_containerListDetails->setVisible(true); } void MergeContactsWidget::onMergeButtonPressed() { // Retrieve the selected contacts QList urisToMerge; urisToMerge << m_person->uri(); // do the merge - Q_FOREACH (const QPersistentModelIndex &pIndex, getContactsCheckedToMerge()) { + const auto lst = getContactsCheckedToMerge(); + for (const QPersistentModelIndex &pIndex : lst) { urisToMerge << pIndex.data(PersonsModel::UriRole).toUrl(); } m_model->createPersonFromUris(urisToMerge); searchForDuplicates(); } QList MergeContactsWidget::getContactsCheckedToMerge() const { QList indexesToMerge; // retrieve all the widget where the box is checked - QPair mergeContact ; - Q_FOREACH (mergeContact, m_listMergeContacts) { + for (const QPair &mergeContact : qAsConst(m_listMergeContacts)) { if (mergeContact.second->isContactSelected()) { indexesToMerge.append(mergeContact.first); } } qCDebug(KPEOPLE_LOG) << "Amount of checked box enable :" << indexesToMerge.size(); return indexesToMerge; } diff --git a/src/widgets/plugins/phonedetailswidget.cpp b/src/widgets/plugins/phonedetailswidget.cpp index 76be2e8..089a690 100644 --- a/src/widgets/plugins/phonedetailswidget.cpp +++ b/src/widgets/plugins/phonedetailswidget.cpp @@ -1,63 +1,64 @@ /* Copyright (C) 2013 David Edmundson This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "phonedetailswidget.h" #include "persondata.h" #include #include #include #include #include K_PLUGIN_FACTORY(PhoneDetailsWidgetFactory, registerPlugin();) K_EXPORT_PLUGIN(PhoneDetailsWidgetFactory("phonedetailswidgetplugin")) using namespace KPeople; PhoneDetailsWidget::PhoneDetailsWidget(QWidget *parent, const QVariantList &args): AbstractPersonDetailsWidget(parent) { Q_UNUSED(args); setTitle(i18nc("Phone details title", "Phone")); setIcon(QIcon::fromTheme("phone")); setLayout(new QVBoxLayout(this)); } void PhoneDetailsWidget::setPerson(PersonData *person) { if (person->phones().size() == 0) { setActive(false); } else { setActive(true); } QLayoutItem *child; while ((child = layout()->takeAt(0)) != 0) { delete child->widget(); delete child; } - Q_FOREACH (const QString &phone, person->phones()) { + const auto lst = person->phones(); + for (const QString &phone : lst) { QLabel *phoneLabel = new QLabel(phone, this); phoneLabel->setTextInteractionFlags(Qt::TextSelectableByMouse); layout()->addWidget(phoneLabel); } }