configurationdialog.cpp
1 //------------------------------------------------------------------------------
2 // configurationdialog.cpp
3 //------------------------------------------------------------------------------
4 //
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (at your option) any later version.
9 //
10 // This program 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
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program; 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 <Qt>
30 #include <QDebug>
31 #include <QKeyEvent>
32 #include <QStandardItemModel>
33 #include <QStandardItem>
34 #include <QTreeView>
35 #include <QAbstractButton>
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  QStandardItemModel *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  {
61  return index;
62  }
63  QModelIndex childIndex = findPageModelIndex(index, page);
64  if (childIndex.isValid())
65  {
66  return childIndex;
67  }
68  }
69  return QModelIndex();
70  }
71 
72  ConfigPage *pageFromIndex(const QModelIndex &index)
73  {
74  QModelIndex pageIndex = index.sibling(index.row(), COL_META);
75  QtMetaPointer metaPointer = pageIndex.data(Qt::UserRole).value<QtMetaPointer>();
76  void* pointer = metaPointer;
77  return static_cast<ConfigPage*>(pointer);
78  }
79 
80  QIcon validationIcon(ConfigPage::Validation validation) const
81  {
82  switch (validation)
83  {
85  return QIcon(":icons/exclamation_16.png");
86  default:
87  return QIcon();
88  }
89  }
90 };
91 
92 DPointered(ConfigurationDialog)
93 
95 : QDialog(parent)
96 {
97  d->setupUi(this);
98 
99  QStandardItemModel *model = new QStandardItemModel(this);
101  column < PrivData<ConfigurationDialog>::COLSIZE; ++column)
102  {
103  model->setHorizontalHeaderItem(column, new QStandardItem());
104  }
105  d->tvOptionsList->setModel(model);
106 
107 #if QT_VERSION >= 0x050000
108  d->tvOptionsList->header()->setSectionResizeMode(
109  PrivData<ConfigurationDialog>::COL_NAME, QHeaderView::Stretch);
110  d->tvOptionsList->header()->setSectionResizeMode(
111  PrivData<ConfigurationDialog>::COL_VALIDATION, QHeaderView::ResizeToContents);
112 #else
113  d->tvOptionsList->header()->setResizeMode(
114  PrivData<ConfigurationDialog>::COL_NAME, QHeaderView::Stretch);
115  d->tvOptionsList->header()->setResizeMode(
116  PrivData<ConfigurationDialog>::COL_VALIDATION, QHeaderView::ResizeToContents);
117 #endif
118 
119  d->currentlyDisplayedPage = NULL;
120  connect(d->buttonBox, SIGNAL( clicked(QAbstractButton *) ), this, SLOT ( btnClicked(QAbstractButton *) ));
121  this->connect(d->tvOptionsList->selectionModel(), SIGNAL(currentChanged(QModelIndex, QModelIndex)),
122  SLOT(switchToItem(QModelIndex, QModelIndex)));
123 }
124 
125 ConfigurationDialog::~ConfigurationDialog()
126 {
127  for(int i = 0; i < d->configPages.count(); ++i)
128  {
129  delete d->configPages[i];
130  }
131 }
132 
134  QStandardItem* rootItem, ConfigPage* configPage, int position)
135 {
136  if (!canConfigPageBeAdded(configPage))
137  {
138  return NULL;
139  }
140 
141  QStandardItemModel* pModel = (QStandardItemModel*)d->tvOptionsList->model();
142  if (rootItem == NULL)
143  {
144  rootItem = pModel->invisibleRootItem();
145  }
146 
147  if (!this->hasItemOnList(rootItem))
148  {
149  return NULL;
150  }
151 
152  QList<QStandardItem*> row;
153 
154  QStandardItem* nameItem = new QStandardItem(configPage->name());
155  nameItem->setIcon(configPage->icon());
156 
157  row.insert(PrivData<ConfigurationDialog>::COL_NAME, nameItem);
158 
159  QtMetaPointer metaPointer = configPage;
160  nameItem->setData(qVariantFromValue(metaPointer), Qt::UserRole);
161 
162  row.insert(PrivData<ConfigurationDialog>::COL_VALIDATION, new QStandardItem());
163 
164  if (position < 0)
165  {
166  rootItem->appendRow(row);
167  }
168  else
169  {
170  rootItem->insertRow(position, row);
171  }
172 
173  d->configPages << configPage;
174  this->connect(configPage, SIGNAL(validationRequested()),
175  SLOT(onPageValidationRequested()));
176 
177  if (!configPage->areSettingsAlreadyRead())
178  {
179  configPage->read();
180  }
181  validatePage(configPage);
182 
183  return nameItem;
184 }
185 
186 QStandardItem* ConfigurationDialog::addLabel(QStandardItem* rootItem, const QString& label, int position)
187 {
188  QStandardItemModel* pModel = (QStandardItemModel*)d->tvOptionsList->model();
189  if (rootItem == NULL)
190  {
191  rootItem = pModel->invisibleRootItem();
192  }
193 
194  if (!this->hasItemOnList(rootItem))
195  {
196  return NULL;
197  }
198 
199  QtMetaPointer metaPointer = (void*)NULL;
200  QVariant variantMetaPointer = qVariantFromValue(metaPointer);
201 
202  QList<QStandardItem*> row;
203 
204  QStandardItem* nameItem = new QStandardItem(label);
205  nameItem->setData(variantMetaPointer, Qt::UserRole);
206  row.insert(PrivData<ConfigurationDialog>::COL_NAME, nameItem);
207  row.insert(PrivData<ConfigurationDialog>::COL_VALIDATION, new QStandardItem());
208 
209  if (position < 0)
210  {
211  rootItem->appendRow(row);
212  }
213  else
214  {
215  rootItem->insertRow(position, row);
216  }
217 
218  return nameItem;
219 }
220 
221 void ConfigurationDialog::btnClicked(QAbstractButton *button)
222 {
223  // Figure out what button we pressed and perform its action.
224  switch(d->buttonBox->standardButton(button))
225  {
226  default:
227  break;
228 
229  case QDialogButtonBox::Ok: // Also does the same as Apply
230  if (this->validate())
231  {
232  this->accept();
233  this->saveSettings();
234  }
235  break;
236 
237  case QDialogButtonBox::Apply:
238  if (this->validate())
239  {
240  this->saveSettings();
241  }
242  break;
243 
244  case QDialogButtonBox::Cancel:
245  this->reject();
246  break;
247  }
248 }
249 
250 bool ConfigurationDialog::canConfigPageBeAdded(ConfigPage* configPage)
251 {
252  return isConfigPageValid(configPage) && !hasConfigPage(configPage);
253 }
254 
255 QModelIndex ConfigurationDialog::findPageModelIndex(ConfigPage *page)
256 {
257  QStandardItemModel *model = static_cast<QStandardItemModel*>(d->tvOptionsList->model());
258  return d->findPageModelIndex(model->indexFromItem(model->invisibleRootItem()), page);
259 }
260 
261 bool ConfigurationDialog::isConfigPageValid(ConfigPage* configPage)
262 {
263  return configPage != NULL && !configPage->name().isEmpty();
264 }
265 
266 bool ConfigurationDialog::hasConfigPage(ConfigPage* configPage)
267 {
268  foreach (ConfigPage* addedPage, d->configPages)
269  {
270  if (configPage == addedPage)
271  {
272  return true;
273  }
274  }
275 
276  return false;
277 }
278 
279 void ConfigurationDialog::keyPressEvent(QKeyEvent* e)
280 {
281  switch (e->key())
282  {
283  case Qt::Key_Enter:
284  case Qt::Key_Return:
285  // Suppress the dialog being accepted on pressing ENTER key.
286  // Dialog would close even in line edits that had "returnPressed()"
287  // signals connected. That wasn't good.
288  e->ignore();
289  break;
290  default:
291  QDialog::keyPressEvent(e);
292  }
293 }
294 
295 bool ConfigurationDialog::hasItemOnList(QStandardItem* pItem) const
296 {
297  if (pItem == NULL)
298  {
299  return false;
300  }
301 
302  QStandardItemModel* pModel = (QStandardItemModel*)d->tvOptionsList->model();
303 
304  // Calling index methods on the invisible root items will always return
305  // invalid values.
306 
307  return pModel->invisibleRootItem() == pItem
308  || pModel->indexFromItem(pItem).isValid();
309 }
310 
311 void ConfigurationDialog::onPageValidationRequested()
312 {
313  ConfigPage *page = qobject_cast<ConfigPage*>(sender());
314  assert(page != NULL);
315  validatePage(page);
316 }
317 
318 void ConfigurationDialog::switchToItem(const QModelIndex& current, const QModelIndex &previous)
319 {
320  if (current.isValid() && current != previous)
321  {
322  ConfigPage* configPage = d->pageFromIndex(current);
323 
324  // Something with sense was selected, display this something
325  // and hide previous box.
326  if (isConfigPageValid(configPage))
327  {
328  showConfigPage(configPage);
329  }
330  }
331 }
332 
334 {
335  return d->tvOptionsList;
336 }
337 
338 void ConfigurationDialog::saveSettings()
339 {
340  // Iterate through every engine and execute it's saving method
341  for (int i = 0; i < d->configPages.count(); ++i)
342  {
343  d->configPages[i]->save();
344  }
345 
346  doSaveSettings();
347 
348  if(isVisible())
349  {
350  // Allow panels such as the one for Wadseeker update their contents.
351  for (int i = 0; i < d->configPages.count(); ++i)
352  {
353  d->configPages[i]->read();
354  validatePage(d->configPages[i]);
355  }
356  }
357 }
358 
360 {
361  if (d->currentlyDisplayedPage != NULL)
362  {
363  validatePage(d->currentlyDisplayedPage);
364  d->currentlyDisplayedPage->hide();
365  d->mainPanel->setTitle(QString());
366  }
367  d->currentlyDisplayedPage = page;
368 
369  if (page != NULL)
370  {
371  page->setAllowSave(true);
372  validatePage(page);
373  d->mainPanel->layout()->addWidget(page);
374  d->mainPanel->setTitle(page->title());
375  page->show();
376  }
377 }
378 
379 void ConfigurationDialog::validatePage(ConfigPage *page)
380 {
381  assert(page != NULL);
382  QModelIndex pageIndex = findPageModelIndex(page);
383  assert(pageIndex.isValid());
384 
385  QModelIndex validationIndex = pageIndex.sibling(pageIndex.row(), PrivData<ConfigurationDialog>::COL_VALIDATION);
386  assert(validationIndex.isValid());
387 
388  QStandardItemModel *model = static_cast<QStandardItemModel*>(d->tvOptionsList->model());
389  QStandardItem *validationItem = model->itemFromIndex(validationIndex);
390  validationItem->setIcon(d->validationIcon(page->validate()));
391 }
Validation
Result of validate()
Definition: configpage.h:49
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:80
virtual QIcon icon() const =0
Reimplement this to return a displayable icon for the ConfigPage.
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:56
bool areSettingsAlreadyRead()
true if settings for this page have already been loaded at least once.
Definition: configpage.cpp:51
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:75
virtual QString name() const =0
Reimplement this to return a list-displayable name for this ConfigPage.
Definition: dptr.h:31
virtual Validation validate()
Validate settings on this page.
Definition: configpage.cpp:85
Validation detected at least one problem.
Definition: configpage.h:54
Base class for configuration pages.
Definition: configpage.h:43
void showConfigPage(ConfigPage *widget)