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

« back to all changes in this revision

Viewing changes to kwin/rules.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
 KWin - the KDE window manager
 
3
 This file is part of the KDE project.
 
4
 
 
5
Copyright (C) 2004 Lubos Lunak <l.lunak@kde.org>
 
6
 
 
7
This program is free software; you can redistribute it and/or modify
 
8
it under the terms of the GNU General Public License as published by
 
9
the Free Software Foundation; either version 2 of the License, or
 
10
(at your option) any later version.
 
11
 
 
12
This program is distributed in the hope that it will be useful,
 
13
but WITHOUT ANY WARRANTY; without even the implied warranty of
 
14
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
15
GNU General Public License for more details.
 
16
 
 
17
You should have received a copy of the GNU General Public License
 
18
along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
19
*********************************************************************/
 
20
 
 
21
#include "rules.h"
 
22
 
 
23
#include <fixx11h.h>
 
24
#include <kconfig.h>
 
25
#include <QRegExp>
 
26
#include <ktemporaryfile.h>
 
27
#include <QFile>
 
28
#include <ktoolinvocation.h>
 
29
 
 
30
#ifndef KCMRULES
 
31
#include "client.h"
 
32
#include "workspace.h"
 
33
#endif
 
34
 
 
35
namespace KWin
 
36
{
 
37
 
 
38
Rules::Rules()
 
39
    : temporary_state(0)
 
40
    , wmclassmatch(UnimportantMatch)
 
41
    , wmclasscomplete(UnimportantMatch)
 
42
    , windowrolematch(UnimportantMatch)
 
43
    , titlematch(UnimportantMatch)
 
44
    , extrarolematch(UnimportantMatch)
 
45
    , clientmachinematch(UnimportantMatch)
 
46
    , types(NET::AllTypesMask)
 
47
    , placementrule(UnusedForceRule)
 
48
    , positionrule(UnusedSetRule)
 
49
    , sizerule(UnusedSetRule)
 
50
    , minsizerule(UnusedForceRule)
 
51
    , maxsizerule(UnusedForceRule)
 
52
    , opacityactiverule(UnusedForceRule)
 
53
    , opacityinactiverule(UnusedForceRule)
 
54
    , tilingoptionrule(UnusedForceRule)
 
55
    , ignorepositionrule(UnusedForceRule)
 
56
    , desktoprule(UnusedSetRule)
 
57
    , typerule(UnusedForceRule)
 
58
    , maximizevertrule(UnusedSetRule)
 
59
    , maximizehorizrule(UnusedSetRule)
 
60
    , minimizerule(UnusedSetRule)
 
61
    , shaderule(UnusedSetRule)
 
62
    , skiptaskbarrule(UnusedSetRule)
 
63
    , skippagerrule(UnusedSetRule)
 
64
    , skipswitcherrule(UnusedSetRule)
 
65
    , aboverule(UnusedSetRule)
 
66
    , belowrule(UnusedSetRule)
 
67
    , fullscreenrule(UnusedSetRule)
 
68
    , noborderrule(UnusedSetRule)
 
69
    , blockcompositingrule(UnusedForceRule)
 
70
    , fsplevelrule(UnusedForceRule)
 
71
    , acceptfocusrule(UnusedForceRule)
 
72
    , moveresizemoderule(UnusedForceRule)
 
73
    , closeablerule(UnusedForceRule)
 
74
    , autogrouprule(UnusedForceRule)
 
75
    , autogroupfgrule(UnusedForceRule)
 
76
    , autogroupidrule(UnusedForceRule)
 
77
    , strictgeometryrule(UnusedForceRule)
 
78
    , shortcutrule(UnusedSetRule)
 
79
    , disableglobalshortcutsrule(UnusedForceRule)
 
80
{
 
81
}
 
82
 
 
83
Rules::Rules(const QString& str, bool temporary)
 
84
    : temporary_state(temporary ? 2 : 0)
 
85
{
 
86
    KTemporaryFile file;
 
87
    if (file.open()) {
 
88
        QByteArray s = str.toUtf8();
 
89
        file.write(s.data(), s.length());
 
90
    }
 
91
    file.flush();
 
92
    KConfig cfg(file.fileName(), KConfig::SimpleConfig);
 
93
    readFromCfg(cfg.group(QString()));
 
94
    if (description.isEmpty())
 
95
        description = "temporary";
 
96
}
 
97
 
 
98
#define READ_MATCH_STRING( var, func ) \
 
99
    var = cfg.readEntry( #var ) func; \
 
100
    var##match = (StringMatch) qMax( FirstStringMatch, \
 
101
                                     qMin( LastStringMatch, static_cast< StringMatch >( cfg.readEntry( #var "match",0 ))));
 
102
 
 
103
#define READ_SET_RULE( var, func, def ) \
 
104
    var = func ( cfg.readEntry( #var, def)); \
 
105
    var##rule = readSetRule( cfg, #var "rule" );
 
106
 
 
107
#define READ_SET_RULE_DEF( var , func, def ) \
 
108
    var = func ( cfg.readEntry( #var, def )); \
 
109
    var##rule = readSetRule( cfg, #var "rule" );
 
110
 
 
111
#define READ_FORCE_RULE( var, func, def) \
 
112
    var = func ( cfg.readEntry( #var, def)); \
 
113
    var##rule = readForceRule( cfg, #var "rule" );
 
114
 
 
115
#define READ_FORCE_RULE2( var, def, func, funcarg ) \
 
116
    var = func ( cfg.readEntry( #var, def),funcarg ); \
 
117
    var##rule = readForceRule( cfg, #var "rule" );
 
118
 
 
119
 
 
120
 
 
121
Rules::Rules(const KConfigGroup& cfg)
 
122
    : temporary_state(0)
 
123
{
 
124
    readFromCfg(cfg);
 
125
}
 
126
 
 
127
static int limit0to4(int i)
 
128
{
 
129
    return qMax(0, qMin(4, i));
 
130
}
 
131
 
 
132
void Rules::readFromCfg(const KConfigGroup& cfg)
 
133
{
 
134
    description = cfg.readEntry("Description");
 
135
    if (description.isEmpty())  // capitalized first, lowercase for backwards compatibility
 
136
        description = cfg.readEntry("description");
 
137
    READ_MATCH_STRING(wmclass, .toLower().toLatin1());
 
138
    wmclasscomplete = cfg.readEntry("wmclasscomplete" , false);
 
139
    READ_MATCH_STRING(windowrole, .toLower().toLatin1());
 
140
    READ_MATCH_STRING(title,);
 
141
    READ_MATCH_STRING(extrarole, .toLower().toLatin1());
 
142
    READ_MATCH_STRING(clientmachine, .toLower().toLatin1());
 
143
    types = cfg.readEntry("types", uint(NET::AllTypesMask));
 
144
    READ_FORCE_RULE2(placement, QString(), Placement::policyFromString, false);
 
145
    READ_SET_RULE_DEF(position, , invalidPoint);
 
146
    READ_SET_RULE(size, , QSize());
 
147
    if (size.isEmpty() && sizerule != (SetRule)Remember)
 
148
        sizerule = UnusedSetRule;
 
149
    READ_FORCE_RULE(minsize, , QSize());
 
150
    if (!minsize.isValid())
 
151
        minsize = QSize(1, 1);
 
152
    READ_FORCE_RULE(maxsize, , QSize());
 
153
    if (maxsize.isEmpty())
 
154
        maxsize = QSize(32767, 32767);
 
155
    READ_FORCE_RULE(opacityactive, , 0);
 
156
    if (opacityactive < 0 || opacityactive > 100)
 
157
        opacityactive = 100;
 
158
    READ_FORCE_RULE(opacityinactive, , 0);
 
159
    if (opacityinactive < 0 || opacityinactive > 100)
 
160
        opacityinactive = 100;
 
161
    READ_FORCE_RULE(tilingoption, , 0);
 
162
    READ_FORCE_RULE(ignoreposition, , false);
 
163
    READ_SET_RULE(desktop, , 0);
 
164
    type = readType(cfg, "type");
 
165
    typerule = type != NET::Unknown ? readForceRule(cfg, "typerule") : UnusedForceRule;
 
166
    READ_SET_RULE(maximizevert, , false);
 
167
    READ_SET_RULE(maximizehoriz, , false);
 
168
    READ_SET_RULE(minimize, , false);
 
169
    READ_SET_RULE(shade, , false);
 
170
    READ_SET_RULE(skiptaskbar, , false);
 
171
    READ_SET_RULE(skippager, , false);
 
172
    READ_SET_RULE(skipswitcher, , false);
 
173
    READ_SET_RULE(above, , false);
 
174
    READ_SET_RULE(below, , false);
 
175
    READ_SET_RULE(fullscreen, , false);
 
176
    READ_SET_RULE(noborder, , false);
 
177
    READ_FORCE_RULE(blockcompositing, , false);
 
178
    READ_FORCE_RULE(fsplevel, limit0to4, 0); // fsp is 0-4
 
179
    READ_FORCE_RULE(acceptfocus, , false);
 
180
    READ_FORCE_RULE(moveresizemode, Options::stringToMoveResizeMode, QString());
 
181
    READ_FORCE_RULE(closeable, , false);
 
182
    READ_FORCE_RULE(autogroup, , false);
 
183
    READ_FORCE_RULE(autogroupfg, , true);
 
184
    READ_FORCE_RULE(autogroupid, , QString());
 
185
    READ_FORCE_RULE(strictgeometry, , false);
 
186
    READ_SET_RULE(shortcut, , QString());
 
187
    READ_FORCE_RULE(disableglobalshortcuts, , false);
 
188
}
 
189
 
 
190
#undef READ_MATCH_STRING
 
191
#undef READ_SET_RULE
 
192
#undef READ_FORCE_RULE
 
193
#undef READ_FORCE_RULE2
 
194
 
 
195
#define WRITE_MATCH_STRING( var, cast, force ) \
 
196
    if ( !var.isEmpty() || force ) \
 
197
    { \
 
198
        cfg.writeEntry( #var, cast var ); \
 
199
        cfg.writeEntry( #var "match", (int)var##match ); \
 
200
    } \
 
201
    else \
 
202
    { \
 
203
        cfg.deleteEntry( #var ); \
 
204
        cfg.deleteEntry( #var "match" ); \
 
205
    }
 
206
 
 
207
#define WRITE_SET_RULE( var, func ) \
 
208
    if ( var##rule != UnusedSetRule ) \
 
209
    { \
 
210
        cfg.writeEntry( #var, func ( var )); \
 
211
        cfg.writeEntry( #var "rule", (int)var##rule ); \
 
212
    } \
 
213
    else \
 
214
    { \
 
215
        cfg.deleteEntry( #var ); \
 
216
        cfg.deleteEntry( #var "rule" ); \
 
217
    }
 
218
 
 
219
#define WRITE_FORCE_RULE( var, func ) \
 
220
    if ( var##rule != UnusedForceRule ) \
 
221
    { \
 
222
        cfg.writeEntry( #var, func ( var )); \
 
223
        cfg.writeEntry( #var "rule", (int)var##rule ); \
 
224
    } \
 
225
    else \
 
226
    { \
 
227
        cfg.deleteEntry( #var ); \
 
228
        cfg.deleteEntry( #var "rule" ); \
 
229
    }
 
230
 
 
231
void Rules::write(KConfigGroup& cfg) const
 
232
{
 
233
    cfg.writeEntry("Description", description);
 
234
    // always write wmclass
 
235
    WRITE_MATCH_STRING(wmclass, (const char*), true);
 
236
    cfg.writeEntry("wmclasscomplete", wmclasscomplete);
 
237
    WRITE_MATCH_STRING(windowrole, (const char*), false);
 
238
    WRITE_MATCH_STRING(title, , false);
 
239
    WRITE_MATCH_STRING(extrarole, (const char*), false);
 
240
    WRITE_MATCH_STRING(clientmachine, (const char*), false);
 
241
    if (types != NET::AllTypesMask)
 
242
        cfg.writeEntry("types", uint(types));
 
243
    else
 
244
        cfg.deleteEntry("types");
 
245
    WRITE_FORCE_RULE(placement, Placement::policyToString);
 
246
    WRITE_SET_RULE(position,);
 
247
    WRITE_SET_RULE(size,);
 
248
    WRITE_FORCE_RULE(minsize,);
 
249
    WRITE_FORCE_RULE(maxsize,);
 
250
    WRITE_FORCE_RULE(opacityactive,);
 
251
    WRITE_FORCE_RULE(opacityinactive,);
 
252
    WRITE_FORCE_RULE(tilingoption,);
 
253
    WRITE_FORCE_RULE(ignoreposition,);
 
254
    WRITE_SET_RULE(desktop,);
 
255
    WRITE_FORCE_RULE(type, int);
 
256
    WRITE_SET_RULE(maximizevert,);
 
257
    WRITE_SET_RULE(maximizehoriz,);
 
258
    WRITE_SET_RULE(minimize,);
 
259
    WRITE_SET_RULE(shade,);
 
260
    WRITE_SET_RULE(skiptaskbar,);
 
261
    WRITE_SET_RULE(skippager,);
 
262
    WRITE_SET_RULE(skipswitcher,);
 
263
    WRITE_SET_RULE(above,);
 
264
    WRITE_SET_RULE(below,);
 
265
    WRITE_SET_RULE(fullscreen,);
 
266
    WRITE_SET_RULE(noborder,);
 
267
    WRITE_FORCE_RULE(blockcompositing,);
 
268
    WRITE_FORCE_RULE(fsplevel,);
 
269
    WRITE_FORCE_RULE(acceptfocus,);
 
270
    WRITE_FORCE_RULE(moveresizemode, Options::moveResizeModeToString);
 
271
    WRITE_FORCE_RULE(closeable,);
 
272
    WRITE_FORCE_RULE(autogroup,);
 
273
    WRITE_FORCE_RULE(autogroupfg,);
 
274
    WRITE_FORCE_RULE(autogroupid,);
 
275
    WRITE_FORCE_RULE(strictgeometry,);
 
276
    WRITE_SET_RULE(shortcut,);
 
277
    WRITE_FORCE_RULE(disableglobalshortcuts,);
 
278
}
 
279
 
 
280
#undef WRITE_MATCH_STRING
 
281
#undef WRITE_SET_RULE
 
282
#undef WRITE_FORCE_RULE
 
283
 
 
284
// returns true if it doesn't affect anything
 
285
bool Rules::isEmpty() const
 
286
{
 
287
    return(placementrule == UnusedForceRule
 
288
           && positionrule == UnusedSetRule
 
289
           && sizerule == UnusedSetRule
 
290
           && minsizerule == UnusedForceRule
 
291
           && maxsizerule == UnusedForceRule
 
292
           && opacityactiverule == UnusedForceRule
 
293
           && opacityinactiverule == UnusedForceRule
 
294
           && tilingoptionrule == UnusedForceRule
 
295
           && ignorepositionrule == UnusedForceRule
 
296
           && desktoprule == UnusedSetRule
 
297
           && typerule == UnusedForceRule
 
298
           && maximizevertrule == UnusedSetRule
 
299
           && maximizehorizrule == UnusedSetRule
 
300
           && minimizerule == UnusedSetRule
 
301
           && shaderule == UnusedSetRule
 
302
           && skiptaskbarrule == UnusedSetRule
 
303
           && skippagerrule == UnusedSetRule
 
304
           && skipswitcherrule == UnusedSetRule
 
305
           && aboverule == UnusedSetRule
 
306
           && belowrule == UnusedSetRule
 
307
           && fullscreenrule == UnusedSetRule
 
308
           && noborderrule == UnusedSetRule
 
309
           && blockcompositingrule == UnusedForceRule
 
310
           && fsplevelrule == UnusedForceRule
 
311
           && acceptfocusrule == UnusedForceRule
 
312
           && moveresizemoderule == UnusedForceRule
 
313
           && closeablerule == UnusedForceRule
 
314
           && autogrouprule == UnusedForceRule
 
315
           && autogroupfgrule == UnusedForceRule
 
316
           && autogroupidrule == UnusedForceRule
 
317
           && strictgeometryrule == UnusedForceRule
 
318
           && shortcutrule == UnusedSetRule
 
319
           && disableglobalshortcutsrule == UnusedForceRule);
 
320
}
 
321
 
 
322
Rules::SetRule Rules::readSetRule(const KConfigGroup& cfg, const QString& key)
 
323
{
 
324
    int v = cfg.readEntry(key, 0);
 
325
    if (v >= DontAffect && v <= ForceTemporarily)
 
326
        return static_cast< SetRule >(v);
 
327
    return UnusedSetRule;
 
328
}
 
329
 
 
330
Rules::ForceRule Rules::readForceRule(const KConfigGroup& cfg, const QString& key)
 
331
{
 
332
    int v = cfg.readEntry(key, 0);
 
333
    if (v == DontAffect || v == Force || v == ForceTemporarily)
 
334
        return static_cast< ForceRule >(v);
 
335
    return UnusedForceRule;
 
336
}
 
337
 
 
338
NET::WindowType Rules::readType(const KConfigGroup& cfg, const QString& key)
 
339
{
 
340
    int v = cfg.readEntry(key, 0);
 
341
    if (v >= NET::Normal && v <= NET::Splash)
 
342
        return static_cast< NET::WindowType >(v);
 
343
    return NET::Unknown;
 
344
}
 
345
 
 
346
bool Rules::matchType(NET::WindowType match_type) const
 
347
{
 
348
    if (types != NET::AllTypesMask) {
 
349
        if (match_type == NET::Unknown)
 
350
            match_type = NET::Normal; // NET::Unknown->NET::Normal is only here for matching
 
351
        if (!NET::typeMatchesMask(match_type, types))
 
352
            return false;
 
353
    }
 
354
    return true;
 
355
}
 
356
 
 
357
bool Rules::matchWMClass(const QByteArray& match_class, const QByteArray& match_name) const
 
358
{
 
359
    if (wmclassmatch != UnimportantMatch) {
 
360
        // TODO optimize?
 
361
        QByteArray cwmclass = wmclasscomplete
 
362
                              ? match_name + ' ' + match_class : match_class;
 
363
        if (wmclassmatch == RegExpMatch && QRegExp(wmclass).indexIn(cwmclass) == -1)
 
364
            return false;
 
365
        if (wmclassmatch == ExactMatch && wmclass != cwmclass)
 
366
            return false;
 
367
        if (wmclassmatch == SubstringMatch && !cwmclass.contains(wmclass))
 
368
            return false;
 
369
    }
 
370
    return true;
 
371
}
 
372
 
 
373
bool Rules::matchRole(const QByteArray& match_role) const
 
374
{
 
375
    if (windowrolematch != UnimportantMatch) {
 
376
        if (windowrolematch == RegExpMatch && QRegExp(windowrole).indexIn(match_role) == -1)
 
377
            return false;
 
378
        if (windowrolematch == ExactMatch && windowrole != match_role)
 
379
            return false;
 
380
        if (windowrolematch == SubstringMatch && !match_role.contains(windowrole))
 
381
            return false;
 
382
    }
 
383
    return true;
 
384
}
 
385
 
 
386
bool Rules::matchTitle(const QString& match_title) const
 
387
{
 
388
    if (titlematch != UnimportantMatch) {
 
389
        if (titlematch == RegExpMatch && QRegExp(title).indexIn(match_title) == -1)
 
390
            return false;
 
391
        if (titlematch == ExactMatch && title != match_title)
 
392
            return false;
 
393
        if (titlematch == SubstringMatch && !match_title.contains(title))
 
394
            return false;
 
395
    }
 
396
    return true;
 
397
}
 
398
 
 
399
bool Rules::matchClientMachine(const QByteArray& match_machine) const
 
400
{
 
401
    if (clientmachinematch != UnimportantMatch) {
 
402
        // if it's localhost, check also "localhost" before checking hostname
 
403
        if (match_machine != "localhost" && isLocalMachine(match_machine)
 
404
                && matchClientMachine("localhost"))
 
405
            return true;
 
406
        if (clientmachinematch == RegExpMatch
 
407
                && QRegExp(clientmachine).indexIn(match_machine) == -1)
 
408
            return false;
 
409
        if (clientmachinematch == ExactMatch
 
410
                && clientmachine != match_machine)
 
411
            return false;
 
412
        if (clientmachinematch == SubstringMatch
 
413
                && !match_machine.contains(clientmachine))
 
414
            return false;
 
415
    }
 
416
    return true;
 
417
}
 
418
 
 
419
#ifndef KCMRULES
 
420
bool Rules::match(const Client* c) const
 
421
{
 
422
    if (!matchType(c->windowType(true)))
 
423
        return false;
 
424
    if (!matchWMClass(c->resourceClass(), c->resourceName()))
 
425
        return false;
 
426
    if (!matchRole(c->windowRole()))
 
427
        return false;
 
428
    if (!matchTitle(c->caption(false)))
 
429
        return false;
 
430
    // TODO extrarole
 
431
    if (!matchClientMachine(c->wmClientMachine(false)))
 
432
        return false;
 
433
    return true;
 
434
}
 
435
 
 
436
bool Rules::update(Client* c)
 
437
{
 
438
    // TODO check this setting is for this client ?
 
439
    bool updated = false;
 
440
    if (positionrule == (SetRule)Remember) {
 
441
        if (!c->isFullScreen()) {
 
442
            QPoint new_pos = position;
 
443
            // don't use the position in the direction which is maximized
 
444
            if ((c->maximizeMode() & MaximizeHorizontal) == 0)
 
445
                new_pos.setX(c->pos().x());
 
446
            if ((c->maximizeMode() & MaximizeVertical) == 0)
 
447
                new_pos.setY(c->pos().y());
 
448
            updated = updated || position != new_pos;
 
449
            position = new_pos;
 
450
        }
 
451
    }
 
452
    if (sizerule == (SetRule)Remember) {
 
453
        if (!c->isFullScreen()) {
 
454
            QSize new_size = size;
 
455
            // don't use the position in the direction which is maximized
 
456
            if ((c->maximizeMode() & MaximizeHorizontal) == 0)
 
457
                new_size.setWidth(c->size().width());
 
458
            if ((c->maximizeMode() & MaximizeVertical) == 0)
 
459
                new_size.setHeight(c->size().height());
 
460
            updated = updated || size != new_size;
 
461
            size = new_size;
 
462
        }
 
463
    }
 
464
    if (desktoprule == (SetRule)Remember) {
 
465
        updated = updated || desktop != c->desktop();
 
466
        desktop = c->desktop();
 
467
    }
 
468
    if (maximizevertrule == (SetRule)Remember) {
 
469
        updated = updated || maximizevert != bool(c->maximizeMode() & MaximizeVertical);
 
470
        maximizevert = c->maximizeMode() & MaximizeVertical;
 
471
    }
 
472
    if (maximizehorizrule == (SetRule)Remember) {
 
473
        updated = updated || maximizehoriz != bool(c->maximizeMode() & MaximizeHorizontal);
 
474
        maximizehoriz = c->maximizeMode() & MaximizeHorizontal;
 
475
    }
 
476
    if (minimizerule == (SetRule)Remember) {
 
477
        updated = updated || minimize != c->isMinimized();
 
478
        minimize = c->isMinimized();
 
479
    }
 
480
    if (shaderule == (SetRule)Remember) {
 
481
        updated = updated || (shade != (c->shadeMode() != ShadeNone));
 
482
        shade = c->shadeMode() != ShadeNone;
 
483
    }
 
484
    if (skiptaskbarrule == (SetRule)Remember) {
 
485
        updated = updated || skiptaskbar != c->skipTaskbar();
 
486
        skiptaskbar = c->skipTaskbar();
 
487
    }
 
488
    if (skippagerrule == (SetRule)Remember) {
 
489
        updated = updated || skippager != c->skipPager();
 
490
        skippager = c->skipPager();
 
491
    }
 
492
    if (skipswitcherrule == (SetRule)Remember) {
 
493
        updated = updated || skipswitcher != c->skipSwitcher();
 
494
        skipswitcher = c->skipSwitcher();
 
495
    }
 
496
    if (aboverule == (SetRule)Remember) {
 
497
        updated = updated || above != c->keepAbove();
 
498
        above = c->keepAbove();
 
499
    }
 
500
    if (belowrule == (SetRule)Remember) {
 
501
        updated = updated || below != c->keepBelow();
 
502
        below = c->keepBelow();
 
503
    }
 
504
    if (fullscreenrule == (SetRule)Remember) {
 
505
        updated = updated || fullscreen != c->isFullScreen();
 
506
        fullscreen = c->isFullScreen();
 
507
    }
 
508
    if (noborderrule == (SetRule)Remember) {
 
509
        updated = updated || noborder != c->noBorder();
 
510
        noborder = c->noBorder();
 
511
    }
 
512
    if (opacityactiverule == (ForceRule)Force) {
 
513
        // TODO
 
514
    }
 
515
    if (opacityinactiverule == (ForceRule)Force) {
 
516
        // TODO
 
517
    }
 
518
    return updated;
 
519
}
 
520
 
 
521
#define APPLY_RULE( var, name, type ) \
 
522
    bool Rules::apply##name( type& arg, bool init ) const \
 
523
    { \
 
524
        if ( checkSetRule( var##rule, init )) \
 
525
            arg = this->var; \
 
526
        return checkSetStop( var##rule ); \
 
527
    }
 
528
 
 
529
#define APPLY_FORCE_RULE( var, name, type ) \
 
530
    bool Rules::apply##name( type& arg ) const \
 
531
    { \
 
532
        if ( checkForceRule( var##rule )) \
 
533
            arg = this->var; \
 
534
        return checkForceStop( var##rule ); \
 
535
    }
 
536
 
 
537
APPLY_FORCE_RULE(placement, Placement, Placement::Policy)
 
538
 
 
539
bool Rules::applyGeometry(QRect& rect, bool init) const
 
540
{
 
541
    QPoint p = rect.topLeft();
 
542
    QSize s = rect.size();
 
543
    bool ret = false; // no short-circuiting
 
544
    if (applyPosition(p, init)) {
 
545
        rect.moveTopLeft(p);
 
546
        ret = true;
 
547
    }
 
548
    if (applySize(s, init)) {
 
549
        rect.setSize(s);
 
550
        ret = true;
 
551
    }
 
552
    return ret;
 
553
}
 
554
 
 
555
bool Rules::applyPosition(QPoint& pos, bool init) const
 
556
{
 
557
    if (this->position != invalidPoint && checkSetRule(positionrule, init))
 
558
        pos = this->position;
 
559
    return checkSetStop(positionrule);
 
560
}
 
561
 
 
562
bool Rules::applySize(QSize& s, bool init) const
 
563
{
 
564
    if (this->size.isValid() && checkSetRule(sizerule, init))
 
565
        s = this->size;
 
566
    return checkSetStop(sizerule);
 
567
}
 
568
 
 
569
APPLY_FORCE_RULE(minsize, MinSize, QSize)
 
570
APPLY_FORCE_RULE(maxsize, MaxSize, QSize)
 
571
APPLY_FORCE_RULE(opacityactive, OpacityActive, int)
 
572
APPLY_FORCE_RULE(opacityinactive, OpacityInactive, int)
 
573
APPLY_FORCE_RULE(ignoreposition, IgnorePosition, bool)
 
574
APPLY_FORCE_RULE(tilingoption, TilingOption, int)
 
575
 
 
576
// the cfg. entry needs to stay named the say for backwards compatibility
 
577
bool Rules::applyIgnoreGeometry(bool& ignore) const
 
578
{
 
579
    return applyIgnorePosition(ignore);
 
580
}
 
581
 
 
582
APPLY_RULE(desktop, Desktop, int)
 
583
APPLY_FORCE_RULE(type, Type, NET::WindowType)
 
584
 
 
585
bool Rules::applyMaximizeHoriz(MaximizeMode& mode, bool init) const
 
586
{
 
587
    if (checkSetRule(maximizehorizrule, init))
 
588
        mode = static_cast< MaximizeMode >((maximizehoriz ? MaximizeHorizontal : 0) | (mode & MaximizeVertical));
 
589
    return checkSetStop(maximizehorizrule);
 
590
}
 
591
 
 
592
bool Rules::applyMaximizeVert(MaximizeMode& mode, bool init) const
 
593
{
 
594
    if (checkSetRule(maximizevertrule, init))
 
595
        mode = static_cast< MaximizeMode >((maximizevert ? MaximizeVertical : 0) | (mode & MaximizeHorizontal));
 
596
    return checkSetStop(maximizevertrule);
 
597
}
 
598
 
 
599
APPLY_RULE(minimize, Minimize, bool)
 
600
 
 
601
bool Rules::applyShade(ShadeMode& sh, bool init) const
 
602
{
 
603
    if (checkSetRule(shaderule, init)) {
 
604
        if (!this->shade)
 
605
            sh = ShadeNone;
 
606
        if (this->shade && sh == ShadeNone)
 
607
            sh = ShadeNormal;
 
608
    }
 
609
    return checkSetStop(shaderule);
 
610
}
 
611
 
 
612
APPLY_RULE(skiptaskbar, SkipTaskbar, bool)
 
613
APPLY_RULE(skippager, SkipPager, bool)
 
614
APPLY_RULE(skipswitcher, SkipSwitcher, bool)
 
615
APPLY_RULE(above, KeepAbove, bool)
 
616
APPLY_RULE(below, KeepBelow, bool)
 
617
APPLY_RULE(fullscreen, FullScreen, bool)
 
618
APPLY_RULE(noborder, NoBorder, bool)
 
619
APPLY_FORCE_RULE(blockcompositing, BlockCompositing, bool)
 
620
APPLY_FORCE_RULE(fsplevel, FSP, int)
 
621
APPLY_FORCE_RULE(acceptfocus, AcceptFocus, bool)
 
622
APPLY_FORCE_RULE(moveresizemode, MoveResizeMode, Options::MoveResizeMode)
 
623
APPLY_FORCE_RULE(closeable, Closeable, bool)
 
624
APPLY_FORCE_RULE(autogroup, Autogrouping, bool)
 
625
APPLY_FORCE_RULE(autogroupfg, AutogroupInForeground, bool)
 
626
APPLY_FORCE_RULE(autogroupid, AutogroupById, QString)
 
627
APPLY_FORCE_RULE(strictgeometry, StrictGeometry, bool)
 
628
APPLY_RULE(shortcut, Shortcut, QString)
 
629
APPLY_FORCE_RULE(disableglobalshortcuts, DisableGlobalShortcuts, bool)
 
630
 
 
631
 
 
632
#undef APPLY_RULE
 
633
#undef APPLY_FORCE_RULE
 
634
 
 
635
bool Rules::isTemporary() const
 
636
{
 
637
    return temporary_state > 0;
 
638
}
 
639
 
 
640
bool Rules::discardTemporary(bool force)
 
641
{
 
642
    if (temporary_state == 0)   // not temporary
 
643
        return false;
 
644
    if (force || --temporary_state == 0) { // too old
 
645
        delete this;
 
646
        return true;
 
647
    }
 
648
    return false;
 
649
}
 
650
 
 
651
#define DISCARD_USED_SET_RULE( var ) \
 
652
    do { \
 
653
        if ( var##rule == ( SetRule ) ApplyNow || ( withdrawn && var##rule == ( SetRule ) ForceTemporarily )) \
 
654
            var##rule = UnusedSetRule; \
 
655
    } while ( false )
 
656
#define DISCARD_USED_FORCE_RULE( var ) \
 
657
    do { \
 
658
        if ( withdrawn && var##rule == ( ForceRule ) ForceTemporarily ) \
 
659
            var##rule = UnusedForceRule; \
 
660
    } while ( false )
 
661
 
 
662
void Rules::discardUsed(bool withdrawn)
 
663
{
 
664
    DISCARD_USED_FORCE_RULE(placement);
 
665
    DISCARD_USED_SET_RULE(position);
 
666
    DISCARD_USED_SET_RULE(size);
 
667
    DISCARD_USED_FORCE_RULE(minsize);
 
668
    DISCARD_USED_FORCE_RULE(maxsize);
 
669
    DISCARD_USED_FORCE_RULE(opacityactive);
 
670
    DISCARD_USED_FORCE_RULE(opacityinactive);
 
671
    DISCARD_USED_FORCE_RULE(tilingoption);
 
672
    DISCARD_USED_FORCE_RULE(ignoreposition);
 
673
    DISCARD_USED_SET_RULE(desktop);
 
674
    DISCARD_USED_FORCE_RULE(type);
 
675
    DISCARD_USED_SET_RULE(maximizevert);
 
676
    DISCARD_USED_SET_RULE(maximizehoriz);
 
677
    DISCARD_USED_SET_RULE(minimize);
 
678
    DISCARD_USED_SET_RULE(shade);
 
679
    DISCARD_USED_SET_RULE(skiptaskbar);
 
680
    DISCARD_USED_SET_RULE(skippager);
 
681
    DISCARD_USED_SET_RULE(skipswitcher);
 
682
    DISCARD_USED_SET_RULE(above);
 
683
    DISCARD_USED_SET_RULE(below);
 
684
    DISCARD_USED_SET_RULE(fullscreen);
 
685
    DISCARD_USED_SET_RULE(noborder);
 
686
    DISCARD_USED_FORCE_RULE(blockcompositing);
 
687
    DISCARD_USED_FORCE_RULE(fsplevel);
 
688
    DISCARD_USED_FORCE_RULE(acceptfocus);
 
689
    DISCARD_USED_FORCE_RULE(moveresizemode);
 
690
    DISCARD_USED_FORCE_RULE(closeable);
 
691
    DISCARD_USED_FORCE_RULE(autogroup);
 
692
    DISCARD_USED_FORCE_RULE(autogroupfg);
 
693
    DISCARD_USED_FORCE_RULE(autogroupid);
 
694
    DISCARD_USED_FORCE_RULE(strictgeometry);
 
695
    DISCARD_USED_SET_RULE(shortcut);
 
696
    DISCARD_USED_FORCE_RULE(disableglobalshortcuts);
 
697
}
 
698
#undef DISCARD_USED_SET_RULE
 
699
#undef DISCARD_USED_FORCE_RULE
 
700
 
 
701
#endif
 
702
 
 
703
QDebug& operator<<(QDebug& stream, const Rules* r)
 
704
{
 
705
    return stream << "[" << r->description << ":" << r->wmclass << "]" ;
 
706
}
 
707
 
 
708
#ifndef KCMRULES
 
709
void WindowRules::discardTemporary()
 
710
{
 
711
    QVector< Rules* >::Iterator it2 = rules.begin();
 
712
    for (QVector< Rules* >::Iterator it = rules.begin();
 
713
            it != rules.end();
 
714
       ) {
 
715
        if ((*it)->discardTemporary(true))
 
716
            ++it;
 
717
        else {
 
718
            *it2++ = *it++;
 
719
        }
 
720
    }
 
721
    rules.erase(it2, rules.end());
 
722
}
 
723
 
 
724
void WindowRules::update(Client* c)
 
725
{
 
726
    bool updated = false;
 
727
    for (QVector< Rules* >::ConstIterator it = rules.constBegin();
 
728
            it != rules.constEnd();
 
729
            ++it)
 
730
        if ((*it)->update(c))    // no short-circuiting here
 
731
            updated = true;
 
732
    if (updated)
 
733
        Workspace::self()->rulesUpdated();
 
734
}
 
735
 
 
736
#define CHECK_RULE( rule, type ) \
 
737
    type WindowRules::check##rule( type arg, bool init ) const \
 
738
    { \
 
739
        if ( rules.count() == 0 ) \
 
740
            return arg; \
 
741
        type ret = arg; \
 
742
        for ( QVector< Rules* >::ConstIterator it = rules.constBegin(); \
 
743
                it != rules.constEnd(); \
 
744
                ++it ) \
 
745
        { \
 
746
            if ( (*it)->apply##rule( ret, init )) \
 
747
                break; \
 
748
        } \
 
749
        return ret; \
 
750
    }
 
751
 
 
752
#define CHECK_FORCE_RULE( rule, type ) \
 
753
    type WindowRules::check##rule( type arg ) const \
 
754
    { \
 
755
        if ( rules.count() == 0 ) \
 
756
            return arg; \
 
757
        type ret = arg; \
 
758
        for ( QVector< Rules* >::ConstIterator it = rules.begin(); \
 
759
                it != rules.end(); \
 
760
                ++it ) \
 
761
        { \
 
762
            if ( (*it)->apply##rule( ret )) \
 
763
                break; \
 
764
        } \
 
765
        return ret; \
 
766
    }
 
767
 
 
768
CHECK_FORCE_RULE(Placement, Placement::Policy)
 
769
 
 
770
QRect WindowRules::checkGeometry(QRect rect, bool init) const
 
771
{
 
772
    return QRect(checkPosition(rect.topLeft(), init), checkSize(rect.size(), init));
 
773
}
 
774
 
 
775
CHECK_RULE(Position, QPoint)
 
776
CHECK_RULE(Size, QSize)
 
777
CHECK_FORCE_RULE(MinSize, QSize)
 
778
CHECK_FORCE_RULE(MaxSize, QSize)
 
779
CHECK_FORCE_RULE(OpacityActive, int)
 
780
CHECK_FORCE_RULE(OpacityInactive, int)
 
781
CHECK_FORCE_RULE(TilingOption, int)
 
782
CHECK_FORCE_RULE(IgnorePosition, bool)
 
783
 
 
784
bool WindowRules::checkIgnoreGeometry(bool ignore) const
 
785
{
 
786
    return checkIgnorePosition(ignore);
 
787
}
 
788
 
 
789
CHECK_RULE(Desktop, int)
 
790
CHECK_FORCE_RULE(Type, NET::WindowType)
 
791
CHECK_RULE(MaximizeVert, KDecorationDefines::MaximizeMode)
 
792
CHECK_RULE(MaximizeHoriz, KDecorationDefines::MaximizeMode)
 
793
 
 
794
KDecorationDefines::MaximizeMode WindowRules::checkMaximize(MaximizeMode mode, bool init) const
 
795
{
 
796
    bool vert = checkMaximizeVert(mode, init) & MaximizeVertical;
 
797
    bool horiz = checkMaximizeHoriz(mode, init) & MaximizeHorizontal;
 
798
    return static_cast< MaximizeMode >((vert ? MaximizeVertical : 0) | (horiz ? MaximizeHorizontal : 0));
 
799
}
 
800
 
 
801
CHECK_RULE(Minimize, bool)
 
802
CHECK_RULE(Shade, ShadeMode)
 
803
CHECK_RULE(SkipTaskbar, bool)
 
804
CHECK_RULE(SkipPager, bool)
 
805
CHECK_RULE(SkipSwitcher, bool)
 
806
CHECK_RULE(KeepAbove, bool)
 
807
CHECK_RULE(KeepBelow, bool)
 
808
CHECK_RULE(FullScreen, bool)
 
809
CHECK_RULE(NoBorder, bool)
 
810
CHECK_FORCE_RULE(BlockCompositing, bool)
 
811
CHECK_FORCE_RULE(FSP, int)
 
812
CHECK_FORCE_RULE(AcceptFocus, bool)
 
813
CHECK_FORCE_RULE(MoveResizeMode, Options::MoveResizeMode)
 
814
CHECK_FORCE_RULE(Closeable, bool)
 
815
CHECK_FORCE_RULE(Autogrouping, bool)
 
816
CHECK_FORCE_RULE(AutogroupInForeground, bool)
 
817
CHECK_FORCE_RULE(AutogroupById, QString)
 
818
CHECK_FORCE_RULE(StrictGeometry, bool)
 
819
CHECK_RULE(Shortcut, QString)
 
820
CHECK_FORCE_RULE(DisableGlobalShortcuts, bool)
 
821
 
 
822
#undef CHECK_RULE
 
823
#undef CHECK_FORCE_RULE
 
824
 
 
825
// Client
 
826
 
 
827
void Client::setupWindowRules(bool ignore_temporary)
 
828
{
 
829
    client_rules = workspace()->findWindowRules(this, ignore_temporary);
 
830
    // check only after getting the rules, because there may be a rule forcing window type
 
831
    if (isTopMenu())  // TODO cannot have restrictions
 
832
        client_rules = WindowRules();
 
833
}
 
834
 
 
835
// Applies Force, ForceTemporarily and ApplyNow rules
 
836
// Used e.g. after the rules have been modified using the kcm.
 
837
void Client::applyWindowRules()
 
838
{
 
839
    // apply force rules
 
840
    // Placement - does need explicit update, just like some others below
 
841
    // Geometry : setGeometry() doesn't check rules
 
842
    QRect orig_geom = QRect(pos(), sizeForClientSize(clientSize()));   // handle shading
 
843
    QRect geom = client_rules.checkGeometry(orig_geom);
 
844
    if (geom != orig_geom)
 
845
        setGeometry(geom);
 
846
    // MinSize, MaxSize handled by Geometry
 
847
    // IgnorePosition
 
848
    setDesktop(desktop());
 
849
    // Type
 
850
    maximize(maximizeMode());
 
851
    // Minimize : functions don't check, and there are two functions
 
852
    if (client_rules.checkMinimize(isMinimized()))
 
853
        minimize();
 
854
    else
 
855
        unminimize();
 
856
    setShade(shadeMode());
 
857
    setSkipTaskbar(skipTaskbar(), true);
 
858
    setSkipPager(skipPager());
 
859
    setSkipSwitcher(skipSwitcher());
 
860
    setKeepAbove(keepAbove());
 
861
    setKeepBelow(keepBelow());
 
862
    setFullScreen(isFullScreen(), true);
 
863
    setNoBorder(noBorder());
 
864
    // FSP
 
865
    // AcceptFocus :
 
866
    if (workspace()->mostRecentlyActivatedClient() == this
 
867
            && !client_rules.checkAcceptFocus(true))
 
868
        workspace()->activateNextClient(this);
 
869
    // MoveResizeMode
 
870
    // Closeable
 
871
    QSize s = adjustedSize();
 
872
    if (s != size())
 
873
        resizeWithChecks(s);
 
874
    // Autogrouping : Only checked on window manage
 
875
    // AutogroupInForeground : Only checked on window manage
 
876
    // AutogroupById : Only checked on window manage
 
877
    // StrictGeometry
 
878
    setShortcut(rules()->checkShortcut(shortcut().toString()));
 
879
    // see also Client::setActive()
 
880
    if (isActive()) {
 
881
        setOpacity(rules()->checkOpacityActive(qRound(opacity() * 100.0)) / 100.0);
 
882
        workspace()->disableGlobalShortcutsForClient(rules()->checkDisableGlobalShortcuts(false));
 
883
    } else
 
884
        setOpacity(rules()->checkOpacityInactive(qRound(opacity() * 100.0)) / 100.0);
 
885
}
 
886
 
 
887
void Client::updateWindowRules()
 
888
{
 
889
    if (!isManaged())  // not fully setup yet
 
890
        return;
 
891
    if (workspace()->rulesUpdatesDisabled())
 
892
        return;
 
893
    client_rules.update(this);
 
894
}
 
895
 
 
896
void Client::finishWindowRules()
 
897
{
 
898
    updateWindowRules();
 
899
    client_rules = WindowRules();
 
900
}
 
901
 
 
902
// Workspace
 
903
 
 
904
WindowRules Workspace::findWindowRules(const Client* c, bool ignore_temporary)
 
905
{
 
906
    QVector< Rules* > ret;
 
907
    for (QList< Rules* >::Iterator it = rules.begin();
 
908
            it != rules.end();
 
909
       ) {
 
910
        if (ignore_temporary && (*it)->isTemporary()) {
 
911
            ++it;
 
912
            continue;
 
913
        }
 
914
        if ((*it)->match(c)) {
 
915
            Rules* rule = *it;
 
916
            kDebug(1212) << "Rule found:" << rule << ":" << c;
 
917
            if (rule->isTemporary())
 
918
                it = rules.erase(it);
 
919
            else
 
920
                ++it;
 
921
            ret.append(rule);
 
922
            continue;
 
923
        }
 
924
        ++it;
 
925
    }
 
926
    return WindowRules(ret);
 
927
}
 
928
 
 
929
void Workspace::editWindowRules(Client* c, bool whole_app)
 
930
{
 
931
    writeWindowRules();
 
932
    QStringList args;
 
933
    args << "--wid" << QString::number(c->window());
 
934
    if (whole_app)
 
935
        args << "--whole-app";
 
936
    KToolInvocation::kdeinitExec("kwin_rules_dialog", args);
 
937
}
 
938
 
 
939
void Workspace::loadWindowRules()
 
940
{
 
941
    while (!rules.isEmpty()) {
 
942
        delete rules.front();
 
943
        rules.pop_front();
 
944
    }
 
945
    KConfig cfg("kwinrulesrc", KConfig::NoGlobals);
 
946
    int count = cfg.group("General").readEntry("count", 0);
 
947
    for (int i = 1;
 
948
            i <= count;
 
949
            ++i) {
 
950
        KConfigGroup cg(&cfg, QString::number(i));
 
951
        Rules* rule = new Rules(cg);
 
952
        rules.append(rule);
 
953
    }
 
954
}
 
955
 
 
956
void Workspace::writeWindowRules()
 
957
{
 
958
    rulesUpdatedTimer.stop();
 
959
    KConfig cfg("kwinrulesrc", KConfig::NoGlobals);
 
960
    QStringList groups = cfg.groupList();
 
961
    for (QStringList::ConstIterator it = groups.constBegin();
 
962
            it != groups.constEnd();
 
963
            ++it)
 
964
        cfg.deleteGroup(*it);
 
965
    cfg.group("General").writeEntry("count", rules.count());
 
966
    int i = 1;
 
967
    for (QList< Rules* >::ConstIterator it = rules.constBegin();
 
968
            it != rules.constEnd();
 
969
            ++it) {
 
970
        if ((*it)->isTemporary())
 
971
            continue;
 
972
        KConfigGroup cg(&cfg, QString::number(i));
 
973
        (*it)->write(cg);
 
974
        ++i;
 
975
    }
 
976
}
 
977
 
 
978
void Workspace::gotTemporaryRulesMessage(const QString& message)
 
979
{
 
980
    bool was_temporary = false;
 
981
    for (QList< Rules* >::ConstIterator it = rules.constBegin();
 
982
            it != rules.constEnd();
 
983
            ++it)
 
984
        if ((*it)->isTemporary())
 
985
            was_temporary = true;
 
986
    Rules* rule = new Rules(message, true);
 
987
    rules.prepend(rule);   // highest priority first
 
988
    if (!was_temporary)
 
989
        QTimer::singleShot(60000, this, SLOT(cleanupTemporaryRules()));
 
990
}
 
991
 
 
992
void Workspace::cleanupTemporaryRules()
 
993
{
 
994
    bool has_temporary = false;
 
995
    for (QList< Rules* >::Iterator it = rules.begin();
 
996
            it != rules.end();
 
997
       ) {
 
998
        if ((*it)->discardTemporary(false))
 
999
            it = rules.erase(it);
 
1000
        else {
 
1001
            if ((*it)->isTemporary())
 
1002
                has_temporary = true;
 
1003
            ++it;
 
1004
        }
 
1005
    }
 
1006
    if (has_temporary)
 
1007
        QTimer::singleShot(60000, this, SLOT(cleanupTemporaryRules()));
 
1008
}
 
1009
 
 
1010
void Workspace::discardUsedWindowRules(Client* c, bool withdrawn)
 
1011
{
 
1012
    bool updated = false;
 
1013
    for (QList< Rules* >::Iterator it = rules.begin();
 
1014
            it != rules.end();
 
1015
       ) {
 
1016
        if (c->rules()->contains(*it)) {
 
1017
            updated = true;
 
1018
            (*it)->discardUsed(withdrawn);
 
1019
            if ((*it)->isEmpty()) {
 
1020
                c->removeRule(*it);
 
1021
                Rules* r = *it;
 
1022
                it = rules.erase(it);
 
1023
                delete r;
 
1024
                continue;
 
1025
            }
 
1026
        }
 
1027
        ++it;
 
1028
    }
 
1029
    if (updated)
 
1030
        rulesUpdated();
 
1031
}
 
1032
 
 
1033
void Workspace::rulesUpdated()
 
1034
{
 
1035
    rulesUpdatedTimer.setSingleShot(true);
 
1036
    rulesUpdatedTimer.start(1000);
 
1037
}
 
1038
 
 
1039
void Workspace::disableRulesUpdates(bool disable)
 
1040
{
 
1041
    rules_updates_disabled = disable;
 
1042
    if (!disable)
 
1043
        foreach (Client * c, clients)
 
1044
        c->updateWindowRules();
 
1045
}
 
1046
 
 
1047
#endif
 
1048
 
 
1049
} // namespace