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 "decoratedclient.h"
10 #include "decorationbridge.h"
11 #include "decorationpalette.h"
12 #include "abstract_client.h"
13 #include "cursor.h"
14 #include "workspace.h"
15 
16 #include <KDecoration2/DecoratedClient>
17 #include <KDecoration2/Decoration>
18 
19 #include <QDebug>
20 #include <QStyle>
21 #include <QToolTip>
22 
23 namespace KWin
24 {
25 namespace Decoration
26 {
27 
DecoratedClientImpl(AbstractClient * client,KDecoration2::DecoratedClient * decoratedClient,KDecoration2::Decoration * decoration)28 DecoratedClientImpl::DecoratedClientImpl(AbstractClient *client, KDecoration2::DecoratedClient *decoratedClient, KDecoration2::Decoration *decoration)
29     : QObject()
30     , ApplicationMenuEnabledDecoratedClientPrivate(decoratedClient, decoration)
31     , m_client(client)
32     , m_clientSize(client->clientSize())
33 {
34     client->setDecoratedClient(QPointer<DecoratedClientImpl>(this));
35     connect(client, &AbstractClient::activeChanged, this,
36         [decoratedClient, client]() {
37             Q_EMIT decoratedClient->activeChanged(client->isActive());
38         }
39     );
40     connect(client, &AbstractClient::clientGeometryChanged, this,
41         [decoratedClient, this]() {
42             if (m_client->clientSize() == m_clientSize) {
43                 return;
44             }
45             const auto oldSize = m_clientSize;
46             m_clientSize = m_client->clientSize();
47             if (oldSize.width() != m_clientSize.width()) {
48                 Q_EMIT decoratedClient->widthChanged(m_clientSize.width());
49             }
50             if (oldSize.height() != m_clientSize.height()) {
51                 Q_EMIT decoratedClient->heightChanged(m_clientSize.height());
52             }
53             Q_EMIT decoratedClient->sizeChanged(m_clientSize);
54         }
55     );
56     connect(client, &AbstractClient::desktopChanged, this,
57         [decoratedClient, client]() {
58             Q_EMIT decoratedClient->onAllDesktopsChanged(client->isOnAllDesktops());
59         }
60     );
61     connect(client, &AbstractClient::captionChanged, this,
62         [decoratedClient, client]() {
63             Q_EMIT decoratedClient->captionChanged(client->caption());
64         }
65     );
66     connect(client, &AbstractClient::iconChanged, this,
67         [decoratedClient, client]() {
68             Q_EMIT decoratedClient->iconChanged(client->icon());
69         }
70     );
71     connect(client, &AbstractClient::shadeChanged, this,
72             &Decoration::DecoratedClientImpl::signalShadeChange);
73     connect(client, &AbstractClient::keepAboveChanged, decoratedClient, &KDecoration2::DecoratedClient::keepAboveChanged);
74     connect(client, &AbstractClient::keepBelowChanged, decoratedClient, &KDecoration2::DecoratedClient::keepBelowChanged);
75     connect(client, &AbstractClient::quickTileModeChanged, decoratedClient,
76         [this, decoratedClient]() {
77             Q_EMIT decoratedClient->adjacentScreenEdgesChanged(adjacentScreenEdges());
78         }
79     );
80     connect(client, &AbstractClient::closeableChanged, decoratedClient, &KDecoration2::DecoratedClient::closeableChanged);
81     connect(client, &AbstractClient::shadeableChanged, decoratedClient, &KDecoration2::DecoratedClient::shadeableChanged);
82     connect(client, &AbstractClient::minimizeableChanged, decoratedClient, &KDecoration2::DecoratedClient::minimizeableChanged);
83     connect(client, &AbstractClient::maximizeableChanged, decoratedClient, &KDecoration2::DecoratedClient::maximizeableChanged);
84 
85     connect(client, &AbstractClient::paletteChanged, decoratedClient, &KDecoration2::DecoratedClient::paletteChanged);
86 
87     connect(client, &AbstractClient::hasApplicationMenuChanged, decoratedClient, &KDecoration2::DecoratedClient::hasApplicationMenuChanged);
88     connect(client, &AbstractClient::applicationMenuActiveChanged, decoratedClient, &KDecoration2::DecoratedClient::applicationMenuActiveChanged);
89 
90     m_toolTipWakeUp.setSingleShot(true);
91     connect(&m_toolTipWakeUp, &QTimer::timeout, this,
92             [this]() {
93                 int fallAsleepDelay = QApplication::style()->styleHint(QStyle::SH_ToolTip_FallAsleepDelay);
94                 this->m_toolTipFallAsleep.setRemainingTime(fallAsleepDelay);
95 
96                 QToolTip::showText(Cursors::self()->mouse()->pos(), this->m_toolTipText);
97                 m_toolTipShowing = true;
98             }
99     );
100 }
101 
~DecoratedClientImpl()102 DecoratedClientImpl::~DecoratedClientImpl()
103 {
104     if (m_toolTipShowing) {
105         requestHideToolTip();
106     }
107 }
108 
signalShadeChange()109 void DecoratedClientImpl::signalShadeChange() {
110     Q_EMIT decoratedClient()->shadedChanged(m_client->isShade());
111 }
112 
113 #define DELEGATE(type, name, clientName) \
114     type DecoratedClientImpl::name() const \
115     { \
116         return m_client->clientName(); \
117     }
118 
119 #define DELEGATE2(type, name) DELEGATE(type, name, name)
120 
DELEGATE2(QString,caption)121 DELEGATE2(QString, caption)
122 DELEGATE2(bool, isActive)
123 DELEGATE2(bool, isCloseable)
124 DELEGATE(bool, isMaximizeable, isMaximizable)
125 DELEGATE(bool, isMinimizeable, isMinimizable)
126 DELEGATE2(bool, isModal)
127 DELEGATE(bool, isMoveable, isMovable)
128 DELEGATE(bool, isResizeable, isResizable)
129 DELEGATE2(bool, isShadeable)
130 DELEGATE2(bool, providesContextHelp)
131 DELEGATE2(int, desktop)
132 DELEGATE2(bool, isOnAllDesktops)
133 DELEGATE2(QPalette, palette)
134 DELEGATE2(QIcon, icon)
135 
136 #undef DELEGATE2
137 #undef DELEGATE
138 
139 #define DELEGATE(type, name, clientName) \
140     type DecoratedClientImpl::name() const \
141     { \
142         return m_client->clientName(); \
143     }
144 
145 DELEGATE(bool, isKeepAbove, keepAbove)
146 DELEGATE(bool, isKeepBelow, keepBelow)
147 DELEGATE(bool, isShaded, isShade)
148 DELEGATE(WId, windowId, window)
149 DELEGATE(WId, decorationId, frameId)
150 
151 #undef DELEGATE
152 
153 #define DELEGATE(name, op) \
154     void DecoratedClientImpl::name() \
155     { \
156         Workspace::self()->performWindowOperation(m_client, Options::op); \
157     }
158 
159 DELEGATE(requestToggleShade, ShadeOp)
160 DELEGATE(requestToggleOnAllDesktops, OnAllDesktopsOp)
161 DELEGATE(requestToggleKeepAbove, KeepAboveOp)
162 DELEGATE(requestToggleKeepBelow, KeepBelowOp)
163 
164 #undef DELEGATE
165 
166 #define DELEGATE(name, clientName) \
167     void DecoratedClientImpl::name() \
168     { \
169         m_client->clientName(); \
170     }
171 
172 DELEGATE(requestContextHelp, showContextHelp)
173 DELEGATE(requestMinimize, minimize)
174 
175 #undef DELEGATE
176 
177 void DecoratedClientImpl::requestClose()
178 {
179     QMetaObject::invokeMethod(m_client, &AbstractClient::closeWindow, Qt::QueuedConnection);
180 }
181 
color(KDecoration2::ColorGroup group,KDecoration2::ColorRole role) const182 QColor DecoratedClientImpl::color(KDecoration2::ColorGroup group, KDecoration2::ColorRole role) const
183 {
184     auto dp = m_client->decorationPalette();
185     if (dp) {
186         return dp->color(group, role);
187     }
188 
189     return QColor();
190 }
191 
requestShowToolTip(const QString & text)192 void DecoratedClientImpl::requestShowToolTip(const QString &text)
193 {
194     if (!DecorationBridge::self()->showToolTips()) {
195         return;
196     }
197 
198     m_toolTipText = text;
199 
200     int wakeUpDelay = QApplication::style()->styleHint(QStyle::SH_ToolTip_WakeUpDelay);
201     m_toolTipWakeUp.start(m_toolTipFallAsleep.hasExpired() ? wakeUpDelay : 20);
202 }
203 
requestHideToolTip()204 void DecoratedClientImpl::requestHideToolTip()
205 {
206     m_toolTipWakeUp.stop();
207     QToolTip::hideText();
208     m_toolTipShowing = false;
209 }
210 
requestShowWindowMenu(const QRect & rect)211 void DecoratedClientImpl::requestShowWindowMenu(const QRect &rect)
212 {
213     Workspace::self()->showWindowMenu(QRect(m_client->pos() + rect.topLeft(), m_client->pos() + rect.bottomRight()), m_client);
214 }
215 
requestShowApplicationMenu(const QRect & rect,int actionId)216 void DecoratedClientImpl::requestShowApplicationMenu(const QRect &rect, int actionId)
217 {
218     Workspace::self()->showApplicationMenu(rect, m_client, actionId);
219 }
220 
showApplicationMenu(int actionId)221 void DecoratedClientImpl::showApplicationMenu(int actionId)
222 {
223     decoration()->showApplicationMenu(actionId);
224 }
225 
requestToggleMaximization(Qt::MouseButtons buttons)226 void DecoratedClientImpl::requestToggleMaximization(Qt::MouseButtons buttons)
227 {
228     QMetaObject::invokeMethod(this, "delayedRequestToggleMaximization", Qt::QueuedConnection, Q_ARG(Options::WindowOperation, options->operationMaxButtonClick(buttons)));
229 }
230 
delayedRequestToggleMaximization(Options::WindowOperation operation)231 void DecoratedClientImpl::delayedRequestToggleMaximization(Options::WindowOperation operation)
232 {
233     Workspace::self()->performWindowOperation(m_client, operation);
234 }
235 
width() const236 int DecoratedClientImpl::width() const
237 {
238     return m_clientSize.width();
239 }
240 
height() const241 int DecoratedClientImpl::height() const
242 {
243     return m_clientSize.height();
244 }
245 
size() const246 QSize DecoratedClientImpl::size() const
247 {
248     return m_clientSize;
249 }
250 
isMaximizedVertically() const251 bool DecoratedClientImpl::isMaximizedVertically() const
252 {
253     return m_client->requestedMaximizeMode() & MaximizeVertical;
254 }
255 
isMaximized() const256 bool DecoratedClientImpl::isMaximized() const
257 {
258     return isMaximizedHorizontally() && isMaximizedVertically();
259 }
260 
isMaximizedHorizontally() const261 bool DecoratedClientImpl::isMaximizedHorizontally() const
262 {
263     return m_client->requestedMaximizeMode() & MaximizeHorizontal;
264 }
265 
adjacentScreenEdges() const266 Qt::Edges DecoratedClientImpl::adjacentScreenEdges() const
267 {
268     Qt::Edges edges;
269     const QuickTileMode mode = m_client->quickTileMode();
270     if (mode.testFlag(QuickTileFlag::Left)) {
271         edges |= Qt::LeftEdge;
272         if (!mode.testFlag(QuickTileFlag::Top) && !mode.testFlag(QuickTileFlag::Bottom)) {
273             // using complete side
274             edges |= Qt::TopEdge | Qt::BottomEdge;
275         }
276     }
277     if (mode.testFlag(QuickTileFlag::Top)) {
278         edges |= Qt::TopEdge;
279     }
280     if (mode.testFlag(QuickTileFlag::Right)) {
281         edges |= Qt::RightEdge;
282         if (!mode.testFlag(QuickTileFlag::Top) && !mode.testFlag(QuickTileFlag::Bottom)) {
283             // using complete side
284             edges |= Qt::TopEdge | Qt::BottomEdge;
285         }
286     }
287     if (mode.testFlag(QuickTileFlag::Bottom)) {
288         edges |= Qt::BottomEdge;
289     }
290     return edges;
291 }
292 
hasApplicationMenu() const293 bool DecoratedClientImpl::hasApplicationMenu() const
294 {
295     return m_client->hasApplicationMenu();
296 }
297 
isApplicationMenuActive() const298 bool DecoratedClientImpl::isApplicationMenuActive() const
299 {
300     return m_client->applicationMenuActive();
301 }
302 
303 }
304 }
305