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 
35 ContactGroupEditorPrivate::ContactGroupEditorPrivate(ContactGroupEditor *parent)
36  : mParent(parent)
37 {
38 }
39 
40 ContactGroupEditorPrivate::~ContactGroupEditorPrivate()
41 {
42  delete mMonitor;
43 }
44 
45 void ContactGroupEditorPrivate::adaptHeaderSizes()
46 {
47  mGui.membersView->header()->setDefaultSectionSize(mGui.membersView->header()->width() / 2);
48  mGui.membersView->header()->resizeSections(QHeaderView::Interactive);
49 }
50 
51 void ContactGroupEditorPrivate::itemFetchDone(KJob *job)
52 {
53  if (job->error()) {
54  return;
55  }
56 
57  auto fetchJob = qobject_cast<ItemFetchJob *>(job);
58  if (!fetchJob) {
59  return;
60  }
61 
62  if (fetchJob->items().isEmpty()) {
63  return;
64  }
65 
66  mItem = fetchJob->items().at(0);
67 
68  mReadOnly = false;
69  if (mMode == ContactGroupEditor::EditMode) {
70  // if in edit mode we have to fetch the parent collection to find out
71  // about the modify rights of the item
72 
73  auto collectionFetchJob = new Akonadi::CollectionFetchJob(mItem.parentCollection(), Akonadi::CollectionFetchJob::Base);
74  mParent->connect(collectionFetchJob, &CollectionFetchJob::result, mParent, [this](KJob *job) {
75  parentCollectionFetchDone(job);
76  });
77  } else {
78  const auto group = mItem.payload<KContacts::ContactGroup>();
79  loadContactGroup(group);
80 
81  setReadOnly(mReadOnly);
82 
83  QTimer::singleShot(0, mParent, [this]() {
84  adaptHeaderSizes();
85  });
86  }
87 }
88 
89 void ContactGroupEditorPrivate::parentCollectionFetchDone(KJob *job)
90 {
91  if (job->error()) {
92  return;
93  }
94 
95  auto fetchJob = qobject_cast<Akonadi::CollectionFetchJob *>(job);
96  if (!fetchJob) {
97  return;
98  }
99 
100  const Akonadi::Collection parentCollection = fetchJob->collections().at(0);
101  if (parentCollection.isValid()) {
102  mReadOnly = !(parentCollection.rights() & Collection::CanChangeItem);
103  }
104 
105  const auto group = mItem.payload<KContacts::ContactGroup>();
106  loadContactGroup(group);
107 
108  setReadOnly(mReadOnly);
109 
110  QTimer::singleShot(0, mParent, [this]() {
111  adaptHeaderSizes();
112  });
113 }
114 
115 void ContactGroupEditorPrivate::storeDone(KJob *job)
116 {
117  if (job->error()) {
118  Q_EMIT mParent->error(job->errorString());
119  return;
120  }
121 
122  if (mMode == ContactGroupEditor::EditMode) {
123  Q_EMIT mParent->contactGroupStored(mItem);
124  } else if (mMode == ContactGroupEditor::CreateMode) {
125  Q_EMIT mParent->contactGroupStored(static_cast<ItemCreateJob *>(job)->item());
126  }
127 }
128 
129 void ContactGroupEditorPrivate::itemChanged(const Item &item, const QSet<QByteArray> &)
130 {
131  Q_UNUSED(item)
132  QPointer<QMessageBox> dlg = new QMessageBox(mParent); // krazy:exclude=qclasses
133 
134  dlg->setInformativeText(i18n("The contact group has been changed by someone else.\nWhat should be done?"));
135  dlg->addButton(i18n("Take over changes"), QMessageBox::AcceptRole);
136  dlg->addButton(i18n("Ignore and Overwrite changes"), QMessageBox::RejectRole);
137 
138  if (dlg->exec() == QMessageBox::AcceptRole) {
139  auto job = new ItemFetchJob(mItem);
140  job->fetchScope().fetchFullPayload();
141  job->fetchScope().setAncestorRetrieval(Akonadi::ItemFetchScope::Parent);
142 
143  mParent->connect(job, &ItemFetchJob::result, mParent, [this](KJob *job) {
144  itemFetchDone(job);
145  });
146  new WaitingOverlay(job, mParent);
147  } else {
148  // Still update the item so that the internal revision match
149  mItem = item;
150  }
151  delete dlg;
152 }
153 
154 void ContactGroupEditorPrivate::loadContactGroup(const KContacts::ContactGroup &group)
155 {
156  mGui.membersView->setSortingEnabled(false);
157  mGui.groupName->setText(group.name());
158 
159  mGroupModel->loadContactGroup(group);
160 
161  const QAbstractItemModel *model = mGui.membersView->model();
162  mGui.membersView->setCurrentIndex(model->index(model->rowCount() - 1, 0));
163 
164  if (mMode == ContactGroupEditor::EditMode) {
165  mGui.membersView->setFocus();
166  }
167 
168  mGui.membersView->header()->resizeSections(QHeaderView::Stretch);
169  mGui.membersView->setSortingEnabled(true);
170 }
171 
172 bool ContactGroupEditorPrivate::storeContactGroup(KContacts::ContactGroup &group)
173 {
174  if (mGui.groupName->text().isEmpty()) {
175  KMessageBox::error(mParent, i18n("The name of the contact group must not be empty."));
176  return false;
177  }
178 
179  group.setName(mGui.groupName->text());
180 
181  if (!mGroupModel->storeContactGroup(group)) {
182  KMessageBox::error(mParent, mGroupModel->lastErrorMessage());
183  return false;
184  }
185 
186  return true;
187 }
188 
189 void ContactGroupEditorPrivate::setupMonitor()
190 {
191  delete mMonitor;
192  mMonitor = new Monitor;
193  mMonitor->setObjectName(QStringLiteral("ContactGroupEditorMonitor"));
194  mMonitor->ignoreSession(Session::defaultSession());
195 
196  QObject::connect(mMonitor, &Monitor::itemChanged, mParent, [this](const Akonadi::Item &item, const QSet<QByteArray> &arrays) {
197  itemChanged(item, arrays);
198  });
199 }
200 
201 void ContactGroupEditorPrivate::setReadOnly(bool readOnly)
202 {
203  mGui.groupName->setReadOnly(readOnly);
204  mGui.membersView->setEnabled(!readOnly);
205 }
206 
207 ContactGroupEditor::ContactGroupEditor(Mode mode, QWidget *parent)
208  : QWidget(parent)
209  , d(new ContactGroupEditorPrivate(this))
210 {
211  d->mMode = mode;
212  d->mGui.setupUi(this);
213 
214  d->mGui.membersView->setEditTriggers(QAbstractItemView::AllEditTriggers);
215 
216  d->mGroupModel = new ContactGroupModel(this);
217  auto proxyModel = new GroupFilterModel(this);
218  proxyModel->setSourceModel(d->mGroupModel);
219  connect(d->mGui.searchField, &QLineEdit::textChanged, this, [proxyModel](const QString &text) {
220  proxyModel->setFilterRegularExpression(text);
221  });
222  d->mGui.membersView->setModel(proxyModel);
223  d->mGui.membersView->setItemDelegate(new ContactGroupEditorDelegate(d->mGui.membersView, this));
224 
225  if (mode == CreateMode) {
226  KContacts::ContactGroup dummyGroup;
227  d->mGroupModel->loadContactGroup(dummyGroup);
228 
229  QTimer::singleShot(0, this, [this]() {
230  d->adaptHeaderSizes();
231  });
232  QTimer::singleShot(0, d->mGui.groupName, qOverload<>(&ContactGroupEditor::setFocus));
233  }
234 
235  d->mGui.membersView->header()->setStretchLastSection(true);
236 }
237 
239 
241 {
242  if (d->mMode == CreateMode) {
243  Q_ASSERT_X(false, "ContactGroupEditor::loadContactGroup", "You are calling loadContactGroup in CreateMode!");
244  }
245 
246  auto job = new ItemFetchJob(item);
247  job->fetchScope().fetchFullPayload();
248  job->fetchScope().setAncestorRetrieval(Akonadi::ItemFetchScope::Parent);
249 
250  connect(job, &ItemModifyJob::result, this, [this](KJob *job) {
251  d->itemFetchDone(job);
252  });
253 
254  d->setupMonitor();
255  d->mMonitor->setItemMonitored(item);
256 
257  new WaitingOverlay(job, this);
258 }
259 
261 {
262  if (d->mMode == EditMode) {
263  if (!d->mItem.isValid()) {
264  return false;
265  }
266 
267  if (d->mReadOnly) {
268  return true;
269  }
270 
271  auto group = d->mItem.payload<KContacts::ContactGroup>();
272 
273  if (!d->storeContactGroup(group)) {
274  return false;
275  }
276 
277  d->mItem.setPayload<KContacts::ContactGroup>(group);
278 
279  auto job = new ItemModifyJob(d->mItem);
280  connect(job, &ItemModifyJob::result, this, [this](KJob *job) {
281  d->storeDone(job);
282  });
283  } else if (d->mMode == CreateMode) {
284  if (!d->mDefaultCollection.isValid()) {
285  const QStringList mimeTypeFilter(KContacts::ContactGroup::mimeType());
286 
288  dlg->setMimeTypeFilter(mimeTypeFilter);
289  dlg->setAccessRightsFilter(Collection::CanCreateItem);
290  dlg->setWindowTitle(i18nc("@title:window", "Select Address Book"));
291  dlg->setDescription(i18n("Select the address book the new contact group shall be saved in:"));
292 
293  if (dlg->exec() == QDialog::Accepted) {
294  setDefaultAddressBook(dlg->selectedCollection());
295  delete dlg;
296  } else {
297  delete dlg;
298  return false;
299  }
300  }
301 
303  if (!d->storeContactGroup(group)) {
304  return false;
305  }
306 
307  Item item;
308  item.setPayload<KContacts::ContactGroup>(group);
310 
311  auto job = new ItemCreateJob(item, d->mDefaultCollection);
312  connect(job, &ItemCreateJob::result, this, [this](KJob *job) {
313  d->storeDone(job);
314  });
315  }
316 
317  return true;
318 }
319 
321 {
322  d->mGroupModel->loadContactGroup(group);
323  d->mGui.membersView->header()->setDefaultSectionSize(d->mGui.membersView->header()->width() / 2);
324  d->mGui.membersView->header()->resizeSections(QHeaderView::Interactive);
325 }
326 
328 {
329  d->mDefaultCollection = collection;
330 }
331 
332 void ContactGroupEditor::groupNameIsValid(bool isValid)
333 {
334 #ifndef QT_NO_STYLE_STYLESHEET
336  if (!isValid) {
338  KStatefulBrush bgBrush(KColorScheme::View, bgColorScheme);
339  styleSheet = QStringLiteral("QLineEdit{ background-color:%1 }").arg(bgBrush.brush(palette()).color().name());
340  }
341  d->mGui.groupName->setStyleSheet(styleSheet);
342 #endif
343 }
344 
345 #include "moc_contactgroupeditor.cpp"
virtual int rowCount(const QModelIndex &parent) const const=0
void result(KJob *job)
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.
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Sat Apr 1 2023 04:09:04 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.