not quite so much needs to be delayed to the init() function
[personal-kdebase.git] / workspace / kwin / effects / presentwindows.cpp
blobc47c8511502e32948127e9e7f2b2b7b22349a793
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>
25 #include <kaction.h>
26 #include <klocale.h>
27 #include <kcolorscheme.h>
28 #include <kconfiggroup.h>
29 #include <kdebug.h>
31 #include <QMouseEvent>
32 #include <QPainter>
34 #include <math.h>
35 #include <assert.h>
36 #include <limits.h>
38 namespace KWin
41 KWIN_EFFECT( presentwindows, PresentWindowsEffect )
43 PresentWindowsEffect::PresentWindowsEffect()
44 : m_borderActivate( ElectricNone )
45 , m_borderActivateAll( ElectricNone )
46 , m_activated( false )
47 , m_allDesktops( false )
48 , m_ignoreMinimized( false )
49 , m_decalOpacity( 0.0 )
50 //, m_input()
51 , m_hasKeyboardGrab( false )
52 , m_tabBoxEnabled( false )
53 //, m_motionManager()
54 //, m_windowData()
55 , m_highlightedWindow( NULL )
56 //, m_gridSizes()
57 //, m_windowFilter()
58 #ifdef KWIN_HAVE_OPENGL_COMPOSITING
59 , m_filterTexture( NULL )
60 //, m_filterTextureRect()
61 //, m_filterFrameRect()
62 #endif
64 KActionCollection* actionCollection = new KActionCollection( this );
65 KAction* a = ( KAction* )actionCollection->addAction( "Expose" );
66 a->setText( i18n( "Toggle Present Windows (Current desktop)" ));
67 a->setGlobalShortcut( KShortcut( Qt::CTRL + Qt::Key_F9 ));
68 connect( a, SIGNAL( triggered(bool) ), this, SLOT( toggleActive() ));
69 KAction* b = ( KAction* )actionCollection->addAction( "ExposeAll" );
70 b->setText( i18n( "Toggle Present Windows (All desktops)" ));
71 b->setGlobalShortcut( KShortcut( Qt::CTRL + Qt::Key_F10 ));
72 connect( b, SIGNAL( triggered(bool) ), this, SLOT( toggleActiveAllDesktops() ));
73 reconfigure( ReconfigureAll );
76 PresentWindowsEffect::~PresentWindowsEffect()
78 effects->unreserveElectricBorder( m_borderActivate );
79 effects->unreserveElectricBorder( m_borderActivateAll );
80 discardFilterTexture();
83 void PresentWindowsEffect::reconfigure( ReconfigureFlags )
85 KConfigGroup conf = effects->effectConfig("PresentWindows");
86 effects->unreserveElectricBorder( m_borderActivate );
87 effects->unreserveElectricBorder( m_borderActivateAll );
88 m_borderActivate = ElectricBorder( conf.readEntry( "BorderActivate", int( ElectricNone )));
89 m_borderActivateAll = ElectricBorder( conf.readEntry( "BorderActivateAll", int( ElectricTopLeft )));
90 effects->reserveElectricBorder( m_borderActivate );
91 effects->reserveElectricBorder( m_borderActivateAll );
92 m_layoutMode = conf.readEntry( "LayoutMode", int( LayoutNatural ));
93 m_showCaptions = conf.readEntry( "DrawWindowCaptions", true );
94 m_showIcons = conf.readEntry( "DrawWindowIcons", true );
95 m_tabBoxAllowed = conf.readEntry( "TabBox", false );
96 m_ignoreMinimized = conf.readEntry( "IgnoreMinimized", false );
97 m_accuracy = conf.readEntry( "Accuracy", 1 ) * 20;
98 m_fillGaps = conf.readEntry( "FillGaps", true );
99 m_fadeDuration = double( animationTime( 150 ));
100 m_showPanel = conf.readEntry( "ShowPanel", false );
103 //-----------------------------------------------------------------------------
104 // Screen painting
106 void PresentWindowsEffect::prePaintScreen( ScreenPrePaintData &data, int time )
108 m_motionManager.calculate( time );
110 // We need to mark the screen as having been transformed otherwise there will be no repainting
111 if( m_activated || m_motionManager.managingWindows() )
112 data.mask |= Effect::PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS;
114 if( m_activated )
115 m_decalOpacity = qMin( 1.0, m_decalOpacity + time / m_fadeDuration );
116 else
117 m_decalOpacity = qMax( 0.0, m_decalOpacity - time / m_fadeDuration );
119 effects->prePaintScreen( data, time );
122 void PresentWindowsEffect::paintScreen( int mask, QRegion region, ScreenPaintData &data )
124 effects->paintScreen( mask, region, data );
126 // Display the filter box
127 #ifdef KWIN_HAVE_OPENGL_COMPOSITING
128 if( m_filterTexture && region.intersects( m_filterFrameRect ))
130 glPushAttrib( GL_CURRENT_BIT | GL_ENABLE_BIT );
131 glEnable( GL_BLEND );
132 glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
134 // First render the frame
135 QColor color = QPalette().color( QPalette::Active, QPalette::Highlight );
136 glColor4f( color.redF(), color.greenF(), color.blueF(), 0.75f );
137 renderRoundBoxWithEdge( m_filterFrameRect );
139 // Then the text on top of it
140 m_filterTexture->bind();
141 m_filterTexture->render( region, m_filterTextureRect );
142 m_filterTexture->unbind();
143 glPopAttrib();
145 #endif
148 void PresentWindowsEffect::postPaintScreen()
150 if( m_motionManager.areWindowsMoving() )
151 effects->addRepaintFull();
152 else if( !m_activated && m_motionManager.managingWindows() )
153 { // We have finished moving them back, stop processing
154 m_motionManager.unmanageAll();
155 m_windowData.clear();
156 effects->setActiveFullScreenEffect( NULL );
159 // Update windows that are changing brightness or opacity
160 foreach( EffectWindow *w, m_windowData.keys() )
162 if( m_windowData[w].opacity > 0.0 && m_windowData[w].opacity < 1.0 )
163 w->addRepaintFull();
164 if( m_windowData[w].highlight > 0.0 && m_windowData[w].highlight < 1.0 )
165 w->addRepaintFull();
168 effects->postPaintScreen();
171 //-----------------------------------------------------------------------------
172 // Window painting
174 void PresentWindowsEffect::prePaintWindow( EffectWindow *w, WindowPrePaintData &data, int time )
176 // TODO: We should also check to see if any windows are fading just in case fading takes longer
177 // than moving the windows when the effect is deactivated.
178 if( m_activated || m_motionManager.areWindowsMoving() )
180 w->enablePainting( EffectWindow::PAINT_DISABLED_BY_MINIMIZE ); // Display always
181 w->enablePainting( EffectWindow::PAINT_DISABLED_BY_DESKTOP );
183 // Calculate window's opacity
184 // TODO: Minimized windows or windows not on the current desktop are only 75% visible?
185 if( m_windowData[w].visible )
186 m_windowData[w].opacity = qMin(/*( w->isMinimized() || !w->isOnCurrentDesktop() ) ? 0.75 :*/ 1.0,
187 m_windowData[w].opacity + time / m_fadeDuration );
188 else
189 m_windowData[w].opacity = qMax( 0.0, m_windowData[w].opacity - time / m_fadeDuration );
190 if( m_windowData[w].opacity == 0.0 )
192 // don't disable painting for panels if show panel is set
193 if( !w->isDock() || ( w->isDock() && !m_showPanel ))
194 w->disablePainting( EffectWindow::PAINT_DISABLED );
196 else if( m_windowData[w].opacity != 1.0 )
197 data.setTranslucent();
199 // Calculate window's brightness
200 if( w == m_highlightedWindow || !m_activated )
201 m_windowData[w].highlight = qMin( 1.0, m_windowData[w].highlight + time / m_fadeDuration );
202 else
203 m_windowData[w].highlight = qMax( 0.0, m_windowData[w].highlight - time / m_fadeDuration );
205 if( m_motionManager.isManaging( w ))
206 data.setTransformed(); // We will be moving this window
208 effects->prePaintWindow( w, data, time );
211 void PresentWindowsEffect::paintWindow( EffectWindow *w, int mask, QRegion region, WindowPaintData &data )
213 if( m_activated || m_motionManager.areWindowsMoving() )
215 if( w->isDock() && m_showPanel )
217 // in case the panel should be shown just display it without any changes
218 effects->paintWindow( w, mask, region, data );
219 return;
221 // Apply opacity and brightness
222 data.opacity *= m_windowData[w].opacity;
223 data.brightness *= interpolate( 0.7, 1.0, m_windowData[w].highlight );
225 if( m_motionManager.isManaging( w ))
227 m_motionManager.apply( w, data );
229 effects->paintWindow( w, mask, region, data );
231 const WindowData &wData = m_windowData[w];
232 if( m_showIcons )
233 paintWindowIcon( w, data );
234 if( m_showCaptions )
236 QString text = w->caption();
237 QRect textArea( w->x() + data.xTranslate, w->y() + data.yTranslate,
238 w->width() * data.xScale, w->height() * data.yScale );
239 textArea.adjust( 10, 10, -10, -10 );
240 double opacity = (0.7 + 0.2 * wData.highlight) * data.opacity * m_decalOpacity;
241 QColor textcolor( 255, 255, 255, int( 255 * opacity ));
242 QColor bgcolor( 0, 0, 0, int( 255 * opacity ));
243 QFont f;
244 f.setBold( true );
245 f.setPointSize( 12 );
246 effects->paintTextWithBackground( text, textArea, textcolor, bgcolor, f );
249 else
250 effects->paintWindow( w, mask, region, data );
252 else
253 effects->paintWindow( w, mask, region, data );
256 //-----------------------------------------------------------------------------
257 // User interaction
259 void PresentWindowsEffect::windowAdded( EffectWindow *w )
261 m_windowData[w].visible = isVisibleWindow( w );
262 m_windowData[w].opacity = 0.0;
263 m_windowData[w].highlight = 0.0;
264 if( isSelectableWindow( w ))
266 m_motionManager.manage( w );
267 rearrangeWindows();
271 void PresentWindowsEffect::windowClosed( EffectWindow *w )
273 if( m_highlightedWindow == w )
274 setHighlightedWindow( findFirstWindow() );
275 m_windowData[w].visible = false; // TODO: Fix this so they do actually fade out
276 rearrangeWindows();
279 void PresentWindowsEffect::windowDeleted( EffectWindow *w )
281 m_windowData.remove( w );
282 m_motionManager.unmanage( w );
285 bool PresentWindowsEffect::borderActivated( ElectricBorder border )
287 if( effects->activeFullScreenEffect() && effects->activeFullScreenEffect() != this )
288 return false;
289 if( border == m_borderActivate && !m_activated )
291 toggleActive();
292 return true;
294 if( border == m_borderActivateAll && !m_activated )
296 toggleActiveAllDesktops();
297 return true;
299 return false;
302 void PresentWindowsEffect::windowInputMouseEvent( Window w, QEvent *e )
304 assert( w == m_input );
306 // Which window are we hovering over? Always trigger as we don't always get move events before clicking
307 // We cannot use m_motionManager.windowAtPoint() as the window might not be visible
308 EffectWindowList windows = m_motionManager.managedWindows();
309 for( int i = 0; i < windows.size(); i++ )
311 if( m_motionManager.transformedGeometry( windows.at( i )).contains( cursorPos() ) &&
312 m_windowData[windows.at( i )].visible )
314 if( windows.at( i ) && m_highlightedWindow != windows.at( i ))
315 setHighlightedWindow( windows.at( i ));
316 break;
320 if( e->type() != QEvent::MouseButtonPress )
321 return;
323 QMouseEvent* me = static_cast<QMouseEvent*>( e );
324 if( me->button() == Qt::LeftButton )
326 if( m_highlightedWindow )
327 effects->activateWindow( m_highlightedWindow );
328 setActive( false );
330 // TODO: User mouse actions. E.g. right-click stickies and middle-click brings to current desktop
333 void PresentWindowsEffect::grabbedKeyboardEvent( QKeyEvent *e )
335 if( e->type() == QEvent::KeyPress )
337 switch( e->key() )
338 { // Wrap only if not auto-repeating
339 case Qt::Key_Left:
340 setHighlightedWindow( relativeWindow( m_highlightedWindow, -1, 0, !e->isAutoRepeat() ));
341 break;
342 case Qt::Key_Right:
343 setHighlightedWindow( relativeWindow( m_highlightedWindow, 1, 0, !e->isAutoRepeat() ));
344 break;
345 case Qt::Key_Up:
346 setHighlightedWindow( relativeWindow( m_highlightedWindow, 0, -1, !e->isAutoRepeat() ));
347 break;
348 case Qt::Key_Down:
349 setHighlightedWindow( relativeWindow( m_highlightedWindow, 0, 1, !e->isAutoRepeat() ));
350 break;
351 case Qt::Key_Home:
352 setHighlightedWindow( relativeWindow( m_highlightedWindow, -1000, 0, false ));
353 break;
354 case Qt::Key_End:
355 setHighlightedWindow( relativeWindow( m_highlightedWindow, 1000, 0, false ));
356 break;
357 case Qt::Key_PageUp:
358 setHighlightedWindow( relativeWindow( m_highlightedWindow, 0, -1000, false ));
359 break;
360 case Qt::Key_PageDown:
361 setHighlightedWindow( relativeWindow( m_highlightedWindow, 0, 1000, false ));
362 break;
363 case Qt::Key_Backspace:
364 if( !m_windowFilter.isEmpty() )
366 m_windowFilter.remove( m_windowFilter.length() - 1, 1 );
367 updateFilterTexture();
368 rearrangeWindows();
370 return;
371 case Qt::Key_Escape:
372 setActive( false );
373 return;
374 case Qt::Key_Return:
375 case Qt::Key_Enter:
376 if( m_highlightedWindow )
377 effects->activateWindow( m_highlightedWindow );
378 setActive( false );
379 return;
380 case Qt::Key_Tab:
381 return; // Nothing at the moment
382 case Qt::Key_Delete:
383 if( !m_windowFilter.isEmpty() )
385 m_windowFilter.clear();
386 updateFilterTexture();
387 rearrangeWindows();
389 break;
390 case 0:
391 return; // HACK: Workaround for Qt bug on unbound keys (#178547)
392 default:
393 if( !e->text().isEmpty() )
395 m_windowFilter.append( e->text() );
396 updateFilterTexture();
397 rearrangeWindows();
398 return;
400 break;
405 //-----------------------------------------------------------------------------
406 // Tab box
408 void PresentWindowsEffect::tabBoxAdded( int mode )
410 if( effects->activeFullScreenEffect() && effects->activeFullScreenEffect() != this )
411 return;
412 if( m_activated )
413 return;
414 if( !m_tabBoxAllowed )
415 return;
416 if( mode == TabBoxWindowsMode && effects->currentTabBoxWindowList().count() > 0 )
418 m_tabBoxEnabled = true;
419 setActive( true );
420 if( m_activated )
421 effects->refTabBox();
425 void PresentWindowsEffect::tabBoxClosed()
427 if( m_activated )
429 effects->unrefTabBox();
430 setActive( false, true );
431 m_tabBoxEnabled = false;
435 void PresentWindowsEffect::tabBoxUpdated()
437 if( m_activated )
438 setHighlightedWindow( effects->currentTabBoxWindow() );
441 //-----------------------------------------------------------------------------
442 // Window rearranging
444 void PresentWindowsEffect::rearrangeWindows()
446 if( !m_activated )
447 return;
449 effects->addRepaintFull(); // Trigger the first repaint
451 // Work out which windows are on which screens
452 EffectWindowList windowlist;
453 QList<EffectWindowList> windowlists;
454 for( int i = 0; i < effects->numScreens(); i++ )
455 windowlists.append( EffectWindowList() );
457 if( m_windowFilter.isEmpty() )
459 if( m_tabBoxEnabled )
460 // Assume we correctly set things up, should be identical to
461 // m_motionManager except just in a slightly different order.
462 windowlist = effects->currentTabBoxWindowList();
463 else
464 windowlist = m_motionManager.managedWindows();
465 foreach( EffectWindow *w, m_motionManager.managedWindows() )
467 windowlists[w->screen()].append( w );
468 m_windowData[w].visible = true;
471 else
472 { // Can we move this filtering somewhere else?
473 foreach( EffectWindow *w, m_motionManager.managedWindows() )
475 if( w->caption().contains( m_windowFilter, Qt::CaseInsensitive ) ||
476 w->windowClass().contains( m_windowFilter, Qt::CaseInsensitive ) ||
477 w->windowRole().contains( m_windowFilter, Qt::CaseInsensitive ))
479 windowlist.append( w );
480 windowlists[w->screen()].append( w );
481 m_windowData[w].visible = true;
483 else
484 m_windowData[w].visible = false;
487 if( windowlist.isEmpty() )
489 setHighlightedWindow( NULL ); // TODO: Having a NULL highlighted window isn't really safe
490 return;
493 // We filtered out the highlighted window
494 if( m_highlightedWindow )
496 if( m_windowData[m_highlightedWindow].visible == false )
497 setHighlightedWindow( findFirstWindow() );
499 else
500 setHighlightedWindow( findFirstWindow() );
502 int screens = m_tabBoxEnabled ? 1 : effects->numScreens();
503 for( int screen = 0; screen < screens; screen++ )
505 EffectWindowList windows;
506 if( m_tabBoxEnabled )
507 { // Kind of cheating here
508 screen = effects->activeScreen();
509 windows = windowlist;
511 else
512 windows = windowlists[screen];
514 // Don't rearrange if the grid is the same size as what it was before to prevent
515 // windows moving to a better spot if one was filtered out.
516 if( m_layoutMode == LayoutRegularGrid &&
517 m_gridSizes[screen].columns * m_gridSizes[screen].rows &&
518 windows.size() < m_gridSizes[screen].columns * m_gridSizes[screen].rows &&
519 windows.size() > ( m_gridSizes[screen].columns - 1) * m_gridSizes[screen].rows &&
520 windows.size() > m_gridSizes[screen].columns * ( m_gridSizes[screen].rows - 1 ))
521 continue;
523 // No point continuing if there is no windows to process
524 if( !windows.count() )
525 continue;
527 if( m_layoutMode == LayoutRegularGrid || m_tabBoxEnabled ) // Force the grid for window switching
528 calculateWindowTransformationsClosest( windows, screen );
529 else if( m_layoutMode == LayoutFlexibleGrid )
530 calculateWindowTransformationsKompose( windows, screen );
531 else
532 calculateWindowTransformationsNatural( windows, screen );
536 void PresentWindowsEffect::calculateWindowTransformationsClosest( EffectWindowList windowlist, int screen )
538 QRect area = effects->clientArea( ScreenArea, screen, effects->currentDesktop() );
539 if( m_showPanel ) // reserve space for the panel
540 area = effects->clientArea( MaximizeArea, screen, effects->currentDesktop() );
541 int columns = int( ceil( sqrt( double( windowlist.count() ))));
542 int rows = int( ceil( windowlist.count() / double( columns )));
544 // Remember the size for later
545 m_gridSizes[screen].columns = columns;
546 m_gridSizes[screen].rows = rows;
548 // Assign slots
549 foreach( EffectWindow *w, windowlist )
550 m_windowData[w].slot = -1;
551 if( m_tabBoxEnabled )
552 { // Rearrange in the correct order. As rearrangeWindows() is only ever
553 // called once so we can use effects->currentTabBoxWindow() here.
554 int selectedWindow = qMax( 0, windowlist.indexOf( effects->currentTabBoxWindow() ));
555 int slot = 0;
556 for( int i = selectedWindow; i < windowlist.count(); i++ )
557 m_windowData[windowlist[i]].slot = slot++;
558 for( int i = selectedWindow - 1; i >= 0; i-- )
559 m_windowData[windowlist[i]].slot = slot++;
561 else
563 for(;;)
565 // Assign each window to the closest available slot
566 assignSlots( windowlist, area, columns, rows );
567 // Leave only the closest window in each slot, remove further conflicts
568 getBestAssignments( windowlist );
569 bool allAssigned = true;
570 foreach( EffectWindow *w, windowlist )
571 if( m_windowData[w].slot == -1 )
573 allAssigned = false;
574 break;
576 if( allAssigned )
577 break;
581 int slotWidth = area.width() / columns;
582 int slotHeight = area.height() / rows;
583 foreach( EffectWindow *w, windowlist )
585 assert( m_windowData[w].slot != -1 );
587 // Work out where the slot is
588 QRect target(
589 area.x() + ( m_windowData[w].slot % columns ) * slotWidth,
590 area.y() + ( m_windowData[w].slot / columns ) * slotHeight,
591 slotWidth, slotHeight );
592 target.adjust( 10, 10, -10, -10 ); // Borders
593 double scale;
594 if( target.width() / double( w->width() ) < target.height() / double( w->height() ))
595 { // Center vertically
596 scale = target.width() / double( w->width() );
597 target.moveTop( target.top() + ( target.height() - int( w->height() * scale )) / 2 );
598 target.setHeight( int( w->height() * scale ));
600 else
601 { // Center horizontally
602 scale = target.height() / double( w->height() );
603 target.moveLeft( target.left() + ( target.width() - int( w->width() * scale )) / 2 );
604 target.setWidth( int( w->width() * scale ));
606 // Don't scale the windows too much
607 if( scale > 2.0 || ( scale > 1.0 && ( w->width() > 300 || w->height() > 300 )))
609 scale = ( w->width() > 300 || w->height() > 300 ) ? 1.0 : 2.0;
610 target = QRect(
611 target.center().x() - int( w->width() * scale ) / 2,
612 target.center().y() - int( w->height() * scale ) / 2,
613 scale * w->width(), scale * w->height() );
615 m_motionManager.moveWindow( w, target );
619 void PresentWindowsEffect::calculateWindowTransformationsKompose( EffectWindowList windowlist, int screen )
621 QRect availRect = effects->clientArea( ScreenArea, screen, effects->currentDesktop() );
622 if( m_showPanel ) // reserve space for the panel
623 availRect = effects->clientArea( MaximizeArea, screen, effects->currentDesktop() );
624 qSort( windowlist ); // The location of the windows should not depend on the stacking order
626 // Following code is taken from Kompose 0.5.4, src/komposelayout.cpp
628 int spacing = 10;
629 int rows, columns;
630 double parentRatio = availRect.width() / (double)availRect.height();
631 // Use more columns than rows when parent's width > parent's height
632 if ( parentRatio > 1 )
634 columns = (int)ceil( sqrt((double)windowlist.count()) );
635 rows = (int)ceil( (double)windowlist.count() / (double)columns );
637 else
639 rows = (int)ceil( sqrt((double)windowlist.count()) );
640 columns = (int)ceil( (double)windowlist.count() / (double)rows );
642 //kDebug(1212) << "Using " << rows << " rows & " << columns << " columns for " << windowlist.count() << " clients";
644 // Calculate width & height
645 int w = (availRect.width() - (columns+1) * spacing ) / columns;
646 int h = (availRect.height() - (rows+1) * spacing ) / rows;
648 EffectWindowList::iterator it( windowlist.begin() );
649 QList<QRect> geometryRects;
650 QList<int> maxRowHeights;
651 // Process rows
652 for ( int i=0; i<rows; ++i )
654 int xOffsetFromLastCol = 0;
655 int maxHeightInRow = 0;
656 // Process columns
657 for ( int j=0; j<columns; ++j )
659 EffectWindow* window;
661 // Check for end of List
662 if ( it == windowlist.end() )
663 break;
664 window = *it;
666 // Calculate width and height of widget
667 double ratio = aspectRatio( window );
669 int widgetw = 100;
670 int widgeth = 100;
671 int usableW = w;
672 int usableH = h;
674 // use width of two boxes if there is no right neighbour
675 if (window == windowlist.last() && j != columns-1)
677 usableW = 2*w;
679 ++it; // We need access to the neighbour in the following
680 // expand if right neighbour has ratio < 1
681 if (j != columns-1 && it != windowlist.end() && aspectRatio(*it) < 1)
683 int addW = w - widthForHeight(*it, h);
684 if ( addW > 0 )
686 usableW = w + addW;
690 if ( ratio == -1 )
692 widgetw = w;
693 widgeth = h;
695 else
697 double widthByHeight = widthForHeight( window, usableH );
698 double heightByWidth = heightForWidth( window, usableW );
699 if ( (ratio >= 1.0 && heightByWidth <= usableH) ||
700 (ratio < 1.0 && widthByHeight > usableW) )
702 widgetw = usableW;
703 widgeth = (int)heightByWidth;
705 else if ( (ratio < 1.0 && widthByHeight <= usableW) ||
706 (ratio >= 1.0 && heightByWidth > usableH) )
708 widgeth = usableH;
709 widgetw = (int)widthByHeight;
711 // Don't upscale large-ish windows
712 if( widgetw > window->width() && ( window->width() > 300 || window->height() > 300 ))
714 widgetw = window->width();
715 widgeth = window->height();
719 // Set the Widget's size
721 int alignmentXoffset = 0;
722 int alignmentYoffset = 0;
723 if ( i==0 && h > widgeth )
724 alignmentYoffset = h - widgeth;
725 if ( j==0 && w > widgetw )
726 alignmentXoffset = w - widgetw;
727 QRect geom( availRect.x() + j * (w + spacing) + spacing + alignmentXoffset + xOffsetFromLastCol,
728 availRect.y() + i * (h + spacing) + spacing + alignmentYoffset,
729 widgetw, widgeth );
730 geometryRects.append(geom);
732 // Set the x offset for the next column
733 if (alignmentXoffset==0)
734 xOffsetFromLastCol += widgetw-w;
735 if (maxHeightInRow < widgeth)
736 maxHeightInRow = widgeth;
738 maxRowHeights.append(maxHeightInRow);
741 int topOffset = 0;
742 for( int i = 0; i < rows; i++ )
744 for( int j = 0; j < columns; j++ )
746 int pos = i*columns + j;
747 if(pos >= windowlist.count())
748 break;
750 EffectWindow* window = windowlist[pos];
751 QRect target = geometryRects[pos];
752 target.setY( target.y() + topOffset );
753 m_windowData[window].slot = pos;
754 m_motionManager.moveWindow( window, target );
756 //kDebug(1212) << "Window '" << window->caption() << "' gets moved to (" <<
757 // mWindowData[window].area.left() << "; " << mWindowData[window].area.right() <<
758 // "), scale: " << mWindowData[window].scale << endl;
760 if ( maxRowHeights[i]-h > 0 )
761 topOffset += maxRowHeights[i]-h;
765 void PresentWindowsEffect::calculateWindowTransformationsNatural( EffectWindowList windowlist, int screen )
767 // If windows do not overlap they scale into nothingness, fix by resetting. To reproduce
768 // just have a single window on a Xinerama screen or have two windows that do not touch.
769 // TODO: Work out why this happens, is most likely a bug in the manager.
770 foreach( EffectWindow *w, windowlist )
771 if( m_motionManager.transformedGeometry( w ) == w->geometry() )
772 m_motionManager.reset( w );
774 if( windowlist.count() == 1 )
776 // Just move the window to its original location to save time
777 m_motionManager.moveWindow( windowlist[0], windowlist[0]->geometry() );
778 return;
781 // As we are using pseudo-random movement (See "slot") we need to make sure the list
782 // is always sorted the same way no matter which window is currently active.
783 qSort( windowlist );
785 QRect area = effects->clientArea( ScreenArea, screen, effects->currentDesktop() );
786 if( m_showPanel ) // reserve space for the panel
787 area = effects->clientArea( MaximizeArea, screen, effects->currentDesktop() );
788 QRect bounds = area;
789 int direction = 0;
790 QHash<EffectWindow*, QRect> targets;
791 foreach( EffectWindow *w, windowlist )
793 bounds = bounds.united( w->geometry() );
794 targets[w] = w->geometry();
795 // Reuse the unused "slot" as a preferred direction attribute. This is used when the window
796 // is on the edge of the screen to try to use as much screen real estate as possible.
797 m_windowData[w].slot = direction;
798 direction++;
799 if( direction == 4 )
800 direction = 0;
803 // Iterate over all windows, if two overlap push them apart _slightly_ as we try to
804 // brute-force the most optimal positions over many iterations.
805 bool overlap;
808 overlap = false;
809 foreach( EffectWindow *w, windowlist )
811 foreach( EffectWindow *e, windowlist )
813 if( w != e && targets[w].adjusted( -5, -5, 5, 5 ).intersects(
814 targets[e].adjusted( -5, -5, 5, 5 )))
816 overlap = true;
818 // Determine pushing direction
819 QPoint diff( targets[e].center() - targets[w].center() );
820 // Prevent dividing by zero and non-movement
821 if( diff.x() == 0 && diff.y() == 0 )
822 diff.setX( 1 );
823 // Try to keep screen aspect ratio
824 //if( bounds.height() / bounds.width() > area.height() / area.width() )
825 // diff.setY( diff.y() / 2 );
826 //else
827 // diff.setX( diff.x() / 2 );
828 // Approximate a vector of between 10px and 20px in magnitude in the same direction
829 diff *= m_accuracy / double( diff.manhattanLength() );
830 // Move both windows apart
831 targets[w].translate( -diff );
832 targets[e].translate( diff );
834 // Try to keep the bounding rect the same aspect as the screen so that more
835 // screen real estate is utilised. We do this by splitting the screen into nine
836 // equal sections, if the window center is in any of the corner sections pull the
837 // window towards the outer corner. If it is in any of the other edge sections
838 // alternate between each corner on that edge. We don't want to determine it
839 // randomly as it will not produce consistant locations when using the filter.
840 // Only move one window so we don't cause large amounts of unnecessary zooming
841 // in some situations. We need to do this even when expanding later just in case
842 // all windows are the same size.
843 // (We are using an old bounding rect for this, hopefully it doesn't matter)
844 int xSection = ( targets[w].x() - bounds.x() ) / ( bounds.width() / 3 );
845 int ySection = ( targets[w].y() - bounds.y() ) / ( bounds.height() / 3 );
846 diff = QPoint( 0, 0 );
847 if( xSection != 1 || ySection != 1 ) // Remove this if you want the center to pull as well
849 if( xSection == 1 )
850 xSection = ( m_windowData[w].slot / 2 ? 2 : 0 );
851 if( ySection == 1 )
852 ySection = ( m_windowData[w].slot % 2 ? 2 : 0 );
854 if( xSection == 0 && ySection == 0 )
855 diff = QPoint( bounds.topLeft() - targets[w].center() );
856 if( xSection == 2 && ySection == 0 )
857 diff = QPoint( bounds.topRight() - targets[w].center() );
858 if( xSection == 2 && ySection == 2 )
859 diff = QPoint( bounds.bottomRight() - targets[w].center() );
860 if( xSection == 0 && ySection == 2 )
861 diff = QPoint( bounds.bottomLeft() - targets[w].center() );
862 if( diff.x() != 0 || diff.y() != 0 )
864 diff *= m_accuracy / double( diff.manhattanLength() );
865 targets[w].translate( diff );
868 // Update bounding rect
869 bounds = bounds.united( targets[w] );
870 bounds = bounds.united( targets[e] );
874 } while( overlap );
876 // Work out scaling by getting the most top-left and most bottom-right window coords.
877 // The 20's and 10's are so that the windows don't touch the edge of the screen.
878 double scale;
879 if( bounds == area )
880 scale = 1.0; // Don't add borders to the screen
881 else if( area.width() / double( bounds.width() ) < area.height() / double( bounds.height() ))
882 scale = ( area.width() - 20 ) / double( bounds.width() );
883 else
884 scale = ( area.height() - 20 ) / double( bounds.height() );
885 // Make bounding rect fill the screen size for later steps
886 bounds = QRect(
887 bounds.x() - ( area.width() - 20 - bounds.width() * scale ) / 2 - 10 / scale,
888 bounds.y() - ( area.height() - 20 - bounds.height() * scale ) / 2 - 10 / scale,
889 area.width() / scale,
890 area.height() / scale
893 // Move all windows back onto the screen and set their scale
894 foreach( EffectWindow *w, windowlist )
895 targets[w] = QRect(
896 ( targets[w].x() - bounds.x() ) * scale + area.x(),
897 ( targets[w].y() - bounds.y() ) * scale + area.y(),
898 targets[w].width() * scale,
899 targets[w].height() * scale
902 // Try to fill the gaps by enlarging windows if they have the space
903 if( m_fillGaps )
905 // Don't expand onto or over the border
906 QRegion borderRegion( area.adjusted( -200, -200, 200, 200 ));
907 borderRegion ^= area.adjusted( 10 / scale, 10 / scale, -10 / scale, -10 / scale );
909 bool moved;
912 moved = false;
913 foreach( EffectWindow *w, windowlist )
915 QRect oldRect;
916 // This may cause some slight distortion if the windows are enlarged a large amount
917 int widthDiff = m_accuracy;
918 int heightDiff = heightForWidth( w, targets[w].width() + widthDiff ) - targets[w].height();
919 int xDiff = widthDiff / 2; // Also move a bit in the direction of the enlarge, allows the
920 int yDiff = heightDiff / 2; // center windows to be enlarged if there is gaps on the side.
922 // Attempt enlarging to the top-right
923 oldRect = targets[w];
924 targets[w] = QRect(
925 targets[w].x() + xDiff,
926 targets[w].y() - yDiff - heightDiff,
927 targets[w].width() + widthDiff,
928 targets[w].height() + heightDiff
930 if( isOverlappingAny( w, targets, borderRegion ))
931 targets[w] = oldRect;
932 else
933 moved = true;
935 // Attempt enlarging to the bottom-right
936 oldRect = targets[w];
937 targets[w] = QRect(
938 targets[w].x() + xDiff,
939 targets[w].y() + yDiff,
940 targets[w].width() + widthDiff,
941 targets[w].height() + heightDiff
943 if( isOverlappingAny( w, targets, borderRegion ))
944 targets[w] = oldRect;
945 else
946 moved = true;
948 // Attempt enlarging to the bottom-left
949 oldRect = targets[w];
950 targets[w] = QRect(
951 targets[w].x() - xDiff - widthDiff,
952 targets[w].y() + yDiff,
953 targets[w].width() + widthDiff,
954 targets[w].height() + heightDiff
956 if( isOverlappingAny( w, targets, borderRegion ))
957 targets[w] = oldRect;
958 else
959 moved = true;
961 // Attempt enlarging to the top-left
962 oldRect = targets[w];
963 targets[w] = QRect(
964 targets[w].x() - xDiff - widthDiff,
965 targets[w].y() - yDiff - heightDiff,
966 targets[w].width() + widthDiff,
967 targets[w].height() + heightDiff
969 if( isOverlappingAny( w, targets, borderRegion ))
970 targets[w] = oldRect;
971 else
972 moved = true;
974 } while( moved );
976 // The expanding code above can actually enlarge windows over 1.0/2.0 scale, we don't like this
977 // We can't add this to the loop above as it would cause a never-ending loop so we have to make
978 // do with the less-than-optimal space usage with using this method.
979 foreach( EffectWindow *w, windowlist )
981 double scale = targets[w].width() / double( w->width() );
982 if( scale > 2.0 || ( scale > 1.0 && ( w->width() > 300 || w->height() > 300 )))
984 scale = ( w->width() > 300 || w->height() > 300 ) ? 1.0 : 2.0;
985 targets[w] = QRect(
986 targets[w].center().x() - int( w->width() * scale ) / 2,
987 targets[w].center().y() - int( w->height() * scale ) / 2,
988 w->width() * scale,
989 w->height() * scale );
994 // Notify the motion manager of the targets
995 foreach( EffectWindow *w, windowlist )
996 m_motionManager.moveWindow( w, targets[w] );
999 //-----------------------------------------------------------------------------
1000 // Helper functions for window rearranging
1002 void PresentWindowsEffect::assignSlots( EffectWindowList windowlist, const QRect &area, int columns, int rows )
1004 QVector< bool > taken;
1005 taken.fill( false, columns * rows );
1006 foreach( EffectWindow* w, windowlist )
1007 if( m_windowData[w].slot != -1 )
1008 taken[ m_windowData[w].slot ] = true;
1009 int slotWidth = area.width() / columns;
1010 int slotHeight = area.height() / rows;
1011 if( m_tabBoxEnabled )
1013 for( int i = 0; i < windowlist.count(); i++ )
1015 EffectWindow *w = windowlist[i];
1016 WindowData *wData = &m_windowData[w];
1017 if( wData->slot != -1 )
1018 continue; // It's already has a slot
1019 int x = i % columns;
1020 int y = i / columns;
1021 QPoint pos = w->geometry().center();
1022 if( pos.x() < area.left() )
1023 pos.setX( area.left() );
1024 if( pos.x() > area.right() )
1025 pos.setX( area.right() );
1026 if( pos.y() < area.top() )
1027 pos.setY( area.top() );
1028 if( pos.y() > area.bottom() )
1029 pos.setY( area.bottom() );
1030 int xdiff = pos.x() - ( area.x() + slotWidth * x + slotWidth / 2 );
1031 int ydiff = pos.y() - ( area.y() + slotHeight * y + slotHeight / 2 );
1032 int dist = int( sqrt( double( xdiff * xdiff + ydiff * ydiff )));
1033 wData->slot = i;
1034 wData->slot_distance = dist;
1037 else
1039 foreach( EffectWindow *w, windowlist )
1041 WindowData *wData = &m_windowData[w];
1042 if( wData->slot != -1 )
1043 continue; // it already has a slot
1044 QPoint pos = w->geometry().center();
1045 if( pos.x() < area.left() )
1046 pos.setX( area.left() );
1047 if( pos.x() > area.right() )
1048 pos.setX( area.right() );
1049 if( pos.y() < area.top() )
1050 pos.setY( area.top() );
1051 if( pos.y() > area.bottom() )
1052 pos.setY( area.bottom() );
1053 int distance = INT_MAX;
1054 for( int x = 0; x < columns; x++ )
1055 for( int y = 0; y < rows; y++ )
1057 int slot = x + y * columns;
1058 if( taken[slot] )
1059 continue;
1060 int xdiff = pos.x() - ( area.x() + slotWidth * x + slotWidth / 2 );
1061 int ydiff = pos.y() - ( area.y() + slotHeight * y + slotHeight / 2 );
1062 int dist = int( sqrt( double( xdiff * xdiff + ydiff * ydiff )));
1063 if( dist < distance )
1065 distance = dist;
1066 wData->slot = slot;
1067 wData->slot_distance = distance;
1074 void PresentWindowsEffect::getBestAssignments( EffectWindowList windowlist )
1076 foreach( EffectWindow* w1, windowlist )
1078 WindowData *windowData1 = &m_windowData[w1];
1079 foreach( EffectWindow* w2, windowlist )
1081 WindowData *windowData2 = &m_windowData[w2];
1082 if( w1 != w2 && windowData1->slot == windowData2->slot &&
1083 windowData1->slot_distance >= windowData2->slot_distance )
1084 windowData1->slot = -1;
1089 bool PresentWindowsEffect::isOverlappingAny( EffectWindow *w, const QHash<EffectWindow*, QRect> &targets, const QRegion &border )
1091 if( border.intersects( targets[w] ))
1092 return true;
1093 // Is there a better way to do this?
1094 foreach( EffectWindow *e, targets.keys() )
1096 if( w == e )
1097 continue;
1098 if( targets[w].adjusted( -5, -5, 5, 5 ).intersects(
1099 targets[e].adjusted( -5, -5, 5, 5 )))
1100 return true;
1102 return false;
1105 //-----------------------------------------------------------------------------
1106 // Activation
1108 void PresentWindowsEffect::setActive( bool active, bool closingTab )
1110 if( effects->activeFullScreenEffect() && effects->activeFullScreenEffect() != this )
1111 return;
1112 if( m_activated == active )
1113 return;
1114 if( m_activated && m_tabBoxEnabled && !closingTab )
1116 effects->closeTabBox();
1117 return;
1119 m_activated = active;
1120 if( m_activated )
1122 m_decalOpacity = 0.0;
1123 m_windowFilter.clear();
1125 // Add every single window to m_windowData (Just calling [w] creates it)
1126 foreach( EffectWindow *w, effects->stackingOrder() )
1128 m_windowData[w].visible = isVisibleWindow( w );
1129 m_windowData[w].opacity = 0.0;
1130 if( w->isOnCurrentDesktop() && !w->isMinimized() )
1131 m_windowData[w].opacity = 1.0;
1132 m_windowData[w].highlight = 1.0;
1135 if( m_tabBoxEnabled )
1137 foreach( EffectWindow *w, effects->currentTabBoxWindowList() )
1139 m_motionManager.manage( w );
1140 m_windowData[w].visible = effects->currentTabBoxWindowList().contains( w );
1142 // Hide windows not in the list
1143 foreach( EffectWindow *w, effects->stackingOrder() )
1144 m_windowData[w].visible = isVisibleWindow( w ) &&
1145 (!isSelectableWindow( w ) || effects->currentTabBoxWindowList().contains( w ));
1147 else
1149 // Filter out special windows such as panels and taskbars
1150 foreach( EffectWindow *w, effects->stackingOrder() )
1151 if( isSelectableWindow( w ))
1152 m_motionManager.manage( w );
1154 if( m_motionManager.managedWindows().isEmpty() ||
1155 (( m_motionManager.managedWindows().count() == 1 ) && m_motionManager.managedWindows().first()->isOnCurrentDesktop() ))
1156 { // No point triggering if there is nothing to do
1157 m_activated = false;
1158 m_windowData.clear();
1159 m_motionManager.unmanageAll();
1160 return;
1163 // Create temporary input window to catch mouse events
1164 m_input = effects->createFullScreenInputWindow( this, Qt::PointingHandCursor );
1165 m_hasKeyboardGrab = effects->grabKeyboard( this );
1166 effects->setActiveFullScreenEffect( this );
1168 m_gridSizes.clear();
1169 for( int i = 0; i < effects->numScreens(); i++ )
1170 m_gridSizes.append( GridSize() );
1172 rearrangeWindows();
1173 if( m_tabBoxEnabled )
1174 setHighlightedWindow( effects->currentTabBoxWindow() );
1175 else
1176 setHighlightedWindow( effects->activeWindow() );
1178 else
1180 // Fade in/out all windows
1181 foreach( EffectWindow *w, effects->stackingOrder() )
1183 EffectWindow *activeWindow = effects->activeWindow();
1184 if( m_tabBoxEnabled )
1185 activeWindow = effects->currentTabBoxWindow();
1186 if( activeWindow )
1187 m_windowData[w].visible = ( w->desktop() == activeWindow->desktop() || w->isOnAllDesktops() ) && !w->isMinimized();
1188 else // Deactivating to an empty desktop
1189 m_windowData[w].visible = ( w->isOnCurrentDesktop() || w->isOnAllDesktops() ) && !w->isMinimized();
1190 if( m_tabBoxEnabled && w == effects->currentTabBoxWindow() )
1191 m_windowData[w].visible = true;
1194 // Move all windows back to their original position
1195 foreach( EffectWindow *w, m_motionManager.managedWindows() )
1196 m_motionManager.moveWindow( w, w->geometry() );
1197 discardFilterTexture();
1198 m_windowFilter.clear();
1200 effects->destroyInputWindow( m_input );
1201 if( m_hasKeyboardGrab )
1202 effects->ungrabKeyboard();
1203 m_hasKeyboardGrab = false;
1205 effects->addRepaintFull(); // Trigger the first repaint
1208 //-----------------------------------------------------------------------------
1209 // Filter box
1211 void PresentWindowsEffect::discardFilterTexture()
1213 #ifdef KWIN_HAVE_OPENGL_COMPOSITING
1214 delete m_filterTexture;
1215 m_filterTexture = NULL;
1216 #endif
1219 void PresentWindowsEffect::updateFilterTexture()
1221 #ifdef KWIN_HAVE_OPENGL_COMPOSITING
1222 discardFilterTexture();
1223 if( m_windowFilter.isEmpty())
1225 effects->addRepaint( m_filterTextureRect );
1226 return;
1228 // Create font for filter text
1229 QFont font;
1230 font.setPointSize( font.pointSize() * 2 );
1231 font.setBold( true );
1232 // Get size of the rect containing filter text
1233 QFontMetrics fm( font );
1234 QRect rect;
1235 QString translatedString = i18n( "Filter:\n%1", m_windowFilter );
1236 rect.setSize( fm.size( 0, translatedString ));
1237 QRect area = effects->clientArea( PlacementArea, effects->activeScreen(), effects->currentDesktop() );
1238 // Create image
1239 QImage im( rect.width(), rect.height(), QImage::Format_ARGB32 );
1240 im.fill( Qt::transparent );
1241 // Paint the filter text to it
1242 QPainter p( &im );
1243 p.setFont( font );
1244 p.setPen( QPalette().color( QPalette::Active, QPalette::HighlightedText ));
1245 p.drawText( rect, Qt::AlignCenter, translatedString );
1246 p.end();
1247 // Create GL texture
1248 m_filterTexture = new GLTexture( im );
1249 // Get position for filter text and it's frame
1250 m_filterTextureRect = QRect(
1251 area.x() + ( area.width() - rect.width() ) / 2,
1252 area.y() + ( area.height() - rect.height() ) / 2,
1253 rect.width(), rect.height() );
1254 m_filterFrameRect = m_filterTextureRect.adjusted( -20, -10, 20, 10 );
1255 // Schedule repaint
1256 effects->addRepaint( m_filterTextureRect );
1257 #endif
1260 //-----------------------------------------------------------------------------
1261 // Helper functions
1263 bool PresentWindowsEffect::isSelectableWindow( EffectWindow *w )
1265 if( w->isSpecialWindow() || w->isUtility() )
1266 return false;
1267 if( w->isDeleted() )
1268 return false;
1269 if( !m_allDesktops && !w->isOnCurrentDesktop() )
1270 return false;
1271 if( !m_tabBoxEnabled && m_ignoreMinimized && w->isMinimized() )
1272 return false;
1273 return true;
1276 bool PresentWindowsEffect::isVisibleWindow( EffectWindow *w )
1278 if( w->isDesktop() )
1279 return true;
1280 return isSelectableWindow( w );
1283 void PresentWindowsEffect::setHighlightedWindow( EffectWindow *w )
1285 if( w == m_highlightedWindow || ( w != NULL && !m_motionManager.isManaging( w )))
1286 return;
1288 if( m_highlightedWindow )
1289 m_highlightedWindow->addRepaintFull(); // Trigger the first repaint
1290 m_highlightedWindow = w;
1291 if( m_highlightedWindow )
1292 m_highlightedWindow->addRepaintFull(); // Trigger the first repaint
1294 if( m_tabBoxEnabled && m_highlightedWindow )
1295 effects->setTabBoxWindow( w );
1298 EffectWindow* PresentWindowsEffect::relativeWindow( EffectWindow *w, int xdiff, int ydiff, bool wrap ) const
1299 { // TODO: Is it possible to select hidden windows?
1300 EffectWindow* next;
1301 QRect area = effects->clientArea( FullArea, 0, effects->currentDesktop() );
1302 QRect detectRect;
1304 // Detect across the width of the desktop
1305 if( xdiff != 0 )
1307 if( xdiff > 0 )
1308 { // Detect right
1309 for( int i = 0; i < xdiff; i++ )
1311 QRectF wArea = m_motionManager.transformedGeometry( w );
1312 detectRect = QRect( 0, wArea.y(), area.width(), wArea.height() );
1313 next = NULL;
1314 foreach( EffectWindow* e, m_motionManager.managedWindows() )
1316 if( !m_windowData[e].visible )
1317 continue;
1318 QRectF eArea = m_motionManager.transformedGeometry( e );
1319 if( eArea.intersects( detectRect ) &&
1320 eArea.x() > wArea.x() )
1322 if( next == NULL )
1323 next = e;
1324 else
1326 QRectF nArea = m_motionManager.transformedGeometry( next );
1327 if( eArea.x() < nArea.x() )
1328 next = e;
1332 if( next == NULL )
1334 if( wrap ) // We are at the right-most window, now get the left-most one to wrap
1335 return relativeWindow( w, -1000, 0, false );
1336 break; // No more windows to the right
1338 w = next;
1340 return w;
1342 else
1343 { // Detect left
1344 for( int i = 0; i < -xdiff; i++ )
1346 QRectF wArea = m_motionManager.transformedGeometry( w );
1347 detectRect = QRect( 0, wArea.y(), area.width(), wArea.height() );
1348 next = NULL;
1349 foreach( EffectWindow* e, m_motionManager.managedWindows() )
1351 if( !m_windowData[e].visible )
1352 continue;
1353 QRectF eArea = m_motionManager.transformedGeometry( e );
1354 if( eArea.intersects( detectRect ) &&
1355 eArea.x() + eArea.width() < wArea.x() + wArea.width() )
1357 if( next == NULL )
1358 next = e;
1359 else
1361 QRectF nArea = m_motionManager.transformedGeometry( next );
1362 if( eArea.x() + eArea.width() > nArea.x() + nArea.width() )
1363 next = e;
1367 if( next == NULL )
1369 if( wrap ) // We are at the left-most window, now get the right-most one to wrap
1370 return relativeWindow( w, 1000, 0, false );
1371 break; // No more windows to the left
1373 w = next;
1375 return w;
1379 // Detect across the height of the desktop
1380 if( ydiff != 0 )
1382 if( ydiff > 0 )
1383 { // Detect down
1384 for( int i = 0; i < ydiff; i++ )
1386 QRectF wArea = m_motionManager.transformedGeometry( w );
1387 detectRect = QRect( wArea.x(), 0, wArea.width(), area.height() );
1388 next = NULL;
1389 foreach( EffectWindow* e, m_motionManager.managedWindows() )
1391 if( !m_windowData[e].visible )
1392 continue;
1393 QRectF eArea = m_motionManager.transformedGeometry( e );
1394 if( eArea.intersects( detectRect ) &&
1395 eArea.y() > wArea.y() )
1397 if( next == NULL )
1398 next = e;
1399 else
1401 QRectF nArea = m_motionManager.transformedGeometry( next );
1402 if( eArea.y() < nArea.y() )
1403 next = e;
1407 if( next == NULL )
1409 if( wrap ) // We are at the bottom-most window, now get the top-most one to wrap
1410 return relativeWindow( w, 0, -1000, false );
1411 break; // No more windows to the bottom
1413 w = next;
1415 return w;
1417 else
1418 { // Detect up
1419 for( int i = 0; i < -ydiff; i++ )
1421 QRectF wArea = m_motionManager.transformedGeometry( w );
1422 detectRect = QRect( wArea.x(), 0, wArea.width(), area.height() );
1423 next = NULL;
1424 foreach( EffectWindow* e, m_motionManager.managedWindows() )
1426 if( !m_windowData[e].visible )
1427 continue;
1428 QRectF eArea = m_motionManager.transformedGeometry( e );
1429 if( eArea.intersects( detectRect ) &&
1430 eArea.y() + eArea.height() < wArea.y() + wArea.height() )
1432 if( next == NULL )
1433 next = e;
1434 else
1436 QRectF nArea = m_motionManager.transformedGeometry( next );
1437 if( eArea.y() + eArea.height() > nArea.y() + nArea.height() )
1438 next = e;
1442 if( next == NULL )
1444 if( wrap ) // We are at the top-most window, now get the bottom-most one to wrap
1445 return relativeWindow( w, 0, 1000, false );
1446 break; // No more windows to the top
1448 w = next;
1450 return w;
1454 abort(); // Should never get here
1457 EffectWindow* PresentWindowsEffect::findFirstWindow() const
1459 EffectWindow *topLeft = NULL;
1460 QRectF topLeftGeometry;
1461 foreach( EffectWindow *w, m_motionManager.managedWindows() )
1463 QRectF geometry = m_motionManager.transformedGeometry( w );
1464 if( m_windowData[w].visible == false )
1465 continue; // Not visible
1466 if( topLeft == NULL )
1468 topLeft = w;
1469 topLeftGeometry = geometry;
1471 else if( geometry.x() < topLeftGeometry.x() || geometry.y() < topLeftGeometry.y() )
1473 topLeft = w;
1474 topLeftGeometry = geometry;
1477 return topLeft;
1480 void PresentWindowsEffect::paintWindowIcon( EffectWindow *w, WindowPaintData &data )
1482 // Don't bother if we don't even have an icon
1483 if( w->icon().isNull() )
1484 return;
1486 WindowData &wData = m_windowData[w];
1487 if( wData.icon.cacheKey() != w->icon().cacheKey())
1488 { // Make sure data.icon is the right QPixmap, and rebind
1489 wData.icon = w->icon();
1490 #ifdef KWIN_HAVE_OPENGL_COMPOSITING
1491 if( effects->compositingType() == OpenGLCompositing )
1493 wData.iconTexture = new GLTexture( wData.icon );
1494 wData.iconTexture->setFilter( GL_LINEAR );
1496 #endif
1497 #ifdef KWIN_HAVE_XRENDER_COMPOSITING
1498 if( effects->compositingType() == XRenderCompositing )
1499 wData.iconPicture = XRenderPicture( wData.icon );
1500 #endif
1502 int icon_margin = 8;
1503 int width = wData.icon.width();
1504 int height = wData.icon.height();
1505 int x = w->x() + data.xTranslate + w->width() * data.xScale * 0.95 - width - icon_margin;
1506 int y = w->y() + data.yTranslate + w->height() * data.yScale * 0.95 - height - icon_margin;
1507 #ifdef KWIN_HAVE_OPENGL_COMPOSITING
1508 if( effects->compositingType() == OpenGLCompositing )
1510 glPushAttrib( GL_CURRENT_BIT | GL_ENABLE_BIT | GL_TEXTURE_BIT );
1511 glEnable( GL_BLEND );
1512 glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
1513 // Render some background
1514 glColor4f( 0, 0, 0, 0.5 * wData.opacity * m_decalOpacity );
1515 renderRoundBox( QRect( x-3, y-3, width+6, height+6 ), 3 );
1516 // Render the icon
1517 glColor4f( 1, 1, 1, 1 * wData.opacity * m_decalOpacity );
1518 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
1519 wData.iconTexture->bind();
1520 wData.iconTexture->render( infiniteRegion(), QRect( x, y, width, height ));
1521 wData.iconTexture->unbind();
1522 glPopAttrib();
1524 #endif
1525 #ifdef KWIN_HAVE_XRENDER_COMPOSITING
1526 if( effects->compositingType() == XRenderCompositing )
1528 XRenderComposite( display(),
1529 wData.icon.depth() == 32 ? PictOpOver : PictOpSrc,
1530 wData.iconPicture, None,
1531 effects->xrenderBufferPicture(),
1532 0, 0, 0, 0, x, y, width, height );
1534 #endif
1537 } // namespace
1539 #include "presentwindows.moc"