Qt Utilities  5.12.0
Common Qt related C++ classes and routines used by my applications such as dialogs, widgets and models
settingsdialog.cpp
Go to the documentation of this file.
1 #include "./settingsdialog.h"
2 
3 #include "./optioncategory.h"
6 #include "./optionpage.h"
7 
8 #include "../misc/dialogutils.h"
9 
10 #include "ui_settingsdialog.h"
11 
12 #include <QItemSelectionModel>
13 #include <QMessageBox>
14 #include <QScrollArea>
15 #include <QShowEvent>
16 #include <QStringBuilder>
17 
18 namespace Dialogs {
19 
31  : QDialog(parent)
32  , m_ui(new Ui::SettingsDialog)
33  , m_categoryModel(new OptionCategoryModel(this))
34  , m_categoryFilterModel(new OptionCategoryFilterModel(this))
35  , m_currentCategory(nullptr)
36  , m_tabBarAlwaysVisible(true)
37 {
38  m_ui->setupUi(this);
39  makeHeading(m_ui->headingLabel);
40  setStyleSheet(dialogStyle());
41  // setup models
42  m_categoryFilterModel->setSourceModel(m_categoryModel);
43  m_ui->categoriesListView->setModel(m_categoryFilterModel);
44  // connect signals and slots
45  // selection models
46  connect(m_ui->categoriesListView->selectionModel(), &QItemSelectionModel::currentChanged, this, &SettingsDialog::currentCategoryChanged);
47  // buttons
48  connect(m_ui->abortPushButton, &QPushButton::clicked, this, &SettingsDialog::reject);
49  connect(m_ui->applyPushButton, &QPushButton::clicked, this, &SettingsDialog::apply);
50  connect(m_ui->okPushButton, &QPushButton::clicked, this, &SettingsDialog::accept);
51  // dialog
52  connect(this, &SettingsDialog::accepted, this, &SettingsDialog::apply);
53  connect(this, &SettingsDialog::rejected, this, &SettingsDialog::reset);
54  // misc
55  connect(m_ui->filterLineEdit, &QLineEdit::textChanged, m_categoryFilterModel, &OptionCategoryFilterModel::setFilterFixedString);
56  connect(m_ui->filterLineEdit, &QLineEdit::textChanged, this, &SettingsDialog::updateTabWidget);
57 }
58 
63 {
64 }
65 
71 {
72  m_tabBarAlwaysVisible = value;
73  if (m_currentCategory) {
74  m_ui->pagesTabWidget->tabBar()->setHidden(!value && m_currentCategory->pages().size() == 1);
75  }
76 }
77 
84 OptionCategory *SettingsDialog::category(int categoryIndex) const
85 {
86  return m_categoryModel->category(categoryIndex);
87 }
88 
96 OptionPage *SettingsDialog::page(int categoryIndex, int pageIndex) const
97 {
98  if (OptionCategory *category = this->category(categoryIndex)) {
99  if (pageIndex < category->pages().length()) {
100  return category->pages()[pageIndex];
101  }
102  }
103  return nullptr;
104 }
105 
109 void SettingsDialog::showEvent(QShowEvent *event)
110 {
111  if (!event->spontaneous()) {
112  for (OptionCategory *category : m_categoryModel->categories()) {
113  for (OptionPage *page : category->pages()) {
114  if (page->hasBeenShown()) {
115  page->reset();
116  }
117  }
118  }
119  }
120 }
121 
129 void SettingsDialog::currentCategoryChanged(const QModelIndex &index)
130 {
131  showCategory(m_categoryModel->category(m_categoryFilterModel->mapToSource(index)));
132 }
133 
139 {
140  if (m_currentCategory) {
141  m_currentCategory->setCurrentIndex(m_ui->pagesTabWidget->currentIndex());
142  }
143  if (category) {
144  if (m_currentCategory != category) {
145  m_currentCategory = category;
146  m_ui->headingLabel->setText(category->displayName());
147  }
148  } else {
149  m_currentCategory = nullptr;
150  m_ui->headingLabel->setText(tr("No category selected"));
151  }
152  updateTabWidget();
153 }
154 
165 {
166  bool hasSingleCategory = singleCategory != nullptr;
167  m_ui->filterLineEdit->setHidden(hasSingleCategory);
168  m_ui->categoriesListView->setHidden(hasSingleCategory);
169  m_ui->headingLabel->setHidden(hasSingleCategory);
170  if (hasSingleCategory) {
171  m_ui->filterLineEdit->clear();
172  categoryModel()->setCategories(QList<OptionCategory *>() << singleCategory);
173  showCategory(singleCategory);
174  }
175 }
176 
180 void SettingsDialog::updateTabWidget()
181 {
182  if (m_currentCategory) {
183  m_ui->pagesTabWidget->setUpdatesEnabled(false);
184  const QString searchKeyWord = m_ui->filterLineEdit->text();
185  int index = 0, pageIndex = 0;
186  for (OptionPage *page : m_currentCategory->pages()) {
187  if (page->matches(searchKeyWord)) {
188  QScrollArea *scrollArea;
189  if (index < m_ui->pagesTabWidget->count()) {
190  scrollArea = qobject_cast<QScrollArea *>(m_ui->pagesTabWidget->widget(index));
191  scrollArea->takeWidget();
192  m_ui->pagesTabWidget->setTabText(index, page->widget()->windowTitle());
193  m_ui->pagesTabWidget->setTabIcon(index, page->widget()->windowIcon());
194  } else {
195  scrollArea = new QScrollArea(m_ui->pagesTabWidget);
196  scrollArea->setFrameStyle(QFrame::NoFrame);
197  scrollArea->setBackgroundRole(QPalette::Base);
198  scrollArea->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
199  scrollArea->setWidgetResizable(true);
200  m_ui->pagesTabWidget->addTab(scrollArea, page->widget()->windowTitle());
201  m_ui->pagesTabWidget->setTabIcon(index, page->widget()->windowIcon());
202  }
203  if (page->widget()->layout()) {
204  page->widget()->layout()->setAlignment(Qt::AlignTop | Qt::AlignLeft);
205  }
206  scrollArea->setWidget(page->widget());
207  ++index;
208  }
209  if (pageIndex == m_currentCategory->currentIndex()) {
210  m_ui->pagesTabWidget->setCurrentIndex(pageIndex);
211  }
212  ++pageIndex;
213  }
214  while (index < m_ui->pagesTabWidget->count()) {
215  QScrollArea *scrollArea = qobject_cast<QScrollArea *>(m_ui->pagesTabWidget->widget(index));
216  scrollArea->takeWidget();
217  m_ui->pagesTabWidget->removeTab(index);
218  delete scrollArea;
219  }
220  m_ui->pagesTabWidget->tabBar()->setHidden(!m_tabBarAlwaysVisible && m_ui->pagesTabWidget->count() == 1);
221  m_ui->pagesTabWidget->setUpdatesEnabled(true);
222  } else {
223  m_ui->pagesTabWidget->clear();
224  }
225 }
226 
232 bool SettingsDialog::apply()
233 {
234  // apply each page in each category and gather error messages
235  QString errorMessage;
236  for (OptionCategory *category : m_categoryModel->categories()) {
237  for (OptionPage *page : category->pages()) {
238  if (!page->hasBeenShown() || page->apply()) {
239  // nothing to apply or no error
240  continue;
241  }
242 
243  // add error message
244  if (errorMessage.isEmpty()) {
245  errorMessage = tr("<p><b>Errors occurred when applying changes:</b></p><ul>");
246  }
247  QStringList &errors = const_cast<OptionPage *>(page)->errors();
248  if (errors.isEmpty()) {
249  errorMessage.append(QStringLiteral("<li><i>") % category->displayName() % QLatin1Char('/') % page->widget()->windowTitle()
250  % QStringLiteral("</i>: ") % tr("unknonw error") % QStringLiteral("</li>"));
251  } else {
252  for (const QString &error : errors) {
253  errorMessage.append(QStringLiteral("<li><i>") % category->displayName() % QLatin1Char('/') % page->widget()->windowTitle()
254  % QStringLiteral("</i>: ") % error % QStringLiteral("</li>"));
255  }
256  errors.clear();
257  }
258  }
259  }
260 
261  // show error messages (if errors occurred)
262  if (!errorMessage.isEmpty()) {
263  errorMessage.append(QStringLiteral("</ul>"));
264  QMessageBox::warning(this, windowTitle(), errorMessage);
265  }
266 
267  // return status
268  emit applied();
269  return errorMessage.isEmpty();
270 }
271 
276 void SettingsDialog::reset()
277 {
278  for (OptionCategory *category : m_categoryModel->categories()) {
280  }
281  emit resetted();
282 }
283 } // namespace Dialogs
QString displayName
Returns the display name of the category.
QWidget * widget()
Returns the widget for the option page.
Definition: optionpage.cpp:45
The OptionCategoryModel class is used by SettingsDialog to store and display option categories...
bool matches(const QString &searchKeyWord)
Returns whether the pages matches the specified searchKeyWord.
Definition: optionpage.cpp:61
The OptionCategory class wraps associated option pages.
The OptionPage class is the base class for SettingsDialog pages.
Definition: optionpage.h:15
void setCategories(const QList< OptionCategory *> categories)
Sets the categories for the model.
OptionPage * page(int categoryIndex, int pageIndex) const
Returns the page for the specified categoryIndex and the specified pageIndex.
OptionCategory * category(const QModelIndex &index) const
Returns the category for the specified model index.
OptionCategory * category(int categoryIndex) const
Returns the category for the specified categoryIndex.
const QList< OptionCategory * > & categories() const
Returns the categories.
void setSingleCategory(OptionCategory *singleCategory)
Enables single-category mode to show only the specified singleCategory.
void showEvent(QShowEvent *event) override
Resets all pages before the dialog is shown by the application.
bool hasBeenShown() const
Returns an indication whether the option page has been shown yet.
Definition: optionpage.h:56
Provides common dialogs such as AboutDialog, EnterPasswordDialog and SettingsDialog.
Definition: dialogutils.h:12
void resetAllPages()
Resets all pages.
int currentIndex() const
Returns the index of the currently shown page.
SettingsDialog(QWidget *parent=nullptr)
Constructs a settings dialog.
QList< OptionPage * > pages
Returns the assigned pages.
void setTabBarAlwaysVisible(bool value)
Sets whether the tab bar is always visible.
virtual void reset()=0
Discards altered settings and resets relevant widgets.
virtual bool apply()=0
Applies altered settings.
The SettingsDialog class provides a framework for creating settings dialogs with different categories...
void showCategory(OptionCategory *category)
Sets the current category to the specified category and updates the relevant widgets to show it...
OptionCategoryModel * categoryModel()
Returns the category model used by the settings dialog to manage the categories.
void setCurrentIndex(int currentIndex)
Sets the current index.
~SettingsDialog() override
Destroys the settings dialog.
The OptionCategoryFilterModel class is used by SettingsDialog to filter option categories.