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)
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:
12
// The above copyright notice and this permission notice shall be included in
13
// all copies or substantial portions of the Software.
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.
23
// $Id: ClientPattern.cc 3903 2005-02-13 16:34:37Z fluxgen $
25
#include "ClientPattern.hh"
27
#include "WinClient.hh"
29
#include "FbTk/StringUtil.hh"
30
#include "FbTk/App.hh"
31
#include "FbTk/stringstream.hh"
49
// needed as well for index on some systems (e.g. solaris)
55
ClientPattern::ClientPattern():
59
// parse the given pattern (to end of line)
60
ClientPattern::ClientPattern(const char *str):
64
/* A rough grammar of a pattern is:
65
PATTERN ::= MATCH+ LIMIT?
66
MATCH ::= '(' word ')'
67
| '(' propertyname '=' word ')'
68
LIMIT ::= '{' number '}'
70
i.e. one or more match definitions, followed by
71
an optional limit on the number of apps to match to
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)
82
int err = 1; // for starting first loop
83
while (had_error == 0 && err > 0) {
84
err = FbTk::StringUtil::getStringBetween(match,
86
'(', ')', " \t\n", true);
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;
95
// need to determine the property used
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) {
102
} else if (strcasecmp(memstr.c_str(), "class") == 0) {
104
} else if (strcasecmp(memstr.c_str(), "title") == 0) {
106
} else if (strcasecmp(memstr.c_str(), "role") == 0) {
109
had_error = pos + match.find_first_of('(') + 1;
112
if (!addTerm(expr, prop)) {
113
had_error = pos + ((str+pos) - index(str+pos, '=')) + 1;
120
if (pos == 0 && had_error == 0) {
121
// no match terms given, this is not allowed
125
if (had_error == 0) {
126
// otherwise, we check for a number
128
err = FbTk::StringUtil::getStringBetween(number,
132
FbTk_istringstream iss(number.c_str());
136
// we don't care if there isn't one
138
// there shouldn't be anything else on the line
140
err = match.find_first_not_of(" \t\n", pos);
141
if ((unsigned) err != match.npos) {
142
// found something, not good
148
m_matchlimit = had_error;
149
// delete all the terms
150
while (!m_terms.empty()) {
151
Term * term = m_terms.back();
158
ClientPattern::~ClientPattern() {
159
// delete all the terms
160
while (!m_terms.empty()) {
161
delete m_terms.back();
166
// return a string representation of this pattern
167
std::string ClientPattern::toString() const {
169
Terms::const_iterator it = m_terms.begin();
170
Terms::const_iterator it_end = m_terms.end();
171
for (; it != it_end; ++it) {
174
switch ((*it)->prop) {
176
// do nothing -> this is the default
179
pat.append("class=");
182
pat.append("title=");
188
pat.append((*it)->orig);
192
if (m_matchlimit > 0) {
194
sprintf(num, " {%d}", m_matchlimit);
200
// does this client match this pattern?
201
bool ClientPattern::match(const WinClient &win) const {
202
if (m_matchlimit != 0 && m_nummatches >= m_matchlimit ||
204
return false; // already matched out
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)))
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) {
223
Term *term = new Term(str, true);
227
if (term->regexp.error()) {
231
m_terms.push_back(term);
235
std::string ClientPattern::getProperty(WinProperty prop, const WinClient &client) const {
238
return client.title();
241
return client.getWMClassClass();
244
return client.getWMClassName();
247
Atom wm_role = XInternAtom(FbTk::App::instance()->display(), "WM_WINDOW_ROLE", False);
248
return client.textProperty(wm_role);
251
return client.getWMClassName();