diff --git a/Makefile.am b/Makefile.am index cedd37af24..a800ea3a71 100644 --- a/Makefile.am +++ b/Makefile.am @@ -11,7 +11,7 @@ kwin_la_SOURCES = workspace.cpp client.cpp placement.cpp atoms.cpp \ options.cpp plugins.cpp events.cpp KWinInterface.skel \ killwindow.cpp geometrytip.cpp sm.cpp group.cpp bridge.cpp \ manage.cpp notifications.cpp activation.cpp useractions.cpp \ - geometry.cpp + geometry.cpp rules.cpp kwin_la_LIBADD = $(LIB_KDEUI) lib/libkdecorations.la kwin_la_LDFLAGS = $(all_libraries) -module -avoid-version diff --git a/activation.cpp b/activation.cpp index dbcf0a17a7..0153d57543 100644 --- a/activation.cpp +++ b/activation.cpp @@ -320,7 +320,11 @@ void Workspace::takeActivity( Client* c, int flags, bool handled ) if( modal != NULL && modal != c ) { if( !modal->isOnDesktop( c->desktop())) + { modal->setDesktop( c->desktop()); + if( modal->desktop() != c->desktop()) // forced desktop + activateClient( modal ); + } // if the click was inside the window (i.e. handled is set), // but it has a modal, there's no need to use handled mode, because // the modal doesn't get the click anyway @@ -663,7 +667,7 @@ KWIN_COMPARE_PREDICATE( SameApplicationActiveHackPredicate, const Client*, && Client::belongToSameApplication( cl, value, true ) && cl != value); Time Client::readUserTimeMapTimestamp( const KStartupInfoId* asn_id, const KStartupInfoData* asn_data, - const SessionInfo* session ) const + bool session ) const { Time time = info->userTime(); kdDebug( 1212 ) << "User timestamp, initial:" << time << endl; @@ -727,7 +731,7 @@ Time Client::readUserTimeMapTimestamp( const KStartupInfoId* asn_id, const KStar // Unless it was the active window at the time // of session saving and there was no user interaction yet, // this check will be done in Workspace::allowClientActiovationTimestamp(). - if( session && !session->fake ) + if( session ) return -1U; if( ignoreFocusStealing() && act != NULL ) time = act->userTime(); diff --git a/client.cpp b/client.cpp index 8cdb98d144..a22d2f6e46 100644 --- a/client.cpp +++ b/client.cpp @@ -27,6 +27,7 @@ License. See the file "COPYING" for the exact licensing terms. #include "workspace.h" #include "atoms.h" #include "notifications.h" +#include "rules.h" #include @@ -78,6 +79,7 @@ Client::Client( Workspace *ws ) transient_for( NULL ), transient_for_id( None ), original_transient_for_id( None ), + client_rules( NULL ), in_group( NULL ), window_group( None ), in_layer( UnknownLayer ), @@ -133,10 +135,8 @@ Client::Client( Workspace *ws ) Pcontexthelp = 0; Pping = 0; input = FALSE; - store_settings = FALSE; skip_pager = FALSE; - max_mode = MaximizeRestore; cmap = None; @@ -175,6 +175,7 @@ void Client::releaseWindow( bool on_shutdown ) { if (moveResizeMode) leaveMoveResize(); + updateWindowRules(); setModal( false ); // otherwise its mainwindow wouldn't get focus hidden = true; // so that it's not considered visible anymore (can't use hideClient(), it would set flags) if( !on_shutdown ) @@ -222,6 +223,7 @@ void Client::destroyClient() { if (moveResizeMode) leaveMoveResize(); + updateWindowRules(); ++block_geometry; setModal( false ); hidden = true; // so that it's not considered visible anymore @@ -1077,15 +1079,11 @@ void Client::setModal( bool m ) // _NET_WM_STATE_MODAL should possibly rather be _NET_WM_WINDOW_TYPE_MODAL_DIALOG } -void Client::toggleOnAllDesktops() - { - setOnAllDesktops( !isOnAllDesktops()); - } - void Client::setDesktop( int desktop ) { if( desktop != NET::OnAllDesktops ) // do range check desktop = KMAX( 1, KMIN( workspace()->numberOfDesktops(), desktop )); + desktop = rules()->checkDesktop( desktop ); if( desk == desktop ) return; int was_desk = desk; @@ -1264,9 +1262,9 @@ void Client::fetchIconicName() /*!\reimp */ -QString Client::caption() const +QString Client::caption( bool full ) const { - return cap_normal + cap_suffix; + return full ? cap_normal + cap_suffix : cap_normal; } void Client::getWMHints() diff --git a/client.h b/client.h index 695a3f70d8..fcbd8ecfd2 100644 --- a/client.h +++ b/client.h @@ -39,6 +39,7 @@ class Client; class WinInfo; class SessionInfo; class Bridge; +class WindowRules; class Client : public QObject, public KDecorationDefines { @@ -66,6 +67,7 @@ class Client : public QObject, public KDecorationDefines void checkGroup( Group* gr = NULL, bool force = false ); // prefer isXXX() instead NET::WindowType windowType( bool strict = false, int supported_types = SUPPORTED_WINDOW_TYPES_MASK ) const; + const WindowRules* rules() const; QRect geometry() const; QSize size() const; @@ -160,9 +162,6 @@ class Client : public QObject, public KDecorationDefines void setModal( bool modal ); bool isModal() const; - bool storeSettings() const; - void setStoreSettings( bool ); - // auxiliary functions, depend on the windowType bool wantsTabFocus() const; bool wantsInput() const; @@ -237,7 +236,7 @@ class Client : public QObject, public KDecorationDefines // updates visibility depending on whether it's on the current desktop void virtualDesktopChange(); - QString caption() const; + QString caption( bool full = true ) const; void keyPressEvent( uint key_code ); // FRAME ?? void updateMouseGrab(); @@ -275,7 +274,6 @@ class Client : public QObject, public KDecorationDefines void closeWindow(); void killWindow(); void maximize( MaximizeMode ); - void toggleOnAllDesktops(); void toggleShade(); void showContextHelp(); void cancelAutoRaise(); @@ -343,6 +341,8 @@ private slots: void fetchName(); void fetchIconicName(); bool hasTransientInternal( const Client* c, bool indirect, ConstClientList& set ) const; + void initWindowRules(); + void updateWindowRules(); void updateWorkareaDiffs(); void checkDirection( int new_diff, int old_diff, QRect& rect, const QRect& area ); @@ -380,7 +380,7 @@ private slots: void rawHide(); // just hides it Time readUserTimeMapTimestamp( const KStartupInfoId* asn_id, const KStartupInfoData* asn_data, - const SessionInfo* session ) const; + bool session ) const; Time readUserCreationTime() const; static bool sameAppWindowRoleMatch( const Client* c1, const Client* c2, bool active_hack ); void startupIdChanged(); @@ -438,7 +438,6 @@ private slots: uint Pcontexthelp : 1; // does the window understand the ContextHelp protocol? uint Pping : 1; // does it support _NET_WM_PING? uint input :1; // does the window want input in its wm_hints - uint store_settings : 1; uint skip_pager : 1; uint motif_may_resize : 1; uint motif_may_move :1; @@ -453,6 +452,7 @@ private slots: uint urgency : 1; // XWMHints, UrgencyHint uint ignore_focus_stealing : 1; // don't apply focus stealing prevention to this client uint check_active_modal : 1; // see Client::addTransient() + WindowRules* client_rules; void getWMHints(); void readIcons(); void getWindowProtocols(); @@ -685,17 +685,6 @@ inline bool Client::keepBelow() const return keep_below; } -inline bool Client::storeSettings() const - { - return store_settings; - } - -inline void Client::setStoreSettings( bool b ) - { - store_settings = b; - } - - inline bool Client::shape() const { return is_shape; @@ -837,6 +826,11 @@ inline bool Client::ignoreFocusStealing() const return ignore_focus_stealing; } +inline const WindowRules* Client::rules() const + { + return client_rules; + } + KWIN_PROCEDURE( CheckIgnoreFocusStealingProcedure, cl->ignore_focus_stealing = options->checkIgnoreFocusStealing( cl )); inline Window Client::moveResizeGrabWindow() const diff --git a/events.cpp b/events.cpp index 9bf1ed777f..b9e10e6275 100644 --- a/events.cpp +++ b/events.cpp @@ -20,6 +20,7 @@ License. See the file "COPYING" for the exact licensing terms. #include "atoms.h" #include "tabbox.h" #include "group.h" +#include "rules.h" #include #include diff --git a/layers.cpp b/layers.cpp index 694240fbc9..67c0e82d2c 100644 --- a/layers.cpp +++ b/layers.cpp @@ -64,13 +64,15 @@ License. See the file "COPYING" for the exact licensing terms. #include +#include + #include "utils.h" #include "client.h" #include "workspace.h" #include "tabbox.h" #include "popupinfo.h" #include "group.h" -#include +#include "rules.h" extern Time qt_x_time; @@ -642,22 +644,36 @@ void Client::restackWindow( Window /*above TODO */, int detail, NET::RequestSour void Client::setKeepAbove( bool b ) { - if ( b == keepAbove() ) + b = rules()->checkKeepAbove( b ); + if( b ) + setKeepBelow( false ); + if ( b == keepAbove() + || ( b && keepBelow())) // forced below + { // force hint change if different + if( bool( info->state() & NET::KeepAbove ) != keepAbove()) + info->setState( keepAbove() ? NET::KeepAbove : 0, NET::KeepAbove ); return; - setKeepBelow( false ); + } keep_above = b; - info->setState( b ? NET::KeepAbove : 0, NET::KeepAbove ); + info->setState( keepAbove() ? NET::KeepAbove : 0, NET::KeepAbove ); // TODO emit a signal about the change to the style plugin workspace()->updateClientLayer( this ); } void Client::setKeepBelow( bool b ) { - if ( b == keepBelow() ) + b = rules()->checkKeepBelow( b ); + if( b ) + setKeepAbove( false ); + if ( b == keepBelow() + || ( b && keepAbove())) // forced above + { // force hint change if different + if( bool( info->state() & NET::KeepBelow ) != keepBelow()) + info->setState( keepBelow() ? NET::KeepBelow : 0, NET::KeepBelow ); return; - setKeepAbove( false ); + } keep_below = b; - info->setState( b ? NET::KeepBelow : 0, NET::KeepBelow ); + info->setState( keepBelow() ? NET::KeepBelow : 0, NET::KeepBelow ); workspace()->updateClientLayer( this ); } diff --git a/lib/kdecoration.h b/lib/kdecoration.h index cdee4642e7..a57e3f6512 100644 --- a/lib/kdecoration.h +++ b/lib/kdecoration.h @@ -86,7 +86,8 @@ public: KeepAboveOp, KeepBelowOp, OperationsOp, - ToggleStoreSettingsOp, + WindowRulesOp, + ToggleStoreSettingsOp = WindowRulesOp, ///< @obsolete HMaximizeOp, VMaximizeOp, LowerOp, diff --git a/manage.cpp b/manage.cpp index ffade9f4e3..903843e5ee 100644 --- a/manage.cpp +++ b/manage.cpp @@ -22,6 +22,7 @@ License. See the file "COPYING" for the exact licensing terms. #include #include "notifications.h" +#include "rules.h" extern Time qt_x_time; extern Atom qt_window_role; @@ -105,8 +106,11 @@ bool Client::manage( Window w, bool isMapped ) } ignore_focus_stealing = options->checkIgnoreFocusStealing( this ); - detectNoBorder(); fetchName(); + window_role = getStringProperty( w, qt_window_role ); + initWindowRules(); + + detectNoBorder(); fetchIconicName(); getWMHints(); // needs to be done before readTransient() because of reading the group getWmClientLeader(); // needs to be done before readTransient() because of same app comparing @@ -114,7 +118,6 @@ bool Client::manage( Window w, bool isMapped ) getIcons(); getWindowProtocols(); getWmNormalHints(); // get xSizeHint - window_role = getStringProperty( w, qt_window_role ); // TODO try to obey all state information from info->state() @@ -122,13 +125,6 @@ bool Client::manage( Window w, bool isMapped ) skip_pager = ( info->state() & NET::SkipPager) != 0; modal = ( info->state() & NET::Modal ) != 0; - // window wants to stay on top? - keep_above = ( info->state() & NET::KeepAbove ) != 0; - // window wants to stay on bottom? - keep_below = ( info->state() & NET::KeepBelow ) != 0; - if( keep_above && keep_below ) - keep_above = keep_below = false; - KStartupInfoId asn_id; KStartupInfoData asn_data; bool asn_valid = workspace()->checkStartupNotification( window(), asn_id, asn_data ); @@ -136,6 +132,7 @@ bool Client::manage( Window w, bool isMapped ) workspace()->updateClientLayer( this ); SessionInfo* session = workspace()->takeSessionInfo( this ); + if ( session ) { if ( session->minimized ) @@ -186,6 +183,7 @@ bool Client::manage( Window w, bool isMapped ) desk = workspace()->currentDesktop(); if( desk != NET::OnAllDesktops ) // do range check desk = KMAX( 1, KMIN( workspace()->numberOfDesktops(), desk )); + desk = rules()->checkDesktop( desk, !isMapped ); info->setDesktop( desk ); workspace()->updateOnAllDesktopsOfTransients( this ); // SELI // onAllDesktopsChange(); decoration doesn't exist here yet @@ -308,6 +306,8 @@ bool Client::manage( Window w, bool isMapped ) // other settings from the previous session if ( session ) { + // session restored windows are not considered to be new windows WRT rules, + // i.e. obey only forcing rules setKeepAbove( session->keepAbove ); setKeepBelow( session->keepBelow ); setSkipTaskbar( session->skipTaskbar, true ); @@ -368,10 +368,8 @@ bool Client::manage( Window w, bool isMapped ) // read other initial states if( info->state() & NET::Shaded ) setShade( ShadeNormal ); - if( info->state() & NET::KeepAbove ) - setKeepAbove( true ); - if( info->state() & NET::KeepBelow ) - setKeepBelow( true ); + setKeepAbove( rules()->checkKeepAbove( info->state() & NET::KeepAbove, !isMapped )); + setKeepBelow( rules()->checkKeepBelow( info->state() & NET::KeepBelow, !isMapped )); if( info->state() & NET::SkipTaskbar ) setSkipTaskbar( true, true ); if( info->state() & NET::SkipPager ) @@ -433,7 +431,7 @@ bool Client::manage( Window w, bool isMapped ) { workspace()->restackClientUnderActive( this ); rawShow(); - if( ( !session || session->fake ) && ( !isSpecialWindow() || isOverride())) + if( !session && ( !isSpecialWindow() || isOverride())) demandAttention(); } } @@ -442,7 +440,7 @@ bool Client::manage( Window w, bool isMapped ) { virtualDesktopChange(); workspace()->raiseClient( this ); - if( ( !session || session->fake ) && !isMapped ) + if( !session && !isMapped ) demandAttention(); } } diff --git a/rules.cpp b/rules.cpp new file mode 100644 index 0000000000..4b91af93b4 --- /dev/null +++ b/rules.cpp @@ -0,0 +1,238 @@ +/***************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2004 Lubos Lunak + +You can Freely distribute this program under the GNU General Public +License. See the file "COPYING" for the exact licensing terms. +******************************************************************/ + +#include "rules.h" + +#include +#include + +#include "client.h" +#include "workspace.h" + +namespace KWinInternal +{ + +static WindowRules dummyRules; // dummy used to avoid NULL checks + +WindowRules::WindowRules() + : wmclassregexp( false ) + , windowroleregexp( false ) + , titleregexp( false ) + , extraroleregexp( false ) + , desktoprule( DontCareRule ) + , aboverule( DontCareRule ) + , belowrule( DontCareRule ) + { + } + +WindowRules::WindowRules( KConfig& cfg ) + : wmclassregexp( false ) + , windowroleregexp( false ) + , titleregexp( false ) + , extraroleregexp( false ) + , desktoprule( DontCareRule ) + , aboverule( DontCareRule ) + , belowrule( DontCareRule ) + { + wmclass = cfg.readEntry( "wmclass" ).lower().latin1(); + wmclassregexp = cfg.readBoolEntry( "wmclassregexp" ); + windowrole = cfg.readEntry( "windowrole" ).lower().latin1(); + windowroleregexp = cfg.readBoolEntry( "windowroleregexp" ); + title = cfg.readEntry( "title" ); + titleregexp = cfg.readBoolEntry( "titleregexp" ); + extrarole = cfg.readEntry( "extrarole" ).lower().latin1(); + extraroleregexp = cfg.readBoolEntry( "extraroleregexp" ); + desktop = cfg.readNumEntry( "desktop" ); + desktoprule = readRule( cfg, "desktoprule" ); + above = cfg.readBoolEntry( "above" ); + aboverule = readRule( cfg, "aboverule" ); + below = cfg.readBoolEntry( "below" ); + belowrule = readRule( cfg, "belowrule" ); + kdDebug() << "READ RULE:" << wmclass << endl; + } + +#define WRITE_MATCH_STRING( var, cast ) \ + if( !var.isEmpty()) \ + { \ + cfg.writeEntry( #var, cast var ); \ + cfg.writeEntry( #var "regexp", var##regexp ); \ + } \ + else \ + { \ + cfg.deleteEntry( #var ); \ + cfg.deleteEntry( #var "regexp" ); \ + } + +#define WRITE_SET_RULE( var ) \ + if( var##rule != DontCareRule ) \ + { \ + cfg.writeEntry( #var, var ); \ + cfg.writeEntry( #var "rule", var##rule ); \ + } \ + else \ + { \ + cfg.deleteEntry( #var ); \ + cfg.deleteEntry( #var "rule" ); \ + } + +void WindowRules::write( KConfig& cfg ) const + { + // always write wmclass + cfg.writeEntry( "wmclass", ( const char* )wmclass ); + cfg.writeEntry( "wmclassregexp", wmclassregexp ); + WRITE_MATCH_STRING( windowrole, (const char*) ); + WRITE_MATCH_STRING( title, ); + WRITE_MATCH_STRING( extrarole, (const char*) ); + WRITE_SET_RULE( desktop ); + WRITE_SET_RULE( above ); + WRITE_SET_RULE( below ); + } + +#undef WRITE_MATCH_STRING +#undef WRITE_SET_RULE + +SettingRule WindowRules::readRule( KConfig& cfg, const QString& key ) + { + int v = cfg.readNumEntry( key ); + if( v >= DontCareRule && v <= LastRule ) + return static_cast< SettingRule >( v ); + return DontCareRule; + } + +void WindowRules::update( Client* c ) + { + // TODO check this setting is for this client ? + if( desktoprule == RememberRule ) + desktop = c->desktop(); + if( aboverule == RememberRule ) + above = c->keepAbove(); + if( belowrule == RememberRule ) + below = c->keepBelow(); + } + +bool WindowRules::match( const Client* c ) const + { + // TODO exactMatch() for regexp? + if( !wmclass.isEmpty()) + { // TODO optimize? + if( wmclassregexp && !QRegExp( wmclass ).exactMatch( c->resourceClass())) + return false; + if( !wmclassregexp && wmclass != c->resourceClass()) + return false; + } + if( !windowrole.isEmpty()) + { + if( windowroleregexp && QRegExp( windowrole ).exactMatch( c->windowRole())) + return false; + if( !windowroleregexp && windowrole != c->windowRole()) + return false; + } + if( !title.isEmpty()) + { + if( titleregexp && QRegExp( title ).exactMatch( c->caption( false ))) + return false; + if( !titleregexp && title != c->caption( false )) + return false; + } + // TODO extrarole + return true; + } + +int WindowRules::checkDesktop( int req_desktop, bool init ) const + { + // TODO chaining? + return checkRule( desktoprule, init ) ? desktop : req_desktop; + } + +bool WindowRules::checkKeepAbove( bool req_above, bool init ) const + { + return checkRule( aboverule, init ) ? above : req_above; + } + +bool WindowRules::checkKeepBelow( bool req_below, bool init ) const + { + return checkRule( belowrule, init ) ? below : req_below; + } + +// Client + +void Client::initWindowRules() + { + client_rules = workspace()->findWindowRules( this ); + } + +void Client::updateWindowRules() + { + client_rules->update( this ); + } + +// Workspace + +WindowRules* Workspace::findWindowRules( const Client* c ) const + { + if( c->isTopMenu()) // TODO cannot have restrictions + return &dummyRules; + for( QValueList< WindowRules* >::ConstIterator it = windowRules.begin(); + it != windowRules.end(); + ++it ) + { + // chaining for wildcard matches + if( (*it)->match( c )) + { + kdDebug() << "RULE FOUND:" << c << endl; + return *it; + } + } + kdDebug() << "RULE DUMMY:" << c << endl; + return &dummyRules; + } + +void Workspace::editWindowRules( Client* c ) + { + // TODO + } + +void Workspace::loadWindowRules() + { + while( !windowRules.isEmpty()) + { + delete windowRules.front(); + windowRules.pop_front(); + } + KConfig cfg( "kwinrulesrc", true ); + cfg.setGroup( "General" ); + int count = cfg.readNumEntry( "count" ); + kdDebug() << "RULES:" << count << endl; + for( int i = 1; + i <= count; + ++i ) + { + cfg.setGroup( QString::number( i )); + WindowRules* setting = new WindowRules( cfg ); + windowRules.append( setting ); + } + } + +void Workspace::writeWindowRules() + { + KConfig cfg( "kwinrulesrc" ); + cfg.setGroup( "General" ); + cfg.writeEntry( "count", windowRules.count()); + int i = 1; + for( QValueList< WindowRules* >::ConstIterator it = windowRules.begin(); + it != windowRules.end(); + ++it, ++i ) + { + cfg.setGroup( QString::number( i )); + (*it)->write( cfg ); + } + } + +} // namespace diff --git a/rules.h b/rules.h new file mode 100644 index 0000000000..f11b3b6c27 --- /dev/null +++ b/rules.h @@ -0,0 +1,77 @@ +/***************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2004 Lubos Lunak + +You can Freely distribute this program under the GNU General Public +License. See the file "COPYING" for the exact licensing terms. +******************************************************************/ + +#ifndef KWIN_RULES_H +#define KWIN_RULES_H + +#include + +class KConfig; + +namespace KWinInternal +{ + +class Client; + +enum SettingRule + { + DontCareRule, + ApplyRule, + ForceRule, + RememberRule, + LastRule = RememberRule + }; + +class WindowRules + { + public: + WindowRules(); + WindowRules( KConfig& ); + void write( KConfig& ) const; + void update( Client* ); + bool match( const Client* c ) const; + int checkDesktop( int desktop, bool init = false ) const; + bool checkKeepAbove( bool above, bool init = false ) const; + bool checkKeepBelow( bool above, bool init = false ) const; + private: + static SettingRule readRule( KConfig&, const QString& key ); + static bool checkRule( SettingRule rule, bool init ); + QCString wmclass; + bool wmclassregexp; + // TODO bool wmclasscomplete - class+name + QCString windowrole; + bool windowroleregexp; + QString title; // TODO "caption" ? + bool titleregexp; + QCString extrarole; + bool extraroleregexp; + // TODO window type? both to which it applies and to which value to force it + int desktop; + SettingRule desktoprule; + bool above; + SettingRule aboverule; + bool below; + SettingRule belowrule; + }; + +inline +bool WindowRules::checkRule( SettingRule rule, bool init ) + { + if( rule != DontCareRule ) + { + if( rule == ForceRule || init ) + return true; + } + return false; + } + +} // namespace + +#endif diff --git a/sm.cpp b/sm.cpp index d07aac49de..75f08cba81 100644 --- a/sm.cpp +++ b/sm.cpp @@ -178,121 +178,21 @@ void Workspace::loadSessionInfo() info->userNoBorder = config->readBoolEntry( QString("userNoBorder")+n, FALSE ); info->windowType = txtToWindowType( config->readEntry( QString("windowType")+n ).latin1()); info->active = ( active_client == i ); - info->fake = false; } } -void Workspace::loadFakeSessionInfo() - { - fakeSession.clear(); - KConfig *config = KGlobal::config(); - config->setGroup("FakeSession" ); - int count = config->readNumEntry( "count" ); - for ( int i = 1; i <= count; i++ ) - { - QString n = QString::number(i); - SessionInfo* info = new SessionInfo; - fakeSession.append( info ); - info->windowRole = config->readEntry( QString("windowRole")+n ).latin1(); - info->resourceName = config->readEntry( QString("resourceName")+n ).latin1(); - info->resourceClass = config->readEntry( QString("resourceClass")+n ).lower().latin1(); - info->wmClientMachine = config->readEntry( QString("clientMachine")+n ).latin1(); - info->geometry = config->readRectEntry( QString("geometry")+n ); - info->restore = config->readRectEntry( QString("restore")+n ); - info->fsrestore = config->readRectEntry( QString("fsrestore")+n ); - info->maximized = config->readNumEntry( QString("maximize")+n, 0 ); - info->fullscreen = config->readNumEntry( QString("fullscreen")+n, 0 ); - info->desktop = config->readNumEntry( QString("desktop")+n, 0 ); - info->minimized = config->readBoolEntry( QString("iconified")+n, FALSE ); - info->onAllDesktops = config->readBoolEntry( QString("sticky")+n, FALSE ); - info->shaded = config->readBoolEntry( QString("shaded")+n, FALSE ); - info->keepAbove = config->readBoolEntry( QString("staysOnTop")+n, FALSE ); - info->keepBelow = config->readBoolEntry( QString("keepBelow")+n, FALSE ); - info->skipTaskbar = config->readBoolEntry( QString("skipTaskbar")+n, FALSE ); - info->skipPager = config->readBoolEntry( QString("skipPager")+n, FALSE ); - info->userNoBorder = config->readBoolEntry( QString("userNoBorder")+n, FALSE ); - info->windowType = txtToWindowType( config->readEntry( QString("windowType")+n ).latin1()); - info->active = false; - info->fake = true; - } - } - -void Workspace::storeFakeSessionInfo( Client* c ) - { - if ( !c->storeSettings() ) - return; - SessionInfo* info = new SessionInfo; - fakeSession.append( info ); - info->windowRole = c->windowRole(); - info->resourceName = c->resourceName(); - info->resourceClass = c->resourceClass(); - info->wmClientMachine = c->wmClientMachine(); - info->geometry = QRect( c->calculateGravitation(TRUE), c->clientSize() ) ; // FRAME - info->restore = c->geometryRestore(); - info->fsrestore = c->geometryFSRestore(); - info->maximized = (int)c->maximizeMode(); - info->fullscreen = (int)c->fullScreenMode(); - info->desktop = c->desktop(); - info->minimized = c->isMinimized(); - info->onAllDesktops = c->isOnAllDesktops(); - info->shaded = c->isShade(); - info->keepAbove = c->keepAbove(); - info->keepBelow = c->keepBelow(); - info->skipTaskbar = c->skipTaskbar( true ); - info->skipPager = c->skipPager(); - info->userNoBorder = c->isUserNoBorder(); - info->windowType = c->windowType(); - info->active = false; - info->fake = true; - } - -void Workspace::writeFakeSessionInfo() - { - KConfig *config = KGlobal::config(); - config->setGroup("FakeSession" ); - int count = 0; - for ( SessionInfo* info = fakeSession.first(); info; info = fakeSession.next() ) - { - count++; - QString n = QString::number(count); - config->writeEntry( QString("windowRole")+n, info->windowRole.data() ); - config->writeEntry( QString("resourceName")+n, info->resourceName.data() ); - config->writeEntry( QString("resourceClass")+n, info->resourceClass.data() ); - config->writeEntry( QString("clientMachine")+n, info->wmClientMachine.data() ); - config->writeEntry( QString("geometry")+n, info->geometry ); - config->writeEntry( QString("restore")+n, info->restore ); - config->writeEntry( QString("fsrestore")+n, info->fsrestore ); - config->writeEntry( QString("maximize")+n, info->maximized ); - config->writeEntry( QString("fullscreen")+n, info->fullscreen ); - config->writeEntry( QString("desktop")+n, info->desktop ); - config->writeEntry( QString("iconified")+n, info->minimized ); - config->writeEntry( QString("onAllDesktops")+n, info->onAllDesktops ); - config->writeEntry( QString("shaded")+n, info->shaded ); - config->writeEntry( QString("staysOnTop")+n, info->keepAbove ); - config->writeEntry( QString("keepBelow")+n, info->keepBelow ); - config->writeEntry( QString("skipTaskbar")+n, info->skipTaskbar ); - config->writeEntry( QString("skipPager")+n, info->skipPager ); - config->writeEntry( QString("userNoBorder")+n, info->userNoBorder ); - config->writeEntry( QString("windowType")+n, windowTypeToTxt( info->windowType )); - } - config->writeEntry( "count", count ); - } - /*! Returns a SessionInfo for client \a c. The returned session info is removed from the storage. It's up to the caller to delete it. This function is called when a new window is mapped and must be managed. - We try to find a matching entry in the session. We also try to find - a matching entry in the fakeSession to see if the user had seclected the - ``store settings'' menu entry. + We try to find a matching entry in the session. May return 0 if there's no session info for the client. */ SessionInfo* Workspace::takeSessionInfo( Client* c ) { SessionInfo *realInfo = 0; - SessionInfo *fakeInfo = 0; QCString sessionId = c->sessionId(); QCString windowRole = c->windowRole(); QCString wmCommand = c->wmCommand(); @@ -333,24 +233,7 @@ SessionInfo* Workspace::takeSessionInfo( Client* c ) realInfo = session.take(); } - // Now search ``fakeSession'' - for (SessionInfo* info = fakeSession.first(); info && !fakeInfo; info = fakeSession.next() ) - if ( info->resourceName == resourceName && - info->resourceClass == resourceClass && - ( windowRole.isEmpty() || windowRole == info->windowRole ) && - sessionInfoWindowTypeMatch( c, info )) - fakeInfo = fakeSession.take(); - - // Reconciliate - if (fakeInfo) - c->setStoreSettings( TRUE ); - if (fakeInfo && realInfo) - delete fakeInfo; - if (realInfo) - return realInfo; - if (fakeInfo) - return fakeInfo; - return 0; + return realInfo; } bool Workspace::sessionInfoWindowTypeMatch( Client* c, SessionInfo* info ) diff --git a/sm.h b/sm.h index 2ed44f3a8d..42e7cba106 100644 --- a/sm.h +++ b/sm.h @@ -45,8 +45,7 @@ struct SessionInfo bool skipPager; bool userNoBorder; NET::WindowType windowType; - bool active; // means 'was active in the saved session', not used otherwise - bool fake; // fake session, i.e. 'save window settings', not SM restored + bool active; // means 'was active in the saved session' }; diff --git a/useractions.cpp b/useractions.cpp index 43235daed7..397793933a 100644 --- a/useractions.cpp +++ b/useractions.cpp @@ -57,7 +57,7 @@ QPopupMenu* Workspace::clientPopup() advanced_popup->insertItem( SmallIconSet( "down" ), i18n("Keep &Below Others"), Options::KeepBelowOp ); advanced_popup->insertItem( SmallIconSet( "window_fullscreen" ), i18n("&Fullscreen"), Options::FullScreenOp ); advanced_popup->insertItem( i18n("&No Border"), Options::NoBorderOp ); - advanced_popup->insertItem( SmallIconSet( "filesave" ), i18n("Sto&re Window Settings"), Options::ToggleStoreSettingsOp ); + advanced_popup->insertItem( SmallIconSet( "filesave" ), i18n("&Special Window Rules"), Options::WindowRulesOp ); popup->insertItem(i18n("Ad&vanced"), advanced_popup ); desk_popup_index = popup->count(); @@ -115,7 +115,6 @@ void Workspace::clientPopupAboutToShow() advanced_popup->setItemChecked( Options::NoBorderOp, popup_client->noBorder() ); advanced_popup->setItemEnabled( Options::NoBorderOp, popup_client->userCanSetNoBorder() ); popup->setItemEnabled( Options::MinimizeOp, popup_client->isMinimizable() ); - advanced_popup->setItemChecked( Options::ToggleStoreSettingsOp, popup_client->storeSettings() ); popup->setItemEnabled( Options::CloseOp, popup_client->isCloseable() ); } @@ -129,7 +128,7 @@ void Workspace::initDesktopPopup() desk_popup->setCheckable( TRUE ); desk_popup->setFont(KGlobalSettings::menuFont()); connect( desk_popup, SIGNAL( activated(int) ), - this, SLOT( sendToDesktop(int) ) ); + this, SLOT( slotSendToDesktop(int) ) ); connect( desk_popup, SIGNAL( aboutToShow() ), this, SLOT( desktopPopupAboutToShow() ) ); @@ -278,8 +277,8 @@ void Workspace::performWindowOperation( Client* c, Options::WindowOperation op ) case Options::KeepBelowOp: c->setKeepBelow( !c->keepBelow() ); break; - case Options::ToggleStoreSettingsOp: - c->setStoreSettings( !c->storeSettings() ); + case Options::WindowRulesOp: + editWindowRules( c ); break; case Options::LowerOp: lowerClient(c); @@ -666,7 +665,7 @@ void Workspace::slotWindowRaiseOrLower() void Workspace::slotWindowOnAllDesktops() { if( active_client ) - active_client->toggleOnAllDesktops(); + active_client->setOnAllDesktops( !active_client->isOnAllDesktops()); } void Workspace::slotWindowFullScreen() @@ -737,7 +736,7 @@ void Workspace::slotKillWindow() Internal slot for the window operation menu */ -void Workspace::sendToDesktop( int desk ) +void Workspace::slotSendToDesktop( int desk ) { if ( !popup_client ) return; diff --git a/workspace.cpp b/workspace.cpp index 383d09f8a1..546d22164e 100644 --- a/workspace.cpp +++ b/workspace.cpp @@ -36,6 +36,7 @@ License. See the file "COPYING" for the exact licensing terms. #include "placement.h" #include "notifications.h" #include "group.h" +#include "rules.h" #include #include @@ -118,7 +119,7 @@ Workspace::Workspace( bool restore ) if ( restore ) loadSessionInfo(); - loadFakeSessionInfo(); + loadWindowRules(); (void) QApplication::desktop(); // trigger creation of desktop widget @@ -393,9 +394,8 @@ Workspace::~Workspace() ++it ) { // only release the window - if( !(*it)->isDesktop()) // TODO ? - storeFakeSessionInfo( *it ); (*it)->releaseWindow( true ); + // no removeClient() is called ! } delete desktop_widget; delete tab_box; @@ -404,7 +404,7 @@ Workspace::~Workspace() if ( root == qt_xrootwin() ) XDeleteProperty(qt_xdisplay(), qt_xrootwin(), atoms->kwin_running); - writeFakeSessionInfo(); + writeWindowRules(); KGlobal::config()->sync(); delete rootInfo; @@ -417,6 +417,11 @@ Workspace::~Workspace() delete topmenu_watcher; delete topmenu_selection; delete topmenu_space; + while( !windowRules.isEmpty()) + { + delete windowRules.front(); + windowRules.pop_front(); + } XDestroyWindow( qt_xdisplay(), null_focus_window ); // TODO ungrabXServer(); _self = 0; @@ -487,8 +492,6 @@ void Workspace::removeClient( Client* c, allowed_t ) if( c->isNormalWindow()) Notify::raise( Notify::Delete ); - storeFakeSessionInfo( c ); - Q_ASSERT( clients.contains( c ) || desktops.contains( c )); clients.remove( c ); desktops.remove( c ); @@ -1169,11 +1172,10 @@ void Workspace::setNumberOfDesktops( int n ) */ void Workspace::sendClientToDesktop( Client* c, int desk, bool dont_activate ) { - if ( c->desktop() == desk ) - return; - bool was_on_desktop = c->isOnDesktop( desk ) || c->isOnAllDesktops(); c->setDesktop( desk ); + if ( c->desktop() == desk ) // no change or desktop forced + return; desk = c->desktop(); // Client did range checking if ( c->isOnDesktop( currentDesktop() ) ) diff --git a/workspace.h b/workspace.h index a74a23026c..fd176fea72 100644 --- a/workspace.h +++ b/workspace.h @@ -40,6 +40,7 @@ class PopupInfo; class RootInfo; class PluginMgr; class Placement; +class WindowRules; class SystemTrayWindow { @@ -196,7 +197,7 @@ class Workspace : public QObject, public KWinInterface, public KDecorationDefine void storeSession( KConfig* config, SMSavePhase phase ); SessionInfo* takeSessionInfo( Client* ); - + WindowRules* findWindowRules( const Client* ) const; // dcop interface void cascadeDesktop(); @@ -316,7 +317,7 @@ class Workspace : public QObject, public KWinInterface, public KDecorationDefine private slots: void desktopPopupAboutToShow(); void clientPopupAboutToShow(); - void sendToDesktop( int ); + void slotSendToDesktop( int ); void clientPopupActivated( int ); void configureWM(); void desktopResized(); @@ -416,15 +417,15 @@ class Workspace : public QObject, public KWinInterface, public KDecorationDefine Client* popup_client; - void loadSessionInfo(); - QWidget* desktop_widget; + void loadSessionInfo(); + void loadWindowRules(); + void writeWindowRules(); + void editWindowRules( Client* ); + QPtrList session; - QPtrList fakeSession; - void loadFakeSessionInfo(); - void storeFakeSessionInfo( Client* c ); - void writeFakeSessionInfo(); + QValueList windowRules; static const char* windowTypeToTxt( NET::WindowType type ); static NET::WindowType txtToWindowType( const char* txt ); static bool sessionInfoWindowTypeMatch( Client* c, SessionInfo* info );