1
/********************************************************************
2
KWin - the KDE window manager
3
This file is part of the KDE project.
5
Copyright (C) 2007 Rivo Laks <rivolaks@hot.ee>
6
Copyright (C) 2008 Lucas Murray <lmurray@undefinedfire.com>
8
This program is free software; you can redistribute it and/or modify
9
it under the terms of the GNU General Public License as published by
10
the Free Software Foundation; either version 2 of the License, or
11
(at your option) any later version.
13
This program is distributed in the hope that it will be useful,
14
but WITHOUT ANY WARRANTY; without even the implied warranty of
15
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
GNU General Public License for more details.
18
You should have received a copy of the GNU General Public License
19
along with this program. If not, see <http://www.gnu.org/licenses/>.
20
*********************************************************************/
22
#include "presentwindows.h"
24
#include <kactioncollection.h>
27
#include <kcolorscheme.h>
28
#include <kconfiggroup.h>
30
#include <kglobalsettings.h>
32
#ifdef KWIN_HAVE_OPENGL_COMPOSITING
33
#include <kwinglutils.h>
36
#include <QMouseEvent>
37
#include <QtGui/QPainter>
38
#include <QtGui/QGraphicsLinearLayout>
39
#include <Plasma/FrameSvg>
40
#include <Plasma/PushButton>
41
#include <Plasma/WindowEffects>
42
#include <netwm_def.h>
53
KWIN_EFFECT(presentwindows, PresentWindowsEffect)
55
PresentWindowsEffect::PresentWindowsEffect()
58
, m_ignoreMinimized(false)
60
, m_hasKeyboardGrab(false)
61
, m_tabBoxEnabled(false)
62
, m_mode(ModeCurrentDesktop)
63
, m_managerWindow(NULL)
64
, m_highlightedWindow(NULL)
65
, m_filterFrame(effects->effectFrame(EffectFrameStyled, false))
67
, m_dragInProgress(false)
69
, m_highlightedDropTarget(NULL)
70
, m_dragToClose(false)
72
m_atomDesktop = XInternAtom(display(), "_KDE_PRESENT_WINDOWS_DESKTOP", False);
73
m_atomWindows = XInternAtom(display(), "_KDE_PRESENT_WINDOWS_GROUP", False);
74
effects->registerPropertyType(m_atomDesktop, true);
75
effects->registerPropertyType(m_atomWindows, true);
77
// Announce support by creating a dummy version on the root window
78
unsigned char dummy = 0;
79
XChangeProperty(display(), rootWindow(), m_atomDesktop, m_atomDesktop, 8, PropModeReplace, &dummy, 1);
80
XChangeProperty(display(), rootWindow(), m_atomWindows, m_atomWindows, 8, PropModeReplace, &dummy, 1);
83
font.setPointSize(font.pointSize() * 2);
85
m_filterFrame->setFont(font);
87
KActionCollection* actionCollection = new KActionCollection(this);
88
KAction* a = (KAction*)actionCollection->addAction("Expose");
89
a->setText(i18n("Toggle Present Windows (Current desktop)"));
90
a->setGlobalShortcut(KShortcut(Qt::CTRL + Qt::Key_F9));
91
shortcut = a->globalShortcut();
92
connect(a, SIGNAL(triggered(bool)), this, SLOT(toggleActive()));
93
connect(a, SIGNAL(globalShortcutChanged(QKeySequence)), this, SLOT(globalShortcutChanged(QKeySequence)));
94
KAction* b = (KAction*)actionCollection->addAction("ExposeAll");
95
b->setText(i18n("Toggle Present Windows (All desktops)"));
96
b->setGlobalShortcut(KShortcut(Qt::CTRL + Qt::Key_F10));
97
shortcutAll = b->globalShortcut();
98
connect(b, SIGNAL(triggered(bool)), this, SLOT(toggleActiveAllDesktops()));
99
connect(b, SIGNAL(globalShortcutChanged(QKeySequence)), this, SLOT(globalShortcutChangedAll(QKeySequence)));
100
KAction* c = (KAction*)actionCollection->addAction("ExposeClass");
101
c->setText(i18n("Toggle Present Windows (Window class)"));
102
c->setGlobalShortcut(KShortcut(Qt::CTRL + Qt::Key_F7));
103
connect(c, SIGNAL(triggered(bool)), this, SLOT(toggleActiveClass()));
104
connect(c, SIGNAL(globalShortcutChanged(QKeySequence)), this, SLOT(globalShortcutChangedClass(QKeySequence)));
105
shortcutClass = c->globalShortcut();
106
reconfigure(ReconfigureAll);
107
connect(effects, SIGNAL(windowAdded(EffectWindow*)), this, SLOT(slotWindowAdded(EffectWindow*)));
108
connect(effects, SIGNAL(windowClosed(EffectWindow*)), this, SLOT(slotWindowClosed(EffectWindow*)));
109
connect(effects, SIGNAL(windowDeleted(EffectWindow*)), this, SLOT(slotWindowDeleted(EffectWindow*)));
110
connect(effects, SIGNAL(windowGeometryShapeChanged(EffectWindow*,QRect)), this, SLOT(slotWindowGeometryShapeChanged(EffectWindow*,QRect)));
111
connect(effects, SIGNAL(tabBoxAdded(int)), this, SLOT(slotTabBoxAdded(int)));
112
connect(effects, SIGNAL(tabBoxClosed()), this, SLOT(slotTabBoxClosed()));
113
connect(effects, SIGNAL(tabBoxUpdated()), this, SLOT(slotTabBoxUpdated()));
114
connect(effects, SIGNAL(tabBoxKeyEvent(QKeyEvent*)), this, SLOT(slotTabBoxKeyEvent(QKeyEvent*)));
115
connect(effects, SIGNAL(propertyNotify(EffectWindow*,long)), this, SLOT(slotPropertyNotify(EffectWindow*,long)));
118
PresentWindowsEffect::~PresentWindowsEffect()
120
XDeleteProperty(display(), rootWindow(), m_atomDesktop);
121
effects->registerPropertyType(m_atomDesktop, false);
122
XDeleteProperty(display(), rootWindow(), m_atomWindows);
123
effects->registerPropertyType(m_atomWindows, false);
124
foreach (ElectricBorder border, m_borderActivate) {
125
effects->unreserveElectricBorder(border);
127
foreach (ElectricBorder border, m_borderActivateAll) {
128
effects->unreserveElectricBorder(border);
130
delete m_filterFrame;
134
void PresentWindowsEffect::reconfigure(ReconfigureFlags)
136
KConfigGroup conf = effects->effectConfig("PresentWindows");
137
foreach (ElectricBorder border, m_borderActivate) {
138
effects->unreserveElectricBorder(border);
140
foreach (ElectricBorder border, m_borderActivateAll) {
141
effects->unreserveElectricBorder(border);
143
m_borderActivate.clear();
144
m_borderActivateAll.clear();
145
QList<int> borderList = QList<int>();
146
borderList.append(int(ElectricNone));
147
borderList = conf.readEntry("BorderActivate", borderList);
148
foreach (int i, borderList) {
149
m_borderActivate.append(ElectricBorder(i));
150
effects->reserveElectricBorder(ElectricBorder(i));
153
borderList.append(int(ElectricTopLeft));
154
borderList = conf.readEntry("BorderActivateAll", borderList);
155
foreach (int i, borderList) {
156
m_borderActivateAll.append(ElectricBorder(i));
157
effects->reserveElectricBorder(ElectricBorder(i));
159
m_layoutMode = conf.readEntry("LayoutMode", int(LayoutNatural));
160
m_showCaptions = conf.readEntry("DrawWindowCaptions", true);
161
m_showIcons = conf.readEntry("DrawWindowIcons", true);
162
m_doNotCloseWindows = !conf.readEntry("AllowClosingWindows", true);
163
m_tabBoxAllowed = conf.readEntry("TabBox", false);
164
m_tabBoxAlternativeAllowed = conf.readEntry("TabBoxAlternative", false);
165
m_ignoreMinimized = conf.readEntry("IgnoreMinimized", false);
166
m_accuracy = conf.readEntry("Accuracy", 1) * 20;
167
m_fillGaps = conf.readEntry("FillGaps", true);
168
m_fadeDuration = double(animationTime(150));
169
m_showPanel = conf.readEntry("ShowPanel", false);
170
m_leftButtonWindow = (WindowMouseAction)conf.readEntry("LeftButtonWindow", (int)WindowActivateAction);
171
m_middleButtonWindow = (WindowMouseAction)conf.readEntry("MiddleButtonWindow", (int)WindowNoAction);
172
m_rightButtonWindow = (WindowMouseAction)conf.readEntry("RightButtonWindow", (int)WindowExitAction);
173
m_leftButtonDesktop = (DesktopMouseAction)conf.readEntry("LeftButtonDesktop", (int)DesktopExitAction);
174
m_middleButtonDesktop = (DesktopMouseAction)conf.readEntry("MiddleButtonDesktop", (int)DesktopNoAction);
175
m_rightButtonDesktop = (DesktopMouseAction)conf.readEntry("RightButtonDesktop", (int)DesktopNoAction);
176
m_dragToClose = conf.readEntry("DragToClose", false);
179
void* PresentWindowsEffect::proxy()
184
void PresentWindowsEffect::toggleActiveClass()
187
if (!effects->activeWindow())
189
m_mode = ModeWindowClass;
190
m_class = effects->activeWindow()->windowClass();
192
setActive(!m_activated);
195
//-----------------------------------------------------------------------------
198
void PresentWindowsEffect::prePaintScreen(ScreenPrePaintData &data, int time)
200
m_motionManager.calculate(time);
202
// We need to mark the screen as having been transformed otherwise there will be no repainting
203
if (m_activated || m_motionManager.managingWindows())
204
data.mask |= Effect::PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS;
207
m_decalOpacity = qMin(1.0, m_decalOpacity + time / m_fadeDuration);
209
m_decalOpacity = qMax(0.0, m_decalOpacity - time / m_fadeDuration);
211
effects->prePaintScreen(data, time);
214
void PresentWindowsEffect::paintScreen(int mask, QRegion region, ScreenPaintData &data)
216
effects->paintScreen(mask, region, data);
218
// Display the filter box
219
if (!m_windowFilter.isEmpty())
220
m_filterFrame->render(region);
221
// Display drop targets
222
for (int i=0; i<m_dropTargets.size(); ++i) {
223
m_dropTargets.at(i)->render();
227
void PresentWindowsEffect::postPaintScreen()
229
if (m_motionManager.areWindowsMoving())
230
effects->addRepaintFull();
231
else if (!m_activated && m_motionManager.managingWindows()) {
232
// We have finished moving them back, stop processing
233
m_motionManager.unmanageAll();
235
DataHash::iterator i = m_windowData.begin();
236
while (i != m_windowData.end()) {
237
delete i.value().textFrame;
238
delete i.value().iconFrame;
241
m_windowData.clear();
243
foreach (EffectWindow * w, effects->stackingOrder()) {
245
w->setData(WindowForceBlurRole, QVariant(false));
248
effects->setActiveFullScreenEffect(NULL);
251
// Update windows that are changing brightness or opacity
252
DataHash::const_iterator i;
253
for (i = m_windowData.constBegin(); i != m_windowData.constEnd(); ++i) {
254
if (i.value().opacity > 0.0 && i.value().opacity < 1.0)
255
i.key()->addRepaintFull();
256
if (i.value().highlight > 0.0 && i.value().highlight < 1.0)
257
i.key()->addRepaintFull();
260
effects->postPaintScreen();
263
//-----------------------------------------------------------------------------
266
void PresentWindowsEffect::prePaintWindow(EffectWindow *w, WindowPrePaintData &data, int time)
268
// TODO: We should also check to see if any windows are fading just in case fading takes longer
269
// than moving the windows when the effect is deactivated.
270
if (m_activated || m_motionManager.areWindowsMoving()) {
271
DataHash::iterator winData = m_windowData.find(w);
272
if (winData == m_windowData.end()) {
273
effects->prePaintWindow(w, data, time);
276
w->enablePainting(EffectWindow::PAINT_DISABLED_BY_MINIMIZE); // Display always
277
w->enablePainting(EffectWindow::PAINT_DISABLED_BY_DESKTOP);
278
if (winData->visible)
279
w->enablePainting(EffectWindow::PAINT_DISABLED_BY_CLIENT_GROUP);
281
// Calculate window's opacity
282
// TODO: Minimized windows or windows not on the current desktop are only 75% visible?
283
if (winData->visible) {
284
if (winData->deleted)
285
winData->opacity = qMax(0.0, winData->opacity - time / m_fadeDuration);
287
winData->opacity = qMin(/*(w->isMinimized() || !w->isOnCurrentDesktop()) ? 0.75 :*/ 1.0,
288
winData->opacity + time / m_fadeDuration);
290
winData->opacity = qMax(0.0, winData->opacity - time / m_fadeDuration);
291
if (winData->opacity <= 0.0) {
292
// don't disable painting for panels if show panel is set
293
if (!(m_showPanel && w->isDock()))
294
w->disablePainting(EffectWindow::PAINT_DISABLED);
295
} else if (winData->opacity != 1.0)
296
data.setTranslucent();
298
// Calculate window's brightness
299
if (w == m_highlightedWindow || w == m_closeWindow || !m_activated)
300
winData->highlight = qMin(1.0, winData->highlight + time / m_fadeDuration);
302
winData->highlight = qMax(0.0, winData->highlight - time / m_fadeDuration);
305
if (winData->deleted) {
306
data.setTranslucent();
307
if (winData->opacity <= 0.0 && winData->referenced) {
308
// it's possible that another effect has referenced the window
309
// we have to keep the window in the list to prevent flickering
310
winData->referenced = false;
313
w->enablePainting(EffectWindow::PAINT_DISABLED_BY_DELETE);
316
// desktop windows on other desktops (Plasma activity per desktop) should not be painted
317
if (w->isDesktop() && !w->isOnCurrentDesktop())
318
w->disablePainting(EffectWindow::PAINT_DISABLED_BY_DESKTOP);
320
if (m_motionManager.isManaging(w))
321
data.setTransformed(); // We will be moving this window
323
effects->prePaintWindow(w, data, time);
326
void PresentWindowsEffect::paintWindow(EffectWindow *w, int mask, QRegion region, WindowPaintData &data)
328
if (m_activated || m_motionManager.areWindowsMoving()) {
329
DataHash::const_iterator winData = m_windowData.constFind(w);
330
if (winData == m_windowData.constEnd() || (w->isDock() && m_showPanel)) {
331
// in case the panel should be shown just display it without any changes
332
effects->paintWindow(w, mask, region, data);
336
// Apply opacity and brightness
337
data.opacity *= winData->opacity;
338
data.brightness *= interpolate(0.7, 1.0, winData->highlight);
340
if (m_motionManager.isManaging(w)) {
341
m_motionManager.apply(w, data);
343
if (!m_motionManager.areWindowsMoving()) {
344
mask |= PAINT_WINDOW_LANCZOS;
346
if (m_dragInProgress && m_dragWindow == w) {
347
QPoint diff = cursorPos() - m_dragStart;
348
data.xTranslate += diff.x();
349
data.yTranslate += diff.y();
351
effects->paintWindow(w, mask, region, data);
353
QRect rect = m_motionManager.transformedGeometry(w).toRect();
355
QPoint point(rect.x() + rect.width() * 0.95,
356
rect.y() + rect.height() * 0.95);
357
winData->iconFrame->setPosition(point);
358
#ifdef KWIN_HAVE_OPENGL_COMPOSITING
359
if (effects->compositingType() == KWin::OpenGLCompositing && data.shader) {
360
const float a = 0.9 * data.opacity * m_decalOpacity * 0.75;
361
data.shader->setUniform(GLShader::ModulationConstant, QVector4D(a, a, a, a));
364
winData->iconFrame->render(region, 0.9 * data.opacity * m_decalOpacity, 0.75);
366
if (m_showCaptions) {
367
QPoint point(rect.x() + rect.width() / 2,
368
rect.y() + rect.height() / 2);
369
winData->textFrame->setPosition(point);
370
#ifdef KWIN_HAVE_OPENGL_COMPOSITING
371
if (effects->compositingType() == KWin::OpenGLCompositing && data.shader) {
372
const float a = 0.9 * data.opacity * m_decalOpacity * 0.75;
373
data.shader->setUniform(GLShader::ModulationConstant, QVector4D(a, a, a, a));
376
winData->textFrame->render(region, 0.9 * data.opacity * m_decalOpacity, 0.75);
379
effects->paintWindow(w, mask, region, data);
381
effects->paintWindow(w, mask, region, data);
384
//-----------------------------------------------------------------------------
387
void PresentWindowsEffect::slotWindowAdded(EffectWindow *w)
391
WindowData *winData = &m_windowData[w];
392
winData->visible = isVisibleWindow(w);
393
winData->opacity = 0.0;
394
winData->highlight = 0.0;
395
winData->textFrame = effects->effectFrame(EffectFrameUnstyled, false);
398
font.setPointSize(12);
399
winData->textFrame->setFont(font);
400
winData->iconFrame = effects->effectFrame(EffectFrameUnstyled, false);
401
winData->iconFrame->setAlignment(Qt::AlignRight | Qt::AlignBottom);
402
winData->iconFrame->setIcon(w->icon());
403
if (isSelectableWindow(w)) {
404
m_motionManager.manage(w);
407
if (w == effects->findWindow(m_closeView->winId())) {
408
winData->visible = true;
409
winData->highlight = 1.0;
411
w->setData(WindowForceBlurRole, QVariant(true));
415
void PresentWindowsEffect::slotWindowClosed(EffectWindow *w)
417
if (m_managerWindow == w)
418
m_managerWindow = NULL;
419
DataHash::iterator winData = m_windowData.find(w);
420
if (winData == m_windowData.end())
422
winData->deleted = true;
423
winData->referenced = true;
425
if (m_highlightedWindow == w)
426
setHighlightedWindow(findFirstWindow());
427
if (m_closeWindow == w) {
429
return; // don't rearrange
433
foreach (EffectWindow *w, m_motionManager.managedWindows()) {
434
winData = m_windowData.find(w);
435
if (winData != m_windowData.end() && !winData->deleted)
436
return; // found one that is not deleted? then we go on
438
setActive(false); //else no need to keep this open
441
void PresentWindowsEffect::slotWindowDeleted(EffectWindow *w)
443
DataHash::iterator winData = m_windowData.find(w);
444
if (winData == m_windowData.end())
446
delete winData->textFrame;
447
delete winData->iconFrame;
448
m_windowData.erase(winData);
449
m_motionManager.unmanage(w);
452
void PresentWindowsEffect::slotWindowGeometryShapeChanged(EffectWindow* w, const QRect& old)
457
if (!m_windowData.contains(w))
462
bool PresentWindowsEffect::borderActivated(ElectricBorder border)
464
if (!m_borderActivate.contains(border) && !m_borderActivateAll.contains(border))
466
if (effects->activeFullScreenEffect() && effects->activeFullScreenEffect() != this)
468
if (m_borderActivate.contains(border))
471
toggleActiveAllDesktops();
475
void PresentWindowsEffect::windowInputMouseEvent(Window w, QEvent *e)
477
assert(w == m_input);
480
QMouseEvent* me = static_cast< QMouseEvent* >(e);
481
if (m_closeView->geometry().contains(me->pos())) {
482
if (!m_closeView->isVisible()) {
485
if (m_closeView->isVisible()) {
486
const QPoint widgetPos = m_closeView->mapFromGlobal(me->pos());
487
const QPointF scenePos = m_closeView->mapToScene(widgetPos);
488
QMouseEvent event(me->type(), widgetPos, me->pos(), me->button(), me->buttons(), me->modifiers());
489
m_closeView->windowInputMouseEvent(&event);
493
// Which window are we hovering over? Always trigger as we don't always get move events before clicking
494
// We cannot use m_motionManager.windowAtPoint() as the window might not be visible
495
EffectWindowList windows = m_motionManager.managedWindows();
496
bool hovering = false;
497
for (int i = 0; i < windows.size(); ++i) {
498
DataHash::const_iterator winData = m_windowData.constFind(windows.at(i));
499
if (winData == m_windowData.constEnd())
501
if (m_motionManager.transformedGeometry(windows.at(i)).contains(cursorPos()) &&
502
winData->visible && !winData->deleted) {
504
if (windows.at(i) && m_highlightedWindow != windows.at(i) && !m_dragInProgress)
505
setHighlightedWindow(windows.at(i));
509
if (m_highlightedWindow && m_motionManager.transformedGeometry(m_highlightedWindow).contains(me->pos()))
514
if (e->type() == QEvent::MouseButtonRelease) {
515
if (me->button() == Qt::LeftButton) {
516
if (m_dragInProgress && m_dragWindow) {
518
for (int i=0; i<m_dropTargets.size(); ++i) {
519
if (m_dropTargets.at(i)->geometry().contains(me->pos())) {
520
m_dragWindow->closeWindow();
524
effects->setElevatedWindow(m_dragWindow, false);
525
m_dragInProgress = false;
527
if (m_highlightedDropTarget) {
528
KIcon icon("user-trash");
529
m_highlightedDropTarget->setIcon(icon.pixmap(QSize(128, 128), QIcon::Normal));
530
m_highlightedDropTarget = NULL;
532
effects->addRepaintFull();
533
XDefineCursor(display(), m_input, QCursor(Qt::PointingHandCursor).handle());
537
// mouse is hovering above a window - use MouseActionsWindow
538
mouseActionWindow(m_leftButtonWindow);
540
// mouse is hovering above desktop - use MouseActionsDesktop
541
mouseActionDesktop(m_leftButtonDesktop);
544
if (me->button() == Qt::MidButton) {
546
// mouse is hovering above a window - use MouseActionsWindow
547
mouseActionWindow(m_middleButtonWindow);
549
// mouse is hovering above desktop - use MouseActionsDesktop
550
mouseActionDesktop(m_middleButtonDesktop);
553
if (me->button() == Qt::RightButton) {
555
// mouse is hovering above a window - use MouseActionsWindow
556
mouseActionWindow(m_rightButtonWindow);
558
// mouse is hovering above desktop - use MouseActionsDesktop
559
mouseActionDesktop(m_rightButtonDesktop);
562
// reset dragging state
563
effects->setElevatedWindow(m_dragWindow, false);
564
m_dragInProgress = false;
566
if (m_highlightedDropTarget) {
567
effects->addRepaint(m_highlightedDropTarget->geometry());
568
KIcon icon("user-trash");
569
m_highlightedDropTarget->setIcon(icon.pixmap(QSize(128, 128), QIcon::Normal));
570
m_highlightedDropTarget = NULL;
572
XDefineCursor(display(), m_input, QCursor(Qt::PointingHandCursor).handle());
573
} else if (e->type() == QEvent::MouseButtonPress && me->button() == Qt::LeftButton && hovering && m_dragToClose) {
574
m_dragStart = me->pos();
575
m_dragWindow = m_highlightedWindow;
576
m_dragInProgress = false;
577
m_highlightedDropTarget = NULL;
578
effects->setElevatedWindow(m_dragWindow, true);
579
effects->addRepaintFull();
581
if (e->type() == QEvent::MouseMove && m_dragWindow) {
582
if ((me->pos() - m_dragStart).manhattanLength() > KGlobalSettings::dndEventDelay() && !m_dragInProgress) {
583
m_dragInProgress = true;
584
XDefineCursor(display(), m_input, QCursor(Qt::ForbiddenCursor).handle());
586
if (!m_dragInProgress) {
589
effects->addRepaintFull();
590
EffectFrame *target = NULL;
591
foreach(EffectFrame *frame, m_dropTargets) {
592
if (frame->geometry().contains(me->pos())) {
597
if (target && !m_highlightedDropTarget) {
598
m_highlightedDropTarget = target;
599
KIcon icon("user-trash");
600
effects->addRepaint(m_highlightedDropTarget->geometry());
601
m_highlightedDropTarget->setIcon(icon.pixmap(QSize(128, 128), QIcon::Active));
602
XDefineCursor(display(), m_input, QCursor(Qt::DragMoveCursor).handle());
603
} else if (!target && m_highlightedDropTarget) {
604
KIcon icon("user-trash");
605
effects->addRepaint(m_highlightedDropTarget->geometry());
606
m_highlightedDropTarget->setIcon(icon.pixmap(QSize(128, 128), QIcon::Normal));
607
m_highlightedDropTarget = NULL;
608
XDefineCursor(display(), m_input, QCursor(Qt::ForbiddenCursor).handle());
613
void PresentWindowsEffect::mouseActionWindow(WindowMouseAction& action)
616
case WindowActivateAction:
617
if (m_highlightedWindow)
618
effects->activateWindow(m_highlightedWindow);
621
case WindowExitAction:
624
case WindowToCurrentDesktopAction:
625
if (m_highlightedWindow)
626
effects->windowToDesktop(m_highlightedWindow, effects->currentDesktop());
628
case WindowToAllDesktopsAction:
629
if (m_highlightedWindow) {
630
if (m_highlightedWindow->isOnAllDesktops())
631
effects->windowToDesktop(m_highlightedWindow, effects->currentDesktop());
633
effects->windowToDesktop(m_highlightedWindow, NET::OnAllDesktops);
636
case WindowMinimizeAction:
637
if (m_highlightedWindow) {
638
if (m_highlightedWindow->isMinimized())
639
m_highlightedWindow->unminimize();
641
m_highlightedWindow->minimize();
644
case WindowCloseAction:
645
if (m_highlightedWindow) {
646
m_highlightedWindow->closeWindow();
654
void PresentWindowsEffect::mouseActionDesktop(DesktopMouseAction& action)
657
case DesktopActivateAction:
658
if (m_highlightedWindow)
659
effects->activateWindow(m_highlightedWindow);
662
case DesktopExitAction:
665
case DesktopShowDesktopAction:
666
effects->setShowingDesktop(true);
674
void PresentWindowsEffect::grabbedKeyboardEvent(QKeyEvent *e)
676
if (e->type() == QEvent::KeyPress) {
677
// check for global shortcuts
678
// HACK: keyboard grab disables the global shortcuts so we have to check for global shortcut (bug 156155)
679
if (m_mode == ModeCurrentDesktop && shortcut.contains(e->key() + e->modifiers())) {
683
if (m_mode == ModeAllDesktops && shortcutAll.contains(e->key() + e->modifiers())) {
684
toggleActiveAllDesktops();
687
if (m_mode == ModeWindowClass && shortcutClass.contains(e->key() + e->modifiers())) {
693
// Wrap only if not auto-repeating
695
if (m_highlightedWindow)
696
setHighlightedWindow(relativeWindow(m_highlightedWindow, -1, 0, !e->isAutoRepeat()));
699
if (m_highlightedWindow)
700
setHighlightedWindow(relativeWindow(m_highlightedWindow, 1, 0, !e->isAutoRepeat()));
703
if (m_highlightedWindow)
704
setHighlightedWindow(relativeWindow(m_highlightedWindow, 0, -1, !e->isAutoRepeat()));
707
if (m_highlightedWindow)
708
setHighlightedWindow(relativeWindow(m_highlightedWindow, 0, 1, !e->isAutoRepeat()));
711
if (m_highlightedWindow)
712
setHighlightedWindow(relativeWindow(m_highlightedWindow, -1000, 0, false));
715
if (m_highlightedWindow)
716
setHighlightedWindow(relativeWindow(m_highlightedWindow, 1000, 0, false));
719
if (m_highlightedWindow)
720
setHighlightedWindow(relativeWindow(m_highlightedWindow, 0, -1000, false));
722
case Qt::Key_PageDown:
723
if (m_highlightedWindow)
724
setHighlightedWindow(relativeWindow(m_highlightedWindow, 0, 1000, false));
726
case Qt::Key_Backspace:
727
if (!m_windowFilter.isEmpty()) {
728
m_windowFilter.remove(m_windowFilter.length() - 1, 1);
738
if (m_highlightedWindow)
739
effects->activateWindow(m_highlightedWindow);
743
return; // Nothing at the moment
745
if (!m_windowFilter.isEmpty()) {
746
m_windowFilter.clear();
752
return; // HACK: Workaround for Qt bug on unbound keys (#178547)
754
if (!e->text().isEmpty()) {
755
m_windowFilter.append(e->text());
765
//-----------------------------------------------------------------------------
768
void PresentWindowsEffect::slotTabBoxAdded(int mode)
770
if (effects->activeFullScreenEffect() && effects->activeFullScreenEffect() != this)
774
if (((mode == TabBoxWindowsMode && m_tabBoxAllowed) ||
775
(mode == TabBoxWindowsAlternativeMode && m_tabBoxAlternativeAllowed)) &&
776
effects->currentTabBoxWindowList().count() > 0) {
777
m_tabBoxEnabled = true;
780
effects->refTabBox();
782
m_tabBoxEnabled = false;
786
void PresentWindowsEffect::slotTabBoxClosed()
789
effects->unrefTabBox();
790
setActive(false, true);
791
m_tabBoxEnabled = false;
795
void PresentWindowsEffect::slotTabBoxUpdated()
798
setHighlightedWindow(effects->currentTabBoxWindow());
801
void PresentWindowsEffect::slotTabBoxKeyEvent(QKeyEvent* event)
805
// not using the "normal" grabbedKeyboardEvent as we don't want to filter in tabbox
806
if (event->type() == QEvent::KeyPress) {
807
switch(event->key()) {
808
// Wrap only if not auto-repeating
810
setHighlightedWindow(relativeWindow(m_highlightedWindow, -1, 0, !event->isAutoRepeat()));
813
setHighlightedWindow(relativeWindow(m_highlightedWindow, 1, 0, !event->isAutoRepeat()));
816
setHighlightedWindow(relativeWindow(m_highlightedWindow, 0, -1, !event->isAutoRepeat()));
819
setHighlightedWindow(relativeWindow(m_highlightedWindow, 0, 1, !event->isAutoRepeat()));
822
setHighlightedWindow(relativeWindow(m_highlightedWindow, -1000, 0, false));
825
setHighlightedWindow(relativeWindow(m_highlightedWindow, 1000, 0, false));
828
setHighlightedWindow(relativeWindow(m_highlightedWindow, 0, -1000, false));
830
case Qt::Key_PageDown:
831
setHighlightedWindow(relativeWindow(m_highlightedWindow, 0, 1000, false));
840
//-----------------------------------------------------------------------------
842
void PresentWindowsEffect::slotPropertyNotify(EffectWindow* w, long a)
844
if (!w || (a != m_atomDesktop && a != m_atomWindows))
845
return; // Not our atom
847
if (a == m_atomDesktop) {
848
QByteArray byteData = w->readProperty(m_atomDesktop, m_atomDesktop, 32);
849
if (byteData.length() < 1) {
850
// Property was removed, end present windows
854
long* data = reinterpret_cast<long*>(byteData.data());
857
// Purposely ending present windows by issuing a NULL target
861
// present windows is active so don't do anything
865
int desktop = data[0];
866
if (desktop > effects->numberOfDesktops())
869
toggleActiveAllDesktops();
871
m_mode = ModeSelectedDesktop;
876
} else if (a == m_atomWindows) {
877
QByteArray byteData = w->readProperty(m_atomWindows, m_atomWindows, 32);
878
if (byteData.length() < 1) {
879
// Property was removed, end present windows
883
long* data = reinterpret_cast<long*>(byteData.data());
886
// Purposely ending present windows by issuing a NULL target
890
// present windows is active so don't do anything
894
// for security clear selected windows
895
m_selectedWindows.clear();
896
int length = byteData.length() / sizeof(data[0]);
897
for (int i = 0; i < length; i++) {
898
EffectWindow* foundWin = effects->findWindow(data[i]);
900
kDebug(1212) << "Invalid window targetted for present windows. Requested:" << data[i];
903
m_selectedWindows.append(foundWin);
905
m_mode = ModeWindowGroup;
911
//-----------------------------------------------------------------------------
912
// Window rearranging
914
void PresentWindowsEffect::rearrangeWindows()
919
effects->addRepaintFull(); // Trigger the first repaint
922
// Work out which windows are on which screens
923
EffectWindowList windowlist;
924
QList<EffectWindowList> windowlists;
925
for (int i = 0; i < effects->numScreens(); i++)
926
windowlists.append(EffectWindowList());
928
if (m_windowFilter.isEmpty()) {
930
// Assume we correctly set things up, should be identical to
931
// m_motionManager except just in a slightly different order.
932
windowlist = effects->currentTabBoxWindowList();
934
windowlist = m_motionManager.managedWindows();
935
foreach (EffectWindow * w, m_motionManager.managedWindows()) {
936
DataHash::iterator winData = m_windowData.find(w);
937
if (winData == m_windowData.end() || winData->deleted)
938
continue; // don't include closed windows
939
windowlists[w->screen()].append(w);
940
winData->visible = true;
943
// Can we move this filtering somewhere else?
944
foreach (EffectWindow * w, m_motionManager.managedWindows()) {
945
DataHash::iterator winData = m_windowData.find(w);
946
if (winData == m_windowData.end() || winData->deleted)
947
continue; // don't include closed windows
948
if (w->caption().contains(m_windowFilter, Qt::CaseInsensitive) ||
949
w->windowClass().contains(m_windowFilter, Qt::CaseInsensitive) ||
950
w->windowRole().contains(m_windowFilter, Qt::CaseInsensitive)) {
951
windowlist.append(w);
952
windowlists[w->screen()].append(w);
953
winData->visible = true;
955
winData->visible = false;
958
if (windowlist.isEmpty()) {
959
setHighlightedWindow(NULL); // TODO: Having a NULL highlighted window isn't really safe
963
// We filtered out the highlighted window
964
if (m_highlightedWindow) {
965
DataHash::iterator winData = m_windowData.find(m_highlightedWindow);
966
if (winData != m_windowData.end() && !winData->visible)
967
setHighlightedWindow(findFirstWindow());
968
} else if (m_tabBoxEnabled)
969
setHighlightedWindow(effects->currentTabBoxWindow());
971
setHighlightedWindow(findFirstWindow());
973
int screens = m_tabBoxEnabled ? 1 : effects->numScreens();
974
for (int screen = 0; screen < screens; screen++) {
975
EffectWindowList windows;
976
if (m_tabBoxEnabled) {
977
// Kind of cheating here
978
screen = effects->activeScreen();
979
windows = windowlist;
981
windows = windowlists[screen];
983
// Don't rearrange if the grid is the same size as what it was before to prevent
984
// windows moving to a better spot if one was filtered out.
985
if (m_layoutMode == LayoutRegularGrid &&
986
m_gridSizes[screen].columns * m_gridSizes[screen].rows &&
987
windows.size() < m_gridSizes[screen].columns * m_gridSizes[screen].rows &&
988
windows.size() > (m_gridSizes[screen].columns - 1) * m_gridSizes[screen].rows &&
989
windows.size() > m_gridSizes[screen].columns *(m_gridSizes[screen].rows - 1) &&
993
// No point continuing if there is no windows to process
994
if (!windows.count())
997
calculateWindowTransformations(windows, screen, m_motionManager);
1000
// Resize text frames if required
1001
QFontMetrics* metrics = NULL; // All fonts are the same
1002
foreach (EffectWindow * w, m_motionManager.managedWindows()) {
1003
DataHash::iterator winData = m_windowData.find(w);
1004
if (winData == m_windowData.end())
1007
metrics = new QFontMetrics(winData->textFrame->font());
1008
QRect geom = m_motionManager.targetGeometry(w).toRect();
1009
QString string = metrics->elidedText(w->caption(), Qt::ElideRight, geom.width() * 0.9);
1010
if (string != winData->textFrame->text())
1011
winData->textFrame->setText(string);
1016
void PresentWindowsEffect::calculateWindowTransformations(EffectWindowList windowlist, int screen,
1017
WindowMotionManager& motionManager, bool external)
1019
if (m_layoutMode == LayoutRegularGrid || m_tabBoxEnabled) // Force the grid for window switching
1020
calculateWindowTransformationsClosest(windowlist, screen, motionManager);
1021
else if (m_layoutMode == LayoutFlexibleGrid)
1022
calculateWindowTransformationsKompose(windowlist, screen, motionManager);
1024
calculateWindowTransformationsNatural(windowlist, screen, motionManager);
1026
// If called externally we don't need to remember this data
1028
m_windowData.clear();
1031
static inline int distance(QPoint &pos1, QPoint &pos2)
1033
const int xdiff = pos1.x() - pos2.x();
1034
const int ydiff = pos1.y() - pos2.y();
1035
return int(sqrt(float(xdiff*xdiff + ydiff*ydiff)));
1038
void PresentWindowsEffect::calculateWindowTransformationsClosest(EffectWindowList windowlist, int screen,
1039
WindowMotionManager& motionManager)
1041
// This layout mode requires at least one window visible
1042
if (windowlist.count() == 0)
1045
QRect area = effects->clientArea(ScreenArea, screen, effects->currentDesktop());
1046
if (m_showPanel) // reserve space for the panel
1047
area = effects->clientArea(MaximizeArea, screen, effects->currentDesktop());
1048
int columns = int(ceil(sqrt(double(windowlist.count()))));
1049
int rows = int(ceil(windowlist.count() / double(columns)));
1051
// Remember the size for later
1052
// If we are using this layout externally we don't need to remember m_gridSizes.
1053
if (m_gridSizes.size() != 0) {
1054
m_gridSizes[screen].columns = columns;
1055
m_gridSizes[screen].rows = rows;
1059
int slotWidth = area.width() / columns;
1060
int slotHeight = area.height() / rows;
1061
QVector<EffectWindow*> takenSlots;
1062
takenSlots.resize(rows*columns);
1065
if (m_tabBoxEnabled) {
1066
// Rearrange in the correct order. As rearrangeWindows() is only ever
1067
// called once so we can use effects->currentTabBoxWindow() here.
1068
int selectedWindow = qMax(0, windowlist.indexOf(effects->currentTabBoxWindow()));
1070
for (int i = selectedWindow; i < windowlist.count(); ++i)
1071
takenSlots[j++] = windowlist[i];
1072
for (int i = selectedWindow - 1; i >= 0; --i)
1073
takenSlots[j++] = windowlist[i];
1076
// precalculate all slot centers
1077
QVector<QPoint> slotCenters;
1078
slotCenters.resize(rows*columns);
1079
for (int x = 0; x < columns; ++x)
1080
for (int y = 0; y < rows; ++y) {
1081
slotCenters[x + y*columns] = QPoint(area.x() + slotWidth * x + slotWidth / 2,
1082
area.y() + slotHeight * y + slotHeight / 2);
1085
// Assign each window to the closest available slot
1086
EffectWindowList tmpList = windowlist; // use a QLinkedList copy instead?
1088
while (!tmpList.isEmpty()) {
1089
EffectWindow *w = tmpList.first();
1090
int slotCandidate = -1, slotCandidateDistance = INT_MAX;
1091
QPoint pos = w->geometry().center();
1092
for (int i = 0; i < columns*rows; ++i) { // all slots
1093
const int dist = distance(pos, slotCenters[i]);
1094
if (dist < slotCandidateDistance) { // window is interested in this slot
1095
EffectWindow *occupier = takenSlots[i];
1096
assert(occupier != w);
1097
if (!occupier || dist < distance((otherPos = occupier->geometry().center()), slotCenters[i])) {
1098
// either nobody lives here, or we're better - takeover the slot if it's our best
1100
slotCandidateDistance = dist;
1104
assert(slotCandidate != -1);
1105
if (takenSlots[slotCandidate])
1106
tmpList << takenSlots[slotCandidate]; // occupier needs a new home now :p
1107
tmpList.removeAll(w);
1108
takenSlots[slotCandidate] = w; // ...and we rumble in =)
1112
for (int slot = 0; slot < columns*rows; ++slot) {
1113
EffectWindow *w = takenSlots[slot];
1114
if (!w) // some slots might be empty
1117
// Work out where the slot is
1119
area.x() + (slot % columns) * slotWidth,
1120
area.y() + (slot / columns) * slotHeight,
1121
slotWidth, slotHeight);
1122
target.adjust(10, 10, -10, -10); // Borders
1124
if (target.width() / double(w->width()) < target.height() / double(w->height())) {
1125
// Center vertically
1126
scale = target.width() / double(w->width());
1127
target.moveTop(target.top() + (target.height() - int(w->height() * scale)) / 2);
1128
target.setHeight(int(w->height() * scale));
1130
// Center horizontally
1131
scale = target.height() / double(w->height());
1132
target.moveLeft(target.left() + (target.width() - int(w->width() * scale)) / 2);
1133
target.setWidth(int(w->width() * scale));
1135
// Don't scale the windows too much
1136
if (scale > 2.0 || (scale > 1.0 && (w->width() > 300 || w->height() > 300))) {
1137
scale = (w->width() > 300 || w->height() > 300) ? 1.0 : 2.0;
1139
target.center().x() - int(w->width() * scale) / 2,
1140
target.center().y() - int(w->height() * scale) / 2,
1141
scale * w->width(), scale * w->height());
1143
motionManager.moveWindow(w, target);
1147
void PresentWindowsEffect::calculateWindowTransformationsKompose(EffectWindowList windowlist, int screen,
1148
WindowMotionManager& motionManager)
1150
// This layout mode requires at least one window visible
1151
if (windowlist.count() == 0)
1154
QRect availRect = effects->clientArea(ScreenArea, screen, effects->currentDesktop());
1155
if (m_showPanel) // reserve space for the panel
1156
availRect = effects->clientArea(MaximizeArea, screen, effects->currentDesktop());
1157
qSort(windowlist); // The location of the windows should not depend on the stacking order
1159
// Following code is taken from Kompose 0.5.4, src/komposelayout.cpp
1163
double parentRatio = availRect.width() / (double)availRect.height();
1164
// Use more columns than rows when parent's width > parent's height
1165
if (parentRatio > 1) {
1166
columns = (int)ceil(sqrt((double)windowlist.count()));
1167
rows = (int)ceil((double)windowlist.count() / (double)columns);
1169
rows = (int)ceil(sqrt((double)windowlist.count()));
1170
columns = (int)ceil((double)windowlist.count() / (double)rows);
1172
//kDebug(1212) << "Using " << rows << " rows & " << columns << " columns for " << windowlist.count() << " clients";
1174
// Calculate width & height
1175
int w = (availRect.width() - (columns + 1) * spacing) / columns;
1176
int h = (availRect.height() - (rows + 1) * spacing) / rows;
1178
EffectWindowList::iterator it(windowlist.begin());
1179
QList<QRect> geometryRects;
1180
QList<int> maxRowHeights;
1182
for (int i = 0; i < rows; ++i) {
1183
int xOffsetFromLastCol = 0;
1184
int maxHeightInRow = 0;
1186
for (int j = 0; j < columns; ++j) {
1187
EffectWindow* window;
1189
// Check for end of List
1190
if (it == windowlist.end())
1194
// Calculate width and height of widget
1195
double ratio = aspectRatio(window);
1202
// use width of two boxes if there is no right neighbour
1203
if (window == windowlist.last() && j != columns - 1) {
1206
++it; // We need access to the neighbour in the following
1207
// expand if right neighbour has ratio < 1
1208
if (j != columns - 1 && it != windowlist.end() && aspectRatio(*it) < 1) {
1209
int addW = w - widthForHeight(*it, h);
1219
double widthByHeight = widthForHeight(window, usableH);
1220
double heightByWidth = heightForWidth(window, usableW);
1221
if ((ratio >= 1.0 && heightByWidth <= usableH) ||
1222
(ratio < 1.0 && widthByHeight > usableW)) {
1224
widgeth = (int)heightByWidth;
1225
} else if ((ratio < 1.0 && widthByHeight <= usableW) ||
1226
(ratio >= 1.0 && heightByWidth > usableH)) {
1228
widgetw = (int)widthByHeight;
1230
// Don't upscale large-ish windows
1231
if (widgetw > window->width() && (window->width() > 300 || window->height() > 300)) {
1232
widgetw = window->width();
1233
widgeth = window->height();
1237
// Set the Widget's size
1239
int alignmentXoffset = 0;
1240
int alignmentYoffset = 0;
1241
if (i == 0 && h > widgeth)
1242
alignmentYoffset = h - widgeth;
1243
if (j == 0 && w > widgetw)
1244
alignmentXoffset = w - widgetw;
1245
QRect geom(availRect.x() + j *(w + spacing) + spacing + alignmentXoffset + xOffsetFromLastCol,
1246
availRect.y() + i *(h + spacing) + spacing + alignmentYoffset,
1248
geometryRects.append(geom);
1250
// Set the x offset for the next column
1251
if (alignmentXoffset == 0)
1252
xOffsetFromLastCol += widgetw - w;
1253
if (maxHeightInRow < widgeth)
1254
maxHeightInRow = widgeth;
1256
maxRowHeights.append(maxHeightInRow);
1260
for (int i = 0; i < rows; i++) {
1261
for (int j = 0; j < columns; j++) {
1262
int pos = i * columns + j;
1263
if (pos >= windowlist.count())
1266
EffectWindow* window = windowlist[pos];
1267
QRect target = geometryRects[pos];
1268
target.setY(target.y() + topOffset);
1269
// @Marrtin: any idea what this is good for?
1270
// DataHash::iterator winData = m_windowData.find(window);
1271
// if (winData != m_windowData.end())
1272
// winData->slot = pos;
1273
motionManager.moveWindow(window, target);
1275
//kDebug(1212) << "Window '" << window->caption() << "' gets moved to (" <<
1276
// mWindowData[window].area.left() << "; " << mWindowData[window].area.right() <<
1277
// "), scale: " << mWindowData[window].scale << endl;
1279
if (maxRowHeights[i] - h > 0)
1280
topOffset += maxRowHeights[i] - h;
1284
void PresentWindowsEffect::calculateWindowTransformationsNatural(EffectWindowList windowlist, int screen,
1285
WindowMotionManager& motionManager)
1287
// If windows do not overlap they scale into nothingness, fix by resetting. To reproduce
1288
// just have a single window on a Xinerama screen or have two windows that do not touch.
1289
// TODO: Work out why this happens, is most likely a bug in the manager.
1290
foreach (EffectWindow * w, windowlist)
1291
if (motionManager.transformedGeometry(w) == w->geometry())
1292
motionManager.reset(w);
1294
if (windowlist.count() == 1) {
1295
// Just move the window to its original location to save time
1296
if (effects->clientArea(FullScreenArea, windowlist[0]).contains(windowlist[0]->geometry())) {
1297
motionManager.moveWindow(windowlist[0], windowlist[0]->geometry());
1302
// As we are using pseudo-random movement (See "slot") we need to make sure the list
1303
// is always sorted the same way no matter which window is currently active.
1306
QRect area = effects->clientArea(ScreenArea, screen, effects->currentDesktop());
1307
if (m_showPanel) // reserve space for the panel
1308
area = effects->clientArea(MaximizeArea, screen, effects->currentDesktop());
1309
QRect bounds = area;
1311
QHash<EffectWindow*, QRect> targets;
1312
QHash<EffectWindow*, int> directions;
1313
foreach (EffectWindow * w, windowlist) {
1314
bounds = bounds.united(w->geometry());
1315
targets[w] = w->geometry();
1316
// Reuse the unused "slot" as a preferred direction attribute. This is used when the window
1317
// is on the edge of the screen to try to use as much screen real estate as possible.
1318
directions[w] = direction;
1324
// Iterate over all windows, if two overlap push them apart _slightly_ as we try to
1325
// brute-force the most optimal positions over many iterations.
1329
foreach (EffectWindow * w, windowlist) {
1330
QRect *target_w = &targets[w];
1331
foreach (EffectWindow * e, windowlist) {
1334
QRect *target_e = &targets[e];
1335
if (target_w->adjusted(-5, -5, 5, 5).intersects(target_e->adjusted(-5, -5, 5, 5))) {
1338
// Determine pushing direction
1339
QPoint diff(target_e->center() - target_w->center());
1340
// Prevent dividing by zero and non-movement
1341
if (diff.x() == 0 && diff.y() == 0)
1343
// Try to keep screen aspect ratio
1344
//if (bounds.height() / bounds.width() > area.height() / area.width())
1345
// diff.setY(diff.y() / 2);
1347
// diff.setX(diff.x() / 2);
1348
// Approximate a vector of between 10px and 20px in magnitude in the same direction
1349
diff *= m_accuracy / double(diff.manhattanLength());
1350
// Move both windows apart
1351
target_w->translate(-diff);
1352
target_e->translate(diff);
1354
// Try to keep the bounding rect the same aspect as the screen so that more
1355
// screen real estate is utilised. We do this by splitting the screen into nine
1356
// equal sections, if the window center is in any of the corner sections pull the
1357
// window towards the outer corner. If it is in any of the other edge sections
1358
// alternate between each corner on that edge. We don't want to determine it
1359
// randomly as it will not produce consistant locations when using the filter.
1360
// Only move one window so we don't cause large amounts of unnecessary zooming
1361
// in some situations. We need to do this even when expanding later just in case
1362
// all windows are the same size.
1363
// (We are using an old bounding rect for this, hopefully it doesn't matter)
1364
int xSection = (target_w->x() - bounds.x()) / (bounds.width() / 3);
1365
int ySection = (target_w->y() - bounds.y()) / (bounds.height() / 3);
1366
diff = QPoint(0, 0);
1367
if (xSection != 1 || ySection != 1) { // Remove this if you want the center to pull as well
1369
xSection = (directions[w] / 2 ? 2 : 0);
1371
ySection = (directions[w] % 2 ? 2 : 0);
1373
if (xSection == 0 && ySection == 0)
1374
diff = QPoint(bounds.topLeft() - target_w->center());
1375
if (xSection == 2 && ySection == 0)
1376
diff = QPoint(bounds.topRight() - target_w->center());
1377
if (xSection == 2 && ySection == 2)
1378
diff = QPoint(bounds.bottomRight() - target_w->center());
1379
if (xSection == 0 && ySection == 2)
1380
diff = QPoint(bounds.bottomLeft() - target_w->center());
1381
if (diff.x() != 0 || diff.y() != 0) {
1382
diff *= m_accuracy / double(diff.manhattanLength());
1383
target_w->translate(diff);
1386
// Update bounding rect
1387
bounds = bounds.united(*target_w);
1388
bounds = bounds.united(*target_e);
1394
// Work out scaling by getting the most top-left and most bottom-right window coords.
1395
// The 20's and 10's are so that the windows don't touch the edge of the screen.
1398
scale = 1.0; // Don't add borders to the screen
1399
else if (area.width() / double(bounds.width()) < area.height() / double(bounds.height()))
1400
scale = (area.width() - 20) / double(bounds.width());
1402
scale = (area.height() - 20) / double(bounds.height());
1403
// Make bounding rect fill the screen size for later steps
1405
bounds.x() - (area.width() - 20 - bounds.width() * scale) / 2 - 10 / scale,
1406
bounds.y() - (area.height() - 20 - bounds.height() * scale) / 2 - 10 / scale,
1407
area.width() / scale,
1408
area.height() / scale
1411
// Move all windows back onto the screen and set their scale
1412
QHash<EffectWindow*, QRect>::iterator target = targets.begin();
1413
while (target != targets.end()) {
1414
target->setRect((target->x() - bounds.x()) * scale + area.x(),
1415
(target->y() - bounds.y()) * scale + area.y(),
1416
target->width() * scale,
1417
target->height() * scale
1422
// Try to fill the gaps by enlarging windows if they have the space
1424
// Don't expand onto or over the border
1425
QRegion borderRegion(area.adjusted(-200, -200, 200, 200));
1426
borderRegion ^= area.adjusted(10 / scale, 10 / scale, -10 / scale, -10 / scale);
1431
foreach (EffectWindow * w, windowlist) {
1433
QRect *target = &targets[w];
1434
// This may cause some slight distortion if the windows are enlarged a large amount
1435
int widthDiff = m_accuracy;
1436
int heightDiff = heightForWidth(w, target->width() + widthDiff) - target->height();
1437
int xDiff = widthDiff / 2; // Also move a bit in the direction of the enlarge, allows the
1438
int yDiff = heightDiff / 2; // center windows to be enlarged if there is gaps on the side.
1440
// Attempt enlarging to the top-right
1442
target->setRect(target->x() + xDiff,
1443
target->y() - yDiff - heightDiff,
1444
target->width() + widthDiff,
1445
target->height() + heightDiff
1447
if (isOverlappingAny(w, targets, borderRegion))
1452
// Attempt enlarging to the bottom-right
1455
target->x() + xDiff,
1456
target->y() + yDiff,
1457
target->width() + widthDiff,
1458
target->height() + heightDiff
1460
if (isOverlappingAny(w, targets, borderRegion))
1465
// Attempt enlarging to the bottom-left
1468
target->x() - xDiff - widthDiff,
1469
target->y() + yDiff,
1470
target->width() + widthDiff,
1471
target->height() + heightDiff
1473
if (isOverlappingAny(w, targets, borderRegion))
1478
// Attempt enlarging to the top-left
1481
target->x() - xDiff - widthDiff,
1482
target->y() - yDiff - heightDiff,
1483
target->width() + widthDiff,
1484
target->height() + heightDiff
1486
if (isOverlappingAny(w, targets, borderRegion))
1493
// The expanding code above can actually enlarge windows over 1.0/2.0 scale, we don't like this
1494
// We can't add this to the loop above as it would cause a never-ending loop so we have to make
1495
// do with the less-than-optimal space usage with using this method.
1496
foreach (EffectWindow * w, windowlist) {
1497
QRect *target = &targets[w];
1498
double scale = target->width() / double(w->width());
1499
if (scale > 2.0 || (scale > 1.0 && (w->width() > 300 || w->height() > 300))) {
1500
scale = (w->width() > 300 || w->height() > 300) ? 1.0 : 2.0;
1502
target->center().x() - int(w->width() * scale) / 2,
1503
target->center().y() - int(w->height() * scale) / 2,
1505
w->height() * scale);
1510
// Notify the motion manager of the targets
1511
foreach (EffectWindow * w, windowlist)
1512
motionManager.moveWindow(w, targets.value(w));
1516
bool PresentWindowsEffect::isOverlappingAny(EffectWindow *w, const QHash<EffectWindow*, QRect> &targets, const QRegion &border)
1518
QHash<EffectWindow*, QRect>::const_iterator winTarget = targets.find(w);
1519
if (winTarget == targets.constEnd())
1521
if (border.intersects(*winTarget))
1523
// Is there a better way to do this?
1524
QHash<EffectWindow*, QRect>::const_iterator target;
1525
for (target = targets.constBegin(); target != targets.constEnd(); ++target) {
1526
if (target == winTarget)
1528
if (winTarget->adjusted(-5, -5, 5, 5).intersects(target->adjusted(-5, -5, 5, 5)))
1534
//-----------------------------------------------------------------------------
1537
void PresentWindowsEffect::setActive(bool active, bool closingTab)
1539
if (effects->activeFullScreenEffect() && effects->activeFullScreenEffect() != this)
1541
if (m_activated == active)
1543
if (m_activated && m_tabBoxEnabled && !closingTab) {
1544
effects->closeTabBox();
1547
m_activated = active;
1549
m_decalOpacity = 0.0;
1550
m_highlightedWindow = NULL;
1551
m_windowFilter.clear();
1553
m_closeView = new CloseWindowView();
1554
connect(m_closeView, SIGNAL(close()), SLOT(closeWindow()));
1556
// Add every single window to m_windowData (Just calling [w] creates it)
1557
foreach (EffectWindow * w, effects->stackingOrder()) {
1558
DataHash::iterator winData;
1559
if ((winData = m_windowData.find(w)) != m_windowData.end()) {
1560
winData->visible = isVisibleWindow(w);
1561
continue; // Happens if we reactivate before the ending animation finishes
1563
winData = m_windowData.insert(w, WindowData());
1564
winData->visible = isVisibleWindow(w);
1565
winData->deleted = false;
1566
winData->referenced = false;
1567
winData->opacity = 0.0;
1568
if (w->isOnCurrentDesktop() && !w->isMinimized())
1569
winData->opacity = 1.0;
1570
winData->highlight = 1.0;
1571
winData->textFrame = effects->effectFrame(EffectFrameUnstyled, false);
1574
font.setPointSize(12);
1575
winData->textFrame->setFont(font);
1576
winData->iconFrame = effects->effectFrame(EffectFrameUnstyled, false);
1577
winData->iconFrame->setAlignment(Qt::AlignRight | Qt::AlignBottom);
1578
winData->iconFrame->setIcon(w->icon());
1581
if (m_tabBoxEnabled) {
1582
DataHash::iterator winData;
1583
foreach (EffectWindow * w, effects->currentTabBoxWindowList()) {
1586
m_motionManager.manage(w);
1587
if ((winData = m_windowData.find(w)) != m_windowData.end())
1588
winData->visible = effects->currentTabBoxWindowList().contains(w);
1590
// Hide windows not in the list
1591
foreach (EffectWindow * w, effects->stackingOrder()) {
1592
if ((winData = m_windowData.find(w)) != m_windowData.end())
1593
winData->visible = isVisibleWindow(w) &&
1594
(!isSelectableWindow(w) || effects->currentTabBoxWindowList().contains(w));
1597
// Filter out special windows such as panels and taskbars
1598
foreach (EffectWindow * w, effects->stackingOrder())
1599
if (isSelectableWindow(w))
1600
m_motionManager.manage(w);
1602
if (m_motionManager.managedWindows().isEmpty() ||
1603
((m_motionManager.managedWindows().count() == 1) && m_motionManager.managedWindows().first()->isOnCurrentDesktop() &&
1604
(m_ignoreMinimized || !m_motionManager.managedWindows().first()->isMinimized()))) {
1605
// No point triggering if there is nothing to do
1606
m_activated = false;
1608
DataHash::iterator i = m_windowData.begin();
1609
while (i != m_windowData.end()) {
1610
delete i.value().textFrame;
1611
delete i.value().iconFrame;
1614
m_windowData.clear();
1616
m_motionManager.unmanageAll();
1620
// Create temporary input window to catch mouse events
1621
m_input = effects->createFullScreenInputWindow(this, Qt::PointingHandCursor);
1622
m_hasKeyboardGrab = effects->grabKeyboard(this);
1623
effects->setActiveFullScreenEffect(this);
1625
m_gridSizes.clear();
1626
for (int i = 0; i < effects->numScreens(); ++i) {
1627
m_gridSizes.append(GridSize());
1628
if (m_dragToClose) {
1629
const QRect screenRect = effects->clientArea(FullScreenArea, i, 1);
1630
EffectFrame *frame = effects->effectFrame(EffectFrameNone, false);
1631
KIcon icon("user-trash");
1632
frame->setIcon(icon.pixmap(QSize(128, 128)));
1633
frame->setPosition(QPoint(screenRect.x() + screenRect.width(), screenRect.y()));
1634
frame->setAlignment(Qt::AlignRight | Qt::AlignTop);
1635
m_dropTargets.append(frame);
1640
if (m_tabBoxEnabled)
1641
setHighlightedWindow(effects->currentTabBoxWindow());
1643
setHighlightedWindow(effects->activeWindow());
1645
foreach (EffectWindow * w, effects->stackingOrder()) {
1647
w->setData(WindowForceBlurRole, QVariant(true));
1651
// Fade in/out all windows
1652
EffectWindow *activeWindow = effects->activeWindow();
1653
if (m_tabBoxEnabled)
1654
activeWindow = effects->currentTabBoxWindow();
1655
int desktop = effects->currentDesktop();
1656
if (activeWindow && !activeWindow->isOnAllDesktops())
1657
desktop = activeWindow->desktop();
1658
foreach (EffectWindow * w, effects->stackingOrder()) {
1659
DataHash::iterator winData = m_windowData.find(w);
1660
if (winData != m_windowData.end())
1661
winData->visible = (w->isOnDesktop(desktop) || w->isOnAllDesktops()) &&
1662
!w->isMinimized() && (w->visibleInClientGroup() || winData->visible);
1666
while (!m_dropTargets.empty()) {
1667
delete m_dropTargets.takeFirst();
1670
// Move all windows back to their original position
1671
foreach (EffectWindow * w, m_motionManager.managedWindows())
1672
m_motionManager.moveWindow(w, w->geometry());
1673
m_filterFrame->free();
1674
m_windowFilter.clear();
1675
m_selectedWindows.clear();
1677
effects->destroyInputWindow(m_input);
1678
if (m_hasKeyboardGrab)
1679
effects->ungrabKeyboard();
1680
m_hasKeyboardGrab = false;
1682
// destroy atom on manager window
1683
if (m_managerWindow) {
1684
if (m_mode == ModeSelectedDesktop)
1685
m_managerWindow->deleteProperty(m_atomDesktop);
1686
else if (m_mode == ModeWindowGroup)
1687
m_managerWindow->deleteProperty(m_atomWindows);
1688
m_managerWindow = NULL;
1691
effects->addRepaintFull(); // Trigger the first repaint
1694
//-----------------------------------------------------------------------------
1697
void PresentWindowsEffect::updateFilterFrame()
1699
QRect area = effects->clientArea(ScreenArea, effects->activeScreen(), effects->currentDesktop());
1700
m_filterFrame->setPosition(QPoint(area.x() + area.width() / 2, area.y() + area.height() / 2));
1701
m_filterFrame->setText(i18n("Filter:\n%1", m_windowFilter));
1704
//-----------------------------------------------------------------------------
1707
bool PresentWindowsEffect::isSelectableWindow(EffectWindow *w)
1709
if (!w->isOnCurrentActivity())
1711
if (w->isSpecialWindow() || w->isUtility())
1715
if (!w->acceptsFocus())
1717
if (!w->visibleInClientGroup())
1719
if (w->isSkipSwitcher())
1721
if (w == effects->findWindow(m_closeView->winId()))
1723
if (m_tabBoxEnabled)
1725
if (m_ignoreMinimized && w->isMinimized())
1729
case ModeAllDesktops:
1731
case ModeCurrentDesktop:
1732
return w->isOnCurrentDesktop();
1733
case ModeSelectedDesktop:
1734
return w->isOnDesktop(m_desktop);
1735
case ModeWindowGroup:
1736
return m_selectedWindows.contains(w);
1737
case ModeWindowClass:
1738
return m_class == w->windowClass();
1742
bool PresentWindowsEffect::isVisibleWindow(EffectWindow *w)
1746
return isSelectableWindow(w);
1749
void PresentWindowsEffect::setHighlightedWindow(EffectWindow *w)
1751
if (w == m_highlightedWindow || (w != NULL && !m_motionManager.isManaging(w)))
1754
m_closeView->hide();
1755
if (m_highlightedWindow)
1756
m_highlightedWindow->addRepaintFull(); // Trigger the first repaint
1757
m_highlightedWindow = w;
1758
if (m_highlightedWindow)
1759
m_highlightedWindow->addRepaintFull(); // Trigger the first repaint
1761
if (m_tabBoxEnabled && m_highlightedWindow)
1762
effects->setTabBoxWindow(w);
1763
updateCloseWindow();
1766
void PresentWindowsEffect::updateCloseWindow()
1768
if (m_doNotCloseWindows)
1770
if (m_closeView->isVisible())
1772
if (!m_highlightedWindow) {
1773
m_closeView->hide();
1776
const QRectF rect = m_motionManager.targetGeometry(m_highlightedWindow);
1777
m_closeView->setGeometry(rect.x() + rect.width() - m_closeView->sceneRect().width(), rect.y(),
1778
m_closeView->sceneRect().width(), m_closeView->sceneRect().height());
1779
if (rect.contains(effects->cursorPos()))
1780
m_closeView->delayedShow();
1782
m_closeView->hide();
1785
void PresentWindowsEffect::closeWindow()
1787
if (m_highlightedWindow)
1788
m_highlightedWindow->closeWindow();
1791
EffectWindow* PresentWindowsEffect::relativeWindow(EffectWindow *w, int xdiff, int ydiff, bool wrap) const
1793
// TODO: Is it possible to select hidden windows?
1795
QRect area = effects->clientArea(FullArea, 0, effects->currentDesktop());
1798
// Detect across the width of the desktop
1802
for (int i = 0; i < xdiff; i++) {
1803
QRectF wArea = m_motionManager.transformedGeometry(w);
1804
detectRect = QRect(0, wArea.y(), area.width(), wArea.height());
1806
foreach (EffectWindow * e, m_motionManager.managedWindows()) {
1807
DataHash::const_iterator winData = m_windowData.find(e);
1808
if (winData == m_windowData.end() || !winData->visible)
1810
QRectF eArea = m_motionManager.transformedGeometry(e);
1811
if (eArea.intersects(detectRect) &&
1812
eArea.x() > wArea.x()) {
1816
QRectF nArea = m_motionManager.transformedGeometry(next);
1817
if (eArea.x() < nArea.x())
1823
if (wrap) // We are at the right-most window, now get the left-most one to wrap
1824
return relativeWindow(w, -1000, 0, false);
1825
break; // No more windows to the right
1832
for (int i = 0; i < -xdiff; i++) {
1833
QRectF wArea = m_motionManager.transformedGeometry(w);
1834
detectRect = QRect(0, wArea.y(), area.width(), wArea.height());
1836
foreach (EffectWindow * e, m_motionManager.managedWindows()) {
1837
DataHash::const_iterator winData = m_windowData.find(e);
1838
if (winData == m_windowData.end() || !winData->visible)
1840
QRectF eArea = m_motionManager.transformedGeometry(e);
1841
if (eArea.intersects(detectRect) &&
1842
eArea.x() + eArea.width() < wArea.x() + wArea.width()) {
1846
QRectF nArea = m_motionManager.transformedGeometry(next);
1847
if (eArea.x() + eArea.width() > nArea.x() + nArea.width())
1853
if (wrap) // We are at the left-most window, now get the right-most one to wrap
1854
return relativeWindow(w, 1000, 0, false);
1855
break; // No more windows to the left
1863
// Detect across the height of the desktop
1867
for (int i = 0; i < ydiff; i++) {
1868
QRectF wArea = m_motionManager.transformedGeometry(w);
1869
detectRect = QRect(wArea.x(), 0, wArea.width(), area.height());
1871
foreach (EffectWindow * e, m_motionManager.managedWindows()) {
1872
DataHash::const_iterator winData = m_windowData.find(e);
1873
if (winData == m_windowData.end() || !winData->visible)
1875
QRectF eArea = m_motionManager.transformedGeometry(e);
1876
if (eArea.intersects(detectRect) &&
1877
eArea.y() > wArea.y()) {
1881
QRectF nArea = m_motionManager.transformedGeometry(next);
1882
if (eArea.y() < nArea.y())
1888
if (wrap) // We are at the bottom-most window, now get the top-most one to wrap
1889
return relativeWindow(w, 0, -1000, false);
1890
break; // No more windows to the bottom
1897
for (int i = 0; i < -ydiff; i++) {
1898
QRectF wArea = m_motionManager.transformedGeometry(w);
1899
detectRect = QRect(wArea.x(), 0, wArea.width(), area.height());
1901
foreach (EffectWindow * e, m_motionManager.managedWindows()) {
1902
DataHash::const_iterator winData = m_windowData.find(e);
1903
if (winData == m_windowData.end() || !winData->visible)
1905
QRectF eArea = m_motionManager.transformedGeometry(e);
1906
if (eArea.intersects(detectRect) &&
1907
eArea.y() + eArea.height() < wArea.y() + wArea.height()) {
1911
QRectF nArea = m_motionManager.transformedGeometry(next);
1912
if (eArea.y() + eArea.height() > nArea.y() + nArea.height())
1918
if (wrap) // We are at the top-most window, now get the bottom-most one to wrap
1919
return relativeWindow(w, 0, 1000, false);
1920
break; // No more windows to the top
1928
abort(); // Should never get here
1931
EffectWindow* PresentWindowsEffect::findFirstWindow() const
1933
EffectWindow *topLeft = NULL;
1934
QRectF topLeftGeometry;
1935
foreach (EffectWindow * w, m_motionManager.managedWindows()) {
1936
DataHash::const_iterator winData = m_windowData.find(w);
1937
if (winData == m_windowData.end())
1939
QRectF geometry = m_motionManager.transformedGeometry(w);
1940
if (winData->visible == false)
1941
continue; // Not visible
1942
if (winData->deleted)
1943
continue; // Window has been closed
1944
if (topLeft == NULL) {
1946
topLeftGeometry = geometry;
1947
} else if (geometry.x() < topLeftGeometry.x() || geometry.y() < topLeftGeometry.y()) {
1949
topLeftGeometry = geometry;
1955
void PresentWindowsEffect::globalShortcutChanged(const QKeySequence& seq)
1957
shortcut = KShortcut(seq);
1960
void PresentWindowsEffect::globalShortcutChangedAll(const QKeySequence& seq)
1962
shortcutAll = KShortcut(seq);
1965
void PresentWindowsEffect::globalShortcutChangedClass(const QKeySequence& seq)
1967
shortcutClass = KShortcut(seq);
1970
/************************************************
1972
************************************************/
1973
CloseWindowView::CloseWindowView(QWidget* parent)
1974
: QGraphicsView(parent)
1975
, m_delayedShowTimer(new QTimer(this))
1977
setWindowFlags(Qt::X11BypassWindowManagerHint);
1978
setAttribute(Qt::WA_TranslucentBackground);
1979
setFrameShape(QFrame::NoFrame);
1980
QPalette pal = palette();
1981
pal.setColor(backgroundRole(), Qt::transparent);
1983
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
1984
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
1987
QGraphicsScene* scene = new QGraphicsScene(this);
1988
m_closeButton = new Plasma::PushButton();
1989
m_closeButton->setIcon(KIcon("window-close"));
1990
scene->addItem(m_closeButton);
1991
connect(m_closeButton, SIGNAL(clicked()), SIGNAL(close()));
1993
QGraphicsLinearLayout *layout = new QGraphicsLinearLayout;
1994
layout->addItem(m_closeButton);
1996
QGraphicsWidget *form = new QGraphicsWidget;
1997
form->setLayout(layout);
1998
form->setGeometry(0, 0, 32, 32);
1999
scene->addItem(form);
2001
m_frame = new Plasma::FrameSvg(this);
2002
m_frame->setImagePath("dialogs/background");
2003
m_frame->setCacheAllRenderedFrames(true);
2004
m_frame->setEnabledBorders(Plasma::FrameSvg::AllBorders);
2005
qreal left, top, right, bottom;
2006
m_frame->getMargins(left, top, right, bottom);
2007
qreal width = form->size().width() + left + right;
2008
qreal height = form->size().height() + top + bottom;
2009
m_frame->resizeFrame(QSizeF(width, height));
2010
Plasma::WindowEffects::enableBlurBehind(winId(), true, m_frame->mask());
2011
Plasma::WindowEffects::overrideShadow(winId(), true);
2012
form->setPos(left, top);
2013
scene->setSceneRect(QRectF(QPointF(0, 0), QSizeF(width, height)));
2017
m_delayedShowTimer->setSingleShot(true);
2018
m_delayedShowTimer->setInterval(500);
2019
connect(m_delayedShowTimer, SIGNAL(timeout()), SLOT(show()));
2022
void CloseWindowView::windowInputMouseEvent(QMouseEvent* e)
2024
if (m_delayedShowTimer->isActive())
2026
if (e->type() == QEvent::MouseMove) {
2028
} else if (e->type() == QEvent::MouseButtonPress) {
2030
} else if (e->type() == QEvent::MouseButtonDblClick) {
2031
mouseDoubleClickEvent(e);
2032
} else if (e->type() == QEvent::MouseButtonRelease) {
2033
mouseReleaseEvent(e);
2037
void CloseWindowView::drawBackground(QPainter* painter, const QRectF& rect)
2040
painter->setRenderHint(QPainter::Antialiasing);
2041
m_frame->paintFrame(painter);
2044
void CloseWindowView::hide()
2046
m_delayedShowTimer->stop();
2050
void CloseWindowView::delayedShow()
2052
if (isVisible() || m_delayedShowTimer->isActive())
2054
m_delayedShowTimer->start();
2060
#include "presentwindows.moc"