KConfigWidgets

kconfigdialog.cpp
1 /*
2  This file is part of the KDE libraries
3  SPDX-FileCopyrightText: 2003 Benjamin C Meyer <ben+kdelibs at meyerhome dot net>
4  SPDX-FileCopyrightText: 2003 Waldo Bastian <[email protected]>
5  SPDX-FileCopyrightText: 2004 Michael Brade <[email protected]>
6  SPDX-FileCopyrightText: 2021 Ahmad Samir <[email protected]>
7 
8  SPDX-License-Identifier: LGPL-2.0-or-later
9 */
10 
11 #include "kconfigdialog.h"
12 
13 #include <KCoreConfigSkeleton>
14 #include <KLocalizedString>
15 #include <KPageWidgetModel>
16 #include <kconfigdialogmanager.h>
17 #include <khelpclient.h>
18 
19 #include <QDialogButtonBox>
20 #include <QIcon>
21 #include <QPushButton>
22 #include <QScrollArea>
23 #include <QScrollBar>
24 #include <QVBoxLayout>
25 
26 #include <vector>
27 
28 class KConfigDialogPrivate
29 {
30 public:
31  KConfigDialogPrivate(const QString &name, KCoreConfigSkeleton *config, KConfigDialog *qq)
32  : q(qq)
33  {
34  const QString dialogName = !name.isEmpty() ? name : QString::asprintf("SettingsDialog-%p", static_cast<void *>(q));
35 
36  q->setObjectName(dialogName);
37  q->setWindowTitle(i18nc("@title:window", "Configure"));
38  q->setFaceType(KPageDialog::List);
39  s_openDialogs.push_back({dialogName, q});
40 
41  QDialogButtonBox *buttonBox = q->buttonBox();
47  updateButtons();
48  });
52  updateButtons();
53  });
55 
56  QObject::connect(q, &KPageDialog::pageRemoved, q, &KConfigDialog::onPageRemoved);
57 
58  manager = new KConfigDialogManager(q, config);
59  setupManagerConnections(manager);
60 
61  if (QPushButton *applyButton = q->buttonBox()->button(QDialogButtonBox::Apply)) {
62  applyButton->setEnabled(false);
63  };
64  }
65 
66  KPageWidgetItem *addPageInternal(QWidget *page, const QString &itemName, const QString &pixmapName, const QString &header);
67 
68  void setupManagerConnections(KConfigDialogManager *manager);
69 
70  void updateApplyButton();
71  void updateDefaultsButton();
72  void updateButtons();
73  void settingsChangedSlot();
74 
75  KConfigDialog *const q;
76  QString mAnchor;
77  QString mHelpApp;
78  bool shown = false;
79  KConfigDialogManager *manager = nullptr;
80 
81  struct WidgetManager {
82  QWidget *widget = nullptr;
83  KConfigDialogManager *manager = nullptr;
84  };
85  std::vector<WidgetManager> m_managerForPage;
86 
87  /**
88  * The list of existing dialogs.
89  */
90  struct OpenDialogInfo {
91  QString dialogName;
92  KConfigDialog *dialog = nullptr;
93  };
94  static std::vector<OpenDialogInfo> s_openDialogs;
95 };
96 
97 std::vector<KConfigDialogPrivate::OpenDialogInfo> KConfigDialogPrivate::s_openDialogs;
98 
100  : KPageDialog(parent)
101  , d(new KConfigDialogPrivate(name, config, this))
102 {
103 }
104 
106 {
107  auto &openDlgs = KConfigDialogPrivate::s_openDialogs;
108  const QString currentObjectName = objectName();
109  auto it = std::find_if(openDlgs.cbegin(), openDlgs.cend(), [=](const KConfigDialogPrivate::OpenDialogInfo &info) {
110  return currentObjectName == info.dialogName;
111  });
112 
113  if (it != openDlgs.cend()) {
114  openDlgs.erase(it);
115  }
116 }
117 
118 KPageWidgetItem *KConfigDialog::addPage(QWidget *page, const QString &itemName, const QString &pixmapName, const QString &header, bool manage)
119 {
120  Q_ASSERT(page);
121  if (!page) {
122  return nullptr;
123  }
124 
125  KPageWidgetItem *item = d->addPageInternal(page, itemName, pixmapName, header);
126  if (manage) {
127  d->manager->addWidget(page);
128  }
129 
130  if (d->shown && manage) {
131  // update the default button if the dialog is shown
133  if (defaultButton) {
134  bool is_default = defaultButton->isEnabled() && d->manager->isDefault();
135  defaultButton->setEnabled(!is_default);
136  }
137  }
138  return item;
139 }
140 
141 KPageWidgetItem *KConfigDialog::addPage(QWidget *page, KCoreConfigSkeleton *config, const QString &itemName, const QString &pixmapName, const QString &header)
142 {
143  Q_ASSERT(page);
144  if (!page) {
145  return nullptr;
146  }
147 
148  KPageWidgetItem *item = d->addPageInternal(page, itemName, pixmapName, header);
149  auto *manager = new KConfigDialogManager(page, config);
150  d->m_managerForPage.push_back({page, manager});
151  d->setupManagerConnections(manager);
152 
153  if (d->shown) {
154  // update the default button if the dialog is shown
156  if (defaultButton) {
157  const bool is_default = defaultButton->isEnabled() && manager->isDefault();
158  defaultButton->setEnabled(!is_default);
159  }
160  }
161  return item;
162 }
163 
164 KPageWidgetItem *KConfigDialogPrivate::addPageInternal(QWidget *page, const QString &itemName, const QString &pixmapName, const QString &header)
165 {
166  QWidget *frame = new QWidget(q);
167  QVBoxLayout *boxLayout = new QVBoxLayout(frame);
168  boxLayout->setContentsMargins(0, 0, 0, 0);
169  boxLayout->setContentsMargins(0, 0, 0, 0);
170 
171  QScrollArea *scroll = new QScrollArea(q);
175  scroll->setWidget(page);
176  scroll->setWidgetResizable(true);
178 
179  if (page->minimumSizeHint().height() > scroll->sizeHint().height() - 2) {
180  if (page->sizeHint().width() < scroll->sizeHint().width() + 2) {
181  // QScrollArea is planning only a vertical scroll bar,
182  // try to avoid the horizontal one by reserving space for the vertical one.
183  // Currently KPageViewPrivate::_k_modelChanged() queries the minimumSizeHint().
184  // We can only set the minimumSize(), so this approach relies on QStackedWidget size calculation.
185  scroll->setMinimumWidth(scroll->sizeHint().width() + qBound(0, scroll->verticalScrollBar()->sizeHint().width(), 200) + 4);
186  }
187  }
188 
189  boxLayout->addWidget(scroll);
190  KPageWidgetItem *item = new KPageWidgetItem(frame, itemName);
191  item->setHeader(header);
192  if (!pixmapName.isEmpty()) {
193  item->setIcon(QIcon::fromTheme(pixmapName));
194  }
195 
196  q->KPageDialog::addPage(item);
197  return item;
198 }
199 
200 void KConfigDialogPrivate::setupManagerConnections(KConfigDialogManager *manager)
201 {
202  q->connect(manager, qOverload<>(&KConfigDialogManager::settingsChanged), q, [this]() {
203  settingsChangedSlot();
204  });
205  q->connect(manager, &KConfigDialogManager::widgetModified, q, [this]() {
206  updateButtons();
207  });
208 
209  QDialogButtonBox *buttonBox = q->buttonBox();
214 }
215 
216 void KConfigDialogPrivate::updateApplyButton()
217 {
218  QPushButton *applyButton = q->buttonBox()->button(QDialogButtonBox::Apply);
219  if (!applyButton) {
220  return;
221  }
222 
223  const bool hasManagerChanged = std::any_of(m_managerForPage.cbegin(), m_managerForPage.cend(), [](const WidgetManager &widgetManager) {
224  return widgetManager.manager->hasChanged();
225  });
226 
227  applyButton->setEnabled(manager->hasChanged() || q->hasChanged() || hasManagerChanged);
228 }
229 
230 void KConfigDialogPrivate::updateDefaultsButton()
231 {
232  QPushButton *restoreDefaultsButton = q->buttonBox()->button(QDialogButtonBox::RestoreDefaults);
233  if (!restoreDefaultsButton) {
234  return;
235  }
236 
237  const bool isManagerDefaulted = std::all_of(m_managerForPage.cbegin(), m_managerForPage.cend(), [](const WidgetManager &widgetManager) {
238  return widgetManager.manager->isDefault();
239  });
240 
241  restoreDefaultsButton->setDisabled(manager->isDefault() && q->isDefault() && isManagerDefaulted);
242 }
243 
244 void KConfigDialog::onPageRemoved(KPageWidgetItem *item)
245 {
246  auto it = std::find_if(d->m_managerForPage.cbegin(), d->m_managerForPage.cend(), [item](const KConfigDialogPrivate::WidgetManager &wm) {
247  return item->widget()->isAncestorOf(wm.widget);
248  });
249 
250  if (it != d->m_managerForPage.cend()) { // There is a manager for this page, so remove it
251  delete it->manager;
252  d->m_managerForPage.erase(it);
253  d->updateButtons();
254  }
255 }
256 
258 {
259  auto &openDlgs = KConfigDialogPrivate::s_openDialogs;
260  auto it = std::find_if(openDlgs.cbegin(), openDlgs.cend(), [name](const KConfigDialogPrivate::OpenDialogInfo &info) {
261  return name == info.dialogName;
262  });
263 
264  return it != openDlgs.cend() ? it->dialog : nullptr;
265 }
266 
268 {
269  KConfigDialog *dialog = exists(name);
270  if (dialog) {
271  dialog->show();
272  }
273  return (dialog != nullptr);
274 }
275 
276 void KConfigDialogPrivate::updateButtons()
277 {
278  static bool only_once = false;
279  if (only_once) {
280  return;
281  }
282  only_once = true;
283 
284  updateApplyButton();
285  updateDefaultsButton();
286 
287  Q_EMIT q->widgetModified();
288  only_once = false;
289 }
290 
291 void KConfigDialogPrivate::settingsChangedSlot()
292 {
293  // Update the buttons
294  updateButtons();
295  Q_EMIT q->settingsChanged(q->objectName());
296 }
297 
299 {
300  if (!d->shown) {
301  updateWidgets();
302  d->manager->updateWidgets();
303  for (auto [widget, manager] : d->m_managerForPage) {
304  manager->updateWidgets();
305  }
306 
307  d->updateApplyButton();
308  d->updateDefaultsButton();
309 
310  d->shown = true;
311  }
313 }
314 
316 {
317 }
318 
320 {
321 }
322 
324 {
325 }
326 
328 {
329  return false;
330 }
331 
333 {
334  return true;
335 }
336 
338 {
339  d->updateButtons();
340 }
341 
343 {
344  d->settingsChangedSlot();
345 }
346 
347 void KConfigDialog::setHelp(const QString &anchor, const QString &appname)
348 {
349  d->mAnchor = anchor;
350  d->mHelpApp = appname;
351 }
352 
354 {
355  KHelpClient::invokeHelp(d->mAnchor, d->mHelpApp);
356 }
357 
358 #include "moc_kconfigdialog.cpp"
QScrollBar * verticalScrollBar() const const
void showEvent(QShowEvent *e) override
void setHelp(const QString &anchor, const QString &appname=QString())
Sets the help path and topic.
Standard KDE configuration dialog class.
Definition: kconfigdialog.h:66
void setSizePolicy(QSizePolicy)
void setDisabled(bool disable)
virtual QSize sizeHint() const const override
KConfigDialog(QWidget *parent, const QString &name, KCoreConfigSkeleton *config)
void clicked(bool checked)
QIcon fromTheme(const QString &name)
ScrollBarAsNeeded
static bool showDialog(const QString &name)
Attempts to show the dialog with the name 'name'.
int width() const const
void setFrameShape(QFrame::Shape)
void updateButtons()
Updates the Apply and Default buttons.
virtual QSize sizeHint() const const override
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
void setStandardButtons(QDialogButtonBox::StandardButtons buttons)
void addWidget(QWidget *widget, int stretch, Qt::Alignment alignment)
void setIcon(const QIcon &icon)
void setHorizontalScrollBarPolicy(Qt::ScrollBarPolicy)
void widgetModified()
If retrieveSettings() was told to track changes then if any known setting was changed this signal wil...
virtual void showHelp()
Displays help for this config dialog.
int height() const const
virtual bool hasChanged()
Returns whether the current state of the dialog is different from the current configuration.
KPageWidgetItem * addPage(QWidget *page, const QString &itemName, const QString &pixmapName=QString(), const QString &header=QString(), bool manage=true)
Adds page to the dialog and to KConfigDialogManager.
void updateWidgets()
Traverse the specified widgets, sets the state of all known widgets according to the state in the set...
~KConfigDialog() override
Deconstructor, removes name from the list of open dialogs.
bool isEmpty() const const
void updateWidgetsDefault()
Traverse the specified widgets, sets the state of all known widgets according to the default state in...
virtual bool isDefault()
Returns whether the current state of the dialog is the same as the default configuration.
virtual void showEvent(QShowEvent *event) override
static KConfigDialog * exists(const QString &name)
See if a dialog with the name 'name' already exists.
virtual void updateWidgets()
Update the dialog based on the settings.
KSharedConfigPtr config()
bool isEnabled() const const
void show()
void settingsChanged()
One or more of the settings have been saved (such as when the user clicks on the Apply button).
Provides a means of automatically retrieving, saving and resetting KConfigSkeleton based settings in ...
void setWidgetResizable(bool resizable)
void updateSettings()
Traverse the specified widgets, saving the settings of all known widgets in the settings object.
QPushButton * button(QDialogButtonBox::StandardButton which) const const
QString name(StandardShortcut id)
void setContentsMargins(int left, int top, int right, int bottom)
QString i18nc(const char *context, const char *text, const TYPE &arg...)
void setMinimumWidth(int minw)
void settingsChangedSlot()
Some setting was changed.
void invokeHelp(const QString &anchor=QString(), const QString &appname=QString())
Invokes the KHelpCenter HTML help viewer from docbook sources.
Definition: khelpclient.cpp:17
QString asprintf(const char *cformat,...)
void setHeader(const QString &header)
virtual void updateWidgetsDefault()
Update the dialog based on the default settings.
QDialogButtonBox * buttonBox()
void setVerticalScrollBarPolicy(Qt::ScrollBarPolicy)
virtual void updateSettings()
Update the settings from the dialog.
void setWidget(QWidget *widget)
void pageRemoved(KPageWidgetItem *page)
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Tue Sep 26 2023 03:47:13 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.