1 /********************************************************************
2 KWin - the KDE window manager
3 This file is part of the KDE project.
5 Copyright (C) 2007 Lubos Lunak <l.lunak@kde.org>
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 "desktopgrid.h"
26 #include <kactioncollection.h>
28 #include <kconfiggroup.h>
29 #include <netwm_def.h>
31 #include <QMouseEvent>
36 KWIN_EFFECT( desktopgrid
, DesktopGridEffect
)
38 const int PROGRESS_TIME
= 300; // ms
40 DesktopGridEffect::DesktopGridEffect()
43 , keyboard_grab( false )
44 , was_window_move( false )
48 KActionCollection
* actionCollection
= new KActionCollection( this );
49 KAction
* a
= static_cast< KAction
* >( actionCollection
->addAction( "ShowDesktopGrid" ));
50 a
->setText( i18n("Show Desktop Grid" ));
51 a
->setGlobalShortcut( KShortcut( Qt::CTRL
+ Qt::Key_F8
));
52 connect( a
, SIGNAL( triggered( bool )), this, SLOT( toggle()));
53 KConfigGroup conf
= effects
->effectConfig("DesktopGrid");
54 slideEnabled
= conf
.readEntry( "Slide", true );
56 borderActivate
= (ElectricBorder
)conf
.readEntry("BorderActivate", (int)ElectricNone
);
57 effects
->reserveElectricBorder( borderActivate
);
60 DesktopGridEffect::~DesktopGridEffect()
62 effects
->unreserveElectricBorder( borderActivate
);
65 void DesktopGridEffect::prePaintScreen( ScreenPrePaintData
& data
, int time
)
69 progress
= qMin( 1.0, progress
+ time
/ double( PROGRESS_TIME
));
70 // PAINT_SCREEN_BACKGROUND_FIRST is needed because screen will be actually painted more than once,
71 // so with normal screen painting second screen paint would erase parts of the first paint
73 data
.mask
|= PAINT_SCREEN_TRANSFORMED
| PAINT_SCREEN_BACKGROUND_FIRST
;
80 else if( progress
!= 0 || activated
)
83 progress
= qMin( 1.0, progress
+ time
/ double( PROGRESS_TIME
));
85 progress
= qMax( 0.0, progress
- time
/ double( PROGRESS_TIME
));
86 // PAINT_SCREEN_BACKGROUND_FIRST is needed because screen will be actually painted more than once,
87 // so with normal screen painting second screen paint would erase parts of the first paint
89 data
.mask
|= PAINT_SCREEN_TRANSFORMED
| PAINT_SCREEN_BACKGROUND_FIRST
;
90 if( !activated
&& progress
== 0 )
93 effects
->prePaintScreen( data
, time
);
96 void DesktopGridEffect::prePaintWindow( EffectWindow
* w
, WindowPrePaintData
& data
, int time
)
100 if( w
->isOnAllDesktops())
102 if( slide_painting_sticky
)
103 data
.setTransformed();
105 w
->disablePainting( EffectWindow::PAINT_DISABLED_BY_DESKTOP
);
107 else if( w
->isOnDesktop( painting_desktop
))
108 w
->enablePainting( EffectWindow::PAINT_DISABLED_BY_DESKTOP
);
110 w
->disablePainting( EffectWindow::PAINT_DISABLED_BY_DESKTOP
);
112 else if( progress
!= 0 )
114 if( w
->isOnDesktop( painting_desktop
))
115 w
->enablePainting( EffectWindow::PAINT_DISABLED_BY_DESKTOP
);
117 w
->disablePainting( EffectWindow::PAINT_DISABLED_BY_DESKTOP
);
118 if( w
== window_move
)
120 data
.setTransformed();
121 if( w
->isOnAllDesktops() && painting_desktop
!= posToDesktop( window_move_pos
- window_move_diff
))
122 w
->disablePainting( EffectWindow::PAINT_DISABLED_BY_DESKTOP
);
125 effects
->prePaintWindow( w
, data
, time
);
128 void DesktopGridEffect::paintScreen( int mask
, QRegion region
, ScreenPaintData
& data
)
132 effects
->paintScreen( mask
, region
, data
);
137 paintSlide( mask
, region
, data
);
140 int desktop_with_move
= -1;
141 if( window_move
!= NULL
)
142 desktop_with_move
= window_move
->isOnAllDesktops()
143 ? posToDesktop( window_move_pos
- window_move_diff
) : window_move
->desktop();
144 for( int desktop
= 1;
145 desktop
<= effects
->numberOfDesktops();
148 if( desktop
!= desktop_with_move
)
150 PaintClipper
pc( desktopRect( desktop
, true ));
151 paintScreenDesktop( desktop
, mask
, region
, data
);
154 // paint the desktop with the window being moved as the last one, i.e. on top of others
155 if( desktop_with_move
!= -1 )
157 QRegion paintreg
= desktopRect( desktop_with_move
, true ); // paint only the desktop
158 paintreg
|= windowRect( window_move
); // and wherever the moved window is
159 PaintClipper
pc( paintreg
);
160 paintScreenDesktop( desktop_with_move
, mask
, region
, data
);
164 void DesktopGridEffect::paintScreenDesktop( int desktop
, int mask
, QRegion region
, ScreenPaintData data
)
166 QRect rect
= desktopRect( desktop
, true );
167 if( region
.contains( rect
)) // this desktop needs painting
169 painting_desktop
= desktop
;
170 ScreenPaintData d
= data
;
171 QRect normal
= desktopRect( effects
->currentDesktop(), false );
172 d
.xTranslate
+= rect
.x(); // - normal.x();
173 d
.yTranslate
+= rect
.y(); // - normal.y();
174 d
.xScale
*= rect
.width() / double( normal
.width());
175 d
.yScale
*= rect
.height() / double( normal
.height());
176 // TODO mask parts that are not visible?
177 effects
->paintScreen( mask
, region
, d
);
181 void DesktopGridEffect::paintSlide( int mask
, QRegion region
, const ScreenPaintData
& data
)
184 Transformations are done by remembering starting position of the change and the progress
185 of it, the destination is computed from the current desktop. Positions of desktops
186 are done using their topleft corner.
188 QPoint destPos
= desktopRect( effects
->currentDesktop(), false ).topLeft();
189 QPoint diffPos
= destPos
- slide_start_pos
;
192 if( effects
->optionRollOverDesktops())
195 Qt::Orientation orientation
;
196 effects
->calcDesktopLayout( &x
, &y
, &orientation
);
197 w
= x
* displayWidth();
198 h
= y
* displayHeight();
199 // wrap around if shorter
200 if( diffPos
.x() > 0 && diffPos
.x() > w
/ 2 )
201 diffPos
.setX( diffPos
.x() - w
);
202 if( diffPos
.x() < 0 && abs( diffPos
.x()) > w
/ 2 )
203 diffPos
.setX( diffPos
.x() + w
);
204 if( diffPos
.y() > 0 && diffPos
.y() > h
/ 2 )
205 diffPos
.setY( diffPos
.y() - h
);
206 if( diffPos
.y() < 0 && abs( diffPos
.y()) > h
/ 2 )
207 diffPos
.setY( diffPos
.y() + h
);
209 QPoint currentPos
= slide_start_pos
+ progress
* diffPos
;
210 QSize
displaySize( displayWidth(), displayHeight());
211 QRegion currentRegion
= QRect( currentPos
, displaySize
);
212 if( effects
->optionRollOverDesktops())
214 currentRegion
|= ( currentRegion
& QRect( -w
, 0, w
, h
)).translated( w
, 0 );
215 currentRegion
|= ( currentRegion
& QRect( 0, -h
, w
, h
)).translated( 0, h
);
216 currentRegion
|= ( currentRegion
& QRect( w
, 0, w
, h
)).translated( -w
, 0 );
217 currentRegion
|= ( currentRegion
& QRect( 0, h
, w
, h
)).translated( 0, -h
);
219 bool do_sticky
= true;
220 for( int desktop
= 1;
221 desktop
<= effects
->numberOfDesktops();
224 QRect rect
= desktopRect( desktop
, false );
225 if( currentRegion
.contains( rect
)) // part of the desktop needs painting
227 painting_desktop
= desktop
;
228 slide_painting_sticky
= do_sticky
;
229 slide_painting_diff
= rect
.topLeft() - currentPos
;
230 if( effects
->optionRollOverDesktops())
232 if( slide_painting_diff
.x() > displayWidth())
233 slide_painting_diff
.setX( slide_painting_diff
.x() - w
);
234 if( slide_painting_diff
.x() < -displayWidth())
235 slide_painting_diff
.setX( slide_painting_diff
.x() + w
);
236 if( slide_painting_diff
.y() > displayHeight())
237 slide_painting_diff
.setY( slide_painting_diff
.y() - h
);
238 if( slide_painting_diff
.y() < -displayHeight())
239 slide_painting_diff
.setY( slide_painting_diff
.y() + h
);
241 do_sticky
= false; // paint on-all-desktop windows only once
242 ScreenPaintData d
= data
;
243 d
.xTranslate
+= slide_painting_diff
.x();
244 d
.yTranslate
+= slide_painting_diff
.y();
245 // TODO mask parts that are not visible?
246 effects
->paintScreen( mask
, region
, d
);
251 void DesktopGridEffect::paintWindow( EffectWindow
* w
, int mask
, QRegion region
, WindowPaintData
& data
)
254 { // don't move windows on all desktops (compensate screen transformation)
255 if( w
->isOnAllDesktops()) // TODO also fix 'Workspace::movingClient'
257 data
.xTranslate
-= slide_painting_diff
.x();
258 data
.yTranslate
-= slide_painting_diff
.y();
261 else if( progress
!= 0 )
263 if( w
== window_move
)
266 Qt::Orientation orientation
;
267 effects
->calcDesktopLayout( &x
, &y
, &orientation
);
268 QRect desktop
= desktopRect( painting_desktop
, false );
269 data
.xTranslate
+= window_move_pos
.x() * x
- ( desktop
.x() + w
->x());
270 data
.yTranslate
+= window_move_pos
.y() * y
- ( desktop
.y() + w
->y());
272 else if( painting_desktop
!= highlighted_desktop
)
273 data
.brightness
*= 0.7;
275 effects
->paintWindow( w
, mask
, region
, data
);
278 void DesktopGridEffect::postPaintScreen()
281 effects
->addRepaintFull();
282 if( activated
? progress
!= 1 : progress
!= 0 )
283 effects
->addRepaintFull(); // trigger next animation repaint
284 effects
->postPaintScreen();
287 // Gives a position of the given desktop when all desktops are arranged in a grid
288 QRect
DesktopGridEffect::desktopRect( int desktop
, bool scaled
) const
291 Qt::Orientation orientation
;
292 effects
->calcDesktopLayout( &x
, &y
, &orientation
);
293 --desktop
; // make it start with 0
295 if( orientation
== Qt::Horizontal
)
296 rect
= QRect(( desktop
% x
) * displayWidth(), ( desktop
/ x
) * displayHeight(),
297 displayWidth(), displayHeight());
299 rect
= QRect(( desktop
/ y
) * displayWidth(), ( desktop
% y
) * displayHeight(),
300 displayWidth(), displayHeight());
303 QRect current
= desktopRect( effects
->currentDesktop(), false );
304 rect
= QRect( qRound( interpolate( rect
.x() - current
.x(), rect
.x() / double( x
), progress
)),
305 qRound( interpolate( rect
.y() - current
.y(), rect
.y() / double( y
), progress
)),
306 qRound( interpolate( rect
.width(), displayWidth() / double( x
), progress
)),
307 qRound( interpolate( rect
.height(), displayHeight() / double( y
), progress
)));
311 int DesktopGridEffect::posToDesktop( const QPoint
& pos
) const
313 for( int desktop
= 1; // TODO could be perhaps optimized
314 desktop
<= effects
->numberOfDesktops();
317 if( desktopRect( desktop
, true ).contains( pos
))
323 QRect
DesktopGridEffect::windowRect( EffectWindow
* w
) const
326 Qt::Orientation orientation
;
327 effects
->calcDesktopLayout( &x
, &y
, &orientation
);
328 if( w
== window_move
) // it's being moved, return moved position
329 return QRect( window_move_pos
, QSize( w
->width() / x
, w
->height() / y
));
330 QRect desktop
= desktopRect( w
->isOnCurrentDesktop()
331 ? effects
->currentDesktop() : w
->desktop(), true );
332 return QRect( desktop
.x() + w
->x() / x
, desktop
.y() + w
->y() / y
,
333 w
->width() / x
, w
->height() / y
);
336 EffectWindow
* DesktopGridEffect::windowAt( const QPoint
& pos
, QRect
* rect
) const
338 if( window_move
!= NULL
&& windowRect( window_move
).contains( pos
))
341 *rect
= windowRect( window_move
);
342 return window_move
; // has special position and is on top
344 EffectWindowList windows
= effects
->stackingOrder();
346 EffectWindowList::Iterator begin
= windows
.begin();
347 EffectWindowList::Iterator end
= windows
.end();
350 qSwap( *begin
++, *end
-- );
352 Qt::Orientation orientation
;
353 effects
->calcDesktopLayout( &x
, &y
, &orientation
);
354 foreach( EffectWindow
* w
, windows
)
356 // don't use windowRect(), take special care of on-all-desktop windows
357 QRect desktop
= desktopRect( w
->isOnAllDesktops()
358 ? posToDesktop( pos
) : w
->desktop(), true );
359 QRect
r( desktop
.x() + w
->x() / x
, desktop
.y() + w
->y() / y
,
360 w
->width() / x
, w
->height() / y
);
361 if( r
.contains( pos
))
371 void DesktopGridEffect::desktopChanged( int old
)
373 if( effects
->activeFullScreenEffect() && effects
->activeFullScreenEffect() != this )
378 slideDesktopChanged( old
);
381 void DesktopGridEffect::slideDesktopChanged( int old
)
385 if( slide
) // old slide still in progress
387 QPoint diffPos
= desktopRect( old
, false ).topLeft() - slide_start_pos
;
390 if( effects
->optionRollOverDesktops())
393 Qt::Orientation orientation
;
394 effects
->calcDesktopLayout( &x
, &y
, &orientation
);
395 w
= x
* displayWidth();
396 h
= y
* displayHeight();
397 // wrap around if shorter
398 if( diffPos
.x() > 0 && diffPos
.x() > w
/ 2 )
399 diffPos
.setX( diffPos
.x() - w
);
400 if( diffPos
.x() < 0 && abs( diffPos
.x()) > w
/ 2 )
401 diffPos
.setX( diffPos
.x() + w
);
402 if( diffPos
.y() > 0 && diffPos
.y() > h
/ 2 )
403 diffPos
.setY( diffPos
.y() - h
);
404 if( diffPos
.y() < 0 && abs( diffPos
.y()) > h
/ 2 )
405 diffPos
.setY( diffPos
.y() + h
);
407 QPoint currentPos
= slide_start_pos
+ progress
* diffPos
;
408 QRegion currentRegion
= QRect( currentPos
, QSize( displayWidth(), displayHeight()));
409 if( effects
->optionRollOverDesktops())
411 currentRegion
|= ( currentRegion
& QRect( -w
, 0, w
, h
)).translated( w
, 0 );
412 currentRegion
|= ( currentRegion
& QRect( 0, -h
, w
, h
)).translated( 0, h
);
413 currentRegion
|= ( currentRegion
& QRect( w
, 0, w
, h
)).translated( -w
, 0 );
414 currentRegion
|= ( currentRegion
& QRect( 0, h
, w
, h
)).translated( 0, -h
);
416 QRect rect
= desktopRect( effects
->currentDesktop(), false );
417 if( currentRegion
.contains( rect
))
418 { // current position is in new current desktop (e.g. quickly changing back),
419 // don't do full progress
420 if( abs( currentPos
.x() - rect
.x()) > abs( currentPos
.y() - rect
.y()))
421 progress
= 1 - abs( currentPos
.x() - rect
.x()) / double( displayWidth());
423 progress
= 1 - abs( currentPos
.y() - rect
.y()) / double( displayHeight());
425 else // current position is not on current desktop, do full progress
427 diffPos
= rect
.topLeft() - currentPos
;
430 // Compute starting point for this new move (given current and end positions)
431 slide_start_pos
= rect
.topLeft() - diffPos
* 1 / ( 1 - progress
);
434 { // at the end, stop
442 slide_start_pos
= desktopRect( old
, false ).topLeft();
445 effects
->addRepaintFull();
448 void DesktopGridEffect::toggle()
450 setActive( !activated
);
453 void DesktopGridEffect::setActive( bool active
)
455 if( effects
->activeFullScreenEffect() && effects
->activeFullScreenEffect() != this )
457 if( activated
== active
)
460 if( activated
&& progress
== 0 )
462 effects
->addRepaintFull();
465 void DesktopGridEffect::setup()
467 keyboard_grab
= effects
->grabKeyboard( this );
468 input
= effects
->createInputWindow( this, 0, 0, displayWidth(), displayHeight(),
469 Qt::PointingHandCursor
);
470 effects
->setActiveFullScreenEffect( this );
471 setHighlightedDesktop( effects
->currentDesktop());
474 void DesktopGridEffect::finish()
477 effects
->ungrabKeyboard();
478 keyboard_grab
= false;
479 if( window_move
!= NULL
)
480 effects
->setElevatedWindow( window_move
, false );
482 effects
->destroyInputWindow( input
);
483 effects
->setActiveFullScreenEffect( 0 );
484 effects
->addRepaintFull(); // to get rid of highlight
487 void DesktopGridEffect::windowInputMouseEvent( Window
, QEvent
* e
)
489 if( e
->type() != QEvent::MouseMove
490 && e
->type() != QEvent::MouseButtonPress
491 && e
->type() != QEvent::MouseButtonRelease
)
493 QMouseEvent
* me
= static_cast< QMouseEvent
* >( e
);
494 if( e
->type() == QEvent::MouseMove
)
496 // highlight desktop under mouse
497 int d
= posToDesktop( me
->pos());
498 if( d
!= highlighted_desktop
)
499 setHighlightedDesktop( d
);
500 if( window_move
!= NULL
) // handle window moving
502 was_window_move
= true;
503 // windowRect() handles window_move specially
504 effects
->addRepaint( windowRect( window_move
));
505 window_move_pos
= me
->pos() + window_move_diff
;
506 effects
->addRepaint( windowRect( window_move
));
509 if( e
->type() == QEvent::MouseButtonPress
)
511 if( me
->buttons() == Qt::LeftButton
)
514 EffectWindow
* w
= windowAt( me
->pos(), &rect
);
515 if( w
!= NULL
&& w
->isMovable())
516 { // prepare it for moving
517 window_move_pos
= rect
.topLeft();
518 window_move_diff
= window_move_pos
- me
->pos();
520 effects
->setElevatedWindow( window_move
, true );
523 else if(( me
->buttons() == Qt::MidButton
|| me
->buttons() == Qt::RightButton
) && window_move
== NULL
)
525 EffectWindow
* w
= windowAt( me
->pos());
528 if( w
->isOnAllDesktops())
529 effects
->windowToDesktop( w
, posToDesktop( me
->pos()));
531 effects
->windowToDesktop( w
, NET::OnAllDesktops
);
532 effects
->addRepaintFull();
536 if( e
->type() == QEvent::MouseButtonRelease
&& me
->buttons() == 0 )
538 if( was_window_move
)
540 if( window_move
!= NULL
)
542 QRect rect
= windowRect( window_move
);
543 int desktop
= posToDesktop( rect
.center());
544 // to desktop's coordinates
545 rect
.translate( -desktopRect( desktop
, true ).topLeft());
547 Qt::Orientation orientation
;
548 effects
->calcDesktopLayout( &x
, &y
, &orientation
);
549 effects
->moveWindow( window_move
, QPoint( rect
.x() * x
, rect
.y() * y
));
550 effects
->windowToDesktop( window_move
, desktop
);
551 effects
->setElevatedWindow( window_move
, false );
555 if( !was_window_move
&& me
->button() == Qt::LeftButton
)
557 effects
->setCurrentDesktop( posToDesktop( me
->pos()));
560 was_window_move
= false;
564 void DesktopGridEffect::windowClosed( EffectWindow
* w
)
566 if( w
== window_move
)
568 effects
->setElevatedWindow( window_move
, false );
573 void DesktopGridEffect::grabbedKeyboardEvent( QKeyEvent
* e
)
575 if( e
->type() == QEvent::KeyPress
)
578 // switch by F<number> or just <number>
579 if( e
->key() >= Qt::Key_F1
&& e
->key() <= Qt::Key_F35
)
580 desktop
= e
->key() - Qt::Key_F1
+ 1;
581 else if( e
->key() >= Qt::Key_0
&& e
->key() <= Qt::Key_9
)
582 desktop
= e
->key() == Qt::Key_0
? 10 : e
->key() - Qt::Key_0
;
585 if( desktop
<= effects
->numberOfDesktops())
587 setHighlightedDesktop( desktop
);
588 effects
->setCurrentDesktop( desktop
);
594 { // wrap only on autorepeat
596 setHighlightedDesktop( effects
->desktopToLeft( highlighted_desktop
,
597 !e
->isAutoRepeat()));
600 setHighlightedDesktop( effects
->desktopToRight( highlighted_desktop
,
601 !e
->isAutoRepeat()));
604 setHighlightedDesktop( effects
->desktopUp( highlighted_desktop
,
605 !e
->isAutoRepeat()));
608 setHighlightedDesktop( effects
->desktopDown( highlighted_desktop
,
609 !e
->isAutoRepeat()));
617 effects
->setCurrentDesktop( highlighted_desktop
);
626 void DesktopGridEffect::setHighlightedDesktop( int d
)
628 if( d
== highlighted_desktop
|| d
<= 0 || d
> effects
->numberOfDesktops())
630 effects
->addRepaint( desktopRect( highlighted_desktop
, true ));
631 highlighted_desktop
= d
;
632 effects
->addRepaint( desktopRect( highlighted_desktop
, true ));
635 bool DesktopGridEffect::borderActivated( ElectricBorder border
)
637 if( effects
->activeFullScreenEffect() && effects
->activeFullScreenEffect() != this )
639 if( border
== borderActivate
&& !activated
)
649 #include "desktopgrid.moc"