diff --git a/kwin/README b/kwin/README index 71c80ce1ae..111b89a1e4 100644 --- a/kwin/README +++ b/kwin/README @@ -1,20 +1,54 @@ -Fri Aug 20 01:30:50 CEST 1999 +This is kwin, kwm next generation. -This is the beginning of kwin, kwm next generation. +Currently supported options in the kwinrc: -WARNING: this thing is hardly usable now, neither ICCCM nor KDE -compliant yet! +[WM] +... general color stuff, usually in kdeglobals ... + +[Windows] + +MoveMode=Opaque|Transparent +ResizeMode=Opaque|Transparent +Placement=Smart|Random|Cascade +AnimateShade=TRUE|FALSE +AnimSteps= +BorderSnapZone= +WindowSnapZone= +TitlebarDoubleClickCommand=Move|Resize| + Maximize|Iconify|Close|Sticky|Shade|Operations + +[MouseBindings] + + CommandActiveTitlebar1 | + CommandActiveTitlebar2 | + CommandActiveTitlebar3 | + CommandInactiveTitlebar1 | + CommandInactiveTitlebar2 | + CommandInactiveTitlebar3 | + CommandWindow1 | + CommandWindow2 | + CommandWindow3 | + CommandAll1 | + CommandAll2 | + CommandAll3 + = + Raise | + Lower | + Operations Menu | + Toggle raise and lower | + Activate and raise | + Activate and lower | + Activate | + Activate, raise and pass click | + Activate and pass click | + Move | + Resize | + Nothing -All it has is a context menu that allows you to switch between two -decoration styles, KDE classic and an experimental style. -Please don't work on the code, I'll finish it during my summer -vacations (four weeks from now on). -kwin was only commited to allow people like Mosfet to have a look at -the Client API (and StdClient) to write nifty new themable decorations. Have fun, Matthias diff --git a/kwin/beclient.cpp b/kwin/beclient.cpp index f6885b1cf3..e834070e3d 100644 --- a/kwin/beclient.cpp +++ b/kwin/beclient.cpp @@ -1,216 +1,215 @@ #include "beclient.h" #include #include #include #include #include #include #include #include "workspace.h" static const char * size_xpm[] = { /* width height num_colors chars_per_pixel */ "16 16 3 1", /* colors */ " s None c None", ". c #707070", "X c white", /* pixels */ " ", " ....... ", " .XXXXXX ", " .X .X ", " .X .X....... ", " .X .XXXXXXXX ", " .X .X .X ", " .X....X .X ", " .XXXXXX .X ", " .X .X ", " .X .X ", " .X .X ", " .X........X ", " .XXXXXXXXXX ", " ", " "}; static QPixmap* size_pix = 0; static bool pixmaps_created = FALSE; static void create_pixmaps() { if ( pixmaps_created ) return; pixmaps_created = true; size_pix = new QPixmap( size_xpm ); } BeClient::BeClient( Workspace *ws, WId w, QWidget *parent, const char *name ) : Client( ws, w, parent, name, WResizeNoErase ) { create_pixmaps(); QGridLayout* g = new QGridLayout( this, 0, 0, 2 ); g->addRowSpacing(1, 2); g->setRowStretch( 2, 10 ); g->addWidget( windowWrapper(), 2, 1 ); g->addItem( new QSpacerItem( 0, 0, QSizePolicy::Fixed, QSizePolicy::Expanding ) ); g->addColSpacing(0, 2); g->addColSpacing(2, 2); g->addRowSpacing(3, 2); QHBoxLayout* hb = new QHBoxLayout; g->addLayout( hb, 0, 1 ); int fh = QMAX( 16, fontMetrics().lineSpacing()); titlebar = new QSpacerItem(40, fh, QSizePolicy::Preferred, QSizePolicy::Minimum ); hb->addItem( titlebar ); hb->addStretch(); } BeClient::~BeClient() { } void BeClient::resizeEvent( QResizeEvent* e) { Client::resizeEvent( e ); doShape(); if ( isVisibleToTLW() && !testWFlags( WNorthWestGravity ) ) { // manual clearing without the titlebar (we selected WResizeNoErase ) QPainter p( this ); QRect t = titlebar->geometry(); t.setTop( 0 ); t.setLeft( 0 ); QRegion r = rect(); r = r.subtract( t ); p.setClipRegion( r ); p.eraseRect( rect() ); } } /*!\reimp */ void BeClient::captionChange( const QString& ) { doShape(); repaint(); } void BeClient::paintEvent( QPaintEvent* ) { QPainter p( this ); QRect bar ( 0, 0, titlebar->geometry().right()+1, titlebar->geometry().bottom() ); qDrawWinPanel( &p, 0, bar.bottom()+2, width(), height() - bar.bottom()-2, options->colorGroup(Options::Frame, false)); qDrawWinPanel( &p, 2, bar.bottom()+4, width()-4, height() - bar.bottom()-6, options->colorGroup(Options::Frame, true)); QRect t = titlebar->geometry(); bar.setBottom( bar.bottom() + 3 ); p.setClipRect( bar ); bar.setBottom( bar.bottom() + 2 ); qDrawWinPanel( &p, bar, options->colorGroup(Options::TitleBar, isActive()), FALSE, &options->colorGroup(Options::TitleBar, isActive()). brush(QColorGroup::Background)); p.setClipping( FALSE ); p.drawPixmap( t.right() - 20, t.center().y()-8, *size_pix ); p.drawPixmap( t.left() +4, t.center().y()-miniIcon().height()/2, miniIcon() ); t.setLeft( t.left() + 20 + 5 ); p.setPen(options->color(Options::Font, isActive())); p.setFont(options->font(isActive())); p.drawText( t, AlignLeft|AlignVCenter, caption() ); } void BeClient::showEvent( QShowEvent* e) { Client::showEvent( e ); doShape(); repaint(); } void BeClient::doShape() { QFontMetrics fm = fontMetrics(); int cap = 20+20+10+10+fm.boundingRect(caption() ).width(); titlebar->changeSize( QMIN( width(), cap), QMAX( 16, fm.lineSpacing()), QSizePolicy::Preferred, QSizePolicy::Minimum ); layout()->activate(); //#### this is broken!!!!! PAUL!!!!! // // // do it manually: #######remove this for Qt-2.01 titlebar->setGeometry( QRect( titlebar->geometry().x(), titlebar->geometry().y(), titlebar->sizeHint().width(), titlebar->sizeHint().height() ) ); QRegion r( rect() ); r = r.subtract( QRect( QPoint( titlebar->geometry().right()+1, 0), QPoint( width(), titlebar->geometry().bottom()) ) ); setMask( r ); } /*!\reimp */ void BeClient::activeChange( bool /* act */ ) { repaint( 0, 0, width(), titlebar->geometry().bottom()+3, FALSE ); } /*!\reimp */ Client::MousePosition BeClient::mousePosition( const QPoint& p ) const { const int range = 16; const int border = 4; int ly = titlebar->geometry().bottom(); int lx = titlebar->geometry().right(); if ( p.x() > titlebar->geometry().right() ) { if ( p.y() <= ly + range && p.x() >= width()-range) return TopRight; else if ( p.y() <= ly + border ) return Top; } else if ( p.y() < ly ) { if ( p.y() > border && p.x() < lx - border ) return Client::mousePosition( p ); if ( p.y() < range && p.x() > lx - range ) return TopRight; else if ( p.x() > lx-border ) return Right; } return Client::mousePosition( p ); } void BeClient::mousePressEvent( QMouseEvent * e ) { Client::mousePressEvent( e ); } void BeClient::mouseReleaseEvent( QMouseEvent * e ) { - workspace()->makeFullScreen( this ); Client::mouseReleaseEvent( e ); } void BeClient::mouseDoubleClickEvent( QMouseEvent * e ) { if ( titlebar->geometry().contains( e->pos() ) ) setShade( !isShade() ); workspace()->requestFocus( this ); } void BeClient::windowWrapperShowEvent( QShowEvent* ) { doShape(); } diff --git a/kwin/client.cpp b/kwin/client.cpp index 342637601c..f30c698463 100644 --- a/kwin/client.cpp +++ b/kwin/client.cpp @@ -1,1589 +1,1599 @@ #include #include #include #include #include #include #include #include #include "workspace.h" #include "client.h" #include "atoms.h" #include #include #include #include #include #include extern Atom qt_wm_state; extern Time kwin_time; static QRect* visible_bound = 0; void Client::drawbound( const QRect& geom ) { if ( visible_bound ) *visible_bound = geom; else visible_bound = new QRect( geom ); QPainter p ( workspace()->desktopWidget() ); p.setPen( QPen( Qt::white, 5 ) ); p.setRasterOp( Qt::XorROP ); p.drawRect( geom ); } void Client::clearbound() { if ( !visible_bound ) return; drawbound( *visible_bound ); delete visible_bound; visible_bound = 0; } void Client::updateShape() { if ( shape() ) XShapeCombineShape(qt_xdisplay(), winId(), ShapeBounding, windowWrapper()->x(), windowWrapper()->y(), window(), ShapeBounding, ShapeSet); else XShapeCombineMask( qt_xdisplay(), winId(), ShapeBounding, 0, 0, None, ShapeSet); } static void sendClientMessage(Window w, Atom a, long x){ XEvent ev; long mask; memset(&ev, 0, sizeof(ev)); ev.xclient.type = ClientMessage; ev.xclient.window = w; ev.xclient.message_type = a; ev.xclient.format = 32; ev.xclient.data.l[0] = x; ev.xclient.data.l[1] = kwin_time; mask = 0L; if (w == qt_xrootwin()) mask = SubstructureRedirectMask; /* magic! */ XSendEvent(qt_xdisplay(), w, False, mask, &ev); } /*! \class WindowWrapper client.h \brief The WindowWrapper class encapsulates a client's managed window. 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, that you can use just like a normal QWidget, although it encapsulates an X window that belongs to another application. In particular, this means adding a client's windowWrapper() to a QLayout for the geometry management. See StdClient for an example on how to do this. */ WindowWrapper::WindowWrapper( WId w, Client *parent, const char* name) : QWidget( parent, name ) { win = w; setMouseTracking( TRUE ); setBackgroundColor( colorGroup().background() ); // we don't want the window to be destroyed when we are destroyed XAddToSaveSet(qt_xdisplay(), win ); // set the border width to 0 XWindowChanges wc; wc.border_width = 0; XConfigureWindow( qt_xdisplay(), win, CWBorderWidth, &wc ); // get the window XReparentWindow( qt_xdisplay(), win, winId(), 0, 0 ); // // overwrite Qt-defaults because we need SubstructureNotifyMask XSelectInput( qt_xdisplay(), winId(), KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | KeymapStateMask | ButtonMotionMask | PointerMotionMask | // need this, too! EnterWindowMask | LeaveWindowMask | FocusChangeMask | ExposureMask | StructureNotifyMask | SubstructureRedirectMask | SubstructureNotifyMask ); XSelectInput( qt_xdisplay(), w, FocusChangeMask | PropertyChangeMask // StructureNotifyMask ); // install a passive grab to catch mouse button events XGrabButton(qt_xdisplay(), AnyButton, AnyModifier, winId(), FALSE, ButtonPressMask, GrabModeSync, GrabModeSync, None, None ); } WindowWrapper::~WindowWrapper() { releaseWindow(); } QSize WindowWrapper::sizeHint() const { return size(); } QSizePolicy WindowWrapper::sizePolicy() const { return QSizePolicy( QSizePolicy::Preferred, QSizePolicy::Preferred ); } /** Gives layout the illusion the wrapper was visible */ void WindowWrapper::pseudoShow() { clearWState( WState_ForceHide ); } void WindowWrapper::resizeEvent( QResizeEvent * ) { if ( win ) { XMoveResizeWindow( qt_xdisplay(), win, 0, 0, width(), height() ); if ( ((Client*)parentWidget())->shape() ) ((Client*)parentWidget())->updateShape(); } } void WindowWrapper::showEvent( QShowEvent* ) { if ( win ) { XMoveResizeWindow( qt_xdisplay(), win, 0, 0, width(), height() ); XMapRaised( qt_xdisplay(), win ); } } void WindowWrapper::hideEvent( QHideEvent* ) { if ( win ) XUnmapWindow( qt_xdisplay(), win ); } void WindowWrapper::invalidateWindow() { win = 0; } /*! Releases the window. The client has done its job and the window is still existing. */ void WindowWrapper::releaseWindow() { if ( win ) { XReparentWindow( qt_xdisplay(), win, ((Client*)parentWidget())->workspace()->rootWin(), parentWidget()->x(), parentWidget()->y() ); XRemoveFromSaveSet(qt_xdisplay(), win ); invalidateWindow(); } } -void WindowWrapper::mousePressEvent( QMouseEvent* ) -{ - XAllowEvents( qt_xdisplay(), ReplayPointer, lastMouseEventTime ); -} - -void WindowWrapper::mouseReleaseEvent( QMouseEvent* ) -{ - XAllowEvents( qt_xdisplay(), ReplayPointer, lastMouseEventTime ); -} - -void WindowWrapper::mouseMoveEvent( QMouseEvent* ) -{ - //#### we sometimes get these but shouldn't..... Maybe a Qt problem - //XAllowEvents( qt_xdisplay(), ReplayPointer, lastMouseEventTime ); -} - bool WindowWrapper::x11Event( XEvent * e) { switch ( e->type ) { case ButtonPress: - case ButtonRelease: - lastMouseEventTime = e->xbutton.time; - if ( e->xbutton.button > 3 ) { - XAllowEvents( qt_xdisplay(), ReplayPointer, lastMouseEventTime ); + { + bool mod1 = (e->xbutton.state & Mod1Mask) == Mod1Mask; + Options::MouseCommand com = Options::MouseNothing; + if ( mod1){ + switch (e->xbutton.button) { + case Button1: + com = options->commandAll1(); + break; + case Button2: + com = options->commandAll2(); + break; + case Button3: + com = options->commandAll3(); + break; + } + } else { + switch (e->xbutton.button) { + case Button1: + com = options->commandWindow1(); + break; + case Button2: + com = options->commandWindow2(); + break; + case Button3: + com = options->commandWindow3(); + break; + default: + com = Options::MouseActivateAndPassClick; + } + } + bool replay = ( (Client*)parentWidget() )->performMouseCommand( com, + QPoint( e->xbutton.x_root, e->xbutton.y_root) ); + + XAllowEvents(qt_xdisplay(), replay? ReplayPointer : SyncPointer, kwin_time); return TRUE; } break; - case MotionNotify: - lastMouseEventTime = e->xmotion.time; + case ButtonRelease: + XAllowEvents(qt_xdisplay(), SyncPointer, kwin_time); break; default: break; } return FALSE; } /*! \class Client client.h \brief The Client class encapsulates a window decoration frame. TODO */ /*! Creates a client on workspace \a ws for window \a w. */ Client::Client( Workspace *ws, WId w, QWidget *parent, const char *name, WFlags f ) : QWidget( parent, name, f | WStyle_Customize | WStyle_NoBorder ) { wspace = ws; win = w; XWindowAttributes attr; if (XGetWindowAttributes(qt_xdisplay(), win, &attr)){ original_geometry.setRect(attr.x, attr.y, attr.width, attr.height ); } mapped = 0; wwrap = new WindowWrapper( w, this ); wwrap->installEventFilter( this ); // set the initial mapping state setMappingState( WithdrawnState ); desk = -1; // and no desktop yet mode = Nowhere; buttonDown = FALSE; moveResizeMode = FALSE; setMouseTracking( TRUE ); active = FALSE; shaded = FALSE; transient_for = None; is_shape = FALSE; is_sticky = FALSE; getWMHints(); getWindowProtocols(); getWmNormalHints(); // get xSizeHint fetchName(); if ( !XGetTransientForHint( qt_xdisplay(), (Window) win, (Window*) &transient_for ) ) transient_for = None; } /*! Destructor, nothing special. */ Client::~Client() { releaseWindow(); } /*! Manages the clients. This means handling the very first maprequest: reparenting, initial geometry, initial state, placement, etc. */ void Client::manage( bool isMapped ) { layout()->setResizeMode( QLayout::Minimum ); QRect geom( original_geometry ); bool placementDone = FALSE; if ( isMapped ) placementDone = TRUE; else { if ( (xSizeHint.flags & PPosition) || (xSizeHint.flags & USPosition) ) { // support for obsolete hints if ( xSizeHint.x != 0 && geom.x() == 0 ) geom.setRect( xSizeHint.x, geom.y(), geom.width(), geom.height() ); if ( xSizeHint.y != 0 && geom.y() == 0 ) geom.setRect( geom.x(), xSizeHint.y, geom.width(), geom.height() ); placementDone = TRUE; } if ( (xSizeHint.flags & USSize) || (xSizeHint.flags & PSize) ) { if ( xSizeHint.width != 0 ) geom.setWidth( xSizeHint.width ); if ( xSizeHint.height != 0 ) geom.setHeight( xSizeHint.height ); } if (xSizeHint.flags & PMaxSize) geom.setSize( geom.size().boundedTo( QSize(xSizeHint.max_width, xSizeHint.max_height ) ) ); if (xSizeHint.flags & PMinSize) geom.setSize( geom.size().expandedTo( QSize(xSizeHint.min_width, xSizeHint.min_height ) ) ); } windowWrapper()->resize( geom.size() ); // the clever activate() trick is necessary layout()->activate(); // resize( geom.width() + width() - windowWrapper()->width(), // geom.height() + height() - windowWrapper()->height() ); resize ( sizeForWindowSize( geom.size() ) ); layout()->activate(); move( geom.x(), geom.y() ); gravitate( FALSE ); if ( !placementDone && transient_for ) { // transient_for workaround for broken qt snapshot, ##### placementDone = TRUE; } if ( !placementDone ) { workspace()->doPlacement( this ); placementDone = TRUE; } if ( (is_shape = Shape::hasShape( win )) ) { updateShape(); } // find out the initial state. Several possibilities exist XWMHints * hints = XGetWMHints(qt_xdisplay(), win ); int state = NormalState; if (hints && (hints->flags & StateHint)) state = hints->initial_state; if (hints) XFree(hints); // ### TODO check XGetWMHints() for initial mapping state, icon, etc. pp. // assume window wants to be visible on the current desktop desk = KWM::desktop( win ); //workspace()->currentDesktop(); KWM::moveToDesktop( win, desk ); // compatibility setMappingState( state ); if ( state == NormalState && isOnDesktop( workspace()->currentDesktop() ) ) { show(); if ( options->focusPolicyIsReasonable() ) workspace()->requestFocus( this ); } } /*! Gets the client's normal WM hints and reconfigures itself respectively. */ void Client::getWmNormalHints() { // TODO keep in mind changing of fix size! if !isWithdrawn()! long msize; if (XGetWMNormalHints(qt_xdisplay(), win, &xSizeHint, &msize) == 0 ) xSizeHint.flags = 0; } /*! Fetches the window's caption (WM_NAME property). It will be stored in the client's caption(). */ void Client::fetchName() { char* name = 0; QString s; if ( XFetchName( qt_xdisplay(), win, &name ) && name ) { s = QString::fromLatin1( name ); XFree( name ); } if ( s.isEmpty() ) s = i18n("unnamed"); if ( s != caption() ) { setCaption( "" ); if (workspace()->hasCaption( s ) ){ int i = 2; QString s2; do { s2 = s + " <" + QString::number(i) + ">"; i++; } while (workspace()->hasCaption(s2) ); s = s2; } setCaption( s ); if ( !isWithdrawn() ) captionChange( caption() ); } } /*! Sets the client window's mapping state. Possible values are WithdrawnState, IconicState, NormalState. */ void Client::setMappingState(int s){ if ( !win) return; unsigned long data[2]; data[0] = (unsigned long) s; data[1] = (unsigned long) None; state = s; XChangeProperty(qt_xdisplay(), win, qt_wm_state, qt_wm_state, 32, PropModeReplace, (unsigned char *)data, 2); } /*! General handler for XEvents concerning the client window */ bool Client::windowEvent( XEvent * e) { switch (e->type) { case UnmapNotify: if ( e->xunmap.window == winId() ) { mapped = 0; return FALSE; } return unmapNotify( e->xunmap ); case MapNotify: if ( e->xmap.window == winId() ) { mapped = 1; return FALSE; } break; case MapRequest: return mapRequest( e->xmaprequest ); case ConfigureRequest: return configureRequest( e->xconfigurerequest ); case PropertyNotify: return propertyNotify( e->xproperty ); case ButtonPress: case ButtonRelease: - XAllowEvents( qt_xdisplay(), e->xbutton.time, ReplayPointer ); break; case FocusIn: if ( e->xfocus.mode == NotifyUngrab ) break; // we don't care if ( e->xfocus.detail == NotifyPointer ) break; // we don't care setActive( TRUE ); break; case FocusOut: if ( e->xfocus.mode == NotifyGrab ) break; // we don't care if ( isShade() ) break; // we neither if ( e->xfocus.detail != NotifyNonlinear ) return TRUE; // hack for motif apps like netscape setActive( FALSE ); break; case ReparentNotify: break; case ClientMessage: return clientMessage( e->xclient ); default: break; } return TRUE; // we accept everything :-) } /*! Handles map requests of the client window */ bool Client::mapRequest( XMapRequestEvent& /* e */ ) { switch ( mappingState() ) { case WithdrawnState: manage(); break; case IconicState: show(); break; case NormalState: show(); // for safety break; } return TRUE; } /*! Handles unmap notify events of the client window */ bool Client::unmapNotify( XUnmapEvent& e ) { if ( e.event != windowWrapper()->winId() && !e.send_event ) return TRUE; switch ( mappingState() ) { case IconicState: // only react on sent events, all others are produced by us if ( e.send_event ) withdraw(); break; case NormalState: if ( ( !mapped || !windowWrapper()->isVisibleTo( this )) && !e.send_event ) return TRUE; // this event was produced by us as well // maybe we will be destroyed soon. Check this first. XEvent ev; if ( XCheckTypedWindowEvent (qt_xdisplay(), windowWrapper()->winId(), DestroyNotify, &ev) ){ workspace()->destroyClient( this ); return TRUE; } // fall through case WithdrawnState: // however that has been possible.... withdraw(); break; } return TRUE; } /*! Withdraws the window and destroys the client afterwards */ void Client::withdraw() { setMappingState( WithdrawnState ); KWM::moveToDesktop( win, -1 ); // compatibility releaseWindow(); workspace()->destroyClient( this ); } /*! Handles configure requests of the client window */ bool Client::configureRequest( XConfigureRequestEvent& e ) { if ( isResize() ) return TRUE; // we have better things to do right now if ( isShade() ) setShade( FALSE ); // compress configure requests XEvent otherEvent; while (XCheckTypedWindowEvent (qt_xdisplay(), win, ConfigureRequest, &otherEvent) ) { if (otherEvent.xconfigurerequest.value_mask == e.value_mask) e = otherEvent.xconfigurerequest; else { XPutBackEvent(qt_xdisplay(), &otherEvent); break; } } if ( e.value_mask & CWBorderWidth ) { // first, get rid of a window border XWindowChanges wc; unsigned int value_mask = 0; wc.border_width = 0; value_mask = CWBorderWidth; XConfigureWindow( qt_xdisplay(), win, value_mask, & wc ); } if ( e.value_mask & (CWX | CWY ) ) { int nx = x() + windowWrapper()->x(); int ny = y() + windowWrapper()->y(); if ( e.value_mask & CWX ) nx = e.x; if ( e.value_mask & CWY ) ny = e.y; move( nx - windowWrapper()->x(), ny - windowWrapper()->y() ); } if ( e.value_mask & (CWWidth | CWHeight ) ) { int nw = windowWrapper()->width(); int nh = windowWrapper()->height(); if ( e.value_mask & CWWidth ) nw = e.width; if ( e.value_mask & CWHeight ) nh = e.height; resize( sizeForWindowSize( QSize( nw, nh ) ) ); } // TODO handle stacking! sendSynteticConfigureNotify(); return TRUE; } /*! Handles property changes of the client window */ bool Client::propertyNotify( XPropertyEvent& e ) { switch ( e.atom ) { case XA_WM_NORMAL_HINTS: getWmNormalHints(); break; case XA_WM_NAME: fetchName(); break; case XA_WM_TRANSIENT_FOR: if ( !XGetTransientForHint( qt_xdisplay(), (Window) win, (Window*) &transient_for ) ) transient_for = None; break; case XA_WM_HINTS: getWMHints(); break; default: if ( e.atom == atoms->wm_protocols ) getWindowProtocols(); else if ( e.atom == atoms->kwm_win_icon ) { getWMHints(); // for the icons } break; } return TRUE; } /*! Handles client messages for the client window */ bool Client::clientMessage( XClientMessageEvent& e ) { if ( e.message_type == atoms->wm_change_state) { if ( e.data.l[0] == IconicState && isNormal() ) iconify(); return TRUE; } else if ( e.message_type == atoms->net_active_window ) { workspace()->activateClient( this ); return TRUE; } return FALSE; } /*! Auxiliary function to inform the client about the current window configuration. */ void Client::sendSynteticConfigureNotify() { XConfigureEvent c; c.type = ConfigureNotify; c.event = win; c.window = win; c.x = x() + windowWrapper()->x(); c.y = y() + windowWrapper()->y(); c.width = windowWrapper()->width(); c.height = windowWrapper()->height(); c.border_width = 0; XSendEvent( qt_xdisplay(), c.event, TRUE, NoEventMask, (XEvent*)&c ); } /*! Adjust the frame size \a frame according to he window's size hints. */ QSize Client::adjustedSize( const QSize& frame) const { // first, get the window size for the given frame size s QSize wsize( frame.width() - ( width() - wwrap->width() ), frame.height() - ( height() - wwrap->height() ) ); return sizeForWindowSize( wsize ); } /*! Calculate the appropriate frame size for the given window size \a wsize. \a wsize is adapted according to the window's size hints (minimum, maximum and incremental size changes). */ QSize Client::sizeForWindowSize( const QSize& wsize, bool ignore_height) const { int w = wsize.width(); int h = wsize.height(); if (w<1) w = 1; if (h<1) h = 1; int bw = 0; int bh = 0; if (xSizeHint.flags & PBaseSize) { bw = xSizeHint.base_width; bh = xSizeHint.base_height; if (w < xSizeHint.base_width) w = xSizeHint.base_width; if (h < xSizeHint.base_height) h = xSizeHint.base_height; } if (xSizeHint.flags & PResizeInc) { if ( xSizeHint.width_inc > 0 ) { int sx = (w - bw) / xSizeHint.width_inc; w = bw + sx * xSizeHint.width_inc; } if ( xSizeHint.height_inc > 0 ) { int sy = (h - bh) / xSizeHint.height_inc; h = bh + sy * xSizeHint.height_inc; } } if (xSizeHint.flags & PMaxSize) { w = QMIN( xSizeHint.max_width, w ); h = QMIN( xSizeHint.max_height, h ); } if (xSizeHint.flags & PMinSize) { w = QMAX( xSizeHint.min_width, w ); h = QMAX( xSizeHint.min_height, h ); } + w = QMAX( minimumWidth(), w ); + h = QMAX( minimumHeight(), h ); + int ww = wwrap->width(); int wh = 0; if ( !wwrap->testWState( WState_ForceHide ) ) wh = wwrap->height(); if ( ignore_height && wsize.height() == 0 ) h = 0; return QSize( width() - ww + w, height()-wh+h ); - -// return QSize( QMIN( QMAX( width() - ww + w, minimumWidth() ), -// maximumWidth() ), -// ignore_height? height()-wh+h : (QMIN( QMAX( height() - wh + h, minimumHeight() ), -// maximumHeight() ) ) ); } /*! Reimplemented to provide move/resize */ void Client::mousePressEvent( QMouseEvent * e) { if ( e->button() == LeftButton ) { if ( options->focusPolicyIsReasonable() ) workspace()->requestFocus( this ); workspace()->raiseClient( this ); mouseMoveEvent( e ); buttonDown = TRUE; moveOffset = e->pos(); invertedMoveOffset = rect().bottomRight() - e->pos(); } else if ( !buttonDown && e->button() == RightButton ) { workspace()->clientPopup( this ) ->popup( e->globalPos() ); } } /*! Reimplemented to provide move/resize */ void Client::mouseReleaseEvent( QMouseEvent * e) { - if ( e->button() == LeftButton ) { + if ( (e->stateAfter() & MouseButtonMask) == 0 ) { buttonDown = FALSE; if ( moveResizeMode ) { clearbound(); if ( ( isMove() && options->moveMode != Options::Opaque ) || ( isResize() && options->resizeMode != Options::Opaque ) ) XUngrabServer( qt_xdisplay() ); setGeometry( geom ); moveResizeMode = FALSE; releaseMouse(); } } } /*! */ void Client::resizeEvent( QResizeEvent * e) { QWidget::resizeEvent( e ); } /*! Reimplemented to provide move/resize */ void Client::mouseMoveEvent( QMouseEvent * e) { if ( !buttonDown ) { mode = mousePosition( e->pos() ); setMouseCursor( mode ); return; } if ( !moveResizeMode ) { QPoint p( e->pos() - moveOffset ); if ( (QABS( p.x()) >= 4) || (QABS( p.y() ) >= 4 )) { moveResizeMode = TRUE; grabMouse( cursor() ); // to keep the right cursor if ( ( isMove() && options->moveMode != Options::Opaque ) || ( isResize() && options->resizeMode != Options::Opaque ) ) XGrabServer( qt_xdisplay() ); } else return; } if ( mode != Center && shaded ) { wwrap->show(); workspace()->requestFocus( this ); shaded = FALSE; } QPoint globalPos = e->globalPos(); // pos() + geometry().topLeft(); QPoint p = globalPos + invertedMoveOffset; QPoint pp = globalPos - moveOffset; QSize mpsize( geometry().right() - pp.x() + 1, geometry().bottom() - pp.y() + 1 ); mpsize = adjustedSize( mpsize ); QPoint mp( geometry().right() - mpsize.width() + 1, geometry().bottom() - mpsize.height() + 1 ); geom = geometry(); switch ( mode ) { case TopLeft: geom = QRect( mp, geometry().bottomRight() ) ; break; case BottomRight: geom = QRect( geometry().topLeft(), p ) ; break; case BottomLeft: geom = QRect( QPoint(mp.x(), geometry().y() ), QPoint( geometry().right(), p.y()) ) ; break; case TopRight: geom = QRect( QPoint(geometry().x(), mp.y() ), QPoint( p.x(), geometry().bottom()) ) ; break; case Top: geom = QRect( QPoint( geometry().left(), mp.y() ), geometry().bottomRight() ) ; break; case Bottom: geom = QRect( geometry().topLeft(), QPoint( geometry().right(), p.y() ) ) ; break; case Left: geom = QRect( QPoint( mp.x(), geometry().top() ), geometry().bottomRight() ) ; break; case Right: geom = QRect( geometry().topLeft(), QPoint( p.x(), geometry().bottom() ) ) ; break; case Center: geom.moveTopLeft( pp ); break; default: break; } if ( isResize() && geom.size() != size() ) { + geom.setSize( adjustedSize( geom.size() ) ); if (options->resizeMode == Options::Opaque ) { - geom.setSize( adjustedSize( geom.size() ) ); setGeometry( geom ); } else if ( options->resizeMode == Options::Transparent ) { clearbound(); drawbound( geom ); } } else if ( isMove() && geom.topLeft() != geometry().topLeft() ) { geom.moveTopLeft( workspace()->adjustClientPosition( this, geom.topLeft() ) ); switch ( options->moveMode ) { case Options::Opaque: move( geom.topLeft() ); break; case Options::Transparent: clearbound(); drawbound( geom ); break; } } } /*! Reimplemented to provide move/resize */ void Client::enterEvent( QEvent * ) { } /*! Reimplemented to provide move/resize */ void Client::leaveEvent( QEvent * ) { if ( !buttonDown ) setCursor( arrowCursor ); } /*! Reimplemented to inform the client about the new window position. */ void Client::setGeometry( int x, int y, int w, int h ) { QWidget::setGeometry(x, y, w, h); sendSynteticConfigureNotify(); } /*! Reimplemented to inform the client about the new window position. */ void Client::move( int x, int y ) { QWidget::move( x, y ); sendSynteticConfigureNotify(); } /*!\reimp */ void Client::showEvent( QShowEvent* ) { setMappingState( NormalState ); windowWrapper()->show();// ########## hack for qt < 2.1 } /*! Reimplemented to hide the window wrapper as well. Also informs the workspace. */ void Client::hideEvent( QHideEvent* ) { windowWrapper()->hide();// ########## hack for qt < 2.1 workspace()->clientHidden( this ); } /*! Late initialialize the client after the window has been managed. Ensure to call the superclasses init() implementation when subclassing. */ void Client::init() { } /*!\fn captionChange( const QString& name ) Indicates that the caption (the window title) changed to \a name. Subclasses shall then repaint the title string in a clever, fast mannor. The default implementation calls repaint( FALSE ); */ void Client::captionChange( const QString& ) { repaint( FALSE ); } /*!\fn activeChange( bool act ) Indicates that the activation state changed to \a act. Subclasses may want to indicate the new state graphically in a clever, fast mannor. The default implementation calls repaint( FALSE ); */ void Client::activeChange( bool ) { repaint( FALSE ); } /*! Indicates that the application's icon changed to \a act. Subclasses may want to indicate the new state graphically in a clever, fast mannor. The default implementation calls repaint( FALSE ); */ void Client::iconChange() { repaint( FALSE ); } /*!\fn maximizeChange( bool max ) Indicates that the window was maximized or demaximized. \a max is set respectively. Subclasses may want to indicate the new state graphically, for example with a different icon. */ void Client::maximizeChange( bool ) { } /*!\fn stickyChange( bool sticky ) Indicates that the window was made sticky or unsticky. \a sticky is set respectively. Subclasses may want to indicate the new state graphically, for example with a different icon. */ void Client::stickyChange( bool ) { } /*! Paints a client window. The default implementation does nothing. To be implemented by subclasses. */ void Client::paintEvent( QPaintEvent * ) { } /*! Releases the window. The client has done its job and the window is still existing. */ void Client::releaseWindow() { if ( win ) { gravitate( TRUE ); windowWrapper()->releaseWindow(); win = 0; } } /*! Invalidates the window to avoid the client accessing it again. This function is called by the workspace when the window has been destroyed. */ void Client::invalidateWindow() { win = 0; windowWrapper()->invalidateWindow(); } -/*! - Returns the minimum size. This function differs from QWidget::minimumSize() - and is to be preferred - */ -QSize Client::minimumSize() const -{ - return QSize( minimumWidth(), minimumHeight() ); -} -/*! - Returns the minimum width. This function differs from QWidget::minimumWidth() - and is to be preferred - */ -int Client::minimumWidth() const -{ -// if (xSizeHint.flags & PMinSize) -// return QMAX( width() - wwrap->width() + xSizeHint.min_width, -// QWidget::minimumWidth() ); -// else - return QWidget::minimumWidth(); -} -/*! - Returns the minimum height. This function differs from QWidget::minimumHeight() - and is to be preferred - */ -int Client::minimumHeight() const -{ -// if (xSizeHint.flags & PMinSize) -// return QMAX( height() - wwrap->height() + xSizeHint.min_height, -// QWidget::minimumHeight() ); -// else - return QWidget::minimumHeight(); -} - -/*! - Returns the maximum size. This function differs from QWidget::maximumSize() - and is to be preferred - */ -QSize Client::maximumSize() const -{ - return QSize( maximumWidth(), maximumHeight() ); -} -/*! - Returns the maximum width. This function differs from QWidget::maximumWidth() - and is to be preferred - */ -int Client::maximumWidth() const -{ -// if (xSizeHint.flags & PMaxSize) -// return QMIN( width() - wwrap->width() + xSizeHint.max_width, -// QWidget::maximumWidth() ); -// else - return QWidget::maximumWidth(); -} -/*! - Returns the maximum height. This function differs from QWidget::maximumHeight() - and is to be preferred - */ -int Client::maximumHeight() const -{ -// if (xSizeHint.flags & PMaxSize) -// return QMIN( height() - wwrap->height() + xSizeHint.max_height, -// QWidget::maximumHeight() ); -// else - return QWidget::maximumHeight(); -} - void Client::iconify() { if ( isShade() ) setShade( FALSE ); if ( workspace()->iconifyMeansWithdraw( this ) ) { setMappingState( WithdrawnState ); hide(); return; } setMappingState( IconicState ); hide(); // TODO animation (virtual function) workspace()->iconifyOrDeiconifyTransientsOf( this ); } void Client::closeWindow() { if ( Pdeletewindow ){ sendClientMessage( win, atoms->wm_protocols, atoms->wm_delete_window); } else { // client will not react on wm_delete_window. We have not choice // but destroy his connection to the XServer. XKillClient(qt_xdisplay(), win ); workspace()->destroyClient( this ); } } void Client::maximize( MaximizeMode m) { if ( isShade() ) setShade( FALSE ); if ( geom_restore.isNull() ) { geom_restore = geometry(); switch ( m ) { case MaximizeVertical: setGeometry( QRect( QPoint( x(), workspace()->geometry().top() ), adjustedSize( QSize( width(), workspace()->geometry().height()) ) ) ); break; case MaximizeHorizontal: setGeometry( QRect( QPoint( workspace()->geometry().left(), y() ), adjustedSize( QSize( workspace()->geometry().width(), height() ) ) ) ); break; default: setGeometry( QRect( workspace()->geometry().topLeft(), adjustedSize(workspace()->geometry().size()) ) ); } maximizeChange( TRUE ); } else { setGeometry( geom_restore ); QRect invalid; geom_restore = invalid; maximizeChange( FALSE ); } } void Client::toggleSticky() { setSticky( !isSticky() ); } void Client::maximize() { maximize( MaximizeFull ); } -void Client::fullScreen() -{ - workspace()->makeFullScreen( this ); -} - /*! Catch events of the WindowWrapper */ bool Client::eventFilter( QObject *o, QEvent * e) { if ( o != wwrap ) return FALSE; switch ( e->type() ) { - case QEvent::MouseButtonPress: - if ( options->focusPolicyIsReasonable() ) - workspace()->requestFocus( this ); - workspace()->raiseClient( this ); - break; case QEvent::MouseButtonRelease: break; case QEvent::Show: windowWrapperShowEvent( (QShowEvent*)e ); break; case QEvent::Hide: windowWrapperHideEvent( (QHideEvent*)e ); break; default: break; } return FALSE; } void Client::gravitate( bool invert ){ int gravity, dx, dy; dx = dy = 0; gravity = NorthWestGravity; if ( xSizeHint.flags & PWinGravity) gravity = xSizeHint.win_gravity; switch (gravity) { case NorthWestGravity: dx = 0; dy = 0; break; case NorthGravity: dx = -windowWrapper()->x(); dy = 0; break; case NorthEastGravity: dx = -( width() - windowWrapper()->width() ); dy = 0; break; case WestGravity: dx = 0; dy = -windowWrapper()->y(); break; case CenterGravity: case StaticGravity: dx = -windowWrapper()->x(); dy = -windowWrapper()->y(); break; case EastGravity: dx = -( width() - windowWrapper()->width() ); dy = -windowWrapper()->y(); break; case SouthWestGravity: dx = 0; dy = -( height() - windowWrapper()->height() ); break; case SouthGravity: dx = -windowWrapper()->x(); dy = -( height() - windowWrapper()->height() ); break; case SouthEastGravity: dx = -( width() - windowWrapper()->width() - 1 ); dy = -( height() - windowWrapper()->height() - 1 ); break; } if (invert) move( x() - dx, y() - dy ); else move( x() + dx, y() + dy ); } /*! Reimplement to handle crossing events (qt should provide xroot, yroot) */ bool Client::x11Event( XEvent * e) { if ( e->type == EnterNotify ) { if ( options->focusPolicy != Options::ClickToFocus ) workspace()->requestFocus( this ); return TRUE; } if ( e->type == LeaveNotify ) { if ( !buttonDown ) setCursor( arrowCursor ); if ( options->focusPolicy == Options::FocusStricklyUnderMouse ) { if ( isActive() && !rect().contains( QPoint( e->xcrossing.x, e->xcrossing.y ) ) ) workspace()->requestFocus( 0 ) ; } return TRUE; } return FALSE; } /*! Returns a logical mouse position for the cursor position \a p. Possible positions are: Nowhere, TopLeft , BottomRight, BottomLeft, TopRight, Top, Bottom, Left, Right, Center */ Client::MousePosition Client::mousePosition( const QPoint& p ) const { const int range = 16; const int border = 4; MousePosition m = Nowhere; if ( ( p.x() > border && p.x() < width() - border ) && ( p.y() > border && p.y() < height() - border ) ) return Center; if ( p.y() <= range && p.x() <= range) m = TopLeft; else if ( p.y() >= height()-range && p.x() >= width()-range) m = BottomRight; else if ( p.y() >= height()-range && p.x() <= range) m = BottomLeft; else if ( p.y() <= range && p.x() >= width()-range) m = TopRight; else if ( p.y() <= border ) m = Top; else if ( p.y() >= height()-border ) m = Bottom; else if ( p.x() <= border ) m = Left; else if ( p.x() >= width()-border ) m = Right; else m = Center; return m; } /*! Sets an appropriate cursor shape for the logical mouse position \a m \sa QWidget::setCursor() */ void Client::setMouseCursor( MousePosition m ) { switch ( m ) { case TopLeft: case BottomRight: setCursor( sizeFDiagCursor ); break; case BottomLeft: case TopRight: setCursor( sizeBDiagCursor ); break; case Top: case Bottom: setCursor( sizeVerCursor ); break; case Left: case Right: setCursor( sizeHorCursor ); break; default: setCursor( arrowCursor ); break; } } bool Client::isShade() const { return shaded; } void Client::setShade( bool s ) { if ( shaded == s ) return; shaded = s; int as = options->animateShade()? options->animSteps() : 1; if (shaded ) { int h = height(); QSize s( sizeForWindowSize( QSize( windowWrapper()->width(), 0), TRUE ) ); windowWrapper()->hide(); repaint( FALSE ); // force direct repaint setWFlags( WNorthWestGravity ); int step = QMAX( 15, QABS( h - s.height() ) / as )+1; do { h -= step; resize ( s.width(), h ); QApplication::syncX(); } while ( h > s.height() + step ); clearWFlags( WNorthWestGravity ); resize (s ); } else { int h = height(); QSize s( sizeForWindowSize( windowWrapper()->size(), TRUE ) ); setWFlags( WNorthWestGravity ); int step = QMAX( 15, QABS( h - s.height() ) / as )+1; do { h += step; resize ( s.width(), h ); // assume a border // we do not have time to wait for X to send us paint events repaint( 0, h - step-5, width(), step+5, TRUE); QApplication::syncX(); } while ( h < s.height() - step ); clearWFlags( WNorthWestGravity ); resize ( s ); windowWrapper()->show(); layout()->activate(); repaint(); if ( isActive() ) workspace()->requestFocus( this ); } workspace()->iconifyOrDeiconifyTransientsOf( this ); } /*! Sets the client's active state to \a act. This function does only change the visual appearance of the client, it does not change the focus setting. Use Workspace::activateClient() or Workspace::requestFocus() instead. If a client receives or looses the focus, it calls setActive() on its own. */ void Client::setActive( bool act) { if ( active == act ) return; active = act; if ( active ) workspace()->setActiveClient( this ); activeChange( active ); } /*! Sets the window's sticky property to b */ void Client::setSticky( bool b ) { if ( is_sticky == b ) return; is_sticky = b; if ( !is_sticky ) setDesktop( workspace()->currentDesktop() ); stickyChange( is_sticky ); } void Client::setDesktop( int desktop) { if ( isOnDesktop( desktop ) ) return; desk = desktop; KWM::moveToDesktop( win, desk );//##### compatibility } void Client::getWMHints() { icon_pix = KWM::icon( win, 32, 32 ); // TODO sizes from workspace miniicon_pix = KWM::miniIcon( win, 16, 16 ); if ( !isWithdrawn() ) iconChange(); input = TRUE; XWMHints *hints = XGetWMHints(qt_xdisplay(), win ); if ( hints ) { if ( hints->flags & InputHint ) input = hints->input; XFree((char*)hints); } } void Client::getWindowProtocols(){ Atom *p; int i,n; Pdeletewindow = 0; Ptakefocus = 0; if (XGetWMProtocols(qt_xdisplay(), win, &p, &n)){ for (i = 0; i < n; i++) if (p[i] == atoms->wm_delete_window) Pdeletewindow = 1; else if (p[i] == atoms->wm_take_focus) Ptakefocus = 1; if (n>0) XFree(p); } } /*! Puts the focus on this window. Clients should never calls this themselves, instead they should use Workspace::requestFocus(). */ void Client::takeFocus() { if ( input ) XSetInputFocus( qt_xdisplay(), win, RevertToPointerRoot, kwin_time ); if ( Ptakefocus ) sendClientMessage(win, atoms->wm_protocols, atoms->wm_take_focus); } /*!\reimp */ void Client::setMask( const QRegion & reg) { mask = reg; QWidget::setMask( reg ); } /*! Returns the main client. For normal windows, this is the window itself. For transient windows, it is the parent. */ Client* Client::mainClient() { if ( !isTransient() ) return this; ClientList saveset; Client* c = this; do { saveset.append( c ); c = workspace()->findClient( c->transientFor() ); } while ( c && c->isTransient() && !saveset.contains( c ) ); return c?c:this; } + +bool Client::performMouseCommand( Options::MouseCommand command, QPoint globalPos) +{ + bool replay = FALSE; + switch (command) { + case Options::MouseRaise: + break; + case Options::MouseLower: + break; + case Options::MouseOperationsMenu: + break; + case Options::MouseToggleRaiseAndLower: + break; + case Options::MouseActivateAndRaise: + workspace()->requestFocus( this ); + workspace()->raiseClient( this ); + break; + case Options::MouseActivateAndLower: + workspace()->requestFocus( this ); + workspace()->raiseClient( this ); + break; + case Options::MouseActivate: + workspace()->requestFocus( this ); + break; + case Options::MouseActivateRaiseAndPassClick: + workspace()->requestFocus( this ); + workspace()->raiseClient( this ); + replay = TRUE; + break; + case Options::MouseActivateAndPassClick: + workspace()->requestFocus( this ); + workspace()->raiseClient( this ); + replay = TRUE; + break; + case Options::MouseMove: + mode = Center; + moveResizeMode = TRUE; + buttonDown = TRUE; + moveOffset = mapFromGlobal( globalPos ); + invertedMoveOffset = rect().bottomRight() - moveOffset; + grabMouse( arrowCursor ); + if ( options->moveMode != Options::Opaque ) + XGrabServer( qt_xdisplay() ); + break; + case Options::MouseResize: + moveResizeMode = TRUE; + buttonDown = TRUE; + moveOffset = mapFromGlobal( globalPos ); + if ( moveOffset.x() < width()/2) { + if ( moveOffset.y() < height()/2) + mode = TopLeft; + else + mode = BottomLeft; + } else { + if ( moveOffset.y() < height()/2) + mode = TopRight; + else + mode = BottomRight; + } + invertedMoveOffset = rect().bottomRight() - moveOffset; + setMouseCursor( mode ); + grabMouse( cursor() ); + if ( options->resizeMode != Options::Opaque ) + XGrabServer( qt_xdisplay() ); + break; + case Options::MouseNothing: + // fall through + default: + replay = TRUE; + break; + } + return replay; +} + + NoBorderClient::NoBorderClient( Workspace *ws, WId w, QWidget *parent, const char *name ) : Client( ws, w, parent, name ) { QHBoxLayout* h = new QHBoxLayout( this ); h->addWidget( windowWrapper() ); } NoBorderClient::~NoBorderClient() { } diff --git a/kwin/client.h b/kwin/client.h index 5899dc4d10..ae278e8d05 100644 --- a/kwin/client.h +++ b/kwin/client.h @@ -1,334 +1,325 @@ #ifndef CLIENT_H #define CLIENT_H #include "options.h" #include #include #include #include #include #include #include class Workspace; class Client; class WindowWrapper : public QWidget { Q_OBJECT public: WindowWrapper( WId w, Client *parent=0, const char* name=0); ~WindowWrapper(); WId window() const; void releaseWindow(); void invalidateWindow(); QSize sizeHint() const; QSizePolicy sizePolicy() const; void pseudoShow(); protected: void resizeEvent( QResizeEvent * ); void showEvent( QShowEvent* ); void hideEvent( QHideEvent* ); - void mousePressEvent( QMouseEvent* ); - void mouseReleaseEvent( QMouseEvent* ); - void mouseMoveEvent( QMouseEvent* ); bool x11Event( XEvent * ); // X11 event private: WId win; Time lastMouseEventTime; }; inline WId WindowWrapper::window() const { return win; } class Client : public QWidget { Q_OBJECT public: Client( Workspace *ws, WId w, QWidget *parent=0, const char *name=0, WFlags f = 0); ~Client(); WId window() const; WindowWrapper* windowWrapper() const; Workspace* workspace() const; void releaseWindow(); void invalidateWindow(); WId transientFor() const; bool isTransient() const; Client* mainClient(); virtual bool windowEvent( XEvent * ); void manage( bool isMapped = FALSE ); void setMappingState( int s ); int mappingState() const; void requestActivation(); void withdraw(); QSize adjustedSize( const QSize& ) const; - QSize minimumSize() const; - int minimumWidth() const; - int minimumHeight() const; - QSize maximumSize() const; - int maximumWidth() const; - int maximumHeight() const; QPixmap icon() const; QPixmap miniIcon() const; // is the window in withdrawn state? bool isWithdrawn(){ return state == WithdrawnState; } // is the window in iconic state? bool isIconified(){ return state == IconicState; } // is the window in normal state? bool isNormal(){ return state == NormalState; } bool isActive() const; void setActive( bool ); int desktop() const; void setDesktop( int ); bool isOnDesktop( int d ) const; bool isShade() const; virtual void setShade( bool ); bool isMaximized() const; enum MaximizeMode { MaximizeVertical, MaximizeHorizontal, MaximizeFull }; bool isSticky() const; void setSticky( bool ); void takeFocus(); void setMask( const QRegion & ); // transparent stuff virtual void drawbound( const QRect& geom ); virtual void clearbound(); // shape extensions bool shape() const; void updateShape(); void setGeometry( int x, int y, int w, int h ); void setGeometry( const QRect &r ) { setGeometry( r.left(), r.top(), r.width(), r.height() ); } void move( int x, int y ); void move( const QPoint & p ) { move( p.x(), p.y() ); } virtual bool wantsTabFocus() const { return TRUE;} //### just for now + bool performMouseCommand( Options::MouseCommand, QPoint globalPos ); public slots: void iconify(); void closeWindow(); void maximize( MaximizeMode ); void maximize(); - void fullScreen(); void toggleSticky(); protected: void paintEvent( QPaintEvent * ); void mousePressEvent( QMouseEvent * ); void mouseReleaseEvent( QMouseEvent * ); void mouseMoveEvent( QMouseEvent * ); void resizeEvent( QResizeEvent * ); virtual void windowWrapperShowEvent( QShowEvent* ){} virtual void windowWrapperHideEvent( QHideEvent* ){} void enterEvent( QEvent * ); void leaveEvent( QEvent * ); void showEvent( QShowEvent* ); void hideEvent( QHideEvent* ); bool x11Event( XEvent * ); // X11 event bool eventFilter( QObject *, QEvent * ); virtual void init(); virtual void captionChange( const QString& name ); virtual void iconChange(); virtual void activeChange( bool ); virtual void maximizeChange( bool ); virtual void stickyChange( bool ); enum MousePosition { Nowhere, TopLeft , BottomRight, BottomLeft, TopRight, Top, Bottom, Left, Right, Center }; virtual MousePosition mousePosition( const QPoint& ) const; virtual void setMouseCursor( MousePosition m ); // handlers for X11 events bool mapRequest( XMapRequestEvent& e ); bool unmapNotify( XUnmapEvent& e ); bool configureRequest( XConfigureRequestEvent& e ); bool propertyNotify( XPropertyEvent& e ); bool clientMessage( XClientMessageEvent& e ); private: QSize sizeForWindowSize( const QSize&, bool ignore_height = FALSE ) const; void getWmNormalHints(); void fetchName(); void gravitate( bool invert ); WId win; WindowWrapper* wwrap; Workspace* wspace; int desk; bool buttonDown; bool moveResizeMode; bool isMove() const { return moveResizeMode && mode == Center; } bool isResize() const { return moveResizeMode && !isMove(); } MousePosition mode; QPoint moveOffset; QPoint invertedMoveOffset; QSize clientSize; XSizeHints xSizeHint; void sendSynteticConfigureNotify(); int state; bool active; QRect original_geometry; QRect geom; //### TODO bool shaded; WId transient_for; bool is_sticky; bool is_shape; void getWMHints(); void getWindowProtocols(); uint Pdeletewindow :1; // does the window understand the DeleteWindow protocol? uint Ptakefocus :1;// does the window understand the TakeFocus protocol? uint input :1; // does the window want input in its wm_hints uint mapped :1; // keeps track of our visiblity within the asynchronous event flow QPixmap icon_pix; QPixmap miniicon_pix; QRect geom_restore; QRegion mask; }; inline WId Client::window() const { return win; } inline WindowWrapper* Client::windowWrapper() const { return wwrap; } inline Workspace* Client::workspace() const { return wspace; } inline WId Client::transientFor() const { return transient_for; } inline bool Client::isTransient() const { return transient_for != 0; } inline int Client::mappingState() const { return state; } inline bool Client::isActive() const { return active; } /*! Returns the virtual desktop within the workspace() the client window is located in, -1 if it isn't located on any special desktop. This may be if the window wasn't mapped yet or if the window is sticky. Do not use desktop() directly, use isOnDesktop() instead. */ inline int Client::desktop() const { return desk; } /*! Returns whether the client is on visible or iconified on the virtual desktop \a d. This is always TRUE for sticky clients. */ inline bool Client::isOnDesktop( int d ) const { return desk == d || desk == -1 || isSticky(); } inline QPixmap Client::icon() const { return icon_pix; } inline QPixmap Client::miniIcon() const { return miniicon_pix; } /*! Is the client maximized? */ inline bool Client::isMaximized() const { return !geom_restore.isNull(); } inline bool Client::isSticky() const { return is_sticky; } inline bool Client::shape() const { return is_shape; } class NoBorderClient : public Client { Q_OBJECT public: NoBorderClient( Workspace *ws, WId w, QWidget *parent=0, const char *name=0 ); ~NoBorderClient(); bool wantsTabFocus() const { return FALSE;} //### just for now }; #endif diff --git a/kwin/options.cpp b/kwin/options.cpp index c2f99d3a5e..c400bc16c9 100644 --- a/kwin/options.cpp +++ b/kwin/options.cpp @@ -1,162 +1,217 @@ #include "options.h" #include #include #include #include #include Options::Options() : QObject( 0, 0) { int i; for(i=0; i < KWINCOLORS*2; ++i) cg[i] = NULL; reload(); //CT fix them for now. Will be read from rc placement = Smart; animate_shade = false; anim_steps = 20; border_snap_zone = window_snap_zone = 10; connect( kapp, SIGNAL( appearanceChanged() ), this, SLOT(reload() ) ); } Options::~Options(){ int i; for(i=0; i < KWINCOLORS*2; ++i){ if(cg[i]){ delete cg[i]; cg[i] = NULL; } } } const QColor& Options::color(ColorType type, bool active) { return(colors[type + (active ? 0 : KWINCOLORS)]); } const QFont& Options::font(bool active) { return(active ? activeFont : inactiveFont); } const QColorGroup& Options::colorGroup(ColorType type, bool active) { int idx = type + (active ? 0 : KWINCOLORS); if(cg[idx]) return(*cg[idx]); cg[idx] = new QColorGroup(Qt::black, colors[idx], colors[idx].light(150), colors[idx].dark(), colors[idx].dark(120), Qt::black, QApplication::palette().normal(). base()); return(*cg[idx]); } void Options::reload() { focusPolicy = ClickToFocus; - resizeMode = Opaque; - moveMode = Opaque;// Transparent; QPalette pal = QApplication::palette(); KConfig *config = KGlobal::config(); config->setGroup("WM"); // normal colors colors[Frame] = pal.normal().background(); colors[Frame] = config->readColorEntry("frame", &colors[Frame]); colors[Handle] = QColor( 140, 140, 140 ); colors[Handle] = config->readColorEntry("handle", &colors[Handle]); colors[ButtonBg] = colors[Frame]; colors[ButtonBg] = config->readColorEntry("activeTitleBtnBg", &colors[Frame]); if(QPixmap::defaultDepth() < 15) colors[ButtonBlend] = colors[ ButtonBg ]; else colors[ButtonBlend] = colors[ ButtonBg ].dark(150); colors[ButtonBlend] = config->readColorEntry("activeTitleBtnBlend", &colors[ButtonBlend]); colors[TitleBar] = pal.normal().highlight(); colors[TitleBar] = config->readColorEntry("activeBackground", &colors[TitleBar]); if(QPixmap::defaultDepth() < 15) colors[TitleBlend] = colors[ TitleBar ]; else colors[TitleBlend] = colors[ TitleBar ].dark(150); colors[TitleBlend] = config->readColorEntry("activeBlend", &colors[TitleBlend]); colors[Font] = pal.normal().highlightedText(); colors[Font] = config->readColorEntry("activeForeground", &colors[Font]); colors[ButtonFg] = Qt::darkGray; colors[ButtonFg] = config->readColorEntry("activeTitleBtnFg", &colors[ButtonFg]); // inactive colors[Frame+KWINCOLORS] = config->readColorEntry("inactiveFrame", &colors[Frame]); colors[TitleBar+KWINCOLORS] = colors[Frame]; colors[TitleBar+KWINCOLORS] = config-> readColorEntry("inactiveBackground", &colors[TitleBar+KWINCOLORS]); if(QPixmap::defaultDepth() < 15) colors[TitleBlend+KWINCOLORS] = colors[ TitleBar+KWINCOLORS ]; else colors[TitleBlend+KWINCOLORS] = colors[ TitleBar+KWINCOLORS ].dark(150); colors[TitleBlend+KWINCOLORS] = config->readColorEntry("inactiveBlend", &colors[TitleBlend+KWINCOLORS]); colors[ButtonBg+KWINCOLORS] = colors[Frame+KWINCOLORS]; colors[ButtonBg+KWINCOLORS] = config->readColorEntry("inactiveTitleBtnBg", &colors[ButtonBg]); if(QPixmap::defaultDepth() < 15) colors[ButtonBlend+KWINCOLORS] = colors[ ButtonBg+KWINCOLORS ]; else colors[ButtonBlend+KWINCOLORS] = colors[ ButtonBg+KWINCOLORS ].dark(150); colors[ButtonBlend+KWINCOLORS] = config->readColorEntry("inactiveTitleBtnBlend", &colors[ButtonBlend+KWINCOLORS]); colors[ButtonFg+KWINCOLORS] = config-> readColorEntry("inactiveTitleBtnFg", &colors[ButtonFg]); colors[Handle+KWINCOLORS] = colors[Frame]; config->readColorEntry("inactiveHandle", &colors[Handle]); colors[Font+KWINCOLORS] = colors[Frame].dark(); colors[Font+KWINCOLORS] = config->readColorEntry("inactiveForeground", &colors[Font+KWINCOLORS]); activeFont = QFont("Helvetica", 12, QFont::Bold); activeFont = config->readFontEntry("activeFont", &activeFont); inactiveFont = config->readFontEntry("inactiveFont", &activeFont); int i; for(i=0; i < KWINCOLORS*2; ++i){ if(cg[i]){ delete cg[i]; cg[i] = NULL; } } - //CT well, what this costs us? - config->setGroup("Actions"); - + config->setGroup( "Windows" ); + moveMode = config->readEntry("MoveMode", "Opaque" ) == "Opaque"?Opaque:Transparent; + resizeMode = config->readEntry("ResizeMode", "Opaque" ) == "Opaque"?Opaque:Transparent; + QString val; val = config->readEntry("Placement","Smart"); if (val == "Smart") placement = Smart; else if (val == "Random") placement = Random; else if (val == "Cascade") placement = Cascade; animate_shade = config->readBoolEntry("AnimateShade", false); anim_steps = config->readNumEntry("AnimSteps", 20); border_snap_zone = config->readNumEntry("BorderSnapZone", 10); window_snap_zone = config->readNumEntry("WindowSnapZone", 10); + + + OpTitlebarDblClick = windowOperation( config->readEntry("TitlebarDoubleClickCommand", "winShade") ); + + // Mouse bindings + config->setGroup( "MouseBindings"); + CmdActiveTitlebar1 = mouseCommand(config->readEntry("CommandActiveTitlebar1","Raise")); + CmdActiveTitlebar2 = mouseCommand(config->readEntry("CommandActiveTitlebar2","Lower")); + CmdActiveTitlebar3 = mouseCommand(config->readEntry("CommandActiveTitlebar3","Operations menu")); + CmdInactiveTitlebar1 = mouseCommand(config->readEntry("CommandInactiveTitlebar1","Activate and raise")); + CmdInactiveTitlebar2 = mouseCommand(config->readEntry("CommandInactiveTitlebar2","Activate and lower")); + CmdInactiveTitlebar3 = mouseCommand(config->readEntry("CommandInactiveTitlebar3","Activate")); + CmdWindow1 = mouseCommand(config->readEntry("CommandWindow1","Activate, raise and pass click")); + CmdWindow2 = mouseCommand(config->readEntry("CommandWindow2","Activate and pass click")); + CmdWindow3 = mouseCommand(config->readEntry("CommandWindow3","Activate and pass click")); + CmdAll1 = mouseCommand(config->readEntry("CommandAll1","Move")); + CmdAll2 = mouseCommand(config->readEntry("CommandAll2","Toggle raise and lower")); + CmdAll3 = mouseCommand(config->readEntry("CommandAll3","Resize")); + +} + +Options::WindowOperation Options::windowOperation(const QString &name){ + if (name == "Move") + return MoveOp; + else if (name == "Resize") + return ResizeOp; + else if (name == "Maximize") + return MaximizeOp; + else if (name == "Iconify") + return IconifyOp; + else if (name == "Close") + return CloseOp; + else if (name == "Sticky") + return StickyOp; + else if (name == "Shade") + return ShadeOp; + else if (name == "Operations") + return OperationsOp; + return NoOp; +} + +Options::MouseCommand Options::mouseCommand(const QString &name) +{ + if (name == "Raise") return MouseRaise; + if (name == "Lower") return MouseLower; + if (name == "Operations menu") return MouseOperationsMenu; + if (name == "Toggle raise and lower") return MouseToggleRaiseAndLower; + if (name == "Activate and raise") return MouseActivateAndRaise; + if (name == "Activate and lower") return MouseActivateAndLower; + if (name == "Activate") return MouseActivate; + if (name == "Activate, raise and pass click") return MouseActivateRaiseAndPassClick; + if (name == "Activate and pass click") return MouseActivateAndPassClick; + if (name == "Move") return MouseMove; + if (name == "Resize") return MouseResize; + if (name == "Nothing") return MouseNothing; + return MouseNothing; } diff --git a/kwin/options.h b/kwin/options.h index 2894cf9b6d..d2021f302a 100644 --- a/kwin/options.h +++ b/kwin/options.h @@ -1,118 +1,179 @@ #ifndef OPTIONS_H #define OPTIONS_H #include #include #include // increment this when you add a color type (mosfet) #define KWINCOLORS 8 class Options : public QObject { Q_OBJECT public: Options(); ~Options(); /*! Different focus policies:
  • ClickToFocus - Clicking into a window activates it. This is also the default.
  • FocusFollowsMouse - Moving the mouse pointer actively onto a window activates it.
  • FocusUnderMouse - The window that happens to be under the mouse pointer becomes active.
  • FocusStricklyUnderMouse - Only the window under the mouse pointer is active. If the mouse points nowhere, nothing has the focus. In practice, this is the same as FocusUnderMouse, since kdesktop can take the focus. Note that FocusUnderMouse and FocusStricklyUnderMouse are not particulary useful. They are only provided for old-fashined die-hard UNIX people ;-)
