diff options
Diffstat (limited to 'src/ClientPattern.cc')
-rw-r--r-- | src/ClientPattern.cc | 251 |
1 files changed, 251 insertions, 0 deletions
diff --git a/src/ClientPattern.cc b/src/ClientPattern.cc new file mode 100644 index 0000000..f41ccb0 --- /dev/null +++ b/src/ClientPattern.cc | |||
@@ -0,0 +1,251 @@ | |||
1 | // ClientPattern.cc for Fluxbox Window Manager | ||
2 | // Copyright (c) 2003 Henrik Kinnunen (fluxgen at users.sourceforge.net) | ||
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,v 1.6 2003/12/17 01:19:39 fluxgen Exp $ | ||
24 | |||
25 | #include "ClientPattern.hh" | ||
26 | #include "RegExp.hh" | ||
27 | #include "StringUtil.hh" | ||
28 | #include "WinClient.hh" | ||
29 | |||
30 | // use GNU extensions | ||
31 | #ifndef _GNU_SOURCE | ||
32 | #define _GNU_SOURCE | ||
33 | #endif // _GNU_SOURCE | ||
34 | |||
35 | |||
36 | #include <iostream> | ||
37 | #include <fstream> | ||
38 | #include <string> | ||
39 | #include <memory> | ||
40 | #include <cstdio> | ||
41 | |||
42 | // needed as well for index on some systems (e.g. solaris) | ||
43 | #include <strings.h> | ||
44 | |||
45 | #ifdef HAVE_CONFIG_H | ||
46 | #include "config.h" | ||
47 | #endif // HAVE_CONFIG_H | ||
48 | |||
49 | #ifdef HAVE_SSTREAM | ||
50 | #include <sstream> | ||
51 | #define FB_istringstream istringstream | ||
52 | #elif HAVE_STRSTREAM | ||
53 | #include <strstream> | ||
54 | #define FB_istringstream istrstream | ||
55 | #else | ||
56 | #error "You dont have sstream or strstream headers!" | ||
57 | #endif // HAVE_STRSTREAM | ||
58 | |||
59 | |||
60 | using namespace std; | ||
61 | |||
62 | ClientPattern::ClientPattern(): | ||
63 | m_matchlimit(0), | ||
64 | m_nummatches(0) {} | ||
65 | |||
66 | // parse the given pattern (to end of line) | ||
67 | ClientPattern::ClientPattern(const char *str): | ||
68 | m_matchlimit(0), | ||
69 | m_nummatches(0) | ||
70 | { | ||
71 | /* A rough grammar of a pattern is: | ||
72 | PATTERN ::= MATCH+ LIMIT? | ||
73 | MATCH ::= '(' word ')' | ||
74 | | '(' propertyname '=' word ')' | ||
75 | LIMIT ::= '{' number '}' | ||
76 | |||
77 | i.e. one or more match definitions, followed by | ||
78 | an optional limit on the number of apps to match to | ||
79 | |||
80 | Match definitions are enclosed in parentheses, and if no | ||
81 | property name is given, then CLASSNAME is assumed. | ||
82 | If no limit is specified, no limit is applied (i.e. limit = infinity) | ||
83 | */ | ||
84 | |||
85 | int had_error = 0; | ||
86 | |||
87 | int pos = 0; | ||
88 | string match; | ||
89 | int err = 1; // for starting first loop | ||
90 | while (had_error == 0 && err > 0) { | ||
91 | err = FbTk::StringUtil::getStringBetween(match, | ||
92 | str + pos, | ||
93 | '(', ')', " \t\n", true); | ||
94 | if (err > 0) { | ||
95 | size_t eq = match.find_first_of('='); | ||
96 | if (eq == match.npos) { | ||
97 | if (!addTerm(match, NAME)) { | ||
98 | had_error = pos + match.find_first_of('(') + 1; | ||
99 | break; | ||
100 | } | ||
101 | } else { | ||
102 | // need to determine the property used | ||
103 | string memstr, expr; | ||
104 | WinProperty prop; | ||
105 | memstr.assign(match, 0, eq); // memstr = our identifier | ||
106 | expr.assign(match, eq+1, match.length()); | ||
107 | if (strcasecmp(memstr.c_str(), "name") == 0) { | ||
108 | prop = NAME; | ||
109 | } else if (strcasecmp(memstr.c_str(), "class") == 0) { | ||
110 | prop = CLASS; | ||
111 | } else if (strcasecmp(memstr.c_str(), "title") == 0) { | ||
112 | prop = TITLE; | ||
113 | } else { | ||
114 | had_error = pos + match.find_first_of('(') + 1; | ||
115 | break; | ||
116 | } | ||
117 | if (!addTerm(expr, prop)) { | ||
118 | had_error = pos + ((str+pos) - index(str+pos, '=')) + 1; | ||
119 | break; | ||
120 | } | ||
121 | } | ||
122 | pos += err; | ||
123 | } | ||
124 | } | ||
125 | if (pos == 0 && had_error == 0) { | ||
126 | // no match terms given, this is not allowed | ||
127 | had_error = 1; | ||
128 | } | ||
129 | |||
130 | if (had_error == 0) { | ||
131 | // otherwise, we check for a number | ||
132 | string number; | ||
133 | err = FbTk::StringUtil::getStringBetween(number, | ||
134 | str+pos, | ||
135 | '{', '}'); | ||
136 | if (err > 0) { | ||
137 | FB_istringstream iss(number.c_str()); | ||
138 | iss >> m_matchlimit; | ||
139 | pos+=err; | ||
140 | } | ||
141 | // we don't care if there isn't one | ||
142 | |||
143 | // there shouldn't be anything else on the line | ||
144 | match = str + pos; | ||
145 | err = match.find_first_not_of(" \t\n", pos); | ||
146 | if ((unsigned) err != match.npos) { | ||
147 | // found something, not good | ||
148 | had_error = err; | ||
149 | } | ||
150 | } | ||
151 | |||
152 | if (had_error > 0) { | ||
153 | m_matchlimit = had_error; | ||
154 | // delete all the terms | ||
155 | while (!m_terms.empty()) { | ||
156 | Term * term = m_terms.back(); | ||
157 | delete term; | ||
158 | m_terms.pop_back(); | ||
159 | } | ||
160 | } | ||
161 | } | ||
162 | |||
163 | ClientPattern::~ClientPattern() { | ||
164 | // delete all the terms | ||
165 | while (!m_terms.empty()) { | ||
166 | delete m_terms.back(); | ||
167 | m_terms.pop_back(); | ||
168 | } | ||
169 | } | ||
170 | |||
171 | // return a string representation of this pattern | ||
172 | std::string ClientPattern::toString() const { | ||
173 | string pat; | ||
174 | Terms::const_iterator it = m_terms.begin(); | ||
175 | Terms::const_iterator it_end = m_terms.end(); | ||
176 | for (; it != it_end; ++it) { | ||
177 | pat.append(" ("); | ||
178 | |||
179 | switch ((*it)->prop) { | ||
180 | case NAME: | ||
181 | // do nothing -> this is the default | ||
182 | break; | ||
183 | case CLASS: | ||
184 | pat.append("class="); | ||
185 | break; | ||
186 | case TITLE: | ||
187 | pat.append("title="); | ||
188 | break; | ||
189 | } | ||
190 | |||
191 | pat.append((*it)->orig); | ||
192 | pat.append(")"); | ||
193 | } | ||
194 | |||
195 | if (m_matchlimit > 0) { | ||
196 | char num[20]; | ||
197 | sprintf(num, " {%d}", m_matchlimit); | ||
198 | pat.append(num); | ||
199 | } | ||
200 | return pat; | ||
201 | } | ||
202 | |||
203 | // does this client match this pattern? | ||
204 | bool ClientPattern::match(const WinClient &win) const { | ||
205 | if (m_matchlimit != 0 && m_nummatches >= m_matchlimit || | ||
206 | m_terms.empty()) | ||
207 | return false; // already matched out | ||
208 | |||
209 | // regmatch everything | ||
210 | // currently, we use an "AND" policy for multiple terms | ||
211 | // changing to OR would require minor modifications in this function only | ||
212 | Terms::const_iterator it = m_terms.begin(); | ||
213 | Terms::const_iterator it_end = m_terms.end(); | ||
214 | for (; it != it_end; ++it) { | ||
215 | if (!(*it)->regexp.match(getProperty((*it)->prop, win))) | ||
216 | return false; | ||
217 | } | ||
218 | return true; | ||
219 | } | ||
220 | |||
221 | // add an expression to match against | ||
222 | // The first argument is a regular expression, the second is the member | ||
223 | // function that we wish to match against. | ||
224 | bool ClientPattern::addTerm(const std::string &str, WinProperty prop) { | ||
225 | |||
226 | Term *term = new Term(str, true); | ||
227 | term->orig = str; | ||
228 | term->prop = prop; | ||
229 | |||
230 | if (term->regexp.error()) { | ||
231 | delete term; | ||
232 | return false; | ||
233 | } | ||
234 | m_terms.push_back(term); | ||
235 | return true; | ||
236 | } | ||
237 | |||
238 | std::string ClientPattern::getProperty(WinProperty prop, const WinClient &client) const { | ||
239 | switch (prop) { | ||
240 | case TITLE: | ||
241 | return client.title(); | ||
242 | break; | ||
243 | case CLASS: | ||
244 | return client.getWMClassClass(); | ||
245 | break; | ||
246 | case NAME: | ||
247 | return client.getWMClassName(); | ||
248 | break; | ||
249 | } | ||
250 | return client.getWMClassName(); | ||
251 | } | ||