22 #include "personsmodel.h"
24 #include "personpluginmanager_p.h"
25 #include "metacontact_p.h"
26 #include "backends/basepersonsdatasource.h"
27 #include "personmanager_p.h"
28 #include "backends/abstractcontact.h"
30 #include <QStandardPaths>
38 class PersonsModelPrivate :
public QObject
44 , genericAvatarImagePath(QStandardPaths::locate(QStandardPaths::QStandardPaths::GenericDataLocation, QStringLiteral(
"kpeople/dummy_avatar.png")))
45 , initialFetchesDoneCount(0)
46 , isInitialized(false)
53 QHash<QString , QString > contactToPersons;
56 QHash<QString , QPersistentModelIndex > personIndex;
59 QList<MetaContact> metacontacts;
61 QString genericAvatarImagePath;
62 QList<AllContactsMonitorPtr> m_sourceMonitors;
64 int initialFetchesDoneCount;
70 void addPerson(
const MetaContact &mc);
71 void removePerson(
const QString &
id);
72 void personChanged(
const QString &personUri);
73 QString personUriForContact(
const QString &contactUri)
const;
74 QVariant dataForContact(
const QString &personUri,
const AbstractContact::Ptr &contact,
int role)
const;
77 void onContactsFetched();
79 void onContactAdded(
const QString &contactUri,
const AbstractContact::Ptr &contact);
80 void onContactChanged(
const QString &contactUri,
const AbstractContact::Ptr &contact);
81 void onContactRemoved(
const QString &contactUri);
84 void onAddContactToPerson(
const QString &contactUri,
const QString &newPersonUri);
85 void onRemoveContactsFromPerson(
const QString &contactUri);
88 void onMonitorInitialFetchComplete(
bool success =
true);
94 PersonsModel::PersonsModel(QObject *parent):
95 QAbstractItemModel(parent),
96 d_ptr(new PersonsModelPrivate(this))
100 Q_FOREACH (BasePersonsDataSource *dataSource, PersonPluginManager::dataSourcePlugins()) {
101 const AllContactsMonitorPtr monitor = dataSource->allContactsMonitor();
102 if (monitor->isInitialFetchComplete()) {
103 QMetaObject::invokeMethod(d,
"onMonitorInitialFetchComplete", Qt::QueuedConnection, Q_ARG(
bool, monitor->initialFetchSuccess()));
106 d, &PersonsModelPrivate::onMonitorInitialFetchComplete);
108 d->m_sourceMonitors << monitor;
110 d->onContactsFetched();
112 connect(PersonManager::instance(), &PersonManager::contactAddedToPerson, d, &PersonsModelPrivate::onAddContactToPerson);
113 connect(PersonManager::instance(), &PersonManager::contactRemovedFromPerson, d, &PersonsModelPrivate::onRemoveContactsFromPerson);
116 PersonsModel::~PersonsModel()
120 QHash<int, QByteArray> PersonsModel::roleNames()
const
122 QHash<int, QByteArray> roles = QAbstractItemModel::roleNames();
123 roles.insert(PersonUriRole,
"personUri");
124 roles.insert(PersonVCardRole,
"personVCard");
125 roles.insert(ContactsVCardRole,
"contactsVCard");
129 QVariant PersonsModel::data(
const QModelIndex &index,
int role)
const
134 if (role < Qt::UserRole && role != Qt::DisplayRole && role != Qt::DecorationRole) {
138 if (index.row() < 0 || index.row() >= rowCount(index.parent())) {
142 if (index.parent().isValid()) {
143 if (role == ContactsVCardRole) {
144 return QVariant::fromValue<AbstractContact::List>(AbstractContact::List());
146 const MetaContact &mc = d->metacontacts.at(index.parent().row());
148 return d->dataForContact(mc.id(), mc.contacts().at(index.row()), role);
150 const MetaContact &mc = d->metacontacts.at(index.row());
151 return d->dataForContact(mc.id(), mc.personAddressee(), role);
155 QVariant PersonsModelPrivate::dataForContact(
const QString &personUri,
const AbstractContact::Ptr &person,
int role)
const
158 case PersonsModel::FormattedNameRole:
160 case PersonsModel::PhotoRole: {
162 if (pic.canConvert<QImage>()) {
163 QImage avatar = pic.value<QImage>();
164 if (!avatar.isNull()) {
167 }
else if (pic.canConvert<QPixmap>()) {
168 QPixmap avatar = pic.value<QPixmap>();
169 if (!avatar.isNull()) {
172 }
else if (pic.canConvert<QUrl>() && pic.toUrl().isLocalFile()) {
173 QPixmap avatar = QPixmap(pic.toUrl().toLocalFile());
174 if (!avatar.isNull()) {
181 return QPixmap(genericAvatarImagePath);
183 case PersonsModel::PersonUriRole:
185 case PersonsModel::PersonVCardRole:
186 return QVariant::fromValue<AbstractContact::Ptr>(person);
187 case PersonsModel::ContactsVCardRole:
188 return QVariant::fromValue<AbstractContact::List>(metacontacts[personIndex[personUri].row()].contacts());
189 case PersonsModel::GroupsRole:
190 return person->customProperty(QStringLiteral(
"all-groups"));
195 int PersonsModel::columnCount(
const QModelIndex &parent)
const
202 int PersonsModel::rowCount(
const QModelIndex &parent)
const
206 if (!parent.isValid()) {
207 return d->metacontacts.size();
210 if (parent.isValid() && !parent.parent().isValid()) {
211 return d->metacontacts.at(parent.row()).contacts().count();
221 return d->isInitialized;
224 QModelIndex PersonsModel::index(
int row,
int column,
const QModelIndex &parent)
const
226 if (row < 0 || column < 0 || row >= rowCount(parent)) {
227 return QModelIndex();
230 if (!parent.isValid()) {
231 return createIndex(row, column, -1);
234 return createIndex(row, column, parent.row());
237 QModelIndex PersonsModel::parent(
const QModelIndex &childIndex)
const
239 if (childIndex.internalId() == -1 || !childIndex.isValid()) {
240 return QModelIndex();
243 return index(childIndex.internalId(), 0, QModelIndex());
246 void PersonsModelPrivate::onMonitorInitialFetchComplete(
bool success)
248 initialFetchesDoneCount++;
252 Q_ASSERT(initialFetchesDoneCount <= m_sourceMonitors.count());
253 if (initialFetchesDoneCount == m_sourceMonitors.count()) {
254 isInitialized =
true;
255 Q_EMIT q->modelInitialized(!hasError);
259 void PersonsModelPrivate::onContactsFetched()
261 QMap<QString, AbstractContact::Ptr> addresseeMap;
264 Q_FOREACH (
const AllContactsMonitorPtr &contactWatcher, m_sourceMonitors) {
265 addresseeMap.unite(contactWatcher->contacts());
269 const QMultiHash<QString, QString> contactMapping = PersonManager::instance()->allPersons();
271 Q_FOREACH (
const QString &key, contactMapping.uniqueKeys()) {
272 QMap<QString, AbstractContact::Ptr> contacts;
273 Q_FOREACH (
const QString &contact, contactMapping.values(key)) {
274 contactToPersons[contact] = key;
275 if (addresseeMap.contains(contact)) {
276 contacts[contact] = addresseeMap.take(contact);
279 if (!contacts.isEmpty()) {
280 addPerson(MetaContact(key, contacts));
285 QMap<QString, AbstractContact::Ptr>::const_iterator i;
286 for (i = addresseeMap.constBegin(); i != addresseeMap.constEnd(); ++i) {
287 addPerson(MetaContact(i.key(), i.value()));
290 Q_FOREACH (
const AllContactsMonitorPtr monitor, m_sourceMonitors) {
297 void PersonsModelPrivate::onContactAdded(
const QString &contactUri,
const AbstractContact::Ptr &contact)
299 const QString &personUri = personUriForContact(contactUri);
301 if (personIndex.contains(personUri)) {
302 int personRow = personIndex[personUri].row();
303 MetaContact &mc = metacontacts[personRow];
306 if (mc.contactUris().contains(contactUri)) {
307 qWarning() <<
"Source emitted contactAdded for a contact we already know about " << contactUri;
308 onContactChanged(contactUri, contact);
310 int newContactPos = mc.contacts().size();
311 q->beginInsertRows(q->index(personRow), newContactPos, newContactPos);
312 mc.insertContact(contactUri, contact);
314 personChanged(personUri);
317 QMap<QString, AbstractContact::Ptr> map;
318 map[contactUri] = contact;
319 addPerson(MetaContact(personUri, map));
323 void PersonsModelPrivate::onContactChanged(
const QString &contactUri,
const AbstractContact::Ptr &contact)
325 const QString &personUri = personUriForContact(contactUri);
326 int personRow = personIndex[personUri].row();
327 int contactRow = metacontacts[personRow].updateContact(contactUri, contact);
329 const QModelIndex contactIndex = q->index(contactRow,
331 q->index(personRow));
333 Q_EMIT q->dataChanged(contactIndex, contactIndex);
335 personChanged(personUri);
338 void PersonsModelPrivate::onContactRemoved(
const QString &contactUri)
340 const QString &personUri = personUriForContact(contactUri);
342 int personRow = personIndex[personUri].row();
344 MetaContact &mc = metacontacts[personRow];
345 int contactPosition = mc.contactUris().indexOf(contactUri);
346 q->beginRemoveRows(q->index(personRow, 0), contactPosition, contactPosition);
347 mc.removeContact(contactUri);
352 removePerson(personUri);
354 personChanged(personUri);
357 void PersonsModelPrivate::onAddContactToPerson(
const QString &contactUri,
const QString &newPersonUri)
359 const QString oldPersonUri = personUriForContact(contactUri);
361 contactToPersons.insert(contactUri, newPersonUri);
363 int oldPersonRow = personIndex[oldPersonUri].row();
365 if (oldPersonRow < 0) {
369 MetaContact &oldMc = metacontacts[oldPersonRow];
372 int contactPosition = oldMc.contactUris().indexOf(contactUri);
373 const AbstractContact::Ptr contact = oldMc.contacts().at(contactPosition);
375 q->beginRemoveRows(q->index(oldPersonRow), contactPosition, contactPosition);
376 oldMc.removeContact(contactUri);
379 if (!oldMc.isValid()) {
380 removePerson(oldPersonUri);
382 personChanged(oldPersonUri);
386 if (personIndex.contains(newPersonUri)) {
387 int newPersonRow = personIndex[newPersonUri].row();
388 MetaContact &newMc = metacontacts[newPersonRow];
389 int newContactPos = newMc.contacts().size();
390 q->beginInsertRows(q->index(newPersonRow), newContactPos, newContactPos);
391 newMc.insertContact(contactUri, contact);
393 personChanged(newPersonUri);
395 QMap<QString, AbstractContact::Ptr> contacts;
396 contacts[contactUri] = contact;
397 addPerson(MetaContact(newPersonUri, contacts));
401 void PersonsModelPrivate::onRemoveContactsFromPerson(
const QString &contactUri)
403 const QString personUri = personUriForContact(contactUri);
404 int personRow = personIndex[personUri].row();
405 MetaContact &mc = metacontacts[personRow];
407 const AbstractContact::Ptr &contact = mc.contact(contactUri);
408 mc.removeContact(contactUri);
409 contactToPersons.remove(contactUri);
413 removePerson(personUri);
415 personChanged(personUri);
420 addPerson(MetaContact(contactUri, contact));
423 void PersonsModelPrivate::addPerson(
const KPeople::MetaContact &mc)
425 const QString &
id = mc.id();
427 int row = metacontacts.size();
428 q->beginInsertRows(QModelIndex(), row, row);
429 metacontacts.append(mc);
430 personIndex[id] = q->index(row);
434 void PersonsModelPrivate::removePerson(
const QString &
id)
436 QPersistentModelIndex index = personIndex.value(
id);
437 if (!index.isValid()) {
441 q->beginRemoveRows(QModelIndex(), index.row(), index.row());
442 personIndex.remove(
id);
443 metacontacts.removeAt(index.row());
447 void PersonsModelPrivate::personChanged(
const QString &personUri)
449 int row = personIndex[personUri].row();
451 const QModelIndex personIndex = q->index(row);
452 Q_EMIT q->dataChanged(personIndex, personIndex);
456 QString PersonsModelPrivate::personUriForContact(
const QString &contactUri)
const
458 QHash<QString, QString>::const_iterator it = contactToPersons.constFind(contactUri);
459 if (it != contactToPersons.constEnd()) {
469 return d->personIndex.value(personUri);
474 return index(row, 0).data(role);
480 if (index.parent().isValid()) {
481 const MetaContact &mc = d->metacontacts.at(index.parent().row());
483 return mc.contacts().at(index.row())->customProperty(key);
485 const MetaContact &mc = d->metacontacts.at(index.row());
486 return mc.personAddressee()->customProperty(key);
490 #include "personsmodel.moc"
The KPeople namespace contains all the classes for Libkpeople.
bool isInitialized() const
Returns if all the backends have been initialized yet.
QModelIndex indexForPersonUri(const QString &personUri) const
This class creates a model of all known contacts from all sources Contacts are represented as a tree ...
Q_SCRIPTABLE QVariant get(int row, int role)
Helper class to ease model access through QML.
QVariant contactCustomProperty(const QModelIndex &index, const QString &key) const
Makes it possible to access custom properties that are not available to the model.