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>
26 #include <kcolorscheme.h>
27 #include <kconfiggroup.h>
30 #include <QMouseEvent>
40 KWIN_EFFECT( presentwindows
, PresentWindowsEffect
)
42 PresentWindowsEffect::PresentWindowsEffect()
43 : mShowWindowsFromAllDesktops ( false )
47 , hasKeyboardGrab( false )
48 , mHighlightedWindow( NULL
)
49 #ifdef KWIN_HAVE_OPENGL_COMPOSITING
50 , filterTexture( NULL
)
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
);
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;
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)
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
);
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();
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
);
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
);
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
) );
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();
250 if( (*it
).area
.contains( cursorPos()))
252 if( mHighlightedWindow
!= it
.key())
253 setHighlightedWindow( it
.key());
259 if( e
->type() != QEvent::MouseButtonPress
)
261 if( static_cast< QMouseEvent
* >( e
)->button() != Qt::LeftButton
)
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
281 // Deactivate effect, no matter if any window was actually activated
285 void PresentWindowsEffect::windowClosed( EffectWindow
* w
)
287 if( mHighlightedWindow
== w
)
288 setHighlightedWindow( findFirstWindow());
289 mWindowsToPresent
.removeAll( w
);
293 void PresentWindowsEffect::setActive(bool active
)
295 if( effects
->activeFullScreenEffect() && effects
->activeFullScreenEffect() != this )
297 if( mActivated
== active
)
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() )
313 if( window
->isDeleted())
315 if( !mShowWindowsFromAllDesktops
&& !window
->isOnCurrentDesktop() )
317 mWindowsToPresent
.append(window
);
320 setHighlightedWindow( effects
->activeWindow());
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()
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()
360 EffectWindowList windowlist
;
361 if( windowFilter
.isEmpty())
362 windowlist
= mWindowsToPresent
;
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())
376 setHighlightedWindow( NULL
);
377 effects
->addRepaintFull();
381 if( !mWindowData
.isEmpty()) // this is not the first arranging
383 bool rearrange
= canRearrangeClosest( windowlist
); // called before manipulating mWindowData
385 EffectWindowList newlist
= windowlist
;
386 EffectWindowList oldlist
= mWindowData
.keys();
389 for( DataHash::ConstIterator it
= mWindowData
.begin();
390 it
!= mWindowData
.end();
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
)
397 if( mHighlightedWindow
!= NULL
&& !mWindowData
.contains( mHighlightedWindow
))
398 setHighlightedWindow( NULL
);
399 for( DataHash::Iterator it
= mWindowData
.begin();
400 it
!= mWindowData
.end();
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());
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
441 foreach( EffectWindow
* window
, windowlist
)
444 // Row/Col of this window
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
;
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
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
);
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
;
510 for ( int i
=0; i
<rows
; ++i
)
512 int xOffsetFromLastCol
= 0;
513 int maxHeightInRow
= 0;
515 for ( int j
=0; j
<columns
; ++j
)
517 EffectWindow
* window
;
519 // Check for end of List
520 if ( it
== windowlist
.end() )
524 // Calculate width and height of widget
525 double ratio
= windowAspectRatio(window
);
532 // use width of two boxes if there is no right neighbour
533 if (window
== windowlist
.last() && j
!= columns
-1)
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
);
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
) )
561 widgeth
= (int)heightForWidth
;
563 else if ( (ratio
< 1.0 && widthForHeight
<= usableW
) ||
564 (ratio
>= 1.0 && heightForWidth
> 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
,
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
);
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())
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;
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;
644 int slotwidth
= area
.width() / columns
;
645 int slotheight
= area
.height() / rows
;
646 for( DataHash::Iterator it
= mWindowData
.begin();
647 it
!= mWindowData
.end();
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
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
));
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
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() );
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
)
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();
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
;
715 int slot
= x
+ y
* columns
;
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
)
727 (*it
).slot_distance
= distance
;
733 void PresentWindowsEffect::getBestAssignments()
735 for( DataHash::Iterator it1
= mWindowData
.begin();
736 it1
!= mWindowData
.end();
739 for( DataHash::ConstIterator it2
= mWindowData
.begin();
740 it2
!= mWindowData
.end();
743 if( it1
.key() != it2
.key() && (*it1
).slot
== (*it2
).slot
744 && (*it1
).slot_distance
>= (*it2
).slot_distance
)
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 )
766 if( border
== borderActivate
&& !mActivated
)
771 if( border
== borderActivateAll
&& !mActivated
)
773 toggleActiveAllDesktops();
779 void PresentWindowsEffect::grabbedKeyboardEvent( QKeyEvent
* e
)
781 if( e
->type() == QEvent::KeyPress
)
784 { // wrap only on autorepeat
786 setHighlightedWindow( relativeWindow( mHighlightedWindow
, -1, 0, !e
->isAutoRepeat()));
789 setHighlightedWindow( relativeWindow( mHighlightedWindow
, 1, 0, !e
->isAutoRepeat()));
792 setHighlightedWindow( relativeWindow( mHighlightedWindow
, 0, -1, !e
->isAutoRepeat()));
795 setHighlightedWindow( relativeWindow( mHighlightedWindow
, 0, 1, !e
->isAutoRepeat()));
798 setHighlightedWindow( relativeWindow( mHighlightedWindow
, -1000, 0, false ));
801 setHighlightedWindow( relativeWindow( mHighlightedWindow
, 1000, 0, false ));
804 setHighlightedWindow( relativeWindow( mHighlightedWindow
, 0, -1000, false ));
806 case Qt::Key_PageDown
:
807 setHighlightedWindow( relativeWindow( mHighlightedWindow
, 0, 1000, false ));
809 case Qt::Key_Backspace
:
810 if( !windowFilter
.isEmpty())
812 windowFilter
.remove( windowFilter
.length() - 1, 1 );
813 updateFilterTexture();
822 if( mHighlightedWindow
!= NULL
)
824 effects
->activateWindow( mHighlightedWindow
);
828 if( mWindowData
.count() == 1 ) // only one window shown
830 effects
->activateWindow( mWindowData
.begin().key());
835 if( !e
->text().isEmpty())
837 windowFilter
.append( e
->text());
838 updateFilterTexture();
847 void PresentWindowsEffect::setHighlightedWindow( EffectWindow
* w
)
849 if( w
== mHighlightedWindow
|| ( w
!= NULL
&& !mWindowData
.contains( w
)))
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 )
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
);
869 grid
[ i
].resize( rows
);
870 for( DataHash::ConstIterator it
= mWindowData
.begin();
871 it
!= mWindowData
.end();
873 grid
[ it
->x
][ it
->y
] = it
.key();
874 int x
= mWindowData
[ w
].x
;
875 int y
= mWindowData
[ w
].y
;
882 { // make sure to find the leftmost (or 'w', which is guaranteed)
884 while( x
>= 0 && grid
[ x
][ y
] == NULL
)
891 if( grid
[ x
][ y
] != NULL
)
902 while( x
<= columns
- 1 && grid
[ x
][ y
] == NULL
)
909 if( grid
[ x
][ y
] != NULL
)
920 while( y
>= 0 && grid
[ x
][ y
] == NULL
)
927 if( grid
[ x
][ y
] != NULL
)
938 while( y
<= rows
- 1 && grid
[ x
][ y
] == NULL
)
945 if( grid
[ x
][ y
] != NULL
)
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();
960 if( (*it
).slot
< minslot
)
962 minslot
= (*it
).slot
;
969 void PresentWindowsEffect::discardFilterTexture()
971 #ifdef KWIN_HAVE_OPENGL_COMPOSITING
972 delete filterTexture
;
973 filterTexture
= NULL
;
977 void PresentWindowsEffect::updateFilterTexture()
979 #ifdef KWIN_HAVE_OPENGL_COMPOSITING
980 discardFilterTexture();
981 if( windowFilter
.isEmpty())
983 effects
->addRepaint( filterTextureRect
);
986 // Create font for filter text
988 font
.setPointSize( font
.pointSize() * 2 );
989 font
.setBold( true );
990 // Get size of the rect containing filter text
991 QFontMetrics
fm( font
);
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());
997 QImage
im( rect
.width(), rect
.height(), QImage::Format_ARGB32
);
998 im
.fill( Qt::transparent
);
999 // Paint the filter text to it
1002 p
.setPen( QPalette().color( QPalette::Active
, QPalette::HighlightedText
) );
1003 p
.drawText( rect
, Qt::AlignCenter
, translatedString
);
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
);
1014 effects
->addRepaint( filterTextureRect
);
1018 void PresentWindowsEffect::paintWindowIcon( EffectWindow
* w
, WindowPaintData
& paintdata
)
1020 // Don't render null icons
1021 if( w
->icon().isNull() )
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
);
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
);
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 );
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 ] =
1070 x
+ width
, y
+ height
,
1073 const float texcoords
[ 4 * 2 ] =
1080 renderGLGeometry( 4, verts
, texcoords
);
1081 data
.iconTexture
.unbind();
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
);
1098 #include "presentwindows.moc"