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 | |
parent | 94f1c164161e8faaf064d8b7cdfe36c9ca978055 (diff) | |
download | fluxbox_pavel-e139cbb0283f7480fc26c58dc3f8a48e69011eab.zip fluxbox_pavel-e139cbb0283f7480fc26c58dc3f8a48e69011eab.tar.bz2 |
add regular expression support in remember capabilities
see ChangeLog for details
Diffstat (limited to 'src')
-rw-r--r-- | src/ClientPattern.cc | 235 | ||||
-rw-r--r-- | src/ClientPattern.hh | 99 | ||||
-rw-r--r-- | src/Makefile.am | 6 | ||||
-rw-r--r-- | src/RegExp.cc | 94 | ||||
-rw-r--r-- | src/RegExp.hh | 66 | ||||
-rw-r--r-- | src/Remember.cc | 199 | ||||
-rw-r--r-- | src/Remember.hh | 32 |
7 files changed, 610 insertions, 121 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 | } | ||
diff --git a/src/ClientPattern.hh b/src/ClientPattern.hh new file mode 100644 index 0000000..00a3127 --- /dev/null +++ b/src/ClientPattern.hh | |||
@@ -0,0 +1,99 @@ | |||
1 | // ClientPattern.hh for Fluxbox Window Manager | ||
2 | // Copyright (c) 2002 Xavier Brouckaert | ||
3 | // Copyright (c) 2003 Henrik Kinnunen (fluxgen at users.sourceforge.net) | ||
4 | // and Simon Bowden (rathnor at users.sourceforge.net) | ||
5 | // | ||
6 | // Permission is hereby granted, free of charge, to any person obtaining a | ||
7 | // copy of this software and associated documentation files (the "Software"), | ||
8 | // to deal in the Software without restriction, including without limitation | ||
9 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, | ||
10 | // and/or sell copies of the Software, and to permit persons to whom the | ||
11 | // Software is furnished to do so, subject to the following conditions: | ||
12 | // | ||
13 | // The above copyright notice and this permission notice shall be included in | ||
14 | // all copies or substantial portions of the Software. | ||
15 | // | ||
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | ||
19 | // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||
21 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | ||
22 | // DEALINGS IN THE SOFTWARE. | ||
23 | |||
24 | // $Id: ClientPattern.hh,v 1.1 2003/06/12 15:12:19 rathnor Exp $ | ||
25 | |||
26 | #ifndef CLIENTPATTERN_HH | ||
27 | #define CLIENTPATTERN_HH | ||
28 | |||
29 | #include "RegExp.hh" | ||
30 | |||
31 | #include <string> | ||
32 | #include <list> | ||
33 | |||
34 | class WinClient; | ||
35 | |||
36 | /** | ||
37 | * This class represents a "pattern" that we can match against a | ||
38 | * Window based on various properties. | ||
39 | */ | ||
40 | class ClientPattern { | ||
41 | public: | ||
42 | ClientPattern(); | ||
43 | // create the pattern from the given string as it would appear in the | ||
44 | // apps file. the bool value returns the character at which | ||
45 | // there was a parse problem, or -1. | ||
46 | explicit ClientPattern(const char * str); | ||
47 | |||
48 | ~ClientPattern(); | ||
49 | |||
50 | // return a string representation of this pattern | ||
51 | std::string toString() const; | ||
52 | |||
53 | enum WinProperty { TITLE, CLASS, NAME }; | ||
54 | |||
55 | // does this client match this pattern? | ||
56 | bool match(const WinClient &win) const; | ||
57 | |||
58 | // add an expression to match against | ||
59 | // The first argument is a regular expression, the second is the member | ||
60 | // function that we wish to match against. | ||
61 | // returns false if the regexp wasn't valid | ||
62 | bool addTerm(const std::string &str, WinProperty prop); | ||
63 | |||
64 | inline void addMatch() { ++m_nummatches; } | ||
65 | inline void delMatch() { --m_nummatches; } | ||
66 | |||
67 | inline bool operator == (const WinClient &win) const { | ||
68 | return match(win); | ||
69 | } | ||
70 | |||
71 | // if there are no terms, then there is assumed to be an error | ||
72 | // the column of the error is stored in m_matchlimit | ||
73 | inline int error() { return (m_terms.empty())?m_matchlimit:0; } | ||
74 | |||
75 | std::string getProperty(WinProperty prop, const WinClient &winclient) const; | ||
76 | |||
77 | private: | ||
78 | // This is the type of the actual pattern we want to match against | ||
79 | // We have a "term" in the whole expression which is the full pattern | ||
80 | // we also need to keep track of the uncompiled regular expression | ||
81 | // for final output | ||
82 | |||
83 | struct Term { | ||
84 | Term(const std::string ®str, bool full_match) :regexp(regstr, full_match){}; | ||
85 | std::string orig; | ||
86 | RegExp regexp; | ||
87 | WinProperty prop; | ||
88 | }; | ||
89 | |||
90 | // our pattern is made up of a sequence of terms | ||
91 | // currently we "and" them all | ||
92 | typedef std::list<Term *> Terms; | ||
93 | |||
94 | Terms m_terms; | ||
95 | |||
96 | int m_matchlimit, m_nummatches; | ||
97 | }; | ||
98 | |||
99 | #endif // CLIENTPATTERN_HH | ||
diff --git a/src/Makefile.am b/src/Makefile.am index 0b2892a..d9288f7 100644 --- a/src/Makefile.am +++ b/src/Makefile.am | |||
@@ -48,6 +48,10 @@ gnome_SOURCE= Gnome.hh Gnome.cc | |||
48 | endif | 48 | endif |
49 | if REMEMBER_SRC | 49 | if REMEMBER_SRC |
50 | REMEMBER_SOURCE= Remember.hh Remember.cc | 50 | REMEMBER_SOURCE= Remember.hh Remember.cc |
51 | # For now we only want regexp if we have remember | ||
52 | if REGEXP_SRC | ||
53 | REGEXP_SOURCE = RegExp.hh RegExp.cc ClientPattern.hh ClientPattern.cc | ||
54 | endif | ||
51 | endif | 55 | endif |
52 | 56 | ||
53 | fluxbox_SOURCES = AtomHandler.hh ArrowButton.hh ArrowButton.cc \ | 57 | fluxbox_SOURCES = AtomHandler.hh ArrowButton.hh ArrowButton.cc \ |
@@ -76,7 +80,7 @@ fluxbox_SOURCES = AtomHandler.hh ArrowButton.hh ArrowButton.cc \ | |||
76 | IntResMenuItem.hh IntResMenuItem.cc FbMenu.hh \ | 80 | IntResMenuItem.hh IntResMenuItem.cc FbMenu.hh \ |
77 | WinClient.hh WinClient.cc \ | 81 | WinClient.hh WinClient.cc \ |
78 | Xinerama.hh \ | 82 | Xinerama.hh \ |
79 | ${REMEMBER_SOURCE} | 83 | ${REMEMBER_SOURCE} ${REGEXP_SOURCE} |
80 | 84 | ||
81 | 85 | ||
82 | LDADD=FbTk/libFbTk.a | 86 | LDADD=FbTk/libFbTk.a |
diff --git a/src/RegExp.cc b/src/RegExp.cc new file mode 100644 index 0000000..2e991da --- /dev/null +++ b/src/RegExp.cc | |||
@@ -0,0 +1,94 @@ | |||
1 | // RegExp.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: RegExp.cc,v 1.1 2003/06/12 15:12:19 rathnor Exp $ | ||
24 | |||
25 | #include "RegExp.hh" | ||
26 | |||
27 | //use GNU extensions | ||
28 | #ifndef _GNU_SOURCE | ||
29 | #define _GNU_SOURCE | ||
30 | #endif // _GNU_SOURCE | ||
31 | |||
32 | #include <string> | ||
33 | #include <iostream> | ||
34 | |||
35 | using namespace std; | ||
36 | |||
37 | |||
38 | /******************************************************** | ||
39 | * RegExp * | ||
40 | ***********/ | ||
41 | |||
42 | // full_match is to say if we match on this regexp using the full string | ||
43 | // or just a substring. Substrings aren't supported if not HAVE_REGEXP | ||
44 | RegExp::RegExp(const std::string &str, bool full_match): | ||
45 | #ifdef USE_REGEXP | ||
46 | m_regex(0) { | ||
47 | string match; | ||
48 | if (full_match) { | ||
49 | match = "^"; | ||
50 | match.append(str); | ||
51 | match.append("$"); | ||
52 | } else { | ||
53 | match = str; | ||
54 | } | ||
55 | |||
56 | m_regex = new regex_t; | ||
57 | int ret = regcomp(m_regex, match.c_str(), REG_NOSUB | REG_EXTENDED); | ||
58 | if (ret != 0) { | ||
59 | char *errstr = 0; | ||
60 | // gives us the length of the string | ||
61 | unsigned int size = regerror(ret, m_regex, errstr, 0); | ||
62 | errstr = new char[size]; | ||
63 | |||
64 | regerror(ret, m_regex, errstr, size); | ||
65 | cerr<<"Error parsing regular expression: "<<errstr<<endl; | ||
66 | delete [] errstr; | ||
67 | delete m_regex; // I don't think I regfree a failed compile? | ||
68 | m_regex = 0; | ||
69 | } | ||
70 | } | ||
71 | #else // notdef USE_REGEXP | ||
72 | m_str(str) {} | ||
73 | #endif // USE_REGEXP | ||
74 | |||
75 | RegExp::~RegExp() { | ||
76 | #ifdef USE_REGEXP | ||
77 | if (m_regex != 0) { | ||
78 | regfree(m_regex); | ||
79 | delete m_regex; | ||
80 | } | ||
81 | #endif // USE_REGEXP | ||
82 | } | ||
83 | |||
84 | bool RegExp::match(const std::string &str) { | ||
85 | #ifdef USE_REGEXP | ||
86 | if (m_regex) | ||
87 | return (regexec(m_regex, str.c_str(), 0, 0, 0) == 0); | ||
88 | else | ||
89 | return false; | ||
90 | #else // notdef USE_REGEXP | ||
91 | return (m_str == str); | ||
92 | #endif // USE_REGEXP | ||
93 | } | ||
94 | |||
diff --git a/src/RegExp.hh b/src/RegExp.hh new file mode 100644 index 0000000..9591bcb --- /dev/null +++ b/src/RegExp.hh | |||
@@ -0,0 +1,66 @@ | |||
1 | // RegExp.hh for Fluxbox Window Manager | ||
2 | // Copyright (c) 2002 Xavier Brouckaert | ||
3 | // Copyright (c) 2003 Henrik Kinnunen (fluxgen at users.sourceforge.net) | ||
4 | // and Simon Bowden (rathnor at users.sourceforge.net) | ||
5 | // | ||
6 | // Permission is hereby granted, free of charge, to any person obtaining a | ||
7 | // copy of this software and associated documentation files (the "Software"), | ||
8 | // to deal in the Software without restriction, including without limitation | ||
9 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, | ||
10 | // and/or sell copies of the Software, and to permit persons to whom the | ||
11 | // Software is furnished to do so, subject to the following conditions: | ||
12 | // | ||
13 | // The above copyright notice and this permission notice shall be included in | ||
14 | // all copies or substantial portions of the Software. | ||
15 | // | ||
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | ||
19 | // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||
21 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | ||
22 | // DEALINGS IN THE SOFTWARE. | ||
23 | |||
24 | // $Id: RegExp.hh,v 1.1 2003/06/12 15:12:19 rathnor Exp $ | ||
25 | |||
26 | #ifndef REGEXP_HH | ||
27 | #define REGEXP_HH | ||
28 | |||
29 | #include "config.h" | ||
30 | |||
31 | #include <string> | ||
32 | |||
33 | /* | ||
34 | * If USE_REGEXP isn't defined, then we match just using simple string equality | ||
35 | */ | ||
36 | |||
37 | #ifdef USE_REGEXP | ||
38 | #include <regex.h> | ||
39 | #include <sys/types.h> | ||
40 | #endif // USE_REGEXP | ||
41 | |||
42 | class WinClient; | ||
43 | |||
44 | class RegExp { | ||
45 | public: | ||
46 | RegExp(const std::string &str, bool full_match = true); | ||
47 | ~RegExp(); | ||
48 | |||
49 | bool match(const std::string &str); | ||
50 | |||
51 | #ifdef USE_REGEXP | ||
52 | inline bool error() { return m_regex == 0; } | ||
53 | #else // notdef USE_REGEXP | ||
54 | inline bool error() { return m_str == ""; } | ||
55 | #endif // USE_REGEXP | ||
56 | |||
57 | private: | ||
58 | #ifdef USE_REGEXP | ||
59 | regex_t* m_regex; | ||
60 | #else // notdef USE_REGEXP | ||
61 | std::string m_str; | ||
62 | #endif // USE_REGEXP | ||
63 | |||
64 | }; | ||
65 | |||
66 | #endif // REGEXP_HH | ||
diff --git a/src/Remember.cc b/src/Remember.cc index 5480d20..a31843b 100644 --- a/src/Remember.cc +++ b/src/Remember.cc | |||
@@ -21,9 +21,10 @@ | |||
21 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | 21 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
22 | // DEALINGS IN THE SOFTWARE. | 22 | // DEALINGS IN THE SOFTWARE. |
23 | 23 | ||
24 | // $Id: Remember.cc,v 1.23 2003/06/06 14:07:22 rathnor Exp $ | 24 | // $Id: Remember.cc,v 1.24 2003/06/12 15:12:19 rathnor Exp $ |
25 | 25 | ||
26 | #include "Remember.hh" | 26 | #include "Remember.hh" |
27 | #include "ClientPattern.hh" | ||
27 | #include "StringUtil.hh" | 28 | #include "StringUtil.hh" |
28 | #include "Screen.hh" | 29 | #include "Screen.hh" |
29 | #include "Window.hh" | 30 | #include "Window.hh" |
@@ -125,32 +126,7 @@ FbTk::Menu *createRememberMenu(Remember &remember, FluxboxWindow &win) { | |||
125 | return menu; | 126 | return menu; |
126 | }; | 127 | }; |
127 | 128 | ||
128 | std::string getWMClass(Window w) { | 129 | }; // end anonymous namespace |
129 | XClassHint ch; | ||
130 | |||
131 | if (XGetClassHint(FbTk::App::instance()->display(), w, &ch) == 0) { | ||
132 | cerr<<"Failed to read class hint!"<<endl; | ||
133 | return ""; | ||
134 | } else { | ||
135 | string instance_name; | ||
136 | if (ch.res_name != 0) { | ||
137 | instance_name = const_cast<char *>(ch.res_name); | ||
138 | XFree(ch.res_name); | ||
139 | } else | ||
140 | instance_name = ""; | ||
141 | |||
142 | if (ch.res_class != 0) { | ||
143 | //m_class_name = const_cast<char *>(ch.res_class); | ||
144 | XFree(ch.res_class); | ||
145 | } else { | ||
146 | //m_class_name = ""; | ||
147 | } | ||
148 | |||
149 | return instance_name; | ||
150 | } | ||
151 | } | ||
152 | |||
153 | }; | ||
154 | 130 | ||
155 | Application::Application() { | 131 | Application::Application() { |
156 | workspace_remember = | 132 | workspace_remember = |
@@ -165,35 +141,56 @@ Application::Application() { | |||
165 | save_on_close_remember = false; | 141 | save_on_close_remember = false; |
166 | } | 142 | } |
167 | 143 | ||
144 | /******************************************************** | ||
145 | * Remember * | ||
146 | ************/ | ||
147 | |||
168 | Remember::Remember() { | 148 | Remember::Remember() { |
169 | load(); | 149 | load(); |
170 | } | 150 | } |
171 | 151 | ||
172 | Application* Remember::add(const char* app_name) { | 152 | Remember::~Remember() { |
173 | if (!app_name) | 153 | // free our resources |
174 | return 0; | 154 | |
175 | Application* a = new Application(); | 155 | // the patterns free the "Application"s |
176 | apps[app_name] = a; | 156 | // the client mapping shouldn't need cleaning |
177 | return a; | 157 | Patterns::iterator it; |
158 | while (!m_pats.empty()) { | ||
159 | it = m_pats.begin(); | ||
160 | delete it->first; // ClientPattern | ||
161 | delete it->second; // Application | ||
162 | m_pats.erase(it); | ||
163 | } | ||
178 | } | 164 | } |
179 | 165 | ||
180 | Application* Remember::find(WinClient &winclient) { | 166 | Application* Remember::find(WinClient &winclient) { |
181 | return find(getWMClass(winclient.window()).c_str()); | 167 | // if it is already associated with a application, return that one |
182 | } | 168 | // otherwise, check it against every pattern that we've got |
183 | 169 | Clients::iterator wc_it = m_clients.find(&winclient); | |
184 | Application* Remember::add(WinClient &winclient) { | 170 | if (wc_it != m_clients.end()) |
185 | return add(getWMClass(winclient.window()).c_str()); | 171 | return wc_it->second; |
172 | else { | ||
173 | Patterns::iterator it = m_pats.begin(); | ||
174 | for (; it != m_pats.end(); it++) | ||
175 | if (it->first->match(winclient)) { | ||
176 | it->first->addMatch(); | ||
177 | m_clients[&winclient] = it->second; | ||
178 | return it->second; | ||
179 | } | ||
180 | } | ||
181 | // oh well, no matches | ||
182 | return 0; | ||
186 | } | 183 | } |
187 | 184 | ||
188 | 185 | Application * Remember::add(WinClient &winclient) { | |
189 | Application* Remember::find(const char* app_name) { | 186 | ClientPattern *p = new ClientPattern(); |
190 | if (!app_name) | 187 | Application *app = new Application(); |
191 | return 0; | 188 | // by default, we match against the WMClass of a window. |
192 | Apps::iterator i = apps.find(app_name); | 189 | p->addTerm(p->getProperty(ClientPattern::NAME, winclient), ClientPattern::NAME); |
193 | if (i != apps.end()) | 190 | m_clients[&winclient] = app; |
194 | return i->second; | 191 | p->addMatch(); |
195 | else | 192 | m_pats.push_back(make_pair(p, app)); |
196 | return 0; | 193 | return app; |
197 | } | 194 | } |
198 | 195 | ||
199 | int Remember::parseApp(ifstream &file, Application &app) { | 196 | int Remember::parseApp(ifstream &file, Application &app) { |
@@ -315,31 +312,24 @@ void Remember::load() { | |||
315 | if (line[0] == '#') | 312 | if (line[0] == '#') |
316 | continue; | 313 | continue; |
317 | string key; | 314 | string key; |
318 | int pos=0; | 315 | int err=0; |
319 | int err = FbTk::StringUtil::getStringBetween(key, | 316 | int pos = FbTk::StringUtil::getStringBetween(key, |
320 | line.c_str(), | 317 | line.c_str(), |
321 | '[', ']'); | 318 | '[', ']'); |
322 | 319 | ||
323 | if (err > 0 && key == "app") { | 320 | if (pos > 0 && key == "app") { |
324 | pos += err; | 321 | ClientPattern *pat = new ClientPattern(line.c_str() + pos); |
325 | string label; | 322 | if ((err = pat->error()) == 0) { |
326 | err = FbTk::StringUtil::getStringBetween(label, | 323 | Application *app = new Application(); |
327 | line.c_str()+pos, | 324 | m_pats.push_back(make_pair(pat, app)); |
328 | '(', ')'); | ||
329 | if (err>0) { | ||
330 | Application *app = 0; | ||
331 | Apps::iterator i = apps.find(label); | ||
332 | if (i == apps.end()) { | ||
333 | app = new Application(); | ||
334 | apps[label] = app; | ||
335 | } else | ||
336 | app = i->second; | ||
337 | row += parseApp(apps_file, *app); | 325 | row += parseApp(apps_file, *app); |
338 | } else | 326 | } else { |
339 | cerr<<"Error1 in apps file. Line("<<row<<")"<<endl; | 327 | cerr<<"Error reading apps file at line "<<row<<", column "<<(err+pos)<<"."<<endl; |
328 | delete pat; // since it didn't work | ||
329 | } | ||
340 | } else | 330 | } else |
341 | cerr<<"Error2 in apps file. Line("<<row<<")"<<endl; | 331 | cerr<<"Error in apps file on line "<<row<<"."<<endl; |
342 | 332 | ||
343 | } | 333 | } |
344 | } else { | 334 | } else { |
345 | #ifdef DEBUG | 335 | #ifdef DEBUG |
@@ -358,28 +348,28 @@ void Remember::save() { | |||
358 | string apps_string; | 348 | string apps_string; |
359 | Fluxbox::instance()->getDefaultDataFilename("apps", apps_string); | 349 | Fluxbox::instance()->getDefaultDataFilename("apps", apps_string); |
360 | ofstream apps_file(apps_string.c_str()); | 350 | ofstream apps_file(apps_string.c_str()); |
361 | Apps::iterator it = apps.begin(); | 351 | Patterns::iterator it = m_pats.begin(); |
362 | Apps::iterator it_end = apps.end(); | 352 | Patterns::iterator it_end = m_pats.end(); |
363 | for (; it != it_end; ++it) { | 353 | for (; it != it_end; ++it) { |
364 | apps_file << "[app] (" << it->first << ")" << endl; | 354 | apps_file << "[app]"<<it->first->toString()<<endl; |
365 | Application *a = it->second; | 355 | Application &a = *it->second; |
366 | if (a->workspace_remember) { | 356 | if (a.workspace_remember) { |
367 | apps_file << " [Workspace]\t{" << a->workspace << "}" << endl; | 357 | apps_file << " [Workspace]\t{" << a.workspace << "}" << endl; |
368 | } | 358 | } |
369 | if (a->dimensions_remember) { | 359 | if (a.dimensions_remember) { |
370 | apps_file << " [Dimensions]\t{" << a->w << " " << a->h << "}" << endl; | 360 | apps_file << " [Dimensions]\t{" << a.w << " " << a.h << "}" << endl; |
371 | } | 361 | } |
372 | if (a->position_remember) { | 362 | if (a.position_remember) { |
373 | apps_file << " [Position]\t{" << a->x << " " << a->y << "}" << endl; | 363 | apps_file << " [Position]\t{" << a.x << " " << a.y << "}" << endl; |
374 | } | 364 | } |
375 | if (a->shadedstate_remember) { | 365 | if (a.shadedstate_remember) { |
376 | apps_file << " [Shaded]\t{" << ((a->shadedstate)?"yes":"no") << "}" << endl; | 366 | apps_file << " [Shaded]\t{" << ((a.shadedstate)?"yes":"no") << "}" << endl; |
377 | } | 367 | } |
378 | if (a->tabstate_remember) { | 368 | if (a.tabstate_remember) { |
379 | apps_file << " [Tab]\t\t{" << ((a->tabstate)?"yes":"no") << "}" << endl; | 369 | apps_file << " [Tab]\t\t{" << ((a.tabstate)?"yes":"no") << "}" << endl; |
380 | } | 370 | } |
381 | if (a->decostate_remember) { | 371 | if (a.decostate_remember) { |
382 | switch (a->decostate) { | 372 | switch (a.decostate) { |
383 | case (0) : | 373 | case (0) : |
384 | apps_file << " [Deco]\t{NONE}" << endl; | 374 | apps_file << " [Deco]\t{NONE}" << endl; |
385 | break; | 375 | break; |
@@ -401,21 +391,21 @@ void Remember::save() { | |||
401 | apps_file << " [Deco]\t{BORDER}" << endl; | 391 | apps_file << " [Deco]\t{BORDER}" << endl; |
402 | break; | 392 | break; |
403 | default: | 393 | default: |
404 | apps_file << " [Deco]\t{0x"<<hex<<a->decostate<<dec<<"}"<<endl; | 394 | apps_file << " [Deco]\t{0x"<<hex<<a.decostate<<dec<<"}"<<endl; |
405 | break; | 395 | break; |
406 | } | 396 | } |
407 | } | 397 | } |
408 | if (a->stuckstate_remember) { | 398 | if (a.stuckstate_remember) { |
409 | apps_file << " [Sticky]\t{" << ((a->stuckstate)?"yes":"no") << "}" << endl; | 399 | apps_file << " [Sticky]\t{" << ((a.stuckstate)?"yes":"no") << "}" << endl; |
410 | } | 400 | } |
411 | if (a->jumpworkspace_remember) { | 401 | if (a.jumpworkspace_remember) { |
412 | apps_file << " [Jump]\t{" << ((a->jumpworkspace)?"yes":"no") << "}" << endl; | 402 | apps_file << " [Jump]\t{" << ((a.jumpworkspace)?"yes":"no") << "}" << endl; |
413 | } | 403 | } |
414 | if (a->layer_remember) { | 404 | if (a.layer_remember) { |
415 | apps_file << " [Layer]\t{" << a->layer << "}" << endl; | 405 | apps_file << " [Layer]\t{" << a.layer << "}" << endl; |
416 | } | 406 | } |
417 | if (a->save_on_close_remember) { | 407 | if (a.save_on_close_remember) { |
418 | apps_file << " [Close]\t{" << ((a->save_on_close)?"yes":"no") << "}" << endl; | 408 | apps_file << " [Close]\t{" << ((a.save_on_close)?"yes":"no") << "}" << endl; |
419 | } | 409 | } |
420 | apps_file << "[end]" << endl; | 410 | apps_file << "[end]" << endl; |
421 | } | 411 | } |
@@ -563,6 +553,7 @@ void Remember::setupWindow(FluxboxWindow &win) { | |||
563 | if (winclient.transientFor()) { | 553 | if (winclient.transientFor()) { |
564 | // still put something in the menu so people don't get confused | 554 | // still put something in the menu so people don't get confused |
565 | // so, we add a disabled item... | 555 | // so, we add a disabled item... |
556 | // TODO: nls | ||
566 | FbTk::MenuItem *item = new FbTk::MenuItem("Remember..."); | 557 | FbTk::MenuItem *item = new FbTk::MenuItem("Remember..."); |
567 | item->setEnabled(false); | 558 | item->setEnabled(false); |
568 | win.menu().insert(item, menupos); | 559 | win.menu().insert(item, menupos); |
@@ -623,10 +614,15 @@ void Remember::setupWindow(FluxboxWindow &win) { | |||
623 | void Remember::updateWindowClose(FluxboxWindow &win) { | 614 | void Remember::updateWindowClose(FluxboxWindow &win) { |
624 | // This doesn't work at present since fluxbox.cc is missing the windowclose stuff. | 615 | // This doesn't work at present since fluxbox.cc is missing the windowclose stuff. |
625 | // I don't trust it (particularly winClient()) while this is the case | 616 | // I don't trust it (particularly winClient()) while this is the case |
617 | |||
626 | return; | 618 | return; |
627 | 619 | ||
628 | WinClient &winclient = win.winClient(); | 620 | WinClient &winclient = win.winClient(); |
629 | Application *app = find(winclient); | 621 | Application *app = find(winclient); |
622 | Clients::iterator wc_it = m_clients.find(&win.winClient()); | ||
623 | |||
624 | if (wc_it != m_clients.end()) | ||
625 | m_clients.erase(wc_it); | ||
630 | 626 | ||
631 | if (!app || !(app->save_on_close_remember && app->save_on_close)) | 627 | if (!app || !(app->save_on_close_remember && app->save_on_close)) |
632 | return; | 628 | return; |
@@ -636,23 +632,6 @@ void Remember::updateWindowClose(FluxboxWindow &win) { | |||
636 | rememberAttrib(winclient, (Attribute) attrib); | 632 | rememberAttrib(winclient, (Attribute) attrib); |
637 | } | 633 | } |
638 | } | 634 | } |
639 | /* | 635 | |
640 | if (app->workspace_remember) | ||
641 | app->rememberWorkspace(win.workspaceNumber()); | ||
642 | if (app->dimensions_remember) | ||
643 | app->rememberDimensions(win.width(), win.height()); | ||
644 | if (app->position_remember) | ||
645 | app->rememberPosition(win.x(), win.y()); | ||
646 | if (app->shadedstate_remember) | ||
647 | app->rememberShadedstate(win.isShaded()); | ||
648 | // external tabs off atm | ||
649 | //if (app->tabstate_remember) ... | ||
650 | if (app->decostate_remember) | ||
651 | app->rememberDecostate(win.decorationMask()); | ||
652 | if (app->stuckstate_remember) | ||
653 | app->rememberStuckstate(win.isStuck()); | ||
654 | if (app->jumpworkspace_remember) | ||
655 | app->rememberJumpworkspace(true); | ||
656 | */ | ||
657 | save(); | 636 | save(); |
658 | } | 637 | } |
diff --git a/src/Remember.hh b/src/Remember.hh index 8058c15..d3a30e0 100644 --- a/src/Remember.hh +++ b/src/Remember.hh | |||
@@ -21,7 +21,7 @@ | |||
21 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | 21 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
22 | // DEALINGS IN THE SOFTWARE. | 22 | // DEALINGS IN THE SOFTWARE. |
23 | 23 | ||
24 | // $Id: Remember.hh,v 1.6 2003/06/05 13:33:27 fluxgen Exp $ | 24 | // $Id: Remember.hh,v 1.7 2003/06/12 15:12:19 rathnor Exp $ |
25 | 25 | ||
26 | /* Based on the original "Remember patch" by Xavier Brouckaert */ | 26 | /* Based on the original "Remember patch" by Xavier Brouckaert */ |
27 | 27 | ||
@@ -32,7 +32,14 @@ | |||
32 | 32 | ||
33 | #include <fstream> | 33 | #include <fstream> |
34 | #include <map> | 34 | #include <map> |
35 | #include <list> | ||
35 | #include <string> | 36 | #include <string> |
37 | #include <utility> | ||
38 | |||
39 | class FluxboxWindow; | ||
40 | class BScreen; | ||
41 | class WinClient; | ||
42 | class ClientPattern; | ||
36 | 43 | ||
37 | class Application { | 44 | class Application { |
38 | public: | 45 | public: |
@@ -99,12 +106,8 @@ public: | |||
99 | 106 | ||
100 | bool save_on_close_remember; | 107 | bool save_on_close_remember; |
101 | bool save_on_close; | 108 | bool save_on_close; |
102 | }; | ||
103 | |||
104 | 109 | ||
105 | class FluxboxWindow; | 110 | }; |
106 | class BScreen; | ||
107 | class WinClient; | ||
108 | 111 | ||
109 | /** | 112 | /** |
110 | * Class Remember is an atomhandler to avoid interfering with | 113 | * Class Remember is an atomhandler to avoid interfering with |
@@ -132,13 +135,21 @@ public: | |||
132 | REM_LASTATTRIB // not actually used | 135 | REM_LASTATTRIB // not actually used |
133 | }; | 136 | }; |
134 | 137 | ||
135 | typedef std::map<std::string, Application *> Apps; | 138 | // a "pattern" to the relevant app |
139 | // each app exists ONLY for that pattern. | ||
140 | // And we need to keep a list of pairs as we want to keep the | ||
141 | // applications in the same order as they will be in the apps file | ||
142 | typedef std::list< std::pair<ClientPattern *, Application *> > Patterns; | ||
143 | |||
144 | // We keep track of which app is assigned to a winclient | ||
145 | // particularly useful to update counters etc on windowclose | ||
146 | typedef std::map<WinClient *, Application *> Clients; | ||
147 | |||
136 | Remember(); | 148 | Remember(); |
149 | ~Remember(); | ||
137 | 150 | ||
138 | Application* find(WinClient &winclient); | 151 | Application* find(WinClient &winclient); |
139 | Application* find(const char* app_name); | ||
140 | Application* add(WinClient &winclient); | 152 | Application* add(WinClient &winclient); |
141 | Application* add(const char* app_name); | ||
142 | 153 | ||
143 | void load(); | 154 | void load(); |
144 | void save(); | 155 | void save(); |
@@ -176,7 +187,8 @@ private: | |||
176 | 187 | ||
177 | // returns number of lines read | 188 | // returns number of lines read |
178 | int parseApp(std::ifstream &file, Application &app); | 189 | int parseApp(std::ifstream &file, Application &app); |
179 | Apps apps; | 190 | Patterns m_pats; |
191 | Clients m_clients; | ||
180 | 192 | ||
181 | }; | 193 | }; |
182 | 194 | ||