diff options
author | rathnor <rathnor> | 2003-06-12 15:12:19 (GMT) |
---|---|---|
committer | rathnor <rathnor> | 2003-06-12 15:12:19 (GMT) |
commit | e139cbb0283f7480fc26c58dc3f8a48e69011eab (patch) | |
tree | 2058a848943cb008dfe17270ad49853747212481 /src/ClientPattern.cc | |
parent | 94f1c164161e8faaf064d8b7cdfe36c9ca978055 (diff) | |
download | fluxbox-e139cbb0283f7480fc26c58dc3f8a48e69011eab.zip fluxbox-e139cbb0283f7480fc26c58dc3f8a48e69011eab.tar.bz2 |
add regular expression support in remember capabilities
see ChangeLog for details
Diffstat (limited to 'src/ClientPattern.cc')
-rw-r--r-- | src/ClientPattern.cc | 235 |
1 files changed, 235 insertions, 0 deletions
diff --git a/src/ClientPattern.cc b/src/ClientPattern.cc new file mode 100644 index 0000000..7ad3a54 --- /dev/null +++ b/src/ClientPattern.cc | |||
@@ -0,0 +1,235 @@ | |||
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.1 2003/06/12 15:12:19 rathnor 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 <sstream> | ||
38 | #include <fstream> | ||
39 | #include <string> | ||
40 | #include <memory> | ||
41 | |||
42 | using namespace std; | ||
43 | |||
44 | /******************************************************** | ||
45 | * ClientPattern * | ||
46 | ***********/ | ||
47 | |||
48 | ClientPattern::ClientPattern(): | ||
49 | m_matchlimit(0), | ||
50 | m_nummatches(0) {} | ||
51 | |||
52 | // parse the given pattern (to end of line) | ||
53 | ClientPattern::ClientPattern(const char *str): | ||
54 | m_matchlimit(0), | ||
55 | m_nummatches(0) | ||
56 | { | ||
57 | /* A rough grammar of a pattern is: | ||
58 | PATTERN ::= MATCH+ LIMIT? | ||
59 | MATCH ::= '(' word ')' | ||
60 | | '(' propertyname '=' word ')' | ||
61 | LIMIT ::= '{' number '}' | ||
62 | |||
63 | i.e. one or more match definitions, followed by | ||
64 | an optional limit on the number of apps to match to | ||
65 | |||
66 | Match definitions are enclosed in parentheses, and if no | ||
67 | property name is given, then CLASSNAME is assumed. | ||
68 | If no limit is specified, no limit is applied (i.e. limit = infinity) | ||
69 | */ | ||
70 | |||
71 | int had_error = 0; | ||
72 | |||
73 | int pos = 0; | ||
74 | string match; | ||
75 | int err = 1; // for starting first loop | ||
76 | while (had_error == 0 && err > 0) { | ||
77 | err = FbTk::StringUtil::getStringBetween(match, | ||
78 | str + pos, | ||
79 | '(', ')', " \t\n", true); | ||
80 | if (err > 0) { | ||
81 | size_t eq = match.find_first_of('='); | ||
82 | if (eq == match.npos) { | ||
83 | if (!addTerm(match, NAME)) { | ||
84 | had_error = pos + match.find_first_of('(') + 1; | ||
85 | break; | ||
86 | } | ||
87 | } else { | ||
88 | // need to determine the property used | ||
89 | string memstr, expr; | ||
90 | WinProperty prop; | ||
91 | memstr.assign(match, 0, eq); // memstr = our identifier | ||
92 | expr.assign(match, eq+1, match.length()); | ||
93 | if (strcasecmp(memstr.c_str(), "name") == 0) { | ||
94 | prop = NAME; | ||
95 | } else if (strcasecmp(memstr.c_str(), "class") == 0) { | ||
96 | prop = CLASS; | ||
97 | } else if (strcasecmp(memstr.c_str(), "title") == 0) { | ||
98 | prop = TITLE; | ||
99 | } else { | ||
100 | had_error = pos + match.find_first_of('(') + 1; | ||
101 | break; | ||
102 | } | ||
103 | if (!addTerm(expr, prop)) { | ||
104 | had_error = pos + ((str+pos) - index(str+pos, '=')) + 1; | ||
105 | break; | ||
106 | } | ||
107 | } | ||
108 | pos += err; | ||
109 | } | ||
110 | } | ||
111 | if (pos == 0 && had_error == 0) { | ||
112 | // no match terms given, this is not allowed | ||
113 | had_error = 1; | ||
114 | } | ||
115 | |||
116 | if (had_error == 0) { | ||
117 | // otherwise, we check for a number | ||
118 | string number; | ||
119 | err = FbTk::StringUtil::getStringBetween(number, | ||
120 | str+pos, | ||
121 | '{', '}'); | ||
122 | if (err > 0) { | ||
123 | istringstream iss(number.c_str()); | ||
124 | iss >> m_matchlimit; | ||
125 | pos+=err; | ||
126 | } | ||
127 | // we don't care if there isn't one | ||
128 | |||
129 | // there shouldn't be anything else on the line | ||
130 | match = str + pos; | ||
131 | err = match.find_first_not_of(" \t\n", pos); | ||
132 | if ((unsigned) err != match.npos) { | ||
133 | // found something, not good | ||
134 | had_error = err; | ||
135 | } | ||
136 | } | ||
137 | |||
138 | if (had_error > 0) { | ||
139 | m_matchlimit = had_error; | ||
140 | // delete all the terms | ||
141 | while (!m_terms.empty()) { | ||
142 | Term * term = m_terms.back(); | ||
143 | delete term; | ||
144 | m_terms.pop_back(); | ||
145 | } | ||
146 | } | ||
147 | } | ||
148 | |||
149 | ClientPattern::~ClientPattern() { | ||
150 | // delete all the terms | ||
151 | while (!m_terms.empty()) { | ||
152 | delete m_terms.back(); | ||
153 | m_terms.pop_back(); | ||
154 | } | ||
155 | } | ||
156 | |||
157 | // return a string representation of this pattern | ||
158 | std::string ClientPattern::toString() const { | ||
159 | string pat; | ||
160 | Terms::const_iterator it = m_terms.begin(); | ||
161 | Terms::const_iterator it_end = m_terms.end(); | ||
162 | for (; it != it_end; ++it) { | ||
163 | pat.append(" ("); | ||
164 | if ((*it)->prop == NAME) { | ||
165 | // do nothing -> this is the default | ||
166 | } else if ((*it)->prop == CLASS) { | ||
167 | pat.append("class="); | ||
168 | } else if ((*it)->prop == TITLE) { | ||
169 | pat.append("title="); | ||
170 | } else { | ||
171 | #ifdef DEBUG | ||
172 | cerr<<"WARNING: unknown window property, can't save properly"<<endl; | ||
173 | #endif //DEBUG | ||
174 | } | ||
175 | pat.append((*it)->orig); | ||
176 | pat.append(")"); | ||
177 | } | ||
178 | |||
179 | if (m_matchlimit > 0) { | ||
180 | char num[20]; | ||
181 | sprintf(num, " {%d}", m_matchlimit); | ||
182 | pat.append(num); | ||
183 | } | ||
184 | return pat; | ||
185 | } | ||
186 | |||
187 | // does this client match this pattern? | ||
188 | bool ClientPattern::match(const WinClient &win) const { | ||
189 | if (m_matchlimit != 0 && m_nummatches >= m_matchlimit || | ||
190 | m_terms.empty()) | ||
191 | return false; // already matched out | ||
192 | |||
193 | // regmatch everything | ||
194 | // currently, we use an "AND" policy for multiple terms | ||
195 | // changing to OR would require minor modifications in this function only | ||
196 | Terms::const_iterator it = m_terms.begin(); | ||
197 | Terms::const_iterator it_end = m_terms.end(); | ||
198 | for (; it != it_end; ++it) { | ||
199 | if (!(*it)->regexp.match(getProperty((*it)->prop, win))) | ||
200 | return false; | ||
201 | } | ||
202 | return true; | ||
203 | } | ||
204 | |||
205 | // add an expression to match against | ||
206 | // The first argument is a regular expression, the second is the member | ||
207 | // function that we wish to match against. | ||
208 | bool ClientPattern::addTerm(const std::string &str, WinProperty prop) { | ||
209 | |||
210 | Term *term = new Term(str, true); | ||
211 | term->orig = str; | ||
212 | term->prop = prop; | ||
213 | |||
214 | if (term->regexp.error()) { | ||
215 | delete term; | ||
216 | return false; | ||
217 | } | ||
218 | m_terms.push_back(term); | ||
219 | return true; | ||
220 | } | ||
221 | |||
222 | std::string ClientPattern::getProperty(WinProperty prop, const WinClient &client) const { | ||
223 | switch (prop) { | ||
224 | case TITLE: | ||
225 | return client.getTitle(); | ||
226 | break; | ||
227 | case CLASS: | ||
228 | return client.getWMClassClass(); | ||
229 | break; | ||
230 | case NAME: | ||
231 | default: | ||
232 | return client.getWMClassName(); | ||
233 | break; | ||
234 | } | ||
235 | } | ||