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  connect(d->mAggregationList, &AggregationListWidget::currentItemChanged, this, [this](QListWidgetItem *item) {
118  d->aggregationListItemClicked(item);
119  });
120 
121  d->mNewAggregationButton = new QPushButton(i18n("New Aggregation"), base);
122  d->mNewAggregationButton->setIcon(QIcon::fromTheme(QStringLiteral("document-new")));
123  g->addWidget(d->mNewAggregationButton, 0, 1);
124 
125  connect(d->mNewAggregationButton, &QPushButton::clicked, this, [this]() {
126  d->newAggregationButtonClicked();
127  });
128 
129  d->mCloneAggregationButton = new QPushButton(i18n("Clone Aggregation"), base);
130  d->mCloneAggregationButton->setIcon(QIcon::fromTheme(QStringLiteral("edit-copy")));
131  g->addWidget(d->mCloneAggregationButton, 1, 1);
132 
133  connect(d->mCloneAggregationButton, &QPushButton::clicked, this, [this]() {
134  d->cloneAggregationButtonClicked();
135  });
136 
137  auto f = new QFrame(base);
138  f->setFrameStyle(QFrame::Sunken | QFrame::HLine);
139  f->setMinimumHeight(24);
140  g->addWidget(f, 2, 1, Qt::AlignVCenter);
141 
142  d->mExportAggregationButton = new QPushButton(i18n("Export Aggregation..."), base);
143  g->addWidget(d->mExportAggregationButton, 3, 1);
144 
145  connect(d->mExportAggregationButton, &QPushButton::clicked, this, [this]() {
146  d->exportAggregationButtonClicked();
147  });
148 
149  d->mImportAggregationButton = new QPushButton(i18n("Import Aggregation..."), base);
150  g->addWidget(d->mImportAggregationButton, 4, 1);
151  connect(d->mImportAggregationButton, &QPushButton::clicked, this, [this]() {
152  d->importAggregationButtonClicked();
153  });
154 
155  f = new QFrame(base);
156  f->setFrameStyle(QFrame::Sunken | QFrame::HLine);
157  f->setMinimumHeight(24);
158  g->addWidget(f, 5, 1, Qt::AlignVCenter);
159 
160  d->mDeleteAggregationButton = new QPushButton(i18n("Delete Aggregation"), base);
161  d->mDeleteAggregationButton->setIcon(QIcon::fromTheme(QStringLiteral("edit-delete")));
162  g->addWidget(d->mDeleteAggregationButton, 6, 1);
163 
164  connect(d->mDeleteAggregationButton, &QPushButton::clicked, this, [this]() {
165  d->deleteAggregationButtonClicked();
166  });
167 
168  d->mEditor = new AggregationEditor(base);
169  g->addWidget(d->mEditor, 8, 0, 1, 2);
170 
171  connect(d->mEditor, &AggregationEditor::aggregationNameChanged, this, [this]() {
172  d->editedAggregationNameChanged();
173  });
174 
175  g->setColumnStretch(0, 1);
176  g->setRowStretch(7, 1);
177 
178  connect(okButton, &QPushButton::clicked, this, [this]() {
179  d->okButtonClicked();
180  });
181 
182  d->fillAggregationList();
183 }
184 
185 ConfigureAggregationsDialog::~ConfigureAggregationsDialog() = default;
186 
187 void ConfigureAggregationsDialog::selectAggregation(const QString &aggregationId)
188 {
189  AggregationListWidgetItem *item = d->findAggregationItemById(aggregationId);
190  if (item) {
191  d->mAggregationList->setCurrentItem(item);
192  d->aggregationListItemClicked(item);
193  }
194 }
195 
196 void ConfigureAggregationsDialog::ConfigureAggregationsDialogPrivate::okButtonClicked()
197 {
198  if (Manager::instance()) {
199  commitEditor();
200 
201  Manager::instance()->removeAllAggregations();
202 
203  const int c = mAggregationList->count();
204  int i = 0;
205  while (i < c) {
206  auto item = dynamic_cast<AggregationListWidgetItem *>(mAggregationList->item(i));
207  if (item) {
208  Manager::instance()->addAggregation(item->aggregation());
209  item->forgetAggregation();
210  }
211  ++i;
212  }
213 
214  Manager::instance()->aggregationsConfigurationCompleted();
215  }
216  Q_EMIT q->okClicked();
217  q->accept(); // this will delete too
218 }
219 
220 void ConfigureAggregationsDialog::ConfigureAggregationsDialogPrivate::commitEditor()
221 {
222  Aggregation *editedAggregation = mEditor->editedAggregation();
223  if (!editedAggregation) {
224  return;
225  }
226 
227  mEditor->commit();
228 
229  AggregationListWidgetItem *editedItem = findAggregationItemByAggregation(editedAggregation);
230  if (!editedItem) {
231  return;
232  }
233  const QString goodName = uniqueNameForAggregation(editedAggregation->name(), editedAggregation);
234  editedAggregation->setName(goodName);
235  editedItem->setText(goodName);
236 }
237 
238 void ConfigureAggregationsDialog::ConfigureAggregationsDialogPrivate::editedAggregationNameChanged()
239 {
240  Aggregation *set = mEditor->editedAggregation();
241  if (!set) {
242  return;
243  }
244 
245  AggregationListWidgetItem *it = findAggregationItemByAggregation(set);
246  if (!it) {
247  return;
248  }
249 
250  const QString goodName = uniqueNameForAggregation(set->name(), set);
251 
252  it->setText(goodName);
253 }
254 
255 void ConfigureAggregationsDialog::ConfigureAggregationsDialogPrivate::fillAggregationList()
256 {
257  if (!Manager::instance()) {
258  return;
259  }
260  const QMap<QString, Aggregation *> &sets = Manager::instance()->aggregations();
262  for (QMap<QString, Aggregation *>::ConstIterator it = sets.constBegin(); it != end; ++it) {
263  (void)new AggregationListWidgetItem(mAggregationList, *(*it));
264  }
265 }
266 
267 void ConfigureAggregationsDialog::ConfigureAggregationsDialogPrivate::aggregationListItemClicked(QListWidgetItem *cur)
268 {
269  commitEditor();
270  updateButton(cur);
271 }
272 
273 void ConfigureAggregationsDialog::ConfigureAggregationsDialogPrivate::updateButton(QListWidgetItem *cur)
274 {
275  const int numberOfSelectedItem(mAggregationList->selectedItems().count());
276 
277  AggregationListWidgetItem *item = cur ? dynamic_cast<AggregationListWidgetItem *>(cur) : nullptr;
278  mDeleteAggregationButton->setEnabled(item && !item->aggregation()->readOnly() && (mAggregationList->count() > 1));
279 
280  mCloneAggregationButton->setEnabled(numberOfSelectedItem == 1);
281  mExportAggregationButton->setEnabled(numberOfSelectedItem > 0);
282  mEditor->editAggregation(item ? item->aggregation() : nullptr);
283  if (item && !item->isSelected()) {
284  item->setSelected(true); // make sure it's true
285  }
286 }
287 
288 AggregationListWidgetItem *ConfigureAggregationsDialog::ConfigureAggregationsDialogPrivate::findAggregationItemByName(const QString &name,
289  Aggregation *skipAggregation)
290 {
291  const int c = mAggregationList->count();
292  int i = 0;
293  while (i < c) {
294  auto item = dynamic_cast<AggregationListWidgetItem *>(mAggregationList->item(i));
295  if (item) {
296  if (item->aggregation() != skipAggregation) {
297  if (item->aggregation()->name() == name) {
298  return item;
299  }
300  }
301  }
302  ++i;
303  }
304  return nullptr;
305 }
306 
307 AggregationListWidgetItem *ConfigureAggregationsDialog::ConfigureAggregationsDialogPrivate::findAggregationItemById(const QString &aggregationId)
308 {
309  const int c = mAggregationList->count();
310  int i = 0;
311  while (i < c) {
312  auto item = dynamic_cast<AggregationListWidgetItem *>(mAggregationList->item(i));
313  if (item) {
314  if (item->aggregation()->id() == aggregationId) {
315  return item;
316  }
317  }
318  ++i;
319  }
320  return nullptr;
321 }
322 
323 AggregationListWidgetItem *ConfigureAggregationsDialog::ConfigureAggregationsDialogPrivate::findAggregationItemByAggregation(Aggregation *set)
324 {
325  const int c = mAggregationList->count();
326  int i = 0;
327  while (i < c) {
328  auto item = dynamic_cast<AggregationListWidgetItem *>(mAggregationList->item(i));
329  if (item) {
330  if (item->aggregation() == set) {
331  return item;
332  }
333  }
334  ++i;
335  }
336  return nullptr;
337 }
338 
339 QString ConfigureAggregationsDialog::ConfigureAggregationsDialogPrivate::uniqueNameForAggregation(const QString &baseName, Aggregation *skipAggregation)
340 {
341  QString ret = baseName;
342  if (ret.isEmpty()) {
343  ret = i18n("Unnamed Aggregation");
344  }
345 
346  int idx = 1;
347 
348  AggregationListWidgetItem *item = findAggregationItemByName(ret, skipAggregation);
349  while (item) {
350  idx++;
351  ret = QStringLiteral("%1 %2").arg(baseName).arg(idx);
352  item = findAggregationItemByName(ret, skipAggregation);
353  }
354  return ret;
355 }
356 
357 void ConfigureAggregationsDialog::ConfigureAggregationsDialogPrivate::newAggregationButtonClicked()
358 {
359  Aggregation emptyAggregation;
360  emptyAggregation.setName(uniqueNameForAggregation(i18n("New Aggregation")));
361  auto item = new AggregationListWidgetItem(mAggregationList, emptyAggregation);
362 
363  mAggregationList->clearSelection();
364  mAggregationList->setCurrentItem(item);
365  mDeleteAggregationButton->setEnabled(item && !item->aggregation()->readOnly());
366 }
367 
368 void ConfigureAggregationsDialog::ConfigureAggregationsDialogPrivate::cloneAggregationButtonClicked()
369 {
370  auto item = dynamic_cast<AggregationListWidgetItem *>(mAggregationList->currentItem());
371  if (!item) {
372  return;
373  }
374  commitEditor();
375  item->setSelected(false);
376  Aggregation copyAggregation(*(item->aggregation()));
377  copyAggregation.setReadOnly(false);
378  copyAggregation.generateUniqueId(); // regenerate id so it becomes different
379  copyAggregation.setName(uniqueNameForAggregation(item->aggregation()->name()));
380  item = new AggregationListWidgetItem(mAggregationList, copyAggregation);
381 
382  mAggregationList->setCurrentItem(item);
383  aggregationListItemClicked(item);
384 }
385 
386 void ConfigureAggregationsDialog::ConfigureAggregationsDialogPrivate::deleteAggregationButtonClicked()
387 {
388  const QList<QListWidgetItem *> list = mAggregationList->selectedItems();
389  if (list.isEmpty()) {
390  return;
391  }
392 
393  mEditor->editAggregation(nullptr); // forget it
394  for (QListWidgetItem *it : list) {
395  auto item = dynamic_cast<AggregationListWidgetItem *>(it);
396  if (!item) {
397  return;
398  }
399  if (!item->aggregation()->readOnly()) {
400  delete item; // this will trigger aggregationListCurrentItemChanged()
401  }
402  if (mAggregationList->count() < 2) {
403  break; // no way: desperately try to keep at least one option set alive :)
404  }
405  }
406 
407  auto newItem = dynamic_cast<AggregationListWidgetItem *>(mAggregationList->currentItem());
408  updateButton(newItem);
409 }
410 
411 void ConfigureAggregationsDialog::ConfigureAggregationsDialogPrivate::importAggregationButtonClicked()
412 {
413  const QString filename = QFileDialog::getOpenFileName(q, i18n("Import Aggregation"));
414  if (!filename.isEmpty()) {
415  KConfig config(filename);
416 
417  if (config.hasGroup(QStringLiteral("MessageListView::Aggregations"))) {
418  KConfigGroup grp(&config, QStringLiteral("MessageListView::Aggregations"));
419  const int cnt = grp.readEntry("Count", 0);
420  int idx = 0;
421  while (idx < cnt) {
422  const QString data = grp.readEntry(QStringLiteral("Set%1").arg(idx), QString());
423  if (!data.isEmpty()) {
424  auto set = new Aggregation();
425  if (set->loadFromString(data)) {
426  set->setReadOnly(false);
427  set->generateUniqueId(); // regenerate id so it becomes different
428  set->setName(uniqueNameForAggregation(set->name()));
429  (void)new AggregationListWidgetItem(mAggregationList, *set);
430  } else {
431  delete set; // b0rken
432  }
433  }
434  ++idx;
435  }
436  }
437  }
438 }
439 
440 void ConfigureAggregationsDialog::ConfigureAggregationsDialogPrivate::exportAggregationButtonClicked()
441 {
442  const QList<QListWidgetItem *> list = mAggregationList->selectedItems();
443  if (list.isEmpty()) {
444  return;
445  }
446  const QString filename = QFileDialog::getSaveFileName(q, i18n("Export Aggregation"), QString(), i18n("All Files (*)"));
447  if (!filename.isEmpty()) {
448  KConfig config(filename);
449 
450  KConfigGroup grp(&config, QStringLiteral("MessageListView::Aggregations"));
451  grp.writeEntry("Count", list.count());
452 
453  int idx = 0;
454  for (QListWidgetItem *item : list) {
455  auto themeItem = static_cast<AggregationListWidgetItem *>(item);
456  grp.writeEntry(QStringLiteral("Set%1").arg(idx), themeItem->aggregation()->saveToString());
457  ++idx;
458  }
459  }
460 }
461 
462 #include "moc_configureaggregationsdialog.cpp"
QMap::const_iterator constBegin() const const
AlignVCenter
void setShortcut(const QKeySequence &key)
void setName(const QString &name)
Sets the name of this OptionSet.
Definition: optionset.h:69
int count(const T &value) const const
void clicked(bool checked)
QIcon fromTheme(const QString &name)
KIOFILEWIDGETS_EXPORT QStringList list(const QString &fileClass)
const QString & name() const
Returns the name of this OptionSet.
Definition: optionset.h:59
void generateUniqueId()
(Re)generates a (hopefully) unique identifier for this option set.
Definition: optionset.cpp:40
QString i18n(const char *text, const TYPE &arg...)
QMap::const_iterator constEnd() const const
bool isEmpty() const const
A widget that allows editing a single MessageList::Aggregation.
bool isEmpty() const const
Key_Return
KSharedConfigPtr config()
QString getSaveFileName(QWidget *parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options)
QString getOpenFileName(QWidget *parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options)
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const const
QString name(StandardShortcut id)
QString i18nc(const char *context, const char *text, const TYPE &arg...)
void setDefault(bool)
A set of aggregation options that can be applied to the MessageList::Model in a single shot.
Definition: aggregation.h:28
WA_DeleteOnClose
const QList< QKeySequence > & end()
bool loadFromString(const QString &data)
Attempts to unpack this configuration object from a string (that is likely to come out from a config ...
Definition: optionset.cpp:69
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Sun Mar 26 2023 04:08:10 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.