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"