/******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 1999, 2000 Matthias Ettrich Copyright (C) 2003 Lubos Lunak 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 . *********************************************************************/ /* This file contains things relevant to direct user actions, such as responses to global keyboard shortcuts, or selecting actions from the window operations menu. */ #include "client.h" #include "workspace.h" #include "effects.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "killwindow.h" #include "tabbox.h" namespace KWin { //**************************************** // Workspace //**************************************** QMenu* Workspace::clientPopup() { if ( !popup ) { popup = new QMenu; popup->setFont(KGlobalSettings::menuFont()); connect( popup, SIGNAL( aboutToShow() ), this, SLOT( clientPopupAboutToShow() ) ); connect( popup, SIGNAL( triggered(QAction*) ), this, SLOT( clientPopupActivated(QAction*) ) ); advanced_popup = new QMenu( popup ); advanced_popup->setFont(KGlobalSettings::menuFont()); mKeepAboveOpAction = advanced_popup->addAction( i18n("Keep &Above Others") ); mKeepAboveOpAction->setIcon( KIcon( "go-up" ) ); KAction *kaction = qobject_cast( keys->action("Window Above Other Windows") ); if ( kaction!=0 ) mKeepAboveOpAction->setShortcut( kaction->globalShortcut().primary() ); mKeepAboveOpAction->setCheckable( true ); mKeepAboveOpAction->setData( Options::KeepAboveOp ); mKeepBelowOpAction = advanced_popup->addAction( i18n("Keep &Below Others") ); mKeepBelowOpAction->setIcon( KIcon( "go-down" ) ); kaction = qobject_cast( keys->action("Window Below Other Windows") ); if ( kaction!=0 ) mKeepBelowOpAction->setShortcut( kaction->globalShortcut().primary() ); mKeepBelowOpAction->setCheckable( true ); mKeepBelowOpAction->setData( Options::KeepBelowOp ); mFullScreenOpAction = advanced_popup->addAction( i18n("&Fullscreen") ); mFullScreenOpAction->setIcon( KIcon( "view-fullscreen" ) ); kaction = qobject_cast( keys->action("Window Fullscreen") ); if ( kaction!=0 ) mFullScreenOpAction->setShortcut( kaction->globalShortcut().primary() ); mFullScreenOpAction->setCheckable( true ); mFullScreenOpAction->setData( Options::FullScreenOp ); mNoBorderOpAction = advanced_popup->addAction( i18n("&No Border") ); kaction = qobject_cast( keys->action("Window No Border") ); if ( kaction!=0 ) mNoBorderOpAction->setShortcut( kaction->globalShortcut().primary() ); mNoBorderOpAction->setCheckable( true ); mNoBorderOpAction->setData( Options::NoBorderOp ); QAction *action = advanced_popup->addAction( i18n("Window &Shortcut...") ); action->setIcon( KIcon("configure-shortcuts") ); kaction = qobject_cast( keys->action("Setup Window Shortcut") ); if ( kaction!=0 ) action->setShortcut( kaction->globalShortcut().primary() ); action->setData( Options::SetupWindowShortcutOp ); action = advanced_popup->addAction( i18n("&Special Window Settings...") ); action->setIcon( KIcon( "preferences-system-windows-actions" ) ); action->setData( Options::WindowRulesOp ); action = advanced_popup->addAction( i18n("&Special Application Settings...") ); action->setIcon( KIcon( "preferences-system-windows-actions" ) ); action->setData( Options::ApplicationRulesOp ); trans_popup = 0; if (compositing()){ trans_popup = new QMenu( popup ); trans_popup->setFont(KGlobalSettings::menuFont()); connect( trans_popup, SIGNAL( triggered(QAction*) ), this, SLOT( setPopupClientOpacity(QAction*))); const int levels[] = { 100, 90, 75, 50, 25, 10 }; for( unsigned int i = 0; i < sizeof( levels ) / sizeof( levels[ 0 ] ); ++i ) { action = trans_popup->addAction( QString::number( levels[ i ] ) + "%" ); action->setCheckable( true ); action->setData( levels[ i ] ); } action = popup->addMenu( trans_popup ); action->setText( i18n("&Opacity") ); } mMoveOpAction = popup->addAction( i18n("&Move") ); mMoveOpAction->setIcon( KIcon( "transform-move" ) ); kaction = qobject_cast( keys->action("Window Move") ); if ( kaction!=0 ) mMoveOpAction->setShortcut( kaction->globalShortcut().primary() ); mMoveOpAction->setData( Options::MoveOp ); mResizeOpAction = popup->addAction( i18n("Re&size") ); kaction = qobject_cast( keys->action("Window Resize") ); if ( kaction!=0 ) mResizeOpAction->setShortcut( kaction->globalShortcut().primary() ); mResizeOpAction->setData( Options::ResizeOp ); mMinimizeOpAction = popup->addAction( i18n("Mi&nimize") ); kaction = qobject_cast( keys->action("Window Minimize") ); if ( kaction!=0 ) mMinimizeOpAction->setShortcut( kaction->globalShortcut().primary() ); mMinimizeOpAction->setData( Options::MinimizeOp ); mMaximizeOpAction = popup->addAction( i18n("Ma&ximize") ); kaction = qobject_cast( keys->action("Window Maximize") ); if ( kaction!=0 ) mMaximizeOpAction->setShortcut( kaction->globalShortcut().primary() ); mMaximizeOpAction->setCheckable( true ); mMaximizeOpAction->setData( Options::MaximizeOp ); mShadeOpAction = popup->addAction( i18n("Sh&ade") ); kaction = qobject_cast( keys->action("Window Shade") ); if ( kaction!=0 ) mShadeOpAction->setShortcut( kaction->globalShortcut().primary() ); mShadeOpAction->setCheckable( true ); mShadeOpAction->setData( Options::ShadeOp ); popup->addSeparator(); // Actions for window tabbing if( decorationSupportsClientGrouping() ) { mRemoveTabGroup = popup->addAction( i18n("Remove &from group") ); kaction = qobject_cast( keys->action("Remove TabGroup") ); if( kaction!=0 ) mRemoveTabGroup->setShortcut( kaction->globalShortcut().primary() ); mRemoveTabGroup->setData( Options::RemoveClientFromGroupOp ); mCloseGroup = popup->addAction( i18n("Close entire &group") ); mCloseGroup->setIcon( KIcon( "window-close" ) ); kaction = qobject_cast( keys->action("Close TabGroup") ); if( kaction!=0 ) mCloseGroup->setShortcut( kaction->globalShortcut().primary() ); mCloseGroup->setData( Options::CloseClientGroupOp ); popup->addSeparator(); } action = popup->addMenu( advanced_popup ); action->setText( i18n("Ad&vanced") ); popup->addSeparator(); if (!KGlobal::config()->isImmutable() && !KAuthorized::authorizeControlModules(Workspace::configModules(true)).isEmpty()) { action = popup->addAction( i18n("Configur&e Window Behavior...") ); action->setIcon( KIcon( "configure" ) ); connect( action, SIGNAL( triggered() ), this, SLOT( configureWM() ) ); popup->addSeparator(); } mCloseOpAction = popup->addAction( i18n("&Close") ); mCloseOpAction->setIcon( KIcon( "window-close" ) ); kaction = qobject_cast( keys->action("Window Close") ); if ( kaction!=0 ) mCloseOpAction->setShortcut( kaction->globalShortcut().primary() ); mCloseOpAction->setData( Options::CloseOp ); } return popup; } void Workspace::discardPopup() { delete popup; popup = NULL; desk_popup = NULL; switch_to_tab_popup = NULL; add_tabs_popup = NULL; } void Workspace::setPopupClientOpacity( QAction* action ) { if( active_popup_client == NULL ) return; int level = action->data().toInt(); active_popup_client->setOpacity( level / 100.0 ); } /*! The client popup menu will become visible soon. Adjust the items according to the respective popup client. */ void Workspace::clientPopupAboutToShow() { if ( !active_popup_client || !popup ) return; if ( numberOfDesktops() == 1 ) { delete desk_popup; desk_popup = 0; } else { initDesktopPopup(); } mResizeOpAction->setEnabled( active_popup_client->isResizable() ); mMoveOpAction->setEnabled( active_popup_client->isMovableAcrossScreens() ); mMaximizeOpAction->setEnabled( active_popup_client->isMaximizable() ); mMaximizeOpAction->setChecked( active_popup_client->maximizeMode() == Client::MaximizeFull ); mShadeOpAction->setEnabled( active_popup_client->isShadeable() ); mShadeOpAction->setChecked( active_popup_client->shadeMode() != ShadeNone ); mKeepAboveOpAction->setChecked( active_popup_client->keepAbove() ); mKeepBelowOpAction->setChecked( active_popup_client->keepBelow() ); mFullScreenOpAction->setEnabled( active_popup_client->userCanSetFullScreen() ); mFullScreenOpAction->setChecked( active_popup_client->isFullScreen() ); mNoBorderOpAction->setEnabled( active_popup_client->userCanSetNoBorder() ); mNoBorderOpAction->setChecked( active_popup_client->noBorder() ); mMinimizeOpAction->setEnabled( active_popup_client->isMinimizable() ); mCloseOpAction->setEnabled( active_popup_client->isCloseable() ); delete switch_to_tab_popup; switch_to_tab_popup = 0; delete add_tabs_popup; add_tabs_popup = 0; if( decorationSupportsClientGrouping() ) { const int tabGroupSize = active_popup_client->clientGroup() ? active_popup_client->clientGroup()->items().count() : 1; if( tabGroupSize > 1 ) initSwitchToTab(); initAddToTabGroup(); mRemoveTabGroup->setVisible( tabGroupSize > 1 ); mCloseGroup->setVisible( tabGroupSize > 1 ); } if( trans_popup != NULL ) { foreach( QAction* action, trans_popup->actions()) { if( action->data().toInt() == qRound( active_popup_client->opacity() * 100 )) action->setChecked( true ); else action->setChecked( false ); } } } void Workspace::initSwitchToTab() { if( switch_to_tab_popup ) return; switch_to_tab_popup = new QMenu( popup ); switch_to_tab_popup->setFont( KGlobalSettings::menuFont() ); connect( switch_to_tab_popup, SIGNAL( triggered( QAction* ) ), this, SLOT( slotSwitchToTab( QAction* ) ) ); connect( switch_to_tab_popup, SIGNAL( aboutToShow() ), this, SLOT( switchToTabPopupAboutToShow() ) ); QAction* action = switch_to_tab_popup->menuAction(); popup->insertAction( mRemoveTabGroup, action ); action->setText( i18n("Switch to window tab") ); } void Workspace::slotSwitchToTab( QAction* action ) { int side = action->data().toInt(); int c_id = active_popup_client->clientGroup()->indexOfVisibleClient(); int size = active_popup_client->clientGroup()->clients().count(); if( side == 0 ) // Left { if( c_id > 0 ) active_popup_client->clientGroup()->setVisible( c_id - 1 ); else active_popup_client->clientGroup()->setVisible( size - 1 ); } else if( side == 1 ) // Right { if( c_id < size - 1 ) active_popup_client->clientGroup()->setVisible( c_id + 1 ); else active_popup_client->clientGroup()->setVisible( 0 ); } else // Find the client { side -= 2; for( QList::const_iterator i = clientGroups.constBegin(); i != clientGroups.constEnd(); ++i ) { if( (*i)->contains( active_popup_client )) { (*i)->setVisible( side ); break; } } } } void Workspace::switchToTabPopupAboutToShow() { if( !switch_to_tab_popup ) return; switch_to_tab_popup->clear(); QAction* action = switch_to_tab_popup->addAction( i18n( "To the left" )); action->setData( 0 ); action = switch_to_tab_popup->addAction( i18n( "To the right" )); action->setData( 1 ); switch_to_tab_popup->addSeparator(); int index = 2; foreach( Client* c, active_popup_client->clientGroup()->clients() ) { if( c != active_popup_client->clientGroup()->visible() ) { action = switch_to_tab_popup->addAction( c->caption() ); action->setData( index ); } index++; } } void Workspace::initAddToTabGroup() { if( add_tabs_popup ) return; add_tabs_popup = new QMenu( popup ); add_tabs_popup->setFont( KGlobalSettings::menuFont() ); connect( add_tabs_popup, SIGNAL( triggered( QAction* ) ), this, SLOT( slotAddToTabGroup( QAction* ) ) ); // Merge to a group connect( add_tabs_popup, SIGNAL( aboutToShow() ), this, SLOT( groupTabPopupAboutToShow() ) ); // Show the possible groups to add QAction* action = add_tabs_popup->menuAction(); popup->insertAction( mRemoveTabGroup, action ); action->setText( i18n("Move window to group") ); } void Workspace::slotAddToTabGroup( QAction* action ) { if( !action->data().isValid() ) return; moveItemToClientGroup( active_popup_client->clientGroup(), active_popup_client->clientGroup()->indexOfClient( active_popup_client ), clientGroups[action->data().toInt()], -1 ); } void Workspace::groupTabPopupAboutToShow() { if( !add_tabs_popup ) return; add_tabs_popup->clear(); int index = 0; for( QList::const_iterator i = clientGroups.constBegin(); i != clientGroups.constEnd(); i++, index++ ) { if( !(*i)->contains( active_popup_client )) { QAction* action = add_tabs_popup->addAction( (*i)->visible()->caption() ); action->setData( index ); } } } void Workspace::initDesktopPopup() { if (desk_popup) return; desk_popup = new QMenu( popup ); desk_popup->setFont(KGlobalSettings::menuFont()); connect( desk_popup, SIGNAL( triggered(QAction*) ), this, SLOT( slotSendToDesktop(QAction*) ) ); connect( desk_popup, SIGNAL( aboutToShow() ), this, SLOT( desktopPopupAboutToShow() ) ); QAction *action = desk_popup->menuAction(); // set it as the first item popup->insertAction( trans_popup ? trans_popup->menuAction() : mMoveOpAction, action); action->setText( i18n("To &Desktop") ); } /*! Adjusts the desktop popup to the current values and the location of the popup client. */ void Workspace::desktopPopupAboutToShow() { if ( !desk_popup ) return; desk_popup->clear(); QAction *action = desk_popup->addAction( i18n("&All Desktops") ); action->setData( 0 ); action->setCheckable( true ); if ( active_popup_client && active_popup_client->isOnAllDesktops() ) action->setChecked( true ); desk_popup->addSeparator(); const int BASE = 10; for ( int i = 1; i <= numberOfDesktops(); i++ ) { QString basic_name("%1 %2"); if (iaddAction( basic_name.arg(i).arg( desktopName(i).replace( '&', "&&" ) ) ); action->setData( i ); action->setCheckable( true ); if ( active_popup_client && !active_popup_client->isOnAllDesktops() && active_popup_client->desktop() == i ) action->setChecked( true ); } } void Workspace::closeActivePopup() { if( active_popup ) { active_popup->close(); active_popup = NULL; active_popup_client = NULL; } } /*! Create the global accel object \c keys. */ void Workspace::initShortcuts() { keys = new KActionCollection( this ); KActionCollection* actionCollection = keys; QAction* a = 0L; // a separate KActionCollection is needed for the shortcut for disabling global shortcuts, // otherwise it would also disable itself disable_shortcuts_keys = new KActionCollection( this ); // TODO: PORT ME (KGlobalAccel related) // FIXME KAccel port... needed? //disable_shortcuts_keys->disableBlocking( true ); #define IN_KWIN #include "kwinbindings.cpp" readShortcuts(); } void Workspace::readShortcuts() { // TODO: PORT ME (KGlobalAccel related) //KGlobalAccel::self()->readSettings(); KAction *kaction = qobject_cast( keys->action("Walk Through Desktops") ); if ( kaction!=0 ) { cutWalkThroughDesktops = kaction->globalShortcut(); connect( kaction, SIGNAL(globalShortcutChanged(QKeySequence)), this, SLOT(slotWalkThroughDesktopsKeyChanged(QKeySequence))); } kaction = qobject_cast( keys->action("Walk Through Desktops (Reverse)") ); if ( kaction!=0 ) { cutWalkThroughDesktopsReverse = kaction->globalShortcut(); connect( kaction, SIGNAL(globalShortcutChanged(QKeySequence)), this, SLOT(slotWalkBackThroughDesktopsKeyChanged(QKeySequence))); } kaction = qobject_cast( keys->action("Walk Through Desktop List") ); if ( kaction!=0 ) { cutWalkThroughDesktopList = kaction->globalShortcut(); connect( kaction, SIGNAL(globalShortcutChanged(QKeySequence)), this, SLOT(slotWalkThroughDesktopListKeyChanged(QKeySequence))); } kaction = qobject_cast( keys->action("Walk Through Desktop List (Reverse)") ); if ( kaction!=0 ) { cutWalkThroughDesktopListReverse = kaction->globalShortcut(); connect( kaction, SIGNAL(globalShortcutChanged(QKeySequence)), this, SLOT(slotWalkBackThroughDesktopListKeyChanged(QKeySequence))); } kaction = qobject_cast( keys->action("Walk Through Windows") ); if ( kaction!=0 ) { cutWalkThroughWindows = kaction->globalShortcut(); connect( kaction, SIGNAL(globalShortcutChanged(QKeySequence)), this, SLOT(slotWalkThroughWindowsKeyChanged(QKeySequence))); } kaction = qobject_cast( keys->action("Walk Through Windows (Reverse)") ); if ( kaction!=0 ) { cutWalkThroughWindowsReverse = kaction->globalShortcut(); connect( kaction, SIGNAL(globalShortcutChanged(QKeySequence)), this, SLOT(slotWalkBackThroughWindowsKeyChanged(QKeySequence))); } kaction = qobject_cast( keys->action("Walk Through Window Tabs") ); if( kaction != 0 ) { cutWalkThroughGroupWindows = kaction->globalShortcut(); connect( kaction, SIGNAL( globalShortcutChanged( QKeySequence ) ), this, SLOT( slotMoveToTabRightKeyChanged( QKeySequence ) ) ); } kaction = qobject_cast( keys->action("Walk Through Window Tabs (Reverse)") ); if( kaction != 0 ) { cutWalkThroughGroupWindowsReverse = kaction->globalShortcut(); connect( kaction, SIGNAL( globalShortcutChanged( QKeySequence ) ), this, SLOT( slotMoveToTabLeftKeyChanged( QKeySequence ) ) ); } kaction = qobject_cast( keys->action("Walk Through Windows Alternative") ); if ( kaction!=0 ) { cutWalkThroughWindowsAlternative = kaction->globalShortcut(); connect( kaction, SIGNAL(globalShortcutChanged(QKeySequence)), this, SLOT(slotWalkThroughWindowsAlternativeKeyChanged(QKeySequence))); } kaction = qobject_cast( keys->action("Walk Through Windows Alternative (Reverse)") ); if ( kaction!=0 ) { cutWalkThroughWindowsAlternativeReverse = kaction->globalShortcut(); connect( kaction, SIGNAL(globalShortcutChanged(QKeySequence)), this, SLOT(slotWalkBackThroughWindowsAlternativeKeyChanged(QKeySequence))); } discardPopup(); // so that it's recreated next time } void Workspace::setupWindowShortcut( Client* c ) { assert( client_keys_dialog == NULL ); // TODO: PORT ME (KGlobalAccel related) //keys->setEnabled( false ); //disable_shortcuts_keys->setEnabled( false ); //client_keys->setEnabled( false ); client_keys_dialog = new ShortcutDialog( c->shortcut().primary()); client_keys_client = c; connect( client_keys_dialog, SIGNAL( dialogDone( bool )), SLOT( setupWindowShortcutDone( bool ))); QRect r = clientArea( ScreenArea, c ); QSize size = client_keys_dialog->sizeHint(); QPoint pos = c->pos() + c->clientPos(); if( pos.x() + size.width() >= r.right()) pos.setX( r.right() - size.width()); if( pos.y() + size.height() >= r.bottom()) pos.setY( r.bottom() - size.height()); client_keys_dialog->move( pos ); client_keys_dialog->show(); active_popup = client_keys_dialog; active_popup_client = c; } void Workspace::setupWindowShortcutDone( bool ok ) { // keys->setEnabled( true ); // disable_shortcuts_keys->setEnabled( true ); // client_keys->setEnabled( true ); if( ok ) client_keys_client->setShortcut( KShortcut( client_keys_dialog->shortcut()).toString()); closeActivePopup(); client_keys_dialog->deleteLater(); client_keys_dialog = NULL; client_keys_client = NULL; } void Workspace::clientShortcutUpdated( Client* c ) { QString key = QString( "_k_session:%1" ).arg(c->window()); QAction* action = client_keys->action( key.toLatin1().constData() ); if( !c->shortcut().isEmpty()) { if( action == NULL ) // new shortcut { action = client_keys->addAction(QString( key )); action->setText( i18n("Activate Window (%1)", c->caption()) ); connect( action, SIGNAL(triggered(bool)), c, SLOT(shortcutActivated()) ); } KAction *kaction = qobject_cast( action ); // no autoloading, since it's configured explicitly here and is not meant to be reused // (the key is the window id anyway, which is kind of random) kaction->setGlobalShortcut( c->shortcut(), KAction::ActiveShortcut, KAction::NoAutoloading ); kaction->setEnabled( true ); } else { KAction *kaction = qobject_cast( action ); if( kaction ) { kaction->forgetGlobalShortcut(); } delete action; } } void Workspace::clientPopupActivated( QAction *action ) { if ( !action->data().isValid() ) return; WindowOperation op = static_cast< WindowOperation >( action->data().toInt() ); Client* c = active_popup_client ? active_popup_client : active_client; QString type; switch( op ) { case FullScreenOp: if( !c->isFullScreen() && c->userCanSetFullScreen()) type = "fullscreenaltf3"; break; case NoBorderOp: if( !c->noBorder() && c->userCanSetNoBorder()) type = "noborderaltf3"; break; default: break; }; if( !type.isEmpty()) helperDialog( type, c ); performWindowOperation( c, op ); } void Workspace::performWindowOperation( Client* c, Options::WindowOperation op ) { if ( !c ) return; if (op == Options::MoveOp || op == Options::UnrestrictedMoveOp ) QCursor::setPos( c->geometry().center() ); if (op == Options::ResizeOp || op == Options::UnrestrictedResizeOp ) QCursor::setPos( c->geometry().bottomRight()); switch ( op ) { case Options::MoveOp: c->performMouseCommand( Options::MouseMove, cursorPos() ); break; case Options::UnrestrictedMoveOp: c->performMouseCommand( Options::MouseUnrestrictedMove, cursorPos() ); break; case Options::ResizeOp: c->performMouseCommand( Options::MouseResize, cursorPos() ); break; case Options::UnrestrictedResizeOp: c->performMouseCommand( Options::MouseUnrestrictedResize, cursorPos() ); break; case Options::CloseOp: c->closeWindow(); break; case Options::MaximizeOp: c->maximize( c->maximizeMode() == Client::MaximizeFull ? Client::MaximizeRestore : Client::MaximizeFull ); break; case Options::HMaximizeOp: c->maximize( c->maximizeMode() ^ Client::MaximizeHorizontal ); break; case Options::VMaximizeOp: c->maximize( c->maximizeMode() ^ Client::MaximizeVertical ); break; case Options::RestoreOp: c->maximize( Client::MaximizeRestore ); break; case Options::MinimizeOp: c->minimize(); break; case Options::ShadeOp: c->performMouseCommand( Options::MouseShade, cursorPos()); break; case Options::OnAllDesktopsOp: c->setOnAllDesktops( !c->isOnAllDesktops() ); break; case Options::FullScreenOp: c->setFullScreen( !c->isFullScreen(), true ); break; case Options::NoBorderOp: c->setNoBorder( !c->noBorder()); break; case Options::KeepAboveOp: { StackingUpdatesBlocker blocker( this ); bool was = c->keepAbove(); c->setKeepAbove( !c->keepAbove() ); if( was && !c->keepAbove()) raiseClient( c ); break; } case Options::KeepBelowOp: { StackingUpdatesBlocker blocker( this ); bool was = c->keepBelow(); c->setKeepBelow( !c->keepBelow() ); if( was && !c->keepBelow()) lowerClient( c ); break; } case Options::OperationsOp: c->performMouseCommand( Options::MouseShade, cursorPos()); break; case Options::WindowRulesOp: editWindowRules( c, false ); break; case Options::ApplicationRulesOp: editWindowRules( c, true ); break; case Options::SetupWindowShortcutOp: setupWindowShortcut( c ); break; case Options::LowerOp: lowerClient(c); break; case Options::ClientGroupDragOp: // Handled by decoration itself case Options::NoOp: break; case Options::RemoveClientFromGroupOp: c->clientGroup()->remove( c ); break; case Options::MoveClientInGroupLeftOp: { int c_id = c->clientGroup()->indexOfClient( c ); int size = c->clientGroup()->clients().count(); if( c_id > 0 ) c->clientGroup()->setVisible( c_id - 1 ); else c->clientGroup()->setVisible( size - 1 ); break; } case Options::MoveClientInGroupRightOp: { int c_id = c->clientGroup()->indexOfClient( c ); int size = c->clientGroup()->clients().count(); if( c_id < size - 1 ) c->clientGroup()->setVisible( c_id + 1 ); else c->clientGroup()->setVisible( 0 ); break; } case Options::CloseClientGroupOp: c->clientGroup()->closeAll(); } } /** * Called by the decoration in the new API to determine what buttons the user has configured for * window tab dragging and the operations menu. */ Options::WindowOperation Client::mouseButtonToWindowOperation( Qt::MouseButtons button ) { Options::MouseCommand com = Options::MouseNothing; bool active = isActive(); if( !wantsInput() ) // we cannot be active, use it anyway active = true; if( button == Qt::LeftButton ) com = active ? options->commandActiveTitlebar1() : options->commandInactiveTitlebar1(); else if( button == Qt::MidButton ) com = active ? options->commandActiveTitlebar2() : options->commandInactiveTitlebar2(); else if( button == Qt::RightButton ) com = active ? options->commandActiveTitlebar3() : options->commandInactiveTitlebar3(); // TODO: Complete the list if( com == Options::MouseClientGroupDrag ) return Options::ClientGroupDragOp; if( com == Options::MouseOperationsMenu ) return Options::OperationsOp; return Options::NoOp; } /*! Performs a mouse command on this client (see options.h) */ bool Client::performMouseCommand( Options::MouseCommand command, const QPoint &globalPos, bool handled ) { bool replay = false; switch (command) { case Options::MouseRaise: workspace()->raiseClient( this ); break; case Options::MouseLower: workspace()->lowerClient( this ); // As this most likely makes the window no longer visible change the // keyboard focus to the next available window. //workspace()->activateNextClient( this ); // Doesn't work when we lower a child window workspace()->activateClient( workspace()->topClientOnDesktop( workspace()->currentDesktop(), -1 )); break; case Options::MouseShade : toggleShade(); cancelShadeHoverTimer(); break; case Options::MouseSetShade: setShade( ShadeNormal ); cancelShadeHoverTimer(); break; case Options::MouseUnsetShade: setShade( ShadeNone ); cancelShadeHoverTimer(); break; case Options::MouseOperationsMenu: if ( isActive() && options->clickRaise ) autoRaise(); workspace()->showWindowMenu( globalPos, this ); break; case Options::MouseToggleRaiseAndLower: workspace()->raiseOrLowerClient( this ); break; case Options::MouseActivateAndRaise: replay = isActive(); // for clickraise mode workspace()->takeActivity( this, ActivityFocus | ActivityRaise, handled && replay ); workspace()->setActiveScreenMouse( globalPos ); break; case Options::MouseActivateAndLower: workspace()->requestFocus( this ); workspace()->lowerClient( this ); workspace()->setActiveScreenMouse( globalPos ); break; case Options::MouseActivate: replay = isActive(); // for clickraise mode workspace()->takeActivity( this, ActivityFocus, handled && replay ); workspace()->setActiveScreenMouse( globalPos ); break; case Options::MouseActivateRaiseAndPassClick: workspace()->takeActivity( this, ActivityFocus | ActivityRaise, handled ); workspace()->setActiveScreenMouse( globalPos ); replay = true; break; case Options::MouseActivateAndPassClick: workspace()->takeActivity( this, ActivityFocus, handled ); workspace()->setActiveScreenMouse( globalPos ); replay = true; break; case Options::MouseActivateRaiseAndMove: case Options::MouseActivateRaiseAndUnrestrictedMove: workspace()->raiseClient( this ); workspace()->requestFocus( this ); workspace()->setActiveScreenMouse( globalPos ); if( options->moveMode == Options::Transparent && isMovableAcrossScreens()) move_faked_activity = workspace()->fakeRequestedActivity( this ); // fallthrough case Options::MouseMove: case Options::MouseUnrestrictedMove: { if (!isMovableAcrossScreens()) break; if( moveResizeMode ) finishMoveResize( false ); mode = PositionCenter; buttonDown = true; moveOffset = QPoint( globalPos.x() - x(), globalPos.y() - y()); // map from global invertedMoveOffset = rect().bottomRight() - moveOffset; unrestrictedMoveResize = ( command == Options::MouseActivateRaiseAndUnrestrictedMove || command == Options::MouseUnrestrictedMove ); if( !startMoveResize()) buttonDown = false; updateCursor(); break; } case Options::MouseResize: case Options::MouseUnrestrictedResize: { if (!isResizable() || isShade()) break; if( moveResizeMode ) finishMoveResize( false ); buttonDown = true; moveOffset = QPoint( globalPos.x() - x(), globalPos.y() - y()); // map from global int x = moveOffset.x(), y = moveOffset.y(); bool left = x < width() / 3; bool right = x >= 2 * width() / 3; bool top = y < height() / 3; bool bot = y >= 2 * height() / 3; if (top) mode = left ? PositionTopLeft : (right ? PositionTopRight : PositionTop); else if (bot) mode = left ? PositionBottomLeft : (right ? PositionBottomRight : PositionBottom); else mode = (x < width() / 2) ? PositionLeft : PositionRight; invertedMoveOffset = rect().bottomRight() - moveOffset; unrestrictedMoveResize = ( command == Options::MouseUnrestrictedResize ); if( !startMoveResize()) buttonDown = false; updateCursor(); break; } case Options::MouseMaximize: maximize( Client::MaximizeFull ); break; case Options::MouseRestore: maximize( Client::MaximizeRestore ); break; case Options::MouseMinimize: minimize(); break; case Options::MouseAbove: { StackingUpdatesBlocker blocker( workspace()); if( keepBelow()) setKeepBelow( false ); else setKeepAbove( true ); break; } case Options::MouseBelow: { StackingUpdatesBlocker blocker( workspace()); if( keepAbove()) setKeepAbove( false ); else setKeepBelow( true ); break; } case Options::MousePreviousDesktop: workspace()->windowToPreviousDesktop( this ); break; case Options::MouseNextDesktop: workspace()->windowToNextDesktop( this ); break; case Options::MouseOpacityMore: if( !isDesktop() ) // No point in changing the opacity of the desktop setOpacity( qMin( opacity() + 0.1, 1.0 )); break; case Options::MouseOpacityLess: if( !isDesktop() ) // No point in changing the opacity of the desktop setOpacity( qMax( opacity() - 0.1, 0.1 )); break; case Options::MouseLeftGroupWindow: { int c_id = clientGroup()->indexOfClient( this ); int size = clientGroup()->clients().count(); if( c_id > 0 ) clientGroup()->setVisible( c_id - 1 ); else clientGroup()->setVisible( size - 1 ); } break; case Options::MouseRightGroupWindow: { int c_id = clientGroup()->indexOfClient( this ); int size = clientGroup()->clients().count(); if( c_id < size - 1 ) clientGroup()->setVisible( c_id + 1 ); else clientGroup()->setVisible( 0 ); } break; case Options::MouseClose: closeWindow(); break; case Options::MouseClientGroupDrag: case Options::MouseNothing: replay = true; break; } return replay; } // KDE4 remove me void Workspace::showWindowMenuAt( unsigned long, int, int ) { slotWindowOperations(); } void Workspace::loadEffect( const QString& name ) { if( effects ) static_cast(effects)->loadEffect( name ); } void Workspace::toggleEffect( const QString& name ) { if( effects ) static_cast(effects)->toggleEffect( name ); } void Workspace::unloadEffect( const QString& name ) { if( effects ) static_cast(effects)->unloadEffect( name ); } void Workspace::reconfigureEffect( const QString& name ) { if( effects ) static_cast(effects)->reconfigureEffect( name ); } QStringList Workspace::loadedEffects() const { QStringList listModulesLoaded; if ( effects ) listModulesLoaded = static_cast(effects)->loadedEffects(); return listModulesLoaded; } QStringList Workspace::listOfEffects() const { QStringList listModules; if ( effects ) listModules = static_cast(effects)->listOfEffects(); return listModules; } void Workspace::slotActivateAttentionWindow() { if( attention_chain.count() > 0 ) activateClient( attention_chain.first()); } void Workspace::slotSwitchDesktopNext() { int d = currentDesktop() + 1; if ( d > numberOfDesktops() ) { if ( options->rollOverDesktops ) { d = 1; } else { return; } } setCurrentDesktop(d); } void Workspace::slotSwitchDesktopPrevious() { int d = currentDesktop() - 1; if ( d <= 0 ) { if ( options->rollOverDesktops ) d = numberOfDesktops(); else return; } setCurrentDesktop(d); } void Workspace::slotSwitchDesktopRight() { int desktop = desktopToRight( currentDesktop(), options->rollOverDesktops); if( desktop == currentDesktop()) return; setCurrentDesktop( desktop ); } void Workspace::slotSwitchDesktopLeft() { int desktop = desktopToLeft( currentDesktop(), options->rollOverDesktops); if( desktop == currentDesktop()) return; setCurrentDesktop( desktop ); } void Workspace::slotSwitchDesktopUp() { int desktop = desktopAbove( currentDesktop(), options->rollOverDesktops); if( desktop == currentDesktop()) return; setCurrentDesktop( desktop ); } void Workspace::slotSwitchDesktopDown() { int desktop = desktopBelow( currentDesktop(), options->rollOverDesktops); if( desktop == currentDesktop()) return; setCurrentDesktop( desktop ); } void Workspace::slotSwitchToDesktop( int i ) { setCurrentDesktop( i ); } void Workspace::slotWindowToDesktop( int i ) { Client* c = active_popup_client ? active_popup_client : active_client; if( i >= 1 && i <= numberOfDesktops() && c && !c->isDesktop() && !c->isDock() && !c->isTopMenu()) sendClientToDesktop( c, i, true ); } void Workspace::slotSwitchToScreen( int i ) { setCurrentScreen( i ); } void Workspace::slotSwitchToNextScreen() { slotSwitchToScreen(( activeScreen() + 1 ) % numScreens()); } void Workspace::slotWindowToScreen( int i ) { Client* c = active_popup_client ? active_popup_client : active_client; if( i >= 0 && i <= numScreens() && c && !c->isDesktop() && !c->isDock() && !c->isTopMenu()) { sendClientToScreen( c, i ); } } void Workspace::slotWindowToNextScreen() { Client* c = active_popup_client ? active_popup_client : active_client; if( c && !c->isDesktop() && !c->isDock() && !c->isTopMenu()) { sendClientToScreen( c, ( c->screen() + 1 ) % numScreens()); } } /*! Maximizes the popup client */ void Workspace::slotWindowMaximize() { Client* c = active_popup_client ? active_popup_client : active_client; if ( c ) performWindowOperation( c, Options::MaximizeOp ); } /*! Maximizes the popup client vertically */ void Workspace::slotWindowMaximizeVertical() { Client* c = active_popup_client ? active_popup_client : active_client; if ( c ) performWindowOperation( c, Options::VMaximizeOp ); } /*! Maximizes the popup client horiozontally */ void Workspace::slotWindowMaximizeHorizontal() { Client* c = active_popup_client ? active_popup_client : active_client; if ( c ) performWindowOperation( c, Options::HMaximizeOp ); } /*! Minimizes the popup client */ void Workspace::slotWindowMinimize() { Client* c = active_popup_client ? active_popup_client : active_client; performWindowOperation( c, Options::MinimizeOp ); } /*! Shades/unshades the popup client respectively */ void Workspace::slotWindowShade() { Client* c = active_popup_client ? active_popup_client : active_client; performWindowOperation( c, Options::ShadeOp ); } /*! Raises the popup client */ void Workspace::slotWindowRaise() { Client* c = active_popup_client ? active_popup_client : active_client; if ( c ) raiseClient( c ); } /*! Lowers the popup client */ void Workspace::slotWindowLower() { Client* c = active_popup_client ? active_popup_client : active_client; if ( c ) { lowerClient( c ); // As this most likely makes the window no longer visible change the // keyboard focus to the next available window. //activateNextClient( c ); // Doesn't work when we lower a child window activateClient( topClientOnDesktop( currentDesktop(), -1 )); } } /*! Does a toggle-raise-and-lower on the popup client; */ void Workspace::slotWindowRaiseOrLower() { Client* c = active_popup_client ? active_popup_client : active_client; if ( c ) raiseOrLowerClient( c ); } void Workspace::slotWindowOnAllDesktops() { Client* c = active_popup_client ? active_popup_client : active_client; if( c ) c->setOnAllDesktops( !c->isOnAllDesktops()); } void Workspace::slotWindowFullScreen() { Client* c = active_popup_client ? active_popup_client : active_client; if( c ) performWindowOperation( c, Options::FullScreenOp ); } void Workspace::slotWindowNoBorder() { Client* c = active_popup_client ? active_popup_client : active_client; if( c ) performWindowOperation( c, Options::NoBorderOp ); } void Workspace::slotWindowAbove() { Client* c = active_popup_client ? active_popup_client : active_client; if( c ) performWindowOperation( c, Options::KeepAboveOp ); } void Workspace::slotWindowBelow() { Client* c = active_popup_client ? active_popup_client : active_client; if( c ) performWindowOperation( c, Options::KeepBelowOp ); } void Workspace::slotSetupWindowShortcut() { Client* c = active_popup_client ? active_popup_client : active_client; if( c ) performWindowOperation( c, Options::SetupWindowShortcutOp ); } /*! Toggles show desktop */ void Workspace::slotToggleShowDesktop() { setShowingDesktop( !showingDesktop() ); } /*! Move window to next desktop */ void Workspace::slotWindowToNextDesktop() { windowToNextDesktop( active_popup_client ? active_popup_client : active_client ); } void Workspace::windowToNextDesktop( Client* c ) { int d = currentDesktop() + 1; if ( d > numberOfDesktops() ) d = 1; if (c && !c->isDesktop() && !c->isDock() && !c->isTopMenu()) { setClientIsMoving( c ); setCurrentDesktop( d ); setClientIsMoving( NULL ); } } /*! Move window to previous desktop */ void Workspace::slotWindowToPreviousDesktop() { windowToPreviousDesktop( active_popup_client ? active_popup_client : active_client ); } void Workspace::windowToPreviousDesktop( Client* c ) { int d = currentDesktop() - 1; if ( d <= 0 ) d = numberOfDesktops(); if (c && !c->isDesktop() && !c->isDock() && !c->isTopMenu()) { setClientIsMoving( c ); setCurrentDesktop( d ); setClientIsMoving( NULL ); } } void Workspace::slotWindowToDesktopRight() { int d = desktopToRight( currentDesktop(), options->rollOverDesktops); if( d == currentDesktop()) return; Client* c = active_popup_client ? active_popup_client : active_client; if (c && !c->isDesktop() && !c->isDock() && !c->isTopMenu()) { setClientIsMoving( c ); setCurrentDesktop( d ); setClientIsMoving( NULL ); } } void Workspace::slotWindowToDesktopLeft() { int d = desktopToLeft( currentDesktop(), options->rollOverDesktops); if( d == currentDesktop()) return; Client* c = active_popup_client ? active_popup_client : active_client; if (c && !c->isDesktop() && !c->isDock() && !c->isTopMenu()) { setClientIsMoving( c ); setCurrentDesktop( d ); setClientIsMoving( NULL ); } } void Workspace::slotWindowToDesktopUp() { int d = desktopAbove( currentDesktop(), options->rollOverDesktops); if( d == currentDesktop()) return; Client* c = active_popup_client ? active_popup_client : active_client; if (c && !c->isDesktop() && !c->isDock() && !c->isTopMenu()) { setClientIsMoving( c ); setCurrentDesktop( d ); setClientIsMoving( NULL ); } } void Workspace::slotWindowToDesktopDown() { int d = desktopBelow( currentDesktop(), options->rollOverDesktops); if( d == currentDesktop()) return; Client* c = active_popup_client ? active_popup_client : active_client; if (c && !c->isDesktop() && !c->isDock() && !c->isTopMenu()) { setClientIsMoving( c ); setCurrentDesktop( d ); setClientIsMoving( NULL ); } } void Workspace::slotSwitchToTabRight() { if( !active_client ) return; int c_id = active_client->clientGroup()->indexOfClient( active_client ); int size = active_client->clientGroup()->clients().count(); if( c_id < size - 1 ) active_client->clientGroup()->setVisible( c_id + 1 ); else active_client->clientGroup()->setVisible( 0 ); } void Workspace::slotSwitchToTabLeft() { if( !active_client ) return; int c_id = active_client->clientGroup()->indexOfClient( active_client ); int size = active_client->clientGroup()->clients().count(); if( c_id > 0 ) active_client->clientGroup()->setVisible( c_id - 1 ); else active_client->clientGroup()->setVisible( size - 1 ); } void Workspace::slotRemoveFromGroup() { if( !active_client ) return; active_client->clientGroup()->remove( active_client ); } /*! Kill Window feature, similar to xkill */ void Workspace::slotKillWindow() { KillWindow kill( this ); kill.start(); } /*! Sends the popup client to desktop \a desk Internal slot for the window operation menu */ void Workspace::slotSendToDesktop( QAction *action ) { int desk = action->data().toInt(); if ( !active_popup_client ) return; if ( desk == 0 ) { // the 'on_all_desktops' menu entry active_popup_client->setOnAllDesktops( !active_popup_client->isOnAllDesktops()); return; } sendClientToDesktop( active_popup_client, desk, false ); } /*! Switches to the nearest window in given direction */ void Workspace::switchWindow( Direction direction ) { if( !active_client ) return; Client *c = active_client; Client *switchTo = 0; int bestScore = 0; int d = c->desktop(); // Centre of the active window QPoint curPos( c->pos().x() + c->geometry().width() / 2, c->pos().y() + c->geometry().height() / 2 ); QList clist = stackingOrder(); for( QList::Iterator i = clist.begin(); i != clist.end(); ++i ) { if( (*i)->wantsTabFocus() && *i != c && (*i)->desktop() == d && ! (*i)->isMinimized() ) { // Centre of the other window QPoint other( (*i)->pos().x() + (*i)->geometry().width() / 2, (*i)->pos().y() + (*i)->geometry().height() / 2 ); int distance; int offset; switch( direction ) { case DirectionNorth: distance = curPos.y() - other.y(); offset = qAbs(other.x() - curPos.x()); break; case DirectionEast: distance = other.x() - curPos.x(); offset = qAbs(other.y() - curPos.y()); break; case DirectionSouth: distance = other.y() - curPos.y(); offset = qAbs(other.x() - curPos.x()); break; case DirectionWest: distance = curPos.x() - other.x(); offset = qAbs(other.y() - curPos.y()); break; default: distance = -1; offset = -1; } if( distance > 0 ) { // Inverse score int score = distance + offset + ( (offset * offset) / distance ); if( score < bestScore || !switchTo ) { switchTo = *i; bestScore = score; } } } } if( switchTo ) activateClient( switchTo ); } /*! Switches to upper window */ void Workspace::slotSwitchWindowUp() { switchWindow( DirectionNorth ); } /*! Switches to lower window */ void Workspace::slotSwitchWindowDown() { switchWindow( DirectionSouth ); } /*! Switches to window on the right */ void Workspace::slotSwitchWindowRight() { switchWindow( DirectionEast ); } /*! Switches to window on the left */ void Workspace::slotSwitchWindowLeft() { switchWindow( DirectionWest ); } /*! Shows the window operations popup menu for the activeClient() */ void Workspace::slotWindowOperations() { if ( !active_client ) return; QPoint pos = active_client->pos() + active_client->clientPos(); showWindowMenu( pos.x(), pos.y(), active_client ); } void Workspace::showWindowMenu( const QRect &pos, Client* cl ) { if (!KAuthorized::authorizeKAction("kwin_rmb")) return; if( !cl ) return; if( active_popup_client != NULL ) // recursion return; if ( cl->isDesktop() || cl->isDock() || cl->isTopMenu()) return; active_popup_client = cl; QMenu* p = clientPopup(); active_popup = p; int x = pos.left(); int y = pos.bottom(); if (y == pos.top()) p->exec( QPoint( x, y ) ); else { QRect area = clientArea(ScreenArea, QPoint(x, y), currentDesktop()); clientPopupAboutToShow(); // needed for sizeHint() to be correct :-/ int popupHeight = p->sizeHint().height(); if (y + popupHeight < area.height()) p->exec( QPoint( x, y ) ); else p->exec( QPoint( x, pos.top() - popupHeight ) ); } // active popup may be already changed (e.g. the window shortcut dialog) if( active_popup == p ) closeActivePopup(); } /*! Closes the popup client */ void Workspace::slotWindowClose() { // TODO: why? // if ( tab_box->isVisible()) // return; Client* c = active_popup_client ? active_popup_client : active_client; performWindowOperation( c, Options::CloseOp ); } /*! Starts keyboard move mode for the popup client */ void Workspace::slotWindowMove() { Client* c = active_popup_client ? active_popup_client : active_client; performWindowOperation( c, Options::UnrestrictedMoveOp ); } /*! Starts keyboard resize mode for the popup client */ void Workspace::slotWindowResize() { Client* c = active_popup_client ? active_popup_client : active_client; performWindowOperation( c, Options::UnrestrictedResizeOp ); } void Client::setShortcut( const QString& _cut ) { QString cut = rules()->checkShortcut( _cut ); if( cut.isEmpty()) return setShortcutInternal( KShortcut()); // Format: // base+(abcdef)base+(abcdef) // E.g. Alt+Ctrl+(ABCDEF) Win+X,Win+(ABCDEF) if( !cut.contains( '(' ) && !cut.contains( ')' ) && !cut.contains( ' ' )) { if( workspace()->shortcutAvailable( KShortcut( cut ), this )) setShortcutInternal( KShortcut( cut )); else setShortcutInternal( KShortcut()); return; } QList< KShortcut > keys; QStringList groups = cut.split( ' '); for( QStringList::ConstIterator it = groups.constBegin(); it != groups.constEnd(); ++it ) { QRegExp reg( "(.*\\+)\\((.*)\\)" ); if( reg.indexIn( *it ) > -1 ) { QString base = reg.cap( 1 ); QString list = reg.cap( 2 ); for( int i = 0; i < list.length(); ++i ) { KShortcut c( base + list[ i ] ); if( !c.isEmpty()) keys.append( c ); } } } for( QList< KShortcut >::ConstIterator it = keys.constBegin(); it != keys.constEnd(); ++it ) { if( _shortcut == *it ) // current one is in the list return; } for( QList< KShortcut >::ConstIterator it = keys.constBegin(); it != keys.constEnd(); ++it ) { if( workspace()->shortcutAvailable( *it, this )) { setShortcutInternal( *it ); return; } } setShortcutInternal( KShortcut()); } void Client::setShortcutInternal( const KShortcut& cut ) { if( _shortcut == cut ) return; _shortcut = cut; updateCaption(); #if 0 workspace()->clientShortcutUpdated( this ); #else // Workaround for kwin<->kglobalaccel deadlock, when KWin has X grab and the kded // kglobalaccel module tries to create the key grab. KWin should preferably grab // they keys itself anyway :(. QTimer::singleShot( 0, this, SLOT( delayedSetShortcut())); #endif } void Client::delayedSetShortcut() { workspace()->clientShortcutUpdated( this ); } bool Workspace::shortcutAvailable( const KShortcut& cut, Client* ignore ) const { // TODO check global shortcuts etc. for( ClientList::ConstIterator it = clients.constBegin(); it != clients.constEnd(); ++it ) { if( (*it) != ignore && (*it)->shortcut() == cut ) return false; } return true; } } // namespace