1
/********************************************************************
2
KWin - the KDE window manager
3
This file is part of the KDE project.
5
Copyright (C) 2009 Michael Zanetti <michael_zanetti@gmx.net>
7
This program is free software; you can redistribute it and/or modify
8
it under the terms of the GNU General Public License as published by
9
the Free Software Foundation; either version 2 of the License, or
10
(at your option) any later version.
12
This program is distributed in the hope that it will be useful,
13
but WITHOUT ANY WARRANTY; without even the implied warranty of
14
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
GNU General Public License for more details.
17
You should have received a copy of the GNU General Public License
18
along with this program. If not, see <http://www.gnu.org/licenses/>.
19
*********************************************************************/
21
#include "slideback.h"
23
#include <kconfiggroup.h>
29
KWIN_EFFECT(slideback, SlideBackEffect)
31
SlideBackEffect::SlideBackEffect()
33
updateStackingOrder();
35
unminimizedWindow = NULL;
36
connect(effects, SIGNAL(windowAdded(EffectWindow*)), this, SLOT(slotWindowAdded(EffectWindow*)));
37
connect(effects, SIGNAL(windowActivated(EffectWindow*)), this, SLOT(slotWindowActivated(EffectWindow*)));
38
connect(effects, SIGNAL(windowDeleted(EffectWindow*)), this, SLOT(slotWindowDeleted(EffectWindow*)));
39
connect(effects, SIGNAL(windowUnminimized(EffectWindow*)), this, SLOT(slotWindowUnminimized(EffectWindow*)));
40
connect(effects, SIGNAL(clientGroupItemSwitched(EffectWindow*,EffectWindow*)), this, SLOT(slotClientGroupItemSwitched(EffectWindow*,EffectWindow*)));
41
connect(effects, SIGNAL(tabBoxClosed()), this, SLOT(slotTabBoxClosed()));
44
static inline bool windowsShareDesktop(EffectWindow *w1, EffectWindow *w2)
46
return w1->isOnAllDesktops() || w2->isOnAllDesktops() || w1->desktop() == w2->desktop();
49
void SlideBackEffect::slotWindowActivated(EffectWindow* w)
51
if (w == NULL || w->keepAbove()) { // plasma popups, yakuake etc...
55
if (disabled || effects->activeFullScreenEffect()) { // TabBox or PresentWindows/Cube in progress
56
updateStackingOrder();
61
if (!isWindowUsable(w) || !stackingOrderChanged() || !isWindowOnTop(w)) { // Focus changed but stacking still the same
62
updateStackingOrder();
66
if (unminimizedWindow == w) { // A window was activated by being unminimized. Don't trigger SlideBack.
67
unminimizedWindow = NULL;
68
updateStackingOrder();
72
if (clientItemShown == w) {
73
clientItemShown = NULL;
74
updateStackingOrder();
77
if (clientItemHidden == w) {
78
clientItemHidden = NULL;
79
updateStackingOrder();
82
// Determine all windows on top of the activated one
83
bool currentFound = false;
84
foreach (EffectWindow * tmp, oldStackingOrder) {
90
if (isWindowUsable(tmp) && windowsShareDesktop(tmp, w)) {
91
// Do we have to move it?
92
if (intersects(w, tmp->geometry())) {
94
slideRect = getSlideDestination(getModalGroupGeometry(w), tmp->geometry());
95
effects->setElevatedWindow(tmp, true);
96
elevatedList.append(tmp);
97
motionManager.manage(tmp);
98
motionManager.moveWindow(tmp, slideRect);
99
destinationList.insert(tmp, slideRect);
100
coveringWindows.append(tmp);
102
//Does it intersect with a moved (elevated) window and do we have to elevate it too?
103
foreach (EffectWindow * elevatedWindow, elevatedList) {
104
if (tmp->geometry().intersects(elevatedWindow->geometry())) {
105
effects->setElevatedWindow(tmp, true);
106
elevatedList.append(tmp);
113
if (tmp->isDock() || tmp->keepAbove()) {
114
effects->setElevatedWindow(tmp, true);
115
elevatedList.append(tmp);
119
// If a window is minimized it could happen that the panels stay elevated without any windows sliding.
120
// clear all elevation settings
121
if (!motionManager.managingWindows()) {
122
foreach (EffectWindow * tmp, elevatedList) {
123
effects->setElevatedWindow(tmp, false);
126
updateStackingOrder();
129
QRect SlideBackEffect::getSlideDestination(const QRect &windowUnderGeometry, const QRect &windowOverGeometry)
131
// Determine the shortest way:
132
int leftSlide = windowUnderGeometry.left() - windowOverGeometry.right() - 20;
133
int rightSlide = windowUnderGeometry.right() - windowOverGeometry.left() + 20;
134
int upSlide = windowUnderGeometry.top() - windowOverGeometry.bottom() - 20;
135
int downSlide = windowUnderGeometry.bottom() - windowOverGeometry.top() + 20;
137
int horizSlide = leftSlide;
138
if (qAbs(horizSlide) > qAbs(rightSlide)) {
139
horizSlide = rightSlide;
141
int vertSlide = upSlide;
142
if (qAbs(vertSlide) > qAbs(downSlide)) {
143
vertSlide = downSlide;
146
QRect slideRect = windowOverGeometry;
147
if (qAbs(horizSlide) < qAbs(vertSlide)) {
148
slideRect.moveLeft(slideRect.x() + horizSlide);
150
slideRect.moveTop(slideRect.y() + vertSlide);
155
void SlideBackEffect::updateStackingOrder()
157
usableOldStackingOrder = usableWindows(effects->stackingOrder());
158
oldStackingOrder = effects->stackingOrder();
161
void SlideBackEffect::prePaintScreen(ScreenPrePaintData &data, int time)
163
if (motionManager.managingWindows()) {
164
motionManager.calculate(time);
165
data.mask |= Effect::PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS;
167
effects->prePaintScreen(data, time);
170
void SlideBackEffect::postPaintScreen()
172
if (motionManager.areWindowsMoving()) {
173
effects->addRepaintFull();
175
effects->postPaintScreen();
178
void SlideBackEffect::prePaintWindow(EffectWindow *w, WindowPrePaintData &data, int time)
180
if (motionManager.isManaging(w)) {
181
data.setTransformed();
184
effects->prePaintWindow(w, data, time);
187
void SlideBackEffect::paintWindow(EffectWindow *w, int mask, QRegion region, WindowPaintData &data)
189
if (stackingOrderChanged() && (w == newTopWindow()) && !disabled) {
190
/* This can happen because of two reasons:
191
- a window has received the focus earlier without being raised and is raised now. -> call windowActivated() now
192
- paintWindow() is called with a new stackingOrder before activateWindow(). Bug? -> don't draw the overlapping content;*/
193
foreach (EffectWindow * tmp, oldStackingOrder) {
194
if (oldStackingOrder.lastIndexOf(tmp) > oldStackingOrder.lastIndexOf(w) && isWindowUsable(tmp) && windowsShareDesktop(tmp, w)) {
195
kDebug(1212) << "screw detected. region:" << region << "clipping:" << tmp->geometry() ;
196
clippedRegions << region.subtracted(tmp->geometry());
197
PaintClipper::push(clippedRegions.last());
198
// region = region.subtracted( tmp->geometry() );
201
// Finally call windowActivated in case a already active window is raised.
202
slotWindowActivated(w);
204
if (motionManager.isManaging(w)) {
205
motionManager.apply(w, data);
207
effects->paintWindow(w, mask, region, data);
208
for (int i = clippedRegions.count() - 1; i > -1; --i)
209
PaintClipper::pop(clippedRegions.at(i));
210
clippedRegions.clear();
213
void SlideBackEffect::postPaintWindow(EffectWindow* w)
215
if (motionManager.isManaging(w)) {
216
if (destinationList.contains(w)) {
217
if (!motionManager.isWindowMoving(w)) { // has window reched its destination?
218
// If we are still intersecting with the activeWindow it is moving. slide to somewhere else
219
// restore the stacking order of all windows not intersecting any more except panels
220
if (coveringWindows.contains(w)) {
221
EffectWindowList tmpList;
222
foreach (EffectWindow * tmp, elevatedList) {
223
QRect elevatedGeometry = tmp->geometry();
224
if (motionManager.isManaging(tmp)) {
225
elevatedGeometry = motionManager.transformedGeometry(tmp).toAlignedRect();
227
if (effects->activeWindow() && !tmp->isDock() && !tmp->keepAbove() && effects->activeWindow()->geometry().intersects(elevatedGeometry)) {
228
QRect newDestination;
229
newDestination = getSlideDestination(getModalGroupGeometry(effects->activeWindow()), elevatedGeometry);
230
if (!motionManager.isManaging(tmp)) {
231
motionManager.manage(tmp);
233
motionManager.moveWindow(tmp, newDestination);
234
destinationList[tmp] = newDestination;
236
if (!tmp->isDock()) {
237
bool keepElevated = false;
238
foreach (EffectWindow * elevatedWindow, tmpList) {
239
if (tmp->geometry().intersects(elevatedWindow->geometry())) {
244
effects->setElevatedWindow(tmp, false);
245
elevatedList.removeAll(tmp);
252
// Move the window back where it belongs
253
motionManager.moveWindow(w, w->geometry());
254
destinationList.remove(w);
258
// is window back at its original position?
259
if (!motionManager.isWindowMoving(w)) {
260
motionManager.unmanage(w);
261
effects->addRepaintFull();
264
if (coveringWindows.contains(w)) {
265
// It could happen that there is no aciveWindow() here if the user clicks the close-button on an inactive window.
266
// Just skip... the window will be removed in windowDeleted() later
267
if (effects->activeWindow() && !intersects(effects->activeWindow(), motionManager.transformedGeometry(w).toAlignedRect())) {
268
coveringWindows.removeAll(w);
269
if (coveringWindows.isEmpty()) {
270
// Restore correct stacking order
271
foreach (EffectWindow * tmp, elevatedList) {
272
effects->setElevatedWindow(tmp, false);
274
elevatedList.clear();
279
effects->postPaintWindow(w);
282
void SlideBackEffect::slotWindowDeleted(EffectWindow* w)
284
usableOldStackingOrder.removeAll(w);
285
oldStackingOrder.removeAll(w);
286
coveringWindows.removeAll(w);
287
elevatedList.removeAll(w);
288
if (motionManager.isManaging(w)) {
289
motionManager.unmanage(w);
293
void SlideBackEffect::slotWindowAdded(EffectWindow *w)
296
updateStackingOrder();
299
void SlideBackEffect::slotWindowUnminimized(EffectWindow* w)
301
// SlideBack should not be triggered on an unminimized window. For this we need to store the last unminimized window.
302
// If a window is unminimized but not on top we need to clear the memory because the windowUnminimized() is not
303
// followed by a windowActivated().
304
if (isWindowOnTop(w)) {
305
unminimizedWindow = w;
307
unminimizedWindow = NULL;
311
void SlideBackEffect::slotClientGroupItemSwitched(EffectWindow* from, EffectWindow* to)
313
clientItemShown = to;
314
clientItemHidden = from;
317
void SlideBackEffect::slotTabBoxClosed()
322
bool SlideBackEffect::isWindowOnTop(EffectWindow* w)
324
EffectWindowList openWindows = usableWindows(effects->stackingOrder());
325
if (!openWindows.isEmpty() && (openWindows.last() == w)) {
331
bool SlideBackEffect::isWindowUsable(EffectWindow* w)
333
return w && (w->isNormalWindow() || w->isDialog()) && !w->keepAbove() && !w->isDeleted() && !w->isMinimized()
334
&& w->visibleInClientGroup();
337
bool SlideBackEffect::intersects(EffectWindow* windowUnder, const QRect &windowOverGeometry)
339
QRect windowUnderGeometry = getModalGroupGeometry(windowUnder);
340
return windowUnderGeometry.intersects(windowOverGeometry);
343
EffectWindowList SlideBackEffect::usableWindows(const EffectWindowList & allWindows)
345
EffectWindowList retList;
346
foreach (EffectWindow * tmp, allWindows) {
347
if (isWindowUsable(tmp)) {
354
bool SlideBackEffect::stackingOrderChanged()
356
return !(usableOldStackingOrder == usableWindows(effects->stackingOrder()));
359
EffectWindow* SlideBackEffect::newTopWindow()
361
EffectWindowList stacking = usableWindows(effects->stackingOrder());
362
return stacking.isEmpty() ? NULL : stacking.last();
365
QRect SlideBackEffect::getModalGroupGeometry(EffectWindow *w)
367
QRect modalGroupGeometry = w->geometry();
369
foreach (EffectWindow * modalWindow, w->mainWindows()) {
370
modalGroupGeometry = modalGroupGeometry.united(getModalGroupGeometry(modalWindow));
373
return modalGroupGeometry;