~ubuntu-branches/ubuntu/precise/fluxbox/precise

« back to all changes in this revision

Viewing changes to src/ClientPattern.cc

  • Committer: Bazaar Package Importer
  • Author(s): Dmitry E. Oboukhov
  • Date: 2008-07-01 10:38:14 UTC
  • mfrom: (2.1.12 intrepid)
  • Revision ID: james.westby@ubuntu.com-20080701103814-khx2b6il152x9p93
Tags: 1.0.0+deb1-8
* x-dev has been removed from build-depends (out-of-date package).
* Standards-Version bumped to 3.8.0.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
// ClientPattern.cc for Fluxbox Window Manager
2
 
// Copyright (c) 2003 - 2005 Henrik Kinnunen (fluxgen at fluxbox dot org)
3
 
//                and Simon Bowden    (rathnor at users.sourceforge.net)
4
 
//
5
 
// Permission is hereby granted, free of charge, to any person obtaining a
6
 
// copy of this software and associated documentation files (the "Software"),
7
 
// to deal in the Software without restriction, including without limitation
8
 
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
9
 
// and/or sell copies of the Software, and to permit persons to whom the
10
 
// Software is furnished to do so, subject to the following conditions:
11
 
//
12
 
// The above copyright notice and this permission notice shall be included in
13
 
// all copies or substantial portions of the Software.
14
 
//
15
 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
 
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
 
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18
 
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
 
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20
 
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21
 
// DEALINGS IN THE SOFTWARE.
22
 
 
23
 
// $Id: ClientPattern.cc 3903 2005-02-13 16:34:37Z fluxgen $
24
 
 
25
 
#include "ClientPattern.hh"
26
 
#include "RegExp.hh"
27
 
#include "WinClient.hh"
28
 
 
29
 
#include "FbTk/StringUtil.hh"
30
 
#include "FbTk/App.hh"
31
 
#include "FbTk/stringstream.hh"
32
 
 
33
 
// use GNU extensions
34
 
#ifndef _GNU_SOURCE
35
 
#define _GNU_SOURCE
36
 
#endif // _GNU_SOURCE
37
 
 
38
 
 
39
 
#include <iostream>
40
 
#include <fstream>
41
 
#include <string>
42
 
#include <memory>
43
 
#ifdef HAVE_CSTDIO
44
 
  #include <cstdio>
45
 
#else
46
 
  #include <stdio.h>
47
 
#endif
48
 
 
49
 
// needed as well for index on some systems (e.g. solaris)
50
 
#include <strings.h> 
51
 
 
52
 
 
53
 
using namespace std;
54
 
 
55
 
ClientPattern::ClientPattern():
56
 
    m_matchlimit(0),
57
 
    m_nummatches(0) {}
58
 
 
59
 
// parse the given pattern (to end of line)
60
 
ClientPattern::ClientPattern(const char *str):
61
 
    m_matchlimit(0),
62
 
    m_nummatches(0)
63
 
