Messagelib

configureaggregationsdialog.cpp
1 /******************************************************************************
2  *
3  * SPDX-FileCopyrightText: 2008 Szymon Tomasz Stefanek <[email protected]>
4  *
5  * SPDX-License-Identifier: GPL-2.0-or-later
6  *
7  *******************************************************************************/
8 
9 #include "utils/configureaggregationsdialog.h"
10 #include "utils/configureaggregationsdialog_p.h"
11 
12 #include "core/aggregation.h"
13 #include "utils/aggregationeditor.h"
14 
15 #include "core/manager.h"
16 
17 #include <QFrame>
18 #include <QGridLayout>
19 #include <QMap>
20 #include <QPushButton>
21 
22 #include <KConfig>
23 #include <KConfigGroup>
24 #include <KLocalizedString>
25 #include <QDialogButtonBox>
26 #include <QFileDialog>
27 #include <QIcon>
28 #include <QVBoxLayout>
29 
30 namespace MessageList
31 {
32 namespace Utils
33 {
34 class AggregationListWidgetItem : public QListWidgetItem
35 {
36 private:
37  Core::Aggregation *mAggregation = nullptr;
38 
39 public:
40  AggregationListWidgetItem(QListWidget *par, const Core::Aggregation &set)
41  : QListWidgetItem(set.name(), par)
42  {
43  mAggregation = new Core::Aggregation(set);
44  }
45 
46  ~AggregationListWidgetItem() override
47  {
48  delete mAggregation;
49  }
50 
51 public:
52  Q_REQUIRED_RESULT Core::Aggregation *aggregation() const
53  {
54  return mAggregation;
55  }
56 
57  void forgetAggregation()
58  {
59  mAggregation = nullptr;
60  }
61 };
62 
63 /**
64  * The widget that lists the available Aggregations.
65  *
66  * At the moment of writing, derived from QListWidget only to override sizeHint().
67  */
68 class AggregationListWidget : public QListWidget
69 {
70 public:
71  AggregationListWidget(QWidget *parent)
72  : QListWidget(parent)
73  {
74  }
75 
76 public:
77  // need a larger but shorter QListWidget
78  Q_REQUIRED_RESULT QSize sizeHint() const override
79  {
80  return {450, 128};
81  }
82 };
83 } // namespace Utils
84 } // namespace MessageList
85 
86 using namespace MessageList::Core;
87 using namespace MessageList::Utils;
88 
89 ConfigureAggregationsDialog::ConfigureAggregationsDialog(QWidget *parent)
90  : QDialog(parent)
91  , d(new ConfigureAggregationsDialogPrivate(this))
92 {
93  setAttribute(Qt::WA_DeleteOnClose);
95  auto mainLayout = new QVBoxLayout(this);
96  QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok);
97  okButton->setDefault(true);
98  okButton->setShortcut(Qt::CTRL | Qt::Key_Return);
99  connect(buttonBox, &QDialogButtonBox::rejected, this, &ConfigureAggregationsDialog::reject);
100  setWindowTitle(i18nc("@title:window", "Customize Message Aggregation Modes"));
101 
102  auto base = new QWidget(this);
103  mainLayout->addWidget(base);
104  mainLayout->addWidget(buttonBox);
105 
106  auto g = new QGridLayout(base);
107  g->setContentsMargins({});
108 
109  d->mAggregationList = new AggregationListWidget(base);
110  d->mAggregationList->setSelectionMode(QAbstractItemView::ExtendedSelection);
111  d->mAggregationList->setSortingEnabled(true);
112  g->addWidget(d->mAggregationList, 0, 0, 7, 1);
113 
114  connect(d->mAggregationList, &AggregationListWidget::itemClicked, this, [this](QListWidgetItem *item) {
115  d->aggregationListItemClicked(item);
116  });
117 
118  d->mNewAggregationButton = new QPushButton(i18n("New Aggregation"), base);
119  d->mNewAggregationButton->setIcon(QIcon::fromTheme(QStringLiteral("document-new")));
120  g->addWidget(d->mNewAggregationButton, 0, 1);
121 
122  connect(d->mNewAggregationButton, &QPushButton::clicked, this, [this]() {
123  d->newAggregationButtonClicked();
124  });
125 
126  d->mCloneAggregationButton = new QPushButton(i18n("Clone Aggregation"), base);
127  d->mCloneAggregationButton->setIcon(QIcon::fromTheme(QStringLiteral("edit-copy")));
128  g->addWidget(d->mCloneAggregationButton, 1, 1);
129 
130  connect(d->mCloneAggregationButton, &QPushButton::clicked, this, [this]() {
131  d->cloneAggregationButtonClicked();
132  });
133 
134  auto f = new QFrame(base);
135  f->setFrameStyle(QFrame::Sunken | QFrame::HLine);
136  f->setMinimumHeight(24);
137  g->addWidget(f, 2, 1, Qt::AlignVCenter);
138 
139  d->mExportAggregationButton = new QPushButton(i18n("Export Aggregation..."), base);
140  g->addWidget(d->mExportAggregationButton, 3, 1);
141 
142  connect(d->mExportAggregationButton, &QPushButton::clicked, this, [this]() {
143  d->exportAggregationButtonClicked();
144  });
145 
146  d->mImportAggregationButton = new QPushButton(i18n("Import Aggregation..."), base);
147  g->addWidget(d->mImportAggregationButton, 4, 1);
148  connect(d->mImportAggregationButton, &QPushButton::clicked, this, [this]() {
149  d->importAggregationButtonClicked();
150  });
151 
152  f = new QFrame(base);
153  f->setFrameStyle(QFrame::Sunken | QFrame::HLine);
154  f->setMinimumHeight(24);
155  g->addWidget(f, 5, 1, Qt::AlignVCenter);
156 
157  d->mDeleteAggregationButton = new QPushButton(i18n("Delete Aggregation"), base);
158  d->mDeleteAggregationButton->setIcon(QIcon::fromTheme(QStringLiteral("edit-delete")));
159  g->addWidget(d->mDeleteAggregationButton, 6, 1);
160 
161  connect(d->mDeleteAggregationButton, &QPushButton::clicked, this, [this]() {
162  d->deleteAggregationButtonClicked();
163  });
164 
165  d->mEditor = new AggregationEditor(base);
166  g->addWidget(d->mEditor, 8, 0, 1, 2);
167 
168  connect(d->mEditor, &AggregationEditor::aggregationNameChanged, this, [this]() {
169  d->editedAggregationNameChanged();
170  });
171 
172  g->setColumnStretch(0, 1);
173  g->setRowStretch(7, 1);
174 
175  connect(okButton, &QPushButton::clicked, this, [this]() {
176  d->okButtonClicked();
177  });
178 
179  d->fillAggregationList();
180 }
181 
182 ConfigureAggregationsDialog::~ConfigureAggregationsDialog() = default;
183 
184 void ConfigureAggregationsDialog::selectAggregation(const QString &aggregationId)
185 {
186  AggregationListWidgetItem *item = d->findAggregationItemById(aggregationId);
187  if (item) {
188  d->mAggregationList->setCurrentItem(item);
189  d->aggregationListItemClicked(item);
190  }
191 }
192 
193 void ConfigureAggregationsDialog::ConfigureAggregationsDialogPrivate::okButtonClicked()
194 {
195  if (Manager::instance()) {
196  commitEditor();
197 
198  Manager::instance()->removeAllAggregations();
199 
200  const int c = mAggregationList->count();
201  int i = 0;
202  while (i < c) {
203  auto item = dynamic_cast<AggregationListWidgetItem *>(mAggregationList->item(i));
204  if (item) {
205  Manager::instance()->addAggregation(item->aggregation());
206  item->forgetAggregation();
207  }
208  ++i;
209  }
210 
211  Manager::instance()->aggregationsConfigurationCompleted();
212  }
213  Q_EMIT q->okClicked();
214  q->close(); // this will delete too
215 }
216 
217 void ConfigureAggregationsDialog::ConfigureAggregationsDialogPrivate::commitEditor()
218 {
219  Aggregation *editedAggregation = mEditor->editedAggregation();
220  if (!editedAggregation) {
221  return;
222  }
223 
224  mEditor->commit();
225 
226  AggregationListWidgetItem *editedItem = findAggregationItemByAggregation(editedAggregation);
227  if (!editedItem) {
228  return;
229  }
230  const QString goodName = uniqueNameForAggregation(editedAggregation->name(), editedAggregation);
231  editedAggregation->setName(goodName);
232  editedItem->setText(goodName);
233 }
234 
235 void ConfigureAggregationsDialog::ConfigureAggregationsDialogPrivate::editedAggregationNameChanged()
236 {
237  Aggregation *set = mEditor->editedAggregation();
238  if (!set) {
239  return;
240  }
241 
242  AggregationListWidgetItem *it = findAggregationItemByAggregation(set);
243  if (!it) {
244  return;
245  }
246 
247  const QString goodName = uniqueNameForAggregation(set->name(), set);
248 
249  it->setText(goodName);
250 }
251 
252 void ConfigureAggregationsDialog::ConfigureAggregationsDialogPrivate::fillAggregationList()
253 {
254  if (!Manager::instance()) {
255  return;
256  }
257  const QMap<QString, Aggregation *> &sets = Manager::instance()->aggregations();
259  for (QMap<QString, Aggregation *>::ConstIterator it = sets.constBegin(); it != end; ++it) {
260  (void)new AggregationListWidgetItem(mAggregationList, *(*it));
261  }
262 }
263 
264 void ConfigureAggregationsDialog::ConfigureAggregationsDialogPrivate::aggregationListItemClicked(QListWidgetItem *cur)
265 {
266  commitEditor();
267  updateButton(cur);
268 }
269 
270 void ConfigureAggregationsDialog::ConfigureAggregationsDialogPrivate::updateButton(QListWidgetItem *cur)
271 {
272  const int numberOfSelectedItem(mAggregationList->selectedItems().count());
273 
274  AggregationListWidgetItem *item = cur ? dynamic_cast<AggregationListWidgetItem *>(cur) : nullptr;
275  mDeleteAggregationButton->setEnabled(item && !item->aggregation()->readOnly() && (mAggregationList->count() > 1));
276 
277  mCloneAggregationButton->setEnabled(numberOfSelectedItem == 1);
278  mExportAggregationButton->setEnabled(numberOfSelectedItem > 0);
279  mEditor->editAggregation(item ? item->aggregation() : nullptr);
280  if (item && !item->isSelected()) {
281  item->setSelected(true); // make sure it's true
282  }
283 }
284 
285 AggregationListWidgetItem *ConfigureAggregationsDialog::ConfigureAggregationsDialogPrivate::findAggregationItemByName(const QString &name,
286  Aggregation *skipAggregation)
287 {
288  const int c = mAggregationList->count();
289  int i = 0;
290  while (i < c) {
291  auto item = dynamic_cast<AggregationListWidgetItem *>(mAggregationList->item(i));
292  if (item) {
293  if (item->aggregation() != skipAggregation) {
294  if (item->aggregation()->name() == name) {
295  return item;
296  }
297  }
298  }
299  ++i;
300  }
301  return nullptr;
302 }
303 
304 AggregationListWidgetItem *ConfigureAggregationsDialog::ConfigureAggregationsDialogPrivate::findAggregationItemById(const QString &aggregationId)
305 {
306  const int c = mAggregationList->count();
307  int i = 0;
308  while (i < c) {
309  auto item = dynamic_cast<AggregationListWidgetItem *>(mAggregationList->item(i));
310  if (item) {
311  if (item->aggregation()->id() == aggregationId) {
312  return item;
313  }
314  }
315  ++i;
316  }
317  return nullptr;
318 }
319 
320 AggregationListWidgetItem *ConfigureAggregationsDialog::ConfigureAggregationsDialogPrivate::findAggregationItemByAggregation(Aggregation *set)
321 {
322  const int c = mAggregationList->count();
323  int i = 0;
324  while (i < c) {
325  auto item = dynamic_cast<AggregationListWidgetItem *>(mAggregationList->item(i));
326  if (item) {
327  if (item->aggregation() == set) {
328  return item;
329  }
330  }
331  ++i;
332  }
333  return nullptr;
334 }
335 
336 QString ConfigureAggregationsDialog::ConfigureAggregationsDialogPrivate::uniqueNameForAggregation(const QString &baseName, Aggregation *skipAggregation)
337 {
338  QString ret = baseName;
339  if (ret.isEmpty()) {
340  ret = i18n("Unnamed Aggregation");
341  }
342 
343  int idx = 1;
344 
345  AggregationListWidgetItem *item = findAggregationItemByName(ret, skipAggregation);
346  while (item) {
347  idx++;
348  ret = QStringLiteral("%1 %2").arg(baseName).arg(idx);
349  item = findAggregationItemByName(ret, skipAggregation);
350  }
351  return ret;
352 }
353 
354 void ConfigureAggregationsDialog::ConfigureAggregationsDialogPrivate::newAggregationButtonClicked()
355 {
356  Aggregation emptyAggregation;
357  emptyAggregation.setName(uniqueNameForAggregation(i18n("New Aggregation")));
358  auto item = new AggregationListWidgetItem(mAggregationList, emptyAggregation);
359 
360  mAggregationList->setCurrentItem(item);
361  mDeleteAggregationButton->setEnabled(item && !item->aggregation()->readOnly());
362 }
363 
364 void ConfigureAggregationsDialog::ConfigureAggregationsDialogPrivate::cloneAggregationButtonClicked()
365 {
366  auto item = dynamic_cast<AggregationListWidgetItem *>(mAggregationList->currentItem());
367  if (!item) {
368  return;
369  }
370  commitEditor();
371  item->setSelected(false);
372  Aggregation copyAggregation(*(item->aggregation()));
373  copyAggregation.setReadOnly(false);
374  copyAggregation.generateUniqueId(); // regenerate id so it becomes different
375  copyAggregation.setName(uniqueNameForAggregation(item->aggregation()->name()));
376  item = new AggregationListWidgetItem(mAggregationList, copyAggregation);
377 
378  mAggregationList->setCurrentItem(item);
379  aggregationListItemClicked(item);
380 }
381 
382 void ConfigureAggregationsDialog::ConfigureAggregationsDialogPrivate::deleteAggregationButtonClicked()
383 {
384  const QList<QListWidgetItem *> list = mAggregationList->selectedItems();
385  if (list.isEmpty()) {
386  return;
387  }
388 
389  mEditor->editAggregation(nullptr); // forget it
390  for (QListWidgetItem *it : list) {
391  auto item = dynamic_cast<AggregationListWidgetItem *>(it);
392  if (!item) {
393  return;
394  }
395  if (!item->aggregation()->readOnly()) {
396  delete item; // this will trigger aggregationListCurrentItemChanged()
397  }
398  if (mAggregationList->count() < 2) {
399  break; // no way: desperately try to keep at least one option set alive :)
400  }
401  }
402 
403  auto newItem = dynamic_cast<AggregationListWidgetItem *>(mAggregationList->currentItem());
404  updateButton(newItem);
405 }
406 
407 void ConfigureAggregationsDialog::ConfigureAggregationsDialogPrivate::importAggregationButtonClicked()
408 {
409  const QString filename = QFileDialog::getOpenFileName(q, i18n("Import Aggregation"));
410  if (!filename.isEmpty()) {
411  KConfig config(filename);
412 
413  if (config.hasGroup(QStringLiteral("MessageListView::Aggregations"))) {
414  KConfigGroup grp(&config, QStringLiteral("MessageListView::Aggregations"));
415  const int cnt = grp.readEntry("Count", 0);
416  int idx = 0;
417  while (idx < cnt) {
418  const QString data = grp.readEntry(QStringLiteral("Set%1").arg(idx), QString());
419  if (!data.isEmpty()) {
420  auto set = new Aggregation();
421  if (set->loadFromString(data)) {
422  set->setReadOnly(false);
423  set->generateUniqueId(); // regenerate id so it becomes different
424  set->setName(uniqueNameForAggregation(set->name()));
425  (void)new AggregationListWidgetItem(mAggregationList, *set);
426  } else {
427  delete set; // b0rken
428  }
429  }
430  ++idx;
431  }
432  }
433  }
434 }
435 
436 void ConfigureAggregationsDialog::ConfigureAggregationsDialogPrivate::exportAggregationButtonClicked()
437 {
438  const QList<QListWidgetItem *> list = mAggregationList->selectedItems();
439  if (list.isEmpty()) {
440  return;
441  }
442  const QString filename = QFileDialog::getSaveFileName(q, i18n("Export Aggregation"), QString(), i18n("All Files (*)"));
443  if (!filename.isEmpty()) {
444  KConfig config(filename);
445 
446  KConfigGroup grp(&config, QStringLiteral("MessageListView::Aggregations"));
447  grp.writeEntry("Count", list.count());
448 
449  int idx = 0;
450  for (QListWidgetItem *item : list) {
451  auto themeItem = static_cast<AggregationListWidgetItem *>(item);
452  grp.writeEntry(QStringLiteral("Set%1").arg(idx), themeItem->aggregation()->saveToString());
453  ++idx;
454  }
455  }
456 }
457 
458 #include "moc_configureaggregationsdialog.cpp"
A set of aggregation options that can be applied to the MessageList::Model in a single shot...
Definition: aggregation.h:28
A widget that allows editing a single MessageList::Aggregation.
QMap::const_iterator constBegin() const const
The implementation independent part of the MessageList library.
Definition: aggregation.h:21
const QLatin1String name
AlignVCenter
KSharedConfigPtr config()
const QString & name() const
Returns the name of this OptionSet.
Definition: optionset.h:59
int count(const T &value) const const
QString i18nc(const char *context, const char *text, const TYPE &arg...)
bool isEmpty() const const
bool isEmpty() const const
void clicked(bool checked)
QMap::const_iterator constEnd() const const
void setShortcut(const QKeySequence &key)
void setName(const QString &name)
Sets the name of this OptionSet.
Definition: optionset.h:69
WA_DeleteOnClose
QString i18n(const char *text, const TYPE &arg...)
const QList< QKeySequence > & end()
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const const
QString getSaveFileName(QWidget *parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options)
QIcon fromTheme(const QString &name)
QString getOpenFileName(QWidget *parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options)
void setDefault(bool)
void setSelected(bool select)
Key_Return
KIOFILEWIDGETS_EXPORT QStringList list(const QString &fileClass)
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Sat Dec 4 2021 23:12:52 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.