From 675bc5d66aa08311e99cb00cf836d16a910a73df Mon Sep 17 00:00:00 2001
From: markt <markt>
Date: Thu, 22 Nov 2007 04:07:57 +0000
Subject: added conditional statements to keys file

---
 ChangeLog                 |  18 +++++
 src/CurrentWindowCmd.cc   |  23 ++++--
 src/CurrentWindowCmd.hh   |  26 ++++++-
 src/FbCommandFactory.cc   | 192 +++++++++++++++++++++++++++++++++++++++-------
 src/FbTk/Command.hh       |   8 ++
 src/FbTk/LogicCommands.cc |  75 ++++++++++++++++++
 src/FbTk/LogicCommands.hh |  97 +++++++++++++++++++++++
 src/FbTk/Makefile.am      |   1 +
 src/FbTk/SimpleCommand.hh |  12 +++
 src/WorkspaceCmd.cc       |  49 +++++++++++-
 src/WorkspaceCmd.hh       |  28 ++++++-
 11 files changed, 486 insertions(+), 43 deletions(-)
 create mode 100644 src/FbTk/LogicCommands.cc
 create mode 100644 src/FbTk/LogicCommands.hh

diff --git a/ChangeLog b/ChangeLog
index 95cce9a..18c51ed 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,23 @@
  (Format: Year/Month/Day)
 Changes for 1.0.1:
