789 lines
27 KiB
C++
789 lines
27 KiB
C++
|
/*****************************************************************
|
||
|
KWin - the KDE window manager
|
||
|
This file is part of the KDE project.
|
||
|
|
||
|
Copyright (C) 2007 Rivo Laks <rivolaks@hot.ee>
|
||
|
|
||
|
You can Freely distribute this program under the GNU General Public
|
||
|
License. See the file "COPYING" for the exact licensing terms.
|
||
|
******************************************************************/
|
||
|
|
||
|
|
||
|
#include "presentwindows.h"
|
||
|
|
||
|
#include <kactioncollection.h>
|
||
|
#include <kaction.h>
|
||
|
#include <klocale.h>
|
||
|
|
||
|
#include <QMouseEvent>
|
||
|
#include <qpainter.h>
|
||
|
|
||
|
#include <math.h>
|
||
|
#include <assert.h>
|
||
|
#include <limits.h>
|
||
|
|
||
|
namespace KWin
|
||
|
{
|
||
|
|
||
|
KWIN_EFFECT( PresentWindows, PresentWindowsEffect )
|
||
|
|
||
|
PresentWindowsEffect::PresentWindowsEffect()
|
||
|
: mShowWindowsFromAllDesktops ( false )
|
||
|
, mActivated( false )
|
||
|
, mActiveness( 0.0 )
|
||
|
, mRearranging( 1.0 )
|
||
|
, hasKeyboardGrab( false )
|
||
|
, mHoverWindow( NULL )
|
||
|
#ifdef HAVE_OPENGL
|
||
|
, filterTexture( NULL )
|
||
|
#endif
|
||
|
{
|
||
|
|
||
|
KActionCollection* actionCollection = new KActionCollection( this );
|
||
|
KAction* a = (KAction*)actionCollection->addAction( "Expose" );
|
||
|
a->setText( i18n("Toggle Expose effect" ));
|
||
|
a->setGlobalShortcut(KShortcut(Qt::CTRL + Qt::Key_F10));
|
||
|
connect(a, SIGNAL(triggered(bool)), this, SLOT(toggleActive()));
|
||
|
KAction* b = (KAction*)actionCollection->addAction( "ExposeAll" );
|
||
|
b->setText( i18n("Toggle Expose effect (incl other desktops)" ));
|
||
|
b->setGlobalShortcut(KShortcut(Qt::CTRL + Qt::Key_F11));
|
||
|
connect(b, SIGNAL(triggered(bool)), this, SLOT(toggleActiveAllDesktops()));
|
||
|
|
||
|
borderActivate = ElectricTopRight; // TODO config options
|
||
|
borderActivateAll = ElectricNone;
|
||
|
|
||
|
effects->reserveElectricBorder( borderActivate );
|
||
|
effects->reserveElectricBorder( borderActivateAll );
|
||
|
}
|
||
|
|
||
|
PresentWindowsEffect::~PresentWindowsEffect()
|
||
|
{
|
||
|
effects->unreserveElectricBorder( borderActivate );
|
||
|
effects->unreserveElectricBorder( borderActivateAll );
|
||
|
discardFilterTexture();
|
||
|
}
|
||
|
|
||
|
|
||
|
void PresentWindowsEffect::prePaintScreen( int* mask, QRegion* region, int time )
|
||
|
{
|
||
|
// How long does it take for the effect to get it's full strength (in ms)
|
||
|
const float changeTime = 300;
|
||
|
if(mActivated)
|
||
|
{
|
||
|
mActiveness = qMin(1.0f, mActiveness + time/changeTime);
|
||
|
if( mRearranging < 1 )
|
||
|
mRearranging = qMin(1.0f, mRearranging + time/changeTime);
|
||
|
}
|
||
|
else if(mActiveness > 0.0f)
|
||
|
{
|
||
|
mActiveness = qMax(0.0f, mActiveness - time/changeTime);
|
||
|
if(mActiveness <= 0.0f)
|
||
|
effectTerminated();
|
||
|
}
|
||
|
|
||
|
// We need to mark the screen windows as transformed. Otherwise the whole
|
||
|
// screen won't be repainted, resulting in artefacts
|
||
|
if( mActiveness > 0.0f )
|
||
|
*mask |= Effect::PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS;
|
||
|
|
||
|
effects->prePaintScreen(mask, region, time);
|
||
|
}
|
||
|
|
||
|
void PresentWindowsEffect::prePaintWindow( EffectWindow* w, int* mask, QRegion* paint, QRegion* clip, int time )
|
||
|
{
|
||
|
if( mActiveness > 0.0f )
|
||
|
{
|
||
|
if( mWindowData.contains(w) )
|
||
|
{
|
||
|
// This window will be transformed by the effect
|
||
|
*mask |= Effect::PAINT_WINDOW_TRANSFORMED;
|
||
|
w->enablePainting( EffectWindow::PAINT_DISABLED_BY_MINIMIZE );
|
||
|
w->enablePainting( EffectWindow::PAINT_DISABLED_BY_DESKTOP );
|
||
|
// If it's minimized window or on another desktop and effect is not
|
||
|
// fully active, then apply some transparency
|
||
|
if( mActiveness < 1.0f && (w->isMinimized() || !w->isOnCurrentDesktop() ))
|
||
|
*mask |= Effect::PAINT_WINDOW_TRANSLUCENT;
|
||
|
// Change window's hover according to cursor pos
|
||
|
WindowData& windata = mWindowData[w];
|
||
|
const float hoverchangetime = 200;
|
||
|
if( windata.area.contains(cursorPos()) )
|
||
|
windata.hover = qMin(1.0f, windata.hover + time / hoverchangetime);
|
||
|
else
|
||
|
windata.hover = qMax(0.0f, windata.hover - time / hoverchangetime);
|
||
|
}
|
||
|
else if( !w->isDesktop())
|
||
|
w->disablePainting( EffectWindow::PAINT_DISABLED );
|
||
|
}
|
||
|
effects->prePaintWindow( w, mask, paint, clip, time );
|
||
|
}
|
||
|
|
||
|
void PresentWindowsEffect::paintScreen( int mask, QRegion region, ScreenPaintData& data )
|
||
|
{
|
||
|
effects->paintScreen( mask, region, data );
|
||
|
#ifdef HAVE_OPENGL
|
||
|
if( filterTexture && region.intersects( filterTextureRect ))
|
||
|
{
|
||
|
glPushAttrib( GL_CURRENT_BIT | GL_ENABLE_BIT );
|
||
|
filterTexture->bind();
|
||
|
glEnable( GL_BLEND );
|
||
|
glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
|
||
|
filterTexture->render( mask, region, filterTextureRect );
|
||
|
filterTexture->unbind();
|
||
|
glPopAttrib();
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
void PresentWindowsEffect::paintWindow( EffectWindow* w, int mask, QRegion region, WindowPaintData& data )
|
||
|
{
|
||
|
if(mActiveness > 0.0f && mWindowData.contains(w))
|
||
|
{
|
||
|
// Change window's position and scale
|
||
|
const WindowData& windata = mWindowData[w];
|
||
|
if( mRearranging < 1 ) // rearranging
|
||
|
{
|
||
|
if( windata.old_area.isEmpty()) // no old position
|
||
|
{
|
||
|
data.xScale = windata.scale;
|
||
|
data.yScale = windata.scale;
|
||
|
data.xTranslate = windata.area.left() - w->x();
|
||
|
data.yTranslate = windata.area.top() - w->y();
|
||
|
data.opacity *= interpolate(0.0, 1.0, mRearranging);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
data.xScale = interpolate(windata.old_scale, windata.scale, mRearranging);
|
||
|
data.yScale = interpolate(windata.old_scale, windata.scale, mRearranging);
|
||
|
data.xTranslate = (int)interpolate(windata.old_area.left() - w->x(),
|
||
|
windata.area.left() - w->x(), mRearranging);
|
||
|
data.yTranslate = (int)interpolate(windata.old_area.top() - w->y(),
|
||
|
windata.area.top() - w->y(), mRearranging);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
data.xScale = interpolate(data.xScale, windata.scale, mActiveness);
|
||
|
data.yScale = interpolate(data.xScale, windata.scale, mActiveness);
|
||
|
data.xTranslate = (int)interpolate(data.xTranslate, windata.area.left() - w->x(), mActiveness);
|
||
|
data.yTranslate = (int)interpolate(data.yTranslate, windata.area.top() - w->y(), mActiveness);
|
||
|
}
|
||
|
// Darken all windows except for the one under the cursor
|
||
|
data.brightness *= interpolate(1.0, 0.7, mActiveness * (1.0f - windata.hover));
|
||
|
// If it's minimized window or on another desktop and effect is not
|
||
|
// fully active, then apply some transparency
|
||
|
if( mActiveness < 1.0f && (w->isMinimized() || !w->isOnCurrentDesktop() ))
|
||
|
data.opacity *= interpolate(0.0, 1.0, mActiveness);
|
||
|
}
|
||
|
|
||
|
// Call the next effect.
|
||
|
effects->paintWindow( w, mask, region, data );
|
||
|
}
|
||
|
|
||
|
void PresentWindowsEffect::postPaintScreen()
|
||
|
{
|
||
|
if( mActivated && mActiveness < 1.0 ) // activating effect
|
||
|
effects->addRepaintFull();
|
||
|
if( mActivated && mRearranging < 1.0 ) // rearranging
|
||
|
effects->addRepaintFull();
|
||
|
if( !mActivated && mActiveness > 0.0 ) // deactivating effect
|
||
|
effects->addRepaintFull();
|
||
|
foreach( const WindowData& d, mWindowData )
|
||
|
{
|
||
|
if( d.hover > 0 && d.hover < 1 ) // changing highlight
|
||
|
effects->addRepaintFull();
|
||
|
}
|
||
|
// Call the next effect.
|
||
|
effects->postPaintScreen();
|
||
|
}
|
||
|
|
||
|
void PresentWindowsEffect::windowInputMouseEvent( Window w, QEvent* e )
|
||
|
{
|
||
|
assert( w == mInput );
|
||
|
if( e->type() == QEvent::MouseMove )
|
||
|
{ // Repaint if the hovered-over window changed.
|
||
|
// (No need to use cursorMoved(), this takes care of it as well)
|
||
|
for( DataHash::ConstIterator it = mWindowData.begin();
|
||
|
it != mWindowData.end();
|
||
|
++it )
|
||
|
{
|
||
|
if( (*it).area.contains( cursorPos()))
|
||
|
{
|
||
|
if( mHoverWindow != it.key())
|
||
|
{
|
||
|
mHoverWindow = it.key();
|
||
|
effects->addRepaintFull(); // screen is transformed, so paint all
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
if( e->type() != QEvent::MouseButtonPress )
|
||
|
return;
|
||
|
if( static_cast< QMouseEvent* >( e )->button() != Qt::LeftButton )
|
||
|
{
|
||
|
setActive( false );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Find out which window (if any) was clicked and activate it
|
||
|
QPoint pos = static_cast< QMouseEvent* >( e )->pos();
|
||
|
for( DataHash::iterator it = mWindowData.begin();
|
||
|
it != mWindowData.end(); ++it )
|
||
|
{
|
||
|
if( it.value().area.contains(pos) )
|
||
|
{
|
||
|
effects->activateWindow( it.key() );
|
||
|
// mWindowData gets cleared and rebuilt when a window is
|
||
|
// activated, so it's dangerous (and unnecessary) to continue
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Deactivate effect, no matter if any window was actually activated
|
||
|
setActive(false);
|
||
|
}
|
||
|
|
||
|
void PresentWindowsEffect::windowClosed( EffectWindow* w )
|
||
|
{
|
||
|
if( mHoverWindow == w )
|
||
|
mHoverWindow = NULL;
|
||
|
mWindowsToPresent.remove( w );
|
||
|
rearrangeWindows();
|
||
|
}
|
||
|
|
||
|
void PresentWindowsEffect::setActive(bool active)
|
||
|
{
|
||
|
if( mActivated == active )
|
||
|
return;
|
||
|
mActivated = active;
|
||
|
mHoverWindow = NULL;
|
||
|
if( mActivated )
|
||
|
{
|
||
|
mWindowData.clear();
|
||
|
effectActivated();
|
||
|
mActiveness = 0;
|
||
|
windowFilter.clear();
|
||
|
mWindowsToPresent.clear();
|
||
|
const EffectWindowList& originalwindowlist = effects->stackingOrder();
|
||
|
// Filter out special windows such as panels and taskbars
|
||
|
foreach( EffectWindow* window, originalwindowlist )
|
||
|
{
|
||
|
if( window->isSpecialWindow() )
|
||
|
continue;
|
||
|
if( window->isDeleted())
|
||
|
continue;
|
||
|
if( !mShowWindowsFromAllDesktops && !window->isOnCurrentDesktop() )
|
||
|
continue;
|
||
|
mWindowsToPresent.append(window);
|
||
|
}
|
||
|
rearrangeWindows();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
mWindowsToPresent.clear();
|
||
|
mRearranging = 1; // turn off
|
||
|
mActiveness = 1; // go back from arranged position
|
||
|
discardFilterTexture();
|
||
|
}
|
||
|
effects->addRepaintFull(); // trigger next animation repaint
|
||
|
}
|
||
|
|
||
|
void PresentWindowsEffect::effectActivated()
|
||
|
{
|
||
|
// Create temporary input window to catch mouse events
|
||
|
mInput = effects->createFullScreenInputWindow( this, Qt::PointingHandCursor );
|
||
|
hasKeyboardGrab = effects->grabKeyboard( this );
|
||
|
}
|
||
|
|
||
|
void PresentWindowsEffect::effectTerminated()
|
||
|
{
|
||
|
// Destroy the temporary input window
|
||
|
effects->destroyInputWindow( mInput );
|
||
|
if( hasKeyboardGrab )
|
||
|
effects->ungrabKeyboard();
|
||
|
hasKeyboardGrab = false;
|
||
|
}
|
||
|
|
||
|
void PresentWindowsEffect::rearrangeWindows()
|
||
|
{
|
||
|
if( !mActivated )
|
||
|
return;
|
||
|
|
||
|
EffectWindowList windowlist;
|
||
|
if( windowFilter.isEmpty())
|
||
|
windowlist = mWindowsToPresent;
|
||
|
else
|
||
|
{
|
||
|
foreach( EffectWindow* w, mWindowsToPresent )
|
||
|
{
|
||
|
if( w->caption().contains( windowFilter, Qt::CaseInsensitive )
|
||
|
|| w->windowClass().contains( windowFilter, Qt::CaseInsensitive ))
|
||
|
windowlist.append( w );
|
||
|
}
|
||
|
}
|
||
|
if( windowlist.isEmpty())
|
||
|
{
|
||
|
mWindowData.clear();
|
||
|
effects->addRepaintFull();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if( !mWindowData.isEmpty()) // this is not the first arranging
|
||
|
{
|
||
|
bool rearrange = canRearrangeClosest( windowlist ); // called before manipulating mWindowData
|
||
|
DataHash newdata;
|
||
|
EffectWindowList newlist = windowlist;
|
||
|
EffectWindowList oldlist = mWindowData.keys();
|
||
|
qSort( newlist );
|
||
|
qSort( oldlist );
|
||
|
for( DataHash::ConstIterator it = mWindowData.begin();
|
||
|
it != mWindowData.end();
|
||
|
++it )
|
||
|
if( windowlist.contains( it.key())) // remove windows that are not in the window list
|
||
|
newdata[ it.key() ] = *it;
|
||
|
mWindowData = newdata;
|
||
|
if( !rearrange && newlist == oldlist )
|
||
|
return;
|
||
|
for( DataHash::Iterator it = mWindowData.begin();
|
||
|
it != mWindowData.end();
|
||
|
++it )
|
||
|
{
|
||
|
(*it).old_area = (*it).area;
|
||
|
(*it).old_scale = (*it).scale;
|
||
|
}
|
||
|
// Initialize new entries
|
||
|
foreach( EffectWindow* w, windowlist )
|
||
|
if( !mWindowData.contains( w ))
|
||
|
{
|
||
|
mWindowData[ w ].hover = 0;
|
||
|
}
|
||
|
mRearranging = 0; // start animation again
|
||
|
}
|
||
|
|
||
|
// Calculate new positions and scales for windows
|
||
|
// calculateWindowTransformationsDumb( windowlist );
|
||
|
// calculateWindowTransformationsKompose( windowlist );
|
||
|
calculateWindowTransformationsClosest( windowlist );
|
||
|
|
||
|
// Schedule entire desktop to be repainted
|
||
|
effects->addRepaintFull();
|
||
|
}
|
||
|
|
||
|
void PresentWindowsEffect::calculateWindowTransformationsDumb(EffectWindowList windowlist)
|
||
|
{
|
||
|
// Calculate number of rows/cols
|
||
|
int rows = windowlist.count() / 4 + 1;
|
||
|
int cols = windowlist.count() / rows + windowlist.count() % rows;
|
||
|
// Get rect which we can use on current desktop. This excludes e.g. panels
|
||
|
QRect placementRect = effects->clientArea( PlacementArea, QPoint( 0, 0 ), 0 );
|
||
|
// Size of one cell
|
||
|
int cellwidth = placementRect.width() / cols;
|
||
|
int cellheight = placementRect.height() / rows;
|
||
|
kDebug() << k_funcinfo << "Got " << windowlist.count() << " clients, using " << rows << "x" << cols << " grid" << endl;
|
||
|
|
||
|
// Calculate position and scale factor for each window
|
||
|
int i = 0;
|
||
|
foreach( EffectWindow* window, windowlist )
|
||
|
{
|
||
|
|
||
|
// Row/Col of this window
|
||
|
int r = i / cols;
|
||
|
int c = i % cols;
|
||
|
mWindowData[window].hover = 0.0f;
|
||
|
mWindowData[window].scale = qMin(cellwidth / (float)window->width(), cellheight / (float)window->height());
|
||
|
mWindowData[window].area.setLeft(placementRect.left() + cellwidth * c);
|
||
|
mWindowData[window].area.setTop(placementRect.top() + cellheight * r);
|
||
|
mWindowData[window].area.setWidth((int)(window->width() * mWindowData[window].scale));
|
||
|
mWindowData[window].area.setHeight((int)(window->height() * mWindowData[window].scale));
|
||
|
|
||
|
kDebug() << k_funcinfo << "Window '" << window->caption() << "' gets moved to (" <<
|
||
|
mWindowData[window].area.left() << "; " << mWindowData[window].area.right() <<
|
||
|
"), scale: " << mWindowData[window].scale << endl;
|
||
|
i++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
float PresentWindowsEffect::windowAspectRatio(EffectWindow* c)
|
||
|
{
|
||
|
return c->width() / (float)c->height();
|
||
|
}
|
||
|
|
||
|
int PresentWindowsEffect::windowWidthForHeight(EffectWindow* c, int h)
|
||
|
{
|
||
|
return (int)((h / (float)c->height()) * c->width());
|
||
|
}
|
||
|
|
||
|
int PresentWindowsEffect::windowHeightForWidth(EffectWindow* c, int w)
|
||
|
{
|
||
|
return (int)((w / (float)c->width()) * c->height());
|
||
|
}
|
||
|
|
||
|
void PresentWindowsEffect::calculateWindowTransformationsKompose(EffectWindowList windowlist)
|
||
|
{
|
||
|
// Get rect which we can use on current desktop. This excludes e.g. panels
|
||
|
QRect availRect = effects->clientArea( PlacementArea, QPoint( 0, 0 ), effects->currentDesktop());
|
||
|
|
||
|
// Following code is taken from Kompose 0.5.4, src/komposelayout.cpp
|
||
|
|
||
|
int spacing = 10;
|
||
|
int rows, columns;
|
||
|
float parentRatio = availRect.width() / (float)availRect.height();
|
||
|
// Use more columns than rows when parent's width > parent's height
|
||
|
if ( parentRatio > 1 )
|
||
|
{
|
||
|
columns = (int)ceil( sqrt(windowlist.count()) );
|
||
|
rows = (int)ceil( (double)windowlist.count() / (double)columns );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
rows = (int)ceil( sqrt(windowlist.count()) );
|
||
|
columns = (int)ceil( (double)windowlist.count() / (double)rows );
|
||
|
}
|
||
|
kDebug() << k_funcinfo << "Using " << rows << " rows & " << columns << " columns for " << windowlist.count() << " clients" << endl;
|
||
|
|
||
|
// Calculate width & height
|
||
|
int w = (availRect.width() - (columns+1) * spacing ) / columns;
|
||
|
int h = (availRect.height() - (rows+1) * spacing ) / rows;
|
||
|
|
||
|
EffectWindowList::iterator it( windowlist.begin() );
|
||
|
QList<QRect> geometryRects;
|
||
|
QList<int> maxRowHeights;
|
||
|
// Process rows
|
||
|
for ( int i=0; i<rows; ++i )
|
||
|
{
|
||
|
int xOffsetFromLastCol = 0;
|
||
|
int maxHeightInRow = 0;
|
||
|
// Process columns
|
||
|
for ( int j=0; j<columns; ++j )
|
||
|
{
|
||
|
EffectWindow* window;
|
||
|
|
||
|
// Check for end of List
|
||
|
if ( it == windowlist.end() )
|
||
|
break;
|
||
|
window = *it;
|
||
|
|
||
|
// Calculate width and height of widget
|
||
|
float ratio = windowAspectRatio(window);
|
||
|
|
||
|
int widgetw = 100;
|
||
|
int widgeth = 100;
|
||
|
int usableW = w;
|
||
|
int usableH = h;
|
||
|
|
||
|
// use width of two boxes if there is no right neighbour
|
||
|
if (window == windowlist.last() && j != columns-1)
|
||
|
{
|
||
|
usableW = 2*w;
|
||
|
}
|
||
|
++it; // We need access to the neighbour in the following
|
||
|
// expand if right neighbour has ratio < 1
|
||
|
if (j != columns-1 && it != windowlist.end() && windowAspectRatio(*it) < 1)
|
||
|
{
|
||
|
int addW = w - windowWidthForHeight(*it, h);
|
||
|
if ( addW > 0 )
|
||
|
{
|
||
|
usableW = w + addW;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( ratio == -1 )
|
||
|
{
|
||
|
widgetw = w;
|
||
|
widgeth = h;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
double widthForHeight = windowWidthForHeight(window, usableH);
|
||
|
double heightForWidth = windowHeightForWidth(window, usableW);
|
||
|
if ( (ratio >= 1.0 && heightForWidth <= usableH) ||
|
||
|
(ratio < 1.0 && widthForHeight > usableW) )
|
||
|
{
|
||
|
widgetw = usableW;
|
||
|
widgeth = (int)heightForWidth;
|
||
|
}
|
||
|
else if ( (ratio < 1.0 && widthForHeight <= usableW) ||
|
||
|
(ratio >= 1.0 && heightForWidth > usableH) )
|
||
|
{
|
||
|
widgeth = usableH;
|
||
|
widgetw = (int)widthForHeight;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Set the Widget's size
|
||
|
|
||
|
int alignmentXoffset = 0;
|
||
|
int alignmentYoffset = 0;
|
||
|
if ( i==0 && h > widgeth )
|
||
|
alignmentYoffset = h - widgeth;
|
||
|
if ( j==0 && w > widgetw )
|
||
|
alignmentXoffset = w - widgetw;
|
||
|
QRect geom( availRect.x() + j * (w + spacing) + spacing + alignmentXoffset + xOffsetFromLastCol,
|
||
|
availRect.y() + i * (h + spacing) + spacing + alignmentYoffset,
|
||
|
widgetw, widgeth );
|
||
|
geometryRects.append(geom);
|
||
|
|
||
|
// Set the x offset for the next column
|
||
|
if (alignmentXoffset==0)
|
||
|
xOffsetFromLastCol += widgetw-w;
|
||
|
if (maxHeightInRow < widgeth)
|
||
|
maxHeightInRow = widgeth;
|
||
|
}
|
||
|
maxRowHeights.append(maxHeightInRow);
|
||
|
}
|
||
|
|
||
|
int topOffset = 0;
|
||
|
for( int i = 0; i < rows; i++ )
|
||
|
{
|
||
|
for( int j = 0; j < columns; j++ )
|
||
|
{
|
||
|
int pos = i*columns + j;
|
||
|
if(pos >= windowlist.count())
|
||
|
break;
|
||
|
|
||
|
EffectWindow* window = windowlist[pos];
|
||
|
QRect geom = geometryRects[pos];
|
||
|
geom.setY( geom.y() + topOffset );
|
||
|
mWindowData[window].area = geom;
|
||
|
mWindowData[window].scale = geom.width() / (float)window->width();
|
||
|
mWindowData[window].hover = 0.0f;
|
||
|
|
||
|
kDebug() << k_funcinfo << "Window '" << window->caption() << "' gets moved to (" <<
|
||
|
mWindowData[window].area.left() << "; " << mWindowData[window].area.right() <<
|
||
|
"), scale: " << mWindowData[window].scale << endl;
|
||
|
}
|
||
|
if ( maxRowHeights[i]-h > 0 )
|
||
|
topOffset += maxRowHeights[i]-h;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void PresentWindowsEffect::calculateWindowTransformationsClosest(EffectWindowList windowlist)
|
||
|
{
|
||
|
QRect area = effects->clientArea( PlacementArea, QPoint( 0, 0 ), effects->currentDesktop());
|
||
|
int columns = int( ceil( sqrt( windowlist.count())));
|
||
|
int rows = int( ceil( windowlist.count() / double( columns )));
|
||
|
foreach( EffectWindow* w, windowlist )
|
||
|
mWindowData[ w ].slot = -1;
|
||
|
for(;;)
|
||
|
{
|
||
|
// Assign each window to the closest available slot
|
||
|
assignSlots( area, columns, rows );
|
||
|
// Leave only the closest window in each slot, remove further conflicts
|
||
|
getBestAssignments();
|
||
|
bool all_assigned = true;
|
||
|
foreach( EffectWindow* w, windowlist )
|
||
|
if( mWindowData[ w ].slot == -1 )
|
||
|
{
|
||
|
all_assigned = false;
|
||
|
break;
|
||
|
}
|
||
|
if( all_assigned )
|
||
|
break; // ok
|
||
|
}
|
||
|
int slotwidth = area.width() / columns;
|
||
|
int slotheight = area.height() / rows;
|
||
|
for( DataHash::Iterator it = mWindowData.begin();
|
||
|
it != mWindowData.end();
|
||
|
++it )
|
||
|
{
|
||
|
QRect geom( area.x() + ((*it).slot % columns ) * slotwidth,
|
||
|
area.y() + ((*it).slot / columns ) * slotheight,
|
||
|
slotwidth, slotheight );
|
||
|
geom.adjust( 10, 10, -10, -10 ); // borders
|
||
|
float scale;
|
||
|
EffectWindow* w = it.key();
|
||
|
if( geom.width() / float( w->width()) < geom.height() / float( w->height()))
|
||
|
{ // center vertically
|
||
|
scale = geom.width() / float( w->width());
|
||
|
geom.moveTop( geom.top() + ( geom.height() - int( w->height() * scale )) / 2 );
|
||
|
geom.setHeight( int( w->height() * scale ));
|
||
|
}
|
||
|
else
|
||
|
{ // center horizontally
|
||
|
scale = geom.height() / float( w->height());
|
||
|
geom.moveLeft( geom.left() + ( geom.width() - int( w->width() * scale )) / 2 );
|
||
|
geom.setWidth( int( w->width() * scale ));
|
||
|
}
|
||
|
(*it).area = geom;
|
||
|
(*it).scale = scale;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void PresentWindowsEffect::assignSlots( const QRect& area, int columns, int rows )
|
||
|
{
|
||
|
QVector< bool > taken;
|
||
|
taken.fill( false, columns * rows );
|
||
|
foreach( const WindowData& d, mWindowData )
|
||
|
{
|
||
|
if( d.slot != -1 )
|
||
|
taken[ d.slot ] = true;
|
||
|
}
|
||
|
int slotwidth = area.width() / columns;
|
||
|
int slotheight = area.height() / rows;
|
||
|
for( DataHash::Iterator it = mWindowData.begin();
|
||
|
it != mWindowData.end();
|
||
|
++it )
|
||
|
{
|
||
|
if( (*it).slot != -1 )
|
||
|
continue; // it already has a slot
|
||
|
QPoint pos = it.key()->geometry().center();
|
||
|
if( pos.x() < area.left())
|
||
|
pos.setX( area.left());
|
||
|
if( pos.x() > area.right())
|
||
|
pos.setX( area.right());
|
||
|
if( pos.y() < area.top())
|
||
|
pos.setY( area.top());
|
||
|
if( pos.y() > area.bottom())
|
||
|
pos.setY( area.bottom());
|
||
|
int distance = INT_MAX;
|
||
|
for( int x = 0;
|
||
|
x < columns;
|
||
|
++x )
|
||
|
for( int y = 0;
|
||
|
y < rows;
|
||
|
++y )
|
||
|
{
|
||
|
int slot = x + y * columns;
|
||
|
if( taken[ slot ] )
|
||
|
continue;
|
||
|
int xdiff = pos.x() - ( area.x() + slotwidth * x + slotwidth / 2 ); // slotwidth/2 for center
|
||
|
int ydiff = pos.y() - ( area.y() + slotheight * y + slotheight / 2 );
|
||
|
int dist = int( sqrt( xdiff * xdiff + ydiff * ydiff ));
|
||
|
if( dist < distance )
|
||
|
{
|
||
|
distance = dist;
|
||
|
(*it).slot = slot;
|
||
|
(*it).slot_distance = distance;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void PresentWindowsEffect::getBestAssignments()
|
||
|
{
|
||
|
for( DataHash::Iterator it1 = mWindowData.begin();
|
||
|
it1 != mWindowData.end();
|
||
|
++it1 )
|
||
|
{
|
||
|
for( DataHash::ConstIterator it2 = mWindowData.begin();
|
||
|
it2 != mWindowData.end();
|
||
|
++it2 )
|
||
|
{
|
||
|
if( it1.key() != it2.key() && (*it1).slot == (*it2).slot
|
||
|
&& (*it1).slot_distance >= (*it2).slot_distance )
|
||
|
{
|
||
|
(*it1).slot = -1;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool PresentWindowsEffect::canRearrangeClosest(EffectWindowList windowlist)
|
||
|
{
|
||
|
QRect area = effects->clientArea( PlacementArea, QPoint( 0, 0 ), effects->currentDesktop());
|
||
|
int columns = int( ceil( sqrt( windowlist.count())));
|
||
|
int rows = int( ceil( windowlist.count() / double( columns )));
|
||
|
int old_columns = int( ceil( sqrt( mWindowData.count())));
|
||
|
int old_rows = int( ceil( mWindowData.count() / double( columns )));
|
||
|
return old_columns != columns || old_rows != rows;
|
||
|
}
|
||
|
|
||
|
bool PresentWindowsEffect::borderActivated( ElectricBorder border )
|
||
|
{
|
||
|
if( border == borderActivate && !mActivated )
|
||
|
{
|
||
|
toggleActive();
|
||
|
return true;
|
||
|
}
|
||
|
if( border == borderActivateAll && !mActivated )
|
||
|
{
|
||
|
toggleActiveAllDesktops();
|
||
|
return true;
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
void PresentWindowsEffect::grabbedKeyboardEvent( QKeyEvent* e )
|
||
|
{
|
||
|
if( e->type() != QEvent::KeyPress )
|
||
|
return;
|
||
|
if( e->key() == Qt::Key_Escape )
|
||
|
{
|
||
|
setActive( false );
|
||
|
return;
|
||
|
}
|
||
|
if( e->key() == Qt::Key_Backspace )
|
||
|
{
|
||
|
if( !windowFilter.isEmpty())
|
||
|
{
|
||
|
windowFilter.remove( windowFilter.length() - 1, 1 );
|
||
|
updateFilterTexture();
|
||
|
rearrangeWindows();
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
if( e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter )
|
||
|
{
|
||
|
if( mHoverWindow != NULL )
|
||
|
{
|
||
|
effects->activateWindow( mHoverWindow );
|
||
|
setActive( false );
|
||
|
return;
|
||
|
}
|
||
|
if( mWindowData.count() == 1 ) // only one window shown
|
||
|
{
|
||
|
effects->activateWindow( mWindowData.begin().key());
|
||
|
setActive( false );
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
if( !e->text().isEmpty())
|
||
|
{
|
||
|
windowFilter.append( e->text());
|
||
|
updateFilterTexture();
|
||
|
rearrangeWindows();
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void PresentWindowsEffect::discardFilterTexture()
|
||
|
{
|
||
|
#ifdef HAVE_OPENGL
|
||
|
delete filterTexture;
|
||
|
filterTexture = NULL;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
void PresentWindowsEffect::updateFilterTexture()
|
||
|
{
|
||
|
#ifdef HAVE_OPENGL
|
||
|
discardFilterTexture();
|
||
|
if( windowFilter.isEmpty())
|
||
|
return;
|
||
|
QFont font;
|
||
|
font.setPointSize( font.pointSize() * 2 );
|
||
|
font.setBold( true );
|
||
|
QRect rect = QFontMetrics( font ).boundingRect( windowFilter );
|
||
|
const int border = 10;
|
||
|
rect.adjust( -border, -border, border, border );
|
||
|
QRect area = effects->clientArea( PlacementArea, QPoint( 0, 0 ), effects->currentDesktop());
|
||
|
QImage im( rect.width(), rect.height(), QImage::Format_ARGB32 );
|
||
|
QColor col = QPalette().highlight();
|
||
|
col.setAlpha( 128 ); // 0.5
|
||
|
im.fill( col.rgba());
|
||
|
QPainter p( &im );
|
||
|
p.setFont( font );
|
||
|
p.setPen( QPalette().highlightedText());
|
||
|
p.drawText( -rect.topLeft(), windowFilter );
|
||
|
p.end();
|
||
|
filterTexture = new GLTexture( im );
|
||
|
filterTextureRect = QRect( area.x() + ( area.width() - rect.width()) / 2,
|
||
|
area.y() + ( area.height() - rect.height()) / 2, rect.width(), rect.height());
|
||
|
effects->addRepaint( filterTextureRect );
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
} // namespace
|
||
|
#include "presentwindows.moc"
|