aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMathias Gumz <akira at fluxbox dot org>2010-09-11 15:09:07 (GMT)
committerMathias Gumz <akira at fluxbox dot org>2010-09-11 15:09:07 (GMT)
commitfaa4c978885ceacfb75b0088e8c5a362a41794f6 (patch)
tree600e09fafca10e702150abac9e852f8aa6ef38c3 /src
parent4e2c7e2167a0e0efbfc73c1b226eaafa808736ee (diff)
downloadfluxbox-faa4c978885ceacfb75b0088e8c5a362a41794f6.zip
fluxbox-faa4c978885ceacfb75b0088e8c5a362a41794f6.tar.bz2
added 'SetXProp' action and (@PROP=foo) clientpattern
these two allow 'tagging' of arbitrary windows with 'tags' (or 'labels'). such 'tagged' windows can then be used in ':NextWindow (@PROP=foo)' commands to quickly cycle through a subset of available windows. since the 'tags' are applied as real xproperties to a window they survive a restart of fluxbox or even another windowmanager. the user can also set the tags by using xprop(1). the next step regarding the UI should be to visualize the tags of a window.
Diffstat (limited to 'src')
-rw-r--r--src/ClientPattern.cc188
-rw-r--r--src/ClientPattern.hh7
-rw-r--r--src/CurrentWindowCmd.cc69
-rw-r--r--src/Focusable.hh2
-rw-r--r--src/WinClient.hh1
-rw-r--r--src/Window.cc4
-rw-r--r--src/Window.hh1
7 files changed, 189 insertions, 83 deletions
diff --git a/src/ClientPattern.cc b/src/ClientPattern.cc
index 7e7e715..71725fb 100644
--- a/src/ClientPattern.cc
+++ b/src/ClientPattern.cc
@@ -66,7 +66,7 @@ struct Name2WinProperty {
66 ClientPattern::WinProperty prop; 66 ClientPattern::WinProperty prop;
67}; 67};
68 68
69Name2WinProperty name_2_winproperties[] = { // sorted for 'bsearch' 69const Name2WinProperty name_2_winproperties[] = { // sorted for 'bsearch'
70 { "class", ClientPattern::CLASS }, 70 { "class", ClientPattern::CLASS },
71 { "focushidden", ClientPattern::FOCUSHIDDEN }, 71 { "focushidden", ClientPattern::FOCUSHIDDEN },
72 { "head", ClientPattern::HEAD }, 72 { "head", ClientPattern::HEAD },
@@ -92,29 +92,43 @@ int name_2_winproperty_cmp(const void* a, const void* b) {
92 reinterpret_cast<const Name2WinProperty*>(b)->name); 92 reinterpret_cast<const Name2WinProperty*>(b)->name);
93} 93}
94 94
95const Name2WinProperty* find_winproperty_by_name(const FbTk::FbString& name) {
96
97 const Name2WinProperty key = { name.c_str(), ClientPattern::CLASS };
98 const Name2WinProperty* result = reinterpret_cast<Name2WinProperty*>(
99 bsearch(&key, name_2_winproperties,
100 sizeof(name_2_winproperties) / sizeof(Name2WinProperty),
101 sizeof(Name2WinProperty),
102 name_2_winproperty_cmp));
103
104 return result;
105}
106
107
95struct Prop2String { 108struct Prop2String {
96 ClientPattern::WinProperty prop; 109 ClientPattern::WinProperty prop;
97 const char* str; 110 const char* str;
98}; 111};
99 112
100Prop2String property_2_strings[] = { // sorted by 'prop' 113Prop2String property_2_strings[] = { // sorted by 'prop'
101 { ClientPattern::TITLE, "title=" }, 114 { ClientPattern::TITLE, "title" },
102 { ClientPattern::CLASS, "class=" }, 115 { ClientPattern::CLASS, "class" },
103 { ClientPattern::NAME, "name=" }, 116 { ClientPattern::NAME, "name" },
104 { ClientPattern::ROLE, "role=" }, 117 { ClientPattern::ROLE, "role" },
105 { ClientPattern::TRANSIENT, "transient=" }, 118 { ClientPattern::TRANSIENT, "transient" },
106 { ClientPattern::MAXIMIZED, "maximized=" }, 119 { ClientPattern::MAXIMIZED, "maximized" },
107 { ClientPattern::MINIMIZED, "minimized=" }, 120 { ClientPattern::MINIMIZED, "minimized" },
108 { ClientPattern::SHADED, "shaded=" }, 121 { ClientPattern::SHADED, "shaded" },
109 { ClientPattern::STUCK, "stuck=" }, 122 { ClientPattern::STUCK, "stuck" },
110 { ClientPattern::FOCUSHIDDEN, "focushidden=" }, 123 { ClientPattern::FOCUSHIDDEN, "focushidden" },
111 { ClientPattern::ICONHIDDEN, "iconhidden=" }, 124 { ClientPattern::ICONHIDDEN, "iconhidden" },
112 { ClientPattern::WORKSPACE, "workspace=" }, 125 { ClientPattern::WORKSPACE, "workspace" },
113 { ClientPattern::WORKSPACENAME, "workspacename=" }, 126 { ClientPattern::WORKSPACENAME, "workspacename" },
114 { ClientPattern::HEAD, "head=" }, 127 { ClientPattern::HEAD, "head" },
115 { ClientPattern::LAYER, "layer=" }, 128 { ClientPattern::LAYER, "layer" },
116 { ClientPattern::URGENT, "urgent=" }, 129 { ClientPattern::URGENT, "urgent" },
117 { ClientPattern::SCREEN, "screen=" } 130 { ClientPattern::SCREEN, "screen" },
131 { ClientPattern::XPROP, "@" },
118}; 132};
119 133
120 134
@@ -129,16 +143,21 @@ Prop2String property_2_strings[] = { // sorted by 'prop'
129 */ 143 */
130struct ClientPattern::Term { 144struct ClientPattern::Term {
131 145
132 Term(const FbTk::FbString& _regstr, WinProperty _prop, bool _negate) : 146 Term(const FbTk::FbString& _regstr, WinProperty _prop, bool _negate, const FbTk::FbString& _xprop) :
133 orig(_regstr), 147 regstr(_regstr),
148 xpropstr(_xprop),
134 regexp(_regstr, true), 149 regexp(_regstr, true),
135 prop(_prop), 150 prop(_prop),
136 negate(_negate) { 151 negate(_negate) {
137 152
153 xprop = XInternAtom(FbTk::App::instance()->display(), xpropstr.c_str(), False);
138 } 154 }
139 155
140 FbTk::FbString orig; 156 // (title=.*bar) or (@FOO=.*bar)
141 FbTk::RegExp regexp; 157 FbTk::FbString regstr; // .*bar
158 FbTk::FbString xpropstr; // @FOO=.*bar
159 Atom xprop; // Atom of 'FOO'
160 FbTk::RegExp regexp; // compiled version of '.*bar'
142 WinProperty prop; 161 WinProperty prop;
143 bool negate; 162 bool negate;
144}; 163};
@@ -175,51 +194,65 @@ ClientPattern::ClientPattern(const char *str):
175 err = FbTk::StringUtil::getStringBetween(match, 194 err = FbTk::StringUtil::getStringBetween(match,
176 str + pos, 195 str + pos,
177 '(', ')', " \t\n", true); 196 '(', ')', " \t\n", true);
197
178 if (err > 0) { 198 if (err > 0) {
179 // need to determine the property used 199
180 string memstr, expr; 200 WinProperty prop = NAME;
181 WinProperty prop; 201 std::string expr;
182 string::size_type eq = match.find_first_of('='); 202 std::string xprop;
183 if (eq == match.npos) {
184 memstr = match;
185 expr = "[current]";
186 } else {
187 memstr.assign(match, 0, eq); // memstr = our identifier
188 expr.assign(match, eq+1, match.length());
189 }
190 bool negate = false; 203 bool negate = false;
191 if (!memstr.empty() && memstr[memstr.length()-1] == '!') { 204
192 negate = true; 205 // need to determine the property used, potential patterns:
193 memstr.assign(memstr, 0, memstr.length()-1); 206 //
207 // A) foo (short for 'title=foo')
208 // B) foo=bar
209 // C) foo!=bar
210 //
211 // D) @foo=bar (xproperty 'foo' equal to 'bar')
212 //
213
214 string propstr = match;
215 string::size_type eq = propstr.find_first_of('=');
216
217 if (eq == propstr.npos) { // A
218 expr = "[current]";
219 } else { // B or C, so strip away the '='
220
221 // 'bar'
222 expr.assign(propstr.begin() + eq + 1, propstr.end());
223
224 // 'foo' or 'foo!'
225 propstr.resize(eq);
226 if (propstr.rfind("!", propstr.npos, 1) != propstr.npos) { // C 'foo!'
227 negate = true;
228 propstr.resize(propstr.size()-1);
229 }
194 } 230 }
195 231
196 memstr = FbTk::StringUtil::toLower(memstr); 232 if (propstr[0] != '@') { // not D
197 233
198 Name2WinProperty key = { memstr.c_str(), CLASS }; 234 const Name2WinProperty* p = find_winproperty_by_name(FbTk::StringUtil::toLower(propstr));
199 Name2WinProperty* i = reinterpret_cast<Name2WinProperty*>(
200 bsearch(&key, name_2_winproperties,
201 sizeof(name_2_winproperties) / sizeof(Name2WinProperty),
202 sizeof(Name2WinProperty),
203 name_2_winproperty_cmp));
204 235
205 if (i) { 236 if (p) {
206 prop = i->prop; 237 prop = p->prop;
207 } else { 238 } else {
208 prop = NAME; 239 expr = match;
209 expr = match; 240 }
241 } else { // D
242 prop = XPROP;
243 xprop.assign(propstr, 1, propstr.size());
210 } 244 }
211 245
212 had_error = !addTerm(expr, prop, negate); 246 had_error = !addTerm(expr, prop, negate, xprop);
213 pos += err; 247 pos += err;
214 } 248 }
215 } 249 }
216 if (pos == 0 && !had_error) { 250 if (pos == 0 && !had_error) { // no match terms given, this is not allowed
217 // no match terms given, this is not allowed
218 had_error = true; 251 had_error = true;
219 } 252 }
220 253
221 if (!had_error) { 254 if (!had_error) { // otherwise, we check for a number
222 // otherwise, we check for a number 255
223 string number; 256 string number;
224 err = FbTk::StringUtil::getStringBetween(number, 257 err = FbTk::StringUtil::getStringBetween(number,
225 str+pos, 258 str+pos,
@@ -251,23 +284,26 @@ ClientPattern::~ClientPattern() {
251 284
252// return a string representation of this pattern 285// return a string representation of this pattern
253string ClientPattern::toString() const { 286string ClientPattern::toString() const {
254 string pat; 287 string result;
255 Terms::const_iterator it = m_terms.begin(); 288 Terms::const_iterator it = m_terms.begin();
256 Terms::const_iterator it_end = m_terms.end(); 289 Terms::const_iterator it_end = m_terms.end();
257 for (; it != it_end; ++it) { 290 for (; it != it_end; ++it) {
258 291 const Term& term = *(*it);
259 pat.append(" ("); 292 result.append(" (");
260 pat.append(property_2_strings[(*it)->prop].str); 293 result.append(property_2_strings[term.prop].str);
261 pat.append((*it)->orig); 294 if (term.prop == XPROP)
262 pat.append(")"); 295 result.append(term.xpropstr);
296 result.append(term.negate ? "!=" : "=");
297 result.append(term.regstr);
298 result.append(")");
263 } 299 }
264 300
265 if (m_matchlimit > 0) { 301 if (m_matchlimit > 0) {
266 pat.append(" {"); 302 result.append(" {");
267 pat.append(FbTk::StringUtil::number2String(m_matchlimit)); 303 result.append(FbTk::StringUtil::number2String(m_matchlimit));
268 pat.append("}"); 304 result.append("}");
269 } 305 }
270 return pat; 306 return result;
271} 307}
272 308
273// does this client match this pattern? 309// does this client match this pattern?
@@ -282,7 +318,10 @@ bool ClientPattern::match(const Focusable &win) const {
282 Terms::const_iterator it_end = m_terms.end(); 318 Terms::const_iterator it_end = m_terms.end();
283 for (; it != it_end; ++it) { 319 for (; it != it_end; ++it) {
284 const Term& term = *(*it); 320 const Term& term = *(*it);
285 if (term.orig == "[current]") { 321 if (term.prop == XPROP) {
322 if (!term.negate ^ (term.regexp.match(win.getTextProperty(term.xprop))))
323 return false;
324 } else if (term.regstr == "[current]") {
286 WinClient *focused = FocusControl::focusedWindow(); 325 WinClient *focused = FocusControl::focusedWindow();
287 if (term.prop == WORKSPACE) { 326 if (term.prop == WORKSPACE) {
288 if (!term.negate ^ (getProperty(term.prop, win) == FbTk::StringUtil::number2String(win.screen().currentWorkspaceID()))) 327 if (!term.negate ^ (getProperty(term.prop, win) == FbTk::StringUtil::number2String(win.screen().currentWorkspaceID())))
@@ -293,7 +332,7 @@ bool ClientPattern::match(const Focusable &win) const {
293 return false; 332 return false;
294 } else if (!focused || (!term.negate ^ (getProperty(term.prop, win) == getProperty(term.prop, *focused)))) 333 } else if (!focused || (!term.negate ^ (getProperty(term.prop, win) == getProperty(term.prop, *focused))))
295 return false; 334 return false;
296 } else if (term.prop == HEAD && term.orig == "[mouse]") { 335 } else if (term.prop == HEAD && term.regstr == "[mouse]") {
297 if (!term.negate ^ (getProperty(term.prop, win) == FbTk::StringUtil::number2String(win.screen().getCurrHead()))) 336 if (!term.negate ^ (getProperty(term.prop, win) == FbTk::StringUtil::number2String(win.screen().getCurrHead())))
298 return false; 337 return false;
299 338
@@ -307,7 +346,7 @@ bool ClientPattern::dependsOnFocusedWindow() const {
307 Terms::const_iterator it = m_terms.begin(), it_end = m_terms.end(); 346 Terms::const_iterator it = m_terms.begin(), it_end = m_terms.end();
308 for (; it != it_end; ++it) { 347 for (; it != it_end; ++it) {
309 if ((*it)->prop != WORKSPACE && (*it)->prop != WORKSPACENAME && 348 if ((*it)->prop != WORKSPACE && (*it)->prop != WORKSPACENAME &&
310 (*it)->orig == "[current]") 349 (*it)->regstr == "[current]")
311 return true; 350 return true;
312 } 351 }
313 return false; 352 return false;
@@ -317,7 +356,7 @@ bool ClientPattern::dependsOnCurrentWorkspace() const {
317 Terms::const_iterator it = m_terms.begin(), it_end = m_terms.end(); 356 Terms::const_iterator it = m_terms.begin(), it_end = m_terms.end();
318 for (; it != it_end; ++it) { 357 for (; it != it_end; ++it) {
319 if (((*it)->prop == WORKSPACE || (*it)->prop == WORKSPACENAME) && 358 if (((*it)->prop == WORKSPACE || (*it)->prop == WORKSPACENAME) &&
320 (*it)->orig == "[current]") 359 (*it)->regstr == "[current]")
321 return true; 360 return true;
322 } 361 }
323 return false; 362 return false;
@@ -326,17 +365,16 @@ bool ClientPattern::dependsOnCurrentWorkspace() const {
326// add an expression to match against 365// add an expression to match against
327// The first argument is a regular expression, the second is the member 366// The first argument is a regular expression, the second is the member
328// function that we wish to match against. 367// function that we wish to match against.
329bool ClientPattern::addTerm(const FbTk::FbString &str, WinProperty prop, bool negate) { 368bool ClientPattern::addTerm(const FbTk::FbString &str, WinProperty prop, bool negate, const FbTk::FbString& xprop) {
330 369
331 bool rc = false; 370 bool rc = false;
332 Term* term = new Term(str, prop, negate); 371 Term* term = new Term(str, prop, negate, xprop);
333 372
334 if (!term) 373 if (!term)
335 return rc; 374 return rc;
336 375
337 if (!term->regexp.error()) { 376 if (rc = !term->regexp.error()) {
338 m_terms.push_back(term); 377 m_terms.push_back(term);
339 rc = true;
340 } else { 378 } else {
341 delete term; 379 delete term;
342 } 380 }
@@ -424,8 +462,11 @@ bool ClientPattern::operator ==(const ClientPattern &pat) const {
424 Terms::const_iterator other_it = pat.m_terms.begin(); 462 Terms::const_iterator other_it = pat.m_terms.begin();
425 Terms::const_iterator other_it_end = pat.m_terms.end(); 463 Terms::const_iterator other_it_end = pat.m_terms.end();
426 for (; it != it_end && other_it != other_it_end; ++it, ++other_it) { 464 for (; it != it_end && other_it != other_it_end; ++it, ++other_it) {
427 if ((*it)->orig != (*other_it)->orig || 465 const Term& i = *(*it);
428 (*it)->negate != (*other_it)->negate) 466 const Term& o = *(*other_it);
467 if (i.regstr != o.regstr ||
468 i.negate != o.negate ||
469 i.xpropstr != o.xpropstr)
429 return false; 470 return false;
430 } 471 }
431 if (it != it_end || other_it != other_it_end) 472 if (it != it_end || other_it != other_it_end)
@@ -433,3 +474,4 @@ bool ClientPattern::operator ==(const ClientPattern &pat) const {
433 474
434 return true; 475 return true;
435} 476}
477
diff --git a/src/ClientPattern.hh b/src/ClientPattern.hh
index 6aa2e11..315edb1 100644
--- a/src/ClientPattern.hh
+++ b/src/ClientPattern.hh
@@ -54,7 +54,8 @@ public:
54 enum WinProperty { 54 enum WinProperty {
55 TITLE = 0, CLASS, NAME, ROLE, TRANSIENT, 55 TITLE = 0, CLASS, NAME, ROLE, TRANSIENT,
56 MAXIMIZED, MINIMIZED, SHADED, STUCK, FOCUSHIDDEN, ICONHIDDEN, 56 MAXIMIZED, MINIMIZED, SHADED, STUCK, FOCUSHIDDEN, ICONHIDDEN,
57 WORKSPACE, WORKSPACENAME, HEAD, LAYER, URGENT, SCREEN 57 WORKSPACE, WORKSPACENAME, HEAD, LAYER, URGENT, SCREEN,
58 XPROP
58 }; 59 };
59 60
60 /// Does this client match this pattern? 61 /// Does this client match this pattern?
@@ -70,9 +71,11 @@ public:
70 * Add an expression to match against 71 * Add an expression to match against
71 * @param str is a regular expression 72 * @param str is a regular expression
72 * @param prop is the member function that we wish to match against 73 * @param prop is the member function that we wish to match against
74 * @param negate is if the term should be negated
75 * @param xprop is the name of the prop if prop is XPROP
73 * @return false if the regexp wasn't valid 76 * @return false if the regexp wasn't valid
74 */ 77 */
75 bool addTerm(const FbTk::FbString &str, WinProperty prop, bool negate = false); 78 bool addTerm(const FbTk::FbString &str, WinProperty prop, bool negate = false, const FbTk::FbString& xprop = FbTk::FbString());
76 79
77 void addMatch() { ++m_nummatches; } 80 void addMatch() { ++m_nummatches; }
78 void removeMatch() { --m_nummatches; } 81 void removeMatch() { --m_nummatches; }
diff --git a/src/CurrentWindowCmd.cc b/src/CurrentWindowCmd.cc
index e2fdb94..1851f2d 100644
--- a/src/CurrentWindowCmd.cc
+++ b/src/CurrentWindowCmd.cc
@@ -227,23 +227,75 @@ REGISTER_COMMAND_PARSER(focus, parseFocusCmd, void);
227 227
228class ActivateTabCmd: public WindowHelperCmd { 228class ActivateTabCmd: public WindowHelperCmd {
229public: 229public:
230 ActivateTabCmd() { } 230 explicit ActivateTabCmd() { }
231protected: 231protected:
232 void real_execute(); 232 void real_execute() {
233 WinClient* winclient = fbwindow().winClientOfLabelButtonWindow(
234 Fluxbox::instance()->lastEvent().xany.window);
235
236 if (winclient && winclient != &fbwindow().winClient()) {
237 fbwindow().setCurrentClient(*winclient, true);
238 }
239
240 }
241};
242
243
244REGISTER_COMMAND(activatetab, ActivateTabCmd, void);
245
246class SetXPropCmd: public WindowHelperCmd {
247public:
248 explicit SetXPropCmd(const FbTk::FbString& name, const FbTk::FbString& value) :
249 m_name(name), m_value(value) { }
250
251protected:
252 void real_execute() {
253
254 WinClient& client = fbwindow().winClient();
255 Atom prop = XInternAtom(client.display(), m_name.c_str(), False);
256
257 client.changeProperty(prop, XInternAtom(client.display(), "UTF8_STRING", False), 8,
258 PropModeReplace, (unsigned char*)m_value.c_str(), m_value.size());
259 }
260
261private:
262 FbTk::FbString m_name;
263 FbTk::FbString m_value;
233}; 264};
234 265
266FbTk::Command<void> *parseSetXPropCmd(const string &command, const string &args, bool trusted) {
267
268 SetXPropCmd* cmd = 0;
269
270 if (trusted) {
271
272 FbTk::FbString name = args;
273
274 FbTk::StringUtil::removeFirstWhitespace(name);
275 FbTk::StringUtil::removeTrailingWhitespace(name);
276
277 if (name.size() > 1 && name[0] != '=') { // the smallest valid argument is 'X='
278
279 FbTk::FbString value;
235 280
236void ActivateTabCmd::real_execute() { 281 size_t eq = name.find('=');
282 if (eq != name.npos && eq != name.size()) {
237 283
238 WinClient* winclient = fbwindow().winClientOfLabelButtonWindow( 284 value.assign(name, eq + 1, name.size());
239 Fluxbox::instance()->lastEvent().xany.window); 285 name.resize(eq);
286 }
240 287
241 if (winclient && winclient != &fbwindow().winClient()) { 288 cmd = new SetXPropCmd(name, value);
242 fbwindow().setCurrentClient(*winclient, true); 289
290 }
243 } 291 }
292
293 return cmd;
244} 294}
245 295
246REGISTER_COMMAND(activatetab, ActivateTabCmd, void); 296REGISTER_COMMAND_PARSER(setxprop, parseSetXPropCmd, void);
297
298
247 299
248} // end anonymous namespace 300} // end anonymous namespace
249 301
@@ -677,6 +729,7 @@ void SetAlphaCmd::real_execute() {
677 : m_unfocus); 729 : m_unfocus);
678} 730}
679 731
732
680REGISTER_COMMAND_WITH_ARGS(matches, MatchCmd, bool); 733REGISTER_COMMAND_WITH_ARGS(matches, MatchCmd, bool);
681 734
682bool MatchCmd::real_execute() { 735bool MatchCmd::real_execute() {
diff --git a/src/Focusable.hh b/src/Focusable.hh
index 47f14d3..4583a62 100644
--- a/src/Focusable.hh
+++ b/src/Focusable.hh
@@ -88,6 +88,8 @@ public:
88 /// @return wm role string (for pattern matching) 88 /// @return wm role string (for pattern matching)
89 virtual std::string getWMRole() const { return "Focusable"; } 89 virtual std::string getWMRole() const { return "Focusable"; }
90 90
91 virtual FbTk::FbString getTextProperty(Atom prop) const { return ""; }
92
91 /// @return whether this window is a transient (for pattern matching) 93 /// @return whether this window is a transient (for pattern matching)
92 virtual bool isTransient() const { return false; } 94 virtual bool isTransient() const { return false; }
93 95
diff --git a/src/WinClient.hh b/src/WinClient.hh
index ebe61a5..157278e 100644
--- a/src/WinClient.hh
+++ b/src/WinClient.hh
@@ -96,6 +96,7 @@ public:
96 std::string getWMRole() const; 96 std::string getWMRole() const;
97 WindowState::WindowType getWindowType() const { return m_window_type; } 97 WindowState::WindowType getWindowType() const { return m_window_type; }
98 void setWindowType(WindowState::WindowType type) { m_window_type = type; } 98 void setWindowType(WindowState::WindowType type) { m_window_type = type; }
99 FbTk::FbString getTextProperty(Atom prop) const { return FbTk::FbWindow::textProperty(prop); }
99 100
100 WinClient *transientFor() { return transient_for; } 101 WinClient *transientFor() { return transient_for; }
101 const WinClient *transientFor() const { return transient_for; } 102 const WinClient *transientFor() const { return transient_for; }
diff --git a/src/Window.cc b/src/Window.cc
index 5f64705..1837cee 100644
--- a/src/Window.cc
+++ b/src/Window.cc
@@ -3363,6 +3363,10 @@ FbTk::FbString FluxboxWindow::getWMRole() const {
3363 return (m_client ? m_client->getWMRole() : "FluxboxWindow"); 3363 return (m_client ? m_client->getWMRole() : "FluxboxWindow");
3364} 3364}
3365 3365
3366FbTk::FbString FluxboxWindow::getTextProperty(Atom prop) const {
3367 return (m_client ? m_client->getTextProperty(prop) : Focusable::getTextProperty(prop));
3368}
3369
3366bool FluxboxWindow::isTransient() const { 3370bool FluxboxWindow::isTransient() const {
3367 return (m_client && m_client->isTransient()); 3371 return (m_client && m_client->isTransient());
3368} 3372}
diff --git a/src/Window.hh b/src/Window.hh
index 080ceac..7f8133e 100644
--- a/src/Window.hh
+++ b/src/Window.hh
@@ -422,6 +422,7 @@ public:
422 const FbTk::FbString &getWMClassName() const; 422 const FbTk::FbString &getWMClassName() const;
423 const FbTk::FbString &getWMClassClass() const; 423 const FbTk::FbString &getWMClassClass() const;
424 std::string getWMRole() const; 424 std::string getWMRole() const;
425 FbTk::FbString getTextProperty(Atom prop) const;
425 void setWindowType(WindowState::WindowType type); 426 void setWindowType(WindowState::WindowType type);
426 bool isTransient() const; 427 bool isTransient() const;
427 428