kwin/kcmkwin/kwinrules/ruleswidget.cpp
Luboš Luňák 3966017ccd Fill (or pre-fill) more values from the detect dialog for matching the window.
svn path=/trunk/kdebase/kwin/; revision=334154
2004-07-30 13:35:55 +00:00

604 lines
20 KiB
C++

/*
* Copyright (c) 2004 Lubos Lunak <l.lunak@kde.org>
*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "ruleswidget.h"
#include <klineedit.h>
#include <krestrictedline.h>
#include <kcombobox.h>
#include <qcheckbox.h>
#include <kpushbutton.h>
#include <qlabel.h>
#include <kwinmodule.h>
#include <klocale.h>
#include <qregexp.h>
#include <qwhatsthis.h>
#include <assert.h>
#include <kmessagebox.h>
#include "../../rules.h"
#include "detectwidget.h"
namespace KWinInternal
{
#define SETUP( var, type ) \
connect( enable_##var, SIGNAL( toggled( bool )), rule_##var, SLOT( setEnabled( bool ))); \
connect( enable_##var, SIGNAL( toggled( bool )), this, SLOT( updateEnable##var())); \
connect( rule_##var, SIGNAL( activated( int )), this, SLOT( updateEnable##var())); \
QWhatsThis::add( enable_##var, enableDesc ); \
QWhatsThis::add( rule_##var, type##RuleDesc );
RulesWidget::RulesWidget( QWidget* parent, const char* name )
: RulesWidgetBase( parent, name )
, detect_dlg( NULL )
{
QString enableDesc =
i18n( "Enable this checkbox to alter this window property for the specified window(s)." );
QString setRuleDesc =
i18n( "Specify how the window property should be affected:<ul>"
"<li><em>Do Not Affect:</em> The window property will not be affected and therefore"
" the default handling for it will be used. Specifying this will block more generic"
" window settings from taking effect.</li>"
"<li><em>Apply Initially:</em> The window property will be only set to the given value"
" after the window is created. No further changes will be affected.</li>"
"<li><em>Remember:</em> The value of the window property will be remembered and every time"
" time the window is created, the last remembered value will be applied.</li>"
"<li><em>Force:</em> The window property will be always forced to the given value.</li></ul>" );
QString forceRuleDesc =
i18n( "Specify how the window property should be affected:<ul>"
"<li><em>Do Not Affect:</em> The window property will not be affected and therefore"
" the default handling for it will be used. Specifying this will block more generic"
" window settings from taking effect.</li>"
"<li><em>Force:</em> The window property will be always forced to the given value.</li></ul>" );
// window tabs have enable signals done in designer
// geometry tab
SETUP( position, set );
SETUP( size, set );
SETUP( desktop, set );
SETUP( maximizehoriz, set );
SETUP( maximizevert, set );
SETUP( minimize, set );
SETUP( shade, set );
SETUP( fullscreen, set );
SETUP( placement, force );
// preferences tab
SETUP( above, set );
SETUP( below, set );
SETUP( noborder, set );
SETUP( skiptaskbar, set );
SETUP( skippager, set );
SETUP( acceptfocus, force );
SETUP( closeable, force );
// workarounds tab
SETUP( fsplevel, force );
SETUP( moveresizemode, force );
SETUP( type, force );
SETUP( ignoreposition, force );
SETUP( minsize, force );
SETUP( maxsize, force );
KWinModule module;
int i;
for( i = 1;
i <= module.numberOfDesktops();
++i )
desktop->insertItem( QString::number( i ).rightJustify( 2 ) + ":" + module.desktopName( i ));
for(;
i <= 16;
++i )
desktop->insertItem( QString::number( i ).rightJustify( 2 ));
desktop->insertItem( i18n( "All Desktops" ));
}
#undef SETUP
#define UPDATE_ENABLE_SLOT( var ) \
void RulesWidget::updateEnable##var() \
{ \
/* leave the label readable label_##var->setEnabled( enable_##var->isChecked() && rule_##var->currentItem() != 0 );*/ \
var->setEnabled( enable_##var->isChecked() && rule_##var->currentItem() != 0 ); \
}
// geometry tab
UPDATE_ENABLE_SLOT( position )
UPDATE_ENABLE_SLOT( size )
UPDATE_ENABLE_SLOT( desktop )
UPDATE_ENABLE_SLOT( maximizehoriz )
UPDATE_ENABLE_SLOT( maximizevert )
UPDATE_ENABLE_SLOT( minimize )
UPDATE_ENABLE_SLOT( shade )
UPDATE_ENABLE_SLOT( fullscreen )
UPDATE_ENABLE_SLOT( placement )
// preferences tab
UPDATE_ENABLE_SLOT( above )
UPDATE_ENABLE_SLOT( below )
UPDATE_ENABLE_SLOT( noborder )
UPDATE_ENABLE_SLOT( skiptaskbar )
UPDATE_ENABLE_SLOT( skippager )
UPDATE_ENABLE_SLOT( acceptfocus )
UPDATE_ENABLE_SLOT( closeable )
// workarounds tab
UPDATE_ENABLE_SLOT( fsplevel )
UPDATE_ENABLE_SLOT( moveresizemode )
UPDATE_ENABLE_SLOT( type )
UPDATE_ENABLE_SLOT( ignoreposition )
UPDATE_ENABLE_SLOT( minsize )
UPDATE_ENABLE_SLOT( maxsize )
#undef UPDATE_ENABLE_SLOT
static const int set_rule_to_combo[] =
{
0, // Unused
0, // Don't Affect
3, // Force
1, // Apply
2, // Remember
};
static const Rules::SetRule combo_to_set_rule[] =
{
( Rules::SetRule )Rules::DontAffect,
( Rules::SetRule )Rules::Apply,
( Rules::SetRule )Rules::Remember,
( Rules::SetRule )Rules::Force
};
static const int force_rule_to_combo[] =
{
0, // Unused
0, // Don't Affect
1 // Force
};
static const Rules::ForceRule combo_to_force_rule[] =
{
( Rules::ForceRule )Rules::DontAffect,
( Rules::ForceRule )Rules::Force
};
static QString positionToStr( const QPoint& p )
{
if( p == invalidPoint )
return QString::null;
return QString::number( p.x()) + "," + QString::number( p.y());
}
static QPoint strToPosition( const QString& str )
{ // two numbers, with + or -, separated by any of , x X :
QRegExp reg( "\\s*([+-]?[0-9]*)\\s*[,xX:]\\s*([+-]?[0-9]*)\\s*" );
if( !reg.exactMatch( str ))
return invalidPoint;
return QPoint( reg.cap( 1 ).toInt(), reg.cap( 2 ).toInt());
}
static QString sizeToStr( const QSize& s )
{
if( !s.isValid())
return QString::null;
return QString::number( s.width()) + "," + QString::number( s.height());
}
static QSize strToSize( const QString& str )
{ // two numbers, with + or -, separated by any of , x X :
QRegExp reg( "\\s*([+-]?[0-9]*)\\s*[,xX:]\\s*([+-]?[0-9]*)\\s*" );
if( !reg.exactMatch( str ))
return QSize();
return QSize( reg.cap( 1 ).toInt(), reg.cap( 2 ).toInt());
}
static int desktopToCombo( int desktop )
{
if( desktop >= 1 && desktop <= 16 )
return desktop - 1;
return 16; // on all desktops
}
static int comboToDesktop( int val )
{
if( val == 16 )
return NET::OnAllDesktops;
return val + 1;
}
static int placementToCombo( Placement::Policy placement )
{
static const int conv[] =
{
1, // NoPlacement
0, // Default
5, // Random
2, // Smart
3, // Cascade
4, // Centered
6, // ZeroCornered
7, // UnderMouse
8 // OnMainWindow
};
return conv[ placement ];
}
static Placement::Policy comboToPlacement( int val )
{
static const Placement::Policy conv[] =
{
Placement::Default,
Placement::NoPlacement,
Placement::Smart,
Placement::Cascade,
Placement::Centered,
Placement::Random,
Placement::ZeroCornered,
Placement::UnderMouse,
Placement::OnMainWindow
};
return conv[ val ];
}
static int moveresizeToCombo( Options::MoveResizeMode mode )
{
return mode == Options::Opaque ? 0 : 1;
}
static Options::MoveResizeMode comboToMoveResize( int val )
{
return val == 0 ? Options::Opaque : Options::Transparent;
}
static int typeToCombo( NET::WindowType type )
{
if( type < NET::Normal || type > NET::Splash )
return 0; // Normal
static const int conv[] =
{
0, // Normal
7, // Desktop
3, // Dock
4, // Toolbar
5, // Menu
1, // Dialog
8, // Override
9, // TopMenu
2, // Utility
6 // Splash
};
return conv[ type ];
}
static NET::WindowType comboToType( int val )
{
static const NET::WindowType conv[] =
{
NET::Normal,
NET::Dialog,
NET::Utility,
NET::Dock,
NET::Toolbar,
NET::Menu,
NET::Splash,
NET::Desktop,
NET::Override,
NET::TopMenu
};
return conv[ val ];
}
#define GENERIC_RULE( var, func, Type, type, uimethod, uimethod0 ) \
if( rules->var##rule == Rules::Unused##Type##Rule ) \
{ \
enable_##var->setChecked( false ); \
rule_##var->setCurrentItem( 0 ); \
var->uimethod0; \
updateEnable##var(); \
} \
else \
{ \
enable_##var->setChecked( true ); \
rule_##var->setCurrentItem( type##_rule_to_combo[ rules->var##rule ] ); \
var->uimethod( func( rules->var )); \
updateEnable##var(); \
}
#define CHECKBOX_SET_RULE( var, func ) GENERIC_RULE( var, func, Set, set, setChecked, setChecked( false ))
#define LINEEDIT_SET_RULE( var, func ) GENERIC_RULE( var, func, Set, set, setText, setText( "" ))
#define COMBOBOX_SET_RULE( var, func ) GENERIC_RULE( var, func, Set, set, setCurrentItem, setCurrentItem( 0 ))
#define CHECKBOX_FORCE_RULE( var, func ) GENERIC_RULE( var, func, Force, force, setChecked, setChecked( false ))
#define LINEEDIT_FORCE_RULE( var, func ) GENERIC_RULE( var, func, Force, force, setText, setText( "" ))
#define COMBOBOX_FORCE_RULE( var, func ) GENERIC_RULE( var, func, Force, force, setCurrentItem, setCurrentItem( 0 ))
void RulesWidget::setRules( Rules* rules )
{
Rules tmp;
if( rules == NULL )
rules = &tmp; // empty
description->setText( rules->description );
wmclass->setText( rules->wmclass );
whole_wmclass->setChecked( rules->wmclasscomplete );
wmclass_match->setCurrentItem( rules->wmclassmatch );
wmclassMatchChanged();
role->setText( rules->windowrole );
role_match->setCurrentItem( rules->windowrolematch );
roleMatchChanged();
types->setSelected( 0, rules->types & NET::NormalMask );
types->setSelected( 1, rules->types & NET::DialogMask );
types->setSelected( 2, rules->types & NET::UtilityMask );
types->setSelected( 3, rules->types & NET::DockMask );
types->setSelected( 4, rules->types & NET::ToolbarMask );
types->setSelected( 5, rules->types & NET::MenuMask );
types->setSelected( 6, rules->types & NET::SplashMask );
types->setSelected( 7, rules->types & NET::DesktopMask );
types->setSelected( 8, rules->types & NET::OverrideMask );
types->setSelected( 9, rules->types & NET::TopMenuMask );
title->setText( rules->title );
title_match->setCurrentItem( rules->titlematch );
titleMatchChanged();
extra->setText( rules->extrarole );
extra_match->setCurrentItem( rules->extrarolematch );
extraMatchChanged();
machine->setText( rules->clientmachine );
machine_match->setCurrentItem( rules->clientmachinematch );
machineMatchChanged();
LINEEDIT_SET_RULE( position, positionToStr );
LINEEDIT_SET_RULE( size, sizeToStr );
COMBOBOX_SET_RULE( desktop, desktopToCombo );
CHECKBOX_SET_RULE( maximizehoriz, );
CHECKBOX_SET_RULE( maximizevert, );
CHECKBOX_SET_RULE( minimize, );
CHECKBOX_SET_RULE( shade, );
CHECKBOX_SET_RULE( fullscreen, );
COMBOBOX_FORCE_RULE( placement, placementToCombo );
CHECKBOX_SET_RULE( above, );
CHECKBOX_SET_RULE( below, );
CHECKBOX_SET_RULE( noborder, );
CHECKBOX_SET_RULE( skiptaskbar, );
CHECKBOX_SET_RULE( skippager, );
CHECKBOX_FORCE_RULE( acceptfocus, );
CHECKBOX_FORCE_RULE( closeable, );
COMBOBOX_FORCE_RULE( fsplevel, );
COMBOBOX_FORCE_RULE( moveresizemode, moveresizeToCombo );
COMBOBOX_FORCE_RULE( type, typeToCombo );
CHECKBOX_FORCE_RULE( ignoreposition, );
LINEEDIT_FORCE_RULE( minsize, sizeToStr );
LINEEDIT_FORCE_RULE( maxsize, sizeToStr );
}
#undef GENERIC_RULE
#undef CHECKBOX_SET_RULE
#undef LINEEDIT_SET_RULE
#undef COMBOBOX_SET_RULE
#undef CHECKBOX_FORCE_RULE
#undef LINEEDIT_FORCE_RULE
#undef COMBOBOX_FORCE_RULE
#define GENERIC_RULE( var, func, Type, type, uimethod ) \
if( enable_##var->isChecked()) \
{ \
rules->var##rule = combo_to_##type##_rule[ rule_##var->currentItem() ]; \
rules->var = func( var->uimethod()); \
} \
else \
rules->var##rule = Rules::Unused##Type##Rule;
#define CHECKBOX_SET_RULE( var, func ) GENERIC_RULE( var, func, Set, set, isChecked )
#define LINEEDIT_SET_RULE( var, func ) GENERIC_RULE( var, func, Set, set, text )
#define COMBOBOX_SET_RULE( var, func ) GENERIC_RULE( var, func, Set, set, currentItem )
#define CHECKBOX_FORCE_RULE( var, func ) GENERIC_RULE( var, func, Force, force, isChecked )
#define LINEEDIT_FORCE_RULE( var, func ) GENERIC_RULE( var, func, Force, force, text )
#define COMBOBOX_FORCE_RULE( var, func ) GENERIC_RULE( var, func, Force, force, currentItem )
Rules* RulesWidget::rules() const
{
Rules* rules = new Rules();
rules->description = description->text();
rules->wmclass = wmclass->text().utf8();
rules->wmclasscomplete = whole_wmclass->isChecked();
rules->wmclassmatch = static_cast< Rules::StringMatch >( wmclass_match->currentItem());
rules->windowrole = role->text().utf8();
rules->windowrolematch = static_cast< Rules::StringMatch >( role_match->currentItem());
rules->types = 0;
bool all_types = true;
for( unsigned int i = 0;
i < types->count();
++i )
if( !types->isSelected( i ))
all_types = false;
if( all_types ) // if all types are selected, use AllTypesMask (for future expansion)
rules->types = NET::AllTypesMask;
else
{
rules->types |= types->isSelected( 0 ) ? NET::NormalMask : 0;
rules->types |= types->isSelected( 1 ) ? NET::DialogMask : 0;
rules->types |= types->isSelected( 2 ) ? NET::UtilityMask : 0;
rules->types |= types->isSelected( 3 ) ? NET::DockMask : 0;
rules->types |= types->isSelected( 4 ) ? NET::ToolbarMask : 0;
rules->types |= types->isSelected( 5 ) ? NET::MenuMask : 0;
rules->types |= types->isSelected( 6 ) ? NET::SplashMask : 0;
rules->types |= types->isSelected( 7 ) ? NET::DesktopMask : 0;
rules->types |= types->isSelected( 8 ) ? NET::OverrideMask : 0;
rules->types |= types->isSelected( 9 ) ? NET::TopMenuMask : 0;
}
rules->title = title->text();
rules->titlematch = static_cast< Rules::StringMatch >( title_match->currentItem());
rules->extrarole = extra->text().utf8();
rules->extrarolematch = static_cast< Rules::StringMatch >( extra_match->currentItem());
rules->clientmachine = machine->text().utf8();
rules->clientmachinematch = static_cast< Rules::StringMatch >( machine_match->currentItem());
LINEEDIT_SET_RULE( position, strToPosition );
LINEEDIT_SET_RULE( size, strToSize );
COMBOBOX_SET_RULE( desktop, comboToDesktop );
CHECKBOX_SET_RULE( maximizehoriz, );
CHECKBOX_SET_RULE( maximizevert, );
CHECKBOX_SET_RULE( minimize, );
CHECKBOX_SET_RULE( shade, );
CHECKBOX_SET_RULE( fullscreen, );
COMBOBOX_FORCE_RULE( placement, comboToPlacement );
CHECKBOX_SET_RULE( above, );
CHECKBOX_SET_RULE( below, );
CHECKBOX_SET_RULE( noborder, );
CHECKBOX_SET_RULE( skiptaskbar, );
CHECKBOX_SET_RULE( skippager, );
CHECKBOX_FORCE_RULE( acceptfocus, );
CHECKBOX_FORCE_RULE( closeable, );
COMBOBOX_FORCE_RULE( fsplevel, );
COMBOBOX_FORCE_RULE( moveresizemode, comboToMoveResize );
COMBOBOX_FORCE_RULE( type, comboToType );
CHECKBOX_FORCE_RULE( ignoreposition, );
LINEEDIT_FORCE_RULE( minsize, strToSize );
LINEEDIT_FORCE_RULE( maxsize, strToSize );
return rules;
}
#undef GENERIC_RULE
#undef CHECKBOX_SET_RULE
#undef LINEEDIT_SET_RULE
#undef COMBOBOX_SET_RULE
#undef CHECKBOX_FORCE_RULE
#undef LINEEDIT_FORCE_RULE
#undef COMBOBOX_FORCE_RULE
#define STRING_MATCH_COMBO( type ) \
void RulesWidget::type##MatchChanged() \
{ \
edit_reg_##type->setEnabled( type##_match->currentItem() == Rules::RegExpMatch ); \
type->setEnabled( type##_match->currentItem() != Rules::UnimportantMatch ); \
}
STRING_MATCH_COMBO( wmclass )
STRING_MATCH_COMBO( role )
STRING_MATCH_COMBO( title )
STRING_MATCH_COMBO( extra )
STRING_MATCH_COMBO( machine )
#undef STRING_MATCH_COMBO
void RulesWidget::detectClicked()
{
assert( detect_dlg == NULL );
detect_dlg = new DetectDialog;
connect( detect_dlg, SIGNAL( detectionDone( bool )), this, SLOT( detected( bool )));
detect_dlg->detect( 0 );
}
bool RulesWidget::setWindow( WId w )
{
assert( detect_dlg == NULL );
detect_dlg_ok = false;
detect_dlg = new DetectDialog;
connect( detect_dlg, SIGNAL( detectionDone( bool )), this, SLOT( detected( bool )));
detect_dlg->detect( w );
return detect_dlg_ok;
}
void RulesWidget::detected( bool ok )
{
if( ok )
{
wmclass->setText( detect_dlg->selectedClass());
wmclass_match->setCurrentItem( Rules::ExactMatch );
wmclassMatchChanged(); // grrr
whole_wmclass->setChecked( detect_dlg->selectedWholeClass());
role->setText( detect_dlg->selectedRole());
role_match->setCurrentItem( detect_dlg->selectedRole().isEmpty()
? Rules::UnimportantMatch : Rules::ExactMatch );
roleMatchChanged();
if( detect_dlg->selectedWholeApp())
{
for( unsigned int i = 0;
i < types->count();
++i )
types->setSelected( i, true );
}
else
{
NET::WindowType type = detect_dlg->selectedType();
for( unsigned int i = 0;
i < types->count();
++i )
types->setSelected( i, false );
types->setSelected( typeToCombo( type ), true );
}
title->setText( detect_dlg->selectedTitle());
title_match->setCurrentItem( detect_dlg->titleMatch());
titleMatchChanged();
machine->setText( detect_dlg->selectedMachine());
machine_match->setCurrentItem( Rules::UnimportantMatch );
machineMatchChanged();
}
delete detect_dlg;
detect_dlg = NULL;
detect_dlg_ok = ok;
}
bool RulesWidget::finalCheck()
{
if( description->text().isEmpty())
{
if( !wmclass->text().isEmpty())
description->setText( i18n( "Settings for %1" ).arg( wmclass->text()));
else
description->setText( i18n( "Unnamed entry" ));
}
bool all_types = true;
for( unsigned int i = 0;
i < types->count();
++i )
if( !types->isSelected( i ))
all_types = false;
if( wmclass_match->currentItem() == Rules::UnimportantMatch && all_types )
{
if( KMessageBox::warningContinueCancel( topLevelWidget(),
i18n( "You have specified the window class as unimportant.\n"
"This means the settings will possibly apply to windows from all applications. "
"If you really want to create a generic setting, it is recommended you at least "
"limit the window types to avoid special window types." )) != KMessageBox::Continue )
return false;
}
return true;
}
RulesDialog::RulesDialog( QWidget* parent, const char* name )
: KDialogBase( parent, name, true, "", Ok | Cancel )
{
widget = new RulesWidget( this );
setMainWidget( widget );
}
Rules* RulesDialog::edit( Rules* r, WId w )
{
rules = r;
widget->setRules( rules );
if( rules == NULL && w != 0 ) // creating new one, read data from window
if( !widget->setWindow( w ))
return rules;
exec();
return rules;
}
void RulesDialog::accept()
{
if( !widget->finalCheck())
return;
rules = widget->rules();
KDialogBase::accept();
}
} // namespace
#include "ruleswidget.moc"