diff options
-rw-r--r-- | doc/asciidoc/client-patterns.txt | 9 | ||||
-rw-r--r-- | doc/asciidoc/fluxbox-keys.txt | 8 | ||||
-rw-r--r-- | src/ClientPattern.cc | 188 | ||||
-rw-r--r-- | src/ClientPattern.hh | 7 | ||||
-rw-r--r-- | src/CurrentWindowCmd.cc | 69 | ||||
-rw-r--r-- | src/Focusable.hh | 2 | ||||
-rw-r--r-- | src/WinClient.hh | 1 | ||||
-rw-r--r-- | src/Window.cc | 4 | ||||
-rw-r--r-- | src/Window.hh | 1 |
9 files changed, 206 insertions, 83 deletions
diff --git a/doc/asciidoc/client-patterns.txt b/doc/asciidoc/client-patterns.txt index 85dac7b..5b00e0c 100644 --- a/doc/asciidoc/client-patterns.txt +++ b/doc/asciidoc/client-patterns.txt | |||
@@ -64,6 +64,9 @@ The following values are accepted for 'propertyname'::: | |||
64 | *Layer*;; | 64 | *Layer*;; |
65 | The string name of the window's layer, which is one of | 65 | The string name of the window's layer, which is one of |
66 | *AboveDock*, *Dock*, *Top*, *Normal*, *Bottom*, *Desktop* | 66 | *AboveDock*, *Dock*, *Top*, *Normal*, *Bottom*, *Desktop* |
67 | *@XPROP*;; | ||
68 | A string, corresponding to any xproperty (Use either the *xprop(1)* | ||
69 | utility or the 'SetXProp' command to set a xproperty to a window) | ||
67 | 70 | ||
68 | .Matches any windows with the CLASSNAME of "xterm" | 71 | .Matches any windows with the CLASSNAME of "xterm" |
69 | .......... | 72 | .......... |
@@ -79,3 +82,9 @@ The following values are accepted for 'propertyname'::: | |||
79 | ........... | 82 | ........... |
80 | (Head=[mouse]) (Layer!=[current]) | 83 | (Head=[mouse]) (Layer!=[current]) |
81 | ........... | 84 | ........... |
85 | |||
86 | .Matches any windows having a xproperty named FOO with "bar" in it | ||
87 | .............. | ||
88 | (@FOO=.*bar.*) | ||
89 | .............. | ||
90 | |||
diff --git a/doc/asciidoc/fluxbox-keys.txt b/doc/asciidoc/fluxbox-keys.txt index 41ad1e2..16bc18c 100644 --- a/doc/asciidoc/fluxbox-keys.txt +++ b/doc/asciidoc/fluxbox-keys.txt | |||
@@ -327,6 +327,10 @@ two arguments;; | |||
327 | heads. If this takes the window beyond the total number of heads, it | 327 | heads. If this takes the window beyond the total number of heads, it |
328 | will wrap around to the beginning. | 328 | will wrap around to the beginning. |
329 | 329 | ||
330 | *SetXProp* 'PROP=value':: | ||
331 | Sets the xproperty 'PROP' of the current window to 'value'. Delete the | ||
332 | content of 'PROP' by using 'PROP='. | ||
333 | |||
330 | Workspace Commands | 334 | Workspace Commands |
331 | ~~~~~~~~~~~~~~~~~~ | 335 | ~~~~~~~~~~~~~~~~~~ |
332 | These commands affect the entire workspace (or "desktop" as it is sometimes | 336 | These commands affect the entire workspace (or "desktop" as it is sometimes |
@@ -643,8 +647,12 @@ Mod4 t :If {Some Matches (xterm)} {NextWindow (xterm)} {Exec xterm} | |||
643 | 647 | ||
644 | # Set a different wallpaper on every workspace: | 648 | # Set a different wallpaper on every workspace: |
645 | ChangeWorkspace :Exec fbsetbg ~/.fluxbox/bg$(xprop -root _NET_CURRENT_DESKTOP | awk '{print $3}').png | 649 | ChangeWorkspace :Exec fbsetbg ~/.fluxbox/bg$(xprop -root _NET_CURRENT_DESKTOP | awk '{print $3}').png |
650 | |||
651 | # Focusses the next window with it's xproperty 'PROP' set to 'foo' | ||
652 | Mod4 p Mod4 Tab :NextWindow (@PROP=foo) | ||
646 | .................. | 653 | .................. |
647 | 654 | ||
655 | |||
648 | AUTHORS | 656 | AUTHORS |
649 | ------- | 657 | ------- |
650 | - Jim Ramsay <i.am at jimramsay com> (>fluxbox-1.0.0) | 658 | - Jim Ramsay <i.am at jimramsay com> (>fluxbox-1.0.0) |
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 | ||
69 | Name2WinProperty name_2_winproperties[] = { // sorted for 'bsearch' | 69 | const 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 | ||
95 | const 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 | |||
95 | struct Prop2String { | 108 | struct Prop2String { |
96 | ClientPattern::WinProperty prop; | 109 | ClientPattern::WinProperty prop; |
97 | const char* str; | 110 | const char* str; |
98 | }; | 111 | }; |
99 | 112 | ||
100 | Prop2String property_2_strings[] = { // sorted by 'prop' | 113 | Prop2String 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 | */ |
130 | struct ClientPattern::Term { | 144 | struct 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 |
253 | string ClientPattern::toString() const { | 286 | string 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. |
329 | bool ClientPattern::addTerm(const FbTk::FbString &str, WinProperty prop, bool negate) { | 368 | bool 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 | ||
228 | class ActivateTabCmd: public WindowHelperCmd { | 228 | class ActivateTabCmd: public WindowHelperCmd { |
229 | public: | 229 | public: |
230 | ActivateTabCmd() { } | 230 | explicit ActivateTabCmd() { } |
231 | protected: | 231 | protected: |
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 | |||
244 | REGISTER_COMMAND(activatetab, ActivateTabCmd, void); | ||
245 | |||
246 | class SetXPropCmd: public WindowHelperCmd { | ||
247 | public: | ||
248 | explicit SetXPropCmd(const FbTk::FbString& name, const FbTk::FbString& value) : | ||
249 | m_name(name), m_value(value) { } | ||
250 | |||
251 | protected: | ||
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 | |||
261 | private: | ||
262 | FbTk::FbString m_name; | ||
263 | FbTk::FbString m_value; | ||
233 | }; | 264 | }; |
234 | 265 | ||
266 | FbTk::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 | ||
236 | void 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 | ||
246 | REGISTER_COMMAND(activatetab, ActivateTabCmd, void); | 296 | REGISTER_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 | |||
680 | REGISTER_COMMAND_WITH_ARGS(matches, MatchCmd, bool); | 733 | REGISTER_COMMAND_WITH_ARGS(matches, MatchCmd, bool); |
681 | 734 | ||
682 | bool MatchCmd::real_execute() { | 735 | bool 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 | ||
3366 | FbTk::FbString FluxboxWindow::getTextProperty(Atom prop) const { | ||
3367 | return (m_client ? m_client->getTextProperty(prop) : Focusable::getTextProperty(prop)); | ||
3368 | } | ||
3369 | |||
3366 | bool FluxboxWindow::isTransient() const { | 3370 | bool 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 | ||