configurationdialog.cpp
1 //------------------------------------------------------------------------------
2 // configurationdialog.cpp
3 //------------------------------------------------------------------------------
4 //
5 // This library is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU Lesser General Public
7 // License as published by the Free Software Foundation; either
8 // version 2.1 of the License, or (at your option) any later version.
9 //
10 // This library is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 // Lesser General Public License for more details.
14 //
15 // You should have received a copy of the GNU Lesser General Public
16 // License along with this library; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18 // 02110-1301 USA
19 //
20 //------------------------------------------------------------------------------
21 // Copyright (C) 2009 "Zalewa" <zalewapl@gmail.com>
22 //------------------------------------------------------------------------------
23 #include "configurationdialog.h"
24 #include "ui_configurationdialog.h"
25 
26 #include "configpage.h"
27 #include "qtmetapointer.h"
28 #include <cassert>
29 #include <QAbstractButton>
30 #include <QDebug>
31 #include <QKeyEvent>
32 #include <QStandardItem>
33 #include <QStandardItemModel>
34 #include <Qt>
35 #include <QTreeView>
36 
37 DClass<ConfigurationDialog> : public Ui::ConfigurationDialog
38 {
39 public:
40  enum Column
41  {
42  COL_FIRST = 0,
43  COL_META = 0,
44  COL_NAME = 0,
45  COL_VALIDATION = 1,
46  COLSIZE,
47  };
48 
49  QList<ConfigPage *> configPages;
50  ConfigPage *currentlyDisplayedPage;
51 
52  QModelIndex findPageModelIndex(const QModelIndex &rootIndex, ConfigPage *page)
53  {
54  auto model = static_cast<QStandardItemModel *>(tvOptionsList->model());
55  for (int row = 0; row < model->rowCount(rootIndex); ++row)
56  {
57  QModelIndex index = model->index(row, COL_META, rootIndex);
58  ConfigPage *pageAtIndex = pageFromIndex(index);
59  if (pageAtIndex == page)
60  return index;
61  QModelIndex childIndex = findPageModelIndex(index, page);
62  if (childIndex.isValid())
63  return childIndex;
64  }
65  return QModelIndex();
66  }
67 
68  ConfigPage *pageFromIndex(const QModelIndex &index)
69  {
70  QModelIndex pageIndex = index.sibling(index.row(), COL_META);
71  QtMetaPointer metaPointer = pageIndex.data(Qt::UserRole).value<QtMetaPointer>();
72  void *pointer = metaPointer;
73  return static_cast<ConfigPage *>(pointer);
74  }
75 
76  QIcon validationIcon(ConfigPage::Validation validation) const
77  {
78  switch (validation)
79  {
81  return QIcon(":icons/exclamation_16.png");
82  default:
83  return QIcon();
84  }
85  }
86 };
87 
88 DPointered(ConfigurationDialog)
89 
91  : QDialog(parent)
92 {
93  d->setupUi(this);
94 
95  auto model = new QStandardItemModel(this);
97  column < PrivData<ConfigurationDialog>::COLSIZE; ++column)
98  {
99  model->setHorizontalHeaderItem(column, new QStandardItem());
100  }
101  d->tvOptionsList->setModel(model);
102 
103  d->tvOptionsList->header()->setSectionResizeMode(
104  PrivData<ConfigurationDialog>::COL_NAME, QHeaderView::Stretch);
105  d->tvOptionsList->header()->setSectionResizeMode(
107 
108  d->currentlyDisplayedPage = nullptr;
109  connect(d->buttonBox, SIGNAL(clicked(QAbstractButton*)), this, SLOT (btnClicked(QAbstractButton*)));
110  this->connect(d->tvOptionsList->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)),
111  SLOT(switchToItem(QModelIndex,QModelIndex)));
112 }
113 
114 ConfigurationDialog::~ConfigurationDialog()
115 {
116  for (int i = 0; i < d->configPages.count(); ++i)
117  delete d->configPages[i];
118 }
119 
121  QStandardItem *rootItem, ConfigPage *configPage, int position)
122 {
123  if (!canConfigPageBeAdded(configPage))
124  return nullptr;
125 
126  auto pModel = (QStandardItemModel *)d->tvOptionsList->model();
127  if (rootItem == nullptr)
128  rootItem = pModel->invisibleRootItem();
129 
130  if (!this->hasItemOnList(rootItem))
131  return nullptr;
132 
133  QList<QStandardItem *> row;
134 
135  QStandardItem *nameItem = new QStandardItem(configPage->name());
136  nameItem->setIcon(configPage->icon());
137 
138  row.insert(PrivData<ConfigurationDialog>::COL_NAME, nameItem);
139 
140  QtMetaPointer metaPointer = configPage;
141  nameItem->setData(qVariantFromValue(metaPointer), Qt::UserRole);
142 
143  row.insert(PrivData<ConfigurationDialog>::COL_VALIDATION, new QStandardItem());
144 
145  if (position < 0)
146  rootItem->appendRow(row);
147  else
148  rootItem->insertRow(position, row);
149 
150  d->configPages << configPage;
151  this->connect(configPage, SIGNAL(validationRequested()),
152  SLOT(onPageValidationRequested()));
153 
154  if (!configPage->areSettingsAlreadyRead())
155  configPage->read();
156  validatePage(configPage);
157 
158  return nameItem;
159 }
160 
161 QStandardItem *ConfigurationDialog::addLabel(QStandardItem *rootItem, const QString &label, int position)
162 {
163  auto pModel = (QStandardItemModel *)d->tvOptionsList->model();
164  if (rootItem == nullptr)
165  rootItem = pModel->invisibleRootItem();
166 
167  if (!this->hasItemOnList(rootItem))
168  return nullptr;
169 
170  QtMetaPointer metaPointer = (void *)nullptr;
171  QVariant variantMetaPointer = qVariantFromValue(metaPointer);
172 
173  QList<QStandardItem *> row;
174 
175  auto nameItem = new QStandardItem(label);
176  nameItem->setData(variantMetaPointer, Qt::UserRole);
177  row.insert(PrivData<ConfigurationDialog>::COL_NAME, nameItem);
178  row.insert(PrivData<ConfigurationDialog>::COL_VALIDATION, new QStandardItem());
179 
180  if (position < 0)
181  rootItem->appendRow(row);
182  else
183  rootItem->insertRow(position, row);
184 
185  return nameItem;
186 }
187 
188 void ConfigurationDialog::btnClicked(QAbstractButton *button)
189 {
190  // Figure out what button we pressed and perform its action.
191  switch (d->buttonBox->standardButton(button))
192  {
193  default:
194  break;
195 
196  case QDialogButtonBox::Ok: // Also does the same as Apply
197  if (this->validate())
198  {
199  this->accept();
200  this->saveSettings();
201  }
202  break;
203 
204  case QDialogButtonBox::Apply:
205  if (this->validate())
206  this->saveSettings();
207  break;
208 
209  case QDialogButtonBox::Cancel:
210  this->reject();
211  break;
212  }
213 }
214 
215 bool ConfigurationDialog::canConfigPageBeAdded(ConfigPage *configPage)
216 {
217  return isConfigPageValid(configPage) && !hasConfigPage(configPage);
218 }
219 
220 QModelIndex ConfigurationDialog::findPageModelIndex(ConfigPage *page)
221 {
222  auto model = static_cast<QStandardItemModel *>(d->tvOptionsList->model());
223  return d->findPageModelIndex(model->indexFromItem(model->invisibleRootItem()), page);
224 }
225 
226 bool ConfigurationDialog::isConfigPageValid(ConfigPage *configPage)
227 {
228  return configPage != nullptr && !configPage->name().isEmpty();
229 }
230 
231 bool ConfigurationDialog::hasConfigPage(ConfigPage *configPage)
232 {
233  for (ConfigPage *addedPage : d->configPages)
234  {
235  if (configPage == addedPage)
236  return true;
237  }
238 
239  return false;
240 }
241 
242 void ConfigurationDialog::keyPressEvent(QKeyEvent *e)
243 {
244  switch (e->key())
245  {
246  case Qt::Key_Enter:
247  case Qt::Key_Return:
248  // Suppress the dialog being accepted on pressing ENTER key.
249  // Dialog would close even in line edits that had "returnPressed()"
250  // signals connected. That wasn't good.
251  e->ignore();
252  break;
253  default:
254  QDialog::keyPressEvent(e);
255  }
256 }
257 
258 bool ConfigurationDialog::hasItemOnList(QStandardItem *pItem) const
259 {
260  if (pItem == nullptr)
261  return false;
262 
263  auto pModel = (QStandardItemModel *)d->tvOptionsList->model();
264 
265  // Calling index methods on the invisible root items will always return
266  // invalid values.
267 
268  return pModel->invisibleRootItem() == pItem
269  || pModel->indexFromItem(pItem).isValid();
270 }
271 
272 void ConfigurationDialog::onPageValidationRequested()
273 {
274  auto page = qobject_cast<ConfigPage *>(sender());
275  assert(page != nullptr);
276  validatePage(page);
277 }
278 
279 void ConfigurationDialog::reject()
280 {
281  for (ConfigPage *page : d->configPages)
282  {
283  page->reject();
284  }
285  QDialog::reject();
286 }
287 
288 void ConfigurationDialog::switchToItem(const QModelIndex &current, const QModelIndex &previous)
289 {
290  if (current.isValid() && current != previous)
291  {
292  ConfigPage *configPage = d->pageFromIndex(current);
293 
294  // Something with sense was selected, display this something
295  // and hide previous box.
296  if (isConfigPageValid(configPage))
297  showConfigPage(configPage);
298  }
299 }
300 
302 {
303  return d->tvOptionsList;
304 }
305 
306 void ConfigurationDialog::saveSettings()
307 {
308  // Iterate through every engine and execute it's saving method
309  for (int i = 0; i < d->configPages.count(); ++i)
310  d->configPages[i]->save();
311 
312  doSaveSettings();
313 
314  if (isVisible())
315  {
316  // Allow panels such as the one for Wadseeker update their contents.
317  for (int i = 0; i < d->configPages.count(); ++i)
318  {
319  d->configPages[i]->read();
320  validatePage(d->configPages[i]);
321  }
322  }
323 }
324 
326 {
327  if (d->currentlyDisplayedPage != nullptr)
328  {
329  validatePage(d->currentlyDisplayedPage);
330  d->currentlyDisplayedPage->hide();
331  d->mainPanel->setTitle(QString());
332  }
333  d->currentlyDisplayedPage = page;
334 
335  if (page != nullptr)
336  {
337  page->setAllowSave(true);
338  validatePage(page);
339  d->mainPanel->layout()->addWidget(page);
340  d->mainPanel->setTitle(page->title());
341  page->show();
342  }
343 }
344 
345 void ConfigurationDialog::validatePage(ConfigPage *page)
346 {
347  assert(page != nullptr);
348  QModelIndex pageIndex = findPageModelIndex(page);
349  assert(pageIndex.isValid());
350 
351  QModelIndex validationIndex = pageIndex.sibling(pageIndex.row(), PrivData<ConfigurationDialog>::COL_VALIDATION);
352  assert(validationIndex.isValid());
353 
354  auto model = static_cast<QStandardItemModel *>(d->tvOptionsList->model());
355  QStandardItem *validationItem = model->itemFromIndex(validationIndex);
356  validationItem->setIcon(d->validationIcon(page->validate()));
357 }
Validation
Result of validate()
Definition: configpage.h:50
virtual QString name() const =0
Reimplement this to return a list-displayable name for this ConfigPage.
QTreeView * optionsTree()
Returns pointer to the tree widget that contains configuration sections list.
virtual QString title() const
Page title, by default returns the same string as name().
Definition: configpage.cpp:88
virtual QStandardItem * addConfigPage(QStandardItem *rootItem, ConfigPage *configPage, int position=-1)
Adds a new configuration page to the options tree view.
void read()
Read configuration from persistence to page contents.
Definition: configpage.cpp:66
bool areSettingsAlreadyRead()
true if settings for this page have already been loaded at least once.
Definition: configpage.cpp:56
void reject()
QStandardItem * addLabel(QStandardItem *rootItem, const QString &label, int position=-1)
Adds a label node to the options tree view.
void setAllowSave(bool b)
Change whether settings on this page can be stored in persisting configuration.
Definition: configpage.cpp:83
Definition: dptr.h:31
virtual Validation validate()
Validate settings on this page.
Definition: configpage.cpp:93
Validation detected at least one problem.
Definition: configpage.h:55
virtual QIcon icon() const =0
Reimplement this to return a displayable icon for the ConfigPage.
Base class for configuration pages.
Definition: configpage.h:44
void showConfigPage(ConfigPage *widget)