~ubuntu-branches/ubuntu/utopic/kde-workspace/utopic-proposed

« back to all changes in this revision

Viewing changes to kwin/effects/slideback/slideback.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Michał Zając
  • Date: 2011-07-09 08:31:15 UTC
  • Revision ID: james.westby@ubuntu.com-20110709083115-ohyxn6z93mily9fc
Tags: upstream-4.6.90
Import upstream version 4.6.90

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/********************************************************************
 
2
 KWin - the KDE window manager
 
3
 This file is part of the KDE project.
 
4
 
 
5
Copyright (C) 2009 Michael Zanetti <michael_zanetti@gmx.net>
 
6
 
 
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.
 
11
 
 
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.
 
16
 
 
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
*********************************************************************/
 
20
 
 
21
#include "slideback.h"
 
22
 
 
23
#include <kconfiggroup.h>
 
24
#include <kdebug.h>
 
25
 
 
26
namespace KWin
 
27
{
 
28
 
 
29
KWIN_EFFECT(slideback, SlideBackEffect)
 
30
 
 
31
SlideBackEffect::SlideBackEffect()
 
32
{
 
33
    updateStackingOrder();
 
34
    disabled = false;
 
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()));
 
42
}
 
43
 
 
44
static inline bool windowsShareDesktop(EffectWindow *w1, EffectWindow *w2)
 
45
{
 
46
    return w1->isOnAllDesktops() || w2->isOnAllDesktops() || w1->desktop() == w2->desktop();
 
47
}
 
48
 
 
49
void SlideBackEffect::slotWindowActivated(EffectWindow* w)
 
50
{
 
51
    if (w == NULL || w->keepAbove()) { // plasma popups, yakuake etc...
 
52
        return;
 
53
    }
 
54
 
 
55
    if (disabled || effects->activeFullScreenEffect()) {  // TabBox or PresentWindows/Cube in progress
 
56
        updateStackingOrder();
 
57
        disabled = false;
 
58
        return;
 
59
    }
 
60
 
 
61
    if (!isWindowUsable(w) || !stackingOrderChanged() || !isWindowOnTop(w)) {         // Focus changed but stacking still the same
 
62
        updateStackingOrder();
 
63
        return;
 
64
    }
 
65
 
 
66
    if (unminimizedWindow == w) {   // A window was activated by being unminimized. Don't trigger SlideBack.
 
67
        unminimizedWindow = NULL;
 
68
        updateStackingOrder();
 
69
        return;
 
70
    }
 
71
 
 
72
    if (clientItemShown == w) {
 
73
        clientItemShown = NULL;
 
74
        updateStackingOrder();
 
75
        return;
 
76
    }
 
77
    if (clientItemHidden == w) {
 
78
        clientItemHidden = NULL;
 
79
        updateStackingOrder();
 
80
        return;
 
81
    }
 
82
    // Determine all windows on top of the activated one
 
83
    bool currentFound = false;
 
84
    foreach (EffectWindow * tmp, oldStackingOrder) {
 
85
        if (!currentFound) {
 
86
            if (tmp == w) {
 
87
                currentFound = true;
 
88
            }
 
89
        } else {
 
90
            if (isWindowUsable(tmp) && windowsShareDesktop(tmp, w)) {
 
91
                // Do we have to move it?
 
92
                if (intersects(w, tmp->geometry())) {
 
93
                    QRect slideRect;
 
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);
 
101
                } else {
 
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);
 
107
                            break;
 
108
                        }
 
109
                    }
 
110
 
 
111
                }
 
112
            }
 
113
            if (tmp->isDock() || tmp->keepAbove()) {
 
114
                effects->setElevatedWindow(tmp, true);
 
115
                elevatedList.append(tmp);
 
116
            }
 
117
        }
 
118
    }
 
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);
 
124
        }
 
125
    }
 
126
    updateStackingOrder();
 
127
}
 
