You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1024 lines
32 KiB
1024 lines
32 KiB
/***************************************************************** |
|
KWin - the KDE window manager |
|
This file is part of the KDE project. |
|
|
|
Copyright (C) 2004 Lubos Lunak <l.lunak@kde.org> |
|
|
|
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 <fixx11h.h> |
|
#include <kconfig.h> |
|
#include <QRegExp> |
|
#include <ktemporaryfile.h> |
|
#include <QFile> |
|
#include <ktoolinvocation.h> |
|
|
|
#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::OnlyLocal); |
|
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->isUserNoBorder(); |
|
noborder = c->isUserNoBorder(); |
|
} |
|
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 |
|
|
|
#ifndef NDEBUG |
|
kdbgstream& operator<<( kdbgstream& stream, const Rules* r ) |
|
{ |
|
return stream << "[" << r->description << ":" << r->wmclass << "]" ; |
|
} |
|
#endif |
|
|
|
#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.begin(); |
|
it != rules.end(); |
|
++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.begin(); \ |
|
it != rules.end(); \ |
|
++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 ); |
|
setUserNoBorder( isUserNoBorder()); |
|
// 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()) |
|
workspace()->disableGlobalShortcutsForClient( rules()->checkDisableGlobalShortcuts( false )); |
|
} |
|
|
|
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" ); |
|
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" ); |
|
QStringList groups = cfg.groupList(); |
|
for( QStringList::ConstIterator it = groups.begin(); |
|
it != groups.end(); |
|
++it ) |
|
cfg.deleteGroup( *it ); |
|
cfg.group("General").writeEntry( "count", rules.count()); |
|
int i = 1; |
|
for( QList< Rules* >::ConstIterator it = rules.begin(); |
|
it != rules.end(); |
|
++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.begin(); |
|
it != rules.end(); |
|
++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
|
|
|