File indexing completed on 2024-11-10 04:56:52

0001 /*
0002     KWin - the KDE window manager
0003     This file is part of the KDE project.
0004 
0005     SPDX-FileCopyrightText: 2009 Martin Gräßlin <mgraesslin@kde.org>
0006     SPDX-FileCopyrightText: 2020 Cyril Rossi <cyril.rossi@enioka.com>
0007     SPDX-FileCopyrightText: 2023 Ismael Asensio <isma.af@gmail.com>
0008 
0009     SPDX-License-Identifier: GPL-2.0-or-later
0010 */
0011 #include "main.h"
0012 #include <kwin_effects_interface.h>
0013 
0014 // Qt
0015 #include <QDBusConnection>
0016 #include <QDBusMessage>
0017 #include <QDialog>
0018 #include <QDialogButtonBox>
0019 #include <QHBoxLayout>
0020 #include <QPointer>
0021 #include <QPushButton>
0022 #include <QSpacerItem>
0023 #include <QStandardItemModel>
0024 #include <QStandardPaths>
0025 #include <QTabWidget>
0026 #include <QVBoxLayout>
0027 
0028 // KDE
0029 #include <KLocalizedString>
0030 #include <KNSWidgets/Button>
0031 #include <KPluginFactory>
0032 #include <KTitleWidget>
0033 // Plasma
0034 #include <KPackage/Package>
0035 #include <KPackage/PackageLoader>
0036 
0037 // own
0038 #include "kwinpluginssettings.h"
0039 #include "kwintabboxconfigform.h"
0040 #include "kwintabboxdata.h"
0041 #include "kwintabboxsettings.h"
0042 #include "layoutpreview.h"
0043 #include "shortcutsettings.h"
0044 
0045 K_PLUGIN_FACTORY_WITH_JSON(KWinTabBoxConfigFactory, "kcm_kwintabbox.json", registerPlugin<KWin::KWinTabBoxConfig>(); registerPlugin<KWin::TabBox::KWinTabboxData>();)
0046 
0047 namespace KWin
0048 {
0049 
0050 using namespace TabBox;
0051 
0052 KWinTabBoxConfig::KWinTabBoxConfig(QObject *parent, const KPluginMetaData &data)
0053     : KCModule(parent, data)
0054     , m_config(KSharedConfig::openConfig("kwinrc"))
0055     , m_data(new KWinTabboxData(this))
0056 {
0057     QTabWidget *tabWidget = new QTabWidget(widget());
0058     m_primaryTabBoxUi = new KWinTabBoxConfigForm(KWinTabBoxConfigForm::TabboxType::Main,
0059                                                  m_data->tabBoxConfig(),
0060                                                  m_data->shortcutConfig(),
0061                                                  tabWidget);
0062     m_alternativeTabBoxUi = new KWinTabBoxConfigForm(KWinTabBoxConfigForm::TabboxType::Alternative,
0063                                                      m_data->tabBoxAlternativeConfig(),
0064                                                      m_data->shortcutConfig(),
0065                                                      tabWidget);
0066     tabWidget->addTab(m_primaryTabBoxUi, i18n("Main"));
0067     tabWidget->addTab(m_alternativeTabBoxUi, i18n("Alternative"));
0068 
0069     KNSWidgets::Button *ghnsButton = new KNSWidgets::Button(i18n("Get New Task Switchers..."), QStringLiteral("kwinswitcher.knsrc"), widget());
0070     connect(ghnsButton, &KNSWidgets::Button::dialogFinished, this, [this](auto changedEntries) {
0071         if (!changedEntries.isEmpty()) {
0072             initLayoutLists();
0073         }
0074     });
0075 
0076     QHBoxLayout *buttonBar = new QHBoxLayout();
0077     QSpacerItem *buttonBarSpacer = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
0078     buttonBar->addItem(buttonBarSpacer);
0079     buttonBar->addWidget(ghnsButton);
0080 
0081     QVBoxLayout *layout = new QVBoxLayout(widget());
0082     KTitleWidget *infoLabel = new KTitleWidget(tabWidget);
0083     infoLabel->setText(i18n("Focus policy settings limit the functionality of navigating through windows."),
0084                        KTitleWidget::InfoMessage);
0085     infoLabel->setIcon(KTitleWidget::InfoMessage, KTitleWidget::ImageLeft);
0086     layout->addWidget(infoLabel, 0);
0087     layout->addWidget(tabWidget, 1);
0088     layout->addLayout(buttonBar);
0089     widget()->setLayout(layout);
0090 
0091     addConfig(m_data->tabBoxConfig(), m_primaryTabBoxUi);
0092     addConfig(m_data->tabBoxAlternativeConfig(), m_alternativeTabBoxUi);
0093 
0094     initLayoutLists();
0095 
0096     createConnections(m_primaryTabBoxUi);
0097     createConnections(m_alternativeTabBoxUi);
0098 
0099     // check focus policy - we don't offer configs for unreasonable focus policies
0100     KConfigGroup config(m_config, QStringLiteral("Windows"));
0101     QString policy = config.readEntry("FocusPolicy", "ClickToFocus");
0102     if ((policy == "FocusUnderMouse") || (policy == "FocusStrictlyUnderMouse")) {
0103         tabWidget->setEnabled(false);
0104         infoLabel->show();
0105     } else {
0106         infoLabel->hide();
0107     }
0108 }
0109 
0110 KWinTabBoxConfig::~KWinTabBoxConfig()
0111 {
0112 }
0113 
0114 static QList<KPackage::Package> availableLnFPackages()
0115 {
0116     QList<KPackage::Package> packages;
0117     QStringList paths;
0118     const QStringList dataPaths = QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation);
0119 
0120     for (const QString &path : dataPaths) {
0121         QDir dir(path + QLatin1String("/plasma/look-and-feel"));
0122         paths << dir.entryList(QDir::AllDirs | QDir::NoDotAndDotDot);
0123     }
0124 
0125     const auto &p = paths;
0126     for (const QString &path : p) {
0127         KPackage::Package pkg = KPackage::PackageLoader::self()->loadPackage(QStringLiteral("Plasma/LookAndFeel"));
0128         pkg.setPath(path);
0129         pkg.setFallbackPackage(KPackage::Package());
0130         if (!pkg.filePath("defaults").isEmpty()) {
0131             KSharedConfigPtr conf = KSharedConfig::openConfig(pkg.filePath("defaults"));
0132             KConfigGroup cg = KConfigGroup(conf, QStringLiteral("kwinrc"));
0133             cg = KConfigGroup(&cg, QStringLiteral("WindowSwitcher"));
0134             if (!cg.readEntry("LayoutName", QString()).isEmpty()) {
0135                 packages << pkg;
0136             }
0137         }
0138     }
0139 
0140     return packages;
0141 }
0142 
0143 void KWinTabBoxConfig::initLayoutLists()
0144 {
0145     QList<KPluginMetaData> offers = KPackage::PackageLoader::self()->listPackages("KWin/WindowSwitcher");
0146     QStandardItemModel *model = new QStandardItemModel;
0147 
0148     auto addToModel = [model](const QString &name, const QString &pluginId, const QString &path) {
0149         QStandardItem *item = new QStandardItem(name);
0150         item->setData(pluginId, Qt::UserRole);
0151         item->setData(path, KWinTabBoxConfigForm::LayoutPath);
0152         item->setData(true, KWinTabBoxConfigForm::AddonEffect);
0153         model->appendRow(item);
0154     };
0155 
0156     const auto lnfPackages = availableLnFPackages();
0157     for (const auto &package : lnfPackages) {
0158         const auto &metaData = package.metadata();
0159         const QString switcherFile = package.filePath("windowswitcher", QStringLiteral("WindowSwitcher.qml"));
0160         if (switcherFile.isEmpty()) {
0161             // Skip lnfs that don't actually ship a switcher
0162             continue;
0163         }
0164 
0165         addToModel(metaData.name(), metaData.pluginId(), switcherFile);
0166     }
0167 
0168     for (const auto &offer : offers) {
0169         const QString pluginName = offer.pluginId();
0170         const QString scriptFile = QStandardPaths::locate(QStandardPaths::GenericDataLocation,
0171                                                           QLatin1String("kwin/tabbox/") + pluginName + QLatin1String("/contents/ui/main.qml"));
0172         if (scriptFile.isEmpty()) {
0173             qWarning() << "scriptfile is null" << pluginName;
0174             continue;
0175         }
0176 
0177         addToModel(offer.name(), pluginName, scriptFile);
0178     }
0179 
0180     model->sort(0);
0181 
0182     m_primaryTabBoxUi->setEffectComboModel(model);
0183     m_alternativeTabBoxUi->setEffectComboModel(model);
0184 }
0185 
0186 void KWinTabBoxConfig::createConnections(KWinTabBoxConfigForm *form)
0187 {
0188     connect(form, &KWinTabBoxConfigForm::effectConfigButtonClicked, this, &KWinTabBoxConfig::configureEffectClicked);
0189     connect(form, &KWinTabBoxConfigForm::configChanged, this, &KWinTabBoxConfig::updateUnmanagedState);
0190 
0191     connect(this, &KWinTabBoxConfig::defaultsIndicatorsVisibleChanged, form, [form, this]() {
0192         form->setDefaultIndicatorVisible(defaultsIndicatorsVisible());
0193     });
0194 }
0195 
0196 void KWinTabBoxConfig::updateUnmanagedState()
0197 {
0198     const bool isNeedSave = m_data->tabBoxConfig()->isSaveNeeded()
0199         || m_data->tabBoxAlternativeConfig()->isSaveNeeded()
0200         || m_data->shortcutConfig()->isSaveNeeded();
0201 
0202     unmanagedWidgetChangeState(isNeedSave);
0203 
0204     const bool isDefault = m_data->tabBoxConfig()->isDefaults()
0205         && m_data->tabBoxAlternativeConfig()->isDefaults()
0206         && m_data->shortcutConfig()->isDefaults();
0207 
0208     unmanagedWidgetDefaultState(isDefault);
0209 }
0210 
0211 void KWinTabBoxConfig::load()
0212 {
0213     KCModule::load();
0214 
0215     m_data->tabBoxConfig()->load();
0216     m_data->tabBoxAlternativeConfig()->load();
0217     m_data->shortcutConfig()->load();
0218 
0219     m_data->pluginsConfig()->load();
0220 
0221     m_primaryTabBoxUi->updateUiFromConfig();
0222     m_alternativeTabBoxUi->updateUiFromConfig();
0223 
0224     updateUnmanagedState();
0225 }
0226 
0227 void KWinTabBoxConfig::save()
0228 {
0229     // effects
0230     const bool highlightWindows = m_primaryTabBoxUi->highlightWindows() || m_alternativeTabBoxUi->highlightWindows();
0231 
0232     // activate effects if they are used otherwise deactivate them.
0233     m_data->pluginsConfig()->setHighlightwindowEnabled(highlightWindows);
0234     m_data->pluginsConfig()->save();
0235 
0236     m_data->tabBoxConfig()->save();
0237     m_data->tabBoxAlternativeConfig()->save();
0238     m_data->shortcutConfig()->save();
0239 
0240     KCModule::save();
0241     updateUnmanagedState();
0242 
0243     // Reload KWin.
0244     QDBusMessage message = QDBusMessage::createSignal("/KWin", "org.kde.KWin", "reloadConfig");
0245     QDBusConnection::sessionBus().send(message);
0246 }
0247 
0248 void KWinTabBoxConfig::defaults()
0249 {
0250     m_data->tabBoxConfig()->setDefaults();
0251     m_data->tabBoxAlternativeConfig()->setDefaults();
0252     m_data->shortcutConfig()->setDefaults();
0253 
0254     m_primaryTabBoxUi->updateUiFromConfig();
0255     m_alternativeTabBoxUi->updateUiFromConfig();
0256 
0257     KCModule::defaults();
0258     updateUnmanagedState();
0259 }
0260 void KWinTabBoxConfig::configureEffectClicked()
0261 {
0262     auto form = qobject_cast<KWinTabBoxConfigForm *>(sender());
0263     Q_ASSERT(form);
0264 
0265     if (form->effectComboCurrentData(KWinTabBoxConfigForm::AddonEffect).toBool()) {
0266         // Show the preview for addon effect
0267         new LayoutPreview(form->effectComboCurrentData(KWinTabBoxConfigForm::LayoutPath).toString(),
0268                           form->config()->showDesktopMode(),
0269                           this);
0270     }
0271 }
0272 
0273 } // namespace
0274 
0275 #include "main.moc"
0276 
0277 #include "moc_main.cpp"