*/ enum FocusPolicy { ClickToFocus, FocusFollowsMouse, FocusUnderMouse, FocusStricklyUnderMouse }; FocusPolicy focusPolicy; enum MoveResizeMode { Transparent, Opaque }; /** * Basic color types that should be recognized by all decoration styles. * Not all styles have to implement all the colors, but for the ones that * are implemented you should retrieve them here. */ // increment KWINCOLORS if you add something (mosfet) enum ColorType{TitleBar=0, TitleBlend, Font, ButtonFg, ButtonBg, ButtonBlend, Frame, Handle}; MoveResizeMode resizeMode; MoveResizeMode moveMode; /** * Placement policies. How workspace decides the way windows get positioned * on the screen. The better the policy, the heavier the resource use. * Normally you don't have to worry. What the WM adds to the startup time * is nil compared to the creation of the window itself in the memory */ enum PlacementPolicy { Random, Smart, Cascade }; PlacementPolicy placement; bool focusPolicyIsReasonable() { return focusPolicy == ClickToFocus || focusPolicy == FocusFollowsMouse; } /** * Return the color for the given decoration. */ const QColor& color(ColorType type, bool active=true); /** * Return a colorgroup using the given decoration color as the background */ const QColorGroup& colorGroup(ColorType type, bool active=true); /** * Return the active or inactive decoration font. */ const QFont& font(bool active=true); /** * Return whether we animate the shading of windows to titlebar or not */ const bool animateShade() { return animate_shade; }; /** * Return the number of animation steps (would this be general?) */ const int animSteps() { return anim_steps; }; /** * Return the size of the zone that triggers snapping on desktop borders */ const int borderSnapZone() { return border_snap_zone; }; /** * Return the number of animation steps (would this be general?) */ const int windowSnapZone() { return window_snap_zone; }; + + + + // mouse bindings + + enum WindowOperation{ + MaximizeOp = 5000, + RestoreOp, + IconifyOp, + MoveOp, + ResizeOp, + CloseOp, + StickyOp, + ShadeOp, + OperationsOp, + NoOp + }; + + WindowOperation operationTitlebarDblClick() { return OpTitlebarDblClick; } + + enum MouseCommand { + MouseRaise, MouseLower, MouseOperationsMenu, MouseToggleRaiseAndLower, + MouseActivateAndRaise, MouseActivateAndLower, MouseActivate, + MouseActivateRaiseAndPassClick, MouseActivateAndPassClick, + MouseMove, MouseResize, MouseNothing + }; + + MouseCommand commandActiveTitlebar1() { return CmdActiveTitlebar1; } + MouseCommand commandActiveTitlebar2() { return CmdActiveTitlebar2; } + MouseCommand commandActiveTitlebar3() { return CmdActiveTitlebar3; } + MouseCommand commandInactiveTitlebar1() { return CmdInactiveTitlebar1; } + MouseCommand commandInactiveTitlebar2() { return CmdInactiveTitlebar2; } + MouseCommand commandInactiveTitlebar3() { return CmdInactiveTitlebar3; } + MouseCommand commandWindow1() { return CmdWindow1; } + MouseCommand commandWindow2() { return CmdWindow2; } + MouseCommand commandWindow3() { return CmdWindow3; } + MouseCommand commandAll1() { return CmdAll1; } + MouseCommand commandAll2() { return CmdAll2; } + MouseCommand commandAll3() { return CmdAll3; } + + + static WindowOperation windowOperation(const QString &name ); + static MouseCommand mouseCommand(const QString &name); + public slots: void reload(); protected: QFont activeFont, inactiveFont; QColor colors[KWINCOLORS*2]; QColorGroup *cg[KWINCOLORS*2]; private: - bool animate_shade; - int anim_steps; - int border_snap_zone, window_snap_zone; + bool animate_shade; + int anim_steps; + int border_snap_zone, window_snap_zone; + + + WindowOperation OpTitlebarDblClick; + + // mouse bindings + MouseCommand CmdActiveTitlebar1; + MouseCommand CmdActiveTitlebar2; + MouseCommand CmdActiveTitlebar3; + MouseCommand CmdInactiveTitlebar1; + MouseCommand CmdInactiveTitlebar2; + MouseCommand CmdInactiveTitlebar3; + MouseCommand CmdWindow1; + MouseCommand CmdWindow2; + MouseCommand CmdWindow3; + MouseCommand CmdAll1; + MouseCommand CmdAll2; + MouseCommand CmdAll3; }; extern Options* options; #endif diff --git a/kwin/stdclient.cpp b/kwin/stdclient.cpp index 4963b9be5d..fdda750ec5 100644 --- a/kwin/stdclient.cpp +++ b/kwin/stdclient.cpp @@ -1,393 +1,393 @@ #include "stdclient.h" #include #include #include #include #include #include #include #include #include "workspace.h" #include "options.h" #include "stdclient_bitmaps.h" static QPixmap* close_pix = 0; static QPixmap* maximize_pix = 0; static QPixmap* minimize_pix = 0; static QPixmap* normalize_pix = 0; static QPixmap* pinup_pix = 0; static QPixmap* pindown_pix = 0; static QPixmap* menu_pix = 0; static QPixmap* dis_close_pix = 0; static QPixmap* dis_maximize_pix = 0; static QPixmap* dis_minimize_pix = 0; static QPixmap* dis_normalize_pix = 0; static QPixmap* dis_pinup_pix = 0; static QPixmap* dis_pindown_pix = 0; static QPixmap* dis_menu_pix = 0; static bool pixmaps_created = FALSE; static void create_pixmaps(); QPixmap* kwin_get_menu_pix_hack() { create_pixmaps(); return menu_pix; } /** * Pixmap creation routine that creates full pixmaps out of bitmaps * for each shade and the user defined titlebutton foreground colors. There * is a large amount of QBitmap constructors/copies here since loadFromData * with type XBM doesn't seem to work with QBitmaps, the only way I could get * a load from data is via the constructor :( Matthias, do you know about * this? */ static void create_pixmaps() { if ( pixmaps_created ) return; pixmaps_created = true; QPainter pact, pdis; QBitmap bitmap; QColor actHigh = options->color(Options::ButtonFg, true).light(150); QColor actMed = options->color(Options::ButtonFg, true); QColor actLow = options->color(Options::ButtonFg, true).dark(120); QColor disHigh = options->color(Options::ButtonFg, false).light(150); QColor disMed = options->color(Options::ButtonFg, false); QColor disLow = options->color(Options::ButtonFg, false).dark(120); close_pix = new QPixmap(16, 16); dis_close_pix = new QPixmap(16, 16); pact.begin(close_pix); pdis.begin(dis_close_pix); bitmap = QBitmap(16, 16, close_white_bits, true); bitmap.setMask(bitmap); pact.setPen(actHigh); pdis.setPen(disHigh); pact.drawPixmap(0, 0, bitmap); pdis.drawPixmap(0, 0, bitmap); bitmap = QBitmap(16, 16, close_dgray_bits, true); pact.setPen(actLow); pdis.setPen(disLow); pact.drawPixmap(0, 0, bitmap); pdis.drawPixmap(0, 0, bitmap); pact.end(); pdis.end(); bitmap = QBitmap(16, 16, close_mask_bits, true); close_pix->setMask(bitmap); dis_close_pix->setMask(bitmap); minimize_pix = new QPixmap(16, 16); dis_minimize_pix = new QPixmap(16, 16); pact.begin(minimize_pix); pdis.begin(dis_minimize_pix); bitmap = QBitmap(16, 16, iconify_white_bits, true); bitmap.setMask(bitmap); pact.setPen(actHigh); pdis.setPen(disHigh); pact.drawPixmap(0, 0, bitmap); pdis.drawPixmap(0, 0, bitmap); bitmap = QBitmap(16, 16, iconify_dgray_bits, true); pact.setPen(actLow); pdis.setPen(disLow); pact.drawPixmap(0, 0, bitmap); pdis.drawPixmap(0, 0, bitmap); pact.end(); pdis.end(); bitmap = QBitmap(16, 16, iconify_mask_bits, true); minimize_pix->setMask(bitmap); dis_minimize_pix->setMask(bitmap); maximize_pix = new QPixmap(16, 16); dis_maximize_pix = new QPixmap(16, 16); pact.begin(maximize_pix); pdis.begin(dis_maximize_pix); bitmap = QBitmap(16, 16, maximize_white_bits, true); bitmap.setMask(bitmap); pact.setPen(actHigh); pdis.setPen(disHigh); pact.drawPixmap(0, 0, bitmap); pdis.drawPixmap(0, 0, bitmap); bitmap = QBitmap(16, 16, maximize_dgray_bits, true); pact.setPen(actLow); pdis.setPen(disLow); pact.drawPixmap(0, 0, bitmap); pdis.drawPixmap(0, 0, bitmap); pact.end(); pdis.end(); bitmap = QBitmap(16, 16, maximize_mask_bits, true); maximize_pix->setMask(bitmap); dis_maximize_pix->setMask(bitmap); normalize_pix = new QPixmap(16, 16); dis_normalize_pix = new QPixmap(16, 16); pact.begin(normalize_pix); pdis.begin(dis_normalize_pix); bitmap = QBitmap(16, 16, maximizedown_white_bits, true); bitmap.setMask(bitmap); pact.setPen(actHigh); pdis.setPen(disHigh); pact.drawPixmap(0, 0, bitmap); pdis.drawPixmap(0, 0, bitmap); bitmap = QBitmap(16, 16, maximizedown_dgray_bits, true); pact.setPen(actLow); pdis.setPen(disLow); pact.drawPixmap(0, 0, bitmap); pdis.drawPixmap(0, 0, bitmap); pact.end(); pdis.end(); bitmap = QBitmap(16, 16, maximizedown_mask_bits, true); normalize_pix->setMask(bitmap); dis_normalize_pix->setMask(bitmap); menu_pix = new QPixmap(16, 16); dis_menu_pix = new QPixmap(16, 16); pact.begin(menu_pix); pdis.begin(dis_menu_pix); bitmap = QBitmap(16, 16, menu_white_bits, true); bitmap.setMask(bitmap); pact.setPen(actHigh); pdis.setPen(disHigh); pact.drawPixmap(0, 0, bitmap); pdis.drawPixmap(0, 0, bitmap); bitmap = QBitmap(16, 16, menu_dgray_bits, true); pact.setPen(actLow); pdis.setPen(disLow); pact.drawPixmap(0, 0, bitmap); pdis.drawPixmap(0, 0, bitmap); pact.end(); pdis.end(); bitmap = QBitmap(16, 16, menu_mask_bits, true); menu_pix->setMask(bitmap); dis_menu_pix->setMask(bitmap); pinup_pix = new QPixmap(16, 16); dis_pinup_pix = new QPixmap(16, 16); pact.begin(pinup_pix); pdis.begin(dis_pinup_pix); bitmap = QBitmap(16, 16, pinup_white_bits, true); bitmap.setMask(bitmap); pact.setPen(actHigh); pdis.setPen(disHigh); pact.drawPixmap(0, 0, bitmap); pdis.drawPixmap(0, 0, bitmap); bitmap = QBitmap(16, 16, pinup_gray_bits, true); pact.setPen(actMed); pdis.setPen(disMed); pact.drawPixmap(0, 0, bitmap); pdis.drawPixmap(0, 0, bitmap); bitmap = QBitmap(16, 16, pinup_dgray_bits, true); bitmap.setMask(bitmap); pact.setPen(actLow); pdis.setPen(disLow); pact.drawPixmap(0, 0, bitmap); pdis.drawPixmap(0, 0, bitmap); pact.end(); pdis.end(); bitmap = QBitmap(16, 16, pinup_mask_bits, true); pinup_pix->setMask(bitmap); dis_pinup_pix->setMask(bitmap); pindown_pix = new QPixmap(16, 16); dis_pindown_pix = new QPixmap(16, 16); pact.begin(pindown_pix); pdis.begin(dis_pindown_pix); bitmap = QBitmap(16, 16, pindown_white_bits, true); bitmap.setMask(bitmap); pact.setPen(actHigh); pdis.setPen(disHigh); pact.drawPixmap(0, 0, bitmap); pdis.drawPixmap(0, 0, bitmap); bitmap = QBitmap(16, 16, pindown_gray_bits, true); pact.setPen(actMed); pdis.setPen(disMed); pact.drawPixmap(0, 0, bitmap); pdis.drawPixmap(0, 0, bitmap); bitmap = QBitmap(16, 16, pindown_dgray_bits, true); bitmap.setMask(bitmap); pact.setPen(actLow); pdis.setPen(disLow); pact.drawPixmap(0, 0, bitmap); pdis.drawPixmap(0, 0, bitmap); pact.end(); pdis.end(); bitmap = QBitmap(16, 16, pindown_mask_bits, true); pindown_pix->setMask(bitmap); dis_pindown_pix->setMask(bitmap); } StdClient::StdClient( Workspace *ws, WId w, QWidget *parent, const char *name ) : Client( ws, w, parent, name, WResizeNoErase ) { create_pixmaps(); QGridLayout* g = new QGridLayout( this, 0, 0, 2 ); g->setRowStretch( 1, 10 ); g->addWidget( windowWrapper(), 1, 1 ); g->addItem( new QSpacerItem( 0, 0, QSizePolicy::Fixed, QSizePolicy::Expanding ) ); g->addColSpacing(0, 2); g->addColSpacing(2, 2); g->addRowSpacing(2, 2); button[0] = new QToolButton( this ); button[1] = new QToolButton( this ); button[2] = new QToolButton( this ); button[3] = new QToolButton( this ); button[4] = new ThreeButtonButton( this ); button[5] = new QToolButton( this ); QHBoxLayout* hb = new QHBoxLayout; g->addLayout( hb, 0, 1 ); hb->addWidget( button[0] ); hb->addWidget( button[1] ); hb->addWidget( button[2] ); int fh = fontMetrics().lineSpacing(); titlebar = new QSpacerItem(10, fh, QSizePolicy::Expanding, QSizePolicy::Minimum ); hb->addItem( titlebar ); hb->addWidget( button[3] ); hb->addWidget( button[4] ); hb->addWidget( button[5] ); for ( int i = 0; i < 6; i++) { button[i]->setBackgroundMode( PaletteBackground ); button[i]->setMouseTracking( TRUE ); button[i]->setFixedSize( 20, 20 ); } if(miniIcon().isNull()) button[0]->setIconSet(isActive() ? *menu_pix : *dis_menu_pix); else button[0]->setIconSet( miniIcon() ); connect( button[0], SIGNAL( pressed() ), this, SLOT( menuButtonPressed() ) ); button[0]->setPopupDelay( 0 ); button[0]->setPopup( workspace()->clientPopup( this ) ); button[1]->setIconSet(isSticky() ? isActive() ? *pindown_pix : *dis_pindown_pix : isActive() ? *pinup_pix : *dis_pinup_pix ); connect( button[1], SIGNAL( clicked() ), this, ( SLOT( toggleSticky() ) ) ); button[1]->hide(); // no sticky for now button[2]->hide(); button[3]->setIconSet(isActive() ? *minimize_pix : *dis_minimize_pix); connect( button[3], SIGNAL( clicked() ), this, ( SLOT( iconify() ) ) ); button[4]->setIconSet(isActive() ? *maximize_pix : *dis_maximize_pix); connect( button[4], SIGNAL( clicked(int) ), this, ( SLOT( maxButtonClicked(int) ) ) ); button[5]->setIconSet(isActive() ? *close_pix : *dis_close_pix); connect( button[5], SIGNAL( clicked() ), this, ( SLOT( closeWindow() ) ) ); if ( isTransient() ) { // lighter decoration for transient windows button[1]->hide(); button[2]->hide(); button[3]->hide(); button[4]->hide(); } } void StdClient::activeChange(bool on) { if(miniIcon().isNull()) button[0]->setIconSet(on ? *menu_pix : *dis_menu_pix); button[1]->setIconSet(isSticky() ? on ? *pindown_pix : *dis_pindown_pix : on ? *pinup_pix : *dis_pinup_pix ); button[3]->setIconSet(on ? *minimize_pix : *dis_minimize_pix); button[4]->setIconSet(on ? *maximize_pix : *dis_maximize_pix); button[5]->setIconSet(on ? *close_pix : *dis_close_pix); Client::activeChange(on); } StdClient::~StdClient() { } void StdClient::resizeEvent( QResizeEvent* e) { Client::resizeEvent( e ); QRegion rr = rect(); QRect t = titlebar->geometry(); t.setTop( 0 ); QRegion r = rr.subtract( QRect( t.x()+1, 0, t.width()-2, 1 ) ); setMask( r ); if ( isVisibleToTLW() && !testWFlags( WNorthWestGravity )) { // manual clearing without the titlebar (we selected WResizeNoErase ) QPainter p( this ); r = rr.subtract( t ); p.setClipRegion( r ); p.fillRect( rect(), colorGroup().brush( QColorGroup::Background ) ); } } /*!\reimp */ void StdClient::captionChange( const QString& ) { repaint( titlebar->geometry(), FALSE ); } /*!\reimp */ void StdClient::maximizeChange( bool m ) { button[4]->setIconSet( m?*normalize_pix:*maximize_pix ); } /*!\reimp */ void StdClient::stickyChange( bool s) { button[1]->setIconSet( s?*pindown_pix:*pinup_pix ); } void StdClient::paintEvent( QPaintEvent* ) { QPainter p( this ); QRect t = titlebar->geometry(); QRegion r = rect(); r = r.subtract( t ); p.setClipRegion( r ); qDrawWinPanel( &p, rect(), colorGroup() ); t.setTop( 1 ); p.setClipRegion( t ); t.setTop( 0 ); p.fillRect( t, options->color(Options::TitleBar, isActive())); p.setPen( options->color(Options::TitleBar, isActive()).light() ); p.drawLine(t.left(), t.top()+1, t.right(), t.top()+1); qDrawShadePanel( &p, t.x(), t.y(), t.width(), t.height(), colorGroup(), true, 1 ); t.setLeft( t.left() + 4 ); t.setRight( t.right() - 2 ); p.setPen(options->color(Options::Font, isActive())); p.setFont(options->font(isActive())); p.drawText( t, AlignLeft|AlignVCenter, caption() ); } void StdClient::mouseDoubleClickEvent( QMouseEvent * e ) { if ( titlebar->geometry().contains( e->pos() ) ) - setShade( !isShade() ); + workspace()->performWindowOperation( this, options->operationTitlebarDblClick() ); workspace()->requestFocus( this ); } void StdClient::init() { button[0]->setIconSet( miniIcon() ); // ### TODO transient etc. } void StdClient::iconChange() { if(miniIcon().isNull()) button[0]->setIconSet(isActive() ? *menu_pix : *dis_menu_pix); else button[0]->setIconSet( miniIcon() ); button[0]->repaint( FALSE ); } /*! Indicates that the menu button has been clicked */ void StdClient::menuButtonPressed() { (void ) workspace()->clientPopup( this ); //trigger the popup menu } void StdClient::maxButtonClicked( int button ) { switch ( button ){ case MidButton: maximize( MaximizeVertical ); break; case RightButton: maximize( MaximizeHorizontal ); break; default: //LeftButton: maximize( MaximizeFull ); break; } } diff --git a/kwin/tabbox.cpp b/kwin/tabbox.cpp index 180d095015..a060786c2c 100644 --- a/kwin/tabbox.cpp +++ b/kwin/tabbox.cpp @@ -1,253 +1,255 @@ #include "tabbox.h" #include "workspace.h" #include "client.h" #include #include #include const bool options_traverse_all = FALSE; // TODO TabBox::TabBox( Workspace *ws, const char *name ) : QWidget( 0, name, WStyle_Customize | WStyle_NoBorder ) { wspace = ws; reset(); } TabBox::~TabBox() { } /*! Sets the current mode to \a mode, either DesktopMode or WindowsMode \sa mode() */ void TabBox::setMode( Mode mode ) { m = mode; } /*! Resets the tab box to display the active client in WindowsMode, or the current desktop in DesktopMode */ void TabBox::reset() { QFont f = font(); f.setBold( TRUE ); f.setPointSize( 14 ); setFont( f ); wmax = 0; if ( mode() == WindowsMode ) { client = workspace()->activeClient(); clients.clear(); Client* c = workspace()->nextClient( client ); Client* stop = c; QFontMetrics fm( fontMetrics() ); int cw = 0; while ( c ) { if ( (options_traverse_all ||c->isOnDesktop(workspace()->currentDesktop())) && (!c->isIconified() || c->mainClient() == c ) ) { if ( client == c ) clients.prepend( c ); else clients += c; cw = fm.width( c->caption() ) + 40; if ( cw > wmax ) wmax = cw; } c = workspace()->nextClient( c ); if ( c == stop ) break; } wmax = QMAX( wmax, int(clients.count())*20 ); } else { // DesktopMode desk = workspace()->currentDesktop(); } int w = QMAX( wmax + 20, qApp->desktop()->width()/3 ); setGeometry( (qApp->desktop()->width()-w)/2, qApp->desktop()->height()/2-fontMetrics().height()*2-10, w, fontMetrics().height()*4 + 20 ); wmax = QMIN( wmax, width() - 12 ); } /*! Shows the next or previous item, depending on \a next */ void TabBox::nextPrev( bool next) { if ( mode() == WindowsMode ) { Client* sign = client; do { if (client != sign && !sign) sign = client; if ( next ) client = workspace()->nextClient(client); else client = workspace()->previousClient(client); } while (client != sign && client && ( !options_traverse_all && !client->isOnDesktop(workspace()->currentDesktop()) ) || ( client->isIconified() && client->mainClient() != client ) ); if (!options_traverse_all && client && !client->isOnDesktop(workspace()->currentDesktop())) client = 0; } else { // DesktopMode if ( next ) { desk++; if ( desk > workspace()->numberOfDesktops() ) desk = 1; } else { desk--; if ( desk < 1 ) desk = workspace()->numberOfDesktops(); } } paintContents(); } /*! Returns the currently displayed client ( only works in WindowsMode ). Returns 0 if no client is displayed. */ Client* TabBox::currentClient() { if ( mode() != WindowsMode ) return 0; return client; } /*! Returns the currently displayed virtual desktop ( only works in DesktopMode ) Returns -1 if no desktop is displayed. */ int TabBox::currentDesktop() { if ( mode() != DesktopMode ) return -1; return desk; } /*! Reimplemented to raise the tab box as well */ void TabBox::showEvent( QShowEvent* ) { raise(); } /*! hide the icon box if necessary */ void TabBox::hideEvent( QHideEvent* ) { } /*! Paints the tab box */ void TabBox::paintEvent( QPaintEvent* ) { { QPainter p( this ); style().drawPanel( &p, 0, 0, width(), height(), colorGroup(), FALSE ); style().drawPanel( &p, 4, 4, width()-8, height()-8, colorGroup(), TRUE ); } paintContents(); } /*! Paints the contents of the tab box. Used in paintEvent() and whenever the contents changes. */ void TabBox::paintContents() { extern QPixmap* kwin_get_menu_pix_hack(); QPixmap* menu_pix = kwin_get_menu_pix_hack(); QPainter p( this ); QRect r( 6, 6, width()-12, height()-32 ); p.fillRect( r, colorGroup().brush( QColorGroup::Background ) ); if ( mode () == WindowsMode ) { if ( currentClient() ) { QString s; if (!client->isOnDesktop(workspace()->currentDesktop())){ s = KWM::desktopName(client->desktop()); s.append(": "); } if (client->isIconified()) s += QString("(")+client->caption()+")"; else s += client->caption(); int textw = fontMetrics().width( s ); r.setLeft( r.left() + (r.width() - textw)/2); if ( !client->icon().isNull() ) { int py = r.center().y() - 16; r.setLeft( r.left() + 20 ); p.drawPixmap( r.left()-42, py, client->icon() ); } p.drawText( r, AlignVCenter, s ); } else { r.setBottom( r.bottom() + 20 ); p.drawText( r, AlignCenter, "*** No Tasks ***" ); } int x = (width() - clients.count() * 20 )/2; int y = height() - 26; for ( ClientList::ConstIterator it = clients.begin(); it != clients.end(); ++it) { if ( workspace()->hasClient( *it ) ) { // safety + p.save(); if ( !(*it)->miniIcon().isNull() ) p.drawPixmap( x, y, (*it)->miniIcon() ); else if ( menu_pix ) p.drawPixmap( x, y, *menu_pix ); p.setPen( (*it)==currentClient()? colorGroup().highlight():colorGroup().background() ); p.drawRect( x-2, y-2, 20, 20 ); + p.setPen( colorGroup().foreground() ); x += 20; } } } else { // DesktopMode p.drawText( r, AlignCenter, QString::number( desk ) ); int x = (width() - workspace()->numberOfDesktops() * 20 )/2; int y = height() - 26; QFont f( font() ); f.setPointSize( 12 ); f.setBold( FALSE ); p.setFont(f ); for ( int i = 1; i <= workspace()->numberOfDesktops(); i++ ) { p.setPen( i == desk? colorGroup().highlight():colorGroup().background() ); p.drawRect( x-2, y-2, 20, 20 ); qDrawWinPanel( &p, QRect( x, y, 16, 16), colorGroup(), FALSE, &colorGroup().brush(QColorGroup::Base ) ); p.setPen( colorGroup().text() ); p.drawText( x, y, 16, 16, AlignCenter, QString::number(i) ); x += 20; } } } diff --git a/kwin/workspace.cpp b/kwin/workspace.cpp index dae383af7a..13efeff3a3 100644 --- a/kwin/workspace.cpp +++ b/kwin/workspace.cpp @@ -1,1687 +1,1695 @@ #include #include #include #include #include #include "workspace.h" #include "client.h" #include "stdclient.h" #include "beclient.h" #include "systemclient.h" #include "tabbox.h" #include "atoms.h" #include #include #include #include #include #include #include extern Time kwin_time; // used to store the return values of // XShapeQueryExtension. // Necessary since shaped window are an extension to X static int kwin_has_shape = 0; static int kwin_shape_event = 0; // does the window w need a shape combine mask around it? bool Shape::hasShape( WId w){ int xws, yws, xbs, ybs; unsigned wws, hws, wbs, hbs; int boundingShaped, clipShaped; if (!kwin_has_shape) return FALSE; XShapeQueryExtents(qt_xdisplay(), w, &boundingShaped, &xws, &yws, &wws, &hws, &clipShaped, &xbs, &ybs, &wbs, &hbs); return boundingShaped != 0; } int Shape::shapeEvent() { return kwin_shape_event; } static Client* clientFactory( Workspace *ws, WId w ) { // hack TODO hints char* name = 0; QString s; if ( XFetchName( qt_xdisplay(), (Window) w, &name ) && name ) { s = QString::fromLatin1( name ); XFree( name ); } if ( s == "THE DESKTOP" ) { XLowerWindow( qt_xdisplay(), w ); Client * c = new NoBorderClient( ws, w); c->setSticky( TRUE ); ws->setDesktopClient( c ); return c; } if ( s == "Kicker" ) { Client * c = new NoBorderClient( ws, w); c->setSticky( TRUE ); return c; } if ( Shape::hasShape( w ) ){ return new NoBorderClient( ws, w ); } KConfig *config = KGlobal::config(); - config->setGroup("style"); + config->setGroup("Style"); // well, it will be soon ;-) QString tmpStr = config->readEntry("Plugin", "standard"); if(tmpStr == "system") return new SystemClient( ws, w ); else if(tmpStr == "be") return new BeClient( ws, w ); else return new StdClient( ws, w ); } Workspace::Workspace() { root = qt_xrootwin(); // no MDI for now (void) QApplication::desktop(); // trigger creation of desktop widget desktop_widget = new QWidget(0, "desktop_widget", Qt::WType_Desktop | Qt::WPaintUnclipped ); // select windowmanager privileges XSelectInput(qt_xdisplay(), root, KeyPressMask | PropertyChangeMask | ColormapChangeMask | SubstructureRedirectMask | SubstructureNotifyMask ); int dummy; kwin_has_shape = XShapeQueryExtension(qt_xdisplay(), &kwin_shape_event, &dummy); // compatibility long data = 1; XChangeProperty(qt_xdisplay(), qt_xrootwin(), atoms->kwm_running, atoms->kwm_running, 32, PropModeAppend, (unsigned char*) &data, 1); keys = 0; grabKey(XK_Tab, Mod1Mask); grabKey(XK_Tab, Mod1Mask | ShiftMask); grabKey(XK_Tab, ControlMask); grabKey(XK_Tab, ControlMask | ShiftMask); createKeybindings(); init(); control_grab = FALSE; tab_grab = FALSE; tab_box = new TabBox( this ); } Workspace::Workspace( WId rootwin ) { qDebug("create MDI workspace for %d", rootwin ); root = rootwin; // select windowmanager privileges XSelectInput(qt_xdisplay(), root, KeyPressMask | PropertyChangeMask | ColormapChangeMask | SubstructureRedirectMask | SubstructureNotifyMask ); control_grab = FALSE; tab_grab = FALSE; tab_box = 0; keys = 0; init(); } void Workspace::init() { tab_box = 0; active_client = 0; should_get_focus = 0; desktop_client = 0; current_desktop = 0; number_of_desktops = 0; popup = 0; desk_popup = 0; popup_client = 0; setNumberOfDesktops( 4 ); // TODO options setCurrentDesktop( 1 ); unsigned int i, nwins; Window dw1, dw2, *wins; XWindowAttributes attr; XGrabServer( qt_xdisplay() ); XQueryTree(qt_xdisplay(), root, &dw1, &dw2, &wins, &nwins); for (i = 0; i < nwins; i++) { XGetWindowAttributes(qt_xdisplay(), wins[i], &attr); if (attr.override_redirect ) continue; if (attr.map_state != IsUnmapped) { if ( addDockwin( wins[i] ) ) continue; Client* c = clientFactory( this, wins[i] ); if ( c != desktop_client ) { clients.append( c ); stacking_order.append( c ); } if ( c->wantsTabFocus() ) focus_chain.append( c ); c->manage( TRUE ); if ( c == desktop_client ) setDesktopClient( c ); if ( root != qt_xrootwin() ) { // TODO may use QWidget:.create qDebug(" create a mdi client"); XReparentWindow( qt_xdisplay(), c->winId(), root, 0, 0 ); c->move(0,0); } } } XFree((void *) wins); XUngrabServer( qt_xdisplay() ); propagateClients(); //CT initialize the cascading info for( int i = 0; i < numberOfDesktops(); i++) { CascadingInfo inf; inf.pos = QPoint(0,0); inf.col = 0; inf.row = 0; cci.append(inf); } } Workspace::~Workspace() { if ( desktop_client ) { WId win = desktop_client->window(); delete desktop_client; XMapWindow( qt_xdisplay(), win ); XLowerWindow( qt_xdisplay(), win ); } for ( ClientList::ConstIterator it = clients.begin(); it != clients.end(); ++it) { WId win = (*it)->window(); delete (*it); XMapWindow( qt_xdisplay(), win ); } delete tab_box; delete popup; delete keys; if ( root == qt_xrootwin() ) XDeleteProperty(qt_xdisplay(), qt_xrootwin(), atoms->kwm_running); } /*! Handles workspace specific XEvents */ bool Workspace::workspaceEvent( XEvent * e ) { Client * c = findClient( e->xany.window ); if ( c ) return c->windowEvent( e ); switch (e->type) { case ButtonPress: case ButtonRelease: break; case UnmapNotify: // this is special due to // SubstructureNotifyMask. e->xany.window is the window the // event is reported to. Take care not to confuse Qt. c = findClient( e->xunmap.window ); if ( c ) return c->windowEvent( e ); // check for dock windows if ( removeDockwin( e->xunmap.window ) ) return TRUE; if ( e->xunmap.event == root ) { // keep track of map/unmap for own own windows to avoid // race conditions c = findClientWidthId( e->xunmap.window ); if ( c ) return c->windowEvent( e ); } if ( e->xunmap.event != e->xunmap.window ) // hide wm typical event from Qt return TRUE; case MapNotify: if ( e->xunmap.event == root ) { // keep track of map/unmap for own own windows to avoid // race conditions c = findClientWidthId( e->xmap.window ); if ( c ) return c->windowEvent( e ); } case ReparentNotify: c = findClient( e->xreparent.window ); if ( c ) (void) c->windowEvent( e ); //do not confuse Qt with these events. After all, _we_ are the //window manager who does the reparenting. return TRUE; case DestroyNotify: if ( removeDockwin( e->xdestroywindow.window ) ) return TRUE; return destroyClient( findClient( e->xdestroywindow.window ) ); case MapRequest: c = findClient( e->xmaprequest.window ); if ( !c ) { if ( e->xmaprequest.parent == root ) { if ( addDockwin( e->xmaprequest.window ) ) return TRUE; c = clientFactory( this, e->xmaprequest.window ); if ( root != qt_xrootwin() ) { // TODO may use QWidget:.create XReparentWindow( qt_xdisplay(), c->winId(), root, 0, 0 ); } if ( c != desktop_client ) { if ( c->wantsTabFocus() ) focus_chain.prepend( c ); clients.append( c ); stacking_order.append( c ); } propagateClients(); } } if ( c ) { bool result = c->windowEvent( e ); if ( c == desktop_client ) setDesktopClient( c ); return result; } break; case ConfigureRequest: c = findClient( e->xconfigurerequest.window ); if ( c ) return c->windowEvent( e ); else if ( e->xconfigurerequest.parent == root ) { XWindowChanges wc; unsigned int value_mask = 0; wc.border_width = 0; wc.x = e->xconfigurerequest.x; wc.y = e->xconfigurerequest.y; wc.width = e->xconfigurerequest.width; wc.height = e->xconfigurerequest.height; wc.sibling = None; wc.stack_mode = Above; value_mask = e->xconfigurerequest.value_mask | CWBorderWidth; XConfigureWindow( qt_xdisplay(), e->xconfigurerequest.window, value_mask, & wc ); return TRUE; } break; case KeyPress: return keyPress(e->xkey); break; case KeyRelease: return keyRelease(e->xkey); break; case FocusIn: break; case FocusOut: break; case ClientMessage: return clientMessage(e->xclient); break; default: if ( e->type == Shape::shapeEvent() ) { c = findClient( ((XShapeEvent *)e)->window ); if ( c ) c->updateShape(); } break; } return FALSE; } bool Workspace::hasClient(Client* c) { for ( ClientList::ConstIterator it = clients.begin(); it != clients.end(); ++it) { if ( (*it) == c ) return TRUE; } return FALSE; } /*! Finds the client that embedds the window \a w */ Client* Workspace::findClient( WId w ) const { for ( ClientList::ConstIterator it = clients.begin(); it != clients.end(); ++it) { if ( (*it)->window() == w ) return *it; } if ( desktop_client && w == desktop_client->window() ) return desktop_client; return 0; } /*! Finds the client with window id \a w */ Client* Workspace::findClientWidthId( WId w ) const { for ( ClientList::ConstIterator it = clients.begin(); it != clients.end(); ++it) { if ( (*it)->winId() == w ) return *it; } return 0; } /*! Returns the workspace's geometry \sa clientArea() */ QRect Workspace::geometry() const { if ( root == qt_xrootwin() ) return QRect( QPoint(0, 0), QApplication::desktop()->size() ); else { // todo caching, keep track of configure notify etc. QRect r; XWindowAttributes attr; if (XGetWindowAttributes(qt_xdisplay(), root, &attr)){ r.setRect(0, 0, attr.width, attr.height ); } return r; } } /*! Returns the workspace's client area. This is the area within the geometry() where clients can be placed, i.e. the full geometry minus space for desktop panels, taskbars, etc. Placement algorithms should refer to clientArea. \sa geometry() */ QRect Workspace::clientArea() const { return geometry(); // for now } /* Destroys the client \a c */ bool Workspace::destroyClient( Client* c) { if ( !c ) return FALSE; clients.remove( c ); stacking_order.remove( c ); focus_chain.remove( c ); c->invalidateWindow(); delete c; clientHidden( c ); if ( c == desktop_client ) desktop_client = 0; propagateClients(); return TRUE; } /*! Auxiliary function to release a passive keyboard grab */ void Workspace::freeKeyboard(bool pass){ if (!pass) XAllowEvents(qt_xdisplay(), AsyncKeyboard, kwin_time); else XAllowEvents(qt_xdisplay(), ReplayKeyboard, kwin_time); QApplication::syncX(); } /*! Handles alt-tab / control-tab */ bool Workspace::keyPress(XKeyEvent key) { if ( root != qt_xrootwin() ) return FALSE; int kc = XKeycodeToKeysym(qt_xdisplay(), key.keycode, 0); int km = key.state & (ControlMask | Mod1Mask | ShiftMask); const bool options_alt_tab_mode_is_CDE_style = FALSE; // TODO if (!control_grab){ if( (kc == XK_Tab) && ( km == (Mod1Mask | ShiftMask) || km == (Mod1Mask) )){ if (!tab_grab){ if (options_alt_tab_mode_is_CDE_style ){ // CDE style raise / lower Client* c = topClientOnDesktop(); Client* nc = c; if (km & ShiftMask){ do { nc = previousStaticClient(nc); } while (nc && nc != c && (!nc->isOnDesktop(currentDesktop()) || nc->isIconified())); } else do { nc = nextStaticClient(nc); } while (nc && nc != c && (!nc->isOnDesktop(currentDesktop()) || nc->isIconified())); if (c && c != nc) ;//TODO lowerClient(c); if (nc) activateClient( nc ); freeKeyboard(FALSE); return TRUE; } XGrabKeyboard(qt_xdisplay(), root, FALSE, GrabModeAsync, GrabModeAsync, kwin_time); tab_grab = TRUE; tab_box->setMode( TabBox::WindowsMode ); tab_box->reset(); } tab_box->nextPrev( (km & ShiftMask) == 0 ); tab_box->show(); } } if (!tab_grab){ if( (kc == XK_Tab) && ( km == (ControlMask | ShiftMask) || km == (ControlMask) )){ //TODO if (!options.ControlTab){ // freeKeyboard(TRUE); // return TRUE; // } if (!control_grab){ XGrabKeyboard(qt_xdisplay(), root, FALSE, GrabModeAsync, GrabModeAsync, kwin_time); control_grab = TRUE; tab_box->setMode( TabBox::DesktopMode ); tab_box->reset(); } tab_box->nextPrev( (km & ShiftMask) == 0 ); tab_box->show(); } } if (control_grab || tab_grab){ if (kc == XK_Escape){ XUngrabKeyboard(qt_xdisplay(), kwin_time); tab_box->hide(); tab_grab = FALSE; control_grab = FALSE; return TRUE; } return FALSE; } freeKeyboard(FALSE); return FALSE; } /*! Handles alt-tab / control-tab */ bool Workspace::keyRelease(XKeyEvent key) { if ( root != qt_xrootwin() ) return FALSE; int i; if (tab_grab){ XModifierKeymap* xmk = XGetModifierMapping(qt_xdisplay()); for (i=0; imax_keypermod; i++) if (xmk->modifiermap[xmk->max_keypermod * Mod1MapIndex + i] == key.keycode){ XUngrabKeyboard(qt_xdisplay(), kwin_time); tab_box->hide(); tab_grab = false; if ( tab_box->currentClient() ){ activateClient( tab_box->currentClient() ); } } } if (control_grab){ XModifierKeymap* xmk = XGetModifierMapping(qt_xdisplay()); for (i=0; imax_keypermod; i++) if (xmk->modifiermap[xmk->max_keypermod * ControlMapIndex + i] == key.keycode){ XUngrabKeyboard(qt_xdisplay(), kwin_time); tab_box->hide(); control_grab = False; if ( tab_box->currentDesktop() != -1 ) setCurrentDesktop( tab_box->currentDesktop() ); } } return FALSE; } /*! auxiliary functions to travers all clients according the focus order. Useful for kwm´s Alt-tab feature. */ Client* Workspace::nextClient( Client* c ) const { if ( focus_chain.isEmpty() ) return 0; ClientList::ConstIterator it = focus_chain.find( c ); if ( it == focus_chain.end() ) return focus_chain.last(); if ( it == focus_chain.begin() ) return focus_chain.last(); --it; return *it; } /*! auxiliary functions to travers all clients according the focus order. Useful for kwm´s Alt-tab feature. */ Client* Workspace::previousClient( Client* c ) const { if ( focus_chain.isEmpty() ) return 0; ClientList::ConstIterator it = focus_chain.find( c ); if ( it == focus_chain.end() ) return focus_chain.first(); ++it; if ( it == focus_chain.end() ) return focus_chain.first(); return *it; } /*! auxiliary functions to travers all clients according the static order. Useful for the CDE-style Alt-tab feature. */ Client* Workspace::nextStaticClient( Client* c ) const { if ( clients.isEmpty() ) return 0; ClientList::ConstIterator it = clients.find( c ); if ( it == clients.end() ) return clients.first(); ++it; if ( it == clients.end() ) return clients.first(); return *it; } /*! auxiliary functions to travers all clients according the static order. Useful for the CDE-style Alt-tab feature. */ Client* Workspace::previousStaticClient( Client* c ) const { if ( clients.isEmpty() ) return 0; ClientList::ConstIterator it = clients.find( c ); if ( it == clients.end() ) return clients.last(); if ( it == clients.begin() ) return clients.last(); --it; return *it; } /*! Returns topmost visible client within the specified layer range on the current desktop, or 0 if no clients are visible. \a fromLayer has to be smaller than \a toLayer. */ Client* Workspace::topClientOnDesktop( int fromLayer, int toLayer) const { fromLayer = toLayer = 0; return 0; } /* Grabs the keysymbol \a keysym with the given modifiers \a mod plus all possibile combinations of Lock and NumLock */ void Workspace::grabKey(KeySym keysym, unsigned int mod){ static int NumLockMask = 0; if (!keysym||!XKeysymToKeycode(qt_xdisplay(), keysym)) return; if (!NumLockMask){ XModifierKeymap* xmk = XGetModifierMapping(qt_xdisplay()); int i; for (i=0; i<8; i++){ if (xmk->modifiermap[xmk->max_keypermod * i] == XKeysymToKeycode(qt_xdisplay(), XK_Num_Lock)) NumLockMask = (1<setActive( FALSE ); active_client = c; if ( active_client ) { focus_chain.remove( c ); if ( c->wantsTabFocus() ) focus_chain.append( c ); } WId w = active_client? active_client->window() : 0; XChangeProperty(qt_xdisplay(), qt_xrootwin(), atoms->net_active_window, XA_WINDOW, 32, PropModeReplace, (unsigned char *)&w, 1); } /*! Tries to activate the client \a c. This function performs what you expect when clicking the respective entry in a taskbar: showing and raising the client (this may imply switching to the another virtual desktop) and putting the focus onto it. Once X really gave focus to the client window as requested, the client itself will call setActiveClient() and the operation is complete. This may not happen with certain focus policies, though. \sa setActiveClient(), requestFocus() */ void Workspace::activateClient( Client* c) { if (!c->isOnDesktop(currentDesktop()) ) { setCurrentDesktop( c->desktop() ); } raiseClient( c ); c->show(); iconifyOrDeiconifyTransientsOf( c ); if ( options->focusPolicyIsReasonable() ) requestFocus( c ); } void Workspace::iconifyOrDeiconifyTransientsOf( Client* c ) { if ( c->isIconified() || c->isShade() ) { for ( ClientList::ConstIterator it = clients.begin(); it != clients.end(); ++it) { if ( (*it)->transientFor() == c->window() && !(*it)->isIconified() && !(*it)->isShade() ) { (*it)->setMappingState( IconicState ); (*it)->hide(); iconifyOrDeiconifyTransientsOf( (*it) ); } } } else { for ( ClientList::ConstIterator it = clients.begin(); it != clients.end(); ++it) { if ( (*it)->transientFor() == c->window() && !(*it)->isVisible() ) { (*it)->show(); iconifyOrDeiconifyTransientsOf( (*it) ); } } } } /*! Returns whether a client with the specified \a caption exists, or not. */ bool Workspace::hasCaption( const QString& caption ) { for ( ClientList::ConstIterator it = clients.begin(); it != clients.end(); ++it) { if ( (*it)->caption() == caption ) return TRUE; } return FALSE; } /*! Tries to activate the client by asking X for the input focus. This function does not perform any show, raise or desktop switching. See Workspace::activateClient() instead. \sa Workspace::activateClient() */ void Workspace::requestFocus( Client* c) { //TODO will be different for non-root clients. (subclassing?) if ( !c ) { focusToNull(); return; } + + if ( !popup || !popup->isVisible() ) + popup_client = c; if ( c->isVisible() && !c->isShade() ) { c->takeFocus(); should_get_focus = c; } else if ( c->isShade() ) { // client cannot accept focus, but at least the window should be active (window menu, et. al. ) focusToNull(); c->setActive( TRUE ); } } /*! Informs the workspace that the client \a c has been hidden. If it was the active client, the workspace activates another one. \a c may already be destroyed */ void Workspace::clientHidden( Client* c ) { if ( c == active_client || ( !active_client && c == should_get_focus ) ) { active_client = 0; should_get_focus = 0; if ( clients.contains( c ) ) { focus_chain.remove( c ); focus_chain.prepend( c ); } if ( options->focusPolicyIsReasonable() ) { for ( ClientList::ConstIterator it = focus_chain.fromLast(); it != focus_chain.begin(); --it) { if ( (*it)->isVisible() ) { requestFocus( *it ); break; } } } } } QPopupMenu* Workspace::clientPopup( Client* c ) { popup_client = c; if ( !popup ) { popup = new QPopupMenu; popup->setCheckable( TRUE ); connect( popup, SIGNAL( aboutToShow() ), this, SLOT( clientPopupAboutToShow() ) ); connect( popup, SIGNAL( activated(int) ), this, SLOT( clientPopupActivated(int) ) ); QPopupMenu* deco = new QPopupMenu( popup ); connect( deco, SIGNAL( activated(int) ), this, SLOT( setDecorationStyle(int) ) ); deco->insertItem( i18n( "KDE Classic" ), 1 ); deco->insertItem( i18n( "Be-like style" ), 2); deco->insertItem( i18n( "System style" ), 3 ); desk_popup = new QPopupMenu( popup ); desk_popup->setCheckable( TRUE ); connect( desk_popup, SIGNAL( activated(int) ), this, SLOT( sendToDesktop(int) ) ); connect( desk_popup, SIGNAL( aboutToShow() ), this, SLOT( desktopPopupAboutToShow() ) ); - popupIdMove = popup->insertItem( i18n("&Move") ); - popupIdSize = popup->insertItem( i18n("&Size") ); - popupIdMinimize = popup->insertItem( i18n("&Mi&nimize") ); - popupIdMaximize = popup->insertItem( i18n("Ma&ximize") ); -// popupIdFullscreen = popup->insertItem( i18n("&Fullscreen") ); - popupIdFullscreen = 0; - popupIdShade = popup->insertItem( i18n("Sh&ade") ); + popup->insertItem( i18n("&Move"), Options::MoveOp ); + popup->insertItem( i18n("&Size"), Options::ResizeOp ); + popup->insertItem( i18n("&Mi&nimize"), Options::IconifyOp ); + popup->insertItem( i18n("Ma&ximize"), Options::MaximizeOp ); + popup->insertItem( i18n("Sh&ade"), Options::ShadeOp ); popup->insertSeparator(); popup->insertItem(i18n("&Decoration"), deco ); popup->insertItem(i18n("&To desktop"), desk_popup ); popup->insertSeparator(); QString k = KAccel::keyToString( keys->currentKey( "Window close" ), true ); - popupIdClose = popup->insertItem(i18n("&Close")+'\t'+k ); + popup->insertItem(i18n("&Close")+'\t'+k, Options::CloseOp ); } return popup; } -void Workspace::clientPopupActivated( int id ) -{ - if ( !popup_client ) +void Workspace::performWindowOperation( Client* c, Options::WindowOperation op ) { + if ( !c ) return; - if ( id == popupIdClose ) - popup_client->closeWindow(); - else if ( id == popupIdMaximize ) - popup_client->maximize(); - else if ( id == popupIdMinimize ) - popup_client->iconify(); - else if ( id == popupIdFullscreen ) - popup_client->fullScreen(); - else if ( id == popupIdShade ) - popup_client->setShade( !popup_client->isShade() ); + + switch ( op ) { + case Options::CloseOp: + c->closeWindow(); + break; + case Options::MaximizeOp: + c->maximize(); + break; + case Options::IconifyOp: + c->iconify(); + break; + case Options::ShadeOp: + c->setShade( !c->isShade() ); + break; + default: + break; + } +} + +void Workspace::clientPopupActivated( int id ) +{ + if ( popup_client ) + performWindowOperation( popup_client, (Options::WindowOperation) id ); } /*! Places the client \a c according to the workspace's layout policy */ void Workspace::doPlacement( Client* c ) { if (options->placement == Options::Random) randomPlacement( c ); else if (options->placement == Options::Smart) smartPlacement( c ); else if (options->placement == Options::Cascade) cascadePlacement( c ); } /*! Place the client \a c according to a simply "random" placement algorithm. */ void Workspace::randomPlacement(Client* c){ const int step = 24; static int px = step; static int py = 2 * step; int tx,ty; QRect maxRect = clientArea(); if (px < maxRect.x()) px = maxRect.x(); if (py < maxRect.y()) py = maxRect.y(); px += step; py += 2*step; if (px > maxRect.width()/2) px = maxRect.x() + step; if (py > maxRect.height()/2) py = maxRect.y() + step; tx = px; ty = py; if (tx + c->width() > maxRect.right()){ tx = maxRect.right() - c->width(); if (tx < 0) tx = 0; px = maxRect.x(); } if (ty + c->height() > maxRect.bottom()){ ty = maxRect.bottom() - c->height(); if (ty < 0) ty = 0; py = maxRect.y(); } c->move( tx, ty ); } /*! Place the client \a c according to a really smart placement algorithm :-) */ void Workspace::smartPlacement(Client* c){ /* * SmartPlacement by Cristian Tibirna (tibirna@kde.org) * adapted for kwm (16-19jan98) and for kwin (16Nov1999) using (with * permission) ideas from fvwm, authored by * Anthony Martin (amartin@engr.csulb.edu). */ const int none = 0, h_wrong = -1, w_wrong = -2; // overlap types long int overlap, min_overlap; int x_optimal, y_optimal; int possible; int cxl, cxr, cyt, cyb; //temp coords int xl, xr, yt, yb; //temp coords // get the maximum allowed windows space QRect maxRect = clientArea(); int x = maxRect.left(), y = maxRect.top(); x_optimal = x; y_optimal = y; //client gabarit int ch = c->height(), cw = c->width(); bool first_pass = true; //CT lame flag. Don't like it. What else would do? //loop over possible positions do { //test if enough room in x and y directions if ( y + ch > maxRect.bottom() ) overlap = h_wrong; // this throws the algorithm to an exit else if( x + cw > maxRect.right() ) overlap = w_wrong; else { overlap = none; //initialize cxl = x; cxr = x + cw; cyt = y; cyb = y + ch; QValueList::ConstIterator l; for(l = clients.begin(); l != clients.end() ; ++l ) { if((*l)->isOnDesktop(currentDesktop()) && (*l) != desktop_client && !(*l)->isIconified() && (*l) != c ) { xl = (*l)->x(); yt = (*l)->y(); xr = xl + (*l)->height(); yb = yt + (*l)->width(); //if windows overlap, calc the overall overlapping if((cxl < xr) && (cxr > xl) && (cyt < yb) && (cyb > yt)) { xl = QMAX(cxl, xl); xr = QMIN(cxr, xr); yt = QMAX(cyt, yt); yb = QMIN(cyb, yb); overlap += (xr - xl) * (yb - yt); } } } } //CT first time we get no overlap we stop. if (overlap == none) { x_optimal = x; y_optimal = y; break; } if (first_pass) { first_pass = false; min_overlap = overlap; } //CT save the best position and the minimum overlap up to now else if ( overlap >= none && overlap < min_overlap) { min_overlap = overlap; x_optimal = x; y_optimal = y; } // really need to loop? test if there's any overlap if ( overlap > none ) { possible = maxRect.right(); if ( possible - cw > x) possible -= cw; // compare to the position of each client on the current desk QValueList::ConstIterator l; for(l = clients.begin(); l != clients.end() ; ++l) { if ( (*l)->isOnDesktop(currentDesktop()) && (*l) != desktop_client && !(*l)->isIconified() && (*l) != c ) { xl = (*l)->x(); yt = (*l)->y(); xr = xl + (*l)->height(); yb = yt + (*l)->width(); // if not enough room above or under the current tested client // determine the first non-overlapped x position if( y < yb && yt < ch + y ) { if( yb > x ) possible = possible < yb ? possible : yb; if( xl - cw > x ) possible = possible < xl - cw ? possible : xl - cw; } } x = possible; } } // ... else ==> not enough x dimension (overlap was wrong on horizontal) else if ( overlap == w_wrong ) { x = maxRect.left(); possible = maxRect.bottom(); if ( possible - ch > y ) possible -= ch; //test the position of each window on current desk QValueList::ConstIterator l; for( l = clients.begin(); l != clients.end() ; ++l ) { if( (*l)->isOnDesktop( currentDesktop() ) && (*l) != desktop_client && (*l) != c && !c->isIconified() ) { xl = (*l)->x(); yt = (*l)->y(); xr = xl + (*l)->height(); yb = yt + (*l)->width(); if( yb > y) possible = possible < yb ? possible : yb; if( yt - ch > y ) possible = possible < yt - ch ? possible : yt - ch; } y = possible; } } } while( overlap != none && overlap != h_wrong ); // place the window c->move( x_optimal, y_optimal ); } /*! Place windows in a cascading order, remembering positions for each desktop */ void Workspace::cascadePlacement (Client* c, bool re_init) { /* cascadePlacement by Cristian Tibirna (tibirna@kde.org) (30Jan98) */ // work coords int xp, yp; //CT how do I get from the 'Client' class the size that NW squarish "handle" int delta_x = 24; int delta_y = 24; int d = currentDesktop() - 1; // get the maximum allowed windows space and desk's origin // (CT 20Nov1999 - is this common to all desktops?) QRect maxRect = clientArea(); // initialize often used vars: width and height of c; we gain speed int ch = c->height(); int cw = c->width(); int H = maxRect.bottom(); int W = maxRect.right(); int X = maxRect.left(); int Y = maxRect.top(); //initialize if needed if (re_init) { cci[d].pos = QPoint(X, Y); cci[d].col = cci[d].row = 0; } xp = cci[d].pos.x(); yp = cci[d].pos.y(); //here to touch in case people vote for resize on placement if ((yp + ch ) > H) yp = Y; if ((xp + cw ) > W) if (!yp) { smartPlacement(c); return; } else xp = X; //if this isn't the first window if ( cci[d].pos.x() != X && cci[d].pos.y() != Y ) { if ( xp != X && yp == Y ) xp = delta_x * (++(cci[d].col)); if ( yp != Y && xp == X ) yp = delta_y * (++(cci[d].row)); // last resort: if still doesn't fit, smart place it if ( ((xp + cw) > W - X) || ((yp + ch) > H - Y) ) { smartPlacement(c); return; } } // place the window c->move( QPoint( xp, yp ) ); // new position cci[d].pos = QPoint( xp + delta_x, yp + delta_y ); } /*! Raises the client \a c taking layers, transient windows and window groups into account. */ void Workspace::raiseClient( Client* c ) { if ( !c ) return; if ( c == desktop_client ) return; // deny stacking_order.remove( c ); stacking_order.append( c ); ClientList saveset; if ( c->transientFor() ) { saveset.append( c ); Client* t = findClient( c->transientFor() ); Client* tmp; while ( t && !saveset.contains( t ) && t->transientFor() ) { tmp = findClient( t->transientFor() ); if ( !tmp ) break; saveset.append( t ); t = tmp; } if ( t && !saveset.contains( t ) && t != desktop_client ) { raiseClient( t ); return; } } saveset.clear(); saveset.append( c ); raiseTransientsOf(saveset, c ); Window* new_stack = new Window[ stacking_order.count()+1]; int i = 0; for ( ClientList::ConstIterator it = stacking_order.fromLast(); it != stacking_order.end(); --it) { new_stack[i++] = (*it)->winId(); } XRaiseWindow(qt_xdisplay(), new_stack[0]); XRestackWindows(qt_xdisplay(), new_stack, i); delete [] new_stack; propagateClients( TRUE ); } /*! Private auxiliary function used in raiseClient() */ void Workspace::raiseTransientsOf( ClientList& safeset, Client* c ) { ClientList local = stacking_order; for ( ClientList::ConstIterator it = local.begin(); it != local.end(); ++it) { if ( (*it)->transientFor() == c->window() && !safeset.contains( *it ) ) { safeset.append( *it ); stacking_order.remove( *it ); stacking_order.append( *it ); raiseTransientsOf( safeset, *it ); } } } /*! Puts the focus on a dummy winodw */ void Workspace::focusToNull(){ static Window w = 0; int mask; XSetWindowAttributes attr; if (w == 0) { mask = CWOverrideRedirect; attr.override_redirect = 1; w = XCreateWindow(qt_xdisplay(), qt_xrootwin(), 0, 0, 1, 1, 0, CopyFromParent, InputOnly, CopyFromParent, mask, &attr); XMapWindow(qt_xdisplay(), w); } XSetInputFocus(qt_xdisplay(), w, RevertToPointerRoot, kwin_time ); //colormapFocus(0); TODO } void Workspace::setDesktopClient( Client* c) { desktop_client = c; if ( desktop_client ) { desktop_client->lower(); desktop_client->setGeometry( geometry() ); } } /*! Sets the current desktop to \a new_desktop Shows/Hides windows according to the stacking order and finally propages the new desktop to the world */ void Workspace::setCurrentDesktop( int new_desktop ){ if (new_desktop == current_desktop || new_desktop < 1 || new_desktop > number_of_desktops ) return; /* optimized Desktop switching: unmapping done from back to front mapping done from front to back => less exposure events */ for ( ClientList::ConstIterator it = stacking_order.fromLast(); it != stacking_order.end(); --it) { if ( (*it)->isVisible() && !(*it)->isOnDesktop( new_desktop ) ) { (*it)->hide(); } } for ( ClientList::ConstIterator it = stacking_order.begin(); it != stacking_order.end(); ++it) { if ( (*it)->isOnDesktop( new_desktop ) && !(*it)->isIconified() ) { (*it)->show(); } } current_desktop = new_desktop; XChangeProperty(qt_xdisplay(), qt_xrootwin(), atoms->net_current_desktop, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)¤t_desktop, 1); // try to restore the focus on this desktop Client* c = active_client?active_client:previousClient(0); Client* stop = c; while ( c && !c->isVisible() ) { c = previousClient( c ); if ( c == stop ) break; } if ( !c || !c->isVisible() ) { // there's no suitable client in the focus chain. Try to find any other client then. for ( ClientList::ConstIterator it = stacking_order.begin(); it != stacking_order.end(); ++it) { if ( (*it)->isVisible() ) { c = *it; break; } } } if ( c && c->isVisible() ) requestFocus( c ); QApplication::syncX(); KWM::switchToDesktop( current_desktop ); // ### compatibility } -void Workspace::makeFullScreen( Client* ) -{ - // not yet implemented -} - // experimental void Workspace::setDecorationStyle( int deco ) { if ( !popup_client ) return; Client* c = popup_client; WId w = c->window(); clients.remove( c ); stacking_order.remove( c ); focus_chain.remove( c ); bool mapped = c->isVisible(); c->hide(); c->releaseWindow(); KWM::moveToDesktop( w, c->desktop() ); KConfig* config = KGlobal::config(); switch ( deco ) { case 2: c = new BeClient( this, w); config->writeEntry("Plugin", "Be"); break; case 3: c = new SystemClient(this, w); config->writeEntry("Plugin", "system"); break; default: c = new StdClient( this, w ); config->writeEntry("Plugin", "standard"); } clients.append( c ); stacking_order.append( c ); c->manage( mapped ); activateClient( c ); } QWidget* Workspace::desktopWidget() { return desktop_widget; } /*! Sets the number of virtual desktops to \a n */ void Workspace::setNumberOfDesktops( int n ) { if ( n == number_of_desktops ) return; number_of_desktops = n; XChangeProperty(qt_xdisplay(), qt_xrootwin(), atoms->net_number_of_desktops, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&number_of_desktops, 1); } /*! Handles client messages sent to the workspace */ bool Workspace::clientMessage( XClientMessageEvent msg ) { if ( msg.message_type == atoms->net_current_desktop ) { setCurrentDesktop( msg.data.l[0] ); return TRUE; } return FALSE; } /*! Propagates the managed clients to the world */ void Workspace::propagateClients( bool onlyStacking ) { WId* cl; int i; if ( !onlyStacking ) { cl = new WId[ clients.count()]; i = 0; for ( ClientList::ConstIterator it = clients.begin(); it != clients.end(); ++it ) { cl[i++] = (*it)->window(); } XChangeProperty(qt_xdisplay(), qt_xrootwin(), atoms->net_client_list, XA_WINDOW, 32, PropModeReplace, (unsigned char *)cl, clients.count()); delete [] cl; } cl = new WId[ stacking_order.count()]; i = 0; for ( ClientList::ConstIterator it = stacking_order.begin(); it != stacking_order.end(); ++it) { cl[i++] = (*it)->window(); } XChangeProperty(qt_xdisplay(), qt_xrootwin(), atoms->net_client_list_stacking, XA_WINDOW, 32, PropModeReplace, (unsigned char *)cl, stacking_order.count()); delete [] cl; } bool Workspace::addDockwin( WId w ) { WId dockFor = 0; if ( !KWin::isDockWindow( w, &dockFor ) ) return FALSE; dockwins.append( DockWindow( w, dockFor ) ); XSelectInput( qt_xdisplay(), w, StructureNotifyMask ); propagateDockwins(); return TRUE; } bool Workspace::removeDockwin( WId w ) { if ( !dockwins.contains( w ) ) return FALSE; dockwins.remove( w ); propagateDockwins(); return TRUE; } bool Workspace::iconifyMeansWithdraw( Client* c) { for ( DockWindowList::ConstIterator it = dockwins.begin(); it != dockwins.end(); ++it ) { if ( (*it).dockFor == c->window() ) return TRUE; } return FALSE; } /*! Propagates the dockwins to the world */ void Workspace::propagateDockwins() { WId* cl = new WId[ dockwins.count()]; int i = 0; for ( DockWindowList::ConstIterator it = dockwins.begin(); it != dockwins.end(); ++it ) { cl[i++] = (*it).dockWin; } XChangeProperty(qt_xdisplay(), qt_xrootwin(), atoms->net_kde_docking_windows, XA_WINDOW, 32, PropModeReplace, (unsigned char *)cl, dockwins.count()); delete [] cl; } void Workspace::createKeybindings(){ keys = new KGlobalAccel(); #include "kwinbindings.cpp" keys->connectItem( "Switch to desktop 1", this, SLOT( slotSwitchDesktop1() )); keys->connectItem( "Switch to desktop 2", this, SLOT( slotSwitchDesktop2() )); keys->connectItem( "Switch to desktop 3", this, SLOT( slotSwitchDesktop3() )); keys->connectItem( "Switch to desktop 4", this, SLOT( slotSwitchDesktop4() )); keys->connectItem( "Switch to desktop 5", this, SLOT( slotSwitchDesktop5() )); keys->connectItem( "Switch to desktop 6", this, SLOT( slotSwitchDesktop6() )); keys->connectItem( "Switch to desktop 7", this, SLOT( slotSwitchDesktop7() )); keys->connectItem( "Switch to desktop 8", this, SLOT( slotSwitchDesktop8() )); keys->connectItem( "Pop-up window operations menu", this, SLOT( slotWindowOperations() ) ); keys->connectItem( "Window close", this, SLOT( slotWindowClose() ) ); keys->readSettings(); } void Workspace::slotSwitchDesktop1(){ setCurrentDesktop(1); } void Workspace::slotSwitchDesktop2(){ setCurrentDesktop(2); } void Workspace::slotSwitchDesktop3(){ setCurrentDesktop(3); } void Workspace::slotSwitchDesktop4(){ setCurrentDesktop(4); } void Workspace::slotSwitchDesktop5(){ setCurrentDesktop(5); } void Workspace::slotSwitchDesktop6(){ setCurrentDesktop(6); } void Workspace::slotSwitchDesktop7(){ setCurrentDesktop(7); } void Workspace::slotSwitchDesktop8(){ setCurrentDesktop(8); } void Workspace::desktopPopupAboutToShow() { if ( !desk_popup ) return; desk_popup->clear(); desk_popup->insertItem( i18n("&All desktops"), 0 ); if ( popup_client->isSticky() ) desk_popup->setItemChecked( 0, TRUE ); int id; for ( int i = 1; i <= numberOfDesktops(); i++ ) { id = desk_popup->insertItem( QString("&")+QString::number(i ), i ); if ( popup_client && !popup_client->isSticky() && popup_client->desktop() == i ) desk_popup->setItemChecked( id, TRUE ); } } void Workspace::clientPopupAboutToShow() { if ( !popup_client || !popup ) return; - popup->setItemChecked( popupIdMaximize, popup_client->isMaximized() ); - popup->setItemChecked( popupIdShade, popup_client->isShade() ); + popup->setItemChecked( Options::MaximizeOp, popup_client->isMaximized() ); + popup->setItemChecked( Options::ShadeOp, popup_client->isShade() ); } void Workspace::sendToDesktop( int desk ) { if ( !popup_client ) return; if ( desk == 0 ) { popup_client->setSticky( !popup_client->isSticky() ); return; } if ( popup_client->isSticky() ) popup_client->setSticky( FALSE ); if ( popup_client->isOnDesktop( desk ) ) return; popup_client->setDesktop( desk ); popup_client->hide(); Client* old = popup_client; for ( ClientList::ConstIterator it = clients.begin(); it != clients.end(); ++it) { if ( (*it)->transientFor() == popup_client->window() ) { popup_client = *it; sendToDesktop( desk ); popup_client = old; } } } void Workspace::slotWindowOperations() { if ( !active_client ) return; QPopupMenu* p = clientPopup( active_client ); p->popup( active_client->mapToGlobal( active_client->windowWrapper()->geometry().topLeft() ) ); } void Workspace::slotWindowClose() { if ( !popup_client ) return; popup_client->closeWindow(); } /* 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. */ QPoint Workspace::adjustClientPosition( Client* c, QPoint pos ) { //CT 16mar98, 27May98 - magics: BorderSnapZone, WindowSnapZone //CT adapted for kwin on 25Nov1999 if (options->windowSnapZone() || options->borderSnapZone()) { int snap; //snap trigger QRect maxRect = clientArea(); int xmin = maxRect.left(); int xmax = maxRect.right(); //desk size int ymin = maxRect.top(); int ymax = maxRect.bottom(); int cx, cy, rx, ry, cw, ch; //these don't change int nx, ny; //buffers int deltaX = xmax, deltaY = ymax; //minimum distance to other clients int lx, ly, lrx, lry; //coords and size for the comparison client, l nx = cx = pos.x(); ny = cy = pos.y(); rx = cx + (cw = c->width()); ry = cy + (ch = c->height()); // border snap snap = options->borderSnapZone(); if (snap) { if ( QABS(cx-xmin) < snap ){ deltaX = QABS(cx - xmin); nx = xmin; } if ((QABS(xmax-rx) < snap) && (QABS(xmax-rx) < deltaX)) { deltaX = abs(xmax-rx); nx = xmax - cw; } if ( QABS(cy-ymin) < snap ){ deltaY = QABS(cy-ymin); ny = ymin; } if ((QABS(ymax-ry) < snap) && (QABS(ymax-ry) < deltaY)) { deltaY = QABS(ymax-ry); ny = ymax - ch; } } // windows snap snap = options->windowSnapZone(); if (snap) { QValueList::ConstIterator l; for (l = clients.begin();l != clients.end();++l ) { if((*l)->isOnDesktop(currentDesktop()) && (*l) != desktop_client && !(*l)->isIconified() && (*l)->transientFor() == None && (*l) != c ) { lx = (*l)->x(); ly = (*l)->y(); lrx = lx + (*l)->width(); lry = ly + (*l)->height(); if( ( ( cy <= lry ) && ( cy >= ly ) ) || ( ( ry >= ly ) && ( ry <= lry ) ) || ( ( ly >= cy ) && ( lry <= ry ) ) ) { if ( ( QABS( lrx - cx ) < snap ) && ( QABS( lrx -cx ) < deltaX ) ) { deltaX = QABS( lrx - cx ); nx = lrx; } if ( ( QABS( rx - lx ) < snap ) && ( QABS( rx - lx ) < deltaX ) ) { deltaX = abs(rx - lx); nx = lx - cw; } } if( ( ( cx <= lrx ) && ( cx >= lx ) ) || ( ( rx >= lx ) && ( rx <= lrx ) ) || ( ( lx >= cx ) && ( lrx <= rx ) ) ) { if ( ( QABS( lry - cy ) < snap ) && ( QABS( lry -cy ) < deltaY ) ) { deltaY = QABS( lry - cy ); ny = lry; } if ( ( QABS( ry-ly ) < snap ) && ( QABS( ry - ly ) < deltaY ) ) { deltaY = QABS( ry - ly ); ny = ly - ch; } } } } } pos = QPoint(nx, ny); } return pos; } diff --git a/kwin/workspace.h b/kwin/workspace.h index 8ff86ee0b1..3977fa6be7 100644 --- a/kwin/workspace.h +++ b/kwin/workspace.h @@ -1,219 +1,218 @@ #ifndef WORKSPACE_H #define WORKSPACE_H #include #include #include #include #include #include - +#include "options.h" class Client; class TabBox; class KGlobalAccel; typedef QValueList ClientList; class DockWindow { public: DockWindow() : dockWin(0),dockFor(0) {} DockWindow( WId w ) : dockWin(w),dockFor(0) {} DockWindow( WId w, WId wf ) : dockWin(w),dockFor(wf) {} bool operator==( const DockWindow& other ) { return dockWin == other.dockWin; } WId dockWin; WId dockFor; }; typedef QValueList DockWindowList; class Shape { public: static bool hasShape( WId w); static int shapeEvent(); }; class Workspace : public QObject { Q_OBJECT public: Workspace(); Workspace( WId rootwin ); virtual ~Workspace(); virtual bool workspaceEvent( XEvent * ); bool hasClient(Client *); Client* findClient( WId w ) const; QRect geometry() const; QRect clientArea() const; bool destroyClient( Client* ); WId rootWin() const; Client* activeClient() const; void setActiveClient( Client* ); void activateClient( Client* ); void requestFocus( Client* c); void doPlacement( Client* c ); QPoint adjustClientPosition( Client* c, QPoint pos ); void raiseClient( Client* c ); void clientHidden( Client* ); int currentDesktop() const; int numberOfDesktops() const; void setNumberOfDesktops( int n ); QWidget* desktopWidget(); void grabKey(KeySym keysym, unsigned int mod); Client* nextClient(Client*) const; Client* previousClient(Client*) const; Client* nextStaticClient(Client*) const; Client* previousStaticClient(Client*) const; //#### TODO right layers as default Client* topClientOnDesktop( int fromLayer = 0, int toLayer = 0) const; QPopupMenu* clientPopup( Client* ); void setDesktopClient( Client* ); - void makeFullScreen( Client* ); - bool iconifyMeansWithdraw( Client* ); void iconifyOrDeiconifyTransientsOf( Client* ); bool hasCaption( const QString& caption ); + void performWindowOperation( Client* c, Options::WindowOperation op ); + + public slots: void setCurrentDesktop( int new_desktop ); // keybindings void slotSwitchDesktop1(); void slotSwitchDesktop2(); void slotSwitchDesktop3(); void slotSwitchDesktop4(); void slotSwitchDesktop5(); void slotSwitchDesktop6(); void slotSwitchDesktop7(); void slotSwitchDesktop8(); void slotWindowOperations(); void slotWindowClose(); - + private slots: void setDecorationStyle( int ); void desktopPopupAboutToShow(); void clientPopupAboutToShow(); void sendToDesktop( int ); void clientPopupActivated( int ); protected: bool keyPress( XKeyEvent key ); bool keyRelease( XKeyEvent key ); bool clientMessage( XClientMessageEvent msg ); private: void init(); KGlobalAccel *keys; void createKeybindings(); WId root; ClientList clients; ClientList stacking_order; ClientList focus_chain; Client* active_client; bool control_grab; bool tab_grab; TabBox* tab_box; void freeKeyboard(bool pass); QGuardedPtr popup_client; QPopupMenu *popup; - int popupIdMove, popupIdSize, popupIdMinimize,popupIdMaximize, - popupIdShade, popupIdFullscreen,popupIdClose; QPopupMenu *desk_popup; Client* should_get_focus; void raiseTransientsOf( ClientList& safeset, Client* c ); void randomPlacement(Client* c); void smartPlacement(Client* c); void cascadePlacement(Client* c, bool re_init = false); void focusToNull(); Client* desktop_client; int current_desktop; int number_of_desktops; Client* findClientWidthId( WId w ) const; QWidget* desktop_widget; //experimental void setDecoration( int deco ); void propagateClients( bool onlyStacking = FALSE); DockWindowList dockwins; bool addDockwin( WId w ); bool removeDockwin( WId w ); void propagateDockwins(); DockWindow findDockwin( WId w ); //CT needed for cascading+ struct CascadingInfo { QPoint pos; int col; int row; }; QValueList cci; // -cascading }; inline WId Workspace::rootWin() const { return root; } /*! Returns the active client, i.e. the client that has the focus (or None if no client has the focus) */ inline Client* Workspace::activeClient() const { return active_client; } /*! Returns the current virtual desktop of this workspace */ inline int Workspace::currentDesktop() const { return current_desktop; } /*! Returns the number of virtual desktops of this workspace */ inline int Workspace::numberOfDesktops() const { return 4; } #endif