File indexing completed on 2024-04-21 15:02:48
0001 /* 0002 Persons Model 0003 SPDX-FileCopyrightText: 2012 Martin Klapetek <martin.klapetek@gmail.com> 0004 SPDX-FileCopyrightText: 2012 Aleix Pol Gonzalez <aleixpol@blue-systems.com> 0005 SPDX-FileCopyrightText: 2013 David Edmundson <davidedmundson@kde.org> 0006 0007 SPDX-License-Identifier: LGPL-2.1-or-later 0008 */ 0009 0010 #include "personsmodel.h" 0011 0012 #include "backends/abstractcontact.h" 0013 #include "backends/basepersonsdatasource.h" 0014 #include "imageprovideruri_p.h" 0015 #include "metacontact_p.h" 0016 #include "personmanager_p.h" 0017 #include "personpluginmanager.h" 0018 0019 #include "kpeople_debug.h" 0020 0021 #include <QPixmap> 0022 #include <QStandardPaths> 0023 #include <QUrl> 0024 0025 namespace KPeople 0026 { 0027 class PersonsModelPrivate : public QObject 0028 { 0029 Q_OBJECT 0030 public: 0031 PersonsModelPrivate(PersonsModel *qq) 0032 : q(qq) 0033 { 0034 } 0035 PersonsModel *const q; 0036 0037 // NOTE This is the opposite way round to the return value from contactMapping() for easier lookups 0038 QHash<QString /*contactUri*/, QString /*PersonUri*/> contactToPersons; 0039 0040 // hash of person objects indexed by ID 0041 QHash<QString /*Person ID*/, QPersistentModelIndex /*Row*/> personIndex; 0042 0043 // a list so we have an order in the model 0044 QVector<MetaContact> metacontacts; 0045 0046 QVector<AllContactsMonitorPtr> m_sourceMonitors; 0047 0048 int initialFetchesDoneCount = 0; 0049 0050 bool isInitialized = false; 0051 bool hasError = false; 0052 0053 // methods that manipulate the model 0054 void addPerson(const MetaContact &mc); 0055 void removePerson(const QString &id); 0056 void personChanged(const QString &personUri); 0057 QString personUriForContact(const QString &contactUri) const; 0058 QVariant dataForContact(const QString &personUri, const AbstractContact::Ptr &contact, int role) const; 0059 0060 // SLOTS 0061 void onContactsFetched(); 0062 // update when a resource signals a contact has changed 0063 void onContactAdded(const QString &contactUri, const AbstractContact::Ptr &contact); 0064 void onContactChanged(const QString &contactUri, const AbstractContact::Ptr &contact); 0065 void onContactRemoved(const QString &contactUri); 0066 0067 // update on metadata changes 0068 void onAddContactToPerson(const QString &contactUri, const QString &newPersonUri); 0069 void onRemoveContactsFromPerson(const QString &contactUri); 0070 0071 public Q_SLOTS: 0072 void onMonitorInitialFetchComplete(bool success = true); 0073 }; 0074 } 0075 0076 using namespace KPeople; 0077 0078 PersonsModel::PersonsModel(QObject *parent) 0079 : QAbstractItemModel(parent) 0080 , d_ptr(new PersonsModelPrivate(this)) 0081 { 0082 Q_D(PersonsModel); 0083 const auto listPlugins = PersonPluginManager::dataSourcePlugins(); 0084 for (BasePersonsDataSource *dataSource : listPlugins) { 0085 const AllContactsMonitorPtr monitor = dataSource->allContactsMonitor(); 0086 if (monitor->isInitialFetchComplete()) { 0087 QMetaObject::invokeMethod(d, "onMonitorInitialFetchComplete", Qt::QueuedConnection, Q_ARG(bool, monitor->initialFetchSuccess())); 0088 } else { 0089 connect(monitor.data(), &AllContactsMonitor::initialFetchComplete, d, &PersonsModelPrivate::onMonitorInitialFetchComplete); 0090 } 0091 d->m_sourceMonitors << monitor; 0092 } 0093 d->onContactsFetched(); 0094 0095 connect(PersonManager::instance(), &PersonManager::contactAddedToPerson, d, &PersonsModelPrivate::onAddContactToPerson); 0096 connect(PersonManager::instance(), &PersonManager::contactRemovedFromPerson, d, &PersonsModelPrivate::onRemoveContactsFromPerson); 0097 0098 initResources(); 0099 } 0100 0101 PersonsModel::~PersonsModel() 0102 { 0103 } 0104 0105 QHash<int, QByteArray> PersonsModel::roleNames() const 0106 { 0107 QHash<int, QByteArray> roles = QAbstractItemModel::roleNames(); 0108 roles.insert(PersonUriRole, "personUri"); 0109 roles.insert(PersonVCardRole, "personVCard"); 0110 roles.insert(ContactsVCardRole, "contactsVCard"); 0111 roles.insert(PhoneNumberRole, "phoneNumber"); 0112 roles.insert(PhotoImageProviderUri, "photoImageProviderUri"); 0113 return roles; 0114 } 0115 0116 QVariant PersonsModel::data(const QModelIndex &index, int role) const 0117 { 0118 Q_D(const PersonsModel); 0119 0120 // optimization - if we don't cover this role, ignore it 0121 if (role < Qt::UserRole && role != Qt::DisplayRole && role != Qt::DecorationRole) { 0122 return QVariant(); 0123 } 0124 0125 if (index.row() < 0 || index.row() >= rowCount(index.parent())) { 0126 return QVariant(); 0127 } 0128 0129 if (index.parent().isValid()) { 0130 if (role == ContactsVCardRole) { 0131 return QVariant::fromValue<AbstractContact::List>(AbstractContact::List()); 0132 } 0133 const MetaContact &mc = d->metacontacts.at(index.parent().row()); 0134 0135 return d->dataForContact(mc.id(), mc.contacts().at(index.row()), role); 0136 } else { 0137 const MetaContact &mc = d->metacontacts.at(index.row()); 0138 return d->dataForContact(mc.id(), mc.personAddressee(), role); 0139 } 0140 } 0141 0142 QVariant PersonsModelPrivate::dataForContact(const QString &personUri, const AbstractContact::Ptr &person, int role) const 0143 { 0144 switch (role) { 0145 case PersonsModel::FormattedNameRole: 0146 return person->customProperty(AbstractContact::NameProperty); 0147 case PersonsModel::PhotoRole: { 0148 QVariant pic = person->customProperty(AbstractContact::PictureProperty); 0149 if (pic.canConvert<QImage>()) { 0150 QImage avatar = pic.value<QImage>(); 0151 if (!avatar.isNull()) { 0152 return avatar; 0153 } 0154 } else if (pic.canConvert<QPixmap>()) { 0155 QPixmap avatar = pic.value<QPixmap>(); 0156 if (!avatar.isNull()) { 0157 return avatar; 0158 } 0159 } else if (pic.canConvert<QUrl>() && pic.toUrl().isLocalFile()) { 0160 QPixmap avatar = QPixmap(pic.toUrl().toLocalFile()); 0161 if (!avatar.isNull()) { 0162 return avatar; 0163 } 0164 } 0165 0166 // If none of the above were valid images, 0167 // return the generic one 0168 return QPixmap(QStringLiteral(":/org.kde.kpeople/pixmaps/dummy_avatar.png")); 0169 } 0170 case PersonsModel::PersonUriRole: 0171 return personUri; 0172 case PersonsModel::PersonVCardRole: 0173 return QVariant::fromValue<AbstractContact::Ptr>(person); 0174 case PersonsModel::ContactsVCardRole: 0175 return QVariant::fromValue<AbstractContact::List>(metacontacts[personIndex[personUri].row()].contacts()); 0176 case PersonsModel::GroupsRole: 0177 return person->customProperty(QStringLiteral("all-groups")); 0178 case PersonsModel::PhoneNumberRole: 0179 return person->customProperty(AbstractContact::PhoneNumberProperty); 0180 case PersonsModel::PhotoImageProviderUri: 0181 return ::photoImageProviderUri(personUri); 0182 } 0183 return QVariant(); 0184 } 0185 0186 int PersonsModel::columnCount(const QModelIndex &parent) const 0187 { 0188 Q_UNUSED(parent); 0189 0190 return 1; 0191 } 0192 0193 int PersonsModel::rowCount(const QModelIndex &parent) const 0194 { 0195 Q_D(const PersonsModel); 0196 0197 if (!parent.isValid()) { 0198 return d->metacontacts.size(); 0199 } 0200 0201 if (parent.isValid() && !parent.parent().isValid()) { 0202 return d->metacontacts.at(parent.row()).contacts().count(); 0203 } 0204 0205 return 0; 0206 } 0207 0208 bool PersonsModel::isInitialized() const 0209 { 0210 Q_D(const PersonsModel); 0211 0212 return d->isInitialized; 0213 } 0214 0215 QModelIndex PersonsModel::index(int row, int column, const QModelIndex &parent) const 0216 { 0217 if (row < 0 || column < 0 || row >= rowCount(parent)) { 0218 return QModelIndex(); 0219 } 0220 // top level items have internalId -1. Anything >=0 is the row of the top level item 0221 if (!parent.isValid()) { 0222 return createIndex(row, column, -1); 0223 } 0224 0225 return createIndex(row, column, parent.row()); 0226 } 0227 0228 QModelIndex PersonsModel::parent(const QModelIndex &childIndex) const 0229 { 0230 if (childIndex.internalId() == -1 || !childIndex.isValid()) { 0231 return QModelIndex(); 0232 } 0233 0234 return index(childIndex.internalId(), 0, QModelIndex()); 0235 } 0236 0237 void PersonsModelPrivate::onMonitorInitialFetchComplete(bool success) 0238 { 0239 initialFetchesDoneCount++; 0240 if (!success) { 0241 hasError = true; 0242 } 0243 Q_ASSERT(initialFetchesDoneCount <= m_sourceMonitors.count()); 0244 if (initialFetchesDoneCount == m_sourceMonitors.count()) { 0245 isInitialized = true; 0246 Q_EMIT q->modelInitialized(!hasError); 0247 } 0248 } 0249 0250 void PersonsModelPrivate::onContactsFetched() 0251 { 0252 QMap<QString, AbstractContact::Ptr> addresseeMap; 0253 0254 // fetch all already loaded contacts from plugins 0255 for (const AllContactsMonitorPtr &contactWatcher : std::as_const(m_sourceMonitors)) { 0256 addresseeMap.insert(contactWatcher->contacts()); 0257 } 0258 0259 // add metacontacts 0260 const QMultiHash<QString, QString> contactMapping = PersonManager::instance()->allPersons(); 0261 0262 for (const QString &key : contactMapping.uniqueKeys()) { 0263 QMap<QString, AbstractContact::Ptr> contacts; 0264 for (const QString &contact : contactMapping.values(key)) { 0265 contactToPersons[contact] = key; 0266 AbstractContact::Ptr ptr = addresseeMap.take(contact); 0267 if (ptr) { 0268 contacts[contact] = ptr; 0269 } 0270 } 0271 if (!contacts.isEmpty()) { 0272 addPerson(MetaContact(key, contacts)); 0273 } 0274 } 0275 0276 // add remaining contacts 0277 QMap<QString, AbstractContact::Ptr>::const_iterator i; 0278 for (i = addresseeMap.constBegin(); i != addresseeMap.constEnd(); ++i) { 0279 addPerson(MetaContact(i.key(), i.value())); 0280 } 0281 0282 for (const AllContactsMonitorPtr &monitor : std::as_const(m_sourceMonitors)) { 0283 connect(monitor.data(), &AllContactsMonitor::contactAdded, this, &PersonsModelPrivate::onContactAdded); 0284 connect(monitor.data(), &AllContactsMonitor::contactChanged, this, &PersonsModelPrivate::onContactChanged); 0285 connect(monitor.data(), &AllContactsMonitor::contactRemoved, this, &PersonsModelPrivate::onContactRemoved); 0286 } 0287 } 0288 0289 void PersonsModelPrivate::onContactAdded(const QString &contactUri, const AbstractContact::Ptr &contact) 0290 { 0291 const QString &personUri = personUriForContact(contactUri); 0292 0293 QHash<QString, QPersistentModelIndex>::const_iterator pidx = personIndex.constFind(personUri); 0294 if (pidx != personIndex.constEnd()) { 0295 int personRow = pidx->row(); 0296 MetaContact &mc = metacontacts[personRow]; 0297 0298 // if the MC object already contains this object, we want to update the row, not do an insert 0299 if (mc.contactUris().contains(contactUri)) { 0300 qCWarning(KPEOPLE_LOG) << "Source emitted contactAdded for a contact we already know about " << contactUri; 0301 onContactChanged(contactUri, contact); 0302 } else { 0303 int newContactPos = mc.contacts().size(); 0304 q->beginInsertRows(q->index(personRow), newContactPos, newContactPos); 0305 mc.insertContact(contactUri, contact); 0306 q->endInsertRows(); 0307 personChanged(personUri); 0308 } 0309 } else { // new contact -> new person 0310 QMap<QString, AbstractContact::Ptr> map; 0311 map[contactUri] = contact; 0312 addPerson(MetaContact(personUri, map)); 0313 } 0314 } 0315 0316 void PersonsModelPrivate::onContactChanged(const QString &contactUri, const AbstractContact::Ptr &contact) 0317 { 0318 const QString &personUri = personUriForContact(contactUri); 0319 int personRow = personIndex[personUri].row(); 0320 int contactRow = metacontacts[personRow].updateContact(contactUri, contact); 0321 0322 const QModelIndex contactIndex = q->index(contactRow, 0, q->index(personRow)); 0323 0324 Q_EMIT q->dataChanged(contactIndex, contactIndex); 0325 0326 personChanged(personUri); 0327 } 0328 0329 void PersonsModelPrivate::onContactRemoved(const QString &contactUri) 0330 { 0331 const QString &personUri = personUriForContact(contactUri); 0332 0333 int personRow = personIndex[personUri].row(); 0334 0335 MetaContact &mc = metacontacts[personRow]; 0336 int contactPosition = mc.contactUris().indexOf(contactUri); 0337 q->beginRemoveRows(q->index(personRow, 0), contactPosition, contactPosition); 0338 mc.removeContact(contactUri); 0339 q->endRemoveRows(); 0340 0341 // if MC object is now invalid remove the person from the list 0342 if (!mc.isValid()) { 0343 removePerson(personUri); 0344 } else { 0345 personChanged(personUri); 0346 } 0347 } 0348 0349 void PersonsModelPrivate::onAddContactToPerson(const QString &contactUri, const QString &newPersonUri) 0350 { 0351 const QString oldPersonUri = personUriForContact(contactUri); 0352 0353 contactToPersons.insert(contactUri, newPersonUri); 0354 0355 int oldPersonRow = personIndex[oldPersonUri].row(); 0356 0357 if (oldPersonRow < 0) { 0358 return; 0359 } 0360 0361 MetaContact &oldMc = metacontacts[oldPersonRow]; 0362 0363 // get contact already in the model, remove it from the previous contact 0364 int contactPosition = oldMc.contactUris().indexOf(contactUri); 0365 const AbstractContact::Ptr contact = oldMc.contacts().at(contactPosition); 0366 0367 q->beginRemoveRows(q->index(oldPersonRow), contactPosition, contactPosition); 0368 oldMc.removeContact(contactUri); 0369 q->endRemoveRows(); 0370 0371 if (!oldMc.isValid()) { 0372 removePerson(oldPersonUri); 0373 } else { 0374 personChanged(oldPersonUri); 0375 } 0376 0377 // if the new person is already in the model, add the contact to it 0378 QHash<QString, QPersistentModelIndex>::const_iterator pidx = personIndex.constFind(newPersonUri); 0379 if (pidx != personIndex.constEnd()) { 0380 int newPersonRow = pidx->row(); 0381 MetaContact &newMc = metacontacts[newPersonRow]; 0382 int newContactPos = newMc.contacts().size(); 0383 q->beginInsertRows(q->index(newPersonRow), newContactPos, newContactPos); 0384 newMc.insertContact(contactUri, contact); 0385 q->endInsertRows(); 0386 personChanged(newPersonUri); 0387 } else { // if the person is not in the model, create a new person and insert it 0388 QMap<QString, AbstractContact::Ptr> contacts; 0389 contacts[contactUri] = contact; 0390 addPerson(MetaContact(newPersonUri, contacts)); 0391 } 0392 } 0393 0394 void PersonsModelPrivate::onRemoveContactsFromPerson(const QString &contactUri) 0395 { 0396 const QString personUri = personUriForContact(contactUri); 0397 int personRow = personIndex[personUri].row(); 0398 MetaContact &mc = metacontacts[personRow]; 0399 0400 const AbstractContact::Ptr &contact = mc.contact(contactUri); 0401 const int index = mc.contactUris().indexOf(contactUri); 0402 0403 q->beginRemoveRows(personIndex[personUri], index, index); 0404 mc.removeContact(contactUri); 0405 q->endRemoveRows(); 0406 contactToPersons.remove(contactUri); 0407 0408 // if we don't want the person object anymore 0409 if (!mc.isValid()) { 0410 removePerson(personUri); 0411 } else { 0412 personChanged(personUri); 0413 } 0414 0415 // now re-insert as a new contact 0416 // we know it's not part of a metacontact anymore so reinsert as a contact 0417 addPerson(MetaContact(contactUri, contact)); 0418 } 0419 0420 void PersonsModelPrivate::addPerson(const KPeople::MetaContact &mc) 0421 { 0422 const QString &id = mc.id(); 0423 0424 int row = metacontacts.size(); 0425 q->beginInsertRows(QModelIndex(), row, row); 0426 metacontacts.append(mc); 0427 personIndex[id] = q->index(row); 0428 q->endInsertRows(); 0429 } 0430 0431 void PersonsModelPrivate::removePerson(const QString &id) 0432 { 0433 QPersistentModelIndex index = personIndex.value(id); 0434 if (!index.isValid()) { // item not found 0435 return; 0436 } 0437 0438 q->beginRemoveRows(QModelIndex(), index.row(), index.row()); 0439 personIndex.remove(id); 0440 metacontacts.removeAt(index.row()); 0441 q->endRemoveRows(); 0442 } 0443 0444 void PersonsModelPrivate::personChanged(const QString &personUri) 0445 { 0446 int row = personIndex[personUri].row(); 0447 if (row >= 0) { 0448 const QModelIndex personIndex = q->index(row); 0449 Q_EMIT q->dataChanged(personIndex, personIndex); 0450 } 0451 } 0452 0453 QString PersonsModelPrivate::personUriForContact(const QString &contactUri) const 0454 { 0455 QHash<QString, QString>::const_iterator it = contactToPersons.constFind(contactUri); 0456 if (it != contactToPersons.constEnd()) { 0457 return *it; 0458 } else { 0459 return contactUri; 0460 } 0461 } 0462 0463 QModelIndex PersonsModel::indexForPersonUri(const QString &personUri) const 0464 { 0465 Q_D(const PersonsModel); 0466 return d->personIndex.value(personUri); 0467 } 0468 0469 QVariant PersonsModel::get(int row, int role) 0470 { 0471 return index(row, 0).data(role); 0472 } 0473 0474 QVariant PersonsModel::contactCustomProperty(const QModelIndex &index, const QString &key) const 0475 { 0476 Q_D(const PersonsModel); 0477 if (index.parent().isValid()) { 0478 const MetaContact &mc = d->metacontacts.at(index.parent().row()); 0479 0480 return mc.contacts().at(index.row())->customProperty(key); 0481 } else { 0482 const MetaContact &mc = d->metacontacts.at(index.row()); 0483 return mc.personAddressee()->customProperty(key); 0484 } 0485 } 0486 0487 #include "moc_personsmodel.cpp" 0488 #include "personsmodel.moc"