diff --git a/client.cpp b/client.cpp index 02859c4f2b..9d202c3424 100644 --- a/client.cpp +++ b/client.cpp @@ -76,7 +76,12 @@ public: m_client->maximize( Client::MaximizeHorizontal ); else m_client->maximize( Client::MaximizeRestore ); - } + } + + if ( mask & NET::StaysOnTop ) { + m_client->setStaysOnTop( state & NET::StaysOnTop ); + m_client->workspace()->raiseClient( m_client ); + } } private: ::Client * m_client; @@ -411,6 +416,7 @@ Client::Client( Workspace *ws, WId w, QWidget *parent, const char *name, WFlags passive_focus = FALSE; is_shape = FALSE; is_sticky = FALSE; + stays_on_top = FALSE; may_move = TRUE; getWMHints(); @@ -426,10 +432,17 @@ Client::Client( Workspace *ws, WId w, QWidget *parent, const char *name, WFlags // should we open this window on a certain desktop? - - if ( info->desktop() ) + if ( info->desktop() == NETWinInfo::OnAllDesktops ) + setSticky( TRUE ); + else if ( info->desktop() ) desk= info->desktop(); // window had the initial desktop property! + + // window wants to stay on top? + stays_on_top = info->state() & NET::StaysOnTop; + + + // if this window is transient, ensure that it is opened on the // same window as its parent. this is necessary when an application @@ -862,7 +875,7 @@ bool Client::propertyNotify( XPropertyEvent& e ) */ bool Client::clientMessage( XClientMessageEvent& e ) { - + if ( e.message_type == atoms->kde_wm_change_state ) { if ( e.data.l[0] == IconicState && isNormal() ) { if ( e.data.l[1] ) @@ -879,7 +892,7 @@ bool Client::clientMessage( XClientMessageEvent& e ) } blockAnimation = FALSE; } else if ( e.message_type == atoms->wm_change_state) { - if ( e.data.l[0] == IconicState && isNormal() ) + if ( e.data.l[0] == IconicState && isNormal() ) iconify(); return TRUE; } @@ -1372,16 +1385,16 @@ void Client::iconify() } Events::raise( Events::Iconify ); setMappingState( IconicState ); - + if ( !isTransient() ) animateIconifyOrDeiconify( TRUE ); hide(); - + workspace()->iconifyOrDeiconifyTransientsOf( this ); } -/*! +/*! Closes the window by either sending a delete_window message or using XKill. */ @@ -1426,7 +1439,7 @@ void Client::maximize( MaximizeMode m) if ( !geom_restore.isNull() ) m = MaximizeRestore; - + if ( m != MaximizeRestore ) { Events::raise( Events::Maximize ); geom_restore = geometry(); @@ -1451,7 +1464,7 @@ void Client::maximize( MaximizeMode m) ); info->setState( NET::MaxHoriz, NET::MaxHoriz ); break; - + case MaximizeRestore: { Events::raise( Events::UnMaximize ); setGeometry(geom_restore); @@ -1753,10 +1766,12 @@ void Client::setSticky( bool b ) if ( is_sticky == b ) return; is_sticky = b; - if ( is_sticky ) - Events::raise( Events::Sticky ); - else - Events::raise( Events::UnSticky ); + if ( isVisible() ) { + if ( is_sticky ) + Events::raise( Events::Sticky ); + else + Events::raise( Events::UnSticky ); + } if ( !is_sticky ) setDesktop( workspace()->currentDesktop() ); else @@ -1766,6 +1781,20 @@ void Client::setSticky( bool b ) } +/*! + Let the window stay on top or not, depending on \a b + + \sa Workspace::setClientOnTop() + */ +void Client::setStaysOnTop( bool b ) +{ + if ( b == staysOnTop() ) + return; + stays_on_top = b; + info->setState( b?NET::StaysOnTop:0, NET::StaysOnTop ); +} + + void Client::setDesktop( int desktop) { desk = desktop; @@ -2208,15 +2237,15 @@ void Client::animateIconifyOrDeiconify( bool iconify) before = QRect( icongeom.x(), icongeom.y(), icongeom.width(), pm.height() ); after = QRect( x(), y(), width(), pm.height() ); } - + lf = (after.left() - before.left())/step; - rf = (after.right() - before.right())/step; - tf = (after.top() - before.top())/step; + rf = (after.right() - before.right())/step; + tf = (after.top() - before.top())/step; bf = (after.bottom() - before.bottom())/step; XGrabServer( qt_xdisplay() ); - + QRect area = before; QRect area2; QPixmap pm2; @@ -2259,7 +2288,7 @@ void Client::animateIconifyOrDeiconify( bool iconify) } while ( t.elapsed() < step); if (area2 == area || need_to_clear ) p.drawPixmap( area2.x(), area2.y(), pm2 ); - + p.end(); XUngrabServer( qt_xdisplay() ); } diff --git a/client.h b/client.h index 944a89e969..38df2a7cc1 100644 --- a/client.h +++ b/client.h @@ -117,6 +117,8 @@ public: bool isSticky() const; void setSticky( bool ); + bool staysOnTop() const; + void setStaysOnTop( bool ); // auxiliary functions, depend on the windowType bool wantsTabFocus() const; @@ -153,7 +155,7 @@ public: QCString sessionId(); QRect adjustedClientArea( const QRect& area ) const; - + public slots: void iconify(); void closeWindow(); @@ -198,7 +200,7 @@ protected: virtual MousePosition mousePosition( const QPoint& ) const; virtual void setMouseCursor( MousePosition m ); - + virtual void animateIconifyOrDeiconify( bool iconify ); virtual QPixmap animationPixmap( int w ); @@ -242,6 +244,7 @@ private: uint shaded :1; uint active :1; uint is_sticky :1; + uint stays_on_top : 1; uint is_shape :1; uint may_move :1; uint passive_focus :1; @@ -343,6 +346,11 @@ inline bool Client::isSticky() const return is_sticky; } +inline bool Client::staysOnTop() const +{ + return stays_on_top; +} + inline bool Client::shape() const { diff --git a/options.cpp b/options.cpp index 711a29489a..3e98e0395e 100644 --- a/options.cpp +++ b/options.cpp @@ -161,7 +161,7 @@ void Options::reload() window_snap_zone = config->readNumEntry("WindowSnapZone", 10); - OpTitlebarDblClick = windowOperation( config->readEntry("TitlebarDoubleClickCommand", "winShade") ); + OpTitlebarDblClick = windowOperation( config->readEntry("TitlebarDoubleClickCommand", "Shade") ); // Mouse bindings config->setGroup( "MouseBindings"); diff --git a/options.h b/options.h index abf8951360..ec20f23ebb 100644 --- a/options.h +++ b/options.h @@ -55,28 +55,28 @@ public: enum FocusPolicy { ClickToFocus, FocusFollowsMouse, FocusUnderMouse, FocusStrictlyUnderMouse }; FocusPolicy focusPolicy; - + /** Different Alt-Tab-Styles: */ enum AltTabStyle { KDE, CDE }; AltTabStyle altTabStyle; - - + + /** MoveResizeMode, either Tranparent or Opaque. */ @@ -148,6 +148,7 @@ public: CloseOp, StickyOp, ShadeOp, + StaysOnTopOp, OperationsOp, NoOp }; diff --git a/stdclient.cpp b/stdclient.cpp index a08f70f122..1e6d9d71a8 100644 --- a/stdclient.cpp +++ b/stdclient.cpp @@ -13,6 +13,7 @@ Copyright (C) 1999, 2000 Matthias Ettrich #include #include #include +#include #include "workspace.h" #include "options.h" @@ -355,8 +356,6 @@ void StdClient::init() { Client::init(); button[0]->setIconSet( miniIcon() ); - - // ### TODO transient etc. } void StdClient::iconChange() @@ -369,12 +368,25 @@ void StdClient::iconChange() } -/*! - Indicates that the menu button has been clicked +/*! + Indicates that the menu button has been clicked. One press shows + the window operation menu, a double click closes the window. */ void StdClient::menuButtonPressed() { - (void ) workspace()->clientPopup( this ); //trigger the popup menu + static QTime* t = 0; + static StdClient* tc = 0; + if ( !t ) + t = new QTime; + + if ( tc != this || t->elapsed() > QApplication::doubleClickInterval() ) + button[0]->setPopup( workspace()->clientPopup( this ) ); + else { + button[0]->setPopup( 0 ); + closeWindow(); + } + t->start(); + tc = this; } diff --git a/workspace.cpp b/workspace.cpp index 52181bd7db..5b6e8f420d 100644 --- a/workspace.cpp +++ b/workspace.cpp @@ -1082,6 +1082,7 @@ QPopupMenu* Workspace::clientPopup( Client* c ) popup->insertItem( i18n("Mi&nimize"), Options::IconifyOp ); popup->insertItem( i18n("Ma&ximize"), Options::MaximizeOp ); popup->insertItem( i18n("Sh&ade"), Options::ShadeOp ); + popup->insertItem( i18n("Always &On Top"), Options::StaysOnTopOp ); popup->insertSeparator(); @@ -1119,6 +1120,10 @@ void Workspace::performWindowOperation( Client* c, Options::WindowOperation op ) case Options::ShadeOp: c->setShade( !c->isShade() ); break; + case Options::StaysOnTopOp: + c->setStaysOnTop( !c->staysOnTop() ); + raiseClient( c ); + break; default: break; } @@ -1450,9 +1455,9 @@ void Workspace::reconfigure() } -/*! - Lowers the client \a c taking layers, transient windows and window - groups into account. +/*! + Lowers the client \a c taking stays-on-top flags, layers, + transient windows and window groups into account. */ void Workspace::lowerClient( Client* c, bool dropFocus ) { @@ -1489,16 +1494,18 @@ void Workspace::lowerClient( Client* c, bool dropFocus ) stacking_order.remove(c); stacking_order.prepend(c); - Window* new_stack = new Window[ stacking_order.count()+1]; + ClientList list = constrainedStackingOrder( stacking_order ); + Window* new_stack = new Window[ list.count() + 1 ]; int i = 0; Client *new_top = 0; - for ( ClientList::ConstIterator it = stacking_order.fromLast(); it != stacking_order.end(); --it) { + for ( ClientList::ConstIterator it = list.fromLast(); it != list.end(); --it) { new_stack[i++] = (*it)->winId(); if (!new_top && (*it)->isVisible()) new_top = (*it); } XRaiseWindow(qt_xdisplay(), new_stack[0]); XRestackWindows(qt_xdisplay(), new_stack, i); delete [] new_stack; + propagateClients( TRUE ); if (dropFocus && new_top) { requestFocus(new_top); @@ -1506,9 +1513,9 @@ void Workspace::lowerClient( Client* c, bool dropFocus ) } -/*! - Raises the client \a c taking layers, transient windows and window - groups into account. +/*! + Raises the client \a c taking layers, stays-on-top flags, + transient windows and window groups into account. */ void Workspace::raiseClient( Client* c ) { @@ -1546,10 +1553,11 @@ void Workspace::raiseClient( Client* c ) saveset.clear(); saveset.append( c ); raiseTransientsOf(saveset, c ); - - Window* new_stack = new Window[ stacking_order.count()+1]; + + ClientList list = constrainedStackingOrder( stacking_order ); + Window* new_stack = new Window[ list.count() + 1 ]; int i = 0; - for ( ClientList::ConstIterator it = stacking_order.fromLast(); it != stacking_order.end(); --it) { + for ( ClientList::ConstIterator it = list.fromLast(); it != list.end(); --it) { new_stack[i++] = (*it)->winId(); } XRaiseWindow(qt_xdisplay(), new_stack[0]); @@ -1583,10 +1591,7 @@ void Workspace::raiseTransientsOf( ClientList& safeset, Client* c ) void Workspace::lowerTransientsOf( ClientList& safeset, Client* c ) { ClientList local = stacking_order; - ClientList::ConstIterator it = local.fromLast(); - /* QT docu says --begin() is undefined, actually it is ==end(), - so the usual for loops work: for(bla;it!=end()...) */ - for (; it!=local.end(); --it) { + for ( ClientList::ConstIterator it = local.fromLast(); it!=local.end(); --it) { if ((*it)->transientFor() == c->window() && !safeset.contains(*it)) { safeset.append( *it ); lowerTransientsOf( safeset, *it ); @@ -1596,6 +1601,30 @@ void Workspace::lowerTransientsOf( ClientList& safeset, Client* c ) } } + + +/*! + Returns a stacking order based upon \a list that fulfills certain contained. + + Currently it obeyes the staysOnTop flag only. + + \sa Client::staysOnTop() + */ +ClientList Workspace::constrainedStackingOrder( const ClientList& list ) +{ + ClientList result; + ClientList::ConstIterator it; + for ( it = list.begin(); it!=list.end(); ++it) { + if ( !(*it)->staysOnTop() ) + result.append( *it ); + } + for ( it = list.begin(); it!=list.end(); ++it) { + if ( (*it)->staysOnTop() ) + result.append( *it ); + } + return result; +} + /*! Puts the focus on a dummy window */ @@ -1785,7 +1814,7 @@ bool Workspace::addSystemTrayWin( WId w ) return TRUE; } -/*! +/*! Check whether \a w is a system tray window. If so, remove it from the respective datastructures and propagate this to the world. */ @@ -2019,6 +2048,7 @@ void Workspace::clientPopupAboutToShow() return; popup->setItemChecked( Options::MaximizeOp, popup_client->isMaximized() ); popup->setItemChecked( Options::ShadeOp, popup_client->isShade() ); + popup->setItemChecked( Options::StaysOnTopOp, popup_client->staysOnTop() ); } @@ -2552,4 +2582,5 @@ QRect Workspace::clientArea() return area; } + #include "workspace.moc" diff --git a/workspace.h b/workspace.h index 870ce00bc5..ffa0ee8ce1 100644 --- a/workspace.h +++ b/workspace.h @@ -139,8 +139,8 @@ public: Client* previousClient(Client*) const; Client* nextStaticClient(Client*) const; Client* previousStaticClient(Client*) const; - - /** + + /** * Returns the list of clients sorted in stacking order, with topmost client * at the last position */ @@ -223,6 +223,8 @@ private: void init(); void createKeybindings(); void freeKeyboard(bool pass); + + ClientList constrainedStackingOrder( const ClientList& list ); Client* clientFactory(WId w); @@ -327,4 +329,5 @@ inline const ClientList& Workspace::stackingOrder() const return stacking_order; } + #endif