aboutsummaryrefslogtreecommitdiff
path: root/src
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
parent94f1c164161e8faaf064d8b7cdfe36c9ca978055 (diff)
downloadfluxbox_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.cc235
-rw-r--r--src/ClientPattern.hh99
-rw-r--r--src/Makefile.am6
-rw-r--r--src/RegExp.cc94
-rw-r--r--src/RegExp.hh66
-rw-r--r--src/Remember.cc199
-rw-r--r--src/Remember.hh32
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
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}
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
34class WinClient;
35
36/**
37 * This class represents a "pattern" that we can match against a
38 * Window based on various properties.
39 */
40class ClientPattern {
41public:
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
77private:
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 &regstr, 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
48endif 48endif
49if REMEMBER_SRC 49if REMEMBER_SRC
50REMEMBER_SOURCE= Remember.hh Remember.cc 50REMEMBER_SOURCE= Remember.hh Remember.cc
51# For now we only want regexp if we have remember
52if REGEXP_SRC
53REGEXP_SOURCE = RegExp.hh RegExp.cc ClientPattern.hh ClientPattern.cc
54endif
51endif 55endif
52 56
53fluxbox_SOURCES = AtomHandler.hh ArrowButton.hh ArrowButton.cc \ 57fluxbox_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
82LDADD=FbTk/libFbTk.a 86LDADD=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
35using 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
44RegExp::RegExp(const std::string &str, bool full_match):
45#ifdef USE_REGEXP
46m_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
72m_str(str) {}
73#endif // USE_REGEXP
74
75RegExp::~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
84bool 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
42class WinClient;
43
44class RegExp {
45public:
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
57private:
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
128std::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
155Application::Application() { 131Application::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
168Remember::Remember() { 148Remember::Remember() {
169 load(); 149 load();
170} 150}
171 151
172Application* Remember::add(const char* app_name) { 152Remember::~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
180Application* Remember::find(WinClient &winclient) { 166Application* 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);
184Application* 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 185Application * Remember::add(WinClient &winclient) {
189Application* 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
199int Remember::parseApp(ifstream &file, Application &app) { 196int 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) {
623void Remember::updateWindowClose(FluxboxWindow &win) { 614void 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
39class FluxboxWindow;
40class BScreen;
41class WinClient;
42class ClientPattern;
36 43
37class Application { 44class Application {
38public: 45public:
@@ -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
105class FluxboxWindow; 110};
106class BScreen;
107class 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