2 * Copyright (c) 2004 Lubos Lunak <l.lunak@kde.org>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 #include <kcmdlineargs.h>
20 #include <kapplication.h>
23 #include <kwindowsystem.h>
24 #include <QtDBus/QtDBus>
28 #include "ruleswidget.h"
29 #include "../../rules.h"
35 static void loadRules( QList
< Rules
* >& rules
)
37 KConfig
_cfg( "kwinrulesrc" );
38 KConfigGroup
cfg(&_cfg
, "General" );
39 int count
= cfg
.readEntry( "count",0 );
44 cfg
= KConfigGroup(&_cfg
,QString::number( i
));
45 Rules
* rule
= new Rules( cfg
);
50 static void saveRules( const QList
< Rules
* >& rules
)
52 KConfig
cfg( "kwinrulesrc" );
53 QStringList groups
= cfg
.groupList();
54 for( QStringList::ConstIterator it
= groups
.constBegin();
55 it
!= groups
.constEnd();
57 cfg
.deleteGroup( *it
);
58 cfg
.group("General").writeEntry( "count", rules
.count());
60 for( QList
< Rules
* >::ConstIterator it
= rules
.constBegin();
61 it
!= rules
.constEnd();
64 KConfigGroup
cg( &cfg
, QString::number( i
));
70 static Rules
* findRule( const QList
< Rules
* >& rules
, Window wid
, bool whole_app
)
72 KWindowInfo info
= KWindowSystem::windowInfo( wid
,
73 NET::WMName
| NET::WMWindowType
,
74 NET::WM2WindowClass
| NET::WM2WindowRole
| NET::WM2ClientMachine
);
75 if( !info
.valid()) // shouldn't really happen
77 QByteArray wmclass_class
= info
.windowClassClass().toLower();
78 QByteArray wmclass_name
= info
.windowClassName().toLower();
79 QByteArray role
= info
.windowRole().toLower();
80 NET::WindowType type
= info
.windowType( NET::NormalMask
| NET::DesktopMask
| NET::DockMask
81 | NET::ToolbarMask
| NET::MenuMask
| NET::DialogMask
| NET::OverrideMask
| NET::TopMenuMask
82 | NET::UtilityMask
| NET::SplashMask
);
83 QString title
= info
.name();
84 // QCString extrarole = ""; // TODO
85 QByteArray machine
= info
.clientMachine().toLower();
86 Rules
* best_match
= NULL
;
87 int match_quality
= 0;
88 for( QList
< Rules
* >::ConstIterator it
= rules
.constBegin();
89 it
!= rules
.constEnd();
92 // try to find an exact match, i.e. not a generic rule
96 if( rule
->wmclassmatch
!= Rules::ExactMatch
)
97 continue; // too generic
98 if( !rule
->matchWMClass( wmclass_class
, wmclass_name
))
100 // from now on, it matches the app - now try to match for a specific window
101 if( rule
->wmclasscomplete
)
104 generic
= false; // this can be considered specific enough (old X apps)
108 if( rule
->windowrolematch
!= Rules::UnimportantMatch
)
110 quality
+= rule
->windowrolematch
== Rules::ExactMatch
? 5 : 1;
113 if( rule
->titlematch
!= Rules::UnimportantMatch
)
115 quality
+= rule
->titlematch
== Rules::ExactMatch
? 3 : 1;
118 if( rule
->types
!= NET::AllTypesMask
)
121 for( unsigned int bit
= 1;
124 if( rule
->types
& bit
)
129 if( generic
) // ignore generic rules, use only the ones that are for this window
134 if( rule
->types
== NET::AllTypesMask
)
137 if( !rule
->matchType( type
)
138 || !rule
->matchRole( role
)
139 || !rule
->matchTitle( title
)
140 || !rule
->matchClientMachine( machine
))
142 if( quality
> match_quality
)
145 match_quality
= quality
;
148 if( best_match
!= NULL
)
150 Rules
* ret
= new Rules
;
153 ret
->description
= i18n( "Application settings for %1", QString::fromLatin1( wmclass_class
));
154 // TODO maybe exclude some types? If yes, then also exclude them above
156 ret
->types
= NET::AllTypesMask
;
157 ret
->titlematch
= Rules::UnimportantMatch
;
158 ret
->clientmachine
= machine
; // set, but make unimportant
159 ret
->clientmachinematch
= Rules::UnimportantMatch
;
160 ret
->extrarolematch
= Rules::UnimportantMatch
;
161 ret
->windowrolematch
= Rules::UnimportantMatch
;
162 if( wmclass_name
== wmclass_class
)
164 ret
->wmclasscomplete
= false;
165 ret
->wmclass
= wmclass_class
;
166 ret
->wmclassmatch
= Rules::ExactMatch
;
170 // WM_CLASS components differ - perhaps the app got -name argument
171 ret
->wmclasscomplete
= true;
172 ret
->wmclass
= wmclass_name
+ ' ' + wmclass_class
;
173 ret
->wmclassmatch
= Rules::ExactMatch
;
177 ret
->description
= i18n( "Window settings for %1", QString::fromLatin1( wmclass_class
));
178 if( type
== NET::Unknown
)
179 ret
->types
= NET::NormalMask
;
181 ret
->types
= 1 << type
; // convert type to its mask
182 ret
->title
= title
; // set, but make unimportant
183 ret
->titlematch
= Rules::UnimportantMatch
;
184 ret
->clientmachine
= machine
; // set, but make unimportant
185 ret
->clientmachinematch
= Rules::UnimportantMatch
;
186 // ret->extrarole = extra; TODO
187 ret
->extrarolematch
= Rules::UnimportantMatch
;
189 && role
!= "unknown" && role
!= "unnamed" ) // Qt sets this if not specified
191 ret
->windowrole
= role
;
192 ret
->windowrolematch
= Rules::ExactMatch
;
193 if( wmclass_name
== wmclass_class
)
195 ret
->wmclasscomplete
= false;
196 ret
->wmclass
= wmclass_class
;
197 ret
->wmclassmatch
= Rules::ExactMatch
;
201 // WM_CLASS components differ - perhaps the app got -name argument
202 ret
->wmclasscomplete
= true;
203 ret
->wmclass
= wmclass_name
+ ' ' + wmclass_class
;
204 ret
->wmclassmatch
= Rules::ExactMatch
;
209 if( wmclass_name
!= wmclass_class
)
211 ret
->wmclasscomplete
= true;
212 ret
->wmclass
= wmclass_name
+ ' ' + wmclass_class
;
213 ret
->wmclassmatch
= Rules::ExactMatch
;
217 // This is a window that has no role set, and both components of WM_CLASS
218 // match (possibly only differing in case), which most likely means either
219 // the application doesn't give a damn about distinguishing its various
220 // windows, or it's an app that uses role for that, but this window
221 // lacks it for some reason. Use non-complete WM_CLASS matching, also
222 // include window title in the matching, and pray it causes many more positive
223 // matches than negative matches.
224 ret
->titlematch
= Rules::ExactMatch
;
225 ret
->wmclasscomplete
= false;
226 ret
->wmclass
= wmclass_class
;
227 ret
->wmclassmatch
= Rules::ExactMatch
;
233 static int edit( Window wid
, bool whole_app
)
235 QList
< Rules
* > rules
;
237 Rules
* orig_rule
= findRule( rules
, wid
, whole_app
);
239 // dlg.edit() creates new Rules instance if edited
240 Rules
* edited_rule
= dlg
.edit( orig_rule
, wid
, true );
241 if( edited_rule
== NULL
|| edited_rule
->isEmpty())
243 rules
.removeAll( orig_rule
);
245 if( orig_rule
!= edited_rule
)
248 else if( edited_rule
!= orig_rule
)
250 int pos
= rules
.indexOf( orig_rule
);
252 rules
[ pos
] = edited_rule
;
254 rules
.prepend( edited_rule
);
258 // Send signal to all kwin instances
259 QDBusMessage message
=
260 QDBusMessage::createSignal("/KWin", "org.kde.KWin", "reloadConfig");
261 QDBusConnection::sessionBus().send(message
);
268 KDE_EXPORT
int kdemain( int argc
, char* argv
[] )
270 KCmdLineArgs::init( argc
, argv
, "kwin_rules_dialog", "kcmkwinrules", ki18n( "KWin" ), "1.0" ,
271 ki18n( "KWin helper utility" ));
273 KCmdLineOptions options
;
274 options
.add("wid <wid>", ki18n("WId of the window for special window settings."));
275 options
.add("whole-app", ki18n("Whether the settings should affect all windows of the application."));
276 KCmdLineArgs::addCmdLineOptions( options
);
278 KCmdLineArgs
* args
= KCmdLineArgs::parsedArgs();
280 Window id
= args
->getOption( "wid" ).toULongLong( &id_ok
);
281 bool whole_app
= args
->isSet( "whole-app" );
283 if( !id_ok
|| id
== None
)
285 KCmdLineArgs::usageError( i18n( "This helper utility is not supposed to be called directly." ));
288 return KWin::edit( id
, whole_app
);