128
 
 
129
QRect SlideBackEffect::getSlideDestination(const QRect &windowUnderGeometry, const QRect &windowOverGeometry)
 
130
{
 
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;
 
136
 
 
137
    int horizSlide = leftSlide;
 
138
    if (qAbs(horizSlide) > qAbs(rightSlide)) {
 
139
        horizSlide = rightSlide;
 
140
    }
 
141
    int vertSlide = upSlide;
 
142
    if (qAbs(vertSlide) > qAbs(downSlide)) {
 
143
        vertSlide = downSlide;
 
144
    }
 
145
 
 
146
    QRect slideRect = windowOverGeometry;
 
147
    if (qAbs(horizSlide) < qAbs(vertSlide)) {
 
148
        slideRect.moveLeft(slideRect.x() + horizSlide);
 
149
    } else {
 
150
        slideRect.moveTop(slideRect.y() + vertSlide);
 
151
    }
 
152
    return slideRect;
 
153
}
 
154
 
 
155
void SlideBackEffect::updateStackingOrder()
 
156
{
 
157
    usableOldStackingOrder = usableWindows(effects->stackingOrder());
 
158
    oldStackingOrder = effects->stackingOrder();
 
159
}
 
160
 
 
161
void SlideBackEffect::prePaintScreen(ScreenPrePaintData &data, int time)
 
162
{
 
163
    if (motionManager.managingWindows()) {
 
164
        motionManager.calculate(time);
 
165
        data.mask |= Effect::PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS;
 
166
    }
 
167
    effects->prePaintScreen(data, time);
 
168
}
 
169
 
 
170
void SlideBackEffect::postPaintScreen()
 
171
{
 
172
    if (motionManager.areWindowsMoving()) {
 
173
        effects->addRepaintFull();
 
174
    }
 
175
    effects->postPaintScreen();
 
176
}
 
177
 
 
178
void SlideBackEffect::prePaintWindow(EffectWindow *w, WindowPrePaintData &data, int time)
 
179
{
 
180
    if (motionManager.isManaging(w)) {
 
181
        data.setTransformed();
 
182
    }
 
183
 
 
184
    effects->prePaintWindow(w, data, time);
 
185
}
 
186
 
 
187
void SlideBackEffect::paintWindow(EffectWindow *w, int mask, QRegion region, WindowPaintData &data)
 
188
{
 
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() );
 
199
            }
 
200
        }
 
201
        // Finally call windowActivated in case a already active window is raised.
 
202
        slotWindowActivated(w);
 
203
    }
 
204
    if (motionManager.isManaging(w)) {
 
205
        motionManager.apply(w, data);
 
206
    }
 
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();
 
211
}
 
212
 
 
213
void SlideBackEffect::postPaintWindow(EffectWindow* w)
 
214
{
 
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();
 
226
                        }
 
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);
 
232
                            }
 
233
                            motionManager.moveWindow(tmp, newDestination);
 
234
                            destinationList[tmp] = newDestination;
 
235
                        } else {
 
236
                            if (!tmp->isDock()) {
 
237
                                bool keepElevated = false;
 
238
                                foreach (EffectWindow * elevatedWindow, tmpList) {
 
239
                                    if (tmp->geometry().intersects(elevatedWindow->geometry())) {
 
240
                                        keepElevated = true;
 
241
                                    }
 
242
                                }
 
243
                                if (!keepElevated) {
 
244
                                    effects->setElevatedWindow(tmp, false);
 
245
                                    elevatedList.removeAll(tmp);
 
246
                                }
 
247
                            }
 
248
                        }
 
249
                        tmpList.append(tmp);
 
250
                    }
 
251
                } else {
 
252
                    // Move the window back where it belongs
 
253
                    motionManager.moveWindow(w, w->geometry());
 
254
                    destinationList.remove(w);
 
255
                }
 
256
            }
 
