File indexing completed on 2026-08-09 14:49:15

0001 /*
0002     SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
0003 
0004     SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0005 */
0006 #include "previewbridge.h"
0007 #include "previewclient.h"
0008 #include "previewitem.h"
0009 #include "previewsettings.h"
0010 
0011 #include <KDecoration2/DecoratedClient>
0012 #include <KDecoration2/Decoration>
0013 
0014 #include <KCModule>
0015 #include <KPluginFactory>
0016 #include <KPluginMetaData>
0017 
0018 #include <QDBusConnection>
0019 #include <QDBusMessage>
0020 #include <QDebug>
0021 #include <QDialog>
0022 #include <QDialogButtonBox>
0023 #include <QPushButton>
0024 #include <QQuickItem>
0025 #include <QQuickRenderControl>
0026 #include <QQuickWindow>
0027 #include <QVBoxLayout>
0028 #include <QWindow>
0029 
0030 namespace KDecoration2
0031 {
0032 namespace Preview
0033 {
0034 
0035 static const QString s_pluginName = QStringLiteral("org.kde.kdecoration2");
0036 
0037 PreviewBridge::PreviewBridge(QObject *parent)
0038     : DecorationBridge(parent)
0039     , m_lastCreatedClient(nullptr)
0040     , m_lastCreatedSettings(nullptr)
0041     , m_valid(false)
0042 {
0043     connect(this, &PreviewBridge::pluginChanged, this, &PreviewBridge::createFactory);
0044 }
0045 
0046 PreviewBridge::~PreviewBridge() = default;
0047 
0048 std::unique_ptr<DecoratedClientPrivate> PreviewBridge::createClient(DecoratedClient *client, Decoration *decoration)
0049 {
0050     auto ptr = std::unique_ptr<PreviewClient>(new PreviewClient(client, decoration));
0051     m_lastCreatedClient = ptr.get();
0052     return ptr;
0053 }
0054 
0055 std::unique_ptr<DecorationSettingsPrivate> PreviewBridge::settings(DecorationSettings *parent)
0056 {
0057     auto ptr = std::unique_ptr<PreviewSettings>(new PreviewSettings(parent));
0058     m_lastCreatedSettings = ptr.get();
0059     return ptr;
0060 }
0061 
0062 void PreviewBridge::registerPreviewItem(PreviewItem *item)
0063 {
0064     m_previewItems.append(item);
0065 }
0066 
0067 void PreviewBridge::unregisterPreviewItem(PreviewItem *item)
0068 {
0069     m_previewItems.removeAll(item);
0070 }
0071 
0072 void PreviewBridge::setPlugin(const QString &plugin)
0073 {
0074     if (m_plugin == plugin) {
0075         return;
0076     }
0077     m_plugin = plugin;
0078     Q_EMIT pluginChanged();
0079 }
0080 
0081 QString PreviewBridge::theme() const
0082 {
0083     return m_theme;
0084 }
0085 
0086 void PreviewBridge::setTheme(const QString &theme)
0087 {
0088     if (m_theme == theme) {
0089         return;
0090     }
0091     m_theme = theme;
0092     Q_EMIT themeChanged();
0093 }
0094 
0095 QString PreviewBridge::plugin() const
0096 {
0097     return m_plugin;
0098 }
0099 
0100 void PreviewBridge::createFactory()
0101 {
0102     m_factory.clear();
0103 
0104     if (m_plugin.isNull()) {
0105         setValid(false);
0106         qWarning() << "Plugin not set";
0107         return;
0108     }
0109 
0110     const auto offers = KPluginMetaData::findPlugins(s_pluginName);
0111     auto item = std::find_if(offers.constBegin(), offers.constEnd(), [this](const auto &plugin) {
0112         return plugin.pluginId() == m_plugin;
0113     });
0114     if (item != offers.constEnd()) {
0115         m_factory = KPluginFactory::loadFactory(*item).plugin;
0116     }
0117 
0118     setValid(!m_factory.isNull());
0119 }
0120 
0121 bool PreviewBridge::isValid() const
0122 {
0123     return m_valid;
0124 }
0125 
0126 void PreviewBridge::setValid(bool valid)
0127 {
0128     if (m_valid == valid) {
0129         return;
0130     }
0131     m_valid = valid;
0132     Q_EMIT validChanged();
0133 }
0134 
0135 Decoration *PreviewBridge::createDecoration(QObject *parent)
0136 {
0137     if (!m_valid) {
0138         return nullptr;
0139     }
0140     QVariantMap args({{QStringLiteral("bridge"), QVariant::fromValue(this)}});
0141     if (!m_theme.isNull()) {
0142         args.insert(QStringLiteral("theme"), m_theme);
0143     }
0144     return m_factory->create<KDecoration2::Decoration>(parent, QVariantList({args}));
0145 }
0146 
0147 DecorationButton *PreviewBridge::createButton(KDecoration2::Decoration *decoration, KDecoration2::DecorationButtonType type, QObject *parent)
0148 {
0149     if (!m_valid) {
0150         return nullptr;
0151     }
0152     return m_factory->create<KDecoration2::DecorationButton>(parent, QVariantList({QVariant::fromValue(type), QVariant::fromValue(decoration)}));
0153 }
0154 
0155 void PreviewBridge::configure(QQuickItem *ctx)
0156 {
0157     if (!m_valid) {
0158         return;
0159     }
0160     // setup the UI
0161     QDialog *dialog = new QDialog();
0162     dialog->setAttribute(Qt::WA_DeleteOnClose);
0163     if (m_lastCreatedClient) {
0164         dialog->setWindowTitle(m_lastCreatedClient->caption());
0165     }
0166 
0167     // create the KCModule through the plugintrader
0168     QVariantMap args;
0169     if (!m_theme.isNull()) {
0170         args.insert(QStringLiteral("theme"), m_theme);
0171     }
0172 
0173     KCModule *kcm = m_factory->create<KCModule>(dialog, QVariantList({args}));
0174 
0175     if (!kcm) {
0176         return;
0177     }
0178 
0179     auto save = [this, kcm] {
0180         kcm->save();
0181         if (m_lastCreatedSettings) {
0182             Q_EMIT m_lastCreatedSettings->decorationSettings()->reconfigured();
0183         }
0184         // Send signal to all kwin instances
0185         QDBusMessage message = QDBusMessage::createSignal(QStringLiteral("/KWin"),
0186                                                           QStringLiteral("org.kde.KWin"),
0187                                                           QStringLiteral("reloadConfig"));
0188         QDBusConnection::sessionBus().send(message);
0189     };
0190     connect(dialog, &QDialog::accepted, this, save);
0191 
0192     QDialogButtonBox *buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel | QDialogButtonBox::RestoreDefaults | QDialogButtonBox::Reset,
0193                                                      dialog);
0194 
0195     QPushButton *reset = buttons->button(QDialogButtonBox::Reset);
0196     reset->setEnabled(false);
0197     // Here we connect our buttons with the dialog
0198     connect(buttons, &QDialogButtonBox::accepted, dialog, &QDialog::accept);
0199     connect(buttons, &QDialogButtonBox::rejected, dialog, &QDialog::reject);
0200     connect(reset, &QPushButton::clicked, kcm, &KCModule::load);
0201     auto changedSignal = static_cast<void (KCModule::*)(bool)>(&KCModule::changed);
0202     connect(kcm, changedSignal, reset, &QPushButton::setEnabled);
0203     connect(buttons->button(QDialogButtonBox::RestoreDefaults), &QPushButton::clicked, kcm, &KCModule::defaults);
0204 
0205     QVBoxLayout *layout = new QVBoxLayout(dialog);
0206     layout->addWidget(kcm);
0207     layout->addWidget(buttons);
0208 
0209     if (ctx->window()) {
0210         dialog->winId(); // so it creates windowHandle
0211         dialog->windowHandle()->setTransientParent(QQuickRenderControl::renderWindowFor(ctx->window()));
0212         dialog->setModal(true);
0213     }
0214 
0215     dialog->show();
0216 }
0217 
0218 BridgeItem::BridgeItem(QObject *parent)
0219     : QObject(parent)
0220     , m_bridge(new PreviewBridge())
0221 {
0222     connect(m_bridge, &PreviewBridge::themeChanged, this, &BridgeItem::themeChanged);
0223     connect(m_bridge, &PreviewBridge::pluginChanged, this, &BridgeItem::pluginChanged);
0224     connect(m_bridge, &PreviewBridge::validChanged, this, &BridgeItem::validChanged);
0225 }
0226 
0227 BridgeItem::~BridgeItem()
0228 {
0229     m_bridge->deleteLater();
0230 }
0231 
0232 }
0233 }