/******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2004 Lubos Lunak This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #include "rules.h" #include #include #include #include #include #include #ifndef KCMRULES #include "client.h" #include "workspace.h" #endif namespace KWin { Rules::Rules() : temporary_state( 0 ) , wmclassmatch( UnimportantMatch ) , wmclasscomplete( UnimportantMatch ) , windowrolematch( UnimportantMatch ) , titlematch( UnimportantMatch ) , extrarolematch( UnimportantMatch ) , clientmachinematch( UnimportantMatch ) , types( NET::AllTypesMask ) , placementrule( UnusedForceRule ) , positionrule( UnusedSetRule ) , sizerule( UnusedSetRule ) , minsizerule( UnusedForceRule ) , maxsizerule( UnusedForceRule ) , opacityactiverule( UnusedForceRule ) , opacityinactiverule( UnusedForceRule ) , ignorepositionrule( UnusedForceRule ) , desktoprule( UnusedSetRule ) , typerule( UnusedForceRule ) , maximizevertrule( UnusedSetRule ) , maximizehorizrule( UnusedSetRule ) , minimizerule( UnusedSetRule ) , shaderule( UnusedSetRule ) , skiptaskbarrule( UnusedSetRule ) , skippagerrule( UnusedSetRule ) , aboverule( UnusedSetRule ) , belowrule( UnusedSetRule ) , fullscreenrule( UnusedSetRule ) , noborderrule( UnusedSetRule ) , fsplevelrule( UnusedForceRule ) , acceptfocusrule( UnusedForceRule ) , moveresizemoderule( UnusedForceRule ) , closeablerule( UnusedForceRule ) , strictgeometryrule( UnusedForceRule ) , shortcutrule( UnusedSetRule ) , disableglobalshortcutsrule( UnusedForceRule ) { } Rules::Rules( const QString& str, bool temporary ) : temporary_state( temporary ? 2 : 0 ) { KTemporaryFile file; if( file.open() ) { QByteArray s = str.toUtf8(); file.write( s.data(), s.length()); } file.flush(); KConfig cfg( file.fileName(), KConfig::SimpleConfig); readFromCfg( cfg.group( QString() ) ); if( description.isEmpty()) description = "temporary"; } #define READ_MATCH_STRING( var, func ) \ var = cfg.readEntry( #var ) func; \ var##match = (StringMatch) qMax( FirstStringMatch, \ qMin( LastStringMatch, static_cast< StringMatch >( cfg.readEntry( #var "match",0 )))); #define READ_SET_RULE( var, func, def ) \ var = func ( cfg.readEntry( #var, def)); \ var##rule = readSetRule( cfg, #var "rule" ); #define READ_SET_RULE_DEF( var , func, def ) \ var = func ( cfg.readEntry( #var, def )); \ var##rule = readSetRule( cfg, #var "rule" ); #define READ_FORCE_RULE( var, func, def) \ var = func ( cfg.readEntry( #var, def)); \ var##rule = readForceRule( cfg, #var "rule" ); #define READ_FORCE_RULE2( var, def, func, funcarg ) \ var = func ( cfg.readEntry( #var, def),funcarg ); \ var##rule = readForceRule( cfg, #var "rule" ); Rules::Rules( const KConfigGroup& cfg ) : temporary_state( 0 ) { readFromCfg( cfg ); } static int limit0to4( int i ) { return qMax( 0, qMin( 4, i )); } void Rules::readFromCfg( const KConfigGroup& cfg ) { description = cfg.readEntry( "Description" ); if( description.isEmpty()) // capitalized first, lowercase for backwards compatibility description = cfg.readEntry( "description" ); READ_MATCH_STRING( wmclass, .toLower().toLatin1() ); wmclasscomplete = cfg.readEntry( "wmclasscomplete" , false); READ_MATCH_STRING( windowrole, .toLower().toLatin1() ); READ_MATCH_STRING( title, ); READ_MATCH_STRING( extrarole, .toLower().toLatin1() ); READ_MATCH_STRING( clientmachine, .toLower().toLatin1() ); types = cfg.readEntry( "types", uint(NET::AllTypesMask) ); READ_FORCE_RULE2( placement,QString(), Placement::policyFromString,false ); READ_SET_RULE_DEF( position, , invalidPoint ); READ_SET_RULE( size,, QSize()); if( size.isEmpty() && sizerule != ( SetRule )Remember) sizerule = UnusedSetRule; READ_FORCE_RULE( minsize,, QSize()); if( !minsize.isValid()) minsize = QSize( 1, 1 ); READ_FORCE_RULE( maxsize, , QSize()); if( maxsize.isEmpty()) maxsize = QSize( 32767, 32767 ); READ_FORCE_RULE( opacityactive, , 0); if( opacityactive < 0 || opacityactive > 100 ) opacityactive = 100; READ_FORCE_RULE( opacityinactive,, 0); if( opacityinactive < 0 || opacityinactive > 100 ) opacityinactive = 100; READ_FORCE_RULE( ignoreposition,, false); READ_SET_RULE( desktop,,0 ); type = readType( cfg, "type" ); typerule = type != NET::Unknown ? readForceRule( cfg, "typerule" ) : UnusedForceRule; READ_SET_RULE( maximizevert,, false); READ_SET_RULE( maximizehoriz,, false); READ_SET_RULE( minimize,, false); READ_SET_RULE( shade,, false); READ_SET_RULE( skiptaskbar,, false); READ_SET_RULE( skippager,, false); READ_SET_RULE( above,, false); READ_SET_RULE( below,, false); READ_SET_RULE( fullscreen,, false); READ_SET_RULE( noborder,,false ); READ_FORCE_RULE( fsplevel,limit0to4,0 ); // fsp is 0-4 READ_FORCE_RULE( acceptfocus, , false); READ_FORCE_RULE( moveresizemode,Options::stringToMoveResizeMode, QString()); READ_FORCE_RULE( closeable, , false); READ_FORCE_RULE( strictgeometry, , false); READ_SET_RULE( shortcut, ,QString() ); READ_FORCE_RULE( disableglobalshortcuts, , false); } #undef READ_MATCH_STRING #undef READ_SET_RULE #undef READ_FORCE_RULE #undef READ_FORCE_RULE2 #define WRITE_MATCH_STRING( var, cast, force ) \ if( !var.isEmpty() || force ) \ { \ cfg.writeEntry( #var, cast var ); \ cfg.writeEntry( #var "match", (int)var##match ); \ } \ else \ { \ cfg.deleteEntry( #var ); \ cfg.deleteEntry( #var "match" ); \ } #define WRITE_SET_RULE( var, func ) \ if( var##rule != UnusedSetRule ) \ { \ cfg.writeEntry( #var, func ( var )); \ cfg.writeEntry( #var "rule", (int)var##rule ); \ } \ else \ { \ cfg.deleteEntry( #var ); \ cfg.deleteEntry( #var "rule" ); \ } #define WRITE_FORCE_RULE( var, func ) \ if( var##rule != UnusedForceRule ) \ { \ cfg.writeEntry( #var, func ( var )); \ cfg.writeEntry( #var "rule", (int)var##rule ); \ } \ else \ { \ cfg.deleteEntry( #var ); \ cfg.deleteEntry( #var "rule" ); \ } void Rules::write( KConfigGroup& cfg ) const { cfg.writeEntry( "Description", description ); // always write wmclass WRITE_MATCH_STRING( wmclass, (const char*), true ); cfg.writeEntry( "wmclasscomplete", wmclasscomplete ); WRITE_MATCH_STRING( windowrole, (const char*), false ); WRITE_MATCH_STRING( title,, false ); WRITE_MATCH_STRING( extrarole, (const char*), false ); WRITE_MATCH_STRING( clientmachine, (const char*), false ); if (types != NET::AllTypesMask) cfg.writeEntry("types", uint(types)); else cfg.deleteEntry("types"); WRITE_FORCE_RULE( placement, Placement::policyToString ); WRITE_SET_RULE( position, ); WRITE_SET_RULE( size, ); WRITE_FORCE_RULE( minsize, ); WRITE_FORCE_RULE( maxsize, ); WRITE_FORCE_RULE( opacityactive, ); WRITE_FORCE_RULE( opacityinactive, ); WRITE_FORCE_RULE( ignoreposition, ); WRITE_SET_RULE( desktop, ); WRITE_FORCE_RULE( type, int ); WRITE_SET_RULE( maximizevert, ); WRITE_SET_RULE( maximizehoriz, ); WRITE_SET_RULE( minimize, ); WRITE_SET_RULE( shade, ); WRITE_SET_RULE( skiptaskbar, ); WRITE_SET_RULE( skippager, ); WRITE_SET_RULE( above, ); WRITE_SET_RULE( below, ); WRITE_SET_RULE( fullscreen, ); WRITE_SET_RULE( noborder, ); WRITE_FORCE_RULE( fsplevel, ); WRITE_FORCE_RULE( acceptfocus, ); WRITE_FORCE_RULE( moveresizemode, Options::moveResizeModeToString ); WRITE_FORCE_RULE( closeable, ); WRITE_FORCE_RULE( strictgeometry, ); WRITE_SET_RULE( shortcut, ); WRITE_FORCE_RULE( disableglobalshortcuts, ); } #undef WRITE_MATCH_STRING #undef WRITE_SET_RULE #undef WRITE_FORCE_RULE // returns true if it doesn't affect anything bool Rules::isEmpty() const { return( placementrule == UnusedForceRule && positionrule == UnusedSetRule && sizerule == UnusedSetRule && minsizerule == UnusedForceRule && maxsizerule == UnusedForceRule && opacityactiverule == UnusedForceRule && opacityinactiverule == UnusedForceRule && ignorepositionrule == UnusedForceRule && desktoprule == UnusedSetRule && typerule == UnusedForceRule && maximizevertrule == UnusedSetRule && maximizehorizrule == UnusedSetRule && minimizerule == UnusedSetRule && shaderule == UnusedSetRule && skiptaskbarrule == UnusedSetRule && skippagerrule == UnusedSetRule && aboverule == UnusedSetRule && belowrule == UnusedSetRule && fullscreenrule == UnusedSetRule && noborderrule == UnusedSetRule && fsplevelrule == UnusedForceRule && acceptfocusrule == UnusedForceRule && moveresizemoderule == UnusedForceRule && closeablerule == UnusedForceRule && strictgeometryrule == UnusedForceRule && shortcutrule == UnusedSetRule && disableglobalshortcutsrule == UnusedForceRule ); } Rules::SetRule Rules::readSetRule( const KConfigGroup& cfg, const QString& key ) { int v = cfg.readEntry( key,0 ); if( v >= DontAffect && v <= ForceTemporarily ) return static_cast< SetRule >( v ); return UnusedSetRule; } Rules::ForceRule Rules::readForceRule( const KConfigGroup& cfg, const QString& key ) { int v = cfg.readEntry( key,0 ); if( v == DontAffect || v == Force || v == ForceTemporarily ) return static_cast< ForceRule >( v ); return UnusedForceRule; } NET::WindowType Rules::readType( const KConfigGroup& cfg, const QString& key ) { int v = cfg.readEntry( key,0 ); if( v >= NET::Normal && v <= NET::Splash ) return static_cast< NET::WindowType >( v ); return NET::Unknown; } bool Rules::matchType( NET::WindowType match_type ) const { if( types != NET::AllTypesMask ) { if( match_type == NET::Unknown ) match_type = NET::Normal; // NET::Unknown->NET::Normal is only here for matching if( !NET::typeMatchesMask( match_type, types )) return false; } return true; } bool Rules::matchWMClass( const QByteArray& match_class, const QByteArray& match_name ) const { if( wmclassmatch != UnimportantMatch ) { // TODO optimize? QByteArray cwmclass = wmclasscomplete ? match_name + ' ' + match_class : match_class; if( wmclassmatch == RegExpMatch && QRegExp( wmclass ).indexIn( cwmclass ) == -1 ) return false; if( wmclassmatch == ExactMatch && wmclass != cwmclass ) return false; if( wmclassmatch == SubstringMatch && !cwmclass.contains( wmclass )) return false; } return true; } bool Rules::matchRole( const QByteArray& match_role ) const { if( windowrolematch != UnimportantMatch ) { if( windowrolematch == RegExpMatch && QRegExp( windowrole ).indexIn( match_role ) == -1 ) return false; if( windowrolematch == ExactMatch && windowrole != match_role ) return false; if( windowrolematch == SubstringMatch && !match_role.contains( windowrole )) return false; } return true; } bool Rules::matchTitle( const QString& match_title ) const { if( titlematch != UnimportantMatch ) { if( titlematch == RegExpMatch && QRegExp( title ).indexIn( match_title ) == -1 ) return false; if( titlematch == ExactMatch && title != match_title ) return false; if( titlematch == SubstringMatch && !match_title.contains( title )) return false; } return true; } bool Rules::matchClientMachine( const QByteArray& match_machine ) const { if( clientmachinematch != UnimportantMatch ) { // if it's localhost, check also "localhost" before checking hostname if( match_machine != "localhost" && isLocalMachine( match_machine ) && matchClientMachine( "localhost" )) return true; if( clientmachinematch == RegExpMatch && QRegExp( clientmachine ).indexIn( match_machine ) == -1 ) return false; if( clientmachinematch == ExactMatch && clientmachine != match_machine ) return false; if( clientmachinematch == SubstringMatch && !match_machine.contains( clientmachine )) return false; } return true; } #ifndef KCMRULES bool Rules::match( const Client* c ) const { if( !matchType( c->windowType( true ))) return false; if( !matchWMClass( c->resourceClass(), c->resourceName())) return false; if( !matchRole( c->windowRole())) return false; if( !matchTitle( c->caption( false ))) return false; // TODO extrarole if( !matchClientMachine( c->wmClientMachine( false ))) return false; return true; } bool Rules::update( Client* c ) { // TODO check this setting is for this client ? bool updated = false; if( positionrule == ( SetRule )Remember) { if( !c->isFullScreen()) { QPoint new_pos = position; // don't use the position in the direction which is maximized if(( c->maximizeMode() & MaximizeHorizontal ) == 0 ) new_pos.setX( c->pos().x()); if(( c->maximizeMode() & MaximizeVertical ) == 0 ) new_pos.setY( c->pos().y()); updated = updated || position != new_pos; position = new_pos; } } if( sizerule == ( SetRule )Remember) { if( !c->isFullScreen()) { QSize new_size = size; // don't use the position in the direction which is maximized if(( c->maximizeMode() & MaximizeHorizontal ) == 0 ) new_size.setWidth( c->size().width()); if(( c->maximizeMode() & MaximizeVertical ) == 0 ) new_size.setHeight( c->size().height()); updated = updated || size != new_size; size = new_size; } } if( desktoprule == ( SetRule )Remember) { updated = updated || desktop != c->desktop(); desktop = c->desktop(); } if( maximizevertrule == ( SetRule )Remember) { updated = updated || maximizevert != bool( c->maximizeMode() & MaximizeVertical ); maximizevert = c->maximizeMode() & MaximizeVertical; } if( maximizehorizrule == ( SetRule )Remember) { updated = updated || maximizehoriz != bool( c->maximizeMode() & MaximizeHorizontal ); maximizehoriz = c->maximizeMode() & MaximizeHorizontal; } if( minimizerule == ( SetRule )Remember) { updated = updated || minimize != c->isMinimized(); minimize = c->isMinimized(); } if( shaderule == ( SetRule )Remember) { updated = updated || ( shade != ( c->shadeMode() != ShadeNone )); shade = c->shadeMode() != ShadeNone; } if( skiptaskbarrule == ( SetRule )Remember) { updated = updated || skiptaskbar != c->skipTaskbar(); skiptaskbar = c->skipTaskbar(); } if( skippagerrule == ( SetRule )Remember) { updated = updated || skippager != c->skipPager(); skippager = c->skipPager(); } if( aboverule == ( SetRule )Remember) { updated = updated || above != c->keepAbove(); above = c->keepAbove(); } if( belowrule == ( SetRule )Remember) { updated = updated || below != c->keepBelow(); below = c->keepBelow(); } if( fullscreenrule == ( SetRule )Remember) { updated = updated || fullscreen != c->isFullScreen(); fullscreen = c->isFullScreen(); } if( noborderrule == ( SetRule )Remember) { updated = updated || noborder != c->noBorder(); noborder = c->noBorder(); } if (opacityactiverule == ( ForceRule )Force) { // TODO } if (opacityinactiverule == ( ForceRule )Force) { // TODO } return updated; } #define APPLY_RULE( var, name, type ) \ bool Rules::apply##name( type& arg, bool init ) const \ { \ if( checkSetRule( var##rule, init )) \ arg = this->var; \ return checkSetStop( var##rule ); \ } #define APPLY_FORCE_RULE( var, name, type ) \ bool Rules::apply##name( type& arg ) const \ { \ if( checkForceRule( var##rule )) \ arg = this->var; \ return checkForceStop( var##rule ); \ } APPLY_FORCE_RULE( placement, Placement, Placement::Policy ) bool Rules::applyGeometry( QRect& rect, bool init ) const { QPoint p = rect.topLeft(); QSize s = rect.size(); bool ret = false; // no short-circuiting if( applyPosition( p, init )) { rect.moveTopLeft( p ); ret = true; } if( applySize( s, init )) { rect.setSize( s ); ret = true; } return ret; } bool Rules::applyPosition( QPoint& pos, bool init ) const { if( this->position != invalidPoint && checkSetRule( positionrule, init )) pos = this->position; return checkSetStop( positionrule ); } bool Rules::applySize( QSize& s, bool init ) const { if( this->size.isValid() && checkSetRule( sizerule, init )) s = this->size; return checkSetStop( sizerule ); } APPLY_FORCE_RULE( minsize, MinSize, QSize ) APPLY_FORCE_RULE( maxsize, MaxSize, QSize ) APPLY_FORCE_RULE( opacityactive, OpacityActive, int ) APPLY_FORCE_RULE( opacityinactive, OpacityInactive, int ) APPLY_FORCE_RULE( ignoreposition, IgnorePosition, bool ) // the cfg. entry needs to stay named the say for backwards compatibility bool Rules::applyIgnoreGeometry( bool& ignore ) const { return applyIgnorePosition( ignore ); } APPLY_RULE( desktop, Desktop, int ) APPLY_FORCE_RULE( type, Type, NET::WindowType ) bool Rules::applyMaximizeHoriz( MaximizeMode& mode, bool init ) const { if( checkSetRule( maximizehorizrule, init )) mode = static_cast< MaximizeMode >(( maximizehoriz ? MaximizeHorizontal : 0 ) | ( mode & MaximizeVertical )); return checkSetStop( maximizehorizrule ); } bool Rules::applyMaximizeVert( MaximizeMode& mode, bool init ) const { if( checkSetRule( maximizevertrule, init )) mode = static_cast< MaximizeMode >(( maximizevert ? MaximizeVertical : 0 ) | ( mode & MaximizeHorizontal )); return checkSetStop( maximizevertrule ); } APPLY_RULE( minimize, Minimize, bool ) bool Rules::applyShade( ShadeMode& sh, bool init ) const { if( checkSetRule( shaderule, init )) { if( !this->shade ) sh = ShadeNone; if( this->shade && sh == ShadeNone ) sh = ShadeNormal; } return checkSetStop( shaderule ); } APPLY_RULE( skiptaskbar, SkipTaskbar, bool ) APPLY_RULE( skippager, SkipPager, bool ) APPLY_RULE( above, KeepAbove, bool ) APPLY_RULE( below, KeepBelow, bool ) APPLY_RULE( fullscreen, FullScreen, bool ) APPLY_RULE( noborder, NoBorder, bool ) APPLY_FORCE_RULE( fsplevel, FSP, int ) APPLY_FORCE_RULE( acceptfocus, AcceptFocus, bool ) APPLY_FORCE_RULE( moveresizemode, MoveResizeMode, Options::MoveResizeMode ) APPLY_FORCE_RULE( closeable, Closeable, bool ) APPLY_FORCE_RULE( strictgeometry, StrictGeometry, bool ) APPLY_RULE( shortcut, Shortcut, QString ) APPLY_FORCE_RULE( disableglobalshortcuts, DisableGlobalShortcuts, bool ) #undef APPLY_RULE #undef APPLY_FORCE_RULE bool Rules::isTemporary() const { return temporary_state > 0; } bool Rules::discardTemporary( bool force ) { if( temporary_state == 0 ) // not temporary return false; if( force || --temporary_state == 0 ) // too old { delete this; return true; } return false; } #define DISCARD_USED_SET_RULE( var ) \ do { \ if( var##rule == ( SetRule ) ApplyNow || ( withdrawn && var##rule == ( SetRule ) ForceTemporarily )) \ var##rule = UnusedSetRule; \ } while( false ) #define DISCARD_USED_FORCE_RULE( var ) \ do { \ if( withdrawn && var##rule == ( ForceRule ) ForceTemporarily ) \ var##rule = UnusedForceRule; \ } while( false ) void Rules::discardUsed( bool withdrawn ) { DISCARD_USED_FORCE_RULE( placement ); DISCARD_USED_SET_RULE( position ); DISCARD_USED_SET_RULE( size ); DISCARD_USED_FORCE_RULE( minsize ); DISCARD_USED_FORCE_RULE( maxsize ); DISCARD_USED_FORCE_RULE( opacityactive ); DISCARD_USED_FORCE_RULE( opacityinactive ); DISCARD_USED_FORCE_RULE( ignoreposition ); DISCARD_USED_SET_RULE( desktop ); DISCARD_USED_FORCE_RULE( type ); DISCARD_USED_SET_RULE( maximizevert ); DISCARD_USED_SET_RULE( maximizehoriz ); DISCARD_USED_SET_RULE( minimize ); DISCARD_USED_SET_RULE( shade ); DISCARD_USED_SET_RULE( skiptaskbar ); DISCARD_USED_SET_RULE( skippager ); DISCARD_USED_SET_RULE( above ); DISCARD_USED_SET_RULE( below ); DISCARD_USED_SET_RULE( fullscreen ); DISCARD_USED_SET_RULE( noborder ); DISCARD_USED_FORCE_RULE( fsplevel ); DISCARD_USED_FORCE_RULE( acceptfocus ); DISCARD_USED_FORCE_RULE( moveresizemode ); DISCARD_USED_FORCE_RULE( closeable ); DISCARD_USED_FORCE_RULE( strictgeometry ); DISCARD_USED_SET_RULE( shortcut ); DISCARD_USED_FORCE_RULE( disableglobalshortcuts ); } #undef DISCARD_USED_SET_RULE #undef DISCARD_USED_FORCE_RULE #endif kdbgstream& operator<<( kdbgstream& stream, const Rules* r ) { return stream << "[" << r->description << ":" << r->wmclass << "]" ; } #ifndef KCMRULES void WindowRules::discardTemporary() { QVector< Rules* >::Iterator it2 = rules.begin(); for( QVector< Rules* >::Iterator it = rules.begin(); it != rules.end(); ) { if( (*it)->discardTemporary( true )) ++it; else { *it2++ = *it++; } } rules.erase( it2, rules.end()); } void WindowRules::update( Client* c ) { bool updated = false; for( QVector< Rules* >::ConstIterator it = rules.constBegin(); it != rules.constEnd(); ++it ) if( (*it)->update( c )) // no short-circuiting here updated = true; if( updated ) Workspace::self()->rulesUpdated(); } #define CHECK_RULE( rule, type ) \ type WindowRules::check##rule( type arg, bool init ) const \ { \ if( rules.count() == 0 ) \ return arg; \ type ret = arg; \ for( QVector< Rules* >::ConstIterator it = rules.constBegin(); \ it != rules.constEnd(); \ ++it ) \ { \ if( (*it)->apply##rule( ret, init )) \ break; \ } \ return ret; \ } #define CHECK_FORCE_RULE( rule, type ) \ type WindowRules::check##rule( type arg ) const \ { \ if( rules.count() == 0 ) \ return arg; \ type ret = arg; \ for( QVector< Rules* >::ConstIterator it = rules.begin(); \ it != rules.end(); \ ++it ) \ { \ if( (*it)->apply##rule( ret )) \ break; \ } \ return ret; \ } CHECK_FORCE_RULE( Placement, Placement::Policy ) QRect WindowRules::checkGeometry( QRect rect, bool init ) const { return QRect( checkPosition( rect.topLeft(), init ), checkSize( rect.size(), init )); } CHECK_RULE( Position, QPoint ) CHECK_RULE( Size, QSize ) CHECK_FORCE_RULE( MinSize, QSize ) CHECK_FORCE_RULE( MaxSize, QSize ) CHECK_FORCE_RULE( OpacityActive, int ) CHECK_FORCE_RULE( OpacityInactive, int ) CHECK_FORCE_RULE( IgnorePosition, bool ) bool WindowRules::checkIgnoreGeometry( bool ignore ) const { return checkIgnorePosition( ignore ); } CHECK_RULE( Desktop, int ) CHECK_FORCE_RULE( Type, NET::WindowType ) CHECK_RULE( MaximizeVert, KDecorationDefines::MaximizeMode ) CHECK_RULE( MaximizeHoriz, KDecorationDefines::MaximizeMode ) KDecorationDefines::MaximizeMode WindowRules::checkMaximize( MaximizeMode mode, bool init ) const { bool vert = checkMaximizeVert( mode, init ) & MaximizeVertical; bool horiz = checkMaximizeHoriz( mode, init ) & MaximizeHorizontal; return static_cast< MaximizeMode >(( vert ? MaximizeVertical : 0 ) | ( horiz ? MaximizeHorizontal : 0 )); } CHECK_RULE( Minimize, bool ) CHECK_RULE( Shade, ShadeMode ) CHECK_RULE( SkipTaskbar, bool ) CHECK_RULE( SkipPager, bool ) CHECK_RULE( KeepAbove, bool ) CHECK_RULE( KeepBelow, bool ) CHECK_RULE( FullScreen, bool ) CHECK_RULE( NoBorder, bool ) CHECK_FORCE_RULE( FSP, int ) CHECK_FORCE_RULE( AcceptFocus, bool ) CHECK_FORCE_RULE( MoveResizeMode, Options::MoveResizeMode ) CHECK_FORCE_RULE( Closeable, bool ) CHECK_FORCE_RULE( StrictGeometry, bool ) CHECK_RULE( Shortcut, QString ) CHECK_FORCE_RULE( DisableGlobalShortcuts, bool ) #undef CHECK_RULE #undef CHECK_FORCE_RULE // Client void Client::setupWindowRules( bool ignore_temporary ) { client_rules = workspace()->findWindowRules( this, ignore_temporary ); // check only after getting the rules, because there may be a rule forcing window type if( isTopMenu()) // TODO cannot have restrictions client_rules = WindowRules(); } // Applies Force, ForceTemporarily and ApplyNow rules // Used e.g. after the rules have been modified using the kcm. void Client::applyWindowRules() { // apply force rules // Placement - does need explicit update, just like some others below // Geometry : setGeometry() doesn't check rules QRect orig_geom = QRect( pos(), sizeForClientSize( clientSize())); // handle shading QRect geom = client_rules.checkGeometry( orig_geom ); if( geom != orig_geom ) setGeometry( geom ); // MinSize, MaxSize handled by Geometry // IgnorePosition setDesktop( desktop()); // Type maximize( maximizeMode()); // Minimize : functions don't check, and there are two functions if( client_rules.checkMinimize( isMinimized())) minimize(); else unminimize(); setShade( shadeMode()); setSkipTaskbar( skipTaskbar(), true ); setSkipPager( skipPager()); setKeepAbove( keepAbove()); setKeepBelow( keepBelow()); setFullScreen( isFullScreen(), true ); setNoBorder( noBorder()); // FSP // AcceptFocus : if( workspace()->mostRecentlyActivatedClient() == this && !client_rules.checkAcceptFocus( true )) workspace()->activateNextClient( this ); // MoveResizeMode // Closeable QSize s = adjustedSize(); if( s != size()) resizeWithChecks( s ); // StrictGeometry setShortcut( rules()->checkShortcut( shortcut().toString())); // see also Client::setActive() if( isActive()) { setOpacity( rules()->checkOpacityActive( qRound( opacity() * 100.0 )) / 100.0 ); workspace()->disableGlobalShortcutsForClient( rules()->checkDisableGlobalShortcuts( false )); } else setOpacity( rules()->checkOpacityInactive( qRound( opacity() * 100.0 )) / 100.0 ); } void Client::updateWindowRules() { if( !isManaged()) // not fully setup yet return; if( workspace()->rulesUpdatesDisabled()) return; client_rules.update( this ); } void Client::finishWindowRules() { updateWindowRules(); client_rules = WindowRules(); } // Workspace WindowRules Workspace::findWindowRules( const Client* c, bool ignore_temporary ) { QVector< Rules* > ret; for( QList< Rules* >::Iterator it = rules.begin(); it != rules.end(); ) { if( ignore_temporary && (*it)->isTemporary()) { ++it; continue; } if( (*it)->match( c )) { Rules* rule = *it; kDebug( 1212 ) << "Rule found:" << rule << ":" << c; if( rule->isTemporary()) it = rules.erase( it ); else ++it; ret.append( rule ); continue; } ++it; } return WindowRules( ret ); } void Workspace::editWindowRules( Client* c, bool whole_app ) { writeWindowRules(); QStringList args; args << "--wid" << QString::number( c->window()); if( whole_app ) args << "--whole-app"; KToolInvocation::kdeinitExec( "kwin_rules_dialog", args ); } void Workspace::loadWindowRules() { while( !rules.isEmpty()) { delete rules.front(); rules.pop_front(); } KConfig cfg( "kwinrulesrc", KConfig::NoGlobals ); int count = cfg.group("General").readEntry( "count",0 ); for( int i = 1; i <= count; ++i ) { KConfigGroup cg( &cfg, QString::number( i )); Rules* rule = new Rules( cg ); rules.append( rule ); } } void Workspace::writeWindowRules() { rulesUpdatedTimer.stop(); KConfig cfg( "kwinrulesrc", KConfig::NoGlobals ); QStringList groups = cfg.groupList(); for( QStringList::ConstIterator it = groups.constBegin(); it != groups.constEnd(); ++it ) cfg.deleteGroup( *it ); cfg.group("General").writeEntry( "count", rules.count()); int i = 1; for( QList< Rules* >::ConstIterator it = rules.constBegin(); it != rules.constEnd(); ++it ) { if( (*it)->isTemporary()) continue; KConfigGroup cg( &cfg, QString::number( i )); (*it)->write( cg ); ++i; } } void Workspace::gotTemporaryRulesMessage( const QString& message ) { bool was_temporary = false; for( QList< Rules* >::ConstIterator it = rules.constBegin(); it != rules.constEnd(); ++it ) if( (*it)->isTemporary()) was_temporary = true; Rules* rule = new Rules( message, true ); rules.prepend( rule ); // highest priority first if( !was_temporary ) QTimer::singleShot( 60000, this, SLOT( cleanupTemporaryRules())); } void Workspace::cleanupTemporaryRules() { bool has_temporary = false; for( QList< Rules* >::Iterator it = rules.begin(); it != rules.end(); ) { if( (*it)->discardTemporary( false )) it = rules.erase( it ); else { if( (*it)->isTemporary()) has_temporary = true; ++it; } } if( has_temporary ) QTimer::singleShot( 60000, this, SLOT( cleanupTemporaryRules())); } void Workspace::discardUsedWindowRules( Client* c, bool withdrawn ) { bool updated = false; for( QList< Rules* >::Iterator it = rules.begin(); it != rules.end(); ) { if( c->rules()->contains( *it )) { updated = true; (*it)->discardUsed( withdrawn ); if( (*it)->isEmpty()) { c->removeRule( *it ); Rules* r = *it; it = rules.erase( it ); delete r; continue; } } ++it; } if( updated ) rulesUpdated(); } void Workspace::rulesUpdated() { rulesUpdatedTimer.setSingleShot( true ); rulesUpdatedTimer.start( 1000 ); } void Workspace::disableRulesUpdates( bool disable ) { rules_updates_disabled = disable; if( !disable ) foreach( Client* c, clients ) c->updateWindowRules(); } #endif } // namespace