File indexing completed on 2024-09-15 03:37:06
0001 /* 0002 This file is part of the KDE libraries 0003 SPDX-FileCopyrightText: 2003 Benjamin C Meyer <ben+kdelibs at meyerhome dot net> 0004 SPDX-FileCopyrightText: 2003 Waldo Bastian <bastian@kde.org> 0005 SPDX-FileCopyrightText: 2004 Michael Brade <brade@kde.org> 0006 SPDX-FileCopyrightText: 2021 Ahmad Samir <a.samirh78@gmail.com> 0007 0008 SPDX-License-Identifier: LGPL-2.0-or-later 0009 */ 0010 0011 #include "kconfigdialog.h" 0012 0013 #include <KCoreConfigSkeleton> 0014 #include <KLocalizedString> 0015 #include <KPageWidgetModel> 0016 #include <kconfigdialogmanager.h> 0017 #include <khelpclient.h> 0018 0019 #include <QDialogButtonBox> 0020 #include <QIcon> 0021 #include <QPushButton> 0022 #include <QScrollArea> 0023 #include <QScrollBar> 0024 #include <QVBoxLayout> 0025 0026 #include <vector> 0027 0028 class KConfigDialogPrivate 0029 { 0030 public: 0031 KConfigDialogPrivate(const QString &name, KCoreConfigSkeleton *config, KConfigDialog *qq) 0032 : q(qq) 0033 { 0034 const QString dialogName = !name.isEmpty() ? name : QString::asprintf("SettingsDialog-%p", static_cast<void *>(q)); 0035 0036 q->setObjectName(dialogName); 0037 q->setWindowTitle(i18nc("@title:window", "Configure")); 0038 q->setFaceType(KPageDialog::List); 0039 s_openDialogs.push_back({dialogName, q}); 0040 0041 QDialogButtonBox *buttonBox = q->buttonBox(); 0042 buttonBox->setStandardButtons(QDialogButtonBox::RestoreDefaults | QDialogButtonBox::Ok | QDialogButtonBox::Apply | QDialogButtonBox::Cancel 0043 | QDialogButtonBox::Help); 0044 QObject::connect(buttonBox->button(QDialogButtonBox::Ok), &QAbstractButton::clicked, q, &KConfigDialog::updateSettings); 0045 QObject::connect(buttonBox->button(QDialogButtonBox::Apply), &QAbstractButton::clicked, q, &KConfigDialog::updateSettings); 0046 QObject::connect(buttonBox->button(QDialogButtonBox::Apply), &QAbstractButton::clicked, q, [this]() { 0047 updateButtons(); 0048 }); 0049 QObject::connect(buttonBox->button(QDialogButtonBox::Cancel), &QAbstractButton::clicked, q, &KConfigDialog::updateWidgets); 0050 QObject::connect(buttonBox->button(QDialogButtonBox::RestoreDefaults), &QAbstractButton::clicked, q, &KConfigDialog::updateWidgetsDefault); 0051 QObject::connect(buttonBox->button(QDialogButtonBox::RestoreDefaults), &QAbstractButton::clicked, q, [this]() { 0052 updateButtons(); 0053 }); 0054 QObject::connect(buttonBox->button(QDialogButtonBox::Help), &QAbstractButton::clicked, q, &KConfigDialog::showHelp); 0055 0056 QObject::connect(q, &KPageDialog::pageRemoved, q, &KConfigDialog::onPageRemoved); 0057 0058 manager = new KConfigDialogManager(q, config); 0059 setupManagerConnections(manager); 0060 0061 if (QPushButton *applyButton = q->buttonBox()->button(QDialogButtonBox::Apply)) { 0062 applyButton->setEnabled(false); 0063 }; 0064 } 0065 0066 KPageWidgetItem *addPageInternal(QWidget *page, const QString &itemName, const QString &pixmapName, const QString &header); 0067 0068 void setupManagerConnections(KConfigDialogManager *manager); 0069 0070 void updateApplyButton(); 0071 void updateDefaultsButton(); 0072 void updateButtons(); 0073 void settingsChangedSlot(); 0074 0075 KConfigDialog *const q; 0076 QString mAnchor; 0077 QString mHelpApp; 0078 bool shown = false; 0079 KConfigDialogManager *manager = nullptr; 0080 0081 struct WidgetManager { 0082 QWidget *widget = nullptr; 0083 KConfigDialogManager *manager = nullptr; 0084 }; 0085 std::vector<WidgetManager> m_managerForPage; 0086 0087 /** 0088 * The list of existing dialogs. 0089 */ 0090 struct OpenDialogInfo { 0091 QString dialogName; 0092 KConfigDialog *dialog = nullptr; 0093 }; 0094 static std::vector<OpenDialogInfo> s_openDialogs; 0095 }; 0096 0097 std::vector<KConfigDialogPrivate::OpenDialogInfo> KConfigDialogPrivate::s_openDialogs; 0098 0099 KConfigDialog::KConfigDialog(QWidget *parent, const QString &name, KCoreConfigSkeleton *config) 0100 : KPageDialog(parent) 0101 , d(new KConfigDialogPrivate(name, config, this)) 0102 { 0103 } 0104 0105 KConfigDialog::~KConfigDialog() 0106 { 0107 auto &openDlgs = KConfigDialogPrivate::s_openDialogs; 0108 const QString currentObjectName = objectName(); 0109 auto it = std::find_if(openDlgs.cbegin(), openDlgs.cend(), [=](const KConfigDialogPrivate::OpenDialogInfo &info) { 0110 return currentObjectName == info.dialogName; 0111 }); 0112 0113 if (it != openDlgs.cend()) { 0114 openDlgs.erase(it); 0115 } 0116 } 0117 0118 KPageWidgetItem *KConfigDialog::addPage(QWidget *page, const QString &itemName, const QString &pixmapName, const QString &header, bool manage) 0119 { 0120 Q_ASSERT(page); 0121 if (!page) { 0122 return nullptr; 0123 } 0124 0125 KPageWidgetItem *item = d->addPageInternal(page, itemName, pixmapName, header); 0126 if (manage) { 0127 d->manager->addWidget(page); 0128 } 0129 0130 if (d->shown && manage) { 0131 // update the default button if the dialog is shown 0132 QPushButton *defaultButton = buttonBox()->button(QDialogButtonBox::RestoreDefaults); 0133 if (defaultButton) { 0134 bool is_default = defaultButton->isEnabled() && d->manager->isDefault(); 0135 defaultButton->setEnabled(!is_default); 0136 } 0137 } 0138 return item; 0139 } 0140 0141 KPageWidgetItem *KConfigDialog::addPage(QWidget *page, KCoreConfigSkeleton *config, const QString &itemName, const QString &pixmapName, const QString &header) 0142 { 0143 Q_ASSERT(page); 0144 if (!page) { 0145 return nullptr; 0146 } 0147 0148 KPageWidgetItem *item = d->addPageInternal(page, itemName, pixmapName, header); 0149 auto *manager = new KConfigDialogManager(page, config); 0150 d->m_managerForPage.push_back({page, manager}); 0151 d->setupManagerConnections(manager); 0152 0153 if (d->shown) { 0154 // update the default button if the dialog is shown 0155 QPushButton *defaultButton = buttonBox()->button(QDialogButtonBox::RestoreDefaults); 0156 if (defaultButton) { 0157 const bool is_default = defaultButton->isEnabled() && manager->isDefault(); 0158 defaultButton->setEnabled(!is_default); 0159 } 0160 } 0161 return item; 0162 } 0163 0164 KPageWidgetItem *KConfigDialogPrivate::addPageInternal(QWidget *page, const QString &itemName, const QString &pixmapName, const QString &header) 0165 { 0166 QWidget *frame = new QWidget(q); 0167 QVBoxLayout *boxLayout = new QVBoxLayout(frame); 0168 boxLayout->setContentsMargins(0, 0, 0, 0); 0169 boxLayout->setContentsMargins(0, 0, 0, 0); 0170 0171 QScrollArea *scroll = new QScrollArea(q); 0172 scroll->setFrameShape(QFrame::NoFrame); 0173 scroll->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); 0174 scroll->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); 0175 scroll->setWidget(page); 0176 scroll->setWidgetResizable(true); 0177 scroll->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); 0178 0179 if (page->minimumSizeHint().height() > scroll->sizeHint().height() - 2) { 0180 if (page->sizeHint().width() < scroll->sizeHint().width() + 2) { 0181 // QScrollArea is planning only a vertical scroll bar, 0182 // try to avoid the horizontal one by reserving space for the vertical one. 0183 // Currently KPageViewPrivate::_k_modelChanged() queries the minimumSizeHint(). 0184 // We can only set the minimumSize(), so this approach relies on QStackedWidget size calculation. 0185 scroll->setMinimumWidth(scroll->sizeHint().width() + qBound(0, scroll->verticalScrollBar()->sizeHint().width(), 200) + 4); 0186 } 0187 } 0188 0189 boxLayout->addWidget(scroll); 0190 KPageWidgetItem *item = new KPageWidgetItem(frame, itemName); 0191 item->setHeader(header); 0192 if (!pixmapName.isEmpty()) { 0193 item->setIcon(QIcon::fromTheme(pixmapName)); 0194 } 0195 0196 q->KPageDialog::addPage(item); 0197 return item; 0198 } 0199 0200 void KConfigDialogPrivate::setupManagerConnections(KConfigDialogManager *manager) 0201 { 0202 q->connect(manager, qOverload<>(&KConfigDialogManager::settingsChanged), q, [this]() { 0203 settingsChangedSlot(); 0204 }); 0205 q->connect(manager, &KConfigDialogManager::widgetModified, q, [this]() { 0206 updateButtons(); 0207 }); 0208 0209 QDialogButtonBox *buttonBox = q->buttonBox(); 0210 q->connect(buttonBox->button(QDialogButtonBox::Ok), &QPushButton::clicked, manager, &KConfigDialogManager::updateSettings); 0211 q->connect(buttonBox->button(QDialogButtonBox::Apply), &QPushButton::clicked, manager, &KConfigDialogManager::updateSettings); 0212 q->connect(buttonBox->button(QDialogButtonBox::Cancel), &QPushButton::clicked, manager, &KConfigDialogManager::updateWidgets); 0213 q->connect(buttonBox->button(QDialogButtonBox::RestoreDefaults), &QPushButton::clicked, manager, &KConfigDialogManager::updateWidgetsDefault); 0214 } 0215 0216 void KConfigDialogPrivate::updateApplyButton() 0217 { 0218 QPushButton *applyButton = q->buttonBox()->button(QDialogButtonBox::Apply); 0219 if (!applyButton) { 0220 return; 0221 } 0222 0223 const bool hasManagerChanged = std::any_of(m_managerForPage.cbegin(), m_managerForPage.cend(), [](const WidgetManager &widgetManager) { 0224 return widgetManager.manager->hasChanged(); 0225 }); 0226 0227 applyButton->setEnabled(manager->hasChanged() || q->hasChanged() || hasManagerChanged); 0228 } 0229 0230 void KConfigDialogPrivate::updateDefaultsButton() 0231 { 0232 QPushButton *restoreDefaultsButton = q->buttonBox()->button(QDialogButtonBox::RestoreDefaults); 0233 if (!restoreDefaultsButton) { 0234 return; 0235 } 0236 0237 const bool isManagerDefaulted = std::all_of(m_managerForPage.cbegin(), m_managerForPage.cend(), [](const WidgetManager &widgetManager) { 0238 return widgetManager.manager->isDefault(); 0239 }); 0240 0241 restoreDefaultsButton->setDisabled(manager->isDefault() && q->isDefault() && isManagerDefaulted); 0242 } 0243 0244 void KConfigDialog::onPageRemoved(KPageWidgetItem *item) 0245 { 0246 auto it = std::find_if(d->m_managerForPage.cbegin(), d->m_managerForPage.cend(), [item](const KConfigDialogPrivate::WidgetManager &wm) { 0247 return item->widget()->isAncestorOf(wm.widget); 0248 }); 0249 0250 if (it != d->m_managerForPage.cend()) { // There is a manager for this page, so remove it 0251 delete it->manager; 0252 d->m_managerForPage.erase(it); 0253 d->updateButtons(); 0254 } 0255 } 0256 0257 KConfigDialog *KConfigDialog::exists(const QString &name) 0258 { 0259 auto &openDlgs = KConfigDialogPrivate::s_openDialogs; 0260 auto it = std::find_if(openDlgs.cbegin(), openDlgs.cend(), [name](const KConfigDialogPrivate::OpenDialogInfo &info) { 0261 return name == info.dialogName; 0262 }); 0263 0264 return it != openDlgs.cend() ? it->dialog : nullptr; 0265 } 0266 0267 bool KConfigDialog::showDialog(const QString &name) 0268 { 0269 KConfigDialog *dialog = exists(name); 0270 if (dialog) { 0271 dialog->show(); 0272 } 0273 return (dialog != nullptr); 0274 } 0275 0276 void KConfigDialogPrivate::updateButtons() 0277 { 0278 static bool only_once = false; 0279 if (only_once) { 0280 return; 0281 } 0282 only_once = true; 0283 0284 updateApplyButton(); 0285 updateDefaultsButton(); 0286 0287 Q_EMIT q->widgetModified(); 0288 only_once = false; 0289 } 0290 0291 void KConfigDialogPrivate::settingsChangedSlot() 0292 { 0293 // Update the buttons 0294 updateButtons(); 0295 Q_EMIT q->settingsChanged(q->objectName()); 0296 } 0297 0298 void KConfigDialog::showEvent(QShowEvent *e) 0299 { 0300 if (!d->shown) { 0301 updateWidgets(); 0302 d->manager->updateWidgets(); 0303 for (auto [widget, manager] : d->m_managerForPage) { 0304 manager->updateWidgets(); 0305 } 0306 0307 d->updateApplyButton(); 0308 d->updateDefaultsButton(); 0309 0310 d->shown = true; 0311 } 0312 KPageDialog::showEvent(e); 0313 } 0314 0315 void KConfigDialog::updateSettings() 0316 { 0317 } 0318 0319 void KConfigDialog::updateWidgets() 0320 { 0321 } 0322 0323 void KConfigDialog::updateWidgetsDefault() 0324 { 0325 } 0326 0327 bool KConfigDialog::hasChanged() 0328 { 0329 return false; 0330 } 0331 0332 bool KConfigDialog::isDefault() 0333 { 0334 return true; 0335 } 0336 0337 void KConfigDialog::updateButtons() 0338 { 0339 d->updateButtons(); 0340 } 0341 0342 void KConfigDialog::settingsChangedSlot() 0343 { 0344 d->settingsChangedSlot(); 0345 } 0346 0347 void KConfigDialog::setHelp(const QString &anchor, const QString &appname) 0348 { 0349 d->mAnchor = anchor; 0350 d->mHelpApp = appname; 0351 } 0352 0353 void KConfigDialog::showHelp() 0354 { 0355 KHelpClient::invokeHelp(d->mAnchor, d->mHelpApp); 0356 } 0357 0358 #include "moc_kconfigdialog.cpp"