257
        } else {
 
258
            // is window back at its original position?
 
259
            if (!motionManager.isWindowMoving(w)) {
 
260
                motionManager.unmanage(w);
 
261
                effects->addRepaintFull();
 
262
            }
 
263
        }
 
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);
 
273
                    }
 
274
                    elevatedList.clear();
 
275
                }
 
276
            }
 
277
        }
 
278
    }
 
279
    effects->postPaintWindow(w);
 
280
}
 
281
 
 
282
void SlideBackEffect::slotWindowDeleted(EffectWindow* w)
 
283
{
 
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);
 
290
    }
 
291
}
 
292
 
 
293
void SlideBackEffect::slotWindowAdded(EffectWindow *w)
 
294
{
 
295
    Q_UNUSED(w);
 
296
    updateStackingOrder();
 
297
}
 
298
 
 
299
void SlideBackEffect::slotWindowUnminimized(EffectWindow* w)
 
300
{
 
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;
 
306
    } else {
 
307
        unminimizedWindow = NULL;
 
308
    }
 
309
}
 
310
 
 
311
void SlideBackEffect::slotClientGroupItemSwitched(EffectWindow* from, EffectWindow* to)
 
312
{
 
313
    clientItemShown = to;
 
314
    clientItemHidden = from;
 
315
}
 
316
 
 
317
void SlideBackEffect::slotTabBoxClosed()
 
318
{
 
319
    disabled = true;
 
320
}
 
321
 
 
322
bool SlideBackEffect::isWindowOnTop(EffectWindow* w)
 
323
{
 
324
    EffectWindowList openWindows = usableWindows(effects->stackingOrder());
 
325
    if (!openWindows.isEmpty() && (openWindows.last() == w)) {
 
326
        return true;
 
327
    }
 
328
    return false;
 
329
}
 
330
 
 
331
bool SlideBackEffect::isWindowUsable(EffectWindow* w)
 
332
{
 
333
    return w && (w->isNormalWindow() || w->isDialog()) && !w->keepAbove() && !w->isDeleted() && !w->isMinimized()
 
334
           && w->visibleInClientGroup();
 
335
}
 
336
 
 
337
bool SlideBackEffect::intersects(EffectWindow* windowUnder, const QRect &windowOverGeometry)
 
338
{
 
339
    QRect windowUnderGeometry = getModalGroupGeometry(windowUnder);
 
340
    return windowUnderGeometry.intersects(windowOverGeometry);
 
341
}
 
342
 
 
343
EffectWindowList SlideBackEffect::usableWindows(const EffectWindowList & allWindows)
 
344
{
 
345
    EffectWindowList retList;
 
346
    foreach (EffectWindow * tmp, allWindows) {
 
347
        if (isWindowUsable(tmp)) {
 
348
            retList.append(tmp);
 
349
        }
 
350
    }
 
351
    return retList;
 
352
}
 
353
 
 
354
bool SlideBackEffect::stackingOrderChanged()
 
355
{
 
356
    return !(usableOldStackingOrder == usableWindows(effects->stackingOrder()));
 
357
}
 
358
 
 
359
EffectWindow* SlideBackEffect::newTopWindow()
 
360
{
 
361
    EffectWindowList stacking = usableWindows(effects->stackingOrder());
 
362
    return stacking.isEmpty() ? NULL : stacking.last();
 
363
}
 
364
 
 
365
QRect SlideBackEffect::getModalGroupGeometry(EffectWindow *w)
 
366
{
 
367
    QRect modalGroupGeometry = w->geometry();
 
368
    if (w->isModal()) {
 
369
        foreach (EffectWindow * modalWindow, w->mainWindows()) {
 
370
            modalGroupGeometry = modalGroupGeometry.united(getModalGroupGeometry(modalWindow));
 
371
        }
 
372
    }
 
373
    return modalGroupGeometry;
 
374
}
 
375
 
 
376
} //Namespace