Make a branch to make krunner Good Enough For Aaron™.
[kdebase/uwolfer.git] / workspace / kwin / effects / presentwindows.cpp
blob6412f3f72b50880782743b77edab2dd66cb8e4f2
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>
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
19 *********************************************************************/
21 #include "presentwindows.h"
23 #include <kactioncollection.h>
24 #include <kaction.h>
25 #include <klocale.h>
26 #include <kcolorscheme.h>
27 #include <kconfiggroup.h>
28 #include <kdebug.h>
30 #include <QMouseEvent>
31 #include <QPainter>
33 #include <math.h>
34 #include <assert.h>
35 #include <limits.h>
37 namespace KWin
40 KWIN_EFFECT( presentwindows, PresentWindowsEffect )
42 PresentWindowsEffect::PresentWindowsEffect()
43 : mShowWindowsFromAllDesktops ( false )
44 , mActivated( false )
45 , mActiveness( 0.0 )
46 , mRearranging( 1.0 )
47 , hasKeyboardGrab( false )
48 , mHighlightedWindow( NULL )
49 #ifdef KWIN_HAVE_OPENGL_COMPOSITING
50 , filterTexture( NULL )
51 #endif
53 KConfigGroup conf = effects->effectConfig("PresentWindows");
55 KActionCollection* actionCollection = new KActionCollection( this );
56 KAction* a = (KAction*)actionCollection->addAction( "Expose" );
57 a->setText( i18n("Toggle Expose Effect" ));
58 a->setGlobalShortcut(KShortcut(Qt::CTRL + Qt::Key_F9));
59 connect(a, SIGNAL(triggered(bool)), this, SLOT(toggleActive()));
60 KAction* b = (KAction*)actionCollection->addAction( "ExposeAll" );
61 b->setText( i18n("Toggle Expose Effect (incl other desktops)" ));
62 b->setGlobalShortcut(KShortcut(Qt::CTRL + Qt::Key_F10));
63 connect(b, SIGNAL(triggered(bool)), this, SLOT(toggleActiveAllDesktops()));
65 borderActivate = (ElectricBorder)conf.readEntry("BorderActivate", (int)ElectricNone);
66 borderActivateAll = (ElectricBorder)conf.readEntry("BorderActivateAll", (int)ElectricTopLeft);
68 effects->reserveElectricBorder( borderActivate );
69 effects->reserveElectricBorder( borderActivateAll );
71 #ifdef KWIN_HAVE_XRENDER_COMPOSITING
72 alphaFormat = XRenderFindStandardFormat( display(), PictStandardARGB32 );
73 #endif
76 PresentWindowsEffect::~PresentWindowsEffect()
78 effects->unreserveElectricBorder( borderActivate );
79 effects->unreserveElectricBorder( borderActivateAll );
80 discardFilterTexture();
84 void PresentWindowsEffect::prePaintScreen( ScreenPrePaintData& data, int time )
86 // How long does it take for the effect to get it's full strength (in ms)
87 const double changeTime = 300;
88 if(mActivated)
90 mActiveness = qMin(1.0, mActiveness + time/changeTime);
91 if( mRearranging < 1 )
92 mRearranging = qMin(1.0, mRearranging + time/changeTime);
94 else if(mActiveness > 0.0)
96 mActiveness = qMax(0.0, mActiveness - time/changeTime);
97 if(mActiveness <= 0.0)
98 effectTerminated();
101 // We need to mark the screen windows as transformed. Otherwise the whole
102 // screen won't be repainted, resulting in artefacts
103 if( mActiveness > 0.0f )
104 data.mask |= Effect::PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS;
106 effects->prePaintScreen(data, time);
109 void PresentWindowsEffect::prePaintWindow( EffectWindow* w, WindowPrePaintData& data, int time )
111 if( mActiveness > 0.0f )
113 if( mWindowData.contains(w) )
115 // This window will be transformed by the effect
116 data.setTransformed();
117 w->enablePainting( EffectWindow::PAINT_DISABLED_BY_MINIMIZE );
118 w->enablePainting( EffectWindow::PAINT_DISABLED_BY_DESKTOP );
119 // If it's minimized window or on another desktop and effect is not
120 // fully active, then apply some transparency
121 if( mActiveness < 1.0f && (w->isMinimized() || !w->isOnCurrentDesktop() ))
122 data.setTranslucent();
123 // Change window's highlight
124 WindowData& windata = mWindowData[w];
125 const double highlightchangetime = 200;
126 if( w == mHighlightedWindow )
127 windata.highlight = qMin(1.0, windata.highlight + time / highlightchangetime);
128 else
129 windata.highlight = qMax(0.0, windata.highlight - time / highlightchangetime);
131 else if( !w->isDesktop())
132 w->disablePainting( EffectWindow::PAINT_DISABLED );
134 effects->prePaintWindow( w, data, time );
137 void PresentWindowsEffect::paintScreen( int mask, QRegion region, ScreenPaintData& data )
139 effects->paintScreen( mask, region, data );
140 #ifdef KWIN_HAVE_OPENGL_COMPOSITING
141 if( filterTexture && region.intersects( filterFrameRect ))
143 glPushAttrib( GL_CURRENT_BIT | GL_ENABLE_BIT );
144 glEnable( GL_BLEND );
145 glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
146 // First render the frame
147 QColor color = QPalette().color( QPalette::Active, QPalette::Highlight );
148 glColor4f( color.redF(), color.greenF(), color.blueF(), 0.75f );
149 renderRoundBoxWithEdge( filterFrameRect );
150 // And then the text on top of it
151 filterTexture->bind();
152 filterTexture->render( region, filterTextureRect );
153 filterTexture->unbind();
154 glPopAttrib();
156 #endif
159 void PresentWindowsEffect::paintWindow( EffectWindow* w, int mask, QRegion region, WindowPaintData& data )
161 if(mActiveness > 0.0f && mWindowData.contains(w))
163 // Change window's position and scale
164 const WindowData& windata = mWindowData[w];
165 if( mRearranging < 1 ) // rearranging
167 if( windata.old_area.isEmpty()) // no old position
169 data.xScale = windata.scale;
170 data.yScale = windata.scale;
171 data.xTranslate = windata.area.left() - w->x();
172 data.yTranslate = windata.area.top() - w->y();
173 data.opacity *= interpolate(0.0, 1.0, mRearranging);
175 else
177 data.xScale = interpolate(windata.old_scale, windata.scale, mRearranging);
178 data.yScale = interpolate(windata.old_scale, windata.scale, mRearranging);
179 data.xTranslate = (int)interpolate(windata.old_area.left() - w->x(),
180 windata.area.left() - w->x(), mRearranging);
181 data.yTranslate = (int)interpolate(windata.old_area.top() - w->y(),
182 windata.area.top() - w->y(), mRearranging);
185 else
187 data.xScale = interpolate(data.xScale, windata.scale, mActiveness);
188 data.yScale = interpolate(data.xScale, windata.scale, mActiveness);
189 data.xTranslate = (int)interpolate(data.xTranslate, windata.area.left() - w->x(), mActiveness);
190 data.yTranslate = (int)interpolate(data.yTranslate, windata.area.top() - w->y(), mActiveness);
192 // Darken all windows except for the one under the cursor
193 data.brightness *= interpolate(1.0, 0.7, mActiveness * (1.0f - windata.highlight));
194 // If it's minimized window or on another desktop and effect is not
195 // fully active, then apply some transparency
196 if( mActiveness < 1.0f && (w->isMinimized() || !w->isOnCurrentDesktop() ))
197 data.opacity *= interpolate(0.0, 1.0, mActiveness);
200 // Call the next effect.
201 effects->paintWindow( w, mask, region, data );
203 if(mActiveness > 0.0f && mWindowData.contains(w))
205 const WindowData& windata = mWindowData[w];
206 paintWindowIcon( w, data );
208 QString text = w->caption();
209 double centerx = w->x() + data.xTranslate + w->width() * data.xScale * 0.5f;
210 double centery = w->y() + data.yTranslate + w->height() * data.yScale * 0.5f;
211 int maxwidth = (int)(w->width() * data.xScale - 20);
212 double opacity = (0.7 + 0.2*windata.highlight) * data.opacity * mActiveness;
213 QColor textcolor( 255, 255, 255, (int)(255*opacity) );
214 QColor bgcolor( 0, 0, 0, (int)(255*opacity) );
215 QFont f;
216 f.setBold( true );
217 f.setPointSize( 12 );
218 effects->paintTextWithBackground( text, QPoint( (int)centerx, (int)centery ), maxwidth,
219 textcolor, bgcolor, f);
223 void PresentWindowsEffect::postPaintScreen()
225 if( mActivated && mActiveness < 1.0 ) // activating effect
226 effects->addRepaintFull();
227 if( mActivated && mRearranging < 1.0 ) // rearranging
228 effects->addRepaintFull();
229 if( !mActivated && mActiveness > 0.0 ) // deactivating effect
230 effects->addRepaintFull();
231 foreach( const WindowData& d, mWindowData )
233 if( d.highlight > 0 && d.highlight < 1 ) // changing highlight
234 effects->addRepaintFull();
236 // Call the next effect.
237 effects->postPaintScreen();
240 void PresentWindowsEffect::windowInputMouseEvent( Window w, QEvent* e )
242 assert( w == mInput );
243 if( e->type() == QEvent::MouseMove )
244 { // Repaint if the highlighted window changed.
245 // (No need to use cursorMoved(), this takes care of it as well)
246 for( DataHash::ConstIterator it = mWindowData.begin();
247 it != mWindowData.end();
248 ++it )
250 if( (*it).area.contains( cursorPos()))
252 if( mHighlightedWindow != it.key())
253 setHighlightedWindow( it.key());
254 return;
257 return;
259 if( e->type() != QEvent::MouseButtonPress )
260 return;
261 if( static_cast< QMouseEvent* >( e )->button() != Qt::LeftButton )
263 setActive( false );
264 return;
267 // Find out which window (if any) was clicked and activate it
268 QPoint pos = static_cast< QMouseEvent* >( e )->pos();
269 for( DataHash::iterator it = mWindowData.begin();
270 it != mWindowData.end(); ++it )
272 if( it.value().area.contains(pos) )
274 effects->activateWindow( it.key() );
275 // mWindowData gets cleared and rebuilt when a window is
276 // activated, so it's dangerous (and unnecessary) to continue
277 break;
281 // Deactivate effect, no matter if any window was actually activated
282 setActive(false);
285 void PresentWindowsEffect::windowClosed( EffectWindow* w )
287 if( mHighlightedWindow == w )
288 setHighlightedWindow( findFirstWindow());
289 mWindowsToPresent.removeAll( w );
290 rearrangeWindows();
293 void PresentWindowsEffect::setActive(bool active)
295 if( effects->activeFullScreenEffect() && effects->activeFullScreenEffect() != this )
296 return;
297 if( mActivated == active )
298 return;
299 mActivated = active;
300 if( mActivated )
302 mWindowData.clear();
303 effectActivated();
304 mActiveness = 0;
305 windowFilter.clear();
306 mWindowsToPresent.clear();
307 const EffectWindowList& originalwindowlist = effects->stackingOrder();
308 // Filter out special windows such as panels and taskbars
309 foreach( EffectWindow* window, originalwindowlist )
311 if( window->isSpecialWindow() )
312 continue;
313 if( window->isDeleted())
314 continue;
315 if( !mShowWindowsFromAllDesktops && !window->isOnCurrentDesktop() )
316 continue;
317 mWindowsToPresent.append(window);
319 rearrangeWindows();
320 setHighlightedWindow( effects->activeWindow());
322 else
324 mWindowsToPresent.clear();
325 mRearranging = 1; // turn off
326 mActiveness = 1; // go back from arranged position
327 discardFilterTexture();
328 mHighlightedWindow = NULL;
329 windowFilter.clear();
331 effects->addRepaintFull(); // trigger next animation repaint
334 void PresentWindowsEffect::effectActivated()
336 // Create temporary input window to catch mouse events
337 mInput = effects->createFullScreenInputWindow( this, Qt::PointingHandCursor );
338 hasKeyboardGrab = effects->grabKeyboard( this );
339 effects->setActiveFullScreenEffect( this );
340 setHighlightedWindow( effects->activeWindow());
343 void PresentWindowsEffect::effectTerminated()
345 mWindowData.clear();
346 // Destroy the temporary input window
347 effects->destroyInputWindow( mInput );
348 if( hasKeyboardGrab )
349 effects->ungrabKeyboard();
350 hasKeyboardGrab = false;
351 effects->setActiveFullScreenEffect( 0 );
352 effects->addRepaintFull(); // to get rid of highlight
355 void PresentWindowsEffect::rearrangeWindows()
357 if( !mActivated )
358 return;
360 EffectWindowList windowlist;
361 if( windowFilter.isEmpty())
362 windowlist = mWindowsToPresent;
363 else
365 foreach( EffectWindow* w, mWindowsToPresent )
367 if( w->caption().contains( windowFilter, Qt::CaseInsensitive )
368 || w->windowClass().contains( windowFilter, Qt::CaseInsensitive )
369 || w->windowRole().contains( windowFilter, Qt::CaseInsensitive ))
370 windowlist.append( w );
373 if( windowlist.isEmpty())
375 mWindowData.clear();
376 setHighlightedWindow( NULL );
377 effects->addRepaintFull();
378 return;
381 if( !mWindowData.isEmpty()) // this is not the first arranging
383 bool rearrange = canRearrangeClosest( windowlist ); // called before manipulating mWindowData
384 DataHash newdata;
385 EffectWindowList newlist = windowlist;
386 EffectWindowList oldlist = mWindowData.keys();
387 qSort( newlist );
388 qSort( oldlist );
389 for( DataHash::ConstIterator it = mWindowData.begin();
390 it != mWindowData.end();
391 ++it )
392 if( windowlist.contains( it.key())) // remove windows that are not in the window list
393 newdata[ it.key() ] = *it;
394 mWindowData = newdata;
395 if( !rearrange && newlist == oldlist )
396 return;
397 if( mHighlightedWindow != NULL && !mWindowData.contains( mHighlightedWindow ))
398 setHighlightedWindow( NULL );
399 for( DataHash::Iterator it = mWindowData.begin();
400 it != mWindowData.end();
401 ++it )
403 (*it).old_area = (*it).area;
404 (*it).old_scale = (*it).scale;
406 // Initialize new entries
407 foreach( EffectWindow* w, windowlist )
408 if( !mWindowData.contains( w ))
410 mWindowData[ w ].highlight = 0;
412 mRearranging = 0; // start animation again
415 // Calculate new positions and scales for windows
416 // calculateWindowTransformationsDumb( windowlist );
417 // calculateWindowTransformationsKompose( windowlist );
418 calculateWindowTransformationsClosest( windowlist );
420 if( !mWindowData.isEmpty() && mHighlightedWindow == NULL )
421 setHighlightedWindow( findFirstWindow());
423 // Schedule entire desktop to be repainted
424 effects->addRepaintFull();
427 void PresentWindowsEffect::calculateWindowTransformationsDumb(EffectWindowList windowlist)
429 // Calculate number of rows/cols
430 int rows = windowlist.count() / 4 + 1;
431 int cols = windowlist.count() / rows + windowlist.count() % rows;
432 // Get rect which we can use on current desktop. This excludes e.g. panels
433 QRect placementRect = effects->clientArea( PlacementArea, effects->activeScreen(), effects->currentDesktop());
434 // Size of one cell
435 int cellwidth = placementRect.width() / cols;
436 int cellheight = placementRect.height() / rows;
437 kDebug() << "Got " << windowlist.count() << " clients, using " << rows << "x" << cols << " grid";
439 // Calculate position and scale factor for each window
440 int i = 0;
441 foreach( EffectWindow* window, windowlist )
444 // Row/Col of this window
445 int r = i / cols;
446 int c = i % cols;
447 mWindowData[window].slot = i;
448 mWindowData[window].x = c;
449 mWindowData[window].y = r;
450 mWindowData[window].highlight = 0.0f;
451 mWindowData[window].scale = qMin(cellwidth / (double)window->width(), cellheight / (double)window->height());
452 mWindowData[window].area.setLeft(placementRect.left() + cellwidth * c);
453 mWindowData[window].area.setTop(placementRect.top() + cellheight * r);
454 mWindowData[window].area.setWidth((int)(window->width() * mWindowData[window].scale));
455 mWindowData[window].area.setHeight((int)(window->height() * mWindowData[window].scale));
457 kDebug() << "Window '" << window->caption() << "' gets moved to (" <<
458 mWindowData[window].area.left() << "; " << mWindowData[window].area.right() <<
459 "), scale: " << mWindowData[window].scale << endl;
460 i++;
464 double PresentWindowsEffect::windowAspectRatio(EffectWindow* c)
466 return c->width() / (double)c->height();
469 int PresentWindowsEffect::windowWidthForHeight(EffectWindow* c, int h)
471 return (int)((h / (double)c->height()) * c->width());
474 int PresentWindowsEffect::windowHeightForWidth(EffectWindow* c, int w)
476 return (int)((w / (double)c->width()) * c->height());
479 void PresentWindowsEffect::calculateWindowTransformationsKompose(EffectWindowList windowlist)
481 // Get rect which we can use on current desktop. This excludes e.g. panels
482 QRect availRect = effects->clientArea( PlacementArea, effects->activeScreen(), effects->currentDesktop());
484 // Following code is taken from Kompose 0.5.4, src/komposelayout.cpp
486 int spacing = 10;
487 int rows, columns;
488 double parentRatio = availRect.width() / (double)availRect.height();
489 // Use more columns than rows when parent's width > parent's height
490 if ( parentRatio > 1 )
492 columns = (int)ceil( sqrt((double)windowlist.count()) );
493 rows = (int)ceil( (double)windowlist.count() / (double)columns );
495 else
497 rows = (int)ceil( sqrt((double)windowlist.count()) );
498 columns = (int)ceil( (double)windowlist.count() / (double)rows );
500 kDebug() << "Using " << rows << " rows & " << columns << " columns for " << windowlist.count() << " clients";
502 // Calculate width & height
503 int w = (availRect.width() - (columns+1) * spacing ) / columns;
504 int h = (availRect.height() - (rows+1) * spacing ) / rows;
506 EffectWindowList::iterator it( windowlist.begin() );
507 QList<QRect> geometryRects;
508 QList<int> maxRowHeights;
509 // Process rows
510 for ( int i=0; i<rows; ++i )
512 int xOffsetFromLastCol = 0;
513 int maxHeightInRow = 0;
514 // Process columns
515 for ( int j=0; j<columns; ++j )
517 EffectWindow* window;
519 // Check for end of List
520 if ( it == windowlist.end() )
521 break;
522 window = *it;
524 // Calculate width and height of widget
525 double ratio = windowAspectRatio(window);
527 int widgetw = 100;
528 int widgeth = 100;
529 int usableW = w;
530 int usableH = h;
532 // use width of two boxes if there is no right neighbour
533 if (window == windowlist.last() && j != columns-1)
535 usableW = 2*w;
537 ++it; // We need access to the neighbour in the following
538 // expand if right neighbour has ratio < 1
539 if (j != columns-1 && it != windowlist.end() && windowAspectRatio(*it) < 1)
541 int addW = w - windowWidthForHeight(*it, h);
542 if ( addW > 0 )
544 usableW = w + addW;
548 if ( ratio == -1 )
550 widgetw = w;
551 widgeth = h;
553 else
555 double widthForHeight = windowWidthForHeight(window, usableH);
556 double heightForWidth = windowHeightForWidth(window, usableW);
557 if ( (ratio >= 1.0 && heightForWidth <= usableH) ||
558 (ratio < 1.0 && widthForHeight > usableW) )
560 widgetw = usableW;
561 widgeth = (int)heightForWidth;
563 else if ( (ratio < 1.0 && widthForHeight <= usableW) ||
564 (ratio >= 1.0 && heightForWidth > usableH) )
566 widgeth = usableH;
567 widgetw = (int)widthForHeight;
571 // Set the Widget's size
573 int alignmentXoffset = 0;
574 int alignmentYoffset = 0;
575 if ( i==0 && h > widgeth )
576 alignmentYoffset = h - widgeth;
577 if ( j==0 && w > widgetw )
578 alignmentXoffset = w - widgetw;
579 QRect geom( availRect.x() + j * (w + spacing) + spacing + alignmentXoffset + xOffsetFromLastCol,
580 availRect.y() + i * (h + spacing) + spacing + alignmentYoffset,
581 widgetw, widgeth );
582 geometryRects.append(geom);
584 // Set the x offset for the next column
585 if (alignmentXoffset==0)
586 xOffsetFromLastCol += widgetw-w;
587 if (maxHeightInRow < widgeth)
588 maxHeightInRow = widgeth;
590 maxRowHeights.append(maxHeightInRow);
593 int topOffset = 0;
594 for( int i = 0; i < rows; i++ )
596 for( int j = 0; j < columns; j++ )
598 int pos = i*columns + j;
599 if(pos >= windowlist.count())
600 break;
602 EffectWindow* window = windowlist[pos];
603 QRect geom = geometryRects[pos];
604 geom.setY( geom.y() + topOffset );
605 mWindowData[window].slot = pos;
606 mWindowData[window].x = j;
607 mWindowData[window].y = i;
608 mWindowData[window].area = geom;
609 mWindowData[window].scale = geom.width() / (double)window->width();
610 mWindowData[window].highlight = 0.0f;
612 kDebug() << "Window '" << window->caption() << "' gets moved to (" <<
613 mWindowData[window].area.left() << "; " << mWindowData[window].area.right() <<
614 "), scale: " << mWindowData[window].scale << endl;
616 if ( maxRowHeights[i]-h > 0 )
617 topOffset += maxRowHeights[i]-h;
621 void PresentWindowsEffect::calculateWindowTransformationsClosest(EffectWindowList windowlist)
623 QRect area = effects->clientArea( PlacementArea, effects->activeScreen(), effects->currentDesktop());
624 int columns = int( ceil( sqrt( (double)windowlist.count())));
625 int rows = int( ceil( windowlist.count() / double( columns )));
626 foreach( EffectWindow* w, windowlist )
627 mWindowData[ w ].slot = -1;
628 for(;;)
630 // Assign each window to the closest available slot
631 assignSlots( area, columns, rows );
632 // Leave only the closest window in each slot, remove further conflicts
633 getBestAssignments();
634 bool all_assigned = true;
635 foreach( EffectWindow* w, windowlist )
636 if( mWindowData[ w ].slot == -1 )
638 all_assigned = false;
639 break;
641 if( all_assigned )
642 break; // ok
644 int slotwidth = area.width() / columns;
645 int slotheight = area.height() / rows;
646 for( DataHash::Iterator it = mWindowData.begin();
647 it != mWindowData.end();
648 ++it )
650 QRect geom( area.x() + ((*it).slot % columns ) * slotwidth,
651 area.y() + ((*it).slot / columns ) * slotheight,
652 slotwidth, slotheight );
653 geom.adjust( 10, 10, -10, -10 ); // borders
654 double scale;
655 EffectWindow* w = it.key();
656 if( geom.width() / double( w->width()) < geom.height() / double( w->height()))
657 { // center vertically
658 scale = geom.width() / double( w->width());
659 geom.moveTop( geom.top() + ( geom.height() - int( w->height() * scale )) / 2 );
660 geom.setHeight( int( w->height() * scale ));
662 else
663 { // center horizontally
664 scale = geom.height() / double( w->height());
665 geom.moveLeft( geom.left() + ( geom.width() - int( w->width() * scale )) / 2 );
666 geom.setWidth( int( w->width() * scale ));
668 // Don't scale the windows too much
669 if( scale > 2.0 )
671 scale = 2.0;
672 QPoint center = geom.center();
673 geom = QRect( geom.center().x() - w->width(), geom.center().y() - w->height(),
674 2 * w->width(), 2 * w->height() );
676 (*it).area = geom;
677 (*it).scale = scale;
681 void PresentWindowsEffect::assignSlots( const QRect& area, int columns, int rows )
683 QVector< bool > taken;
684 taken.fill( false, columns * rows );
685 foreach( const WindowData& d, mWindowData )
687 if( d.slot != -1 )
688 taken[ d.slot ] = true;
690 int slotwidth = area.width() / columns;
691 int slotheight = area.height() / rows;
692 for( DataHash::Iterator it = mWindowData.begin();
693 it != mWindowData.end();
694 ++it )
696 if( (*it).slot != -1 )
697 continue; // it already has a slot
698 QPoint pos = it.key()->geometry().center();
699 if( pos.x() < area.left())
700 pos.setX( area.left());
701 if( pos.x() > area.right())
702 pos.setX( area.right());
703 if( pos.y() < area.top())
704 pos.setY( area.top());
705 if( pos.y() > area.bottom())
706 pos.setY( area.bottom());
707 int distance = INT_MAX;
708 for( int x = 0;
709 x < columns;
710 ++x )
711 for( int y = 0;
712 y < rows;
713 ++y )
715 int slot = x + y * columns;
716 if( taken[ slot ] )
717 continue;
718 int xdiff = pos.x() - ( area.x() + slotwidth * x + slotwidth / 2 ); // slotwidth/2 for center
719 int ydiff = pos.y() - ( area.y() + slotheight * y + slotheight / 2 );
720 int dist = int( sqrt( (double)(xdiff * xdiff + ydiff * ydiff) ));
721 if( dist < distance )
723 distance = dist;
724 (*it).slot = slot;
725 (*it).x = x;
726 (*it).y = y;
727 (*it).slot_distance = distance;
733 void PresentWindowsEffect::getBestAssignments()
735 for( DataHash::Iterator it1 = mWindowData.begin();
736 it1 != mWindowData.end();
737 ++it1 )
739 for( DataHash::ConstIterator it2 = mWindowData.begin();
740 it2 != mWindowData.end();
741 ++it2 )
743 if( it1.key() != it2.key() && (*it1).slot == (*it2).slot
744 && (*it1).slot_distance >= (*it2).slot_distance )
746 (*it1).slot = -1;
752 bool PresentWindowsEffect::canRearrangeClosest(EffectWindowList windowlist)
754 QRect area = effects->clientArea( PlacementArea, effects->activeScreen(), effects->currentDesktop());
755 int columns = int( ceil( sqrt( (double)windowlist.count())));
756 int rows = int( ceil( windowlist.count() / double( columns )));
757 int old_columns = int( ceil( sqrt( (double)mWindowData.count())));
758 int old_rows = int( ceil( mWindowData.count() / double( columns )));
759 return old_columns != columns || old_rows != rows;
762 bool PresentWindowsEffect::borderActivated( ElectricBorder border )
764 if( effects->activeFullScreenEffect() && effects->activeFullScreenEffect() != this )
765 return false;
766 if( border == borderActivate && !mActivated )
768 toggleActive();
769 return true;
771 if( border == borderActivateAll && !mActivated )
773 toggleActiveAllDesktops();
774 return true;
776 return false;
779 void PresentWindowsEffect::grabbedKeyboardEvent( QKeyEvent* e )
781 if( e->type() == QEvent::KeyPress )
783 switch( e->key())
784 { // wrap only on autorepeat
785 case Qt::Key_Left:
786 setHighlightedWindow( relativeWindow( mHighlightedWindow, -1, 0, !e->isAutoRepeat()));
787 break;
788 case Qt::Key_Right:
789 setHighlightedWindow( relativeWindow( mHighlightedWindow, 1, 0, !e->isAutoRepeat()));
790 break;
791 case Qt::Key_Up:
792 setHighlightedWindow( relativeWindow( mHighlightedWindow, 0, -1, !e->isAutoRepeat()));
793 break;
794 case Qt::Key_Down:
795 setHighlightedWindow( relativeWindow( mHighlightedWindow, 0, 1, !e->isAutoRepeat()));
796 break;
797 case Qt::Key_Home:
798 setHighlightedWindow( relativeWindow( mHighlightedWindow, -1000, 0, false ));
799 break;
800 case Qt::Key_End:
801 setHighlightedWindow( relativeWindow( mHighlightedWindow, 1000, 0, false ));
802 break;
803 case Qt::Key_PageUp:
804 setHighlightedWindow( relativeWindow( mHighlightedWindow, 0, -1000, false ));
805 break;
806 case Qt::Key_PageDown:
807 setHighlightedWindow( relativeWindow( mHighlightedWindow, 0, 1000, false ));
808 break;
809 case Qt::Key_Backspace:
810 if( !windowFilter.isEmpty())
812 windowFilter.remove( windowFilter.length() - 1, 1 );
813 updateFilterTexture();
814 rearrangeWindows();
816 return;
817 case Qt::Key_Escape:
818 setActive( false );
819 return;
820 case Qt::Key_Return:
821 case Qt::Key_Enter:
822 if( mHighlightedWindow != NULL )
824 effects->activateWindow( mHighlightedWindow );
825 setActive( false );
826 return;
828 if( mWindowData.count() == 1 ) // only one window shown
830 effects->activateWindow( mWindowData.begin().key());
831 setActive( false );
833 return;
834 default:
835 if( !e->text().isEmpty())
837 windowFilter.append( e->text());
838 updateFilterTexture();
839 rearrangeWindows();
840 return;
842 break;
847 void PresentWindowsEffect::setHighlightedWindow( EffectWindow* w )
849 if( w == mHighlightedWindow || ( w != NULL && !mWindowData.contains( w )))
850 return;
851 effects->addRepaintFull(); // everything is transformed anyway
852 mHighlightedWindow = w;
855 // returns a window which is to relative position <xdiff,ydiff> from the given window
856 EffectWindow* PresentWindowsEffect::relativeWindow( EffectWindow* w, int xdiff, int ydiff, bool wrap ) const
858 if( mWindowData.count() == 0 )
859 return NULL;
860 if( w == NULL )
861 return findFirstWindow();
862 int columns = int( ceil( sqrt( (double)mWindowData.count())));
863 int rows = int( ceil( mWindowData.count() / double( columns )));
864 QVector< QVector< EffectWindow* > > grid;
865 grid.resize( columns );
866 for( int i = 0;
867 i < columns;
868 ++i )
869 grid[ i ].resize( rows );
870 for( DataHash::ConstIterator it = mWindowData.begin();
871 it != mWindowData.end();
872 ++it )
873 grid[ it->x ][ it->y ] = it.key();
874 int x = mWindowData[ w ].x;
875 int y = mWindowData[ w ].y;
876 while( xdiff > 0 )
878 ++x;
879 if( x == columns )
881 if( !wrap )
882 { // make sure to find the leftmost (or 'w', which is guaranteed)
883 --x;
884 while( x >= 0 && grid[ x ][ y ] == NULL )
885 --x;
886 break;
888 else
889 x = 0;
891 if( grid[ x ][ y ] != NULL )
892 --xdiff;
894 while( xdiff < 0 )
896 --x;
897 if( x < 0 )
899 if( !wrap )
901 ++x;
902 while( x <= columns - 1 && grid[ x ][ y ] == NULL )
903 ++x;
904 break;
906 else
907 x = columns - 1;
909 if( grid[ x ][ y ] != NULL )
910 ++xdiff;
912 while( ydiff > 0 )
914 ++y;
915 if( y == rows )
917 if( !wrap )
919 --y;
920 while( y >= 0 && grid[ x ][ y ] == NULL )
921 --y;
922 break;
924 else
925 y = 0;
927 if( grid[ x ][ y ] != NULL )
928 --ydiff;
930 while( ydiff < 0 )
932 --y;
933 if( y < 0 )
935 if( !wrap )
937 ++y;
938 while( y <= rows - 1 && grid[ x ][ y ] == NULL )
939 ++y;
940 break;
942 else
943 y = rows - 1;
945 if( grid[ x ][ y ] != NULL )
946 ++ydiff;
948 return grid[ x ][ y ];
951 // returns the window that is the most to the topleft, if any
952 EffectWindow* PresentWindowsEffect::findFirstWindow() const
954 int minslot = INT_MAX;
955 EffectWindow* ret = NULL;
956 for( DataHash::ConstIterator it = mWindowData.begin();
957 it != mWindowData.end();
958 ++it )
960 if( (*it).slot < minslot )
962 minslot = (*it).slot;
963 ret = it.key();
966 return ret;
969 void PresentWindowsEffect::discardFilterTexture()
971 #ifdef KWIN_HAVE_OPENGL_COMPOSITING
972 delete filterTexture;
973 filterTexture = NULL;
974 #endif
977 void PresentWindowsEffect::updateFilterTexture()
979 #ifdef KWIN_HAVE_OPENGL_COMPOSITING
980 discardFilterTexture();
981 if( windowFilter.isEmpty())
983 effects->addRepaint( filterTextureRect );
984 return;
986 // Create font for filter text
987 QFont font;
988 font.setPointSize( font.pointSize() * 2 );
989 font.setBold( true );
990 // Get size of the rect containing filter text
991 QFontMetrics fm( font );
992 QRect rect;
993 QString translatedString = i18n( "Filter:\n%1", windowFilter );
994 rect.setSize( fm.size( 0, translatedString ));
995 QRect area = effects->clientArea( PlacementArea, effects->activeScreen(), effects->currentDesktop());
996 // Create image
997 QImage im( rect.width(), rect.height(), QImage::Format_ARGB32 );
998 im.fill( Qt::transparent );
999 // Paint the filter text to it
1000 QPainter p( &im );
1001 p.setFont( font );
1002 p.setPen( QPalette().color( QPalette::Active, QPalette::HighlightedText ) );
1003 p.drawText( rect, Qt::AlignCenter, translatedString );
1004 p.end();
1005 // Create GL texture
1006 filterTexture = new GLTexture( im );
1007 // Get position for filter text and it's frame
1008 filterTextureRect = QRect( area.x() + ( area.width() - rect.width()) / 2,
1009 area.y() + ( area.height() - rect.height()) / 2, rect.width(), rect.height());
1010 const int borderh = 10;
1011 const int borderw = 20;
1012 filterFrameRect = filterTextureRect.adjusted( -borderw, -borderh, borderw, borderh );
1013 // Schedule repaint
1014 effects->addRepaint( filterTextureRect );
1015 #endif
1018 void PresentWindowsEffect::paintWindowIcon( EffectWindow* w, WindowPaintData& paintdata )
1020 // Don't render null icons
1021 if( w->icon().isNull() )
1023 return;
1026 WindowData& data = mWindowData[ w ];
1027 // TODO: find out why this doesn't work properly
1028 // if( data.icon.cacheKey() != w->icon().cacheKey())
1029 { // make sure data.icon is the right QPixmap, and rebind
1030 data.icon = w->icon();
1031 #ifdef KWIN_HAVE_OPENGL_COMPOSITING
1032 if( effects->compositingType() == OpenGLCompositing )
1034 data.iconTexture.load( data.icon );
1035 data.iconTexture.setFilter( GL_LINEAR );
1037 #endif
1038 #ifdef KWIN_HAVE_XRENDER_COMPOSITING
1039 if( effects->compositingType() == XRenderCompositing )
1041 if( data.iconPicture != None )
1042 XRenderFreePicture( display(), data.iconPicture );
1043 data.iconPicture = XRenderCreatePicture( display(),
1044 data.icon.handle(), alphaFormat, 0, NULL );
1046 #endif
1048 int icon_margin = 8;
1049 int width = data.icon.width();
1050 int height = data.icon.height();
1051 int x = w->x() + paintdata.xTranslate + w->width() * paintdata.xScale * 0.95 - width - icon_margin;
1052 int y = w->y() + paintdata.yTranslate + w->height() * paintdata.yScale * 0.95 - height - icon_margin;
1053 #ifdef KWIN_HAVE_OPENGL_COMPOSITING
1054 if( effects->compositingType() == OpenGLCompositing )
1056 glPushAttrib( GL_CURRENT_BIT | GL_ENABLE_BIT | GL_TEXTURE_BIT );
1057 glEnable( GL_BLEND );
1058 glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
1059 // Render some background
1060 glColor4f( 0, 0, 0, 0.5 * mActiveness );
1061 renderRoundBox( QRect( x-3, y-3, width+6, height+6 ), 3 );
1062 // Render the icon
1063 glColor4f( 1, 1, 1, 1 * mActiveness );
1064 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
1065 data.iconTexture.bind();
1066 const float verts[ 4 * 2 ] =
1068 x, y,
1069 x, y + height,
1070 x + width, y + height,
1071 x + width, y
1073 const float texcoords[ 4 * 2 ] =
1075 0, 1,
1076 0, 0,
1077 1, 0,
1078 1, 1
1080 renderGLGeometry( 4, verts, texcoords );
1081 data.iconTexture.unbind();
1082 glPopAttrib();
1084 #endif
1085 #ifdef KWIN_HAVE_XRENDER_COMPOSITING
1086 if( effects->compositingType() == XRenderCompositing )
1088 XRenderComposite( display(),
1089 data.icon.depth() == 32 ? PictOpOver : PictOpSrc,
1090 data.iconPicture, None,
1091 effects->xrenderBufferPicture(),
1092 0, 0, 0, 0, x, y, width, height );
1094 #endif
1097 } // namespace
1098 #include "presentwindows.moc"