+*07/11/22:
+   * Added conditional statements to key commands (Mark)
+     - for example, this will search for an open xterm window, cycle through
+       them if there are any, or else open one:
+       Mod4 t :If {Some Matches (xterm)} {NextWindow (xterm)} {Exec xterm}
+     - the syntax is :If {<test>} {<command if true>} {<command if false>}
+     - `Matches <pattern>' is currently the only test you can make; when used
+       alone, it tests the focused window or the clicked window for OnWindow
+       mouse events
+     - there are many ways to combine tests:
+       - `Some <test>' returns true if any open client matches <test>
+       - `Every <test>' returns true if every open client matches <test>
+       - `Not <test>' negates the value of <test>
+       - `Or {<test>} {<test>} ...' returns true if any of the tests is true
+       - `And {<test>} {<test>} ...' returns true if all of the tests are true
+       - `Xor {<test>} {<test>} ...' returns the boolean xor of the truth values
+     FbCommandFactory.cc CurrentWindowCmd.cc/hh WorkspaceCmd.cc/hh
+     FbTk/Command.hh FbTk/SimpleCommand.hh, added files FbTk/LogicCommands.cc/hh
 *07/11/16:
    * Added new key command :Focus [<pattern>] that focuses a window (e.g., using
      OnWindow or specified using a window pattern) (Mark, thanks Tomas Janousek)
diff --git a/src/CurrentWindowCmd.cc b/src/CurrentWindowCmd.cc
index 929be5d..a5265f9 100644
--- a/src/CurrentWindowCmd.cc
+++ b/src/CurrentWindowCmd.cc
@@ -33,20 +33,25 @@
 #include "FocusControl.hh"
 
 void WindowHelperCmd::execute() {
-    m_win = 0;
     if (WindowCmd<void>::window() || FocusControl::focusedFbWindow())
         real_execute();
 }
 
-void WindowHelperCmd::execute(FluxboxWindow &win) {
-    m_win = &win;
-    real_execute();
+FluxboxWindow &WindowHelperCmd::fbwindow() {
+    // will exist from execute above
+    FluxboxWindow *tmp = WindowCmd<void>::window();
+    if (tmp) return *tmp;
+    return *FocusControl::focusedFbWindow();
 }
 
-FluxboxWindow &WindowHelperCmd::fbwindow() {
+bool WindowHelperBoolCmd::bool_execute() {
+    if (WindowCmd<void>::window() || FocusControl::focusedFbWindow())
+        return real_execute();
+    return false;
+}
+
+FluxboxWindow &WindowHelperBoolCmd::fbwindow() {
     // will exist from execute above
-    if (m_win)
-        return *m_win;
     FluxboxWindow *tmp = WindowCmd<void>::window();
     if (tmp) return *tmp;
     return *FocusControl::focusedFbWindow();
@@ -227,3 +232,7 @@ void SetAlphaCmd::real_execute() {
     } else
         fbwindow().setUnfocusedAlpha(m_unfocus);
 }
+
+bool MatchCmd::real_execute() {
+    return m_pat.match(fbwindow());
+}
diff --git a/src/CurrentWindowCmd.hh b/src/CurrentWindowCmd.hh
index 53dc7b8..e7370de 100644
--- a/src/CurrentWindowCmd.hh
+++ b/src/CurrentWindowCmd.hh
@@ -27,22 +27,30 @@
 
 #include "Command.hh"
 #include "Window.hh"
+#include "ClientPattern.hh"
 
 /// helper class for window commands
 /// calls real_execute if there's a focused window or a window in button press/release window
 class WindowHelperCmd: public FbTk::Command {
 public:
-    explicit WindowHelperCmd(FluxboxWindow *win = 0): m_win(win) { }
+    explicit WindowHelperCmd() { }
 
     void execute();
-    void execute(FluxboxWindow &fbwin);
 
 protected:
     FluxboxWindow &fbwindow();
     virtual void real_execute() = 0;
+};
 
-private:
-    FluxboxWindow *m_win;
+class WindowHelperBoolCmd: public FbTk::BoolCommand {
+public:
+    explicit WindowHelperBoolCmd() { }
+
+    bool bool_execute();
+
+protected:
+    FluxboxWindow &fbwindow();
+    virtual bool real_execute() = 0;
 };
 
 /// command that calls FluxboxWindow::<the function> on execute()
@@ -221,4 +229,14 @@ private:
     int m_focus, m_unfocus;
     int m_relative, m_un_relative;
 };
+
+class MatchCmd: public WindowHelperBoolCmd {
+public:
+    MatchCmd(const std::string &pat): m_pat(pat.c_str()) { };
+protected:
+    bool real_execute();
+private:
+    ClientPattern m_pat;
+};
+
 #endif // CURRENTWINDOWCMD_HH
diff --git a/src/FbCommandFactory.cc b/src/FbCommandFactory.cc
index ce024eb..92099f2 100644
--- a/src/FbCommandFactory.cc
+++ b/src/FbCommandFactory.cc
@@ -34,6 +34,7 @@
 #include "Screen.hh"
 
 #include "FbTk/StringUtil.hh"
+#include "FbTk/LogicCommands.hh"
 #include "FbTk/MacroCommand.hh"
 #include "FbTk/stringstream.hh"
 
@@ -50,6 +51,10 @@ using std::vector;
 using std::cerr;
 using std::endl;
 
+using namespace FbTk;
+using FbTk::StringUtil::removeFirstWhitespace;
+using FbTk::StringUtil::toLower;
+
 // autoregister this module to command parser
 FbCommandFactory FbCommandFactory::s_autoreg;
 
@@ -60,6 +65,109 @@ static int getint(const char *str, int defaultvalue) {
     return defaultvalue;
 }
 
+BoolCommand *parseBoolCommand(string &line, bool trusted) {
+    // parse arguments and command
+    string command = line;
+    string arguments;
+    string::size_type first_pos = removeFirstWhitespace(command);
+    FbTk::StringUtil::removeTrailingWhitespace(command);
+    string::size_type second_pos = command.find_first_of(" \t", first_pos);
+    if (second_pos != string::npos) {
+        // ok we have arguments, parsing them here
+        arguments = command.substr(second_pos);
+        removeFirstWhitespace(arguments);
+        command.erase(second_pos); // remove argument from command
+    }
+
+    // now we have parsed command and arguments
+    command = toLower(command);
+
+    if (command == "matches") {
+        return new MatchCmd(arguments);
+    } else if (command == "some") {
+        BoolCommand *boolcmd = parseBoolCommand(arguments, trusted);
+        if (!boolcmd)
+            return 0;
+        return new SomeCmd(RefCount<BoolCommand>(boolcmd));
+    } else if (command == "every") {
+        BoolCommand *boolcmd = parseBoolCommand(arguments, trusted);
+        if (!boolcmd)
+            return 0;
+        return new EveryCmd(RefCount<BoolCommand>(boolcmd));
+    } else if (command == "not") {
+        BoolCommand *boolcmd = parseBoolCommand(arguments, trusted);
+        if (!boolcmd)
+            return 0;
+        RefCount<BoolCommand> ref(boolcmd);
+        return new NotCommand(ref);
+    } else if (command == "and") {
+        int pos = 0, err = 0;
+        AndCommand *andcmd = new AndCommand();
+        string cmd;
+
+        while (true) {
+            RefCount<BoolCommand> tmp(0);
+            err = StringUtil::getStringBetween(cmd, arguments.c_str() + pos,
+                                               '{', '}', " \t\n", true);
+            pos += err;
+            if (err == 0)
+                break;
+
+            tmp = parseBoolCommand(cmd, trusted);
+            if (*tmp)
+                andcmd->add(tmp);
+        }
+
+        if (andcmd->size() > 0)
+            return andcmd;
+        delete andcmd;
+    } else if (command == "or") {
+        int pos = 0, err = 0;
+        OrCommand *orcmd = new OrCommand();
+        string cmd;
+
+        while (true) {
+            RefCount<BoolCommand> tmp(0);
+            err = StringUtil::getStringBetween(cmd, arguments.c_str() + pos,
+                                               '{', '}', " \t\n", true);
+            pos += err;
+            if (err == 0)
+                break;
+
+            tmp = parseBoolCommand(cmd, trusted);
+            if (*tmp)
+                orcmd->add(tmp);
+        }
+
+        if (orcmd->size() > 0)
+            return orcmd;
+        delete orcmd;
+    } else if (command == "xor") {
+        int pos = 0, err = 0;
+        XorCommand *xorcmd = new XorCommand();
+        string cmd;
+
+        while (true) {
+            RefCount<BoolCommand> tmp(0);
+            err = StringUtil::getStringBetween(cmd, arguments.c_str() + pos,
+                                               '{', '}', " \t\n", true);
+            pos += err;
+            if (err == 0)
+                break;
+
+            tmp = parseBoolCommand(cmd, trusted);
+            if (*tmp)
+                xorcmd->add(tmp);
+        }
+
+        if (xorcmd->size() > 0)
+            return xorcmd;
+        delete xorcmd;
+    }
+
+    return 0;
+}
+
 }; // end anonymous namespace
 
 FbCommandFactory::FbCommandFactory() {
@@ -74,6 +182,7 @@ FbCommandFactory::FbCommandFactory() {
         "close",
         "closeallwindows",
         "commanddialog",
+        "cond",
         "custommenu",
         "deiconify",
         "detachclient",
@@ -91,6 +200,7 @@ FbCommandFactory::FbCommandFactory() {
         "gotowindow",
         "hidemenus",
         "iconify",
+        "if",
         "keymode",
         "kill",
         "killwindow",
@@ -246,15 +356,15 @@ FbTk::Command *FbCommandFactory::stringToCommand(const std::string &command,
     // Current focused window commands
     //
     else if (command == "fullscreen")
-        return new WindowListCmd(FbTk::RefCount<WindowHelperCmd>(new FullscreenCmd()), arguments);
+        return new WindowListCmd(FbTk::RefCount<FbTk::Command>(new FullscreenCmd()), arguments);
     else if (command == "minimizewindow" || command == "minimize" || command == "iconify")
-        return new WindowListCmd(FbTk::RefCount<WindowHelperCmd>(new CurrentWindowCmd(&FluxboxWindow::iconify)), arguments);
+        return new WindowListCmd(FbTk::RefCount<FbTk::Command>(new CurrentWindowCmd(&FluxboxWindow::iconify)), arguments);
     else if (command == "maximizewindow" || command == "maximize")
-        return new WindowListCmd(FbTk::RefCount<WindowHelperCmd>(new CurrentWindowCmd(&FluxboxWindow::maximizeFull)), arguments);
+        return new WindowListCmd(FbTk::RefCount<FbTk::Command>(new CurrentWindowCmd(&FluxboxWindow::maximizeFull)), arguments);
     else if (command == "maximizevertical")
-        return new WindowListCmd(FbTk::RefCount<WindowHelperCmd>(new CurrentWindowCmd(&FluxboxWindow::maximizeVertical)), arguments);
+        return new WindowListCmd(FbTk::RefCount<FbTk::Command>(new CurrentWindowCmd(&FluxboxWindow::maximizeVertical)), arguments);
     else if (command == "maximizehorizontal")
-        return new WindowListCmd(FbTk::RefCount<WindowHelperCmd>(new CurrentWindowCmd(&FluxboxWindow::maximizeHorizontal)), arguments);
+        return new WindowListCmd(FbTk::RefCount<FbTk::Command>(new CurrentWindowCmd(&FluxboxWindow::maximizeHorizontal)), arguments);
     else if (command == "setalpha") {
         typedef vector<string> StringTokens;
         StringTokens tokens;
@@ -281,7 +391,7 @@ FbTk::Command *FbCommandFactory::stringToCommand(const std::string &command,
         if (pos != string::npos && pos != arguments.size())
             pat = arguments.c_str() + pos;
 
-        return new WindowListCmd(FbTk::RefCount<WindowHelperCmd>(new SetAlphaCmd(focused, relative, unfocused, un_rel)), pat);
+        return new WindowListCmd(FbTk::RefCount<FbTk::Command>(new SetAlphaCmd(focused, relative, unfocused, un_rel)), pat);
     } else if (command == "startmoving")
         return new StartMovingCmd();
     else if (command == "startresizing") {
@@ -331,7 +441,7 @@ FbTk::Command *FbCommandFactory::stringToCommand(const std::string &command,
         if (pos != string::npos && pos != arguments.size())
             pat = arguments.c_str() + pos;
 
-        FbTk::RefCount<WindowHelperCmd> cmd;
+        FbTk::RefCount<FbTk::Command> cmd;
         if (command == "resizeto")
             cmd = new ResizeToCmd(dx, dy);
         else
@@ -386,7 +496,7 @@ FbTk::Command *FbCommandFactory::stringToCommand(const std::string &command,
         if (pos != string::npos && pos != arguments.size())
             pat = arguments.c_str() + pos;
 
-        return new WindowListCmd(FbTk::RefCount<WindowHelperCmd>(new MoveToCmd(dx, dy, refc)), pat);
+        return new WindowListCmd(FbTk::RefCount<FbTk::Command>(new MoveToCmd(dx, dy, refc)), pat);
     } else if (command == "move" || command == "moveright" ||
                command == "moveleft" || command == "moveup" ||
                command == "movedown") {
@@ -412,29 +522,29 @@ FbTk::Command *FbCommandFactory::stringToCommand(const std::string &command,
         if (pos != string::npos && pos != arguments.size())
             pat = arguments.c_str() + pos;
 
-        return new WindowListCmd(FbTk::RefCount<WindowHelperCmd>(new MoveCmd(dx, dy)), pat);
+        return new WindowListCmd(FbTk::RefCount<FbTk::Command>(new MoveCmd(dx, dy)), pat);
     } else if (command == "raise")
-        return new WindowListCmd(FbTk::RefCount<WindowHelperCmd>(new CurrentWindowCmd(&FluxboxWindow::raise)), arguments);
+        return new WindowListCmd(FbTk::RefCount<FbTk::Command>(new CurrentWindowCmd(&FluxboxWindow::raise)), arguments);
     else if (command == "raiselayer")
-        return new WindowListCmd(FbTk::RefCount<WindowHelperCmd>(new CurrentWindowCmd(&FluxboxWindow::raiseLayer)), arguments);
+        return new WindowListCmd(FbTk::RefCount<FbTk::Command>(new CurrentWindowCmd(&FluxboxWindow::raiseLayer)), arguments);
     else if (command == "lower")
-        return new WindowListCmd(FbTk::RefCount<WindowHelperCmd>(new CurrentWindowCmd(&FluxboxWindow::lower)), arguments);
+        return new WindowListCmd(FbTk::RefCount<FbTk::Command>(new CurrentWindowCmd(&FluxboxWindow::lower)), arguments);
     else if (command == "lowerlayer")
-        return new WindowListCmd(FbTk::RefCount<WindowHelperCmd>(new CurrentWindowCmd(&FluxboxWindow::lowerLayer)), arguments);
+        return new WindowListCmd(FbTk::RefCount<FbTk::Command>(new CurrentWindowCmd(&FluxboxWindow::lowerLayer)), arguments);
     else if (command == "activate" || command == "focus")
-        return new WindowListCmd(FbTk::RefCount<WindowHelperCmd>(new CurrentWindowCmd((void (FluxboxWindow::*)())&FluxboxWindow::focus)), arguments);
+        return new WindowListCmd(FbTk::RefCount<FbTk::Command>(new CurrentWindowCmd((void (FluxboxWindow::*)())&FluxboxWindow::focus)), arguments);
     else if (command == "close")
-        return new WindowListCmd(FbTk::RefCount<WindowHelperCmd>(new CurrentWindowCmd(&FluxboxWindow::close)), arguments);
+        return new WindowListCmd(FbTk::RefCount<FbTk::Command>(new CurrentWindowCmd(&FluxboxWindow::close)), arguments);
     else if (command == "closeallwindows")
         return new CloseAllWindowsCmd();
     else if (command == "killwindow" || command == "kill")
-        return new WindowListCmd(FbTk::RefCount<WindowHelperCmd>(new CurrentWindowCmd(&FluxboxWindow::kill)), arguments);
+        return new WindowListCmd(FbTk::RefCount<FbTk::Command>(new CurrentWindowCmd(&FluxboxWindow::kill)), arguments);
     else if (command == "shade" || command == "shadewindow")
-        return new WindowListCmd(FbTk::RefCount<WindowHelperCmd>(new CurrentWindowCmd(&FluxboxWindow::shade)), arguments);
+        return new WindowListCmd(FbTk::RefCount<FbTk::Command>(new CurrentWindowCmd(&FluxboxWindow::shade)), arguments);
     else if (command == "stick" || command == "stickwindow")
-        return new WindowListCmd(FbTk::RefCount<WindowHelperCmd>(new CurrentWindowCmd(&FluxboxWindow::stick)), arguments);
+        return new WindowListCmd(FbTk::RefCount<FbTk::Command>(new CurrentWindowCmd(&FluxboxWindow::stick)), arguments);
     else if (command == "toggledecor")
-        return new WindowListCmd(FbTk::RefCount<WindowHelperCmd>(new CurrentWindowCmd(&FluxboxWindow::toggleDecoration)), arguments);
+        return new WindowListCmd(FbTk::RefCount<FbTk::Command>(new CurrentWindowCmd(&FluxboxWindow::toggleDecoration)), arguments);
     else if (command == "sethead") {
         int num = 0;
         string pat;
@@ -443,7 +553,7 @@ FbTk::Command *FbCommandFactory::stringToCommand(const std::string &command,
         string::size_type pos = arguments.find('(');
         if (pos != string::npos && pos != arguments.size())
             pat = arguments.c_str() + pos;
-        return new WindowListCmd(FbTk::RefCount<WindowHelperCmd>(new SetHeadCmd(num)), pat);
+        return new WindowListCmd(FbTk::RefCount<FbTk::Command>(new SetHeadCmd(num)), pat);
     } else if (command == "tab" || command == "sendtonextworkspace" ||
                command == "sendtoprevworkspace" ||
                command == "taketonextworkspace" ||
@@ -457,7 +567,7 @@ FbTk::Command *FbCommandFactory::stringToCommand(const std::string &command,
         string::size_type pos = arguments.find('(');
         if (pos != string::npos && pos != arguments.size())
             pat = arguments.c_str() + pos;
-        FbTk::RefCount<WindowHelperCmd> cmd;
+        FbTk::RefCount<FbTk::Command> cmd;
 
         if (command == "tab")
             cmd = new GoToTabCmd(num);
@@ -475,15 +585,15 @@ FbTk::Command *FbCommandFactory::stringToCommand(const std::string &command,
             cmd = new TakeToWorkspaceCmd(num-1);
         return new WindowListCmd(cmd, pat);
     } else if (command == "nexttab")
-        return new WindowListCmd(FbTk::RefCount<WindowHelperCmd>(new CurrentWindowCmd(&FluxboxWindow::nextClient)), arguments);
+        return new WindowListCmd(FbTk::RefCount<FbTk::Command>(new CurrentWindowCmd(&FluxboxWindow::nextClient)), arguments);
     else if (command == "prevtab")
-        return new WindowListCmd(FbTk::RefCount<WindowHelperCmd>(new CurrentWindowCmd(&FluxboxWindow::prevClient)), arguments);
+        return new WindowListCmd(FbTk::RefCount<FbTk::Command>(new CurrentWindowCmd(&FluxboxWindow::prevClient)), arguments);
     else if (command == "movetableft")
-        return new WindowListCmd(FbTk::RefCount<WindowHelperCmd>(new CurrentWindowCmd(&FluxboxWindow::moveClientLeft)), arguments);
+        return new WindowListCmd(FbTk::RefCount<FbTk::Command>(new CurrentWindowCmd(&FluxboxWindow::moveClientLeft)), arguments);
     else if (command == "movetabright")
-        return new WindowListCmd(FbTk::RefCount<WindowHelperCmd>(new CurrentWindowCmd(&FluxboxWindow::moveClientRight)), arguments);
+        return new WindowListCmd(FbTk::RefCount<FbTk::Command>(new CurrentWindowCmd(&FluxboxWindow::moveClientRight)), arguments);
     else if (command == "detachclient")
-        return new WindowListCmd(FbTk::RefCount<WindowHelperCmd>(new CurrentWindowCmd(&FluxboxWindow::detachCurrentClient)), arguments);
+        return new WindowListCmd(FbTk::RefCount<FbTk::Command>(new CurrentWindowCmd(&FluxboxWindow::detachCurrentClient)), arguments);
     else if (command == "windowmenu")
         return new CurrentWindowCmd(&FluxboxWindow::popupMenu);
     //
@@ -683,6 +793,36 @@ FbTk::Command *FbCommandFactory::stringToCommand(const std::string &command,
         return macro;
 
       delete macro;
+    } else if (command == "if" || command == "cond") {
+        string cmd;
+        int err = 0, pos = 0;
+        RefCount<BoolCommand> cond(0);
+        RefCount<Command> t(0), f(0);
+
+        err = FbTk::StringUtil::getStringBetween(cmd, arguments.c_str(),
+                                                 '{', '}', " \t\n", true);
+        if (err > 0)
+            cond = parseBoolCommand(cmd, trusted);
+        if (err == 0 || *cond == 0)
+            return 0;
+
+        pos = err;
+        err = FbTk::StringUtil::getStringBetween(cmd, arguments.c_str() + pos,
+                                                 '{', '}', " \t\n", true);
+        if (err > 0)
+            t = CommandParser::instance().parseLine(cmd, trusted);
+        if (err == 0 || *t == 0)
+            return 0;
+
+        pos += err;
+        err = FbTk::StringUtil::getStringBetween(cmd, arguments.c_str() + pos,
+                                                 '{', '}', " \t\n", true);
+        if (err > 0)
+            f = CommandParser::instance().parseLine(cmd, trusted);
+        if (err == 0 || *f == 0)
+            return 0;
+
+        return new FbTk::IfCommand(cond, t, f);
     }
     return 0;
 }
diff --git a/src/FbTk/Command.hh b/src/FbTk/Command.hh
index 391aa78..5d38cb8 100644
--- a/src/FbTk/Command.hh
+++ b/src/FbTk/Command.hh
@@ -31,6 +31,14 @@ public:
     virtual void execute() = 0;
 };
 
+/// Interface class for boolean commands
+class BoolCommand: public Command {
+public:
+    virtual ~BoolCommand() { }
+    virtual void execute() { bool_execute(); }
+    virtual bool bool_execute() = 0;
+};
+
 } // end namespace FbTk
 
 #endif // FBTK_COMMAND_HH
diff --git a/src/FbTk/LogicCommands.cc b/src/FbTk/LogicCommands.cc
new file mode 100644
index 0000000..99b5b53
--- /dev/null
+++ b/src/FbTk/LogicCommands.cc
@@ -0,0 +1,75 @@
+// LogicCommands.cc for FbTk
+// Copyright (c) 2007 Fluxbox Team (fluxgen at fluxbox dot org)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+
+// $Id: $
+
+#include "LogicCommands.hh"
+
+namespace FbTk {
+
+void OrCommand::add(RefCount<BoolCommand> &com) {
+    m_commandlist.push_back(com);
+}
+
+size_t OrCommand::size() const {
+    return m_commandlist.size();
+}
+
+bool OrCommand::bool_execute() {
+    for (size_t i=0; i < m_commandlist.size(); ++i) {
+        if (m_commandlist[i]->bool_execute())
+            return true;
+    }
+    return false;
+}
+
+void AndCommand::add(RefCount<BoolCommand> &com) {
+    m_commandlist.push_back(com);
+}
+
+size_t AndCommand::size() const {
+    return m_commandlist.size();
+}
+
+bool AndCommand::bool_execute() {
+    for (size_t i=0; i < m_commandlist.size(); ++i) {
+        if (!m_commandlist[i]->bool_execute())
+            return false;
+    }
+    return true;
+}
+
+void XorCommand::add(RefCount<BoolCommand> &com) {
+    m_commandlist.push_back(com);
+}
+
+size_t XorCommand::size() const {
+    return m_commandlist.size();
+}
+
+bool XorCommand::bool_execute() {
+    bool ret = false;
+    for (size_t i=0; i < m_commandlist.size(); ++i)
+        ret ^= m_commandlist[i]->bool_execute();
+    return ret;
+}
+
+}; // end namespace FbTk
diff --git a/src/FbTk/LogicCommands.hh b/src/FbTk/LogicCommands.hh
new file mode 100644
index 0000000..a94dad8
--- /dev/null
+++ b/src/FbTk/LogicCommands.hh
@@ -0,0 +1,97 @@
+// LogicCommands.hh for FbTk
+// Copyright (c) 2007 Fluxbox Team (fluxgen at fluxbox dot org)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+
+// $Id: $
+
+#ifndef FBTK_LOGICCOMMANDS_HH
+#define FBTK_LOGICCOMMANDS_HH
+
+#include "Command.hh"
+#include "RefCount.hh"
+
+#include <vector>
+
+namespace FbTk {
+
+/// executes a boolcommand and uses the result to decide what to do
+class IfCommand: public Command {
+public:
+    IfCommand(RefCount<BoolCommand> &cond,
+              RefCount<Command> &t, RefCount<Command> &f):
+        m_cond(cond), m_t(t), m_f(f) { }
+    void execute() {
+        if (m_cond->bool_execute())
+            m_t->execute();
+        else
+            m_f->execute();
+    }
+
+private:
+    RefCount<BoolCommand> m_cond;
+    RefCount<Command> m_t, m_f;
+};
+
+/// executes a list of boolcommands until one is true
+class OrCommand: public BoolCommand {
+public:
+    void add(RefCount<BoolCommand> &com);
+    size_t size() const;
+    bool bool_execute();
+
+private:
+    std::vector<RefCount<BoolCommand> > m_commandlist;
+};
+
+/// executes a list of boolcommands until one is false
+class AndCommand: public BoolCommand {
+public:
+    void add(RefCount<BoolCommand> &com);
+    size_t size() const;
+    bool bool_execute();
+
+private:
+    std::vector<RefCount<BoolCommand> > m_commandlist;
+};
+
+/// executes a list of boolcommands, returning the parity
+class XorCommand: public BoolCommand {
+public:
+    void add(RefCount<BoolCommand> &com);
+    size_t size() const;
+    bool bool_execute();
+
+private:
+    std::vector<RefCount<BoolCommand> > m_commandlist;
+};
+
+/// executes a boolcommand and returns the negation
+class NotCommand: public BoolCommand {
+public:
+    NotCommand(RefCount<BoolCommand> &com): m_command(com) { }
+    bool bool_execute() { return !m_command->bool_execute(); }
+
+private:
+    RefCount<BoolCommand> m_command;
+};
+
+} // end namespace FbTk
+
+#endif // FBTK_LOGICCOMMANDS_HH
diff --git a/src/FbTk/Makefile.am b/src/FbTk/Makefile.am
index d98c8f6..2bf9da7 100644
--- a/src/FbTk/Makefile.am
+++ b/src/FbTk/Makefile.am
@@ -21,6 +21,7 @@ libFbTk_a_SOURCES = App.hh App.cc Color.cc Color.hh Command.hh \
 	FbWindow.hh FbWindow.cc Font.cc Font.hh FontImp.hh \
 	I18n.cc I18n.hh \
 	ImageControl.hh ImageControl.cc \
+	LogicCommands.hh LogicCommands.cc \
 	MacroCommand.hh MacroCommand.cc \
 	Menu.hh Menu.cc MenuItem.hh MenuItem.cc \
 	MultiButtonMenuItem.hh MultiButtonMenuItem.cc \
diff --git a/src/FbTk/SimpleCommand.hh b/src/FbTk/SimpleCommand.hh
index 8cc7283..7ae91a5 100644
--- a/src/FbTk/SimpleCommand.hh
+++ b/src/FbTk/SimpleCommand.hh
@@ -39,6 +39,18 @@ private:
     Action m_action;
 };
 
+template <typename Receiver, typename ReturnType=bool>
+class SimpleBoolCommand: public BoolCommand {
+public:
+    typedef ReturnType (Receiver::* Action)();
+    SimpleBoolCommand(Receiver &r, Action a):
+        m_receiver(r), m_action(a) { }
+    bool bool_execute() { return (bool)(m_receiver.*m_action)(); }
+private:
+    Receiver &m_receiver;
+    Action m_action;
+};
+
 } // end namespace FbTk
 
 #endif // FBTK_SIMPLECOMMAND_HH
diff --git a/src/WorkspaceCmd.cc b/src/WorkspaceCmd.cc
index 79ceb40..26f113d 100644
--- a/src/WorkspaceCmd.cc
+++ b/src/WorkspaceCmd.cc
@@ -30,6 +30,7 @@
 #include "fluxbox.hh"
 #include "WinClient.hh"
 #include "FocusControl.hh"
+#include "WindowCmd.hh"
 
 #include "FbTk/KeyUtil.hh"
 
@@ -54,10 +55,54 @@ void WindowListCmd::execute() {
         FocusControl::Focusables::iterator it = win_list.begin(),
                                            it_end = win_list.end();
         for (; it != it_end; ++it) {
-            if (m_pat.match(**it) && (*it)->fbwindow())
-                m_cmd->execute(*(*it)->fbwindow());
+            if (m_pat.match(**it) && (*it)->fbwindow()) {
+                WindowCmd<void>::setWindow((*it)->fbwindow());
+                m_cmd->execute();
+            }
+        }
+    }
+}
+
+bool SomeCmd::bool_execute() {
+    if (m_pat.error())
+        return m_cmd->bool_execute();
+
+    BScreen *screen = Fluxbox::instance()->keyScreen();
+    if (screen != 0) {
+        FocusControl::Focusables win_list(screen->focusControl().creationOrderList().clientList());
+
+        FocusControl::Focusables::iterator it = win_list.begin(),
+                                           it_end = win_list.end();
+        for (; it != it_end; ++it) {
+            WinClient *client = dynamic_cast<WinClient *>(*it);
+            if (!client) continue;
+            WindowCmd<void>::setClient(client);
+            if (m_cmd->bool_execute())
+                return true;
+        }
+    }
+    return false;
+}
+
+bool EveryCmd::bool_execute() {
+    if (m_pat.error())
+        return m_cmd->bool_execute();
+
+    BScreen *screen = Fluxbox::instance()->keyScreen();
+    if (screen != 0) {
+        FocusControl::Focusables win_list(screen->focusControl().creationOrderList().clientList());
+
+        FocusControl::Focusables::iterator it = win_list.begin(),
+                                           it_end = win_list.end();
+        for (; it != it_end; ++it) {
+            WinClient *client = dynamic_cast<WinClient *>(*it);
+            if (!client) continue;
+            WindowCmd<void>::setClient(client);
+            if (!m_cmd->bool_execute())
+                return false;
         }
     }
+    return true;
 }
 
 void AttachCmd::execute() {
diff --git a/src/WorkspaceCmd.hh b/src/WorkspaceCmd.hh
index a530ae8..4f4545d 100644
--- a/src/WorkspaceCmd.hh
+++ b/src/WorkspaceCmd.hh
@@ -32,17 +32,37 @@
 
 #include "FbTk/RefCount.hh"
 
-class WindowHelperCmd;
-
 class WindowListCmd: public FbTk::Command {
 public:
-    WindowListCmd(FbTk::RefCount<WindowHelperCmd> cmd, const std::string &pat):
+    WindowListCmd(FbTk::RefCount<FbTk::Command> cmd, const std::string &pat):
             m_cmd(cmd), m_pat(pat.c_str()) { }
 
     void execute();
 
 private:
-    FbTk::RefCount<WindowHelperCmd> m_cmd;
+    FbTk::RefCount<FbTk::Command> m_cmd;
+    ClientPattern m_pat;
+};
+
+class SomeCmd: public FbTk::BoolCommand {
+public:
+    SomeCmd(FbTk::RefCount<FbTk::BoolCommand> cmd): m_cmd(cmd) { }
+
+    bool bool_execute();
+
+private:
+    FbTk::RefCount<FbTk::BoolCommand> m_cmd;
+    ClientPattern m_pat;
+};
+
+class EveryCmd: public FbTk::BoolCommand {
+public:
+    EveryCmd(FbTk::RefCount<FbTk::BoolCommand> cmd): m_cmd(cmd) { }
+
+    bool bool_execute();
+
+private:
+    FbTk::RefCount<FbTk::BoolCommand> m_cmd;
     ClientPattern m_pat;
 };
 
-- 
cgit v0.11.2