2009-04-15 19:31:20 +00:00
|
|
|
/********************************************************************
|
|
|
|
KWin - the KDE window manager
|
|
|
|
This file is part of the KDE project.
|
|
|
|
|
|
|
|
Copyright (C) 2009 Michael Zanetti <michael_zanetti@gmx.net>
|
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*********************************************************************/
|
|
|
|
|
|
|
|
#include "slideback.h"
|
|
|
|
|
|
|
|
#include <kconfiggroup.h>
|
|
|
|
#include <kdebug.h>
|
|
|
|
|
|
|
|
namespace KWin
|
|
|
|
{
|
|
|
|
|
|
|
|
KWIN_EFFECT( slideback, SlideBackEffect )
|
|
|
|
|
|
|
|
SlideBackEffect::SlideBackEffect()
|
|
|
|
{
|
2009-05-24 14:00:35 +00:00
|
|
|
updateStackingOrder();
|
|
|
|
disabled = false;
|
2009-07-06 11:30:46 +00:00
|
|
|
unminimizedWindow = NULL;
|
2009-04-15 19:31:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void SlideBackEffect::windowActivated( EffectWindow* w )
|
|
|
|
{
|
2009-05-17 19:38:06 +00:00
|
|
|
if( w == NULL || w->keepAbove()) // plasma popups, yakuake etc...
|
2009-04-15 19:31:20 +00:00
|
|
|
{
|
|
|
|
return;
|
2009-05-17 19:38:06 +00:00
|
|
|
}
|
|
|
|
|
2009-04-15 19:31:20 +00:00
|
|
|
if( disabled || effects->activeFullScreenEffect() ) // TabBox or PresentWindows/Cube in progress
|
|
|
|
{
|
|
|
|
updateStackingOrder();
|
|
|
|
disabled = false;
|
|
|
|
return;
|
|
|
|
}
|
2009-07-06 11:13:48 +00:00
|
|
|
|
2009-05-17 19:38:06 +00:00
|
|
|
if( !isWindowUsable( w ) || !stackingOrderChanged() ) // Focus changed but stacking still the same
|
2009-04-15 19:31:20 +00:00
|
|
|
{
|
|
|
|
updateStackingOrder();
|
|
|
|
return;
|
|
|
|
}
|
2009-07-06 11:13:48 +00:00
|
|
|
|
|
|
|
if( unminimizedWindow == w ){ // A window was activated by beeing unminimized. Don't trigger SlideBack.
|
|
|
|
unminimizedWindow = NULL;
|
|
|
|
updateStackingOrder();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2009-04-15 19:31:20 +00:00
|
|
|
// Determine all windows on top of the activated one
|
|
|
|
bool currentFound = false;
|
|
|
|
foreach( EffectWindow *tmp, oldStackingOrder )
|
|
|
|
{
|
|
|
|
if( !currentFound )
|
|
|
|
{
|
|
|
|
if( tmp == w )
|
|
|
|
{
|
|
|
|
currentFound = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2009-04-27 16:15:08 +00:00
|
|
|
if( isWindowUsable( tmp ) && ( tmp->isOnDesktop( w->desktop() ) || w->isOnAllDesktops() ) )
|
2009-04-15 19:31:20 +00:00
|
|
|
{
|
|
|
|
// Do we have to move it?
|
2009-04-22 12:49:28 +00:00
|
|
|
if( intersects( w, tmp->geometry() ) )
|
2009-04-15 19:31:20 +00:00
|
|
|
{
|
2009-04-22 12:49:28 +00:00
|
|
|
QRect slideRect;
|
|
|
|
if( w->isModal() )
|
|
|
|
{
|
|
|
|
QRect modalGroupGeometry = w->geometry();
|
|
|
|
foreach( EffectWindow *modalWindow, w->mainWindows() )
|
|
|
|
{
|
|
|
|
modalGroupGeometry = modalGroupGeometry.united( modalWindow->geometry() );
|
|
|
|
}
|
|
|
|
slideRect = getSlideDestination( modalGroupGeometry, tmp->geometry() );
|
|
|
|
}
|
|
|
|
else
|
2009-04-27 12:57:52 +00:00
|
|
|
{
|
2009-04-22 12:49:28 +00:00
|
|
|
slideRect = getSlideDestination( w->geometry(), tmp->geometry() );
|
2009-04-27 12:57:52 +00:00
|
|
|
}
|
2009-04-15 19:31:20 +00:00
|
|
|
effects->setElevatedWindow( tmp, true );
|
|
|
|
elevatedList.append( tmp );
|
|
|
|
motionManager.manage( tmp );
|
|
|
|
motionManager.moveWindow( tmp, slideRect );
|
|
|
|
destinationList.insert( tmp, slideRect );
|
|
|
|
coveringWindows.append( tmp );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
//Does it intersect with a moved (elevated) window and do we have to elevate it too?
|
|
|
|
foreach( EffectWindow *elevatedWindow, elevatedList )
|
|
|
|
{
|
|
|
|
if( tmp->geometry().intersects( elevatedWindow->geometry() ) )
|
|
|
|
{
|
|
|
|
effects->setElevatedWindow( tmp, true );
|
|
|
|
elevatedList.append( tmp );
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
2009-05-17 19:38:06 +00:00
|
|
|
if( tmp->isDock() || tmp->keepAbove() )
|
2009-04-15 19:31:20 +00:00
|
|
|
{
|
|
|
|
effects->setElevatedWindow( tmp, true );
|
|
|
|
elevatedList.append( tmp );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// If a window is minimized it could happen that the panels stay elevated without any windows sliding.
|
|
|
|
// clear all elevation settings
|
|
|
|
if( !motionManager.managingWindows() )
|
|
|
|
{
|
|
|
|
foreach( EffectWindow* tmp, elevatedList )
|
|
|
|
{
|
|
|
|
effects->setElevatedWindow( tmp, false );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
updateStackingOrder();
|
|
|
|
}
|
|
|
|
|
2009-04-20 21:19:14 +00:00
|
|
|
QRect SlideBackEffect::getSlideDestination( const QRect &windowUnderGeometry, const QRect &windowOverGeometry)
|
|
|
|
{
|
|
|
|
// Determine the shortest way:
|
|
|
|
int leftSlide = windowUnderGeometry.left() - windowOverGeometry.right() - 20;
|
|
|
|
int rightSlide = windowUnderGeometry.right() - windowOverGeometry.left() + 20;
|
|
|
|
int upSlide = windowUnderGeometry.top() - windowOverGeometry.bottom() - 20;
|
|
|
|
int downSlide = windowUnderGeometry.bottom() - windowOverGeometry.top() + 20;
|
|
|
|
|
|
|
|
int horizSlide = leftSlide;
|
2009-06-03 20:44:41 +00:00
|
|
|
if( qAbs( horizSlide ) > qAbs( rightSlide ) )
|
2009-04-20 21:19:14 +00:00
|
|
|
{
|
|
|
|
horizSlide = rightSlide;
|
|
|
|
}
|
|
|
|
int vertSlide = upSlide;
|
2009-06-03 20:44:41 +00:00
|
|
|
if( qAbs( vertSlide ) > qAbs( downSlide ) )
|
2009-04-20 21:19:14 +00:00
|
|
|
{
|
|
|
|
vertSlide = downSlide;
|
|
|
|
}
|
|
|
|
|
|
|
|
QRect slideRect = windowOverGeometry;
|
2009-06-03 20:44:41 +00:00
|
|
|
if( qAbs( horizSlide ) < qAbs( vertSlide ) )
|
2009-04-20 21:19:14 +00:00
|
|
|
{
|
|
|
|
slideRect.moveLeft( slideRect.x() + horizSlide );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
slideRect.moveTop( slideRect.y() + vertSlide );
|
|
|
|
}
|
|
|
|
return slideRect;
|
|
|
|
}
|
|
|
|
|
2009-04-15 19:31:20 +00:00
|
|
|
void SlideBackEffect::updateStackingOrder()
|
|
|
|
{
|
|
|
|
usableOldStackingOrder = usableWindows( effects->stackingOrder() );
|
|
|
|
oldStackingOrder = effects->stackingOrder();
|
|
|
|
}
|
|
|
|
|
|
|
|
void SlideBackEffect::prePaintScreen( ScreenPrePaintData &data, int time )
|
|
|
|
{
|
|
|
|
if( motionManager.managingWindows() )
|
|
|
|
{
|
|
|
|
motionManager.calculate( time );
|
|
|
|
data.mask |= Effect::PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS;
|
|
|
|
}
|
|
|
|
effects->prePaintScreen( data, time );
|
|
|
|
}
|
|
|
|
|
|
|
|
void SlideBackEffect::postPaintScreen()
|
|
|
|
{
|
|
|
|
if( motionManager.areWindowsMoving() )
|
|
|
|
{
|
|
|
|
effects->addRepaintFull();
|
|
|
|
}
|
|
|
|
effects->postPaintScreen();
|
|
|
|
}
|
|
|
|
|
|
|
|
void SlideBackEffect::prePaintWindow( EffectWindow *w, WindowPrePaintData &data, int time )
|
2009-05-19 13:52:50 +00:00
|
|
|
{
|
|
|
|
if( motionManager.isManaging( w ) )
|
|
|
|
{
|
|
|
|
data.setTransformed();
|
|
|
|
}
|
|
|
|
|
|
|
|
effects->prePaintWindow( w, data, time );
|
|
|
|
}
|
|
|
|
|
|
|
|
void SlideBackEffect::paintWindow( EffectWindow *w, int mask, QRegion region, WindowPaintData &data )
|
2009-04-15 19:31:20 +00:00
|
|
|
{
|
2009-05-24 14:26:00 +00:00
|
|
|
if( stackingOrderChanged() && ( w == newTopWindow() ) && !disabled )
|
2009-04-15 19:31:20 +00:00
|
|
|
{
|
|
|
|
/* This can happen because of two reasons:
|
|
|
|
- a window has received the focus earlier without beeing raised and is raised now. -> call windowActivated() now
|
|
|
|
- paintWindow() is called with a new stackingOrder before activateWindow(). Bug? -> don't draw the overlapping content;*/
|
|
|
|
foreach( EffectWindow *tmp, oldStackingOrder )
|
|
|
|
{
|
|
|
|
if( oldStackingOrder.lastIndexOf( tmp ) > oldStackingOrder.lastIndexOf( w ) && isWindowUsable( tmp ) )
|
|
|
|
{
|
2009-05-19 13:52:50 +00:00
|
|
|
kDebug() << "screw detected. region:" << region << "clipping:" << tmp->geometry() ;
|
|
|
|
PaintClipper::push( region.subtracted( tmp->geometry() ) );
|
|
|
|
clippedRegions.prepend( region.subtracted( tmp->geometry() ) );
|
|
|
|
// region = region.subtracted( tmp->geometry() );
|
2009-04-15 19:31:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
// Finally call windowActivated in case a already active window is raised.
|
2009-05-24 14:26:00 +00:00
|
|
|
windowActivated( w );
|
2009-04-15 19:31:20 +00:00
|
|
|
}
|
2009-04-27 12:57:52 +00:00
|
|
|
if( motionManager.isManaging( w ) )
|
2009-04-15 19:31:20 +00:00
|
|
|
{
|
2009-04-27 12:57:52 +00:00
|
|
|
motionManager.apply( w, data );
|
2009-04-15 19:31:20 +00:00
|
|
|
}
|
|
|
|
effects->paintWindow( w, mask, region, data );
|
2009-05-18 10:22:51 +00:00
|
|
|
if( !clippedRegions.isEmpty() )
|
|
|
|
{
|
|
|
|
foreach( const QRegion ®ion, clippedRegions )
|
|
|
|
{
|
|
|
|
PaintClipper::pop( region );
|
|
|
|
}
|
|
|
|
clippedRegions.clear();
|
|
|
|
}
|
2009-05-19 13:52:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void SlideBackEffect::postPaintWindow( EffectWindow* w )
|
|
|
|
{
|
2009-04-15 19:31:20 +00:00
|
|
|
if( motionManager.isManaging( w ) )
|
|
|
|
{
|
|
|
|
if( destinationList.contains( w ) )
|
|
|
|
{
|
|
|
|
// has window reched its destination?
|
2009-06-03 20:44:41 +00:00
|
|
|
if(( qAbs( motionManager.transformedGeometry( w ).x() - destinationList[w].x() ) < 1 ) &&
|
|
|
|
( qAbs( motionManager.transformedGeometry( w ).y() - destinationList[w].y() ) < 1 ) )
|
2009-04-15 19:31:20 +00:00
|
|
|
{
|
2009-04-20 21:19:14 +00:00
|
|
|
// If we are still intersecting with the activeWindow it is moving. slide to somewhere else
|
|
|
|
// restore the stacking order of all windows not intersecting any more except panels
|
|
|
|
if( coveringWindows.contains( w ) )
|
|
|
|
{
|
2009-04-22 11:24:19 +00:00
|
|
|
EffectWindowList tmpList;
|
2009-04-20 21:19:14 +00:00
|
|
|
foreach( EffectWindow *tmp, elevatedList )
|
|
|
|
{
|
2009-04-27 12:57:52 +00:00
|
|
|
QRect elevatedGeometry = tmp->geometry();
|
|
|
|
if( motionManager.isManaging( tmp ) )
|
|
|
|
{
|
|
|
|
elevatedGeometry = motionManager.transformedGeometry( tmp ).toAlignedRect();
|
|
|
|
}
|
2009-05-17 19:38:06 +00:00
|
|
|
if( effects->activeWindow() && !tmp->isDock() && !tmp->keepAbove() && effects->activeWindow()->geometry().intersects( elevatedGeometry ) )
|
2009-04-20 21:19:14 +00:00
|
|
|
{
|
2009-04-22 12:49:28 +00:00
|
|
|
QRect newDestination;
|
|
|
|
if( effects->activeWindow()->isModal() )
|
|
|
|
{
|
2009-04-27 12:57:52 +00:00
|
|
|
QRect modalGroupGeometry = effects->activeWindow()->geometry();
|
2009-04-22 12:49:28 +00:00
|
|
|
foreach( EffectWindow *modalWindow, effects->activeWindow()->mainWindows() )
|
2009-04-27 12:57:52 +00:00
|
|
|
{
|
2009-04-22 12:49:28 +00:00
|
|
|
modalGroupGeometry = modalGroupGeometry.united( modalWindow->geometry() );
|
2009-04-27 12:57:52 +00:00
|
|
|
}
|
|
|
|
newDestination = getSlideDestination( modalGroupGeometry, elevatedGeometry );
|
2009-04-22 12:49:28 +00:00
|
|
|
}
|
|
|
|
else
|
2009-04-27 12:57:52 +00:00
|
|
|
{
|
|
|
|
newDestination = getSlideDestination( effects->activeWindow()->geometry(), elevatedGeometry );
|
|
|
|
}
|
2009-04-22 11:24:19 +00:00
|
|
|
if( !motionManager.isManaging( tmp ) )
|
2009-04-27 12:57:52 +00:00
|
|
|
{
|
2009-04-22 11:24:19 +00:00
|
|
|
motionManager.manage( tmp );
|
2009-04-27 12:57:52 +00:00
|
|
|
}
|
2009-04-20 21:19:14 +00:00
|
|
|
motionManager.moveWindow( tmp, newDestination );
|
|
|
|
destinationList[tmp] = newDestination;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if( !tmp->isDock() )
|
|
|
|
{
|
2009-04-22 11:24:19 +00:00
|
|
|
bool keepElevated = false;
|
|
|
|
foreach( EffectWindow *elevatedWindow, tmpList)
|
|
|
|
{
|
|
|
|
if( tmp->geometry().intersects( elevatedWindow->geometry() ) )
|
|
|
|
{
|
|
|
|
keepElevated = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if( !keepElevated )
|
|
|
|
{
|
|
|
|
effects->setElevatedWindow( tmp, false );
|
|
|
|
elevatedList.removeAll( tmp );
|
|
|
|
}
|
2009-04-20 21:19:14 +00:00
|
|
|
}
|
|
|
|
}
|
2009-04-22 11:24:19 +00:00
|
|
|
tmpList.append(tmp);
|
2009-04-20 21:19:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Move the window back where it belongs
|
|
|
|
motionManager.moveWindow( w, w->geometry() );
|
|
|
|
destinationList.remove( w );
|
|
|
|
}
|
2009-04-15 19:31:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// is window back at its original position?
|
2009-06-03 20:44:41 +00:00
|
|
|
if(( qAbs( motionManager.transformedGeometry( w ).x() - w->geometry().x() ) < 1 ) &&
|
|
|
|
( qAbs( motionManager.transformedGeometry( w ).y() - w->geometry().y() ) < 1 ) )
|
2009-04-15 19:31:20 +00:00
|
|
|
{
|
|
|
|
motionManager.unmanage( w );
|
2009-07-24 09:03:10 +00:00
|
|
|
effects->addRepaintFull();
|
2009-04-15 19:31:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if( coveringWindows.contains( w ) )
|
|
|
|
{
|
|
|
|
// It could happen that there is no aciveWindow() here if the user clicks the close-button on an inactive window.
|
|
|
|
// Just skip... the window will be removed in windowDeleted() later
|
2009-04-22 12:49:28 +00:00
|
|
|
if( effects->activeWindow() && !intersects( effects->activeWindow(), motionManager.transformedGeometry( w ).toAlignedRect() ) )
|
2009-04-15 19:31:20 +00:00
|
|
|
{
|
|
|
|
coveringWindows.removeAll( w );
|
|
|
|
if( coveringWindows.isEmpty() )
|
|
|
|
{
|
|
|
|
// Restore correct stacking order
|
|
|
|
foreach( EffectWindow *tmp, elevatedList )
|
|
|
|
{
|
|
|
|
effects->setElevatedWindow( tmp, false );
|
|
|
|
}
|
2009-04-20 21:19:14 +00:00
|
|
|
elevatedList.clear();
|
2009-04-15 19:31:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
effects->postPaintWindow( w );
|
|
|
|
}
|
|
|
|
|
|
|
|
void SlideBackEffect::windowDeleted( EffectWindow* w )
|
|
|
|
{
|
|
|
|
usableOldStackingOrder.removeAll( w );
|
|
|
|
oldStackingOrder.removeAll( w );
|
|
|
|
coveringWindows.removeAll( w );
|
|
|
|
elevatedList.removeAll( w );
|
2009-05-24 21:15:23 +00:00
|
|
|
if( motionManager.isManaging( w ) )
|
|
|
|
{
|
|
|
|
motionManager.unmanage( w );
|
|
|
|
}
|
2009-04-15 19:31:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void SlideBackEffect::windowAdded( KWin::EffectWindow* w )
|
|
|
|
{
|
2009-04-22 12:49:28 +00:00
|
|
|
Q_UNUSED( w );
|
2009-04-15 19:31:20 +00:00
|
|
|
updateStackingOrder();
|
|
|
|
}
|
|
|
|
|
2009-07-06 11:13:48 +00:00
|
|
|
void SlideBackEffect::windowUnminimized( EffectWindow* w)
|
|
|
|
{
|
|
|
|
// SlideBack should not be triggered on an unminimized window. For this we need to store the last unminimized window.
|
|
|
|
// If a window is unminimized but not on top we need to clear the memory because the windowUnminimized() is not
|
|
|
|
// followed by a windowActivated().
|
|
|
|
if( isWindowOnTop( w ) )
|
|
|
|
{
|
|
|
|
unminimizedWindow = w;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
unminimizedWindow = NULL;
|
|
|
|
}
|
|
|
|
}
|
2009-04-15 19:31:20 +00:00
|
|
|
|
|
|
|
void SlideBackEffect::tabBoxClosed()
|
|
|
|
{
|
|
|
|
disabled = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool SlideBackEffect::isWindowOnTop( EffectWindow* w )
|
|
|
|
{
|
|
|
|
return usableWindows( effects->stackingOrder() ).last() == w ? true : false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool SlideBackEffect::isWindowUsable( EffectWindow* w )
|
|
|
|
{
|
2009-05-17 19:38:06 +00:00
|
|
|
return w && ( w->isNormalWindow() || w->isDialog() ) && !w->keepAbove() && !w->isDeleted() && !w->isMinimized();
|
2009-04-15 19:31:20 +00:00
|
|
|
}
|
|
|
|
|
2009-04-22 12:49:28 +00:00
|
|
|
bool SlideBackEffect::intersects( EffectWindow* windowUnder, const QRect &windowOverGeometry )
|
|
|
|
{
|
|
|
|
QRect windowUnderGeometry = windowUnder->geometry();
|
|
|
|
if( windowUnder->isModal() )
|
|
|
|
{
|
|
|
|
foreach( EffectWindow *tmp, windowUnder->mainWindows() )
|
|
|
|
{
|
|
|
|
windowUnderGeometry = windowUnderGeometry.united( tmp->geometry() );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return windowUnderGeometry.intersects( windowOverGeometry );
|
|
|
|
}
|
|
|
|
|
2009-04-15 19:31:20 +00:00
|
|
|
EffectWindowList SlideBackEffect::usableWindows( const EffectWindowList & allWindows )
|
|
|
|
{
|
|
|
|
EffectWindowList retList;
|
|
|
|
foreach( EffectWindow *tmp, allWindows )
|
|
|
|
{
|
|
|
|
if( isWindowUsable( tmp ) )
|
|
|
|
{
|
|
|
|
retList.append( tmp );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return retList;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool SlideBackEffect::stackingOrderChanged()
|
|
|
|
{
|
|
|
|
return !( usableOldStackingOrder == usableWindows( effects->stackingOrder() ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
EffectWindow* SlideBackEffect::newTopWindow()
|
|
|
|
{
|
|
|
|
EffectWindowList stacking = usableWindows( effects->stackingOrder() );
|
|
|
|
return stacking.isEmpty() ? NULL : stacking.last();
|
|
|
|
}
|
|
|
|
|
|
|
|
} //Namespace
|