diff --git a/data/Makefile.am b/data/Makefile.am index 9ec6183bf..6486eb8c0 100644 --- a/data/Makefile.am +++ b/data/Makefile.am @@ -1,20 +1,25 @@ -kconf_PROGRAMS = kwin_update_window_settings +kconf_PROGRAMS = kwin_update_window_settings kwin_update_default_rules kconfdir = $(libdir)/kconf_update_bin kwin_update_window_settings_SOURCES = update_window_settings.cpp - kwin_update_window_settings_LDADD = $(LIB_KDECORE) $(KDE_RPATH) kwin_update_window_settings_LDFLAGS = $(all_libraries) +kwin_update_default_rules_SOURCES = update_default_rules.cpp +kwin_update_default_rules_LDADD = $(LIB_KDECORE) $(KDE_RPATH) +kwin_update_default_rules_LDFLAGS = $(all_libraries) + INCLUDES = $(all_includes) METASOURCES = AUTO sounddir= $(kde_sounddir) sound_DATA = pop.wav update_DATA = kwin.upd kwinsticky.upd kwiniconify.upd kwin3_plugin.upd kwin_focus1.upd \ - kwinupdatewindowsettings.upd kwin_focus2.upd + kwinupdatewindowsettings.upd kwin_focus2.upd kwin_fsp_workarounds_1.upd update_SCRIPTS = pluginlibFix.pl kwin3_plugin.pl kwin_focus1.sh kwin_focus2.sh - updatedir = $(kde_datadir)/kconf_update + +kwin_default_rules_DATA = fsp_workarounds_1 +kwin_default_rulesdir = $(kde_datadir)/kwin/default_rules diff --git a/kcmkwin/kwinrules/main.cpp b/kcmkwin/kwinrules/main.cpp index 82966a953..0c2263586 100644 --- a/kcmkwin/kwinrules/main.cpp +++ b/kcmkwin/kwinrules/main.cpp @@ -1,290 +1,295 @@ /* * 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #include #include "ruleswidget.h" #include "../../rules.h" #include namespace KWinInternal { static void loadRules( QList< Rules* >& rules ) { KConfig cfg( "kwinrulesrc", true ); cfg.setGroup( "General" ); int count = cfg.readEntry( "count",0 ); for( int i = 1; i <= count; ++i ) { cfg.setGroup( QString::number( i )); Rules* rule = new Rules( cfg ); rules.append( rule ); } } static void saveRules( const QList< Rules* >& rules ) { KConfig cfg( "kwinrulesrc" ); + QStringList groups = cfg.groupList(); + for( QStringList::ConstIterator it = groups.begin(); + it != groups.end(); + ++it ) + cfg.deleteGroup( *it ); cfg.setGroup( "General" ); cfg.writeEntry( "count", rules.count()); int i = 1; for( QList< Rules* >::ConstIterator it = rules.begin(); it != rules.end(); ++it ) { cfg.setGroup( QString::number( i )); (*it)->write( cfg ); ++i; } } static Rules* findRule( const QList< Rules* >& rules, Window wid, bool whole_app ) { KWin::WindowInfo info = KWin::windowInfo( wid, NET::WMName | NET::WMWindowType, NET::WM2WindowClass | NET::WM2WindowRole | NET::WM2ClientMachine ); if( !info.valid()) // shouldn't really happen return NULL; QByteArray wmclass_class = info.windowClassClass().lower(); QByteArray wmclass_name = info.windowClassName().lower(); QByteArray role = info.windowRole().lower(); NET::WindowType type = info.windowType( NET::NormalMask | NET::DesktopMask | NET::DockMask | NET::ToolbarMask | NET::MenuMask | NET::DialogMask | NET::OverrideMask | NET::TopMenuMask | NET::UtilityMask | NET::SplashMask ); QString title = info.name(); // QCString extrarole = ""; // TODO QByteArray machine = info.clientMachine().lower(); Rules* best_match = NULL; int match_quality = 0; for( QList< Rules* >::ConstIterator it = rules.begin(); it != rules.end(); ++it ) { // try to find an exact match, i.e. not a generic rule Rules* rule = *it; int quality = 0; bool generic = true; if( rule->wmclassmatch != Rules::ExactMatch ) continue; // too generic if( !rule->matchWMClass( wmclass_class, wmclass_name )) continue; // from now on, it matches the app - now try to match for a specific window if( rule->wmclasscomplete ) { quality += 1; generic = false; // this can be considered specific enough (old X apps) } if( !whole_app ) { if( rule->windowrolematch != Rules::UnimportantMatch ) { quality += rule->windowrolematch == Rules::ExactMatch ? 5 : 1; generic = false; } if( rule->titlematch != Rules::UnimportantMatch ) { quality += rule->titlematch == Rules::ExactMatch ? 3 : 1; generic = false; } if( rule->types != NET::AllTypesMask ) { int bits = 0; for( int bit = 1; bit < 1 << 31; bit <<= 1 ) if( rule->types & bit ) ++bits; if( bits == 1 ) quality += 2; } if( generic ) // ignore generic rules, use only the ones that are for this window continue; } else { if( rule->types == NET::AllTypesMask ) quality += 2; } if( !rule->matchType( type ) || !rule->matchRole( role ) || !rule->matchTitle( title ) || !rule->matchClientMachine( machine )) continue; if( quality > match_quality ) { best_match = rule; match_quality = quality; } } if( best_match != NULL ) return best_match; Rules* ret = new Rules; if( whole_app ) { ret->description = i18n( "Application settings for %1" ).arg( QString::fromLatin1( wmclass_class )); // TODO maybe exclude some types? If yes, then also exclude them above // when searching. ret->types = NET::AllTypesMask; ret->titlematch = Rules::UnimportantMatch; ret->clientmachine = machine; // set, but make unimportant ret->clientmachinematch = Rules::UnimportantMatch; ret->extrarolematch = Rules::UnimportantMatch; ret->windowrolematch = Rules::UnimportantMatch; if( wmclass_name == wmclass_class ) { ret->wmclasscomplete = false; ret->wmclass = wmclass_class; ret->wmclassmatch = Rules::ExactMatch; } else { // WM_CLASS components differ - perhaps the app got -name argument ret->wmclasscomplete = true; ret->wmclass = wmclass_name + ' ' + wmclass_class; ret->wmclassmatch = Rules::ExactMatch; } return ret; } ret->description = i18n( "Window settings for %1" ).arg( QString::fromLatin1( wmclass_class )); if( type == NET::Unknown ) ret->types = NET::NormalMask; else ret->types = 1 << type; // convert type to its mask ret->title = title; // set, but make unimportant ret->titlematch = Rules::UnimportantMatch; ret->clientmachine = machine; // set, but make unimportant ret->clientmachinematch = Rules::UnimportantMatch; // ret->extrarole = extra; TODO ret->extrarolematch = Rules::UnimportantMatch; if( !role.isEmpty() && role != "unknown" && role != "unnamed" ) // Qt sets this if not specified { ret->windowrole = role; ret->windowrolematch = Rules::ExactMatch; if( wmclass_name == wmclass_class ) { ret->wmclasscomplete = false; ret->wmclass = wmclass_class; ret->wmclassmatch = Rules::ExactMatch; } else { // WM_CLASS components differ - perhaps the app got -name argument ret->wmclasscomplete = true; ret->wmclass = wmclass_name + ' ' + wmclass_class; ret->wmclassmatch = Rules::ExactMatch; } } else // no role set { if( wmclass_name != wmclass_class ) { ret->wmclasscomplete = true; ret->wmclass = wmclass_name + ' ' + wmclass_class; ret->wmclassmatch = Rules::ExactMatch; } else { // This is a window that has no role set, and both components of WM_CLASS // match (possibly only differing in case), which most likely means either // the application doesn't give a damn about distinguishing its various // windows, or it's an app that uses role for that, but this window // lacks it for some reason. Use non-complete WM_CLASS matching, also // include window title in the matching, and pray it causes many more positive // matches than negative matches. ret->titlematch = Rules::ExactMatch; ret->wmclasscomplete = false; ret->wmclass = wmclass_class; ret->wmclassmatch = Rules::ExactMatch; } } return ret; } static int edit( Window wid, bool whole_app ) { QList< Rules* > rules; loadRules( rules ); Rules* orig_rule = findRule( rules, wid, whole_app ); RulesDialog dlg; // dlg.edit() creates new Rules instance if edited Rules* edited_rule = dlg.edit( orig_rule, wid, true ); if( edited_rule == NULL || edited_rule->isEmpty()) { rules.remove( orig_rule ); delete orig_rule; if( orig_rule != edited_rule ) delete edited_rule; } else if( edited_rule != orig_rule ) { QList< Rules* >::Iterator pos = rules.find( orig_rule ); if( pos != rules.end()) *pos = edited_rule; else rules.prepend( edited_rule ); delete orig_rule; } saveRules( rules ); if( !kapp->dcopClient()->isAttached()) kapp->dcopClient()->attach(); kapp->dcopClient()->send("kwin*", "", "reconfigure()", QByteArray()); return 0; } } // namespace static const KCmdLineOptions options[] = { // no need for I18N_NOOP(), this is not supposed to be used directly { "wid ", "WId of the window for special window settings.", 0 }, { "whole-app", "Whether the settings should affect all windows of the application.", 0 }, KCmdLineLastOption }; extern "C" KDE_EXPORT int kdemain( int argc, char* argv[] ) { KLocale::setMainCatalog( "kcmkwinrules" ); KCmdLineArgs::init( argc, argv, "kwin_rules_dialog", I18N_NOOP( "KWin" ), I18N_NOOP( "KWin helper utility" ), "1.0" ); KCmdLineArgs::addCmdLineOptions( options ); KApplication app; KCmdLineArgs* args = KCmdLineArgs::parsedArgs(); bool id_ok = false; Window id = args->getOption( "wid" ).toULongLong( &id_ok ); bool whole_app = args->isSet( "whole-app" ); args->clear(); if( !id_ok || id == None ) { KCmdLineArgs::usage( i18n( "This helper utility is not supposed to be called directly." )); return 1; } return KWinInternal::edit( id, whole_app ); } diff --git a/kcmkwin/kwinrules/ruleslist.cpp b/kcmkwin/kwinrules/ruleslist.cpp index 6aa980405..94c748d00 100644 --- a/kcmkwin/kwinrules/ruleslist.cpp +++ b/kcmkwin/kwinrules/ruleslist.cpp @@ -1,195 +1,200 @@ /* * 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "ruleslist.h" #include #include #include #include #include #include "ruleswidget.h" namespace KWinInternal { KCMRulesList::KCMRulesList( QWidget* parent, const char* name ) : KCMRulesListBase( parent, name ) { // connect both current/selected, so that current==selected (stupid QListBox :( ) connect( rules_listbox, SIGNAL( currentChanged( Q3ListBoxItem* )), SLOT( activeChanged( Q3ListBoxItem*))); connect( rules_listbox, SIGNAL( selectionChanged( Q3ListBoxItem* )), SLOT( activeChanged( Q3ListBoxItem*))); connect( new_button, SIGNAL( clicked()), SLOT( newClicked())); connect( modify_button, SIGNAL( clicked()), SLOT( modifyClicked())); connect( delete_button, SIGNAL( clicked()), SLOT( deleteClicked())); connect( moveup_button, SIGNAL( clicked()), SLOT( moveupClicked())); connect( movedown_button, SIGNAL( clicked()), SLOT( movedownClicked())); connect( rules_listbox, SIGNAL( doubleClicked ( Q3ListBoxItem * ) ), SLOT( modifyClicked())); load(); } KCMRulesList::~KCMRulesList() { for( QVector< Rules* >::Iterator it = rules.begin(); it != rules.end(); ++it ) delete *it; rules.clear(); } void KCMRulesList::activeChanged( Q3ListBoxItem* item ) { if( item != NULL ) rules_listbox->setSelected( item, true ); // make current==selected modify_button->setEnabled( item != NULL ); delete_button->setEnabled( item != NULL ); moveup_button->setEnabled( item != NULL && item->prev() != NULL ); movedown_button->setEnabled( item != NULL && item->next() != NULL ); } void KCMRulesList::newClicked() { RulesDialog dlg; Rules* rule = dlg.edit( NULL, 0, false ); if( rule == NULL ) return; int pos = rules_listbox->currentItem() + 1; rules_listbox->insertItem( rule->description, pos ); rules_listbox->setSelected( pos, true ); rules.insert( rules.begin() + pos, rule ); emit changed( true ); } void KCMRulesList::modifyClicked() { int pos = rules_listbox->currentItem(); if ( pos == -1 ) return; RulesDialog dlg; Rules* rule = dlg.edit( rules[ pos ], 0, false ); if( rule == rules[ pos ] ) return; delete rules[ pos ]; rules[ pos ] = rule; rules_listbox->changeItem( rule->description, pos ); emit changed( true ); } void KCMRulesList::deleteClicked() { int pos = rules_listbox->currentItem(); assert( pos != -1 ); rules_listbox->removeItem( pos ); rules.erase( rules.begin() + pos ); emit changed( true ); } void KCMRulesList::moveupClicked() { int pos = rules_listbox->currentItem(); assert( pos != -1 ); if( pos > 0 ) { QString txt = rules_listbox->text( pos ); rules_listbox->removeItem( pos ); rules_listbox->insertItem( txt, pos - 1 ); rules_listbox->setSelected( pos - 1, true ); Rules* rule = rules[ pos ]; rules[ pos ] = rules[ pos - 1 ]; rules[ pos - 1 ] = rule; } emit changed( true ); } void KCMRulesList::movedownClicked() { int pos = rules_listbox->currentItem(); assert( pos != -1 ); if( pos < int( rules_listbox->count()) - 1 ) { QString txt = rules_listbox->text( pos ); rules_listbox->removeItem( pos ); rules_listbox->insertItem( txt, pos + 1 ); rules_listbox->setSelected( pos + 1, true ); Rules* rule = rules[ pos ]; rules[ pos ] = rules[ pos + 1 ]; rules[ pos + 1 ] = rule; } emit changed( true ); } void KCMRulesList::load() { rules_listbox->clear(); for( QVector< Rules* >::Iterator it = rules.begin(); it != rules.end(); ++it ) delete *it; rules.clear(); KConfig cfg( "kwinrulesrc", true ); cfg.setGroup( "General" ); int count = cfg.readEntry( "count",0 ); rules.reserve( count ); for( int i = 1; i <= count; ++i ) { cfg.setGroup( QString::number( i )); Rules* rule = new Rules( cfg ); rules.append( rule ); rules_listbox->insertItem( rule->description ); } if( rules.count() > 0 ) rules_listbox->setSelected( 0, true ); else activeChanged( NULL ); } void KCMRulesList::save() { KConfig cfg( "kwinrulesrc" ); + QStringList groups = cfg.groupList(); + for( QStringList::ConstIterator it = groups.begin(); + it != groups.end(); + ++it ) + cfg.deleteGroup( *it ); cfg.setGroup( "General" ); cfg.writeEntry( "count", rules.count()); int i = 1; for( QVector< Rules* >::ConstIterator it = rules.begin(); it != rules.end(); ++it ) { cfg.setGroup( QString::number( i )); (*it)->write( cfg ); ++i; } } void KCMRulesList::defaults() { load(); } } // namespace #include "ruleslist.moc" diff --git a/rules.cpp b/rules.cpp index 4f440a93a..bb7b71fbf 100644 --- a/rules.cpp +++ b/rules.cpp @@ -1,1056 +1,1061 @@ /***************************************************************** 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 #include #include #include #include #ifndef KCMRULES #include "client.h" #include "workspace.h" #endif namespace KWinInternal { 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 ) { KTempFile file; QFile* f = file.file(); if( f != NULL ) { QByteArray s = str.toUtf8(); f->writeBlock( s.data(), s.length()); } file.close(); KSimpleConfig cfg( file.name()); readFromCfg( cfg ); if( description.isEmpty()) description = "temporary"; file.unlink(); } #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( KConfig& cfg ) : temporary_state( 0 ) { readFromCfg( cfg ); } static int limit0to4( int i ) { return QMAX( 0, QMIN( 4, i )); } void Rules::readFromCfg( KConfig& cfg ) { description = cfg.readEntry( "description" ); READ_MATCH_STRING( wmclass, .lower().latin1() ); wmclasscomplete = cfg.readEntry( "wmclasscomplete" , QVariant(false)).toBool(); READ_MATCH_STRING( windowrole, .lower().latin1() ); READ_MATCH_STRING( title, ); READ_MATCH_STRING( extrarole, .lower().latin1() ); READ_MATCH_STRING( clientmachine, .lower().latin1() ); types = cfg.readUnsignedLongNumEntry( "types", 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" ); \ } #define WRITE_WITH_DEFAULT( var, default ) \ if( var != default ) \ cfg.writeEntry( #var, var ); \ else \ cfg.deleteEntry( #var ); void Rules::write( KConfig& 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 ); WRITE_WITH_DEFAULT( QVariant::fromValue(types), static_cast(NET::AllTypesMask) ); 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 #undef WRITE_WITH_DEFAULT // 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( KConfig& 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( KConfig& 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( KConfig& 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 ).search( 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 ).search( 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 ).search( 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 ).search( 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) { updated = updated || (uint) (opacityactive/100.0*0xffffffff) != c->ruleOpacityActive(); opacityactive = (uint)(((double)c->ruleOpacityActive())/0xffffffff*100); } if (opacityinactiverule == ( ForceRule )Force) { updated = updated || (uint) (opacityinactive/100.0*0xffffffff) != c->ruleOpacityInactive(); opacityinactive = (uint)(((double)c->ruleOpacityInactive())/0xffffffff*100); } 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() { checkAndSetInitialRuledOpacity(); // 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; client_rules.update( this ); } void Client::finishWindowRules() { updateWindowRules(); client_rules = WindowRules(); } void Client::checkAndSetInitialRuledOpacity() //apply kwin-rules for window-translucency upon hitting apply or starting to manage client { int tmp; //active translucency tmp = -1; tmp = rules()->checkOpacityActive(tmp); if( tmp != -1 ) //rule did apply and returns valid value { rule_opacity_active = (uint)((tmp/100.0)*0xffffffff); } else rule_opacity_active = 0; //inactive translucency tmp = -1; tmp = rules()->checkOpacityInactive(tmp); if( tmp != -1 ) //rule did apply and returns valid value { rule_opacity_inactive = (uint)((tmp/100.0)*0xffffffff); } else rule_opacity_inactive = 0; return; if( isDock() ) //workaround for docks, as they don't have active/inactive settings and don't aut, therefore we take only the active one... { uint tmp = rule_opacity_active ? rule_opacity_active : options->dockOpacity; setOpacity(tmp < 0xFFFFFFFF && (rule_opacity_active || options->translucentDocks), tmp); } else updateOpacity(); } // 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 << endl; if( rule->isTemporary()) it = rules.remove( 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", true ); cfg.setGroup( "General" ); int count = cfg.readEntry( "count",0 ); for( int i = 1; i <= count; ++i ) { cfg.setGroup( QString::number( i )); Rules* rule = new Rules( cfg ); 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.setGroup( "General" ); cfg.writeEntry( "count", rules.count()); int i = 1; for( QList< Rules* >::ConstIterator it = rules.begin(); it != rules.end(); ++it ) { if( (*it)->isTemporary()) continue; cfg.setGroup( QString::number( i )); (*it)->write( cfg ); ++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.remove( 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.remove( it ); delete r; continue; } } ++it; } if( updated ) rulesUpdated(); } void Workspace::rulesUpdated() { rulesUpdatedTimer.start( 1000, true ); } #endif } // namespace