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);
43
cfg = KConfigGroup(&_cfg, QString::number(i));
44
Rules* rule = new Rules(cfg);
49
static void saveRules(const QList< Rules* >& rules)
51
KConfig cfg("kwinrulesrc");
52
QStringList groups = cfg.groupList();
53
for (QStringList::ConstIterator it = groups.constBegin();
54
it != groups.constEnd();
57
cfg.group("General").writeEntry("count", rules.count());
59
for (QList< Rules* >::ConstIterator it = rules.constBegin();
60
it != rules.constEnd();
62
KConfigGroup cg(&cfg, QString::number(i));
68
static Rules* findRule(const QList< Rules* >& rules, Window wid, bool whole_app)
70
KWindowInfo info = KWindowSystem::windowInfo(wid,
71
NET::WMName | NET::WMWindowType,
72
NET::WM2WindowClass | NET::WM2WindowRole | NET::WM2ClientMachine);
73
if (!info.valid()) // shouldn't really happen
75
QByteArray wmclass_class = info.windowClassClass().toLower();
76
QByteArray wmclass_name = info.windowClassName().toLower();
77
QByteArray role = info.windowRole().toLower();
78
NET::WindowType type = info.windowType(NET::NormalMask | NET::DesktopMask | NET::DockMask
79
| NET::ToolbarMask | NET::MenuMask | NET::DialogMask | NET::OverrideMask | NET::TopMenuMask
80
| NET::UtilityMask | NET::SplashMask);
81
QString title = info.name();
82
// QCString extrarole = ""; // TODO
83
QByteArray machine = info.clientMachine().toLower();
84
Rules* best_match = NULL;
85
int match_quality = 0;
86
for (QList< Rules* >::ConstIterator it = rules.constBegin();
87
it != rules.constEnd();
89
// try to find an exact match, i.e. not a generic rule
93
if (rule->wmclassmatch != Rules::ExactMatch)
94
continue; // too generic
95
if (!rule->matchWMClass(wmclass_class, wmclass_name))
97
// from now on, it matches the app - now try to match for a specific window
98
if (rule->wmclasscomplete) {
100
generic = false; // this can be considered specific enough (old X apps)
103
if (rule->windowrolematch != Rules::UnimportantMatch) {
104
quality += rule->windowrolematch == Rules::ExactMatch ? 5 : 1;
107
if (rule->titlematch != Rules::UnimportantMatch) {
108
quality += rule->titlematch == Rules::ExactMatch ? 3 : 1;
111
if (rule->types != NET::AllTypesMask) {
113
for (unsigned int bit = 1;
116
if (rule->types & bit)
121
if (generic) // ignore generic rules, use only the ones that are for this window
124
if (rule->types == NET::AllTypesMask)
127
if (!rule->matchType(type)
128
|| !rule->matchRole(role)
129
|| !rule->matchTitle(title)
130
|| !rule->matchClientMachine(machine))
132
if (quality > match_quality) {
134
match_quality = quality;
137
if (best_match != NULL)
139
Rules* ret = new Rules;
141
ret->description = i18n("Application settings for %1", QString::fromLatin1(wmclass_class));
142
// TODO maybe exclude some types? If yes, then also exclude them above
144
ret->types = NET::AllTypesMask;
145
ret->titlematch = Rules::UnimportantMatch;
146
ret->clientmachine = machine; // set, but make unimportant
147
ret->clientmachinematch = Rules::UnimportantMatch;
148
ret->extrarolematch = Rules::UnimportantMatch;
149
ret->windowrolematch = Rules::UnimportantMatch;
150
if (wmclass_name == wmclass_class) {
151
ret->wmclasscomplete = false;
152
ret->wmclass = wmclass_class;
153
ret->wmclassmatch = Rules::ExactMatch;
155
// WM_CLASS components differ - perhaps the app got -name argument
156
ret->wmclasscomplete = true;
157
ret->wmclass = wmclass_name + ' ' + wmclass_class;
158
ret->wmclassmatch = Rules::ExactMatch;
162
ret->description = i18n("Window settings for %1", QString::fromLatin1(wmclass_class));
163
if (type == NET::Unknown)
164
ret->types = NET::NormalMask;
166
ret->types = 1 << type; // convert type to its mask
167
ret->title = title; // set, but make unimportant
168
ret->titlematch = Rules::UnimportantMatch;
169
ret->clientmachine = machine; // set, but make unimportant
170
ret->clientmachinematch = Rules::UnimportantMatch;
171
// ret->extrarole = extra; TODO
172
ret->extrarolematch = Rules::UnimportantMatch;
174
&& role != "unknown" && role != "unnamed") { // Qt sets this if not specified
175
ret->windowrole = role;
176
ret->windowrolematch = Rules::ExactMatch;
177
if (wmclass_name == wmclass_class) {
178
ret->wmclasscomplete = false;
179
ret->wmclass = wmclass_class;
180
ret->wmclassmatch = Rules::ExactMatch;
182
// WM_CLASS components differ - perhaps the app got -name argument
183
ret->wmclasscomplete = true;
184
ret->wmclass = wmclass_name + ' ' + wmclass_class;
185
ret->wmclassmatch = Rules::ExactMatch;
187
} else { // no role set
188
if (wmclass_name != wmclass_class) {
189
ret->wmclasscomplete = true;
190
ret->wmclass = wmclass_name + ' ' + wmclass_class;
191
ret->wmclassmatch = Rules::ExactMatch;
193
// This is a window that has no role set, and both components of WM_CLASS
194
// match (possibly only differing in case), which most likely means either
195
// the application doesn't give a damn about distinguishing its various
196
// windows, or it's an app that uses role for that, but this window
197
// lacks it for some reason. Use non-complete WM_CLASS matching, also
198
// include window title in the matching, and pray it causes many more positive
199
// matches than negative matches.
200
ret->titlematch = Rules::ExactMatch;
201
ret->wmclasscomplete = false;
202
ret->wmclass = wmclass_class;
203
ret->wmclassmatch = Rules::ExactMatch;
209
static int edit(Window wid, bool whole_app)
211
QList< Rules* > rules;
213
Rules* orig_rule = findRule(rules, wid, whole_app);
215
// dlg.edit() creates new Rules instance if edited
216
Rules* edited_rule = dlg.edit(orig_rule, wid, true);
217
if (edited_rule == NULL || edited_rule->isEmpty()) {
218
rules.removeAll(orig_rule);
220
if (orig_rule != edited_rule)
222
} else if (edited_rule != orig_rule) {
223
int pos = rules.indexOf(orig_rule);
225
rules[ pos ] = edited_rule;
227
rules.prepend(edited_rule);
231
// Send signal to all kwin instances
232
QDBusMessage message =
233
QDBusMessage::createSignal("/KWin", "org.kde.KWin", "reloadConfig");
234
QDBusConnection::sessionBus().send(message);
241
KDE_EXPORT int kdemain(int argc, char* argv[])
243
KCmdLineArgs::init(argc, argv, "kwin_rules_dialog", "kcmkwinrules", ki18n("KWin"), "1.0" ,
244
ki18n("KWin helper utility"));
246
KCmdLineOptions options;
247
options.add("wid <wid>", ki18n("WId of the window for special window settings."));
248
options.add("whole-app", ki18n("Whether the settings should affect all windows of the application."));
249
KCmdLineArgs::addCmdLineOptions(options);
251
KCmdLineArgs* args = KCmdLineArgs::parsedArgs();
253
Window id = args->getOption("wid").toULongLong(&id_ok);
254
bool whole_app = args->isSet("whole-app");
256
if (!id_ok || id == None) {
257
KCmdLineArgs::usageError(i18n("This helper utility is not supposed to be called directly."));
260
return KWin::edit(id, whole_app);