Akonadi Contacts

contactgroupeditor.cpp
1 /*
2  This file is part of Akonadi Contact.
3 
4  SPDX-FileCopyrightText: 2007-2009 Tobias Koenig <[email protected]>
5 
6  SPDX-License-Identifier: LGPL-2.0-or-later
7 */
8 
9 #include "contactgroupeditor.h"
10 #include "contactgroupeditor_p.h"
11 #include <KStatefulBrush>
12 
13 #include "contactgroupeditordelegate_p.h"
14 #include "contactgroupmodel_p.h"
15 #include "waitingoverlay_p.h"
16 
17 #include <Akonadi/CollectionDialog>
18 #include <Akonadi/CollectionFetchJob>
19 #include <Akonadi/ItemCreateJob>
20 #include <Akonadi/ItemFetchJob>
21 #include <Akonadi/ItemFetchScope>
22 #include <Akonadi/ItemModifyJob>
23 #include <Akonadi/Monitor>
24 #include <Akonadi/Session>
25 #include <KColorScheme>
26 #include <KContacts/ContactGroup>
27 #include <KLocalizedString>
28 #include <KMessageBox>
29 
30 #include <QMessageBox>
31 #include <QTimer>
32 
33 using namespace Akonadi;
34 using namespace Akonadi;
35 
36 ContactGroupEditorPrivate::ContactGroupEditorPrivate(ContactGroupEditor *parent)
37  : mParent(parent)
38 {
39 }
40 
41 ContactGroupEditorPrivate::~ContactGroupEditorPrivate()
42 {
43  delete mMonitor;
44 }
45 
46 void ContactGroupEditorPrivate::adaptHeaderSizes()
47 {
48  mGui.membersView->header()->setDefaultSectionSize(mGui.membersView->header()->width() / 2);
49  mGui.membersView->header()->resizeSections(QHeaderView::Interactive);
50 }
51 
52 void ContactGroupEditorPrivate::itemFetchDone(KJob *job)
53 {
54  if (job->error()) {
55  return;
56  }
57 
58  auto fetchJob = qobject_cast<ItemFetchJob *>(job);
59  if (!fetchJob) {
60  return;
61  }
62 
63  if (fetchJob->items().isEmpty()) {
64  return;
65  }
66 
67  mItem = fetchJob->items().at(0);
68 
69  mReadOnly = false;
70  if (mMode == ContactGroupEditor::EditMode) {
71  // if in edit mode we have to fetch the parent collection to find out
72  // about the modify rights of the item
73 
74  auto collectionFetchJob = new Akonadi::CollectionFetchJob(mItem.parentCollection(), Akonadi::CollectionFetchJob::Base);
75  mParent->connect(collectionFetchJob, &CollectionFetchJob::result, mParent, [this](KJob *job) {
76  parentCollectionFetchDone(job);
77  });
78  } else {
79  const auto group = mItem.payload<KContacts::ContactGroup>();
80  loadContactGroup(group);
81 
82  setReadOnly(mReadOnly);
83 
84  QTimer::singleShot(0, mParent, [this]() {
85  adaptHeaderSizes();
86  });
87  }
88 }
89 
90 void ContactGroupEditorPrivate::parentCollectionFetchDone(KJob *job)
91 {
92  if (job->error()) {
93  return;
94  }
95 
96  auto fetchJob = qobject_cast<Akonadi::CollectionFetchJob *>(job);
97  if (!fetchJob) {
98  return;
99  }
100 
101  const Akonadi::Collection parentCollection = fetchJob->collections().at(0);
102  if (parentCollection.isValid()) {
103  mReadOnly = !(parentCollection.rights() & Collection::CanChangeItem);
104  }
105 
106  const auto group = mItem.payload<KContacts::ContactGroup>();
107  loadContactGroup(group);
108 
109  setReadOnly(mReadOnly);
110 
111  QTimer::singleShot(0, mParent, [this]() {
112  adaptHeaderSizes();
113  });
114 }
115 
116 void ContactGroupEditorPrivate::storeDone(KJob *job)
117 {
118  if (job->error()) {
119  Q_EMIT mParent->error(job->errorString());
120  return;
121  }
122 
123  if (mMode == ContactGroupEditor::EditMode) {
124  Q_EMIT mParent->contactGroupStored(mItem);
125  } else if (mMode == ContactGroupEditor::CreateMode) {
126  Q_EMIT mParent->contactGroupStored(static_cast<ItemCreateJob *>(job)->item());
127  }
128 }
129 
130 void ContactGroupEditorPrivate::itemChanged(const Item &item, const QSet<QByteArray> &)
131 {
132  Q_UNUSED(item)
133  QPointer<QMessageBox> dlg = new QMessageBox(mParent); // krazy:exclude=qclasses
134 
135  dlg->setInformativeText(i18n("The contact group has been changed by someone else.\nWhat should be done?"));
136  dlg->addButton(i18n("Take over changes"), QMessageBox::AcceptRole);
137  dlg->addButton(i18n("Ignore and Overwrite changes"), QMessageBox::RejectRole);
138 
139  if (dlg->exec() == QMessageBox::AcceptRole) {
140  auto job = new ItemFetchJob(mItem);
141  job->fetchScope().fetchFullPayload();
142  job->fetchScope().setAncestorRetrieval(Akonadi::ItemFetchScope::Parent);
143 
144  mParent->connect(job, &ItemFetchJob::result, mParent, [this](KJob *job) {
145  itemFetchDone(job);
146  });
147  new WaitingOverlay(job, mParent);
148  } else {
149  // Still update the item so that the internal revision match
150  mItem = item;
151  }
152  delete dlg;
153 }
154 
155 void ContactGroupEditorPrivate::loadContactGroup(const KContacts::ContactGroup &group)
156 {
157  mGui.membersView->setSortingEnabled(false);
158  mGui.groupName->setText(group.name());
159 
160  mGroupModel->loadContactGroup(group);
161 
162  const QAbstractItemModel *model = mGui.membersView->model();
163  mGui.membersView->setCurrentIndex(model->index(model->rowCount() - 1, 0));
164 
165  if (mMode == ContactGroupEditor::EditMode) {
166  mGui.membersView->setFocus();
167  }
168 
169  mGui.membersView->header()->resizeSections(QHeaderView::Stretch);
170  mGui.membersView->setSortingEnabled(true);
171 }
172 
173 bool ContactGroupEditorPrivate::storeContactGroup(KContacts::ContactGroup &group)
174 {
175  if (mGui.groupName->text().isEmpty()) {
176  KMessageBox::error(mParent, i18n("The name of the contact group must not be empty."));
177  return false;
178  }
179 
180  group.setName(mGui.groupName->text());
181 
182  if (!mGroupModel->storeContactGroup(group)) {
183  KMessageBox::error(mParent, mGroupModel->lastErrorMessage());
184  return false;
185  }
186 
187  return true;
188 }
189 
190 void ContactGroupEditorPrivate::setupMonitor()
191 {
192  delete mMonitor;
193  mMonitor = new Monitor;
194  mMonitor->setObjectName(QLatin1StringView("ContactGroupEditorMonitor"));
195  mMonitor->ignoreSession(Session::defaultSession());
196 
197  QObject::connect(mMonitor, &Monitor::itemChanged, mParent, [this](const Akonadi::Item &item, const QSet<QByteArray> &arrays) {
198  itemChanged(item, arrays);
199  });
200 }
201 
202 void ContactGroupEditorPrivate::setReadOnly(bool readOnly)
203 {
204  mGui.groupName->setReadOnly(readOnly);
205  mGui.membersView->setEnabled(!readOnly);
206 }
207 
208 ContactGroupEditor::ContactGroupEditor(Mode mode, QWidget *parent)
209  : QWidget(parent)
210  , d(new ContactGroupEditorPrivate(this))
211 {
212  d->mMode = mode;
213  d->mGui.setupUi(this);
214 
215  d->mGui.membersView->setEditTriggers(QAbstractItemView::AllEditTriggers);
216 
217  d->mGroupModel = new ContactGroupModel(this);
218  auto proxyModel = new GroupFilterModel(this);
219  proxyModel->setSourceModel(d->mGroupModel);
220  connect(d->mGui.searchField, &QLineEdit::textChanged, this, [proxyModel](const QString &text) {
221  proxyModel->setFilterRegularExpression(text);
222  });
223  d->mGui.membersView->setModel(proxyModel);
224  d->mGui.membersView->setItemDelegate(new ContactGroupEditorDelegate(d->mGui.membersView, this));
225 
226  if (mode == CreateMode) {
227  KContacts::ContactGroup dummyGroup;
228  d->mGroupModel->loadContactGroup(dummyGroup);
229 
230  QTimer::singleShot(0, this, [this]() {
231  d->adaptHeaderSizes();
232  });
233  QTimer::singleShot(0, d->mGui.groupName, qOverload<>(&ContactGroupEditor::setFocus));
234  }
235 
236  d->mGui.membersView->header()->setStretchLastSection(true);
237 }
238 
240 
242 {
243  if (d->mMode == CreateMode) {
244  Q_ASSERT_X(false, "ContactGroupEditor::loadContactGroup", "You are calling loadContactGroup in CreateMode!");
245  }
246 
247  auto job = new ItemFetchJob(item);
248  job->fetchScope().fetchFullPayload();
249  job->fetchScope().setAncestorRetrieval(Akonadi::ItemFetchScope::Parent);
250 
251  connect(job, &ItemModifyJob::result, this, [this](KJob *job) {
252  d->itemFetchDone(job);
253  });
254 
255  d->setupMonitor();
256  d->mMonitor->setItemMonitored(item);
257 
258  new WaitingOverlay(job, this);
259 }
260 
262 {
263  if (d->mMode == EditMode) {
264  if (!d->mItem.isValid()) {
265  return false;
266  }
267 
268  if (d->mReadOnly) {
269  return true;
270  }
271 
272  auto group = d->mItem.payload<KContacts::ContactGroup>();
273 
274  if (!d->storeContactGroup(group)) {
275  return false;
276  }
277 
278  d->mItem.setPayload<KContacts::ContactGroup>(group);
279 
280  auto job = new ItemModifyJob(d->mItem);
281  connect(job, &ItemModifyJob::result, this, [this](KJob *job) {
282  d->storeDone(job);
283  });
284  } else if (d->mMode == CreateMode) {
285  if (!d->mDefaultCollection.isValid()) {
286  const QStringList mimeTypeFilter(KContacts::ContactGroup::mimeType());
287 
289  dlg->setMimeTypeFilter(mimeTypeFilter);
290  dlg->setAccessRightsFilter(Collection::CanCreateItem);
291  dlg->setWindowTitle(i18nc("@title:window", "Select Address Book"));
292  dlg->setDescription(i18n("Select the address book the new contact group shall be saved in:"));
293 
294  if (dlg->exec() == QDialog::Accepted) {
295  setDefaultAddressBook(dlg->selectedCollection());
296  delete dlg;
297  } else {
298  delete dlg;
299  return false;
300  }
301  }
302 
304  if (!d->storeContactGroup(group)) {
305  return false;
306  }
307 
308  Item item;
309  item.setPayload<KContacts::ContactGroup>(group);
311 
312  auto job = new ItemCreateJob(item, d->mDefaultCollection);
313  connect(job, &ItemCreateJob::result, this, [this](KJob *job) {
314  d->storeDone(job);
315  });
316  }
317 
318  return true;
319 }
320 
322 {
323  d->mGroupModel->loadContactGroup(group);
324  d->mGui.membersView->header()->setDefaultSectionSize(d->mGui.membersView->header()->width() / 2);
325  d->mGui.membersView->header()->resizeSections(QHeaderView::Interactive);
326 }
327 
329 {
330  d->mDefaultCollection = collection;
331 }
332 
333 void ContactGroupEditor::groupNameIsValid(bool isValid)
334 {
335 #ifndef QT_NO_STYLE_STYLESHEET
337  if (!isValid) {
339  KStatefulBrush bgBrush(KColorScheme::View, bgColorScheme);
340  styleSheet = QStringLiteral("QLineEdit{ background-color:%1 }").arg(bgBrush.brush(palette()).color().name());
341  }
342  d->mGui.groupName->setStyleSheet(styleSheet);
343 #endif
344 }
345 
346 #include "moc_contactgroupeditor.cpp"
virtual int rowCount(const QModelIndex &parent) const const=0
void loadContactGroup(const Akonadi::Item &group)
Loads the contact group into the editor.
void setMimeType(const QString &mimeType)
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
@ CreateMode
Creates a new contact group.
QString i18n(const char *text, const TYPE &arg...)
Mode
Describes the mode of the contact group editor.
void textChanged(const QString &text)
An widget to edit contact groups in Akonadi.
@ EditMode
Edits an existing contact group.
Rights rights() const
~ContactGroupEditor() override
Destroys the contact group editor.
static QString mimeType()
void setupUi(QWidget *widget)
void error(QWidget *parent, const QString &text, const QString &title, const KGuiItem &buttonOk, Options options=Notify)
QString name() const
void setObjectName(const QString &name)
virtual QModelIndex index(int row, int column, const QModelIndex &parent) const const=0
QString i18nc(const char *context, const char *text, const TYPE &arg...)
void setContactGroupTemplate(const KContacts::ContactGroup &group)
Sets a contact group that is used as template in create mode.
bool isValid() const
void setPayload(const T &p)
void setName(const QString &name)
virtual QString errorString() const
int error() const
void setFocus()
bool saveContactGroup()
Saves the contact group from the editor back to the storage.
void setDefaultAddressBook(const Akonadi::Collection &addressbook)
Sets the addressbook which shall be used to store new contact groups.
A widget for editing the display name of a contact.
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Tue Nov 28 2023 03:51:43 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.