File indexing completed on 2024-05-19 05:38:25
0001 /* 0002 KCMStyle 0003 SPDX-FileCopyrightText: 2002 Karol Szwed <gallium@kde.org> 0004 SPDX-FileCopyrightText: 2002 Daniel Molkentin <molkentin@kde.org> 0005 SPDX-FileCopyrightText: 2007 Urs Wolfer <uwolfer @ kde.org> 0006 SPDX-FileCopyrightText: 2009 Davide Bettio <davide.bettio@kdemail.net> 0007 SPDX-FileCopyrightText: 2019 Kai Uwe Broulik <kde@broulik.de> 0008 SPDX-FileCopyrightText: 2019 Cyril Rossi <cyril.rossi@enioka.com> 0009 0010 SPDX-FileCopyrightText: 2007 Paolo Capriotti <p.capriotti@gmail.com> 0011 SPDX-FileCopyrightText: 2007 Ivan Cukic <ivan.cukic+kde@gmail.com> 0012 SPDX-FileCopyrightText: 2008 Petri Damsten <damu@iki.fi> 0013 SPDX-FileCopyrightText: 2000 TrollTech AS. 0014 0015 SPDX-License-Identifier: GPL-2.0-only 0016 */ 0017 0018 #include "kcmstyle.h" 0019 #include "kcm_style_debug.h" 0020 0021 #include "../kcms-common_p.h" 0022 #include "styleconfdialog.h" 0023 0024 #include <KConfigGroup> 0025 #include <KLocalizedString> 0026 #include <KPluginFactory> 0027 #include <KToolBar> 0028 0029 #include <QDBusPendingCallWatcher> 0030 #include <QDBusPendingReply> 0031 #include <QLibrary> 0032 #include <QMetaEnum> 0033 #include <QQuickItem> 0034 #include <QQuickRenderControl> 0035 #include <QQuickWindow> 0036 #include <QStyleFactory> 0037 #include <QWidget> 0038 #include <QWindow> 0039 0040 #include "krdb.h" 0041 0042 #include "kded_interface.h" 0043 0044 #include "previewitem.h" 0045 #include "styledata.h" 0046 0047 using namespace Qt::StringLiterals; 0048 0049 K_PLUGIN_FACTORY_WITH_JSON(KCMStyleFactory, "kcm_style.json", registerPlugin<KCMStyle>(); registerPlugin<StyleData>();) 0050 0051 extern "C" { 0052 Q_DECL_EXPORT void kcminit() 0053 { 0054 uint flags = KRdbExportQtSettings | KRdbExportQtColors | KRdbExportXftSettings | KRdbExportGtkTheme; 0055 KConfig _config(QStringLiteral("kcmdisplayrc"), KConfig::NoGlobals); 0056 KConfigGroup config(&_config, QStringLiteral("X11")); 0057 0058 // This key is written by the "colors" module. 0059 bool exportKDEColors = config.readEntry("exportKDEColors", true); 0060 if (exportKDEColors) { 0061 flags |= KRdbExportColors; 0062 } 0063 runRdb(flags); 0064 } 0065 } 0066 0067 KCMStyle::KCMStyle(QObject *parent, const KPluginMetaData &data) 0068 : KQuickManagedConfigModule(parent, data) 0069 , m_data(new StyleData(this)) 0070 , m_model(new StylesModel(this)) 0071 { 0072 const char *uri{"org.kde.private.kcms.style"}; 0073 0074 qmlRegisterUncreatableType<KCMStyle>(uri, 1, 0, "KCM", QStringLiteral("Cannot create instances of KCM")); 0075 qmlRegisterAnonymousType<StyleSettings>(uri, 1); 0076 qmlRegisterAnonymousType<StylesModel>(uri, 1); 0077 qmlRegisterType<PreviewItem>(uri, 1, 0, "PreviewItem"); 0078 0079 connect(m_model, &StylesModel::selectedStyleChanged, this, [this](const QString &style) { 0080 styleSettings()->setWidgetStyle(style); 0081 }); 0082 connect(styleSettings(), &StyleSettings::widgetStyleChanged, this, [this] { 0083 m_model->setSelectedStyle(styleSettings()->widgetStyle()); 0084 }); 0085 connect(styleSettings(), &StyleSettings::iconsOnButtonsChanged, this, [this] { 0086 m_effectsDirty = true; 0087 }); 0088 connect(styleSettings(), &StyleSettings::iconsInMenusChanged, this, [this] { 0089 m_effectsDirty = true; 0090 }); 0091 0092 m_gtkPage = new GtkPage(this); 0093 connect(m_gtkPage, &GtkPage::gtkThemeSettingsChanged, this, [this]() { 0094 settingsChanged(); 0095 }); 0096 } 0097 0098 KCMStyle::~KCMStyle() = default; 0099 0100 GtkPage *KCMStyle::gtkPage() const 0101 { 0102 return m_gtkPage; 0103 } 0104 0105 StylesModel *KCMStyle::model() const 0106 { 0107 return m_model; 0108 } 0109 0110 StyleSettings *KCMStyle::styleSettings() const 0111 { 0112 return m_data->settings(); 0113 } 0114 0115 KCMStyle::ToolBarStyle KCMStyle::mainToolBarStyle() const 0116 { 0117 return m_mainToolBarStyle; 0118 } 0119 0120 void KCMStyle::setMainToolBarStyle(ToolBarStyle style) 0121 { 0122 if (m_mainToolBarStyle != style) { 0123 m_mainToolBarStyle = style; 0124 Q_EMIT mainToolBarStyleChanged(); 0125 0126 const QMetaEnum toolBarStyleEnum = QMetaEnum::fromType<ToolBarStyle>(); 0127 styleSettings()->setToolButtonStyle(toolBarStyleEnum.valueToKey(m_mainToolBarStyle)); 0128 m_effectsDirty = true; 0129 } 0130 } 0131 0132 KCMStyle::ToolBarStyle KCMStyle::otherToolBarStyle() const 0133 { 0134 return m_otherToolBarStyle; 0135 } 0136 0137 void KCMStyle::setOtherToolBarStyle(ToolBarStyle style) 0138 { 0139 if (m_otherToolBarStyle != style) { 0140 m_otherToolBarStyle = style; 0141 Q_EMIT otherToolBarStyleChanged(); 0142 0143 const QMetaEnum toolBarStyleEnum = QMetaEnum::fromType<ToolBarStyle>(); 0144 styleSettings()->setToolButtonStyleOtherToolbars(toolBarStyleEnum.valueToKey(m_otherToolBarStyle)); 0145 m_effectsDirty = true; 0146 } 0147 } 0148 0149 void KCMStyle::configure(const QString &title, const QString &styleName, QQuickItem *ctx) 0150 { 0151 if (m_styleConfigDialog) { 0152 return; 0153 } 0154 0155 const QString configPage = m_model->styleConfigPage(styleName); 0156 if (configPage.isEmpty()) { 0157 return; 0158 } 0159 0160 QLibrary library(QPluginLoader(configPage).fileName()); 0161 if (!library.load()) { 0162 qCWarning(KCM_STYLE_DEBUG) << "Failed to load style config page" << configPage << library.errorString(); 0163 Q_EMIT showErrorMessage(i18n("There was an error loading the configuration dialog for this style.")); 0164 return; 0165 } 0166 0167 auto allocPtr = library.resolve("allocate_kstyle_config"); 0168 if (!allocPtr) { 0169 qCWarning(KCM_STYLE_DEBUG) << "Failed to resolve allocate_kstyle_config in" << configPage; 0170 Q_EMIT showErrorMessage(i18n("There was an error loading the configuration dialog for this style.")); 0171 return; 0172 } 0173 0174 m_styleConfigDialog = new StyleConfigDialog(nullptr /*this*/, title); 0175 m_styleConfigDialog->setAttribute(Qt::WA_DeleteOnClose); 0176 m_styleConfigDialog->setWindowModality(Qt::WindowModal); 0177 m_styleConfigDialog->winId(); // so it creates windowHandle 0178 0179 if (ctx && ctx->window()) { 0180 if (QWindow *actualWindow = QQuickRenderControl::renderWindowFor(ctx->window())) { 0181 m_styleConfigDialog->windowHandle()->setTransientParent(actualWindow); 0182 } 0183 } 0184 0185 typedef QWidget *(*factoryRoutine)(QWidget *parent); 0186 0187 // Get the factory, and make the widget. 0188 factoryRoutine factory = (factoryRoutine)(allocPtr); // Grmbl. So here I am on my 0189 //"never use C casts" moralizing streak, and I find that one can't go void* -> function ptr 0190 // even with a reinterpret_cast. 0191 0192 QWidget *pluginConfig = factory(m_styleConfigDialog.data()); 0193 0194 // Insert it in... 0195 m_styleConfigDialog->setMainWidget(pluginConfig); 0196 0197 //..and connect it to the wrapper 0198 connect(pluginConfig, SIGNAL(changed(bool)), m_styleConfigDialog.data(), SLOT(setDirty(bool))); 0199 connect(m_styleConfigDialog.data(), SIGNAL(defaults()), pluginConfig, SLOT(defaults())); 0200 connect(m_styleConfigDialog.data(), SIGNAL(save()), pluginConfig, SLOT(save())); 0201 0202 connect(m_styleConfigDialog.data(), &QDialog::accepted, this, [this, styleName] { 0203 if (!m_styleConfigDialog->isDirty()) { 0204 return; 0205 } 0206 0207 // Force re-rendering of the preview, to apply settings 0208 Q_EMIT styleReconfigured(styleName); 0209 0210 // For now, ask all KDE apps to recreate their styles to apply the setitngs 0211 notifyKcmChange(GlobalChangeType::StyleChanged); 0212 0213 // When user edited a style, assume they want to use it, too 0214 styleSettings()->setWidgetStyle(styleName); 0215 }); 0216 0217 m_styleConfigDialog->show(); 0218 } 0219 0220 bool KCMStyle::gtkConfigKdedModuleLoaded() const 0221 { 0222 return m_gtkConfigKdedModuleLoaded; 0223 } 0224 0225 void KCMStyle::checkGtkConfigKdedModuleLoaded() 0226 { 0227 org::kde::kded6 kdedInterface(QStringLiteral("org.kde.kded6"), QStringLiteral("/kded"), QDBusConnection::sessionBus()); 0228 auto call = kdedInterface.loadedModules(); 0229 auto *watcher = new QDBusPendingCallWatcher(call, this); 0230 connect(watcher, &QDBusPendingCallWatcher::finished, this, [this](QDBusPendingCallWatcher *watcher) { 0231 QDBusPendingReply<QStringList> reply = *watcher; 0232 watcher->deleteLater(); 0233 0234 if (reply.isError()) { 0235 qCWarning(KCM_STYLE_DEBUG) << "Failed to check whether GTK Config KDED module is loaded" << reply.error().message(); 0236 return; 0237 } 0238 0239 const bool isLoaded = reply.value().contains(QLatin1String("gtkconfig")); 0240 if (m_gtkConfigKdedModuleLoaded != isLoaded) { 0241 m_gtkConfigKdedModuleLoaded = isLoaded; 0242 Q_EMIT gtkConfigKdedModuleLoadedChanged(); 0243 } 0244 }); 0245 } 0246 0247 void KCMStyle::load() 0248 { 0249 checkGtkConfigKdedModuleLoaded(); 0250 0251 m_gtkPage->load(); 0252 0253 KQuickManagedConfigModule::load(); 0254 m_model->load(); 0255 m_previousStyle = styleSettings()->widgetStyle(); 0256 0257 loadSettingsToModel(); 0258 0259 m_effectsDirty = false; 0260 } 0261 0262 void KCMStyle::save() 0263 { 0264 m_gtkPage->save(); 0265 0266 // Check whether the new style can actually be loaded before saving it. 0267 // Otherwise apps will use the default style despite something else having been written to the config 0268 bool newStyleLoaded = false; 0269 if (styleSettings()->widgetStyle() != m_previousStyle) { 0270 std::unique_ptr<QStyle> newStyle(QStyleFactory::create(styleSettings()->widgetStyle())); 0271 if (newStyle) { 0272 newStyleLoaded = true; 0273 m_previousStyle = styleSettings()->widgetStyle(); 0274 } else { 0275 const QString styleDisplay = m_model->data(m_model->index(m_model->indexOfStyle(styleSettings()->widgetStyle()), 0), Qt::DisplayRole).toString(); 0276 Q_EMIT showErrorMessage(i18n("Failed to apply selected style '%1'.", styleDisplay)); 0277 0278 // Reset selected style back to current in case of failure 0279 styleSettings()->setWidgetStyle(m_previousStyle); 0280 } 0281 } 0282 0283 KQuickManagedConfigModule::save(); 0284 0285 // Export the changes we made to qtrc, and update all qt-only 0286 // applications on the fly, ensuring that we still follow the user's 0287 // export fonts/colors settings. 0288 uint flags = KRdbExportQtSettings | KRdbExportGtkTheme; 0289 KConfig _kconfig(QStringLiteral("kcmdisplayrc"), KConfig::NoGlobals); 0290 KConfigGroup kconfig(&_kconfig, u"X11"_s); 0291 bool exportKDEColors = kconfig.readEntry("exportKDEColors", true); 0292 if (exportKDEColors) { 0293 flags |= KRdbExportColors; 0294 } 0295 runRdb(flags); 0296 0297 // Now allow KDE apps to reconfigure themselves. 0298 if (newStyleLoaded) { 0299 notifyKcmChange(GlobalChangeType::StyleChanged); 0300 } 0301 0302 if (m_effectsDirty) { 0303 // This notifies listeners about: 0304 // - GraphicEffectsLevel' config entry, (e.g. to set QPlatformTheme::ThemeHint::UiEffects) 0305 // - ShowIconsOnPushButtons config entry, (e.g. to set QPlatformTheme::DialogButtonBoxButtonsHaveIcons) 0306 notifyKcmChange(GlobalChangeType::SettingsChanged, GlobalSettingsCategory::SETTINGS_STYLE); 0307 0308 // FIXME - Doesn't apply all settings correctly due to bugs in KApplication/KToolbar. 0309 // Is this ^ still an issue? 0310 KToolBar::emitToolbarStyleChanged(); 0311 } 0312 0313 m_effectsDirty = false; 0314 } 0315 0316 void KCMStyle::defaults() 0317 { 0318 m_gtkPage->defaults(); 0319 0320 // TODO the old code had a fallback chain but do we actually support not having Breeze for Plasma? 0321 // defaultStyle() -> oxygen -> plastique -> windows -> platinum -> motif 0322 0323 KQuickManagedConfigModule::defaults(); 0324 0325 loadSettingsToModel(); 0326 } 0327 0328 void KCMStyle::loadSettingsToModel() 0329 { 0330 Q_EMIT styleSettings()->widgetStyleChanged(); 0331 0332 const QMetaEnum toolBarStyleEnum = QMetaEnum::fromType<ToolBarStyle>(); 0333 setMainToolBarStyle(static_cast<ToolBarStyle>(toolBarStyleEnum.keyToValue(qUtf8Printable(styleSettings()->toolButtonStyle())))); 0334 setOtherToolBarStyle(static_cast<ToolBarStyle>(toolBarStyleEnum.keyToValue(qUtf8Printable(styleSettings()->toolButtonStyleOtherToolbars())))); 0335 } 0336 0337 bool KCMStyle::isDefaults() const 0338 { 0339 return m_gtkPage->isDefaults(); 0340 } 0341 0342 bool KCMStyle::isSaveNeeded() const 0343 { 0344 return m_gtkPage->isSaveNeeded(); 0345 } 0346 0347 #include "kcmstyle.moc"