~ubuntu-branches/ubuntu/utopic/kde-workspace/utopic-proposed

« back to all changes in this revision

Viewing changes to kwin/kcmkwin/kwinrules/main.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Michał Zając
  • Date: 2011-07-09 08:31:15 UTC
  • Revision ID: james.westby@ubuntu.com-20110709083115-ohyxn6z93mily9fc
Tags: upstream-4.6.90
Import upstream version 4.6.90

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (c) 2004 Lubos Lunak <l.lunak@kde.org>
 
3
 *
 
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.
 
8
 *
 
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.
 
13
 *
 
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.
 
17
 */
 
18
 
 
19
#include <kcmdlineargs.h>
 
20
#include <kapplication.h>
 
21
#include <kconfig.h>
 
22
#include <klocale.h>
 
23
#include <kwindowsystem.h>
 
24
#include <QtDBus/QtDBus>
 
25
#include <X11/Xlib.h>
 
26
#include <fixx11h.h>
 
27
 
 
28
#include "ruleswidget.h"
 
29
#include "../../rules.h"
 
30
#include <QByteArray>
 
31
 
 
32
namespace KWin
 
33
{
 
34
 
 
35
static void loadRules(QList< Rules* >& rules)
 
36
{
 
37
    KConfig _cfg("kwinrulesrc");
 
38
    KConfigGroup cfg(&_cfg, "General");
 
39
    int count = cfg.readEntry("count", 0);
 
40
    for (int i = 1;
 
41
            i <= count;
 
42
            ++i) {
 
43
        cfg = KConfigGroup(&_cfg, QString::number(i));
 
44
        Rules* rule = new Rules(cfg);
 
45
        rules.append(rule);
 
46
    }
 
47
}
 
48
 
 
49
static void saveRules(const QList< Rules* >& rules)
 
50
{
 
51
    KConfig cfg("kwinrulesrc");
 
52
    QStringList groups = cfg.groupList();
 
53
    for (QStringList::ConstIterator it = groups.constBegin();
 
54
            it != groups.constEnd();
 
55
            ++it)
 
56
        cfg.deleteGroup(*it);
 
57
    cfg.group("General").writeEntry("count", rules.count());
 
58
    int i = 1;
 
59
    for (QList< Rules* >::ConstIterator it = rules.constBegin();
 
60
            it != rules.constEnd();
 
61
            ++it) {
 
62
        KConfigGroup cg(&cfg, QString::number(i));
 
63
        (*it)->write(cg);
 
64
        ++i;
 
65
    }
 
66
}
 
67
 
 
68
static Rules* findRule(const QList< Rules* >& rules, Window wid, bool whole_app)
 
69
{
 
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
 
74
        return NULL;
 
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();
 
88
            ++it) {
 
89
        // try to find an exact match, i.e. not a generic rule
 
90
        Rules* rule = *it;
 
91
        int quality = 0;
 
92
        bool generic = true;
 
93
        if (rule->wmclassmatch != Rules::ExactMatch)
 
94
            continue; // too generic
 
95
        if (!rule->matchWMClass(wmclass_class, wmclass_name))
 
96
            continue;
 
97
        // from now on, it matches the app - now try to match for a specific window
 
98
        if (rule->wmclasscomplete) {
 
99
            quality += 1;
 
100
            generic = false;  // this can be considered specific enough (old X apps)
 
101
        }
 
102
        if (!whole_app) {
 
103
            if (rule->windowrolematch != Rules::UnimportantMatch) {
 
104
                quality += rule->windowrolematch == Rules::ExactMatch ? 5 : 1;
 
105
                generic = false;
 
106
            }
 
107
            if (rule->titlematch != Rules::UnimportantMatch) {
 
108
                quality += rule->titlematch == Rules::ExactMatch ? 3 : 1;
 
109
                generic = false;
 
110
            }
 
111
            if (rule->types != NET::AllTypesMask) {
 
112
                int bits = 0;
 
113
                for (unsigned int bit = 1;
 
114
                        bit < 1U << 31;
 
115
                        bit <<= 1)
 
116
                    if (rule->types & bit)
 
117
                        ++bits;
 
118
                if (bits == 1)
 
119
                    quality += 2;
 
120
            }
 
121
            if (generic)   // ignore generic rules, use only the ones that are for this window
 
122
                continue;
 
123
        } else {
 
124
            if (rule->types == NET::AllTypesMask)
 
125
                quality += 2;
 
126
        }
 
127
        if (!rule->matchType(type)
 
128
                || !rule->matchRole(role)
 
129
                || !rule->matchTitle(title)
 
130
                || !rule->matchClientMachine(machine))
 
131
            continue;
 
132
        if (quality > match_quality) {
 
133
            best_match = rule;
 
134
            match_quality = quality;
 
135
        }
 
136
    }
 
137
    if (best_match != NULL)
 
138
        return best_match;
 
139
    Rules* ret = new Rules;
 
140
    if (whole_app) {
 
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
 
143
        // when searching.
 
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;
 
154
        } else {
 
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;
 
159
        }
 
160
        return ret;
 
161
    }
 
162
    ret->description = i18n("Window settings for %1", QString::fromLatin1(wmclass_class));
 
163
    if (type == NET::Unknown)
 
164
        ret->types = NET::NormalMask;
 
165
    else
 
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;
 
173
    if (!role.isEmpty()
 
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;
 
181
        } else {
 
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;
 
186
        }
 
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;
 
192
        } else {
 
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;
 
204
        }
 
205
    }
 
206
    return ret;
 
207
}
 
208
 
 
209
static int edit(Window wid, bool whole_app)
 
210
{
 
211
    QList< Rules* > rules;
 
212
    loadRules(rules);
 
213
    Rules* orig_rule = findRule(rules, wid, whole_app);
 
214
    RulesDialog dlg;
 
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);
 
219
        delete orig_rule;
 
220
        if (orig_rule != edited_rule)
 
221
            delete edited_rule;
 
222
    } else if (edited_rule != orig_rule) {
 
223
        int pos = rules.indexOf(orig_rule);
 
224
        if (pos != -1)
 
225
            rules[ pos ] = edited_rule;
 
226
        else
 
227
            rules.prepend(edited_rule);
 
228
        delete orig_rule;
 
229
    }
 
230
    saveRules(rules);
 
231
    // Send signal to all kwin instances
 
232
    QDBusMessage message =
 
233
        QDBusMessage::createSignal("/KWin", "org.kde.KWin", "reloadConfig");
 
234
    QDBusConnection::sessionBus().send(message);
 
235
    return 0;
 
236
}
 
237
 
 
238
} // namespace
 
239
 
 
240
extern "C"
 
241
KDE_EXPORT int kdemain(int argc, char* argv[])
 
242
{
 
243
    KCmdLineArgs::init(argc, argv, "kwin_rules_dialog", "kcmkwinrules", ki18n("KWin"), "1.0" ,
 
244
                       ki18n("KWin helper utility"));
 
245
 
 
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);
 
250
    KApplication app;
 
251
    KCmdLineArgs* args = KCmdLineArgs::parsedArgs();
 
252
    bool id_ok = false;
 
253
    Window id = args->getOption("wid").toULongLong(&id_ok);
 
254
    bool whole_app = args->isSet("whole-app");
 
255
    args->clear();
 
256
    if (!id_ok || id == None) {
 
257
        KCmdLineArgs::usageError(i18n("This helper utility is not supposed to be called directly."));
 
258
        return 1;
 
259
    }
 
260
    return KWin::edit(id, whole_app);
 
261
}