File indexing completed on 2024-11-24 04:50:42
0001 // SPDX-FileCopyrightText: 2009 Tobias Koenig <tokoe@kde.org> 0002 // SPDX-FileCopyrightText: 2022 Carl Schwan <carl@carlschwan.eu> 0003 // SPDX-License-Identifier: LGPL-2.0-or-later 0004 0005 #include "contactgroupmodel.h" 0006 0007 #include <Akonadi/ItemFetchJob> 0008 #include <Akonadi/ItemFetchScope> 0009 #include <KContacts/Addressee> 0010 0011 #include <KLocalizedString> 0012 0013 using namespace Akonadi; 0014 0015 struct GroupMember { 0016 KContacts::ContactGroup::ContactReference reference; 0017 KContacts::ContactGroup::Data data; 0018 KContacts::Addressee referencedContact; 0019 bool isReference = false; 0020 bool loadingError = false; 0021 }; 0022 0023 class ContactGroupModelPrivate 0024 { 0025 public: 0026 explicit ContactGroupModelPrivate(ContactGroupModel *parent) 0027 : mParent(parent) 0028 { 0029 } 0030 0031 void resolveContactReference(const KContacts::ContactGroup::ContactReference &reference, int row, const QString &preferredEmail = {}) 0032 { 0033 Item item; 0034 if (!reference.gid().isEmpty()) { 0035 item.setGid(reference.gid()); 0036 } else { 0037 item.setId(reference.uid().toLongLong()); 0038 } 0039 auto job = new ItemFetchJob(item, mParent); 0040 job->setProperty("row", row); 0041 job->fetchScope().fetchFullPayload(); 0042 0043 mParent->connect(job, &ItemFetchJob::result, mParent, [this, preferredEmail](KJob *job) { 0044 itemFetched(job, preferredEmail); 0045 }); 0046 } 0047 0048 void itemFetched(KJob *job, const QString &preferredEmail) 0049 { 0050 const int row = job->property("row").toInt(); 0051 0052 if (job->error()) { 0053 mMembers[row].loadingError = true; 0054 Q_EMIT mParent->dataChanged(mParent->index(row, 0, {}), mParent->index(row, 0, {})); 0055 return; 0056 } 0057 0058 auto fetchJob = qobject_cast<ItemFetchJob *>(job); 0059 0060 if (fetchJob->items().count() != 1) { 0061 mMembers[row].loadingError = true; 0062 Q_EMIT mParent->dataChanged(mParent->index(row, 0, {}), mParent->index(row, 0, {})); 0063 return; 0064 } 0065 0066 const Item item = fetchJob->items().at(0); 0067 const auto contact = item.payload<KContacts::Addressee>(); 0068 0069 GroupMember &member = mMembers[row]; 0070 member.referencedContact = contact; 0071 if (!preferredEmail.isEmpty() && contact.preferredEmail() != preferredEmail) { 0072 // we are using a special email address 0073 member.reference.setPreferredEmail(preferredEmail); 0074 } 0075 Q_EMIT mParent->dataChanged(mParent->index(row, 0, {}), mParent->index(row, 0, {})); 0076 } 0077 0078 void normalizeMemberList() 0079 { 0080 // check whether a normalization is needed or not 0081 bool needsNormalization = false; 0082 if (mMembers.isEmpty()) { 0083 needsNormalization = true; 0084 } else { 0085 for (int i = 0; i < mMembers.count(); ++i) { 0086 const GroupMember &member = mMembers[i]; 0087 if (!member.isReference && !(i == mMembers.count() - 1)) { 0088 if (member.data.name().isEmpty() && member.data.email().isEmpty()) { 0089 needsNormalization = true; 0090 break; 0091 } 0092 } 0093 } 0094 0095 const GroupMember &member = mMembers.last(); 0096 if (member.isReference || !(member.data.name().isEmpty() && member.data.email().isEmpty())) { 0097 needsNormalization = true; 0098 } 0099 } 0100 0101 // if not, avoid to update the model and view 0102 if (!needsNormalization) { 0103 return; 0104 } 0105 0106 bool foundEmpty = false; 0107 0108 // remove all empty lines first except the last line 0109 do { 0110 foundEmpty = false; 0111 for (int i = 0; i < mMembers.count(); ++i) { 0112 const GroupMember &member = mMembers[i]; 0113 if (!member.isReference && !(i == mMembers.count() - 1)) { 0114 if (member.data.name().isEmpty() && member.data.email().isEmpty()) { 0115 mParent->beginRemoveRows(QModelIndex(), i, i); 0116 mMembers.remove(i); 0117 mParent->endRemoveRows(); 0118 foundEmpty = true; 0119 break; 0120 } 0121 } 0122 } 0123 } while (foundEmpty); 0124 } 0125 0126 ContactGroupModel *const mParent; 0127 QList<GroupMember> mMembers; 0128 KContacts::ContactGroup mGroup; 0129 QString mLastErrorMessage; 0130 bool mIsEditing; 0131 }; 0132 0133 ContactGroupModel::ContactGroupModel(bool isEditing, QObject *parent) 0134 : QAbstractListModel(parent) 0135 , d(new ContactGroupModelPrivate(this)) 0136 { 0137 d->mIsEditing = isEditing; 0138 } 0139 0140 ContactGroupModel::~ContactGroupModel() = default; 0141 0142 void ContactGroupModel::loadContactGroup(const KContacts::ContactGroup &contactGroup) 0143 { 0144 beginResetModel(); 0145 0146 d->mMembers.clear(); 0147 d->mGroup = contactGroup; 0148 0149 for (int i = 0; i < d->mGroup.dataCount(); ++i) { 0150 const KContacts::ContactGroup::Data data = d->mGroup.data(i); 0151 GroupMember member; 0152 member.isReference = false; 0153 member.data = data; 0154 0155 d->mMembers.append(member); 0156 } 0157 0158 for (int i = 0; i < d->mGroup.contactReferenceCount(); ++i) { 0159 const KContacts::ContactGroup::ContactReference reference = d->mGroup.contactReference(i); 0160 GroupMember member; 0161 member.isReference = true; 0162 member.reference = reference; 0163 0164 d->mMembers.append(member); 0165 0166 d->resolveContactReference(reference, d->mMembers.count() - 1); 0167 } 0168 0169 d->normalizeMemberList(); 0170 0171 endResetModel(); 0172 } 0173 0174 bool ContactGroupModel::storeContactGroup(KContacts::ContactGroup &group) const 0175 { 0176 group.removeAllContactReferences(); 0177 group.removeAllContactData(); 0178 0179 for (int i = 0; i < d->mMembers.count(); ++i) { 0180 const GroupMember &member = d->mMembers[i]; 0181 if (member.isReference) { 0182 group.append(member.reference); 0183 } else { 0184 if (member.data.email().isEmpty()) { 0185 d->mLastErrorMessage = i18n("The member with name <b>%1</b> is missing an email address", member.data.name()); 0186 return false; 0187 } 0188 group.append(member.data); 0189 } 0190 } 0191 0192 return true; 0193 } 0194 0195 QString ContactGroupModel::lastErrorMessage() const 0196 { 0197 return d->mLastErrorMessage; 0198 } 0199 0200 QModelIndex ContactGroupModel::index(int row, int col, const QModelIndex &index) const 0201 { 0202 Q_UNUSED(index) 0203 return createIndex(row, col); 0204 } 0205 0206 QModelIndex ContactGroupModel::parent(const QModelIndex &index) const 0207 { 0208 Q_UNUSED(index) 0209 return {}; 0210 } 0211 0212 QVariant ContactGroupModel::data(const QModelIndex &index, int role) const 0213 { 0214 if (!index.isValid()) { 0215 return {}; 0216 } 0217 0218 if (index.row() < 0 || index.row() >= d->mMembers.count()) { 0219 return {}; 0220 } 0221 0222 const GroupMember &member = d->mMembers[index.row()]; 0223 0224 switch (role) { 0225 case Qt::DisplayRole: 0226 case DisplayNameRole: 0227 if (member.loadingError) { 0228 return i18n("Contact does not exist any more"); 0229 } 0230 if (member.isReference) { 0231 return member.referencedContact.realName(); 0232 } 0233 return member.data.name(); 0234 0235 case EmailRole: 0236 if (member.loadingError) { 0237 return QString(); 0238 } 0239 if (member.isReference) { 0240 if (!member.reference.preferredEmail().isEmpty()) { 0241 return member.reference.preferredEmail(); 0242 } else { 0243 return member.referencedContact.preferredEmail(); 0244 } 0245 } 0246 0247 return member.data.email(); 0248 0249 case IconNameRole: 0250 if (member.loadingError) { 0251 return QStringLiteral("emblem-important"); 0252 } 0253 return {}; 0254 case IsReferenceRole: 0255 return member.isReference; 0256 0257 case AllEmailsRole: 0258 if (member.isReference) { 0259 return member.referencedContact.emails(); 0260 } else { 0261 return QStringList(); 0262 } 0263 } 0264 0265 return {}; 0266 } 0267 0268 QHash<int, QByteArray> ContactGroupModel::roleNames() const 0269 { 0270 return { 0271 {Qt::DisplayRole, QByteArray("display")}, 0272 {EmailRole, QByteArray("email")}, 0273 {DisplayNameRole, QByteArray("displayName")}, 0274 {IconNameRole, QByteArray("iconName")}, 0275 }; 0276 } 0277 0278 bool ContactGroupModel::setData(const QModelIndex &index, const QVariant &value, int role) 0279 { 0280 if (!index.isValid()) { 0281 return false; 0282 } 0283 0284 if (index.row() < 0 || index.row() >= d->mMembers.count()) { 0285 return false; 0286 } 0287 0288 GroupMember &member = d->mMembers[index.row()]; 0289 0290 if (role == Qt::EditRole) { 0291 if (member.isReference) { 0292 if (index.column() == 0) { 0293 member.reference.setUid(QString::number(value.toLongLong())); 0294 d->resolveContactReference(member.reference, index.row()); 0295 } 0296 if (index.column() == 1) { 0297 const QString email = value.toString(); 0298 if (email != member.referencedContact.preferredEmail()) { 0299 member.reference.setPreferredEmail(email); 0300 } else { 0301 member.reference.setPreferredEmail(QString()); 0302 } 0303 } 0304 } else { 0305 if (index.column() == 0) { 0306 member.data.setName(value.toString()); 0307 } else { 0308 member.data.setEmail(value.toString()); 0309 } 0310 } 0311 0312 d->normalizeMemberList(); 0313 0314 return true; 0315 } 0316 0317 if (role == IsReferenceRole) { 0318 if ((value.toBool() == true) && !member.isReference) { 0319 member.isReference = true; 0320 } 0321 if ((value.toBool() == false) && member.isReference) { 0322 member.isReference = false; 0323 member.data.setName(member.referencedContact.realName()); 0324 member.data.setEmail(member.referencedContact.preferredEmail()); 0325 } 0326 0327 return true; 0328 } 0329 0330 return false; 0331 } 0332 0333 QVariant ContactGroupModel::headerData(int section, Qt::Orientation orientation, int role) const 0334 { 0335 if (section < 0 || section > 1) { 0336 return {}; 0337 } 0338 0339 if (orientation != Qt::Horizontal) { 0340 return {}; 0341 } 0342 0343 if (role != Qt::DisplayRole) { 0344 return {}; 0345 } 0346 0347 if (section == 0) { 0348 return i18nc("contact's name", "Name"); 0349 } else { 0350 return i18nc("contact's email address", "EMail"); 0351 } 0352 } 0353 0354 Qt::ItemFlags ContactGroupModel::flags(const QModelIndex &index) const 0355 { 0356 if (!index.isValid() || index.row() < 0 || index.row() >= d->mMembers.count()) { 0357 return Qt::ItemIsEnabled; 0358 } 0359 0360 if (d->mMembers[index.row()].loadingError) { 0361 return {Qt::ItemIsEnabled}; 0362 } 0363 0364 Qt::ItemFlags parentFlags = QAbstractItemModel::flags(index); 0365 return parentFlags | Qt::ItemIsEnabled | Qt::ItemIsEditable; 0366 } 0367 0368 int ContactGroupModel::rowCount(const QModelIndex &parent) const 0369 { 0370 if (!parent.isValid()) { 0371 return d->mMembers.count(); 0372 } else { 0373 return 0; 0374 } 0375 } 0376 0377 void ContactGroupModel::removeContact(int row) 0378 { 0379 beginRemoveRows({}, row, row); 0380 d->mMembers.remove(row); 0381 endRemoveRows(); 0382 } 0383 0384 void ContactGroupModel::addContactFromReference(const QString &uid, const QString &email) 0385 { 0386 GroupMember member; 0387 member.isReference = true; 0388 member.reference.setUid(uid); 0389 beginInsertRows({}, d->mMembers.count(), d->mMembers.count()); 0390 d->mMembers.append(member); 0391 endInsertRows(); 0392 0393 d->resolveContactReference(member.reference, d->mMembers.count() - 1, email); 0394 } 0395 0396 void ContactGroupModel::addContactFromData(const QString &name, const QString &email) 0397 { 0398 GroupMember member; 0399 member.isReference = false; 0400 member.data.setName(name); 0401 member.data.setEmail(email); 0402 beginInsertRows({}, d->mMembers.count(), d->mMembers.count()); 0403 d->mMembers.append(member); 0404 endInsertRows(); 0405 } 0406 0407 #include "moc_contactgroupmodel.cpp"