diff --git a/client.cpp b/client.cpp index 6bb524c36d..3b40337991 100644 --- a/client.cpp +++ b/client.cpp @@ -20,6 +20,8 @@ extern Atom qt_wm_state; extern Time kwin_time; +static bool resizeHorizontalDirectionFixed = FALSE; +static bool resizeVerticalDirectionFixed = FALSE; static QRect* visible_bound = 0; @@ -82,7 +84,7 @@ static void sendClientMessage(Window w, Atom a, long x){ There's not much to know about this class, it's completley handled by the abstract class Client. You get access to the window wrapper with - Client:.windowWrapper(). The big advantage of WindowWrapper is, + Client::windowWrapper(). The big advantage of WindowWrapper is, that you can use just like a normal QWidget, although it encapsulates an X window that belongs to another application. @@ -823,6 +825,7 @@ void Client::mouseReleaseEvent( QMouseEvent * e) setGeometry( geom ); moveResizeMode = FALSE; releaseMouse(); + releaseKeyboard(); } } } @@ -1402,12 +1405,12 @@ void Client::setShade( bool s ) */ void Client::setActive( bool act) { + if ( act ) + workspace()->setActiveClient( this ); + if ( active == act ) return; - active = act; - if ( active ) - workspace()->setActiveClient( this ); activeChange( active ); } @@ -1517,7 +1520,7 @@ Client* Client::mainClient() Returns whether the window provides context help or not. If it does, you should show a help menu item or a help button lie '?' and call contextHelp() if this is invoked. - + \sa contextHelp() */ bool Client::providesContextHelp() const @@ -1529,7 +1532,7 @@ bool Client::providesContextHelp() const /*! Invokes context help on the window. Only works if the window actually provides context help. - + \sa providesContextHelp() */ void Client::contextHelp() @@ -1541,6 +1544,9 @@ void Client::contextHelp() } +/*! + Performs a mouse command on this client (see options.h) + */ bool Client::performMouseCommand( Options::MouseCommand command, QPoint globalPos) { bool replay = FALSE; @@ -1581,6 +1587,7 @@ bool Client::performMouseCommand( Options::MouseCommand command, QPoint globalPo moveOffset = mapFromGlobal( globalPos ); invertedMoveOffset = rect().bottomRight() - moveOffset; grabMouse( arrowCursor ); + grabKeyboard(); if ( options->moveMode != Options::Opaque ) XGrabServer( qt_xdisplay() ); break; @@ -1602,6 +1609,9 @@ bool Client::performMouseCommand( Options::MouseCommand command, QPoint globalPo invertedMoveOffset = rect().bottomRight() - moveOffset; setMouseCursor( mode ); grabMouse( cursor() ); + grabKeyboard(); + resizeHorizontalDirectionFixed = FALSE; + resizeVerticalDirectionFixed = FALSE; if ( options->resizeMode != Options::Opaque ) XGrabServer( qt_xdisplay() ); break; @@ -1615,6 +1625,98 @@ bool Client::performMouseCommand( Options::MouseCommand command, QPoint globalPo } +void Client::keyPressEvent( QKeyEvent * e ) +{ + if ( !isMove() && !isResize() ) + return; + bool is_control = e->state() & ControlButton; + int delta = is_control?1:8; + QPoint pos = QCursor::pos(); + switch ( e->key() ) { + case Key_Left: + pos.rx() -= delta; + if ( pos.x() <= workspace()->geometry().left() ) { + moveOffset.rx() += delta; + invertedMoveOffset.rx() += delta; + } + if ( isResize() && !resizeHorizontalDirectionFixed ) { + resizeHorizontalDirectionFixed = TRUE; + if ( mode == BottomRight ) + mode = BottomLeft; + else if ( mode == TopRight ) + mode = TopLeft; + setMouseCursor( mode ); + grabMouse( cursor() ); + } + break; + case Key_Right: + pos.rx() += delta; + if ( pos.x() >= workspace()->geometry().right() ) { + moveOffset.rx() -= delta; + invertedMoveOffset.rx() -= delta; + } + if ( isResize() && !resizeHorizontalDirectionFixed ) { + resizeHorizontalDirectionFixed = TRUE; + if ( mode == BottomLeft ) + mode = BottomRight; + else if ( mode == TopLeft ) + mode = TopRight; + setMouseCursor( mode ); + grabMouse( cursor() ); + } + break; + case Key_Up: + pos.ry() -= delta; + if ( pos.y() <= workspace()->geometry().top() ) { + moveOffset.ry() += delta; + invertedMoveOffset.ry() += delta; + } + if ( isResize() && !resizeVerticalDirectionFixed ) { + resizeVerticalDirectionFixed = TRUE; + if ( mode == BottomLeft ) + mode = TopLeft; + else if ( mode == BottomRight ) + mode = TopRight; + setMouseCursor( mode ); + grabMouse( cursor() ); + } + break; + case Key_Down: + pos.ry() += delta; + if ( pos.y() >= workspace()->geometry().bottom() ) { + moveOffset.ry() -= delta; + invertedMoveOffset.ry() -= delta; + } + if ( isResize() && !resizeVerticalDirectionFixed ) { + resizeVerticalDirectionFixed = TRUE; + if ( mode == TopLeft ) + mode = BottomLeft; + else if ( mode == TopRight ) + mode = BottomRight; + setMouseCursor( mode ); + grabMouse( cursor() ); + } + break; + case Key_Space: + case Key_Return: + case Key_Enter: + clearbound(); + if ( ( isMove() && options->moveMode != Options::Opaque ) + || ( isResize() && options->resizeMode != Options::Opaque ) ) + XUngrabServer( qt_xdisplay() ); + setGeometry( geom ); + moveResizeMode = FALSE; + releaseMouse(); + releaseKeyboard(); + buttonDown = FALSE; + break; + default: + return; + } + QCursor::setPos( pos ); +} + + NoBorderClient::NoBorderClient( Workspace *ws, WId w, QWidget *parent, const char *name ) : Client( ws, w, parent, name ) { diff --git a/client.h b/client.h index a9600dbe4e..9f079b7f2f 100644 --- a/client.h +++ b/client.h @@ -129,7 +129,7 @@ public: virtual bool wantsTabFocus() const { return TRUE;} //### just for now - + bool providesContextHelp() const; bool performMouseCommand( Options::MouseCommand, QPoint globalPos ); @@ -147,6 +147,7 @@ protected: void mousePressEvent( QMouseEvent * ); void mouseReleaseEvent( QMouseEvent * ); void mouseMoveEvent( QMouseEvent * ); + void keyPressEvent( QKeyEvent * ); void resizeEvent( QResizeEvent * ); virtual void windowWrapperShowEvent( QShowEvent* ){} virtual void windowWrapperHideEvent( QHideEvent* ){} diff --git a/kwinbindings.cpp b/kwinbindings.cpp index 73caded50b..68007dbcf2 100644 --- a/kwinbindings.cpp +++ b/kwinbindings.cpp @@ -15,3 +15,7 @@ keys->insertItem(i18n("Window maximize horizontal"),"Window maximize horizontal", ""); keys->insertItem(i18n("Window iconify"),"Window iconify", ""); keys->insertItem(i18n("Window shade"),"Window shade", ""); + keys->insertItem(i18n("Window move"),"Window move", ""); + keys->insertItem(i18n("Window resize"),"Window resize", ""); + + keys->insertItem(i18n("Mouse emulation"),"Mouse emulation", "F12"); diff --git a/workspace.cpp b/workspace.cpp index 02a8d5decd..ba574d4744 100644 --- a/workspace.cpp +++ b/workspace.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include extern Time kwin_time; @@ -120,6 +121,7 @@ Workspace::Workspace() control_grab = FALSE; tab_grab = FALSE; + mouse_emulation = FALSE; tab_box = new TabBox( this ); } @@ -235,6 +237,11 @@ Workspace::~Workspace() */ bool Workspace::workspaceEvent( XEvent * e ) { + if ( mouse_emulation && e->type == ButtonPress || e->type == ButtonRelease ) { + mouse_emulation = FALSE; + XUngrabKeyboard( qt_xdisplay(), kwin_time ); + } + Client * c = findClient( e->xany.window ); if ( c ) return c->windowEvent( e ); @@ -349,9 +356,20 @@ bool Workspace::workspaceEvent( XEvent * e ) } break; case KeyPress: + if ( QWidget::keyboardGrabber() ) { + freeKeyboard( FALSE ); + break; + } + if ( mouse_emulation ) + return keyPressMouseEmulation( e->xkey ); return keyPress(e->xkey); - break; case KeyRelease: + if ( QWidget::keyboardGrabber() ) { + freeKeyboard( FALSE ); + break; + } + if ( mouse_emulation ) + return FALSE; return keyRelease(e->xkey); break; case FocusIn: @@ -831,6 +849,8 @@ void Workspace::requestFocus( Client* c) if ( !popup || !popup->isVisible() ) popup_client = c; + + active_client = c; if ( c->isVisible() && !c->isShade() ) { c->takeFocus(); @@ -916,6 +936,12 @@ void Workspace::performWindowOperation( Client* c, Options::WindowOperation op ) return; switch ( op ) { + case Options::MoveOp: + c->performMouseCommand( Options::MouseMove, QCursor::pos() ); + break; + case Options::ResizeOp: + c->performMouseCommand( Options::MouseResize, QCursor::pos() ); + break; case Options::CloseOp: c->closeWindow(); break; @@ -935,8 +961,7 @@ void Workspace::performWindowOperation( Client* c, Options::WindowOperation op ) void Workspace::clientPopupActivated( int id ) { - if ( popup_client ) - performWindowOperation( popup_client, (Options::WindowOperation) id ); + performWindowOperation( popup_client, (Options::WindowOperation) id ); } /*! @@ -1499,6 +1524,11 @@ void Workspace::propagateClients( bool onlyStacking ) } + +/*! + Check whether \a w is a dock window. If so, add it to the respective + datastructures and propagate it to the world. + */ bool Workspace::addDockwin( WId w ) { WId dockFor = 0; @@ -1512,6 +1542,10 @@ bool Workspace::addDockwin( WId w ) return TRUE; } +/*! + Check whether \a w is a dock window. If so, remove it from the + respective datastructures and propagate this to the world. + */ bool Workspace::removeDockwin( WId w ) { if ( !dockwins.contains( w ) ) @@ -1521,6 +1555,12 @@ bool Workspace::removeDockwin( WId w ) return TRUE; } + +/*! + Returns whether iconify means actually withdraw for client \a. This + is TRUE for clients that have a docking window. In that case the + docking window will serve as icon replacement. + */ bool Workspace::iconifyMeansWithdraw( Client* c) { for ( DockWindowList::ConstIterator it = dockwins.begin(); it != dockwins.end(); ++it ) { @@ -1547,6 +1587,9 @@ void Workspace::propagateDockwins() } +/*! + Create the global accel object \c keys. + */ void Workspace::createKeybindings(){ keys = new KGlobalAccel(); @@ -1568,7 +1611,10 @@ void Workspace::createKeybindings(){ keys->connectItem( "Window maximize vertical", this, SLOT( slotWindowMaximizeVertical() ) ); keys->connectItem( "Window iconify", this, SLOT( slotWindowIconify() ) ); keys->connectItem( "Window shade", this, SLOT( slotWindowShade() ) ); + keys->connectItem( "Window move", this, SLOT( slotWindowMove() ) ); + keys->connectItem( "Window resize", this, SLOT( slotWindowResize() ) ); + keys->connectItem( "Mouse emulation", this, SLOT( slotMouseEmulation() ) ); keys->readSettings(); } @@ -1598,33 +1644,70 @@ void Workspace::slotSwitchDesktop8(){ } + +/*! + Maximizes the popup client + */ void Workspace::slotWindowMaximize() { if ( popup_client ) popup_client->maximize( Client::MaximizeFull ); } + +/*! + Maximizes the popup client vertically + */ void Workspace::slotWindowMaximizeVertical() { if ( popup_client ) popup_client->maximize( Client::MaximizeVertical ); } + +/*! + Maximizes the popup client horiozontally + */ void Workspace::slotWindowMaximizeHorizontal() { if ( popup_client ) popup_client->maximize( Client::MaximizeHorizontal ); } + + +/*! + Iconifies the popup client + */ void Workspace::slotWindowIconify() { - if ( popup_client ) - popup_client->iconify(); + performWindowOperation( popup_client, Options::IconifyOp ); } +/*! + Shades/unshades the popup client respectively + */ void Workspace::slotWindowShade() { - if ( popup_client ) - popup_client->setShade( !popup_client->isShade() ); + performWindowOperation( popup_client, Options::ShadeOp ); } + +/*! + Invokes keyboard mouse emulation + */ +void Workspace::slotMouseEmulation() +{ + if ( XGrabKeyboard(qt_xdisplay(), + root, FALSE, + GrabModeAsync, GrabModeAsync, + kwin_time) == GrabSuccess ) { + mouse_emulation = TRUE; + } +} + + +/*! + Adjusts the desktop popup to the current values and the location of + the popup client. + */ void Workspace::desktopPopupAboutToShow() { if ( !desk_popup ) @@ -1641,6 +1724,12 @@ void Workspace::desktopPopupAboutToShow() } } + +/*! + The client popup menu will become visible soon. + + Adjust the items according to the respective popup client. + */ void Workspace::clientPopupAboutToShow() { if ( !popup_client || !popup ) @@ -1649,6 +1738,10 @@ void Workspace::clientPopupAboutToShow() popup->setItemChecked( Options::ShadeOp, popup_client->isShade() ); } + +/*! + Sends the activeClient() to desktop \a desk + */ void Workspace::sendToDesktop( int desk ) { if ( !popup_client ) @@ -1677,6 +1770,10 @@ void Workspace::sendToDesktop( int desk ) } } + +/*! + Shows the window operations popup menu for the activeClient() + */ void Workspace::slotWindowOperations() { if ( !active_client ) @@ -1685,14 +1782,34 @@ void Workspace::slotWindowOperations() p->popup( active_client->mapToGlobal( active_client->windowWrapper()->geometry().topLeft() ) ); } + +/*! + Closes the popup client + */ void Workspace::slotWindowClose() { - if ( popup_client ) - popup_client->closeWindow(); + performWindowOperation( popup_client, Options::CloseOp ); } -/* +/*! + Starts keyboard move mode for the popup client + */ +void Workspace::slotWindowMove() +{ + performWindowOperation( popup_client, Options::MoveOp ); +} + +/*! + Starts keyboard resize mode for the popup client + */ +void Workspace::slotWindowResize() +{ + performWindowOperation( popup_client, Options::ResizeOp ); +} + + +/*! Client \a c is moved around to position \a pos. This gives the workspace the opportunity to interveniate and to implement snap-to-windows functionality. @@ -1793,3 +1910,93 @@ QPoint Workspace::adjustClientPosition( Client* c, QPoint pos ) } return pos; } + + +/*! + Handles keypress event during mouse emulation + */ +bool Workspace::keyPressMouseEmulation( XKeyEvent key ) +{ + if ( root != qt_xrootwin() ) + return FALSE; + int kc = XKeycodeToKeysym(qt_xdisplay(), key.keycode, 0); + int km = key.state & (ControlMask | Mod1Mask | ShiftMask); + + bool is_control = km & ControlMask; + bool is_alt = km & Mod1Mask; + int delta = is_control?1:is_alt?32:8; + QPoint pos = QCursor::pos(); + + switch ( kc ) { + case XK_Left: + case XK_KP_Left: + pos.rx() -= delta; + break; + case XK_Right: + case XK_KP_Right: + pos.rx() += delta; + break; + case XK_Up: + case XK_KP_Up: + pos.ry() -= delta; + break; + case XK_Down: + case XK_KP_Down: + pos.ry() += delta; + break; + case XK_Return: + case XK_space: + case XK_KP_Enter: + case XK_KP_Space: + { + Window root; + Window child = qt_xrootwin(); + int root_x, root_y, lx, ly; + uint state; + Window w; + Client * c = 0; + do { + w = child; + if (!c) + c = findClientWidthId( w ); + XQueryPointer( qt_xdisplay(), w, &root, &child, + &root_x, &root_y, &lx, &ly, &state ); + } while ( child != None && child != w ); + + if ( c && !c->isActive() ) + activateClient( c ); + + QWidget* widget = QWidget::find( w ); + if ( (!widget || widget->inherits("QToolButton") ) && !findClient( w ) ) { + XButtonEvent e; + e.type = ButtonPress; + e.window = w; + e.root = qt_xrootwin(); + e.subwindow = w; + e.time = kwin_time; + e.x = lx; + e.y = ly; + e.x_root = root_x; + e.y_root = root_y; + e.state = key.state; + e.button = Button1; + XSendEvent( qt_xdisplay(), w, TRUE, ButtonPressMask, (XEvent*)&e ); + e.type = ButtonRelease; + e.state = key.state & Button1Mask; + XSendEvent( qt_xdisplay(), w, TRUE, ButtonReleaseMask, (XEvent*)&e ); + } + } + // fall through + case XK_Escape: + XUngrabKeyboard(qt_xdisplay(), kwin_time); + mouse_emulation = FALSE; + return TRUE; + default: + return FALSE; + } + + QCursor::setPos( pos ); + return TRUE; + +} + diff --git a/workspace.h b/workspace.h index df9dc3c7f3..dc3beb5159 100644 --- a/workspace.h +++ b/workspace.h @@ -97,7 +97,7 @@ public: bool iconifyMeansWithdraw( Client* ); void iconifyOrDeiconifyTransientsOf( Client* ); - + bool hasCaption( const QString& caption ); void performWindowOperation( Client* c, Options::WindowOperation op ); @@ -123,7 +123,10 @@ public slots: void slotWindowOperations(); void slotWindowClose(); - + void slotWindowMove(); + void slotWindowResize(); + + void slotMouseEmulation(); private slots: void setDecorationStyle( int ); @@ -135,6 +138,7 @@ private slots: protected: bool keyPress( XKeyEvent key ); bool keyRelease( XKeyEvent key ); + bool keyPressMouseEmulation( XKeyEvent key ); bool clientMessage( XClientMessageEvent msg ); private: @@ -148,6 +152,7 @@ private: Client* active_client; bool control_grab; bool tab_grab; + bool mouse_emulation; TabBox* tab_box; void freeKeyboard(bool pass); QGuardedPtr popup_client; @@ -181,7 +186,7 @@ private: bool removeDockwin( WId w ); void propagateDockwins(); DockWindow findDockwin( WId w ); - + //CT needed for cascading+ struct CascadingInfo { QPoint pos;