/***************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 1999, 2000 Matthias Ettrich <ettrich@kde.org> Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org> You can Freely distribute this program under the GNU General Public License. See the file "COPYING" for the exact licensing terms. ******************************************************************/ /* 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 <fixx11h.h> #include <qpopupmenu.h> #include <kglobalsettings.h> #include <kiconloader.h> #include <klocale.h> #include <kconfig.h> #include <kglobalaccel.h> #include <kapplication.h> #include "popupinfo.h" #include "killwindow.h" #include "tabbox.h" namespace KWinInternal { //**************************************** // Workspace //**************************************** QPopupMenu* Workspace::clientPopup() { if ( !popup ) { popup = new QPopupMenu; popup->setCheckable( TRUE ); popup->setFont(KGlobalSettings::menuFont()); connect( popup, SIGNAL( aboutToShow() ), this, SLOT( clientPopupAboutToShow() ) ); connect( popup, SIGNAL( activated(int) ), this, SLOT( clientPopupActivated(int) ) ); advanced_popup = new QPopupMenu( popup ); advanced_popup->setCheckable( TRUE ); advanced_popup->setFont(KGlobalSettings::menuFont()); connect( advanced_popup, SIGNAL( activated(int) ), this, SLOT( clientPopupActivated(int) ) ); advanced_popup->insertItem( SmallIconSet( "up" ), i18n("Keep &Above Others")+'\t'+keys->shortcut("Window Above Other Windows").seq(0).toString(), Options::KeepAboveOp ); advanced_popup->insertItem( SmallIconSet( "down" ), i18n("Keep &Below Others")+'\t'+keys->shortcut("Window Below Other Windows").seq(0).toString(), Options::KeepBelowOp ); advanced_popup->insertItem( SmallIconSet( "window_fullscreen" ), i18n("&Fullscreen")+'\t'+keys->shortcut("Window Fullscreen").seq(0).toString(), Options::FullScreenOp ); advanced_popup->insertItem( i18n("&No Border")+'\t'+keys->shortcut("Window No Border").seq(0).toString(), Options::NoBorderOp ); advanced_popup->insertItem( SmallIconSet( "filesave" ), i18n("&Special Window Settings..."), Options::WindowRulesOp ); popup->insertItem(i18n("Ad&vanced"), advanced_popup ); desk_popup_index = popup->count(); popup->insertItem( SmallIconSet( "move" ), i18n("&Move")+'\t'+keys->shortcut("Window Move").seq(0).toString(), Options::MoveOp ); popup->insertItem( i18n("Re&size")+'\t'+keys->shortcut("Window Resize").seq(0).toString(), Options::ResizeOp ); popup->insertItem( i18n("Mi&nimize")+'\t'+keys->shortcut("Window Minimize").seq(0).toString(), Options::MinimizeOp ); popup->insertItem( i18n("Ma&ximize")+'\t'+keys->shortcut("Window Maximize").seq(0).toString(), Options::MaximizeOp ); popup->insertItem( i18n("Sh&ade")+'\t'+keys->shortcut("Window Shade").seq(0).toString(), Options::ShadeOp ); popup->insertSeparator(); if (!KGlobal::config()->isImmutable() && !kapp->authorizeControlModules(Workspace::configModules(true)).isEmpty()) { popup->insertItem(SmallIconSet( "configure" ), i18n("Configur&e Window Behavior..."), this, SLOT( configureWM() )); popup->insertSeparator(); } popup->insertItem( SmallIconSet( "fileclose" ), i18n("&Close")+'\t'+keys->shortcut("Window Close").seq(0).toString(), Options::CloseOp ); } return popup; } /*! The client popup menu will become visible soon. Adjust the items according to the respective popup client. */ void Workspace::clientPopupAboutToShow() { if ( !popup_client || !popup ) return; if ( numberOfDesktops() == 1 ) { delete desk_popup; desk_popup = 0; } else { initDesktopPopup(); } popup->setItemEnabled( Options::ResizeOp, popup_client->isResizable() ); popup->setItemEnabled( Options::MoveOp, popup_client->isMovable() ); popup->setItemEnabled( Options::MaximizeOp, popup_client->isMaximizable() ); popup->setItemChecked( Options::MaximizeOp, popup_client->maximizeMode() == Client::MaximizeFull ); // This should be checked also when hover unshaded popup->setItemChecked( Options::ShadeOp, popup_client->shadeMode() != ShadeNone ); popup->setItemEnabled( Options::ShadeOp, popup_client->isShadeable()); advanced_popup->setItemChecked( Options::KeepAboveOp, popup_client->keepAbove() ); advanced_popup->setItemChecked( Options::KeepBelowOp, popup_client->keepBelow() ); advanced_popup->setItemChecked( Options::FullScreenOp, popup_client->isFullScreen() ); advanced_popup->setItemEnabled( Options::FullScreenOp, popup_client->userCanSetFullScreen() ); advanced_popup->setItemChecked( Options::NoBorderOp, popup_client->noBorder() ); advanced_popup->setItemEnabled( Options::NoBorderOp, popup_client->userCanSetNoBorder() ); popup->setItemEnabled( Options::MinimizeOp, popup_client->isMinimizable() ); popup->setItemEnabled( Options::CloseOp, popup_client->isCloseable() ); } void Workspace::initDesktopPopup() { if (desk_popup) return; desk_popup = new QPopupMenu( popup ); desk_popup->setCheckable( TRUE ); desk_popup->setFont(KGlobalSettings::menuFont()); connect( desk_popup, SIGNAL( activated(int) ), this, SLOT( slotSendToDesktop(int) ) ); connect( desk_popup, SIGNAL( aboutToShow() ), this, SLOT( desktopPopupAboutToShow() ) ); popup->insertItem(i18n("To &Desktop"), desk_popup, -1, desk_popup_index ); } /*! 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(); desk_popup->insertItem( i18n("&All Desktops"), 0 ); if ( active_client && active_client->isOnAllDesktops() ) desk_popup->setItemChecked( 0, TRUE ); desk_popup->insertSeparator( -1 ); int id; const int BASE = 10; for ( int i = 1; i <= numberOfDesktops(); i++ ) { QString basic_name("%1 %2"); if (i<BASE) { basic_name.prepend('&'); } id = desk_popup->insertItem( basic_name .arg(i) .arg( desktopName(i).replace( '&', "&&" )), i ); if ( active_client && !active_client->isOnAllDesktops() && active_client->desktop() == i ) desk_popup->setItemChecked( id, TRUE ); } } /*! Create the global accel object \c keys. */ void Workspace::initShortcuts() { keys = new KGlobalAccel( this ); #include "kwinbindings.cpp" readShortcuts(); } void Workspace::readShortcuts() { keys->readSettings(); cutWalkThroughDesktops = keys->shortcut("Walk Through Desktops"); cutWalkThroughDesktopsReverse = keys->shortcut("Walk Through Desktops (Reverse)"); cutWalkThroughDesktopList = keys->shortcut("Walk Through Desktop List"); cutWalkThroughDesktopListReverse = keys->shortcut("Walk Through Desktop List (Reverse)"); cutWalkThroughWindows = keys->shortcut("Walk Through Windows"); cutWalkThroughWindowsReverse = keys->shortcut("Walk Through Windows (Reverse)"); keys->updateConnections(); delete popup; popup = NULL; // so that it's recreated next time desk_popup = NULL; } void Workspace::clientPopupActivated( int id ) { WindowOperation op = static_cast< WindowOperation >( id ); Client* c = popup_client ? 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, QCursor::pos() ); break; case Options::UnrestrictedMoveOp: c->performMouseCommand( Options::MouseUnrestrictedMove, QCursor::pos() ); break; case Options::ResizeOp: c->performMouseCommand( Options::MouseResize, QCursor::pos() ); break; case Options::UnrestrictedResizeOp: c->performMouseCommand( Options::MouseUnrestrictedResize, QCursor::pos() ); 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::MinimizeOp: c->minimize(); break; case Options::ShadeOp: c->toggleShade(); break; case Options::OnAllDesktopsOp: c->setOnAllDesktops( !c->isOnAllDesktops() ); break; case Options::FullScreenOp: c->setFullScreen( !c->isFullScreen(), true ); break; case Options::NoBorderOp: c->setUserNoBorder( !c->isUserNoBorder()); break; case Options::KeepAboveOp: c->setKeepAbove( !c->keepAbove() ); break; case Options::KeepBelowOp: c->setKeepBelow( !c->keepBelow() ); break; case Options::WindowRulesOp: editWindowRules( c ); break; case Options::LowerOp: lowerClient(c); break; default: break; } } /*! Performs a mouse command on this client (see options.h) */ bool Client::performMouseCommand( Options::MouseCommand command, QPoint globalPos, bool handled ) { bool replay = FALSE; switch (command) { case Options::MouseRaise: workspace()->raiseClient( this ); break; case Options::MouseLower: workspace()->lowerClient( this ); break; case Options::MouseShade : toggleShade(); 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 ); break; case Options::MouseActivateAndLower: workspace()->requestFocus( this ); workspace()->lowerClient( this ); break; case Options::MouseActivate: replay = isActive(); // for clickraise mode workspace()->takeActivity( this, ActivityFocus, handled && replay ); break; case Options::MouseActivateRaiseAndPassClick: workspace()->takeActivity( this, ActivityFocus | ActivityRaise, handled ); replay = TRUE; break; case Options::MouseActivateAndPassClick: workspace()->takeActivity( this, ActivityFocus, handled ); replay = TRUE; break; case Options::MouseActivateRaiseAndMove: case Options::MouseActivateRaiseAndUnrestrictedMove: workspace()->raiseClient( this ); workspace()->requestFocus( this ); if( options->moveMode == Options::Transparent && isMovable()) move_faked_activity = workspace()->fakeRequestedActivity( this ); // fallthrough case Options::MouseMove: case Options::MouseUnrestrictedMove: { if (!isMovable()) 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 ); setCursor( mode ); if( !startMoveResize()) { buttonDown = false; setCursor( mode ); } break; } case Options::MouseResize: case Options::MouseUnrestrictedResize: { if (!isResizable() || isShade()) // SHADE 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 ); setCursor( mode ); if( !startMoveResize()) { buttonDown = false; setCursor( mode ); } break; } case Options::MouseMinimize: minimize(); break; case Options::MouseNothing: // fall through default: replay = TRUE; break; } return replay; } // KDE4 remove me void Workspace::showWindowMenuAt( unsigned long, int, int ) { slotWindowOperations(); } 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); popupinfo->showInfo( desktopName(currentDesktop()) ); } void Workspace::slotSwitchDesktopPrevious() { int d = currentDesktop() - 1; if ( d <= 0 ) { if ( options->rollOverDesktops ) d = numberOfDesktops(); else return; } setCurrentDesktop(d); popupinfo->showInfo( desktopName(currentDesktop()) ); } void Workspace::slotSwitchDesktopRight() { int desktop = desktopToRight( currentDesktop()); if( desktop == currentDesktop()) return; setCurrentDesktop( desktop ); popupinfo->showInfo( desktopName(currentDesktop()) ); } void Workspace::slotSwitchDesktopLeft() { int desktop = desktopToLeft( currentDesktop()); if( desktop == currentDesktop()) return; setCurrentDesktop( desktop ); popupinfo->showInfo( desktopName(currentDesktop()) ); } void Workspace::slotSwitchDesktopUp() { int desktop = desktopUp( currentDesktop()); if( desktop == currentDesktop()) return; setCurrentDesktop( desktop ); popupinfo->showInfo( desktopName(currentDesktop()) ); } void Workspace::slotSwitchDesktopDown() { int desktop = desktopDown( currentDesktop()); if( desktop == currentDesktop()) return; setCurrentDesktop( desktop ); popupinfo->showInfo( desktopName(currentDesktop()) ); } void Workspace::slotSwitchToDesktop( int i ) { setCurrentDesktop( i ); popupinfo->showInfo( desktopName(currentDesktop()) ); } void Workspace::slotWindowToDesktop( int i ) { if( i >= 1 && i <= numberOfDesktops() && active_client && !active_client->isDesktop() && !active_client->isDock() && !active_client->isTopMenu()) sendClientToDesktop( active_client, i, true ); } /*! Maximizes the popup client */ void Workspace::slotWindowMaximize() { if ( active_client ) performWindowOperation( active_client, Options::MaximizeOp ); } /*! Maximizes the popup client vertically */ void Workspace::slotWindowMaximizeVertical() { if ( active_client ) performWindowOperation( active_client, Options::VMaximizeOp ); } /*! Maximizes the popup client horiozontally */ void Workspace::slotWindowMaximizeHorizontal() { if ( active_client ) performWindowOperation( active_client, Options::HMaximizeOp ); } /*! Minimizes the popup client */ void Workspace::slotWindowMinimize() { performWindowOperation( active_client, Options::MinimizeOp ); } /*! Shades/unshades the popup client respectively */ void Workspace::slotWindowShade() { performWindowOperation( active_client, Options::ShadeOp ); } /*! Raises the popup client */ void Workspace::slotWindowRaise() { if ( active_client ) raiseClient( active_client ); } /*! Lowers the popup client */ void Workspace::slotWindowLower() { if ( active_client ) lowerClient( active_client ); } /*! Does a toggle-raise-and-lower on the popup client; */ void Workspace::slotWindowRaiseOrLower() { if ( active_client ) raiseOrLowerClient( active_client ); } void Workspace::slotWindowOnAllDesktops() { if( active_client ) active_client->setOnAllDesktops( !active_client->isOnAllDesktops()); } void Workspace::slotWindowFullScreen() { if( active_client ) performWindowOperation( active_client, Options::FullScreenOp ); } void Workspace::slotWindowNoBorder() { if( active_client ) performWindowOperation( active_client, Options::NoBorderOp ); } void Workspace::slotWindowAbove() { if( active_client ) performWindowOperation( active_client, Options::KeepAboveOp ); } void Workspace::slotWindowBelow() { if( active_client ) performWindowOperation( active_client, Options::KeepBelowOp ); } /*! Move window to next desktop */ void Workspace::slotWindowToNextDesktop() { int d = currentDesktop() + 1; if ( d > numberOfDesktops() ) d = 1; if (active_client && !active_client->isDesktop() && !active_client->isDock() && !active_client->isTopMenu()) { setClientIsMoving( active_client ); setCurrentDesktop( d ); setClientIsMoving( NULL ); popupinfo->showInfo( desktopName(currentDesktop()) ); } } /*! Move window to previous desktop */ void Workspace::slotWindowToPreviousDesktop() { int d = currentDesktop() - 1; if ( d <= 0 ) d = numberOfDesktops(); if (active_client && !active_client->isDesktop() && !active_client->isDock() && !active_client->isTopMenu()) { setClientIsMoving( active_client ); setCurrentDesktop( d ); setClientIsMoving( NULL ); popupinfo->showInfo( desktopName(currentDesktop()) ); } } void Workspace::slotWindowToDesktopRight() { int d = desktopToRight( currentDesktop()); if( d == currentDesktop()) return; if (active_client && !active_client->isDesktop() && !active_client->isDock() && !active_client->isTopMenu()) { setClientIsMoving( active_client ); setCurrentDesktop( d ); setClientIsMoving( NULL ); popupinfo->showInfo( desktopName(currentDesktop()) ); } } void Workspace::slotWindowToDesktopLeft() { int d = desktopToLeft( currentDesktop()); if( d == currentDesktop()) return; if (active_client && !active_client->isDesktop() && !active_client->isDock() && !active_client->isTopMenu()) { setClientIsMoving( active_client ); setCurrentDesktop( d ); setClientIsMoving( NULL ); popupinfo->showInfo( desktopName(currentDesktop()) ); } } void Workspace::slotWindowToDesktopUp() { int d = desktopUp( currentDesktop()); if( d == currentDesktop()) return; if (active_client && !active_client->isDesktop() && !active_client->isDock() && !active_client->isTopMenu()) { setClientIsMoving( active_client ); setCurrentDesktop( d ); setClientIsMoving( NULL ); popupinfo->showInfo( desktopName(currentDesktop()) ); } } void Workspace::slotWindowToDesktopDown() { int d = desktopDown( currentDesktop()); if( d == currentDesktop()) return; if (active_client && !active_client->isDesktop() && !active_client->isDock() && !active_client->isTopMenu()) { setClientIsMoving( active_client ); setCurrentDesktop( d ); setClientIsMoving( NULL ); popupinfo->showInfo( desktopName(currentDesktop()) ); } } /*! 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( int desk ) { if ( !popup_client ) return; if ( desk == 0 ) { // the 'on_all_desktops' menu entry popup_client->setOnAllDesktops( !popup_client->isOnAllDesktops()); return; } sendClientToDesktop( popup_client, desk, false ); } /*! 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 (!kapp->authorizeKAction("kwin_rmb")) return; if( !cl ) return; if( popup_client != NULL ) // recursion return; if ( cl->isDesktop() || cl->isDock() || cl->isTopMenu()) return; popup_client = cl; QPopupMenu* p = clientPopup(); 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()); int popupHeight = p->sizeHint().height(); if (y + popupHeight < area.height()) { p->exec( QPoint( x, y ) ); } else { p->exec( QPoint( x, pos.top() - popupHeight ) ); } } popup_client = 0; } /*! Closes the popup client */ void Workspace::slotWindowClose() { if ( tab_box->isVisible() || popupinfo->isVisible() ) return; performWindowOperation( active_client, Options::CloseOp ); } /*! Starts keyboard move mode for the popup client */ void Workspace::slotWindowMove() { performWindowOperation( active_client, Options::UnrestrictedMoveOp ); } /*! Starts keyboard resize mode for the popup client */ void Workspace::slotWindowResize() { performWindowOperation( active_client, Options::UnrestrictedResizeOp ); } } // namespace