Make a branch to make krunner Good Enough For Aaron™.
[kdebase/uwolfer.git] / workspace / kwin / effects / desktopgrid.cpp
blob8bb5b99e9eb51b6362722985fe1f657362f8142f
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"
23 #include <math.h>
25 #include <kaction.h>
26 #include <kactioncollection.h>
27 #include <klocale.h>
28 #include <kconfiggroup.h>
29 #include <netwm_def.h>
30 #include <QEvent>
31 #include <QMouseEvent>
33 namespace KWin
36 KWIN_EFFECT( desktopgrid, DesktopGridEffect )
38 const int PROGRESS_TIME = 300; // ms
40 DesktopGridEffect::DesktopGridEffect()
41 : progress( 0 )
42 , activated( false )
43 , keyboard_grab( false )
44 , was_window_move( false )
45 , window_move( NULL )
46 , slide( 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 )
67 if( slide )
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
72 if( progress != 1 )
73 data.mask |= PAINT_SCREEN_TRANSFORMED | PAINT_SCREEN_BACKGROUND_FIRST;
74 else
76 slide = false;
77 progress = 0;
80 else if( progress != 0 || activated )
82 if( activated )
83 progress = qMin( 1.0, progress + time / double( PROGRESS_TIME ));
84 else
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
88 if( progress != 0 )
89 data.mask |= PAINT_SCREEN_TRANSFORMED | PAINT_SCREEN_BACKGROUND_FIRST;
90 if( !activated && progress == 0 )
91 finish();
93 effects->prePaintScreen( data, time );
96 void DesktopGridEffect::prePaintWindow( EffectWindow* w, WindowPrePaintData& data, int time )
98 if( slide )
100 if( w->isOnAllDesktops())
102 if( slide_painting_sticky )
103 data.setTransformed();
104 else
105 w->disablePainting( EffectWindow::PAINT_DISABLED_BY_DESKTOP );
107 else if( w->isOnDesktop( painting_desktop ))
108 w->enablePainting( EffectWindow::PAINT_DISABLED_BY_DESKTOP );
109 else
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 );
116 else
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 )
130 if( progress == 0 )
132 effects->paintScreen( mask, region, data );
133 return;
135 if( slide )
137 paintSlide( mask, region, data );
138 return;
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();
146 ++desktop )
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;
190 int w = 0;
191 int h = 0;
192 if( effects->optionRollOverDesktops())
194 int x, y;
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();
222 ++desktop )
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 )
253 if( slide )
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 )
265 int x, y;
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()
280 if( slide )
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
290 int x, y;
291 Qt::Orientation orientation;
292 effects->calcDesktopLayout( &x, &y, &orientation );
293 --desktop; // make it start with 0
294 QRect rect;
295 if( orientation == Qt::Horizontal )
296 rect = QRect(( desktop % x ) * displayWidth(), ( desktop / x ) * displayHeight(),
297 displayWidth(), displayHeight());
298 else
299 rect = QRect(( desktop / y ) * displayWidth(), ( desktop % y ) * displayHeight(),
300 displayWidth(), displayHeight());
301 if( !scaled )
302 return rect;
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 )));
308 return rect;
311 int DesktopGridEffect::posToDesktop( const QPoint& pos ) const
313 for( int desktop = 1; // TODO could be perhaps optimized
314 desktop <= effects->numberOfDesktops();
315 ++desktop )
317 if( desktopRect( desktop, true ).contains( pos ))
318 return desktop;
320 return 0;
323 QRect DesktopGridEffect::windowRect( EffectWindow* w ) const
325 int x, y;
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 ))
340 if( rect != NULL )
341 *rect = windowRect( window_move );
342 return window_move; // has special position and is on top
344 EffectWindowList windows = effects->stackingOrder();
345 // qReverse()
346 EffectWindowList::Iterator begin = windows.begin();
347 EffectWindowList::Iterator end = windows.end();
348 --end;
349 while( begin < end )
350 qSwap( *begin++, *end-- );
351 int x, y;
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 ))
363 if( rect != NULL )
364 *rect = r;
365 return w;
368 return NULL;
371 void DesktopGridEffect::desktopChanged( int old )
373 if( effects->activeFullScreenEffect() && effects->activeFullScreenEffect() != this )
374 return;
375 if( activated )
376 setActive( false );
377 else
378 slideDesktopChanged( old );
381 void DesktopGridEffect::slideDesktopChanged( int old )
383 if( !slideEnabled )
384 return;
385 if( slide ) // old slide still in progress
387 QPoint diffPos = desktopRect( old, false ).topLeft() - slide_start_pos;
388 int w = 0;
389 int h = 0;
390 if( effects->optionRollOverDesktops())
392 int x, y;
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());
422 else
423 progress = 1 - abs( currentPos.y() - rect.y()) / double( displayHeight());
425 else // current position is not on current desktop, do full progress
426 progress = 0;
427 diffPos = rect.topLeft() - currentPos;
428 if( progress <= 0 )
430 // Compute starting point for this new move (given current and end positions)
431 slide_start_pos = rect.topLeft() - diffPos * 1 / ( 1 - progress );
433 else
434 { // at the end, stop
435 slide = false;
436 progress = 0;
439 else
441 progress = 0;
442 slide_start_pos = desktopRect( old, false ).topLeft();
443 slide = true;
445 effects->addRepaintFull();
448 void DesktopGridEffect::toggle()
450 setActive( !activated );
453 void DesktopGridEffect::setActive( bool active )
455 if( effects->activeFullScreenEffect() && effects->activeFullScreenEffect() != this )
456 return;
457 if( activated == active )
458 return;
459 activated = active;
460 if( activated && progress == 0 )
461 setup();
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()
476 if( keyboard_grab )
477 effects->ungrabKeyboard();
478 keyboard_grab = false;
479 if( window_move != NULL )
480 effects->setElevatedWindow( window_move, false );
481 window_move = NULL;
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 )
492 return;
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 )
513 QRect rect;
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();
519 window_move = w;
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());
526 if( w != NULL )
528 if( w->isOnAllDesktops())
529 effects->windowToDesktop( w, posToDesktop( me->pos()));
530 else
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());
546 int x, y;
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 );
552 window_move = NULL;
555 if( !was_window_move && me->button() == Qt::LeftButton )
557 effects->setCurrentDesktop( posToDesktop( me->pos()));
558 setActive( false );
560 was_window_move = false;
564 void DesktopGridEffect::windowClosed( EffectWindow* w )
566 if( w == window_move )
568 effects->setElevatedWindow( window_move, false );
569 window_move = NULL;
573 void DesktopGridEffect::grabbedKeyboardEvent( QKeyEvent* e )
575 if( e->type() == QEvent::KeyPress )
577 int desktop = -1;
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;
583 if( desktop != -1 )
585 if( desktop <= effects->numberOfDesktops())
587 setHighlightedDesktop( desktop );
588 effects->setCurrentDesktop( desktop );
589 setActive( false );
591 return;
593 switch( e->key())
594 { // wrap only on autorepeat
595 case Qt::Key_Left:
596 setHighlightedDesktop( effects->desktopToLeft( highlighted_desktop,
597 !e->isAutoRepeat()));
598 break;
599 case Qt::Key_Right:
600 setHighlightedDesktop( effects->desktopToRight( highlighted_desktop,
601 !e->isAutoRepeat()));
602 break;
603 case Qt::Key_Up:
604 setHighlightedDesktop( effects->desktopUp( highlighted_desktop,
605 !e->isAutoRepeat()));
606 break;
607 case Qt::Key_Down:
608 setHighlightedDesktop( effects->desktopDown( highlighted_desktop,
609 !e->isAutoRepeat()));
610 break;
611 case Qt::Key_Escape:
612 setActive( false );
613 return;
614 case Qt::Key_Enter:
615 case Qt::Key_Return:
616 case Qt::Key_Space:
617 effects->setCurrentDesktop( highlighted_desktop );
618 setActive( false );
619 return;
620 default:
621 break;
626 void DesktopGridEffect::setHighlightedDesktop( int d )
628 if( d == highlighted_desktop || d <= 0 || d > effects->numberOfDesktops())
629 return;
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 )
638 return false;
639 if( border == borderActivate && !activated )
641 toggle();
642 return true;
644 return false;
647 } // namespace
649 #include "desktopgrid.moc"