1 /*
2     KWin - the KDE window manager
3     This file is part of the KDE project.
4 
5     SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
6 
7     SPDX-License-Identifier: GPL-2.0-or-later
8 */
9 #include "settings.h"
10 // KWin
11 #include "decorationbridge.h"
12 #include "composite.h"
13 #include "virtualdesktops.h"
14 #include "workspace.h"
15 #include "appmenu.h"
16 
17 #include <config-kwin.h>
18 
19 #include <KDecoration2/DecorationSettings>
20 
21 #include <KConfigGroup>
22 
23 #include <QFontDatabase>
24 
25 namespace KWin
26 {
27 namespace Decoration
28 {
SettingsImpl(KDecoration2::DecorationSettings * parent)29 SettingsImpl::SettingsImpl(KDecoration2::DecorationSettings *parent)
30     : QObject()
31     , DecorationSettingsPrivate(parent)
32     , m_borderSize(KDecoration2::BorderSize::Normal)
33 {
34     readSettings();
35 
36     auto c = connect(Compositor::self(), &Compositor::compositingToggled,
37             parent, &KDecoration2::DecorationSettings::alphaChannelSupportedChanged);
38     connect(VirtualDesktopManager::self(), &VirtualDesktopManager::countChanged, this,
39         [parent](uint previous, uint current) {
40             if (previous != 1 && current != 1) {
41                 return;
42             }
43             Q_EMIT parent->onAllDesktopsAvailableChanged(current > 1);
44         }
45     );
46     // prevent changes in Decoration due to Compositor being destroyed
47     connect(Compositor::self(), &Compositor::aboutToDestroy, this,
48         [c] { disconnect(c); }
49     );
50     connect(Workspace::self(), &Workspace::configChanged, this, &SettingsImpl::readSettings);
51     connect(DecorationBridge::self(), &DecorationBridge::metaDataLoaded, this, &SettingsImpl::readSettings);
52 }
53 
54 SettingsImpl::~SettingsImpl() = default;
55 
isAlphaChannelSupported() const56 bool SettingsImpl::isAlphaChannelSupported() const
57 {
58     return Compositor::self()->compositing();
59 }
60 
isOnAllDesktopsAvailable() const61 bool SettingsImpl::isOnAllDesktopsAvailable() const
62 {
63     return VirtualDesktopManager::self()->count() > 1;
64 }
65 
isCloseOnDoubleClickOnMenu() const66 bool SettingsImpl::isCloseOnDoubleClickOnMenu() const
67 {
68     return m_closeDoubleClickMenu;
69 }
70 
71 static QHash<KDecoration2::DecorationButtonType, QChar> s_buttonNames;
initButtons()72 static void initButtons()
73 {
74     if (!s_buttonNames.isEmpty()) {
75         return;
76     }
77     s_buttonNames[KDecoration2::DecorationButtonType::Menu]            = QChar('M');
78     s_buttonNames[KDecoration2::DecorationButtonType::ApplicationMenu] = QChar('N');
79     s_buttonNames[KDecoration2::DecorationButtonType::OnAllDesktops]   = QChar('S');
80     s_buttonNames[KDecoration2::DecorationButtonType::ContextHelp]     = QChar('H');
81     s_buttonNames[KDecoration2::DecorationButtonType::Minimize]        = QChar('I');
82     s_buttonNames[KDecoration2::DecorationButtonType::Maximize]        = QChar('A');
83     s_buttonNames[KDecoration2::DecorationButtonType::Close]           = QChar('X');
84     s_buttonNames[KDecoration2::DecorationButtonType::KeepAbove]       = QChar('F');
85     s_buttonNames[KDecoration2::DecorationButtonType::KeepBelow]       = QChar('B');
86     s_buttonNames[KDecoration2::DecorationButtonType::Shade]           = QChar('L');
87 }
88 
buttonsToString(const QVector<KDecoration2::DecorationButtonType> & buttons)89 static QString buttonsToString(const QVector<KDecoration2::DecorationButtonType> &buttons)
90 {
91     auto buttonToString = [](KDecoration2::DecorationButtonType button) -> QChar {
92         const auto it = s_buttonNames.constFind(button);
93         if (it != s_buttonNames.constEnd()) {
94             return it.value();
95         }
96         return QChar();
97     };
98     QString ret;
99     for (auto button : buttons) {
100         ret.append(buttonToString(button));
101     }
102     return ret;
103 }
104 
readDecorationButtons(const KConfigGroup & config,const char * key,const QVector<KDecoration2::DecorationButtonType> & defaultValue) const105 QVector< KDecoration2::DecorationButtonType > SettingsImpl::readDecorationButtons(const KConfigGroup &config,
106                                                                                     const char *key,
107                                                                                     const QVector< KDecoration2::DecorationButtonType > &defaultValue) const
108 {
109     initButtons();
110     auto buttonsFromString = [](const QString &buttons) -> QVector<KDecoration2::DecorationButtonType> {
111         QVector<KDecoration2::DecorationButtonType> ret;
112         for (auto it = buttons.begin(); it != buttons.end(); ++it) {
113             for (auto it2 = s_buttonNames.constBegin(); it2 != s_buttonNames.constEnd(); ++it2) {
114                 if (it2.value() == (*it)) {
115                     ret << it2.key();
116                 }
117             }
118         }
119         return ret;
120     };
121     return buttonsFromString(config.readEntry(key, buttonsToString(defaultValue)));
122 }
123 
stringToSize(const QString & name)124 static KDecoration2::BorderSize stringToSize(const QString &name)
125 {
126     static const QMap<QString, KDecoration2::BorderSize> s_sizes = QMap<QString, KDecoration2::BorderSize>({
127         {QStringLiteral("None"), KDecoration2::BorderSize::None},
128         {QStringLiteral("NoSides"), KDecoration2::BorderSize::NoSides},
129         {QStringLiteral("Tiny"), KDecoration2::BorderSize::Tiny},
130         {QStringLiteral("Normal"), KDecoration2::BorderSize::Normal},
131         {QStringLiteral("Large"), KDecoration2::BorderSize::Large},
132         {QStringLiteral("VeryLarge"), KDecoration2::BorderSize::VeryLarge},
133         {QStringLiteral("Huge"), KDecoration2::BorderSize::Huge},
134         {QStringLiteral("VeryHuge"), KDecoration2::BorderSize::VeryHuge},
135         {QStringLiteral("Oversized"), KDecoration2::BorderSize::Oversized}
136     });
137     auto it = s_sizes.constFind(name);
138     if (it == s_sizes.constEnd()) {
139         // non sense values are interpreted just like normal
140         return KDecoration2::BorderSize::Normal;
141     }
142     return it.value();
143 }
144 
readSettings()145 void SettingsImpl::readSettings()
146 {
147     KConfigGroup config = kwinApp()->config()->group(QStringLiteral("org.kde.kdecoration2"));
148     const auto &left = readDecorationButtons(config, "ButtonsOnLeft", QVector<KDecoration2::DecorationButtonType >({
149         KDecoration2::DecorationButtonType::Menu,
150         KDecoration2::DecorationButtonType::OnAllDesktops
151     }));
152     if (left != m_leftButtons) {
153         m_leftButtons = left;
154         Q_EMIT decorationSettings()->decorationButtonsLeftChanged(m_leftButtons);
155     }
156     const auto &right = readDecorationButtons(config, "ButtonsOnRight", QVector<KDecoration2::DecorationButtonType >({
157         KDecoration2::DecorationButtonType::ContextHelp,
158         KDecoration2::DecorationButtonType::Minimize,
159         KDecoration2::DecorationButtonType::Maximize,
160         KDecoration2::DecorationButtonType::Close
161     }));
162     if (right != m_rightButtons) {
163         m_rightButtons = right;
164         Q_EMIT decorationSettings()->decorationButtonsRightChanged(m_rightButtons);
165     }
166     ApplicationMenu::self()->setViewEnabled(left.contains(KDecoration2::DecorationButtonType::ApplicationMenu) || right.contains(KDecoration2::DecorationButtonType::ApplicationMenu));
167     const bool close = config.readEntry("CloseOnDoubleClickOnMenu", false);
168     if (close != m_closeDoubleClickMenu) {
169         m_closeDoubleClickMenu = close;
170         Q_EMIT decorationSettings()->closeOnDoubleClickOnMenuChanged(m_closeDoubleClickMenu);
171     }
172     m_autoBorderSize = config.readEntry("BorderSizeAuto", true);
173 
174     auto size = stringToSize(config.readEntry("BorderSize", QStringLiteral("Normal")));
175     if (m_autoBorderSize) {
176         /* Falls back to Normal border size, if the plugin does not provide a valid recommendation. */
177         size = stringToSize(DecorationBridge::self()->recommendedBorderSize());
178     }
179     if (size != m_borderSize) {
180         m_borderSize = size;
181         Q_EMIT decorationSettings()->borderSizeChanged(m_borderSize);
182     }
183     const QFont font = QFontDatabase::systemFont(QFontDatabase::TitleFont);
184     if (font != m_font) {
185         m_font = font;
186         Q_EMIT decorationSettings()->fontChanged(m_font);
187     }
188 
189     Q_EMIT decorationSettings()->reconfigured();
190 }
191 
192 }
193 }
194