Warning, file /plasma/plasma-workspace/kcms/style/kcmstyle.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

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