File indexing completed on 2024-11-17 04:40:40
0001 /* 0002 This file is part of Akonadi Contact. 0003 0004 SPDX-FileCopyrightText: 2007-2009 Tobias Koenig <tokoe@kde.org> 0005 0006 SPDX-License-Identifier: LGPL-2.0-or-later 0007 */ 0008 0009 #include "contactgroupeditor.h" 0010 #include "contactgroupeditor_p.h" 0011 #include <KStatefulBrush> 0012 0013 #include "contactgroupeditordelegate_p.h" 0014 #include "contactgroupmodel_p.h" 0015 #include "waitingoverlay_p.h" 0016 0017 #include <Akonadi/CollectionDialog> 0018 #include <Akonadi/CollectionFetchJob> 0019 #include <Akonadi/ItemCreateJob> 0020 #include <Akonadi/ItemFetchJob> 0021 #include <Akonadi/ItemFetchScope> 0022 #include <Akonadi/ItemModifyJob> 0023 #include <Akonadi/Monitor> 0024 #include <Akonadi/Session> 0025 #include <KColorScheme> 0026 #include <KContacts/ContactGroup> 0027 #include <KLocalizedString> 0028 #include <KMessageBox> 0029 0030 #include <QMessageBox> 0031 #include <QTimer> 0032 0033 using namespace Akonadi; 0034 using namespace Akonadi; 0035 0036 ContactGroupEditorPrivate::ContactGroupEditorPrivate(ContactGroupEditor *parent) 0037 : mParent(parent) 0038 { 0039 } 0040 0041 ContactGroupEditorPrivate::~ContactGroupEditorPrivate() 0042 { 0043 delete mMonitor; 0044 } 0045 0046 void ContactGroupEditorPrivate::adaptHeaderSizes() 0047 { 0048 mGui.membersView->header()->setDefaultSectionSize(mGui.membersView->header()->width() / 2); 0049 mGui.membersView->header()->resizeSections(QHeaderView::Interactive); 0050 } 0051 0052 void ContactGroupEditorPrivate::itemFetchDone(KJob *job) 0053 { 0054 if (job->error()) { 0055 return; 0056 } 0057 0058 auto fetchJob = qobject_cast<ItemFetchJob *>(job); 0059 if (!fetchJob) { 0060 return; 0061 } 0062 0063 if (fetchJob->items().isEmpty()) { 0064 return; 0065 } 0066 0067 mItem = fetchJob->items().at(0); 0068 0069 mReadOnly = false; 0070 if (mMode == ContactGroupEditor::EditMode) { 0071 // if in edit mode we have to fetch the parent collection to find out 0072 // about the modify rights of the item 0073 0074 auto collectionFetchJob = new Akonadi::CollectionFetchJob(mItem.parentCollection(), Akonadi::CollectionFetchJob::Base); 0075 mParent->connect(collectionFetchJob, &CollectionFetchJob::result, mParent, [this](KJob *job) { 0076 parentCollectionFetchDone(job); 0077 }); 0078 } else { 0079 const auto group = mItem.payload<KContacts::ContactGroup>(); 0080 loadContactGroup(group); 0081 0082 setReadOnly(mReadOnly); 0083 0084 QTimer::singleShot(0, mParent, [this]() { 0085 adaptHeaderSizes(); 0086 }); 0087 } 0088 } 0089 0090 void ContactGroupEditorPrivate::parentCollectionFetchDone(KJob *job) 0091 { 0092 if (job->error()) { 0093 return; 0094 } 0095 0096 auto fetchJob = qobject_cast<Akonadi::CollectionFetchJob *>(job); 0097 if (!fetchJob) { 0098 return; 0099 } 0100 0101 const Akonadi::Collection parentCollection = fetchJob->collections().at(0); 0102 if (parentCollection.isValid()) { 0103 mReadOnly = !(parentCollection.rights() & Collection::CanChangeItem); 0104 } 0105 0106 const auto group = mItem.payload<KContacts::ContactGroup>(); 0107 loadContactGroup(group); 0108 0109 setReadOnly(mReadOnly); 0110 0111 QTimer::singleShot(0, mParent, [this]() { 0112 adaptHeaderSizes(); 0113 }); 0114 } 0115 0116 void ContactGroupEditorPrivate::storeDone(KJob *job) 0117 { 0118 if (job->error()) { 0119 Q_EMIT mParent->error(job->errorString()); 0120 return; 0121 } 0122 0123 if (mMode == ContactGroupEditor::EditMode) { 0124 Q_EMIT mParent->contactGroupStored(mItem); 0125 } else if (mMode == ContactGroupEditor::CreateMode) { 0126 Q_EMIT mParent->contactGroupStored(static_cast<ItemCreateJob *>(job)->item()); 0127 } 0128 } 0129 0130 void ContactGroupEditorPrivate::itemChanged(const Item &item, const QSet<QByteArray> &) 0131 { 0132 Q_UNUSED(item) 0133 QPointer<QMessageBox> dlg = new QMessageBox(mParent); // krazy:exclude=qclasses 0134 0135 dlg->setInformativeText(i18n("The contact group has been changed by someone else.\nWhat should be done?")); 0136 dlg->addButton(i18n("Take over changes"), QMessageBox::AcceptRole); 0137 dlg->addButton(i18n("Ignore and Overwrite changes"), QMessageBox::RejectRole); 0138 0139 if (dlg->exec() == QMessageBox::AcceptRole) { 0140 auto job = new ItemFetchJob(mItem); 0141 job->fetchScope().fetchFullPayload(); 0142 job->fetchScope().setAncestorRetrieval(Akonadi::ItemFetchScope::Parent); 0143 0144 mParent->connect(job, &ItemFetchJob::result, mParent, [this](KJob *job) { 0145 itemFetchDone(job); 0146 }); 0147 new WaitingOverlay(job, mParent); 0148 } else { 0149 // Still update the item so that the internal revision match 0150 mItem = item; 0151 } 0152 delete dlg; 0153 } 0154 0155 void ContactGroupEditorPrivate::loadContactGroup(const KContacts::ContactGroup &group) 0156 { 0157 mGui.membersView->setSortingEnabled(false); 0158 mGui.groupName->setText(group.name()); 0159 0160 mGroupModel->loadContactGroup(group); 0161 0162 const QAbstractItemModel *model = mGui.membersView->model(); 0163 mGui.membersView->setCurrentIndex(model->index(model->rowCount() - 1, 0)); 0164 0165 if (mMode == ContactGroupEditor::EditMode) { 0166 mGui.membersView->setFocus(); 0167 } 0168 0169 mGui.membersView->header()->resizeSections(QHeaderView::Stretch); 0170 mGui.membersView->setSortingEnabled(true); 0171 } 0172 0173 bool ContactGroupEditorPrivate::storeContactGroup(KContacts::ContactGroup &group) 0174 { 0175 if (mGui.groupName->text().isEmpty()) { 0176 KMessageBox::error(mParent, i18n("The name of the contact group must not be empty.")); 0177 return false; 0178 } 0179 0180 group.setName(mGui.groupName->text()); 0181 0182 if (!mGroupModel->storeContactGroup(group)) { 0183 KMessageBox::error(mParent, mGroupModel->lastErrorMessage()); 0184 return false; 0185 } 0186 0187 return true; 0188 } 0189 0190 void ContactGroupEditorPrivate::setupMonitor() 0191 { 0192 delete mMonitor; 0193 mMonitor = new Monitor; 0194 mMonitor->setObjectName(QLatin1StringView("ContactGroupEditorMonitor")); 0195 mMonitor->ignoreSession(Session::defaultSession()); 0196 0197 QObject::connect(mMonitor, &Monitor::itemChanged, mParent, [this](const Akonadi::Item &item, const QSet<QByteArray> &arrays) { 0198 itemChanged(item, arrays); 0199 }); 0200 } 0201 0202 void ContactGroupEditorPrivate::setReadOnly(bool readOnly) 0203 { 0204 mGui.groupName->setReadOnly(readOnly); 0205 mGui.membersView->setEnabled(!readOnly); 0206 } 0207 0208 ContactGroupEditor::ContactGroupEditor(Mode mode, QWidget *parent) 0209 : QWidget(parent) 0210 , d(new ContactGroupEditorPrivate(this)) 0211 { 0212 d->mMode = mode; 0213 d->mGui.setupUi(this); 0214 0215 d->mGui.membersView->setEditTriggers(QAbstractItemView::AllEditTriggers); 0216 0217 d->mGroupModel = new ContactGroupModel(this); 0218 auto proxyModel = new GroupFilterModel(this); 0219 proxyModel->setSourceModel(d->mGroupModel); 0220 connect(d->mGui.searchField, &QLineEdit::textChanged, this, [proxyModel](const QString &text) { 0221 proxyModel->setFilterRegularExpression(text); 0222 }); 0223 d->mGui.membersView->setModel(proxyModel); 0224 d->mGui.membersView->setItemDelegate(new ContactGroupEditorDelegate(d->mGui.membersView, this)); 0225 0226 if (mode == CreateMode) { 0227 KContacts::ContactGroup dummyGroup; 0228 d->mGroupModel->loadContactGroup(dummyGroup); 0229 0230 QTimer::singleShot(0, this, [this]() { 0231 d->adaptHeaderSizes(); 0232 }); 0233 QTimer::singleShot(0, d->mGui.groupName, qOverload<>(&ContactGroupEditor::setFocus)); 0234 } 0235 0236 d->mGui.membersView->header()->setStretchLastSection(true); 0237 } 0238 0239 ContactGroupEditor::~ContactGroupEditor() = default; 0240 0241 void ContactGroupEditor::loadContactGroup(const Akonadi::Item &item) 0242 { 0243 if (d->mMode == CreateMode) { 0244 Q_ASSERT_X(false, "ContactGroupEditor::loadContactGroup", "You are calling loadContactGroup in CreateMode!"); 0245 } 0246 0247 auto job = new ItemFetchJob(item); 0248 job->fetchScope().fetchFullPayload(); 0249 job->fetchScope().setAncestorRetrieval(Akonadi::ItemFetchScope::Parent); 0250 0251 connect(job, &ItemModifyJob::result, this, [this](KJob *job) { 0252 d->itemFetchDone(job); 0253 }); 0254 0255 d->setupMonitor(); 0256 d->mMonitor->setItemMonitored(item); 0257 0258 new WaitingOverlay(job, this); 0259 } 0260 0261 bool ContactGroupEditor::saveContactGroup() 0262 { 0263 if (d->mMode == EditMode) { 0264 if (!d->mItem.isValid()) { 0265 return false; 0266 } 0267 0268 if (d->mReadOnly) { 0269 return true; 0270 } 0271 0272 auto group = d->mItem.payload<KContacts::ContactGroup>(); 0273 0274 if (!d->storeContactGroup(group)) { 0275 return false; 0276 } 0277 0278 d->mItem.setPayload<KContacts::ContactGroup>(group); 0279 0280 auto job = new ItemModifyJob(d->mItem); 0281 connect(job, &ItemModifyJob::result, this, [this](KJob *job) { 0282 d->storeDone(job); 0283 }); 0284 } else if (d->mMode == CreateMode) { 0285 if (!d->mDefaultCollection.isValid()) { 0286 const QStringList mimeTypeFilter(KContacts::ContactGroup::mimeType()); 0287 0288 QPointer<CollectionDialog> dlg = new CollectionDialog(this); 0289 dlg->setMimeTypeFilter(mimeTypeFilter); 0290 dlg->setAccessRightsFilter(Collection::CanCreateItem); 0291 dlg->setWindowTitle(i18nc("@title:window", "Select Address Book")); 0292 dlg->setDescription(i18n("Select the address book the new contact group shall be saved in:")); 0293 0294 if (dlg->exec() == QDialog::Accepted) { 0295 setDefaultAddressBook(dlg->selectedCollection()); 0296 delete dlg; 0297 } else { 0298 delete dlg; 0299 return false; 0300 } 0301 } 0302 0303 KContacts::ContactGroup group; 0304 if (!d->storeContactGroup(group)) { 0305 return false; 0306 } 0307 0308 Item item; 0309 item.setPayload<KContacts::ContactGroup>(group); 0310 item.setMimeType(KContacts::ContactGroup::mimeType()); 0311 0312 auto job = new ItemCreateJob(item, d->mDefaultCollection); 0313 connect(job, &ItemCreateJob::result, this, [this](KJob *job) { 0314 d->storeDone(job); 0315 }); 0316 } 0317 0318 return true; 0319 } 0320 0321 void ContactGroupEditor::setContactGroupTemplate(const KContacts::ContactGroup &group) 0322 { 0323 d->mGroupModel->loadContactGroup(group); 0324 d->mGui.membersView->header()->setDefaultSectionSize(d->mGui.membersView->header()->width() / 2); 0325 d->mGui.membersView->header()->resizeSections(QHeaderView::Interactive); 0326 } 0327 0328 void ContactGroupEditor::setDefaultAddressBook(const Akonadi::Collection &collection) 0329 { 0330 d->mDefaultCollection = collection; 0331 } 0332 0333 void ContactGroupEditor::groupNameIsValid(bool isValid) 0334 { 0335 #ifndef QT_NO_STYLE_STYLESHEET 0336 QString styleSheet; 0337 if (!isValid) { 0338 const KColorScheme::BackgroundRole bgColorScheme(KColorScheme::NegativeBackground); 0339 KStatefulBrush bgBrush(KColorScheme::View, bgColorScheme); 0340 styleSheet = QStringLiteral("QLineEdit{ background-color:%1 }").arg(bgBrush.brush(palette()).color().name()); 0341 } 0342 d->mGui.groupName->setStyleSheet(styleSheet); 0343 #endif 0344 } 0345 0346 #include "moc_contactgroupeditor.cpp"