Akonadi Contacts

contactgroupmodel.cpp
1 /*
2  This file is part of Akonadi Contact.
3 
4  SPDX-FileCopyrightText: 2009 Tobias Koenig <[email protected]>
5 
6  SPDX-License-Identifier: LGPL-2.0-or-later
7 */
8 
9 #include "contactgroupmodel_p.h"
10 
11 #include <itemfetchjob.h>
12 #include <itemfetchscope.h>
13 #include <kcontacts/addressee.h>
14 
15 #include <KIconEngine>
16 #include <KIconLoader>
17 #include <KLocalizedString>
18 #include <QIcon>
19 
20 using namespace Akonadi;
21 
22 struct GroupMember {
25  KContacts::Addressee referencedContact;
26  bool isReference = false;
27  bool loadingError = false;
28 };
29 
30 class Q_DECL_HIDDEN ContactGroupModel::Private
31 {
32 public:
33  Private(ContactGroupModel *parent)
34  : mParent(parent)
35  {
36  }
37 
38  void resolveContactReference(const KContacts::ContactGroup::ContactReference &reference, int row)
39  {
40  Item item;
41  if (!reference.gid().isEmpty()) {
42  item.setGid(reference.gid());
43  } else {
44  item.setId(reference.uid().toLongLong());
45  }
46  auto job = new ItemFetchJob(item, mParent);
47  job->setProperty("row", row);
48  job->fetchScope().fetchFullPayload();
49 
50  mParent->connect(job, &ItemFetchJob::result, mParent, [this](KJob *job) {
51  itemFetched(job);
52  });
53  }
54 
55  void itemFetched(KJob *job)
56  {
57  const int row = job->property("row").toInt();
58 
59  if (job->error()) {
60  mMembers[row].loadingError = true;
61  Q_EMIT mParent->dataChanged(mParent->index(row, 0, QModelIndex()), mParent->index(row, 1, QModelIndex()));
62  return;
63  }
64 
65  auto fetchJob = qobject_cast<ItemFetchJob *>(job);
66 
67  if (fetchJob->items().count() != 1) {
68  mMembers[row].loadingError = true;
69  Q_EMIT mParent->dataChanged(mParent->index(row, 0, QModelIndex()), mParent->index(row, 1, QModelIndex()));
70  return;
71  }
72 
73  const Item item = fetchJob->items().at(0);
74  const auto contact = item.payload<KContacts::Addressee>();
75 
76  GroupMember &member = mMembers[row];
77  member.referencedContact = contact;
78  Q_EMIT mParent->dataChanged(mParent->index(row, 0, QModelIndex()), mParent->index(row, 1, QModelIndex()));
79  }
80 
81  void normalizeMemberList()
82  {
83  // check whether a normalization is needed or not
84  bool needsNormalization = false;
85  if (mMembers.isEmpty()) {
86  needsNormalization = true;
87  } else {
88  for (int i = 0; i < mMembers.count(); ++i) {
89  const GroupMember &member = mMembers[i];
90  if (!member.isReference && !(i == mMembers.count() - 1)) {
91  if (member.data.name().isEmpty() && member.data.email().isEmpty()) {
92  needsNormalization = true;
93  break;
94  }
95  }
96  }
97 
98  const GroupMember &member = mMembers.last();
99  if (member.isReference || !(member.data.name().isEmpty() && member.data.email().isEmpty())) {
100  needsNormalization = true;
101  }
102  }
103 
104  // if not, avoid to update the model and view
105  if (!needsNormalization) {
106  return;
107  }
108 
109  bool foundEmpty = false;
110 
111  // add an empty line at the end
112  mParent->beginInsertRows(QModelIndex(), mMembers.count(), mMembers.count());
113  GroupMember member;
114  member.isReference = false;
115  mMembers.append(member);
116  mParent->endInsertRows();
117 
118  // remove all empty lines first except the last line
119  do {
120  foundEmpty = false;
121  for (int i = 0; i < mMembers.count(); ++i) {
122  const GroupMember &member = mMembers[i];
123  if (!member.isReference && !(i == mMembers.count() - 1)) {
124  if (member.data.name().isEmpty() && member.data.email().isEmpty()) {
125  mParent->beginRemoveRows(QModelIndex(), i, i);
126  mMembers.remove(i);
127  mParent->endRemoveRows();
128  foundEmpty = true;
129  break;
130  }
131  }
132  }
133  } while (foundEmpty);
134  }
135 
136  ContactGroupModel *mParent = nullptr;
137  QVector<GroupMember> mMembers;
139  QString mLastErrorMessage;
140 };
141 
142 ContactGroupModel::ContactGroupModel(QObject *parent)
143  : QAbstractItemModel(parent)
144  , d(new Private(this))
145 {
146 }
147 
148 ContactGroupModel::~ContactGroupModel()
149 {
150  delete d;
151 }
152 
153 void ContactGroupModel::loadContactGroup(const KContacts::ContactGroup &contactGroup)
154 {
155  Q_EMIT layoutAboutToBeChanged();
156 
157  d->mMembers.clear();
158  d->mGroup = contactGroup;
159 
160  for (int i = 0; i < d->mGroup.dataCount(); ++i) {
161  const KContacts::ContactGroup::Data data = d->mGroup.data(i);
162  GroupMember member;
163  member.isReference = false;
164  member.data = data;
165 
166  d->mMembers.append(member);
167  }
168 
169  for (int i = 0; i < d->mGroup.contactReferenceCount(); ++i) {
170  const KContacts::ContactGroup::ContactReference reference = d->mGroup.contactReference(i);
171  GroupMember member;
172  member.isReference = true;
173  member.reference = reference;
174 
175  d->mMembers.append(member);
176 
177  d->resolveContactReference(reference, d->mMembers.count() - 1);
178  }
179 
180  d->normalizeMemberList();
181 
182  Q_EMIT layoutChanged();
183 }
184 
185 bool ContactGroupModel::storeContactGroup(KContacts::ContactGroup &group) const
186 {
188  group.removeAllContactData();
189 
190  for (int i = 0; i < d->mMembers.count(); ++i) {
191  const GroupMember &member = d->mMembers[i];
192  if (member.isReference) {
193  group.append(member.reference);
194  } else {
195  if (i != (d->mMembers.count() - 1)) {
196  if (member.data.email().isEmpty()) {
197  d->mLastErrorMessage = i18n("The member with name <b>%1</b> is missing an email address", member.data.name());
198  return false;
199  }
200  group.append(member.data);
201  }
202  }
203  }
204 
205  return true;
206 }
207 
208 QString ContactGroupModel::lastErrorMessage() const
209 {
210  return d->mLastErrorMessage;
211 }
212 
213 QModelIndex ContactGroupModel::index(int row, int col, const QModelIndex &index) const
214 {
215  Q_UNUSED(index)
216  return createIndex(row, col);
217 }
218 
219 QModelIndex ContactGroupModel::parent(const QModelIndex &index) const
220 {
221  Q_UNUSED(index)
222  return QModelIndex();
223 }
224 
225 QVariant ContactGroupModel::data(const QModelIndex &index, int role) const
226 {
227  if (!index.isValid()) {
228  return QVariant();
229  }
230 
231  if (index.row() < 0 || index.row() >= d->mMembers.count()) {
232  return QVariant();
233  }
234 
235  if (index.column() < 0 || index.column() > 1) {
236  return QVariant();
237  }
238 
239  const GroupMember &member = d->mMembers[index.row()];
240 
241  if (role == Qt::DisplayRole) {
242  if (member.loadingError) {
243  if (index.column() == 0) {
244  return i18n("Contact does not exist any more");
245  } else {
246  return QString();
247  }
248  }
249 
250  if (member.isReference) {
251  if (index.column() == 0) {
252  return member.referencedContact.realName();
253  } else {
254  if (!member.reference.preferredEmail().isEmpty()) {
255  return member.reference.preferredEmail();
256  } else {
257  return member.referencedContact.preferredEmail();
258  }
259  }
260  } else {
261  if (index.column() == 0) {
262  return member.data.name();
263  } else {
264  return member.data.email();
265  }
266  }
267  }
268 
269  if (role == Qt::DecorationRole) {
270  if (index.column() == 1) {
271  return QVariant();
272  }
273 
274  if (member.loadingError) {
275  return QIcon::fromTheme(QStringLiteral("emblem-important"));
276  }
277 
278  if (index.row() == (d->mMembers.count() - 1)) {
279  return QIcon::fromTheme(QStringLiteral("contact-new"));
280  }
281 
282  if (member.isReference) {
283  return QIcon(new KIconEngine(QStringLiteral("x-office-contact"), KIconLoader::global(), QStringList() << QStringLiteral("emblem-symbolic-link")));
284  } else {
285  return QIcon::fromTheme(QStringLiteral("x-office-contact"));
286  }
287  }
288 
289  if (role == Qt::EditRole) {
290  if (member.isReference) {
291  if (index.column() == 0) {
292  return member.referencedContact.realName();
293  } else {
294  if (!member.reference.preferredEmail().isEmpty()) {
295  return member.reference.preferredEmail();
296  } else {
297  return member.referencedContact.preferredEmail();
298  }
299  }
300  } else {
301  if (index.column() == 0) {
302  return member.data.name();
303  } else {
304  return member.data.email();
305  }
306  }
307  }
308 
309  if (role == IsReferenceRole) {
310  return member.isReference;
311  }
312 
313  if (role == AllEmailsRole) {
314  if (member.isReference) {
315  return member.referencedContact.emails();
316  } else {
317  return QStringList();
318  }
319  }
320 
321  return QVariant();
322 }
323 
324 bool ContactGroupModel::setData(const QModelIndex &index, const QVariant &value, int role)
325 {
326  if (!index.isValid()) {
327  return false;
328  }
329 
330  if (index.row() < 0 || index.row() >= d->mMembers.count()) {
331  return false;
332  }
333 
334  if (index.column() < 0 || index.column() > 1) {
335  return false;
336  }
337 
338  GroupMember &member = d->mMembers[index.row()];
339 
340  if (role == Qt::EditRole) {
341  if (member.isReference) {
342  if (index.column() == 0) {
343  member.reference.setUid(QString::number(value.toLongLong()));
344  d->resolveContactReference(member.reference, index.row());
345  }
346  if (index.column() == 1) {
347  const QString email = value.toString();
348  if (email != member.referencedContact.preferredEmail()) {
349  member.reference.setPreferredEmail(email);
350  } else {
351  member.reference.setPreferredEmail(QString());
352  }
353  }
354  } else {
355  if (index.column() == 0) {
356  member.data.setName(value.toString());
357  } else {
358  member.data.setEmail(value.toString());
359  }
360  }
361 
362  d->normalizeMemberList();
363 
364  return true;
365  }
366 
367  if (role == IsReferenceRole) {
368  if ((value.toBool() == true) && !member.isReference) {
369  member.isReference = true;
370  }
371  if ((value.toBool() == false) && member.isReference) {
372  member.isReference = false;
373  member.data.setName(member.referencedContact.realName());
374  member.data.setEmail(member.referencedContact.preferredEmail());
375  }
376 
377  return true;
378  }
379 
380  return false;
381 }
382 
383 QVariant ContactGroupModel::headerData(int section, Qt::Orientation orientation, int role) const
384 {
385  if (section < 0 || section > 1) {
386  return QVariant();
387  }
388 
389  if (orientation != Qt::Horizontal) {
390  return QVariant();
391  }
392 
393  if (role != Qt::DisplayRole) {
394  return QVariant();
395  }
396 
397  if (section == 0) {
398  return i18nc("contact's name", "Name");
399  } else {
400  return i18nc("contact's email address", "EMail");
401  }
402 }
403 
404 Qt::ItemFlags ContactGroupModel::flags(const QModelIndex &index) const
405 {
406  if (!index.isValid() || index.row() < 0 || index.row() >= d->mMembers.count()) {
407  return Qt::ItemIsEnabled;
408  }
409 
410  if (d->mMembers[index.row()].loadingError) {
412  }
413 
414  Qt::ItemFlags parentFlags = QAbstractItemModel::flags(index);
415  return parentFlags | Qt::ItemIsEnabled | Qt::ItemIsEditable;
416 }
417 
418 int ContactGroupModel::columnCount(const QModelIndex &parent) const
419 {
420  if (!parent.isValid()) {
421  return 2;
422  } else {
423  return 0;
424  }
425 }
426 
427 int ContactGroupModel::rowCount(const QModelIndex &parent) const
428 {
429  if (!parent.isValid()) {
430  return d->mMembers.count();
431  } else {
432  return 0;
433  }
434 }
435 
436 bool ContactGroupModel::removeRows(int row, int count, const QModelIndex &parent)
437 {
438  if (parent.isValid()) {
439  return false;
440  }
441 
442  beginRemoveRows(QModelIndex(), row, row + count - 1);
443  for (int i = 0; i < count; ++i) {
444  d->mMembers.remove(row);
445  }
446  endRemoveRows();
447 
448  return true;
449 }
450 
451 #include "moc_contactgroupmodel_p.cpp"
qlonglong toLongLong(bool *ok) const const
void setId(Id identifier)
T payload() const
void setGid(const QString &gid)
bool isValid() const const
QString number(int n, int base)
QVariant property(const char *name) const const
int toInt(bool *ok) const const
QString i18nc(const char *context, const char *text, const TYPE &arg...)
DisplayRole
bool isEmpty() const const
int row() const const
static KIconLoader * global()
QString i18n(const char *text, const TYPE &arg...)
int column() const const
bool toBool() const const
virtual Qt::ItemFlags flags(const QModelIndex &index) const const
QIcon fromTheme(const QString &name)
void append(const ContactReference &reference)
Orientation
void result(KJob *job)
QObject * parent() const const
QString toString() const const
qlonglong toLongLong(bool *ok, int base) const const
Q_EMITQ_EMIT
int error() const
typedef ItemFlags
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Wed Jun 23 2021 23:09:24 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.