From 5fc7e93d69a8be790e8092af7724c2eaaa9a8a67 Mon Sep 17 00:00:00 2001 From: Nikhil Marathe Date: Sun, 25 Apr 2010 16:43:14 +0000 Subject: [PATCH] Tiling is here! This commit merges the kwin-tiling branch. Ideally it shouldn't break anything and add a few features ;-) It was applied as a patch. Do not attempt to merge the branch directly, it has a few issues. This feature is currently experimental, although it hasn't crashed in quite a long time. It lacks some features and probably leaks some memory. Fixes will be on the way. Season Of KDE 2009 project by Nikhil Marathe svn path=/trunk/KDE/kdebase/workspace/; revision=1118677 --- CMakeLists.txt | 17 + activation.cpp | 3 + client.cpp | 7 + geometry.cpp | 66 +- kcmkwin/kwinoptions/windows.cpp | 80 ++ kcmkwin/kwinoptions/windows.h | 15 + kcmkwin/kwinrules/ruleswidget.cpp | 15 + kcmkwin/kwinrules/ruleswidget.h | 3 + kcmkwin/kwinrules/ruleswidgetbase.ui | 1290 ++++++++++++++------------ kwin.notifyrc | 15 + kwinbindings.cpp | 22 + options.cpp | 4 + options.h | 15 + org.kde.KWin.xml | 4 + rules.cpp | 7 + rules.h | 4 + sm.cpp | 14 + tile.cpp | 152 +++ tile.h | 125 +++ tiling.cpp | 504 ++++++++++ tilinglayout.cpp | 218 +++++ tilinglayout.h | 118 +++ tilinglayoutfactory.cpp | 126 +++ tilinglayoutfactory.h | 71 ++ tilinglayouts/columns/columns.cpp | 162 ++++ tilinglayouts/columns/columns.h | 49 + tilinglayouts/floating/floating.cpp | 61 ++ tilinglayouts/floating/floating.h | 59 ++ tilinglayouts/spiral/spiral.cpp | 82 ++ tilinglayouts/spiral/spiral.h | 49 + useractions.cpp | 9 + workspace.cpp | 33 + workspace.h | 77 ++ 33 files changed, 2883 insertions(+), 593 deletions(-) create mode 100644 tile.cpp create mode 100644 tile.h create mode 100644 tiling.cpp create mode 100644 tilinglayout.cpp create mode 100644 tilinglayout.h create mode 100644 tilinglayoutfactory.cpp create mode 100644 tilinglayoutfactory.h create mode 100644 tilinglayouts/columns/columns.cpp create mode 100644 tilinglayouts/columns/columns.h create mode 100644 tilinglayouts/floating/floating.cpp create mode 100644 tilinglayouts/floating/floating.h create mode 100644 tilinglayouts/spiral/spiral.cpp create mode 100644 tilinglayouts/spiral/spiral.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 0dc61dfc5e..78ba4141cd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -105,8 +105,25 @@ set(kwin_KDEINIT_SRCS compositingprefs.cpp desktoplayout.cpp paintredirector.cpp + tile.cpp + tiling.cpp + tilinglayout.cpp + tilinglayoutfactory.cpp + + # tiling layouts + # spiral + #tilinglayouts/spiral/spiralfactory.cpp + tilinglayouts/spiral/spiral.cpp + + # columns + #tilinglayouts/columns/columnsfactory.cpp + tilinglayouts/columns/columns.cpp + + # floating + tilinglayouts/floating/floating.cpp ) +add_subdirectory( tilinglayouts ) qt4_add_dbus_adaptor( kwin_KDEINIT_SRCS org.kde.KWin.xml workspace.h KWin::Workspace ) diff --git a/activation.cpp b/activation.cpp index 5b5edb7392..bea853d570 100644 --- a/activation.cpp +++ b/activation.cpp @@ -259,6 +259,9 @@ void Workspace::setActiveClient( Client* c, allowed_t ) updateColormap(); if( effects ) static_cast(effects)->windowActivated( active_client ? active_client->effectWindow() : NULL ); + + if( tilingMode() ) + notifyWindowActivated( active_client ); --set_active_client_recursion; } diff --git a/client.cpp b/client.cpp index 982ac9b202..70f851d635 100644 --- a/client.cpp +++ b/client.cpp @@ -895,6 +895,9 @@ void Client::minimize( bool avoid_animation ) if( effects && !avoid_animation ) // TODO: Shouldn't it tell effects at least about the change? static_cast(effects)->windowMinimized( effectWindow()); + // when tiling, request a rearrangement + workspace()->notifyWindowMinimizeToggled( this ); + // Update states of all other windows in this group if( clientGroup() ) clientGroup()->updateStates( this ); @@ -911,9 +914,13 @@ void Client::unminimize( bool avoid_animation ) updateAllowedActions(); workspace()->updateMinimizedOfTransients( this ); updateWindowRules(); + workspace()->updateAllTiles(); if( effects && !avoid_animation ) static_cast( effects )->windowUnminimized( effectWindow() ); + // when tiling, request a rearrangement + workspace()->notifyWindowMinimizeToggled( this ); + // Update states of all other windows in this group if( clientGroup() ) clientGroup()->updateStates( this ); diff --git a/geometry.cpp b/geometry.cpp index 801129c5e4..8880a053bd 100644 --- a/geometry.cpp +++ b/geometry.cpp @@ -2155,6 +2155,7 @@ void Client::move( int x, int y, ForceGeometry_t force ) workspace()->checkActiveScreen( this ); workspace()->updateStackingOrder(); workspace()->checkUnredirect(); + workspace()->notifyWindowMove( this, moveResizeGeom, initialMoveResizeGeom ); // client itself is not damaged const QRect deco_rect = decorationRect().translated( geom.x(), geom.y() ); addWorkspaceRepaint( deco_rect_before_block ); @@ -2807,15 +2808,56 @@ bool Client::startMoveResize() void Client::finishMoveResize( bool cancel ) { + // store for notification + bool wasResize = isResize(); + bool wasMove = isMove(); + leaveMoveResize(); - if( isElectricBorderMaximizing() ) + + if( workspace()->tilingMode() ) { - cancel = true; + if( wasResize ) + workspace()->notifyWindowResizeDone( this, moveResizeGeom, initialMoveResizeGeom, cancel ); + else if( wasMove ) + workspace()->notifyWindowMoveDone( this, moveResizeGeom, initialMoveResizeGeom, cancel ); } + else + { if( cancel ) setGeometry( initialMoveResizeGeom ); else setGeometry( moveResizeGeom ); + } + if( cancel ) + setGeometry( initialMoveResizeGeom ); + + if( isElectricBorderMaximizing() ) + { + cancel = true; + } + if( isElectricBorderMaximizing() ) + { + switch( electricMode ) + { + case ElectricMaximizeMode: + if( maximizeMode() == MaximizeFull ) + setMaximize( false, false ); + else + setMaximize( true, true ); + workspace()->restoreElectricBorderSize( ElectricTop ); + break; + case ElectricLeftMode: + setQuickTileMode( QuickTileLeft ); + workspace()->restoreElectricBorderSize( ElectricLeft ); + break; + case ElectricRightMode: + setQuickTileMode( QuickTileRight ); + workspace()->restoreElectricBorderSize( ElectricRight ); + break; + } + electricMaximizing = false; + workspace()->hideElectricBorderWindowOutline(); + } if( isElectricBorderMaximizing() ) { switch( electricMode ) @@ -2841,6 +2883,7 @@ void Client::finishMoveResize( bool cancel ) } checkMaximizeGeometry(); // FRAME update(); + Notify::raise( isResize() ? Notify::ResizeEnd : Notify::MoveEnd ); if( effects ) static_cast(effects)->windowUserMovedResized( effectWindow(), false, true ); @@ -2999,6 +3042,11 @@ void Client::handleMoveResize( int x, int y, int x_root, int y_root ) bool update = false; if( isResize()) { + // query layout for supported resize mode + if( workspace()->tilingMode() ) + { + mode = workspace()->supportedTilingResizeMode( this, mode ); + } // first resize (without checking constrains), then snap, then check bounds, then check constrains QRect orig = initialMoveResizeGeom; Sizemode sizemode = SizemodeAny; @@ -3033,10 +3081,19 @@ void Client::handleMoveResize( int x, int y, int x_root, int y_root ) sizemode = SizemodeFixedW; break; case PositionCenter: + // exception for tiling + // Center means no resizing allowed + if( workspace()->tilingMode() ) + { + finishMoveResize( false ); + buttonDown = false; + return; + } default: abort(); break; } + workspace()->notifyWindowResize( this, moveResizeGeom, initialMoveResizeGeom ); // adjust new size to snap to other windows/borders moveResizeGeom = workspace()->adjustClientSize( this, moveResizeGeom, mode ); @@ -3208,9 +3265,13 @@ void Client::handleMoveResize( int x, int y, int x_root, int y_root ) if( update ) performMoveResize(); + if ( isMove() ) + { + workspace()->notifyWindowMove( this, moveResizeGeom, initialMoveResizeGeom ); workspace()->checkElectricBorder(globalPos, xTime()); } + } void Client::performMoveResize() { @@ -3229,6 +3290,7 @@ void Client::performMoveResize() if( rules()->checkMoveResizeMode ( isResize() ? options->resizeMode : options->moveMode ) == Options::Opaque ) { + if( !workspace()->tilingMode() ) setGeometry( moveResizeGeom ); positionGeometryTip(); } diff --git a/kcmkwin/kwinoptions/windows.cpp b/kcmkwin/kwinoptions/windows.cpp index 2ede7a6dec..d029247bf4 100644 --- a/kcmkwin/kwinoptions/windows.cpp +++ b/kcmkwin/kwinoptions/windows.cpp @@ -66,6 +66,9 @@ #define KWIN_AUTOGROUP_FOREGROUND "AutogroupInForeground" #define KWIN_SEPARATE_SCREEN_FOCUS "SeparateScreenFocus" #define KWIN_ACTIVE_MOUSE_SCREEN "ActiveMouseScreen" +#define KWIN_TILINGON "TilingOn" +#define KWIN_TILING_DEFAULT_LAYOUT "TilingDefaultLayout" +#define KWIN_TILING_RAISE_POLICY "TilingRaisePolicy" //CT 15mar 98 - magics #define KWM_BRDR_SNAP_ZONE "BorderSnapZone" @@ -636,6 +639,54 @@ KAdvancedConfig::KAdvancedConfig (bool _standAlone, KConfig *_config, const KCom connect(hideUtilityWindowsForInactive, SIGNAL(toggled(bool)), SLOT(changed())); vLay->addWidget( hideUtilityWindowsForInactive, 1, 0, 1, 2 ); + tilBox = new KButtonGroup(this); + tilBox->setTitle(i18n("Tiling")); + QGridLayout *tilBoxLay = new QGridLayout(tilBox); + + tilingOn = new QCheckBox( i18n( "Enable Tiling" ), tilBox ); + tilingOn->setWhatsThis( + i18n( "A tiling window manager lays out all the windows in a non-overlapping manner." + " This way all windows are always visible.") ); + tilBoxLay->addWidget( tilingOn ); + connect( tilingOn, SIGNAL( toggled(bool) ), SLOT( tilingOnChanged(bool) ) ); + connect( tilingOn, SIGNAL( toggled(bool) ), SLOT( changed() ) ); + + tilingLayoutLabel = new QLabel( i18n("Default Tiling &Layout"), tilBox ); + tilBoxLay->addWidget( tilingLayoutLabel, 1, 0 ); + + tilingLayoutCombo = new QComboBox( tilBox ); + + // NOTE: add your layout to the bottom of this list + tilingLayoutCombo->addItem( "Spiral" ); + tilingLayoutCombo->addItem( "Columns" ); + tilingLayoutCombo->addItem( "Floating" ); + + tilingLayoutLabel->setBuddy( tilingLayoutCombo ); + connect( tilingLayoutCombo, SIGNAL( activated(int) ), SLOT( changed() ) ); + tilBoxLay->addWidget( tilingLayoutCombo, 1, 1 ); + + tilingRaiseLabel = new QLabel( i18n("Floating &Windows Raising"), tilBox ); + tilBoxLay->addWidget( tilingRaiseLabel, 2, 0 ); + + tilingRaiseCombo = new QComboBox( tilBox ); + tilingRaiseCombo->addItem( i18nc( "Window Raising Policy", "Raise/Lower all floating windows" ) ); + tilingRaiseCombo->addItem( i18nc( "Window Raising Policy", "Raise/Lower current window only") ); + tilingRaiseCombo->addItem( i18nc( "Window Raising Policy", "Floating windows are always on top" ) ); + wtstr = i18n("The window raising policy determines how floating windows are stacked" + "
    " + "
  • Raise/Lower all will raise all floating windows when a" + " floating window is activated.
  • " + "
  • Raise/Lower current will raise only the current window.
  • " + "
  • Floating windows on top will always keep floating windows on top, even" + " when a tiled window is activated." + "
") ; + tilingRaiseCombo->setWhatsThis( wtstr ); + connect( tilingRaiseCombo, SIGNAL( activated(int) ), SLOT( changed() ) ); + tilingRaiseLabel->setBuddy( tilingRaiseCombo ); + tilBoxLay->addWidget( tilingRaiseCombo, 2, 1 ); + + lay->addWidget( tilBox ); + lay->addStretch(); load(); @@ -661,6 +712,24 @@ void KAdvancedConfig::shadeHoverChanged(bool a) { shadeHover->setEnabled(a); } +void KAdvancedConfig::setTilingOn( bool on ) { + tilingOn->setChecked( on ); +} + +void KAdvancedConfig::setTilingLayout( int l ) { + tilingLayoutCombo->setCurrentIndex( l ); +} + +void KAdvancedConfig::setTilingRaisePolicy( int l ) { + tilingRaiseCombo->setCurrentIndex( l ); +} + +void KAdvancedConfig::tilingOnChanged( bool a ) { + tilingLayoutLabel->setEnabled( a ); + tilingLayoutCombo->setEnabled( a ); + tilingRaiseLabel->setEnabled( a ); + tilingRaiseCombo->setEnabled( a ); +} void KAdvancedConfig::showEvent( QShowEvent *ev ) { @@ -719,6 +788,10 @@ void KAdvancedConfig::load( void ) setAutogroupSimilarWindows( cg.readEntry( KWIN_AUTOGROUP_SIMILAR, false)); setAutogroupInForeground( cg.readEntry( KWIN_AUTOGROUP_FOREGROUND, true)); + setTilingOn( cg.readEntry( KWIN_TILINGON, false ) ); + setTilingLayout( cg.readEntry( KWIN_TILING_DEFAULT_LAYOUT, 0 ) ); + setTilingRaisePolicy( cg.readEntry( KWIN_TILING_RAISE_POLICY, 0 ) ); + emit KCModule::changed(false); } @@ -769,6 +842,10 @@ void KAdvancedConfig::save( void ) QDBusConnection::sessionBus().send(message); } + + cg.writeEntry( KWIN_TILINGON, tilingOn->isChecked() ); + cg.writeEntry( KWIN_TILING_DEFAULT_LAYOUT, tilingLayoutCombo->currentIndex() ); + cg.writeEntry( KWIN_TILING_RAISE_POLICY, tilingRaiseCombo->currentIndex() ); emit KCModule::changed(false); } @@ -778,6 +855,9 @@ void KAdvancedConfig::defaults() setShadeHoverInterval(250); setPlacement(SMART_PLACEMENT); setHideUtilityWindowsForInactive( true ); + setTilingOn( false ); + setTilingLayout( 0 ); + setTilingRaisePolicy( 0 ); setInactiveTabsSkipTaskbar( false ); setAutogroupSimilarWindows( false ); setAutogroupInForeground( true ); diff --git a/kcmkwin/kwinoptions/windows.h b/kcmkwin/kwinoptions/windows.h index 2890209072..29c73dd7e0 100644 --- a/kcmkwin/kwinoptions/windows.h +++ b/kcmkwin/kwinoptions/windows.h @@ -196,6 +196,7 @@ private slots: void changed() { emit KCModule::changed(true); } + void tilingOnChanged( bool a ); private: int getShadeHoverInterval (void ); @@ -225,7 +226,21 @@ private: int getPlacement( void ); //CT void setPlacement(int); //CT + KComboBox *placementCombo; + + // ------------------------------ + // Tiling related widgets/methods + // ------------------------------ + KButtonGroup *tilBox; + QCheckBox *tilingOn; + QLabel *tilingLayoutLabel; + QLabel *tilingRaiseLabel; + QComboBox *tilingLayoutCombo; + QComboBox *tilingRaiseCombo; + void setTilingOn( bool ); + void setTilingLayout( int ); + void setTilingRaisePolicy( int ); }; #endif // KKWMWINDOWS_H diff --git a/kcmkwin/kwinrules/ruleswidget.cpp b/kcmkwin/kwinrules/ruleswidget.cpp index 0d7f7e5426..89c9febe26 100644 --- a/kcmkwin/kwinrules/ruleswidget.cpp +++ b/kcmkwin/kwinrules/ruleswidget.cpp @@ -103,6 +103,7 @@ RulesWidget::RulesWidget( QWidget* parent ) SETUP( autogroupid, force ); SETUP( opacityactive, force ); SETUP( opacityinactive, force ); + SETUP( tilingoption, force ); SETUP( shortcut, force ); // workarounds tab SETUP( fsplevel, force ); @@ -153,6 +154,7 @@ UPDATE_ENABLE_SLOT( autogroupfg ) UPDATE_ENABLE_SLOT( autogroupid ) UPDATE_ENABLE_SLOT( opacityactive ) UPDATE_ENABLE_SLOT( opacityinactive ) +UPDATE_ENABLE_SLOT( tilingoption ) void RulesWidget::updateEnableshortcut() { shortcut->setEnabled( enable_shortcut->isChecked() && rule_shortcut->currentIndex() != 0 ); @@ -269,6 +271,16 @@ int RulesWidget::comboToDesktop( int val ) const return val + 1; } +int RulesWidget::tilingToCombo( int t ) const + { + return qBound(0, t, 1); + } + +int RulesWidget::comboToTiling( int val ) const + { + return val; // 0 is tiling, 1 is floating + } + static int placementToCombo( Placement::Policy placement ) { static const int conv[] = @@ -431,6 +443,7 @@ void RulesWidget::setRules( Rules* rules ) LINEEDIT_FORCE_RULE( autogroupid, ); LINEEDIT_FORCE_RULE( opacityactive, intToStr ); LINEEDIT_FORCE_RULE( opacityinactive, intToStr ); + COMBOBOX_FORCE_RULE( tilingoption, tilingToCombo ); LINEEDIT_SET_RULE( shortcut, ); COMBOBOX_FORCE_RULE( fsplevel, ); COMBOBOX_FORCE_RULE( moveresizemode, moveresizeToCombo ); @@ -524,6 +537,7 @@ Rules* RulesWidget::rules() const LINEEDIT_FORCE_RULE( autogroupid, ); LINEEDIT_FORCE_RULE( opacityactive, strToInt ); LINEEDIT_FORCE_RULE( opacityinactive, strToInt ); + COMBOBOX_FORCE_RULE( tilingoption, comboToTiling ); LINEEDIT_SET_RULE( shortcut, ); COMBOBOX_FORCE_RULE( fsplevel, ); COMBOBOX_FORCE_RULE( moveresizemode, comboToMoveResize ); @@ -644,6 +658,7 @@ void RulesWidget::prefillUnusedValues( const KWindowInfo& info ) //LINEEDIT_PREFILL( autogroupid, ); LINEEDIT_PREFILL( opacityactive, intToStr, 100 /*get the actual opacity somehow*/); LINEEDIT_PREFILL( opacityinactive, intToStr, 100 /*get the actual opacity somehow*/); + COMBOBOX_PREFILL( tilingoption, tilingToCombo, 0 ); //LINEEDIT_PREFILL( shortcut, ); //COMBOBOX_PREFILL( fsplevel, ); //COMBOBOX_PREFILL( moveresizemode, moveresizeToCombo ); diff --git a/kcmkwin/kwinrules/ruleswidget.h b/kcmkwin/kwinrules/ruleswidget.h index e6aafd6e07..e4b02d7a50 100644 --- a/kcmkwin/kwinrules/ruleswidget.h +++ b/kcmkwin/kwinrules/ruleswidget.h @@ -77,6 +77,7 @@ class RulesWidget void updateEnableautogroupid(); void updateEnableopacityactive(); void updateEnableopacityinactive(); + void updateEnabletilingoption(); // workarounds tab void updateEnablefsplevel(); void updateEnablemoveresizemode(); @@ -92,6 +93,8 @@ class RulesWidget private: int desktopToCombo( int d ) const; int comboToDesktop( int val ) const; + int tilingToCombo( int t ) const; + int comboToTiling( int val ) const; void prefillUnusedValues( const KWindowInfo& info ); DetectDialog* detect_dlg; bool detect_dlg_ok; diff --git a/kcmkwin/kwinrules/ruleswidgetbase.ui b/kcmkwin/kwinrules/ruleswidgetbase.ui index 46c9f3f55f..d2a5e19b0b 100644 --- a/kcmkwin/kwinrules/ruleswidgetbase.ui +++ b/kcmkwin/kwinrules/ruleswidgetbase.ui @@ -1,7 +1,8 @@ - + + KWin::RulesWidgetBase - - + + 0 0 @@ -9,14 +10,14 @@ 441 - - + + 0 - - - 0 + + + 3 @@ -30,141 +31,141 @@ &Window - - - - + + + + De&scription: - + false - + description - - + + - - - + + + Window &class (application type): - + false - + wmclass - - + + - - - + + + Match w&hole window class - - + + - + Unimportant - + Exact Match - + Substring Match - + Regular Expression - - - + + + false - + Edit - + - - - + + + Window &role: - + false - + role - - + + - - + + - + Unimportant - + Exact Match - + Substring Match - + Regular Expression - - - + + + false - + Edit - - + + - - + + Qt::Horizontal - + 40 20 @@ -173,18 +174,18 @@ - - + + &Detect Window Properties - + Qt::Horizontal - + 40 20 @@ -194,15 +195,15 @@ - + - + Qt::Vertical - + QSizePolicy::Expanding - + 20 40 @@ -210,12 +211,12 @@ - - - + + + Qt::Horizontal - + 40 20 @@ -223,12 +224,12 @@ - - - + + + Qt::Horizontal - + 40 20 @@ -236,9 +237,9 @@ - - - + + + Qt::Horizontal @@ -257,242 +258,242 @@ Window &Extra - - - - + + + + Window &types: - + false - + types - - - + + + QAbstractItemView::ExtendedSelection - + Normal Window - + Dialog Window - + Utility Window - + Dock (panel) - + Toolbar - + Torn-Off Menu - + Splash Screen - + Desktop - + Override Type - + Standalone Menubar - - - + + + Window t&itle: - + false - + title - - + + - - + + - + Unimportant - + Exact Match - + Substring Match - + Regular Expression - - - + + + false - + Edit - + - - - + + + Extra role: - + false - + extra - - + + - - + + - + Unimportant - + Exact Match - + Substring Match - + Regular Expression - - - + + + false - + Edit - + - - - + + + &Machine (hostname): - + false - + machine - - + + - - + + - + Unimportant - + Exact Match - + Substring Match - + Regular Expression - - - + + + false - + Edit - + - - - + + + Qt::Horizontal - + 40 20 @@ -500,12 +501,12 @@ - - - + + + Qt::Horizontal - + 40 20 @@ -513,12 +514,12 @@ - - - + + + Qt::Horizontal - + 40 20 @@ -526,12 +527,12 @@ - - - + + + Qt::Vertical - + 20 0 @@ -553,494 +554,494 @@ &Geometry - - - - + + + + false - + Do Not Affect - + Apply Initially - + Remember - + Force - + Apply Now - + Force Temporarily - - - + + + false - + 0123456789-+,xX: - - - + + + &Size - - - + + + &Position - - - + + + false - + 0123456789-+,xX: - - - + + + false - + Do Not Affect - + Apply Initially - + Remember - + Force - + Apply Now - + Force Temporarily - - - + + + false - + - - - + + + false - + Do Not Affect - + Apply Initially - + Remember - + Force - + Apply Now - + Force Temporarily - - - + + + false - + - - - + + + Maximized &horizontally - - - + + + false - + - - - + + + false - + Do Not Affect - + Apply Initially - + Remember - + Force - + Apply Now - + Force Temporarily - - - + + + &Fullscreen - - - + + + false - + Do Not Affect - + Apply Initially - + Remember - + Force - + Apply Now - + Force Temporarily - - - + + + Maximized &vertically - - - + + + false - + Do Not Affect - + Apply Initially - + Remember - + Force - + Apply Now - + Force Temporarily - - - + + + false - - - + + + &Desktop - - - + + + Sh&aded - - - + + + false - + - - - + + + false - + Do Not Affect - + Apply Initially - + Remember - + Force - + Apply Now - + Force Temporarily - - - + + + false - + - - - + + + M&inimized - - - + + + false - + Do Not Affect - + Force - + Force Temporarily - - - + + + false - + Default - + No Placement - + Smart - + Maximizing - + Cascade - + Centered - + Random - + Top-Left Corner - + Under Mouse - + On Main Window - - - + + + P&lacement - + - + Qt::Vertical - + QSizePolicy::Expanding - + 20 16 @@ -1048,38 +1049,38 @@ - - - + + + false - + Do Not Affect - + Apply Initially - + Remember - + Force - + Apply Now - + Force Temporarily @@ -1099,52 +1100,52 @@ &Preferences - - - - + + + + Keep &above - - - + + + Keep &below - - - + + + Skip pa&ger - - - + + + Skip &taskbar - - - + + + &No border - - - + + + Accept &focus - - - + + + &Closeable @@ -1183,17 +1184,17 @@ false - + Do Not Affect - + Force - + Force Temporarily @@ -1204,73 +1205,83 @@ false - + 0123456789 - - - + + + false - + - - - + + + false - + - - - + + + false - + - - - + + + false - + - - - + + + false - + - - - + + + false - + - - + + + + false + + + + + + + + false @@ -1377,25 +1388,10 @@ false - - - Do Not Affect - - - - - Force - - - - - Force Temporarily - - - - + + false @@ -1416,8 +1412,8 @@ - - + + false @@ -1428,174 +1424,240 @@ + Force + + + + + Force Temporarily + + + + + + + + false + + + + Do Not Affect + + + + + Force + + + + + Force Temporarily + + + + + + + + false + + + + Do Not Affect + + + + + Force + + + + + Force Temporarily + + + + + + + + false + + + + Do Not Affect + + + + Apply Initially - + Remember - + Force - + Apply Now - + Force Temporarily - - - + + + false - + Do Not Affect - + Apply Initially - + Remember - + Force - + Apply Now - + Force Temporarily - - - + + + false - + Do Not Affect - + Apply Initially - + Remember - + Force - + Apply Now - + Force Temporarily - - - + + + false - + Do Not Affect - + Apply Initially - + Remember - + Force - + Apply Now - + Force Temporarily - - - + + + false - + Do Not Affect - + Apply Initially - + Remember - + Force - + Apply Now - + Force Temporarily @@ -1603,13 +1665,13 @@ - + Qt::Vertical - + QSizePolicy::Expanding - + 20 80 @@ -1623,17 +1685,17 @@ false - + Do Not Affect - + Force - + Force Temporarily @@ -1644,7 +1706,7 @@ false - + 0123456789 @@ -1661,7 +1723,7 @@ Shortcut - + @@ -1672,32 +1734,32 @@ false - + Do Not Affect - + Apply Initially - + Remember - + Force - + Apply Now - + Force Temporarily @@ -1717,6 +1779,52 @@ + + + + T&iling + + + + + + + false + + + + Do Not Affect + + + + + Force + + + + + Force Temporarily + + + + + + + + false + + + + Tiled + + + + + Floating + + + + @@ -1731,326 +1839,326 @@ W&orkarounds - - - - + + + + &Focus stealing prevention - - - + + + false - + Do Not Affect - + Force - + Force Temporarily - - - + + + false - + Normal Window - + Dialog Window - + Utility Window - + Dock (panel) - + Toolbar - + Torn-Off Menu - + Splash Screen - + Desktop - + Override Type - + Standalone Menubar - - - + + + false - + Opaque - + Transparent - - - + + + Window &type - - - + + + &Moving/resizing - - - + + + false - + Do Not Affect - + Force - + Force Temporarily - - - + + + false - + Do Not Affect - + Force - + Force Temporarily - - - + + + false - + None - + Low - + Normal - + High - + Extreme - - - + + + false - + 0123456789-+,xX: - - - + + + M&inimum size - - - + + + false - + Do Not Affect - + Force - + Force Temporarily - - - + + + M&aximum size - - - + + + false - + 0123456789-+,xX: - - - + + + false - + Do Not Affect - + Force - + Force Temporarily - - - + + + Ignore requested &geometry - - - + + + false - + Do Not Affect - + Force - + Force Temporarily - - - + + + false - + - + - + Qt::Vertical - + QSizePolicy::Expanding - + 20 160 @@ -2058,85 +2166,85 @@ - - - + + + Strictly obey geometry - + - - - + + + false - + Do Not Affect - + Force - + Force Temporarily - - - + + + false - + - - - + + + false - + - - - + + + Block global shortcuts - + - - - + + + false - + Do Not Affect - + Force - + Force Temporarily @@ -2150,9 +2258,9 @@ - KComboBox - QComboBox -
kcombobox.h
+ KPushButton + QPushButton +
kpushbutton.h
KLineEdit @@ -2160,9 +2268,9 @@
klineedit.h
- KPushButton - QPushButton -
kpushbutton.h
+ KComboBox + QComboBox +
kcombobox.h
KRestrictedLine @@ -2285,11 +2393,11 @@ KWin::RulesWidgetBase detectClicked() - + 20 20 - + 20 20 @@ -2301,11 +2409,11 @@ KWin::RulesWidgetBase wmclassMatchChanged() - + 20 20 - + 20 20 @@ -2317,11 +2425,11 @@ KWin::RulesWidgetBase roleMatchChanged() - + 20 20 - + 20 20 @@ -2333,11 +2441,11 @@ KWin::RulesWidgetBase titleMatchChanged() - + 20 20 - + 20 20 @@ -2349,11 +2457,11 @@ KWin::RulesWidgetBase extraMatchChanged() - + 20 20 - + 20 20 @@ -2365,11 +2473,11 @@ KWin::RulesWidgetBase machineMatchChanged() - + 20 20 - + 20 20 @@ -2381,11 +2489,11 @@ KWin::RulesWidgetBase shortcutEditClicked() - + 20 20 - + 20 20 diff --git a/kwin.notifyrc b/kwin.notifyrc index f3f499b69b..34cfb73c39 100644 --- a/kwin.notifyrc +++ b/kwin.notifyrc @@ -6775,3 +6775,18 @@ Comment[x-test]=xxSome effects are not supported by backend or hardware.xx Comment[zh_CN]=因为后端或硬件的关系,一些效果不受支持。 Comment[zh_TW]=有些效果未被後端介面或硬體支援。 Action=Popup + +[Event/tilingenabled] +Name=Tiling Enabled +Comment=Tiling mode has been enabled +Action=Popup + +[Event/tilingdisabled] +Name=Tiling Disabled +Comment=Tiling mode has been disabled +Action=Popup + +[Event/tilinglayoutchanged] +Name=Tiling Layout Changed +Comment=Tiling Layout has been changed +Action=Popup diff --git a/kwinbindings.cpp b/kwinbindings.cpp index 529e086c86..a0f56bf388 100644 --- a/kwinbindings.cpp +++ b/kwinbindings.cpp @@ -219,6 +219,28 @@ along with this program. If not, see . DEF( I18N_NOOP("Block Global Shortcuts"), 0, slotDisableGlobalShortcuts()); DEF( I18N_NOOP("Suspend Compositing"), Qt::SHIFT+Qt::ALT+Qt::Key_F12, slotToggleCompositing()); + a = actionCollection->addAction( "Group:Tiling" ); + a->setText( i18n("Tiling") ); + DEF( I18N_NOOP("Toggle Tiling"), Qt::SHIFT+Qt::ALT+Qt::Key_F11, slotToggleTiling() ); + DEF( I18N_NOOP("Toggle Orientation"), Qt::META+Qt::Key_Space, slotToggleOrientation() ); + DEF( I18N_NOOP("Increase Ratio"), Qt::META+Qt::Key_Right, slotTileIncreaseRatio() ); + DEF( I18N_NOOP("Decrease Ratio"), Qt::META+Qt::Key_Left, slotTileDecreaseRatio() ); + DEF( I18N_NOOP("Toggle Floating"), Qt::META+Qt::Key_F, slotToggleFloating() ); + + DEF( I18N_NOOP("Switch Focus Left") , Qt::META+Qt::Key_H, slotLeft() ); + DEF( I18N_NOOP("Switch Focus Right") , Qt::META+Qt::Key_L, slotRight() ); + DEF( I18N_NOOP("Switch Focus Up") , Qt::META+Qt::Key_K, slotTop() ); + DEF( I18N_NOOP("Switch Focus Down") , Qt::META+Qt::Key_J, slotBottom() ); + DEF( I18N_NOOP("Move Left") , Qt::SHIFT+Qt::META+Qt::Key_H, slotMoveLeft() ); + DEF( I18N_NOOP("Move Right") , Qt::SHIFT+Qt::META+Qt::Key_L, slotMoveRight() ); + DEF( I18N_NOOP("Move Up") , Qt::SHIFT+Qt::META+Qt::Key_K, slotMoveTop() ); + DEF( I18N_NOOP("Move Down") , Qt::SHIFT+Qt::META+Qt::Key_J, slotMoveBottom() ); + DEF( I18N_NOOP("Next Layout"), Qt::META+Qt::Key_PageDown, slotNextTileLayout() ); + DEF( I18N_NOOP("Previous Layout"), Qt::META+Qt::Key_PageUp, slotPreviousTileLayout() ); + // NOTE: temporary + DEF( I18N_NOOP("Test Dump tiles"), Qt::META+Qt::Key_D, dumpTiles() ); + DEF( I18N_NOOP("Test Below Cursor"), Qt::META+Qt::Key_X, belowCursor() ); + #undef DEF #undef DEF2 diff --git a/options.cpp b/options.cpp index b960f63edc..1b0f352415 100644 --- a/options.cpp +++ b/options.cpp @@ -124,6 +124,10 @@ unsigned long Options::updateSettings() shadeHover = config.readEntry("ShadeHover", false); shadeHoverInterval = config.readEntry("ShadeHoverInterval", 250 ); + tilingOn = config.readEntry( "TilingOn", false ); + tilingLayout = config.readEntry( "TilingDefaultLayout", 0 ); + tilingRaisePolicy = config.readEntry( "TilingRaisePolicy", 0 ); + // important: autoRaise implies ClickRaise clickRaise = autoRaise || config.readEntry("ClickRaise", true); diff --git a/options.h b/options.h index a21106a4e1..312fac28a1 100644 --- a/options.h +++ b/options.h @@ -116,6 +116,21 @@ class Options : public KDecorationOptions */ int shadeHoverInterval; + /** + * Whether tiling is enabled or not + */ + bool tilingOn; + + /** + * Tiling Layout + */ + int tilingLayout; + + /** + * Tiling window raise policy. + */ + int tilingRaisePolicy; + /** Different Alt-Tab-Styles:
    diff --git a/org.kde.KWin.xml b/org.kde.KWin.xml index 9ed31f1174..9c66c29b71 100644 --- a/org.kde.KWin.xml +++ b/org.kde.KWin.xml @@ -69,5 +69,9 @@ + + + + diff --git a/rules.cpp b/rules.cpp index 81ab820ffa..b6055dd224 100644 --- a/rules.cpp +++ b/rules.cpp @@ -51,6 +51,7 @@ Rules::Rules() , maxsizerule( UnusedForceRule ) , opacityactiverule( UnusedForceRule ) , opacityinactiverule( UnusedForceRule ) + , tilingoptionrule( UnusedForceRule ) , ignorepositionrule( UnusedForceRule ) , desktoprule( UnusedSetRule ) , typerule( UnusedForceRule ) @@ -153,6 +154,7 @@ void Rules::readFromCfg( const KConfigGroup& cfg ) READ_FORCE_RULE( opacityinactive,, 0); if( opacityinactive < 0 || opacityinactive > 100 ) opacityinactive = 100; + READ_FORCE_RULE( tilingoption,, 0 ); READ_FORCE_RULE( ignoreposition,, false); READ_SET_RULE( desktop,,0 ); type = readType( cfg, "type" ); @@ -241,6 +243,7 @@ void Rules::write( KConfigGroup& cfg ) const WRITE_FORCE_RULE( maxsize, ); WRITE_FORCE_RULE( opacityactive, ); WRITE_FORCE_RULE( opacityinactive, ); + WRITE_FORCE_RULE( tilingoption, ); WRITE_FORCE_RULE( ignoreposition, ); WRITE_SET_RULE( desktop, ); WRITE_FORCE_RULE( type, int ); @@ -280,6 +283,7 @@ bool Rules::isEmpty() const && maxsizerule == UnusedForceRule && opacityactiverule == UnusedForceRule && opacityinactiverule == UnusedForceRule + && tilingoptionrule == UnusedForceRule && ignorepositionrule == UnusedForceRule && desktoprule == UnusedSetRule && typerule == UnusedForceRule @@ -576,6 +580,7 @@ APPLY_FORCE_RULE( maxsize, MaxSize, QSize ) APPLY_FORCE_RULE( opacityactive, OpacityActive, int ) APPLY_FORCE_RULE( opacityinactive, OpacityInactive, int ) APPLY_FORCE_RULE( ignoreposition, IgnorePosition, bool ) +APPLY_FORCE_RULE( tilingoption, TilingOption, int ) // the cfg. entry needs to stay named the say for backwards compatibility bool Rules::applyIgnoreGeometry( bool& ignore ) const @@ -672,6 +677,7 @@ void Rules::discardUsed( bool withdrawn ) DISCARD_USED_FORCE_RULE( maxsize ); DISCARD_USED_FORCE_RULE( opacityactive ); DISCARD_USED_FORCE_RULE( opacityinactive ); + DISCARD_USED_FORCE_RULE( tilingoption ); DISCARD_USED_FORCE_RULE( ignoreposition ); DISCARD_USED_SET_RULE( desktop ); DISCARD_USED_FORCE_RULE( type ); @@ -781,6 +787,7 @@ CHECK_FORCE_RULE( MinSize, QSize ) CHECK_FORCE_RULE( MaxSize, QSize ) CHECK_FORCE_RULE( OpacityActive, int ) CHECK_FORCE_RULE( OpacityInactive, int ) +CHECK_FORCE_RULE( TilingOption, int ) CHECK_FORCE_RULE( IgnorePosition, bool ) bool WindowRules::checkIgnoreGeometry( bool ignore ) const diff --git a/rules.h b/rules.h index 60d469d93b..d645962a04 100644 --- a/rules.h +++ b/rules.h @@ -61,6 +61,7 @@ class WindowRules QSize checkMaxSize( QSize s ) const; int checkOpacityActive(int s) const; int checkOpacityInactive(int s) const; + int checkTilingOption( int s ) const; bool checkIgnoreGeometry( bool ignore ) const; int checkDesktop( int desktop, bool init = false ) const; NET::WindowType checkType( NET::WindowType type ) const; @@ -115,6 +116,7 @@ class Rules bool applyMaxSize( QSize& s ) const; bool applyOpacityActive(int& s) const; bool applyOpacityInactive(int& s) const; + bool applyTilingOption( int& s ) const; bool applyIgnoreGeometry( bool& ignore ) const; bool applyDesktop( int& desktop, bool init ) const; bool applyType( NET::WindowType& type ) const; @@ -214,6 +216,8 @@ class Rules ForceRule opacityactiverule; int opacityinactive; ForceRule opacityinactiverule; + int tilingoption; + ForceRule tilingoptionrule; bool ignoreposition; ForceRule ignorepositionrule; int desktop; diff --git a/sm.cpp b/sm.cpp index 7404fafd38..2e926f407b 100644 --- a/sm.cpp +++ b/sm.cpp @@ -82,6 +82,17 @@ void Workspace::storeSession( KConfig* config, SMSavePhase phase ) KConfigGroup cg(config, "Session"); int count = 0; int active_client = -1; + + if( phase == SMSavePhase2 || phase == SMSavePhase2Full ) + { + cg.writeEntry( "tiling", tilingMode() ); + if( tilingMode() ) + { + kDebug(1212) << "Tiling was ON"; + setTilingMode( false ); + } + } + for (ClientList::Iterator it = clients.begin(); it != clients.end(); ++it) { Client* c = (*it); @@ -167,6 +178,9 @@ void Workspace::loadSessionInfo() { session.clear(); KConfigGroup cg(kapp->sessionConfig(), "Session"); + + setTilingMode( cg.readEntry( "tiling", false ) ); + int count = cg.readEntry( "count",0 ); int active_client = cg.readEntry( "active",0 ); for ( int i = 1; i <= count; i++ ) diff --git a/tile.cpp b/tile.cpp new file mode 100644 index 0000000000..907b228452 --- /dev/null +++ b/tile.cpp @@ -0,0 +1,152 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2009 Nikhil Marathe + +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 . +*********************************************************************/ + +#include "tile.h" + +#include + +#include + +#include "client.h" +#include "workspace.h" + +namespace KWin +{ + +Tile::Tile( Client *c, const QRect& area) + : m_client(c), + m_floating(false) + { + setGeometry(area); + if( c ) + { + m_prevGeom = c->geometry(); + } + if( c && !c->isResizable() ) + floatTile(); + } + +/* + * NOTE: Why isn't left/right/parent copied? + * Because they might be deleted at any point, so we can't keep pointers to them + * Also it doesn't make sense in the areas where copy is actually going to be used. + * Since we will be getting a new parent and children. + */ +Tile::Tile( const Tile& orig ) + : m_client( orig.client() ), + m_floating( orig.floating() ), + m_prevGeom( orig.m_prevGeom ) + { + setGeometry( orig.geometry() ); + } + +Tile::~Tile() + { + restorePreviousGeometry(); + + m_client = NULL; + } + +void Tile::commit() + { + m_client->setGeometry(geometry(), ForceGeometrySet); + } + +void Tile::setGeometry(int x, int y, int w, int h) + { + QRect old = m_geom; + m_geom.setTopLeft( QPoint(x, y) ); + m_geom.setWidth( w ); + m_geom.setHeight( h ); + + if( old == m_geom ) + return; + + if( floating() ) + m_prevGeom = m_geom; + + } + +void Tile::floatTile() + { + if( floating() ) return; + + // note, order of setting m_floating to true + // then calling restore is important + // childGeometryChanged will check for ignoreGeometry() + m_floating = true; + + restorePreviousGeometry(); + + commit(); + client()->workspace()->notifyWindowActivated( client() ); + // TODO: notify layout manager + } + +void Tile::unfloatTile() + { + if( !floating() ) return; + + m_floating = false; + m_prevGeom = m_client->geometry(); + + setGeometry( m_client->workspace()->clientArea( PlacementArea, m_client ) ); + commit(); + // TODO: notify layout manager + + } + +void Tile::restorePreviousGeometry() + { + // why this check? + // sometimes we remove a Tile, but don't want to remove the children + // so the children are set to NULL. In this case leaf() will return + // true but m_client will still be null + if( !m_client ) return; + if( m_prevGeom.isNull() ) + { + QRect area = m_client->workspace()->clientArea( PlacementArea, m_client ); + m_client->workspace()->place( m_client, area ); + } + else + { + m_client->setGeometry( m_prevGeom, ForceGeometrySet ); + } + setGeometry( m_client->geometry() ); + } + +inline bool Tile::minimized() const + { + return m_client->isMinimized(); + } + +void Tile::focus() + { + m_client->workspace()->activateClient( m_client, true ); + } + +void Tile::dumpTile(const QString& indent ) const + { + kDebug(1212) << indent << m_client + << ( floating() ? "floating" : "not floating" ) + << ( ignoreGeometry() ? "ignored" : "tiled" ) + << m_geom ; + } +} diff --git a/tile.h b/tile.h new file mode 100644 index 0000000000..f2dc0c4237 --- /dev/null +++ b/tile.h @@ -0,0 +1,125 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2009 Nikhil Marathe + +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 . +*********************************************************************/ + +#ifndef KWIN_TILE_H +#define KWIN_TILE_H + +#include +#include + +namespace KWin +{ + +class Client; +class Workspace; + +class Tile + { + public: + enum Direction { + Top, + Right, + Bottom, + Left + }; + + enum LayoutMode { + UseRatio, // uses m_ratio + UseGeometry, // uses current geometry of children + Equal // distribute equally + }; + + Tile( Client *c, const QRect& area); + Tile(const Tile& orig); + virtual ~Tile(); + void setGeometry(const QRect& area); + void setGeometry(int x, int y, int w, int h); + + void resize( const QRect area ); + + void restorePreviousGeometry(); + + void commit(); + + void focus(); + + // :| the float datatype interferes with naming + void floatTile(); + void unfloatTile(); + + bool minimized() const; + bool floating() const; + bool ignoreGeometry() const; + QRect geometry() const; + Client* client() const; + + void dumpTile( const QString& indent = "" ) const; + + private: + + // ------------- + // PROPERTIES + // ------------- + + // our client + Client *m_client; + + // tiled geometry + QRect m_geom; + // before tiling was enabled, if any + QRect m_prevGeom; + + bool m_floating; + + }; + +inline QRect Tile::geometry() const + { + return m_geom; + } + +inline Client* Tile::client() const + { + return m_client; + } + +inline bool Tile::floating() const + { + return m_floating; + } + +/* + * should be respected by all geometry modifying methods. + * It returns true if the Tile is 'out' of the layout, + * due to being minimized, floating or for some other reason. + */ +inline bool Tile::ignoreGeometry() const + { + return minimized() || floating(); + } + +inline void Tile::setGeometry(const QRect& area) + { + setGeometry(area.x(), area.y(), area.width(), area.height()); + } + +} // namespace +#endif + diff --git a/tiling.cpp b/tiling.cpp new file mode 100644 index 0000000000..cb6367f5e5 --- /dev/null +++ b/tiling.cpp @@ -0,0 +1,504 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2009 Nikhil Marathe + +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 . +*********************************************************************/ + +// 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 +#include +#include "lib/kdecoration.h" + + +namespace KWin +{ + +bool Workspace::tilingMode() const + { + return tilingMode_; + } + +void Workspace::setTilingMode( bool tiling ) + { + if( tilingMode() == tiling ) return; + tilingMode_ = tiling; + + if( tilingMode_ ) + { + tilingLayouts.resize( numberOfDesktops() + 1 ); + foreach( Client *c, stackingOrder() ) + { + createTile( c ); + } + } + else + { + foreach( TilingLayout *t, tilingLayouts ) + { + if( t ) + delete t; + } + tilingLayouts.clear(); + } + } + +void Workspace::slotToggleTiling() + { + if ( tilingMode() ) + { + setTilingMode( false ); + QString message = i18n( "Tiling Disabled" ); + KNotification::event( "tilingdisabled", message, QPixmap(), NULL, KNotification::CloseOnTimeout, KComponentData( "kwin" ) ); + } + else + { + setTilingMode( 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( !tilingMode() || !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::DefaultL, 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(); + // TODO: if application specific settings + // to ignore, put them here + + // as suggested by Lucas Murray + // see Client::manage ( ~ line 270 of manage.cpp at time of writing ) + // This is useful to ignore plasma widget placing. + // According to the ICCCM if an application + // or user requests a certain geometry + // respect it + long msize; + XSizeHints xSizeHint; + XGetWMNormalHints(display(), c->window(), &xSizeHint, &msize); + if( xSizeHint.flags & PPosition || + xSizeHint.flags & USPosition ) { + kDebug(1212) << "Not tileable due to USPosition requirement"; + return false; + } + + kDebug(1212) << "c->isNormalWindow()" << c->isNormalWindow() ; + kDebug(1212) << "c->isUtility() " << c->isUtility() ; + kDebug(1212) << "c->isDialog() " << c->isDialog() ; + kDebug(1212) << "c->isSplash() " << c->isSplash() ; + kDebug(1212) << "c->isToolbar() " << c->isToolbar() ; + kDebug(1212) << "c->isPopupMenu() " << c->isPopupMenu() ; + kDebug(1212) << "c->isTransient() " << c->isTransient() ; + if( !c->isNormalWindow() || + c->isUtility() || + c->isDialog() || + c->isSplash() || + c->isToolbar() || + c->isPopupMenu() || + c->isTransient() ) + { + return false; + } + + return true; + } + +void Workspace::belowCursor() + { + // TODO + } + +Tile* Workspace::getNiceTile() const + { + if( !tilingMode() ) 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::notifyWindowResize( Client *c, const QRect &moveResizeGeom, const QRect &orig ) + { + if( tilingLayouts.value( c->desktop() ) == NULL ) + return; + tilingLayouts[ c->desktop() ]->clientResized( c, moveResizeGeom, orig ); + } + +void Workspace::notifyWindowMove( Client *c, const QRect &moveResizeGeom, const QRect &orig ) + { + if( tilingLayouts.value( c->desktop() ) == NULL ) + { + c->setGeometry( moveResizeGeom ); + return; + } + tilingLayouts[ c->desktop() ]->clientMoved( c, moveResizeGeom, orig ); + updateAllTiles(); + } + +void Workspace::notifyWindowResizeDone( Client *c, const QRect &moveResizeGeom, const QRect &orig, bool canceled ) + { + if( canceled ) + notifyWindowResize( c, orig, moveResizeGeom ); + else + notifyWindowResize( c, moveResizeGeom, orig ); + } + +void Workspace::notifyWindowMoveDone( Client *c, const QRect &moveResizeGeom, const QRect &orig, bool canceled ) + { + if( canceled ) + notifyWindowMove( c, orig, moveResizeGeom ); + else + notifyWindowMove( c, moveResizeGeom, orig ); + } + +void Workspace::notifyWindowDesktopChanged( 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::DefaultL, this ); + } + + if( t ) + tilingLayouts[ c->desktop() ]->addTile( t ); + + tilingLayouts[ old_desktop ]->removeTile( c ); + tilingLayouts[ old_desktop ]->commit(); + } + } + +// TODO: make this configurable +/* + * If a floating window was activated, raise all floating windows. + * If a tiled window was activated, lower all floating windows. + */ +void Workspace::notifyWindowActivated( Client *c ) + { + if( c == NULL ) + return; + + if( options->tilingRaisePolicy == 1 ) // individual raise/lowers + return; + + if( tilingLayouts.value( c->desktop() ) ) + { + QList 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 ) + { + kDebug(1212) << t->client() << t->floating(); + 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::notifyWindowMinimizeToggled( Client *c ) + { + if( tilingLayouts.value( c->desktop() ) ) + { + tilingLayouts[ c->desktop() ]->clientMinimizeToggled( c ); + } + } + +void Workspace::notifyWindowMaximized( 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 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::slotLeft() + { + focusTile( Tile::Left ); + } + +void Workspace::slotRight() + { + focusTile( Tile::Right ); + } + +void Workspace::slotTop() + { + focusTile( Tile::Top ); + } + +void Workspace::slotBottom() + { + focusTile( Tile::Bottom ); + } + +void Workspace::slotMoveLeft() + { + moveTile( Tile::Left ); + } + +void Workspace::slotMoveRight() + { + moveTile( Tile::Right ); + } + +void Workspace::slotMoveTop() + { + moveTile( Tile::Top ); + } + +void Workspace::slotMoveBottom() + { + 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 + diff --git a/tilinglayout.cpp b/tilinglayout.cpp new file mode 100644 index 0000000000..38b5035421 --- /dev/null +++ b/tilinglayout.cpp @@ -0,0 +1,218 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2009 Nikhil Marathe + +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 . +*********************************************************************/ + +#include "tilinglayout.h" + +#include + +#include "client.h" +#include "tile.h" +#include "workspace.h" + +namespace KWin +{ + +TilingLayout::TilingLayout( Workspace *w ) + : m_workspace( w ) + { + } + +TilingLayout::~TilingLayout() + { + } + +unsigned int TilingLayout::findTilePos( Client *c ) const + { + unsigned int i = 0; + foreach( Tile *t, m_tiles ) + { + if( t->client() == c ) + return i; + i++; + } + return -1; + } + +Tile* TilingLayout::findTile( Client *c ) const + { + unsigned int i = findTilePos( c ); + if( i != -1 ) + return m_tiles[ i ]; + return NULL; + } + +void TilingLayout::clientMinimizeToggled( Client *c ) + { + Tile *t = findTile( c ); + if( t ) + arrange( layoutArea( t ) ); + } + +void TilingLayout::clientResized( Client *c, const QRect &moveResizeGeom, const QRect &orig ) + { + if( moveResizeGeom == orig ) + return; + + Tile *t = findTile( c ); + if( !t || t->ignoreGeometry() ) + { + c->setGeometry( moveResizeGeom ); + return; + } + + t->setGeometry( moveResizeGeom ); + commit(); + } + +void TilingLayout::clientMoved( Client *c, const QRect &moveResizeGeom, const QRect &orig ) + { + if( moveResizeGeom == orig ) + return; + + Tile *t = findTile( c ); + if( !t ) + { + c->setGeometry( moveResizeGeom ); + return; + } + if( t->floating() ) + { + t->setGeometry( moveResizeGeom ); + t->commit(); + return; + } + + Tile *r = findTileBelowPoint( QCursor::pos() ); + // TODO: if the client moved in from another desktop, don't swap, add + if( r && t ) + { + swapTiles( r, t ); + } + } + +void TilingLayout::swapTiles( Tile *a, Tile *b ) + { + if( a && b ) + { + // t is the tile the user requested a move of + // r is the tile below it + int a_index = tiles().indexOf( a ); + int b_index = tiles().indexOf( b ); + + // use m_tiles since tiles() is const + // not sure how good an idea this is + m_tiles.replace( a_index, b ); + m_tiles.replace( b_index, a ); + arrange( layoutArea( a ) ); + } + } + +void TilingLayout::addTileNoArrange( Tile * t ) + { + m_tiles.append( t ); + postAddTile( t ); + } + +void TilingLayout::addTile( Tile *t ) + { + addTileNoArrange( t ); + arrange( layoutArea( t ) ); + } + +void TilingLayout::addTile( Client *c ) + { + } + +void TilingLayout::removeTileNoArrange( Tile * t ) + { + if( t == NULL ) + return; + preRemoveTile( t ); + m_tiles.removeOne( t ); + } + +const QRect TilingLayout::layoutArea( Tile *t ) const + { + return m_workspace->clientArea( PlacementArea, t->client() ); + } + +void TilingLayout::removeTile( Tile *t ) + { + if( t == NULL ) + return; + removeTileNoArrange( t ); + arrange( layoutArea( t ) ); + } + +void TilingLayout::removeTile( Client *c ) + { + removeTile( findTile( c ) ); + } + +void TilingLayout::toggleFloatTile( Client *c ) + { + Tile *t = findTile( c ); + if( t && t->floating() ) + t->unfloatTile(); + else if( t ) + t->floatTile(); + + if( t ) + arrange( layoutArea( t ) ); + } + +Tile* TilingLayout::findTileBelowPoint( const QPoint &p ) const + { + foreach( Tile *t, tiles() ) + { + if( t->floating() ) + continue; + if( t->geometry().contains( p ) ) + return t; + } + + return NULL; + } + +void TilingLayout::commit() + { + foreach( Tile *t, m_tiles ) + t->commit(); + } + +/* + * Default is to allow no resizing + */ +KDecorationDefines::Position TilingLayout::resizeMode( Client *c, KDecorationDefines::Position currentMode ) const + { + Tile *t = findTile( c ); + // if not tiled, allow resize + if( !t ) + return currentMode; + + if( t && t->floating() ) + return currentMode; + // We return PositionCenter since it makes + // no sense in resizing. + return KDecorationDefines::PositionCenter; + } + +} // end namespace + diff --git a/tilinglayout.h b/tilinglayout.h new file mode 100644 index 0000000000..48c988c016 --- /dev/null +++ b/tilinglayout.h @@ -0,0 +1,118 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2009 Nikhil Marathe + +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 . +*********************************************************************/ + +#ifndef KWIN_TILINGLAYOUT_H +#define KWIN_TILINGLAYOUT_H + +#include +#include + +#include "lib/kdecoration.h" + +namespace KWin +{ + +class Workspace; +class Client; +class Tile; + +class TilingLayout + { + public: + TilingLayout( Workspace *w ); + virtual ~TilingLayout(); + + virtual void clientResized( Client *c, const QRect &moveResizeGeom, const QRect &orig ); + void clientMoved( Client *c, const QRect &moveResizeGeom, const QRect &orig ); + void clientMinimizeToggled( Client *c ); + + void commit(); + + void setLayoutType( int t ); + int layoutType() const; + + void addTile( Tile *t ); + void addTile( Client *c ); + void removeTile( Tile *t ); + void removeTile( Client *c ); + void toggleFloatTile( Client *c ); + void swapTiles( Tile *a, Tile *b ); + + virtual KDecorationDefines::Position resizeMode( Client *c, KDecorationDefines::Position currentMode ) const; + + const QList& tiles() const; + Tile* findTile( Client *c ) const; + + protected: + Workspace * workspace() const; + const QRect layoutArea( Tile *t ) const; + // currently only required by floating layout + virtual void postAddTile( Tile *t ); + virtual void preRemoveTile( Tile *t ); + + private: + unsigned int findTilePos( Client *c ) const; + virtual void arrange( QRect wgeom ) = 0; + + void addTileNoArrange( Tile *t ); + void removeTileNoArrange( Tile *t ); + + Tile* findTileBelowPoint( const QPoint &p ) const; + + + QList m_tiles; + int m_layoutType; + Workspace *m_workspace; + + + friend class TilingLayoutFactory; + }; + +inline void TilingLayout::setLayoutType( int t ) + { + m_layoutType = t; + } + +inline int TilingLayout::layoutType() const + { + return m_layoutType; + } + +inline const QList& TilingLayout::tiles() const + { + return m_tiles; + } + +inline Workspace* TilingLayout::workspace() const + { + return m_workspace; + } + +inline void TilingLayout::postAddTile( Tile *t ) + { + } + +inline void TilingLayout::preRemoveTile( Tile *t ) + { + } + +} // end namespace + +#endif diff --git a/tilinglayoutfactory.cpp b/tilinglayoutfactory.cpp new file mode 100644 index 0000000000..a684ebe90c --- /dev/null +++ b/tilinglayoutfactory.cpp @@ -0,0 +1,126 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2009 Nikhil Marathe + +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 . +*********************************************************************/ + +#include "tilinglayoutfactory.h" + +#include +#include +#include +#include +#include + +#include "tile.h" +#include "client.h" + +#include "tilinglayouts/spiral/spiral.h" +#include "tilinglayouts/columns/columns.h" +#include "tilinglayouts/floating/floating.h" + +// w is the workspace pointer +#define ADD_LAYOUT( lay ) \ + case lay##L:\ + kDebug(1212) << #lay;\ + layout = new lay( w );\ + layout->setLayoutType( lay##L );\ + KNotification::event( "tilinglayoutchanged", \ + i18n( "Layout changed to %1" ).arg( #lay ),\ + QPixmap(), NULL, KNotification::CloseOnTimeout, KComponentData( "kwin" ) );\ + break + +namespace KWin +{ + +TilingLayout* TilingLayoutFactory::createLayout( int type, Workspace *w ) + { + Q_ASSERT( type != First && type != Last ); + TilingLayout *layout; + + /* For new layouts, make a case entry here */ + switch( type ) + { + case DefaultL: // NOTE: fall through makes first layout default + layout = createLayout( indexToLayoutIndex( options->tilingLayout ), w ); + break; + + ADD_LAYOUT( Spiral ); + ADD_LAYOUT( Columns ); + ADD_LAYOUT( Floating ); + + default: + kDebug(1212) << "INVALID LAYOUT!"; + return NULL; + } + return layout; + } + +// if next, goes next, otherwise previous +TilingLayout* TilingLayoutFactory::cycleLayout( TilingLayout *curr, bool next ) + { + int type = curr->layoutType(); + + if( next ) + { + type++; + + if( type >= Last ) + type = First + 1; + } + else + { + type--; + + if( type <= First ) + type = Last - 1; + } + + QList tiles = curr->tiles(); //root->flatten(); + + TilingLayout *l = createLayout( type, curr->workspace() ); + + foreach( Tile *t, tiles ) + { + curr->removeTileNoArrange( t ); + } + + Tile *last = tiles.takeLast(); + foreach( Tile *t, tiles ) + { + l->addTileNoArrange( t ); + } + l->addTile( last ); + + return l; + } + +/** + * Returns the appropriate layout enum item + * Meant to be used with a combo box. + * This function handles the issues of DefaultL and First and Last layouts + */ +int TilingLayoutFactory::indexToLayoutIndex( int index ) + { + int layout = DefaultL + index + 1; + if( layout >= Last ) + layout = DefaultL + 1; + if( layout <= First ) + layout = Last - 1; + return layout; + } +} // end namespace diff --git a/tilinglayoutfactory.h b/tilinglayoutfactory.h new file mode 100644 index 0000000000..c4a8b8376a --- /dev/null +++ b/tilinglayoutfactory.h @@ -0,0 +1,71 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2009 Nikhil Marathe + +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 . +*********************************************************************/ + +#ifndef KWIN_TILINGLAYOUTFACTORY_H +#define KWIN_TILINGLAYOUTFACTORY_H + +namespace KWin +{ + +class Workspace; +class TilingLayout; +class Tile; +class TilingLayoutFactory + { + public: + /** When adding your own layout, edit this + * Remember to suffix an L for now + */ + enum Layouts { + First, // special, do not modify/move + DefaultL, + + /* Actual layouts */ + SpiralL, + ColumnsL, + FloatingL, + /* Put your layout above this line ^^^ */ + + Last // special, do not modify/move + }; + + static TilingLayout* createLayout( int type, Workspace * ); + static TilingLayout* nextLayout( TilingLayout *curr ); + static TilingLayout* previousLayout( TilingLayout *curr ); + + static int indexToLayoutIndex( int index ); + + private: + static TilingLayout* cycleLayout( TilingLayout *curr, bool next ); + + }; + +inline TilingLayout* TilingLayoutFactory::nextLayout( TilingLayout *curr ) + { + return cycleLayout( curr, true ); + } + +inline TilingLayout* TilingLayoutFactory::previousLayout( TilingLayout *curr ) + { + return cycleLayout( curr, false ); + } +} // end namespace + +#endif diff --git a/tilinglayouts/columns/columns.cpp b/tilinglayouts/columns/columns.cpp new file mode 100644 index 0000000000..c6481f912d --- /dev/null +++ b/tilinglayouts/columns/columns.cpp @@ -0,0 +1,162 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2009 Nikhil Marathe + +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 . +*********************************************************************/ + +#include "columns.h" + +#include "client.h" +#include "tile.h" + +#include "lib/kdecoration.h" + +namespace KWin +{ + +// TODO: caching of actually tiled windows +// Columns is doing a lot of looping +// checking which tiles are actually *tiled* +// ( ie. not floating or minimized ) +// This can probably be moved to TilingLayout +// and cached. But remember to preserve order! + +Columns::Columns( Workspace *w ) + : TilingLayout( w ) + , m_leftWidth( 0 ) + { + } + +KDecorationDefines::Position Columns::resizeMode( Client *c, KDecorationDefines::Position currentMode ) const + { + Tile *t = findTile( c ); + + if( !t ) + return currentMode; + + if( t && t->floating() ) + return currentMode; + + QList tiled( tiles() ); + + QMutableListIterator i(tiled); + while( i.hasNext() ) + { + Tile *tile = i.next(); + if( tile->ignoreGeometry() ) + i.remove(); + } + + if( tiled.first() == t + && ( currentMode == KDecorationDefines::PositionRight + || currentMode == KDecorationDefines::PositionTopRight + || currentMode == KDecorationDefines::PositionBottomRight ) ) + { + return KDecorationDefines::PositionRight; + } + + // in right column so only left resize allowed + if( tiled.contains( t ) + && ( tiled.first() != t ) + && ( currentMode == KDecorationDefines::PositionLeft + || currentMode == KDecorationDefines::PositionTopLeft + || currentMode == KDecorationDefines::PositionBottomLeft ) ) + { + return KDecorationDefines::PositionLeft; + } + + return KDecorationDefines::PositionCenter; + } + +void Columns::clientResized( Client *c, const QRect &moveResizeGeom, const QRect &orig ) + { + TilingLayout::clientResized( c, moveResizeGeom, orig ); + + Tile *t = findTile( c ); + + QList tiled( tiles() ); + + QMutableListIterator i(tiled); + while( i.hasNext() ) + { + Tile *tile = i.next(); + if( tile->ignoreGeometry() ) + i.remove(); + } + + if( tiled.first() == t ) + { + m_leftWidth = moveResizeGeom.width(); + } + else + { + m_leftWidth = layoutArea( t ).width() - moveResizeGeom.width(); + } + + arrange( layoutArea( t ) ); + } + +void Columns::arrange( QRect wgeom ) + { + QList tiled( tiles() ); + + QMutableListIterator i(tiled); + while( i.hasNext() ) + { + Tile *t = i.next(); + if( t->ignoreGeometry() ) + i.remove(); + } + + int n = tiled.length(); + if( n < 1 ) + return; + if( n == 1 ) + { + tiled.first()->setGeometry( wgeom ); + tiled.first()->commit(); + return; + } + + // save the original before we mangle it + int totalWidth = wgeom.width(); + if( m_leftWidth == 0 ) + m_leftWidth = wgeom.width() / 2; + + if( n > 1 ) + wgeom.setWidth( m_leftWidth ); + tiled.first()->setGeometry( wgeom ); + tiled.first()->commit(); + + wgeom.moveLeft( wgeom.x() + m_leftWidth ); + wgeom.setWidth( totalWidth - m_leftWidth ); + int ht = wgeom.height()/(n-1); + wgeom.setHeight( ht ); + + int mult = 0; + int originalTop = wgeom.y(); + for( QList::const_iterator it = ++tiled.begin() ; it != tiled.end() ; it++ ) + { + if( (*it)->floating() ) + continue; + (*it)->setGeometry( wgeom ); + (*it)->commit(); + mult++; + wgeom.moveTop( originalTop + mult*ht ); + } + } +} diff --git a/tilinglayouts/columns/columns.h b/tilinglayouts/columns/columns.h new file mode 100644 index 0000000000..33ab808c74 --- /dev/null +++ b/tilinglayouts/columns/columns.h @@ -0,0 +1,49 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2009 Nikhil Marathe + +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 . +*********************************************************************/ + +#ifndef COLUMNS_H +#define COLUMNS_H + +#include + +#include "tilinglayout.h" + +namespace KWin +{ +class Workspace; +class Tile; +class Client; + +// simulates a 2 column right layout for now, make it arbitrary +// in columns and direction +class Columns : public TilingLayout + { + public: + Columns( Workspace * ); + KDecorationDefines::Position resizeMode( Client *c, KDecorationDefines::Position currentMode ) const; + void clientResized( Client *c, const QRect &moveResizeGeom, const QRect &orig ); + + private: + void arrange( QRect wgeom ); + int m_leftWidth; // width of left column + }; +} // end namespace + +#endif diff --git a/tilinglayouts/floating/floating.cpp b/tilinglayouts/floating/floating.cpp new file mode 100644 index 0000000000..0428d6f1a8 --- /dev/null +++ b/tilinglayouts/floating/floating.cpp @@ -0,0 +1,61 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2009 Nikhil Marathe + +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 . +*********************************************************************/ + +#include "floating.h" + +#include "tile.h" +#include "client.h" + +namespace KWin +{ + +Floating::Floating( Workspace *w ) + : TilingLayout( w ) + { + } + +void Floating::postAddTile( Tile *t ) + { + if( t->floating() ) + was_floating.insert( t ); + } + +void Floating::arrange( QRect wgeom ) + { + foreach( Tile *t, tiles() ) + { + if( !t->floating() ) + t->floatTile(); + workspace()->place( t->client(), wgeom ); + t->setGeometry( t->client()->geometry() ); + } + } + +void Floating::preRemoveTile( Tile *t ) + { + if( ! was_floating.contains( t ) ) + t->unfloatTile(); + } + +Floating::~Floating() + { + } + +} diff --git a/tilinglayouts/floating/floating.h b/tilinglayouts/floating/floating.h new file mode 100644 index 0000000000..5481b1af80 --- /dev/null +++ b/tilinglayouts/floating/floating.h @@ -0,0 +1,59 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2009 Nikhil Marathe + +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 . +*********************************************************************/ + +#ifndef FLOATING_H +#define FLOATING_H + +#include +#include + +#include "tilinglayout.h" +#include "tile.h" + +namespace KWin +{ +class Workspace; +class Floating : public TilingLayout + { + public: + Floating( Workspace * ); + ~Floating(); + + private: + void arrange( QRect wgeom ); + void postAddTile( Tile *t ); + void preRemoveTile( Tile *t ); + + Tile::Direction m_dir; + Tile *m_split; + + /* + * Tiles are added to was_floating if they + * were floating before being added to this layout + * + * When the layout is changed, was_floating is + * referred to, to restore the final state of the + * Tile + */ + QSet was_floating; + }; +} // end namespace + +#endif diff --git a/tilinglayouts/spiral/spiral.cpp b/tilinglayouts/spiral/spiral.cpp new file mode 100644 index 0000000000..d7596cb8f2 --- /dev/null +++ b/tilinglayouts/spiral/spiral.cpp @@ -0,0 +1,82 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2009 Nikhil Marathe + +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 . +*********************************************************************/ + +#include "spiral.h" + +#include "client.h" +#include "tile.h" + +namespace KWin +{ + +Spiral::Spiral( Workspace *w ) + : TilingLayout( w ) + { + } + +Spiral::~Spiral() + { + } + +void Spiral::arrange( QRect wgeom ) + { + QList tiled( tiles() ); + + QMutableListIterator it(tiled); + while( it.hasNext() ) + { + Tile *t = it.next(); + if( t->ignoreGeometry() ) + it.remove(); + } + + int n = tiled.length(); + int i = 1; + + foreach( Tile *t, tiled ) + { + if( t->floating() ) + continue; + + if( i < n ) + { + if( i % 2 == 0 ) + wgeom.setHeight( wgeom.height() / 2 ); + else + wgeom.setWidth( wgeom.width() / 2 ); + } + + if( i % 4 == 0 ) + wgeom.moveLeft( wgeom.x() - wgeom.width() ); + else if( i % 2 == 0 || ( i % 4 == 3 && i < n ) ) + wgeom.moveLeft( wgeom.x() + wgeom.width() ); + + if( i % 4 == 1 && i != 1 ) + wgeom.moveTop( wgeom.y() - wgeom.height() ); + else if( i % 2 == 1 && i != 1 + || ( i % 4 == 0 && i < n ) ) + wgeom.moveTop( wgeom.y() + wgeom.height() ); + + t->setGeometry( wgeom ); + t->commit(); + i++; + } + } +} diff --git a/tilinglayouts/spiral/spiral.h b/tilinglayouts/spiral/spiral.h new file mode 100644 index 0000000000..01d5e4214b --- /dev/null +++ b/tilinglayouts/spiral/spiral.h @@ -0,0 +1,49 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2009 Nikhil Marathe + +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 . +*********************************************************************/ + +#ifndef SPIRAL_H +#define SPIRAL_H + +#include + +#include "tilinglayout.h" + +namespace KWin +{ +class Workspace; +class Tile; +class Client; + +class Spiral : public TilingLayout + { + public: + Spiral( Workspace * ); + ~Spiral(); + + void addTile( Tile *t ); + void removeTile( Tile *t ); + + private: + void arrange( QRect wgeom ); + + }; +} // end namespace + +#endif diff --git a/useractions.cpp b/useractions.cpp index 5f71163712..c98b9d4773 100644 --- a/useractions.cpp +++ b/useractions.cpp @@ -668,6 +668,15 @@ void Workspace::performWindowOperation( Client* c, Options::WindowOperation op ) if ( !c ) return; + if( tilingMode() + && ( op == Options::MaximizeOp + || op == Options::HMaximizeOp + || op == Options::VMaximizeOp + || op == Options::RestoreOp ) ) + { + notifyWindowMaximized( c, op ); + } + if (op == Options::MoveOp || op == Options::UnrestrictedMoveOp ) QCursor::setPos( c->geometry().center() ); if (op == Options::ResizeOp || op == Options::UnrestrictedResizeOp ) diff --git a/workspace.cpp b/workspace.cpp index 57a4668d8d..dfcd65c337 100644 --- a/workspace.cpp +++ b/workspace.cpp @@ -44,6 +44,7 @@ along with this program. If not, see . #include #include "client.h" +#include "tile.h" #include "tabbox.h" #include "desktopchangeosd.h" #include "atoms.h" @@ -56,6 +57,7 @@ along with this program. If not, see . #include "scene.h" #include "deleted.h" #include "effects.h" +#include "tilinglayout.h" #include #include @@ -151,6 +153,7 @@ Workspace::Workspace( bool restore ) , transSlider( NULL ) , transButton( NULL ) , forceUnredirectCheck( true ) + , tilingMode_( false ) { (void) new KWinAdaptor( this ); @@ -469,6 +472,9 @@ void Workspace::init() outline_bottom = XCreateWindow( QX11Info::display(), QX11Info::appRootWindow(), 0, 0, 1, 1, 0, CopyFromParent, CopyFromParent, CopyFromParent, CWOverrideRedirect, &attr ); + // Enable/disable tiling + setTilingMode( options->tilingOn ); + // SELI TODO: This won't work with unreasonable focus policies, // and maybe in rare cases also if the selected client doesn't // want focus @@ -550,6 +556,11 @@ Client* Workspace::createClient( Window w, bool is_mapped ) return NULL; } addClient( c, Allowed ); + + tilingLayouts.resize( numberOfDesktops() + 1 ); + + createTile( c ); + if( scene ) scene->windowAdded( c ); if( effects ) @@ -649,6 +660,11 @@ void Workspace::removeClient( Client* c, allowed_t ) tab_box->nextPrev( true ); Q_ASSERT( clients.contains( c ) || desktops.contains( c )); + if( tilingMode() && tilingLayouts.value(c->desktop()) ) + { + removeTile( c ); + } + // TODO: if marked client is removed, notify the marked list clients.removeAll( c ); desktops.removeAll( c ); unconstrained_stacking_order.removeAll( c ); @@ -1134,6 +1150,10 @@ void Workspace::slotReconfigure() (*it)->applyWindowRules(); discardUsedWindowRules( *it, false ); } + + setTilingMode( options->tilingOn ); + // just so that we reset windows in the right manner, 'activate' the current active window + notifyWindowActivated( activeClient() ); rootInfo->setSupported( NET::WM2FrameOverlap, mgr->factory()->supports( AbilityExtendIntoClientArea ) ); } @@ -1398,7 +1418,14 @@ bool Workspace::setCurrentDesktop( int new_desktop ) rootInfo->setCurrentDesktop( currentDesktop() ); if( movingClient && !movingClient->isOnDesktop( new_desktop )) + { + int old_desktop = movingClient->desktop(); movingClient->setDesktop( new_desktop ); + if( tilingMode() ) + { + notifyWindowDesktopChanged( movingClient, old_desktop ); + } + } for( int i = stacking_order.size() - 1; i >= 0 ; --i ) if( stacking_order.at( i )->isOnDesktop( new_desktop )) @@ -1532,6 +1559,7 @@ void Workspace::setNumberOfDesktops( int n ) ++it) if( !(*it)->isOnAllDesktops() && (*it)->desktop() > numberOfDesktops() ) sendClientToDesktop( *it, numberOfDesktops(), true ); + // TODO: Tile should have a method allClients, push them into other tiles } if( old_number_of_desktops > numberOfDesktops() ) { @@ -1550,6 +1578,8 @@ void Workspace::setNumberOfDesktops( int n ) for( int i = 0; i < int( desktop_focus_chain.size() ); i++ ) desktop_focus_chain[i] = i+1; + tilingLayouts.resize( numberOfDesktops() + 1 ); + // reset the desktop change osd desktop_change_osd->numberDesktopsChanged(); // inform effects @@ -1564,6 +1594,7 @@ void Workspace::setNumberOfDesktops( int n ) */ void Workspace::sendClientToDesktop( Client* c, int desk, bool dont_activate ) { + int old_desktop = c->desktop(); bool was_on_desktop = c->isOnDesktop( desk ) || c->isOnAllDesktops(); c->setDesktop( desk ); if( c->desktop() != desk ) // No change or desktop forced @@ -1582,6 +1613,8 @@ void Workspace::sendClientToDesktop( Client* c, int desk, bool dont_activate ) else raiseClient( c ); + notifyWindowDesktopChanged( c, old_desktop ); + ClientList transients_stacking_order = ensureStackingOrder( c->transients() ); for( ClientList::ConstIterator it = transients_stacking_order.constBegin(); it != transients_stacking_order.constEnd(); diff --git a/workspace.h b/workspace.h index 80b53d21d0..368443b079 100644 --- a/workspace.h +++ b/workspace.h @@ -61,6 +61,8 @@ class TabBox; } class Client; +class Tile; +class TilingLayout; class ClientGroup; class DesktopChangeOSD; class RootInfo; @@ -169,6 +171,23 @@ class Workspace : public QObject, public KDecorationDefines void reserveElectricBorderActions( bool reserve ); void reserveElectricBorderSwitching( bool reserve ); + //------------------------------------------------- + // Tiling + public: + bool tilingMode() const; + void setTilingMode( bool tiling ); + void updateAllTiles(); + void notifyWindowResize( Client *c, const QRect &moveResizeGeom, const QRect &orig ); + void notifyWindowMove( Client *c, const QRect &moveResizeGeom, const QRect &orig ); + void notifyWindowResizeDone( Client *c, const QRect &moveResizeGeom, const QRect &orig, bool canceled ); + void notifyWindowMoveDone( Client *c, const QRect &moveResizeGeom, const QRect &orig, bool canceled ); + void notifyWindowDesktopChanged( Client *c, int old_desktop ); + void notifyWindowActivated( Client *c ); + void notifyWindowMinimizeToggled( Client *c ); + void notifyWindowMaximized( Client *c, WindowOperation op ); + + Position supportedTilingResizeMode( Client *c, Position currentMode ); + //------------------------------------------------- // Desktop layout @@ -286,6 +305,9 @@ class Workspace : public QObject, public KDecorationDefines int currentDesktop_; bool desktopLayoutDynamicity_; + bool tilingMode_; + QVector tilingLayouts; + //------------------------------------------------- // Unsorted @@ -396,6 +418,9 @@ class Workspace : public QObject, public KDecorationDefines void circulateDesktopApplications(); bool compositingActive(); bool waitForCompositingSetup(); + void toggleTiling(); + void nextTileLayout(); + void previousTileLayout(); void setCurrentScreen( int new_screen ); @@ -652,6 +677,24 @@ class Workspace : public QObject, public KDecorationDefines void suspendCompositing(); void suspendCompositing( bool suspend ); + void slotToggleTiling(); + void slotToggleFloating(); + void slotNextTileLayout(); + void slotPreviousTileLayout(); + + void slotLeft(); + void slotRight(); + void slotTop(); + void slotBottom(); + void slotMoveLeft(); + void slotMoveRight(); + void slotMoveTop(); + void slotMoveBottom(); + void belowCursor(); + + // NOTE: debug method + void dumpTiles() const; + void slotSwitchToTabLeft(); // Slot to move left the active Client. void slotSwitchToTabRight(); // Slot to move right the active Client. void slotRemoveFromGroup(); // Slot to remove the active client from its group. @@ -799,6 +842,16 @@ class Workspace : public QObject, public KDecorationDefines static NET::WindowType txtToWindowType( const char* txt ); static bool sessionInfoWindowTypeMatch( Client* c, SessionInfo* info ); + Tile* getNiceTile() const; + void createTile( Client *c ); + void removeTile( Client *c ); + bool tileable( Client *c ); + // int, and not Tile::Direction because + // we are using a forward declaration for Tile + Tile* findAdjacentTile( Tile *ref, int d ); + void focusTile( int d ); + void moveTile( int d ); + Client* active_client; Client* last_active_client; Client* most_recently_raised; // Used ONLY by raiseOrLowerClient() @@ -1276,6 +1329,30 @@ inline void Workspace::removeClientGroup( ClientGroup* group ) clientGroups.removeAll( group ); } +/* + * Called from D-BUS + */ +inline void Workspace::toggleTiling() + { + slotToggleTiling(); + } + +/* + * Called from D-BUS + */ +inline void Workspace::nextTileLayout() + { + slotNextTileLayout(); + } + +/* + * Called from D-BUS + */ +inline void Workspace::previousTileLayout() + { + slotPreviousTileLayout(); + } + } // namespace #endif