You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
495 lines
13 KiB
495 lines
13 KiB
/******************************************************************** |
|
KWin - the KDE window manager |
|
This file is part of the KDE project. |
|
|
|
Copyright (C) 2009 Nikhil Marathe <nsm.nikhil@gmail.com> |
|
|
|
This program is free software; you can redistribute it and/or modify |
|
it under the terms of the GNU General Public License as published by |
|
the Free Software Foundation; either version 2 of the License, or |
|
(at your option) any later version. |
|
|
|
This program is distributed in the hope that it will be useful, |
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
GNU General Public License for more details. |
|
|
|
You should have received a copy of the GNU General Public License |
|
along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
*********************************************************************/ |
|
|
|
// all the tiling related code that is extensions to existing KWin classes |
|
// Includes Workspace for now |
|
|
|
#include "client.h" |
|
#include "workspace.h" |
|
#include "tile.h" |
|
#include "tilinglayout.h" |
|
#include "tilinglayoutfactory.h" |
|
|
|
#include <knotification.h> |
|
#include <klocale.h> |
|
#include <kwindowinfo.h> |
|
#include <kwindowsystem.h> |
|
#include "lib/kdecoration.h" |
|
|
|
|
|
namespace KWin |
|
{ |
|
|
|
bool Workspace::tilingEnabled() const |
|
{ |
|
return tilingEnabled_; |
|
} |
|
|
|
void Workspace::setTilingEnabled( bool tiling ) |
|
{ |
|
if( tilingEnabled() == tiling ) return; |
|
|
|
tilingEnabled_ = tiling; |
|
|
|
KSharedConfig::Ptr _config = KGlobal::config(); |
|
KConfigGroup config( _config, "Windows" ); |
|
config.writeEntry("TilingOn", tilingEnabled_); |
|
config.sync(); |
|
options->tilingOn = tilingEnabled_; |
|
options->tilingLayout = static_cast<TilingLayoutFactory::Layouts>(config.readEntry( "TilingDefaultLayout", 0 )); |
|
options->tilingRaisePolicy = config.readEntry( "TilingRaisePolicy", 0 ); |
|
|
|
if( tilingEnabled_ ) |
|
{ |
|
tilingLayouts.resize( numberOfDesktops() + 1 ); |
|
foreach( Client *c, stackingOrder() ) |
|
{ |
|
createTile( c ); |
|
} |
|
} |
|
else |
|
{ |
|
qDeleteAll( tilingLayouts ); |
|
tilingLayouts.clear(); |
|
} |
|
} |
|
|
|
void Workspace::slotToggleTiling() |
|
{ |
|
if ( tilingEnabled() ) |
|
{ |
|
setTilingEnabled( false ); |
|
QString message = i18n( "Tiling Disabled" ); |
|
KNotification::event( "tilingdisabled", message, QPixmap(), NULL, KNotification::CloseOnTimeout, KComponentData( "kwin" ) ); |
|
} |
|
else |
|
{ |
|
setTilingEnabled( true ); |
|
QString message = i18n( "Tiling Enabled" ); |
|
KNotification::event( "tilingenabled", message, QPixmap(), NULL, KNotification::CloseOnTimeout, KComponentData( "kwin" ) ); |
|
} |
|
} |
|
|
|
void Workspace::createTile( Client *c ) |
|
{ |
|
if( c == NULL ) |
|
return; |
|
|
|
if( c->desktop() < 0 || c->desktop() >= tilingLayouts.size() ) return; |
|
|
|
kDebug(1212) << "Now tiling " << c->caption(); |
|
if( !tilingEnabled() || !tileable(c) ) |
|
return; |
|
|
|
Tile *t = new Tile( c, clientArea( PlacementArea, c ) ); |
|
if( !tileable( c ) ) |
|
{ |
|
kDebug(1212) << c->caption() << "is not tileable"; |
|
t->floatTile(); |
|
} |
|
|
|
if( !tilingLayouts.value(c->desktop()) ) |
|
{ |
|
tilingLayouts[c->desktop()] = TilingLayoutFactory::createLayout( TilingLayoutFactory::DefaultLayout, this ); |
|
} |
|
tilingLayouts[c->desktop()]->addTile( t ); |
|
tilingLayouts[c->desktop()]->commit(); |
|
} |
|
|
|
void Workspace::removeTile( Client *c ) |
|
{ |
|
if( tilingLayouts[ c->desktop() ] ) |
|
tilingLayouts[ c->desktop() ]->removeTile( c ); |
|
} |
|
|
|
bool Workspace::tileable( Client *c ) |
|
{ |
|
|
|
kDebug(1212) << c->caption(); |
|
KWindowInfo info = KWindowSystem::windowInfo( c->window(), -1U, NET::WM2WindowClass ); |
|
kDebug(1212) << "WINDOW CLASS IS "<< info.windowClassClass(); |
|
if( info.windowClassClass() == "Plasma-desktop" ) |
|
{ |
|
return false; |
|
} |
|
// TODO: if application specific settings |
|
// to ignore, put them here |
|
|
|
if( !c->isNormalWindow() ) |
|
{ |
|
return false; |
|
} |
|
|
|
// 0 means tile it, if we get 1 (floating), don't tile |
|
if( c->rules()->checkTilingOption( 0 ) == 1 ) |
|
{ |
|
return false; |
|
} |
|
|
|
kDebug() << "Tiling" << c; |
|
return true; |
|
} |
|
|
|
void Workspace::belowCursor() |
|
{ |
|
// TODO |
|
} |
|
|
|
Tile* Workspace::getNiceTile() const |
|
{ |
|
if( !tilingEnabled() ) return NULL; |
|
if( !tilingLayouts.value( activeClient()->desktop() ) ) return NULL; |
|
|
|
return tilingLayouts[ activeClient()->desktop() ]->findTile( activeClient() ); |
|
// TODO |
|
} |
|
|
|
void Workspace::updateAllTiles() |
|
{ |
|
foreach( TilingLayout *t, tilingLayouts ) |
|
{ |
|
if( !t ) continue; |
|
t->commit(); |
|
} |
|
} |
|
|
|
/* |
|
* Resize the neighbouring clients to close any gaps |
|
*/ |
|
void Workspace::notifyTilingWindowResize( Client *c, const QRect &moveResizeGeom, const QRect &orig ) |
|
{ |
|
if( tilingLayouts.value( c->desktop() ) == NULL ) |
|
return; |
|
tilingLayouts[ c->desktop() ]->clientResized( c, moveResizeGeom, orig ); |
|
} |
|
|
|
void Workspace::notifyTilingWindowMove( Client *c, const QRect &moveResizeGeom, const QRect &orig ) |
|
{ |
|
if( tilingLayouts.value( c->desktop() ) == NULL ) |
|
{ |
|
return; |
|
} |
|
tilingLayouts[ c->desktop() ]->clientMoved( c, moveResizeGeom, orig ); |
|
updateAllTiles(); |
|
} |
|
|
|
void Workspace::notifyTilingWindowResizeDone( Client *c, const QRect &moveResizeGeom, const QRect &orig, bool canceled ) |
|
{ |
|
if( canceled ) |
|
notifyTilingWindowResize( c, orig, moveResizeGeom ); |
|
else |
|
notifyTilingWindowResize( c, moveResizeGeom, orig ); |
|
} |
|
|
|
void Workspace::notifyTilingWindowMoveDone( Client *c, const QRect &moveResizeGeom, const QRect &orig, bool canceled ) |
|
{ |
|
if( canceled ) |
|
notifyTilingWindowMove( c, orig, moveResizeGeom ); |
|
else |
|
notifyTilingWindowMove( c, moveResizeGeom, orig ); |
|
} |
|
|
|
void Workspace::notifyTilingWindowDesktopChanged( Client *c, int old_desktop ) |
|
{ |
|
if( c->desktop() < 1 || c->desktop() > numberOfDesktops() ) |
|
return; |
|
|
|
if( tilingLayouts.value( old_desktop ) ) |
|
{ |
|
Tile *t = tilingLayouts[ old_desktop ]->findTile( c ); |
|
|
|
// TODO: copied from createTile(), move this into separate method? |
|
if( !tilingLayouts.value( c->desktop() ) ) |
|
{ |
|
tilingLayouts[c->desktop()] = TilingLayoutFactory::createLayout( TilingLayoutFactory::DefaultLayout, this ); |
|
} |
|
|
|
if( t ) |
|
tilingLayouts[ c->desktop() ]->addTile( t ); |
|
|
|
tilingLayouts[ old_desktop ]->removeTile( c ); |
|
tilingLayouts[ old_desktop ]->commit(); |
|
} |
|
} |
|
|
|
/* |
|
* Implements the 3 raising modes in Window Behaviour -> Advanced |
|
*/ |
|
void Workspace::notifyTilingWindowActivated( Client *c ) |
|
{ |
|
if( c == NULL ) |
|
return; |
|
|
|
if( options->tilingRaisePolicy == 1 ) // individual raise/lowers |
|
return; |
|
|
|
if( tilingLayouts.value( c->desktop() ) ) |
|
{ |
|
QList<Tile *> tiles = tilingLayouts[ c->desktop() ]->tiles(); |
|
|
|
StackingUpdatesBlocker blocker( this ); |
|
|
|
Tile *tile_to_raise = tilingLayouts[ c->desktop() ]->findTile( c ); |
|
|
|
if( !tile_to_raise ) |
|
{ |
|
return; |
|
} |
|
|
|
kDebug(1212) << "FOUND TILE"; |
|
bool raise_floating = false; |
|
if( options->tilingRaisePolicy == 2 ) // floating always on top |
|
raise_floating = true; |
|
else |
|
raise_floating = tile_to_raise->floating(); |
|
|
|
foreach( Tile *t, tiles ) |
|
{ |
|
if( t->floating() == raise_floating && t != tile_to_raise ) |
|
raiseClient( t->client() ); |
|
} |
|
// raise the current tile last so that it ends up on top |
|
// but only if it supposed to be raised, required to support tilingRaisePolicy |
|
kDebug(1212) << "Raise floating? " << raise_floating << "to raise is floating?" << tile_to_raise->floating(); |
|
if( tile_to_raise->floating() == raise_floating ) |
|
raiseClient( tile_to_raise->client() ); |
|
} |
|
} |
|
|
|
void Workspace::notifyTilingWindowMinimizeToggled( Client *c ) |
|
{ |
|
if( tilingLayouts.value( c->desktop() ) ) |
|
{ |
|
tilingLayouts[ c->desktop() ]->clientMinimizeToggled( c ); |
|
} |
|
} |
|
|
|
void Workspace::notifyTilingWindowMaximized( Client *c, Options::WindowOperation op ) |
|
{ |
|
if( tilingLayouts.value( c->desktop() ) ) |
|
{ |
|
Tile *t = tilingLayouts[ c->desktop() ]->findTile( c ); |
|
if( !t ) |
|
{ |
|
createTile( c ); |
|
t = tilingLayouts[ c->desktop() ]->findTile( c ); |
|
|
|
// if still no tile, it couldn't be tiled |
|
// so ignore it |
|
if( !t ) |
|
return; |
|
} |
|
|
|
// if window IS tiled and a maximize |
|
// is attempted, make the window float. |
|
// That is all we do since that can |
|
// mess up the layout. |
|
// In all other cases, don't do |
|
// anything, let the user manage toggling |
|
// using Meta+F |
|
if ( !t->floating() |
|
&& ( op == Options::MaximizeOp |
|
|| op == Options::HMaximizeOp |
|
|| op == Options::VMaximizeOp ) ) |
|
{ |
|
tilingLayouts[ c->desktop() ]->toggleFloatTile( c ); |
|
} |
|
|
|
} |
|
} |
|
|
|
Tile* Workspace::findAdjacentTile( Tile *ref, int d ) |
|
{ |
|
QRect reference = ref->geometry(); |
|
QPoint origin = reference.center(); |
|
|
|
Tile *closest = NULL; |
|
int minDist = -1; |
|
|
|
QList<Tile *> tiles = tilingLayouts[ ref->client()->desktop() ]->tiles(); |
|
|
|
foreach( Tile *t, tiles ) |
|
{ |
|
if( t->client() == ref->client() || t->ignoreGeometry() ) |
|
continue; |
|
|
|
bool consider = false; |
|
|
|
QRect other = t->geometry(); |
|
QPoint otherCenter = other.center(); |
|
|
|
switch( d ) |
|
{ |
|
case Tile::Top: |
|
consider = otherCenter.y() < origin.y() |
|
&& other.bottom() < reference.top(); |
|
break; |
|
|
|
case Tile::Right: |
|
consider = otherCenter.x() > origin.x() |
|
&& other.left() > reference.right(); |
|
break; |
|
|
|
case Tile::Bottom: |
|
consider = otherCenter.y() > origin.y() |
|
&& other.top() > reference.bottom(); |
|
break; |
|
|
|
case Tile::Left: |
|
consider = otherCenter.x() < origin.x() |
|
&& other.right() < reference.left(); |
|
break; |
|
|
|
default: |
|
abort(); |
|
} |
|
|
|
if( consider ) |
|
{ |
|
int dist = ( otherCenter - origin ).manhattanLength(); |
|
if( minDist > dist || minDist < 0 ) |
|
{ |
|
minDist = dist; |
|
closest = t; |
|
} |
|
} |
|
} |
|
return closest; |
|
} |
|
|
|
void Workspace::focusTile( int d ) |
|
{ |
|
Tile *t = getNiceTile(); |
|
if( t ) |
|
{ |
|
Tile *adj = findAdjacentTile(t, d); |
|
if( adj ) |
|
activateClient( adj->client() ); |
|
} |
|
} |
|
|
|
void Workspace::moveTile( int d ) |
|
{ |
|
Tile *t = getNiceTile(); |
|
if( t ) |
|
{ |
|
Tile* adj = findAdjacentTile( t, d ); |
|
|
|
tilingLayouts[ t->client()->desktop() ]->swapTiles( t, adj ); |
|
} |
|
} |
|
|
|
void Workspace::slotFocusTileLeft() |
|
{ |
|
focusTile( Tile::Left ); |
|
} |
|
|
|
void Workspace::slotFocusTileRight() |
|
{ |
|
focusTile( Tile::Right ); |
|
} |
|
|
|
void Workspace::slotFocusTileTop() |
|
{ |
|
focusTile( Tile::Top ); |
|
} |
|
|
|
void Workspace::slotFocusTileBottom() |
|
{ |
|
focusTile( Tile::Bottom ); |
|
} |
|
|
|
void Workspace::slotMoveTileLeft() |
|
{ |
|
moveTile( Tile::Left ); |
|
} |
|
|
|
void Workspace::slotMoveTileRight() |
|
{ |
|
moveTile( Tile::Right ); |
|
} |
|
|
|
void Workspace::slotMoveTileTop() |
|
{ |
|
moveTile( Tile::Top ); |
|
} |
|
|
|
void Workspace::slotMoveTileBottom() |
|
{ |
|
moveTile( Tile::Bottom ); |
|
} |
|
|
|
void Workspace::slotToggleFloating() |
|
{ |
|
Client *c = activeClient(); |
|
if( tilingLayouts.value( c->desktop() ) ) |
|
{ |
|
tilingLayouts[ c->desktop() ]->toggleFloatTile( c ); |
|
} |
|
} |
|
|
|
void Workspace::slotNextTileLayout() |
|
{ |
|
if( tilingLayouts.value( currentDesktop() ) ) |
|
{ |
|
|
|
tilingLayouts.replace( currentDesktop(), TilingLayoutFactory::nextLayout( tilingLayouts[currentDesktop()] ) ); |
|
|
|
tilingLayouts[currentDesktop()]->commit(); |
|
} |
|
} |
|
|
|
void Workspace::slotPreviousTileLayout() |
|
{ |
|
if( tilingLayouts.value( currentDesktop() ) ) |
|
{ |
|
|
|
tilingLayouts.replace( currentDesktop(), TilingLayoutFactory::previousLayout( tilingLayouts[currentDesktop()] ) ); |
|
|
|
tilingLayouts[currentDesktop()]->commit(); |
|
} |
|
} |
|
|
|
KDecorationDefines::Position Workspace::supportedTilingResizeMode( Client *c, KDecorationDefines::Position currentMode ) |
|
{ |
|
if( tilingLayouts.value( c->desktop() ) ) |
|
{ |
|
return tilingLayouts[c->desktop()]->resizeMode( c, currentMode ); |
|
} |
|
return currentMode; |
|
} |
|
|
|
void Workspace::dumpTiles() const |
|
{ |
|
foreach( TilingLayout *t, tilingLayouts ) |
|
{ |
|
if( !t ) { |
|
kDebug(1212) << "EMPTY DESKTOP"; |
|
continue; |
|
} |
|
kDebug(1212) << "Desktop" << tilingLayouts.indexOf( t ); |
|
foreach( Tile *tile, t->tiles() ) |
|
{ |
|
tile->dumpTile( "--" ); |
|
} |
|
} |
|
} |
|
} // namespace |
|
|
|
|