aboutsummaryrefslogtreecommitdiff
path: root/src/ClientPattern.cc
diff options
context:
space:
mode:
authorrathnor <rathnor>2003-06-12 15:12:19 (GMT)
committerrathnor <rathnor>2003-06-12 15:12:19 (GMT)
commite139cbb0283f7480fc26c58dc3f8a48e69011eab (patch)
tree2058a848943cb008dfe17270ad49853747212481 /src/ClientPattern.cc
parent94f1c164161e8faaf064d8b7cdfe36c9ca978055 (diff)
downloadfluxbox-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.cc235
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
42using namespace std;
43
44/********************************************************
45 * ClientPattern *
46 ***********/
47
48ClientPattern::ClientPattern():
49 m_matchlimit(0),
50 m_nummatches(0) {}
51
52// parse the given pattern (to end of line)
53ClientPattern::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
149ClientPattern::~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
158std::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?
188bool 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.
208bool 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
222std::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}