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 #include "gui/commongui.h"
26 
27 #include "configpage.h"
28 #include "qtmetapointer.h"
29 #include <cassert>
30 #include <QAbstractButton>
31 #include <QDebug>
32 #include <QKeyEvent>
33 #include <QStandardItem>
34 #include <QStandardItemModel>
35 #include <Qt>
36 #include <QTreeView>
37 
38 DClass<ConfigurationDialog> : public Ui::ConfigurationDialog
39 {
40 public:
41  enum Column
42  {
43  COL_FIRST = 0,
44  COL_META = 0,
45  COL_NAME = 0,
46  COL_VALIDATION = 1,
47  COLSIZE,
48  };
49 
50  QList<ConfigPage *> configPages;
51  ConfigPage *currentlyDisplayedPage;
52 
53  QModelIndex findPageModelIndex(const QModelIndex &rootIndex, ConfigPage *page)
54  {
55  auto model = static_cast<QStandardItemModel *>(tvOptionsList->model());
56  for (int row = 0; row < model->rowCount(rootIndex); ++row)
57  {
58  QModelIndex index = model->index(row, COL_META, rootIndex);
59  ConfigPage *pageAtIndex = pageFromIndex(index);
60  if (pageAtIndex == page)
61  return index;
62  QModelIndex childIndex = findPageModelIndex(index, page);
63  if (childIndex.isValid())
64  return childIndex;
65  }
66  return QModelIndex();
67  }
68 
69  ConfigPage *pageFromIndex(const QModelIndex &index)
70  {
71  QModelIndex pageIndex = index.sibling(index.row(), COL_META);
72  QtMetaPointer metaPointer = pageIndex.data(Qt::UserRole).value<QtMetaPointer>();
73  void *pointer = metaPointer;
74  return static_cast<ConfigPage *>(pointer);
75  }
76 
77  QIcon validationIcon(ConfigPage::Validation validation) const
78  {
79  switch (validation)
80  {
82  return QIcon(":icons/exclamation_16.png");
83  default:
84  return QIcon();
85  }
86  }
87 };
88 
89 DPointered(ConfigurationDialog)
90 
92  : QDialog(parent)
93 {
94  d->setupUi(this);
96 
97  auto model = new QStandardItemModel(this);
99  column < PrivData<ConfigurationDialog>::COLSIZE; ++column)
100  {
101  model->setHorizontalHeaderItem(column, new QStandardItem());
102  }
103  d->tvOptionsList->setModel(model);
104 
105  d->tvOptionsList->header()->setSectionResizeMode(
106  PrivData<ConfigurationDialog>::COL_NAME, QHeaderView::Stretch);
107  d->tvOptionsList->header()->setSectionResizeMode(
108  PrivData<ConfigurationDialog>::COL_VALIDATION, QHeaderView::ResizeToContents);
109 
110  d->currentlyDisplayedPage = nullptr;
111  connect(d->buttonBox, SIGNAL(clicked(QAbstractButton*)), this, SLOT (btnClicked(QAbstractButton*)));
112  this->connect(d->tvOptionsList->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)),
113  SLOT(switchToItem(QModelIndex,QModelIndex)));
114 }
115 
116 ConfigurationDialog::~ConfigurationDialog()
117 {
118  for (int i = 0; i < d->configPages.count(); ++i)
119  delete d->configPages[i];
120 }
121 
123  QStandardItem *rootItem, ConfigPage *configPage, int position)
124 {
125  if (!canConfigPageBeAdded(configPage))
126  return nullptr;
127 
128  auto pModel = (QStandardItemModel *)d->tvOptionsList->model();
129  if (rootItem == nullptr)
130  rootItem = pModel->invisibleRootItem();
131 
132  if (!this->hasItemOnList(rootItem))
133  return nullptr;
134 
135  QList<QStandardItem *> row;
136 
137  QStandardItem *nameItem = new QStandardItem(configPage->name());
138  nameItem->setIcon(configPage->icon());
139  nameItem->setToolTip(configPage->title());
140 
141  row.insert(PrivData<ConfigurationDialog>::COL_NAME, nameItem);
142 
143  QtMetaPointer metaPointer = configPage;
144  nameItem->setData(QVariant::fromValue(metaPointer), Qt::UserRole);
145 
146  row.insert(PrivData<ConfigurationDialog>::COL_VALIDATION, new QStandardItem());
147 
148  if (position < 0)
149  rootItem->appendRow(row);
150  else
151  rootItem->insertRow(position, row);
152 
153  d->configPages << configPage;
154  this->connect(configPage, SIGNAL(validationRequested()),
155  SLOT(onPageValidationRequested()));
156 
157  if (!configPage->areSettingsAlreadyRead())
158  configPage->read();
159  validatePage(configPage);
160 
161  return nameItem;
162 }
163 
164 QStandardItem *ConfigurationDialog::addLabel(QStandardItem *rootItem, const QString &label, int position)
165 {
166  auto pModel = (QStandardItemModel *)d->tvOptionsList->model();
167  if (rootItem == nullptr)
168  rootItem = pModel->invisibleRootItem();
169 
170  if (!this->hasItemOnList(rootItem))
171  return nullptr;
172 
173  QtMetaPointer metaPointer = (void *)nullptr;
174  QVariant variantMetaPointer = QVariant::fromValue(metaPointer);
175 
176  QList<QStandardItem *> row;
177 
178  auto nameItem = new QStandardItem(label);
179  nameItem->setData(variantMetaPointer, Qt::UserRole);
180  row.insert(PrivData<ConfigurationDialog>::COL_NAME, nameItem);
181  row.insert(PrivData<ConfigurationDialog>::COL_VALIDATION, new QStandardItem());
182 
183  if (position < 0)
184  rootItem->appendRow(row);
185  else
186  rootItem->insertRow(position, row);
187 
188  return nameItem;
189 }
190 
191 void ConfigurationDialog::btnClicked(QAbstractButton *button)
192 {
193  // Figure out what button we pressed and perform its action.
194  switch (d->buttonBox->standardButton(button))
195  {
196  default:
197  break;
198 
199  case QDialogButtonBox::Ok: // Also does the same as Apply
200  if (this->validate())
201  {
202  this->accept();
203  this->saveSettings();
204  }
205  break;
206 
207  case QDialogButtonBox::Apply:
208  if (this->validate())
209  this->saveSettings();
210  break;
211 
212  case QDialogButtonBox::Cancel:
213  this->reject();
214  break;
215  }
216 }
217 
218 bool ConfigurationDialog::canConfigPageBeAdded(ConfigPage *configPage)
219 {
220  return isConfigPageValid(configPage) && !hasConfigPage(configPage);
221 }
222 
223 QModelIndex ConfigurationDialog::findPageModelIndex(ConfigPage *page)
224 {
225  auto model = static_cast<QStandardItemModel *>(d->tvOptionsList->model());
226  return d->findPageModelIndex(model->indexFromItem(model->invisibleRootItem()), page);
227 }
228 
229 bool ConfigurationDialog::isConfigPageValid(ConfigPage *configPage)
230 {
231  return configPage != nullptr && !configPage->name().isEmpty();
232 }
233 
234 bool ConfigurationDialog::hasConfigPage(ConfigPage *configPage)
235 {
236  for (ConfigPage *addedPage : d->configPages)
237  {
238  if (configPage == addedPage)
239  return true;
240  }
241 
242  return false;
243 }
244 
245 void ConfigurationDialog::keyPressEvent(QKeyEvent *e)
246 {
247  switch (e->key())
248  {
249  case Qt::Key_Enter:
250  case Qt::Key_Return:
251  // Suppress the dialog being accepted on pressing ENTER key.
252  // Dialog would close even in line edits that had "returnPressed()"
253  // signals connected. That wasn't good.
254  e->ignore();
255  break;
256  default:
257  QDialog::keyPressEvent(e);
258  }
259 }
260 
261 bool ConfigurationDialog::hasItemOnList(QStandardItem *pItem) const
262 {
263  if (pItem == nullptr)
264  return false;
265 
266  auto pModel = (QStandardItemModel *)d->tvOptionsList->model();
267 
268  // Calling index methods on the invisible root items will always return
269  // invalid values.
270 
271  return pModel->invisibleRootItem() == pItem
272  || pModel->indexFromItem(pItem).isValid();
273 }
274 
275 void ConfigurationDialog::onPageValidationRequested()
276 {
277  auto page = qobject_cast<ConfigPage *>(sender());
278  assert(page != nullptr);
279  validatePage(page);
280 }
281 
282 void ConfigurationDialog::reject()
283 {
284  for (ConfigPage *page : d->configPages)
285  {
286  page->reject();
287  }
288  QDialog::reject();
289 }
290 
291 void ConfigurationDialog::switchToItem(const QModelIndex &current, const QModelIndex &previous)
292 {
293  if (current.isValid() && current != previous)
294  {
295  ConfigPage *configPage = d->pageFromIndex(current);
296  if (isConfigPageValid(configPage))
297  showConfigPage(configPage);
298  else
299  showConfigPage(nullptr);
300  }
301 }
302 
304 {
305  return d->tvOptionsList;
306 }
307 
308 void ConfigurationDialog::saveSettings()
309 {
310  // Iterate through every engine and execute it's saving method
311  for (int i = 0; i < d->configPages.count(); ++i)
312  d->configPages[i]->save();
313 
314  doSaveSettings();
315 
316  if (isVisible())
317  {
318  // Allow panels such as the one for Wadseeker update their contents.
319  for (int i = 0; i < d->configPages.count(); ++i)
320  {
321  d->configPages[i]->read();
322  validatePage(d->configPages[i]);
323  }
324  }
325 }
326 
328 {
329  if (d->currentlyDisplayedPage != nullptr)
330  {
331  validatePage(d->currentlyDisplayedPage);
332  d->currentlyDisplayedPage->hide();
333  d->mainPanel->setTitle(QString());
334  }
335  d->currentlyDisplayedPage = page;
336 
337  if (page != nullptr)
338  {
339  page->setAllowSave(true);
340  validatePage(page);
341  d->mainPanel->layout()->addWidget(page);
342  d->mainPanel->setTitle(page->title());
343  page->show();
344  }
345 }
346 
347 void ConfigurationDialog::validatePage(ConfigPage *page)
348 {
349  assert(page != nullptr);
350  QModelIndex pageIndex = findPageModelIndex(page);
351  assert(pageIndex.isValid());
352 
353  QModelIndex validationIndex = pageIndex.sibling(pageIndex.row(), PrivData<ConfigurationDialog>::COL_VALIDATION);
354  assert(validationIndex.isValid());
355 
356  auto model = static_cast<QStandardItemModel *>(d->tvOptionsList->model());
357  QStandardItem *validationItem = model->itemFromIndex(validationIndex);
358  validationItem->setIcon(d->validationIcon(page->validate()));
359 }