{
64
 
    /* A rough grammar of a pattern is:
65
 
       PATTERN ::= MATCH+ LIMIT?
66
 
       MATCH ::= '(' word ')' 
67
 
                 | '(' propertyname '=' word ')'
68
 
       LIMIT ::= '{' number '}'
69
 
                 
70
 
       i.e. one or more match definitions, followed by
71
 
            an optional limit on the number of apps to match to
72
 
       
73
 
       Match definitions are enclosed in parentheses, and if no
74
 
       property name is given, then CLASSNAME is assumed.
75
 
       If no limit is specified, no limit is applied (i.e. limit = infinity)
76
 
    */
77
 
 
78
 
    int had_error = 0;
79
 
 
80
 
    int pos = 0;
81
 
    string match;
82
 
    int err = 1; // for starting first loop
83
 
    while (had_error == 0 && err > 0) {
84
 
        err = FbTk::StringUtil::getStringBetween(match, 
85
 
                                                 str + pos,
86
 
                                                 '(', ')', " \t\n", true);
87
 
        if (err > 0) {
88
 
            size_t eq = match.find_first_of('=');
89
 
            if (eq == match.npos) {
90
 
                if (!addTerm(match, NAME)) {
91
 
                    had_error = pos + match.find_first_of('(') + 1;
92
 
                    break;
93
 
                }
94
 
            } else {
95
 
                // need to determine the property used
96
 
                string memstr, expr;
97
 
                WinProperty prop;
98
 
                memstr.assign(match, 0, eq); // memstr = our identifier
99
 
                expr.assign(match, eq+1, match.length());
100
 
                if (strcasecmp(memstr.c_str(), "name") == 0) {
101
 
                    prop = NAME;
102
 
                } else if (strcasecmp(memstr.c_str(), "class") == 0) {
103
 
                    prop = CLASS;
104
 
                } else if (strcasecmp(memstr.c_str(), "title") == 0) {
105
 
                    prop = TITLE;
106
 
                } else if (strcasecmp(memstr.c_str(), "role") == 0) {
107
 
                    prop = ROLE;
108
 
                } else {
109
 
                    had_error = pos + match.find_first_of('(') + 1;
110
 
                    break;
111
 
                }
112
 
                if (!addTerm(expr, prop)) {
113
 
                    had_error = pos + ((str+pos) - index(str+pos, '=')) + 1;
114
 
                    break;
115
 
                }
116
 
            }
117
 
            pos += err;
118
 
        } 
119
 
    }
120
 
    if (pos == 0 && had_error == 0) {
121
 
        // no match terms given, this is not allowed
122
 
        had_error = 1;
123
 
    }
124
 
 
125
 
    if (had_error == 0) {
126
 
        // otherwise, we check for a number
127
 
        string number;
128
 
        err = FbTk::StringUtil::getStringBetween(number, 
129
 
                                             str+pos,
130
 
                                             '{', '}');
131
 
        if (err > 0) {
132
 
            FbTk_istringstream iss(number.c_str());
133
 
            iss >> m_matchlimit;
134
 
            pos+=err;
135
 
        }
136
 
        // we don't care if there isn't one
137
 
        
138
 
        // there shouldn't be anything else on the line
139
 
        match = str + pos;
140
 
        err = match.find_first_not_of(" \t\n", pos);
141
 
        if ((unsigned) err != match.npos) {
142
 
            // found something, not good
143
 
            had_error = err;
144
 
        }
145
 
    }
146
 
 
147
 
    if (had_error > 0) {
148
 
        m_matchlimit = had_error;
149
 
        // delete all the terms
150
 
        while (!m_terms.empty()) {
151
 
            Term * term = m_terms.back();
152
 
            delete term;
153
 
            m_terms.pop_back();
154
 
        }
155
 
    }
156
 
157
 
 
158
 
ClientPattern::~ClientPattern() {
159
 
    // delete all the terms
160
 
    while (!m_terms.empty()) {
161
 
        delete m_terms.back();
162
 
        m_terms.pop_back();
163
 
    }
164
 
}
165
 
 
166
 
// return a string representation of this pattern
167
 
std::string ClientPattern::toString() const {
168
 
    string pat;
169
 
    Terms::const_iterator it = m_terms.begin();
170
 
    Terms::const_iterator it_end = m_terms.end();
171
 
    for (; it != it_end; ++it) {
172
 
        pat.append(" (");
173
 
 
174
 
        switch ((*it)->prop) {
175
 
        case NAME:
176
 
            // do nothing -> this is the default
177
 
            break;
178
 
        case CLASS:
179
 
            pat.append("class=");
180
 
            break;
181
 
        case TITLE:
182
 
            pat.append("title=");
183
 
            break;
184
 
        case ROLE:
185
 
            pat.append("role=");
186
 
        }
187
 
 
188
 
        pat.append((*it)->orig);
189
 
        pat.append(")");
190
 
    }
191
 
 
192
 
    if (m_matchlimit > 0) {
193
 
        char num[20];
194
 
        sprintf(num, " {%d}", m_matchlimit);
195
 
        pat.append(num);
196
 
    }
197
 
    return pat;
198
 
}
199
 
 
200
 
// does this client match this pattern?
201
 
bool ClientPattern::match(const WinClient &win) const {
202
 
    if (m_matchlimit != 0 && m_nummatches >= m_matchlimit || 
203
 
        m_terms.empty())
204
 
        return false; // already matched out
205
 
 
206
 
    // regmatch everything
207
 
    // currently, we use an "AND" policy for multiple terms
208
 
    // changing to OR would require minor modifications in this function only
209
 
    Terms::const_iterator it = m_terms.begin();
210
 
    Terms::const_iterator it_end = m_terms.end();
211
 
    for (; it != it_end; ++it) {
212
 
        if (!(*it)->regexp.match(getProperty((*it)->prop, win)))
213
 
            return false;
214
 
    }
215
 
    return true;
216
 
}
217
 
 
218
 
// add an expression to match against
219
 
// The first argument is a regular expression, the second is the member
220
 
// function that we wish to match against.
221
 
bool ClientPattern::addTerm(const std::string &str, WinProperty prop) {
222
 
 
223
 
    Term *term = new Term(str, true);
224
 
    term->orig = str;
225
 
    term->prop = prop;
226
 
 
227
 
    if (term->regexp.error()) {
228
 
        delete term;
229
 
        return false;
230
 
    }
231
 
    m_terms.push_back(term);
232
 
    return true;
233
 
}
234
 
 
235
 
std::string ClientPattern::getProperty(WinProperty prop, const WinClient &client) const {
236
 
    switch (prop) {
237
 
    case TITLE:
238
 
        return client.title();
239
 
        break;
240
 
    case CLASS:
241
 
        return client.getWMClassClass();
242
 
        break;
243
 
    case NAME:
244
 
        return client.getWMClassName();
245
 
        break;
246
 
    case ROLE:
247
 
        Atom wm_role = XInternAtom(FbTk::App::instance()->display(), "WM_WINDOW_ROLE", False);
248
 
        return client.textProperty(wm_role);
249
 
        break;
250
 
    }
251
 
    return client.getWMClassName();
252
 
}