# # author: mathias gumz # # purpose: # # make commands more flexible by using patternmatching as in the apps # file for working with windows. # # comments: # # enhanced ClientPattern to except this: # # title= 1st entry of WM_CLASS # class= 2nd entry of WM_CLASS # name= WM_NAME # role= WM_ROLE # # icons= iconified? # groupleader= active tab, (TODO: maybe bad name of the pattern) # tabbed= windows with more than 1 client # stuck= stuck window? # shaded= shaded window? # # layer=[@] layernumber, current layer has a @ at the end # workspace=[@] workspacenumber, current workspace has a @ at # the end # # atm i changed the "NextWindow|PrevWindow|NextGroup|PrevGroup" actions to # accept those patterns as parameters. the old numerical argument is # translated to a new pattern so this patch should work out of the box with # unmodified keys :) # # its not yet done completly but it shows what can be done with such # patterns, further ideas: # # :ShowDesktop (layer=.*@) -> hide all windows on current layer (current as # in layer of the currently focussed window) # :NextWindow (class=xterm) -> focus to next xterm # # # each pattern is allowed only once as argument, so if you specify something # like: # # (class=xterm) (icons=yes) (class=gvim) # # last entry overrides old values, so in this case only gvim's will match. # # # THOUGHTS / PROBLEMS: # # o what if no regexp-support is compiled into fluxbox? # # # ############################################################################# # have fun and play with this patch, # # regards, mathias / akira # Index: src/WorkspaceCmd.hh =================================================================== --- src/WorkspaceCmd.hh (Revision 4071) +++ src/WorkspaceCmd.hh (Arbeitskopie) @@ -24,23 +24,29 @@ #ifndef WORKSPACECMD_HH #define WORKSPACECMD_HH -#include "Command.hh" + +#include "FbTk/Command.hh" #include "Screen.hh" +#include "ClientPattern.hh" class NextWindowCmd: public FbTk::Command { public: - explicit NextWindowCmd(int option):m_option(option) { } + explicit NextWindowCmd(int option, const char* pattern) : + m_option(option), m_pattern(pattern) { } void execute(); private: const int m_option; + const ClientPattern m_pattern; }; class PrevWindowCmd: public FbTk::Command { public: - explicit PrevWindowCmd(int option):m_option(option) { } + explicit PrevWindowCmd(int option, const char* pattern) : + m_option(option), m_pattern(pattern) { } void execute(); private: const int m_option; + const ClientPattern m_pattern; }; class DirFocusCmd: public FbTk::Command { Index: src/FbCommands.cc =================================================================== --- src/FbCommands.cc (Revision 4071) +++ src/FbCommands.cc (Arbeitskopie) @@ -28,6 +28,8 @@ #include "Workspace.hh" #include "Window.hh" #include "Keys.hh" +#include "WinClient.hh" +#include "ClientPattern.hh" #include "FbTk/Theme.hh" #include "FbTk/Menu.hh" @@ -365,5 +367,27 @@ break; }; } + + +PrintPatternCmd::PrintPatternCmd(const char* pattern) : m_pattern(pattern) { +} + + +void PrintPatternCmd::execute() { + BScreen::FocusedWindows::const_iterator it; + const BScreen::FocusedWindows& wins = Fluxbox::instance()->keyScreen()->getFocusedList(); + + cout << m_pattern.toString() << "\n"; + cout << "------------------\n"; + + for(it = wins.begin(); it != wins.end(); it++) { + if (m_pattern == *(*it)) + cout << "match [" << (*it)->title() << "]\n"; + } + + cout << "------------------\n"; +} + + }; // end namespace FbCommands Index: src/Screen.cc =================================================================== --- src/Screen.cc (Revision 4071) +++ src/Screen.cc (Arbeitskopie) @@ -51,6 +51,7 @@ #include "MenuCreator.hh" #include "WinClient.hh" +#include "ClientPattern.hh" #include "FbWinFrame.hh" #include "Strut.hh" #include "CommandParser.hh" @@ -1381,12 +1382,17 @@ } } +void BScreen::nextFocus() { + const static ClientPattern pattern("(icons=no) (tabbed=no) (workspace=.*@)"); + nextFocus(0, pattern); +} -void BScreen::nextFocus(int opts) { - const int num_windows = currentWorkspace()->numberOfWindows(); +void BScreen::prevFocus() { + const static ClientPattern pattern("(icons=no) (tabbed=no) (workspace=.*@)"); + prevFocus(0, pattern); +} - if (num_windows < 1) - return; +void BScreen::nextFocus(int opts, const ClientPattern& pattern) { if (!(opts & CYCLELINEAR)) { if (!cycling_focus) { @@ -1411,13 +1417,14 @@ if ((*it) == (*cycling_window)) { break; } + + cout << "[" << + (*(*it)).title() << "] :" + << pattern.toString() << "\n"; + + if (pattern.match(*(*it))) { - FluxboxWindow *fbwin = (*it)->fbwindow(); - if (fbwin && !fbwin->isIconic() && - (fbwin->isStuck() - || fbwin->workspaceNumber() == currentWorkspaceID())) { - // either on this workspace, or stuck - + FluxboxWindow* fbwin = (*it)->fbwindow(); // keep track of the originally selected window in a set WinClient &last_client = fbwin->winClient(); @@ -1465,7 +1472,9 @@ if (it == wins.end()) it = wins.begin(); // see if the window should be skipped - if (! (doSkipWindow((*it)->winClient(), opts) || !(*it)->setInputFocus()) ) + if (! pattern.match((*it)->winClient()) || + ! (doSkipWindow((*it)->winClient(), opts) || + ! (*it)->setInputFocus()) ) break; } while ((*it) != focused_group); @@ -1476,11 +1485,7 @@ } -void BScreen::prevFocus(int opts) { - int num_windows = currentWorkspace()->numberOfWindows(); - - if (num_windows < 1) - return; +void BScreen::prevFocus(int opts, const ClientPattern& pattern) { if (!(opts & CYCLELINEAR)) { if (!cycling_focus) { @@ -1507,12 +1512,10 @@ break; } - FluxboxWindow *fbwin = (*it)->fbwindow(); - if (fbwin && !fbwin->isIconic() && - (fbwin->isStuck() - || fbwin->workspaceNumber() == currentWorkspaceID())) { - // either on this workspace, or stuck + if (pattern.match(*(*it))) { + FluxboxWindow* fbwin = (*it)->fbwindow(); + // keep track of the originally selected window in a set WinClient &last_client = fbwin->winClient(); @@ -1561,7 +1564,9 @@ it = wins.end(); --it; // see if the window should be skipped - if (! (doSkipWindow((*it)->winClient(), opts) || !(*it)->setInputFocus()) ) + if (! pattern.match((*it)->winClient()) || + ! (doSkipWindow((*it)->winClient(), opts) || + ! (*it)->setInputFocus()) ) break; } while ((*it) != focused_group); @@ -2024,10 +2029,10 @@ bool BScreen::doSkipWindow(const WinClient &winclient, int opts) { const FluxboxWindow *win = winclient.fbwindow(); return (!win || - (opts & CYCLESKIPSTUCK) != 0 && win->isStuck() || // skip if stuck + //(opts & CYCLESKIPSTUCK) != 0 && win->isStuck() || // skip if stuck // skip if not active client (i.e. only visit each fbwin once) - (opts & CYCLEGROUPS) != 0 && win->winClient().window() != winclient.window() || - (opts & CYCLESKIPSHADED) != 0 && win->isShaded() || // skip if shaded + //(opts & CYCLEGROUPS) != 0 && win->winClient().window() != winclient.window() || + //(opts & CYCLESKIPSHADED) != 0 && win->isShaded() || // skip if shaded win->isFocusHidden() ); } Index: src/WorkspaceCmd.cc =================================================================== --- src/WorkspaceCmd.cc (Revision 4071) +++ src/WorkspaceCmd.cc (Arbeitskopie) @@ -50,17 +50,17 @@ unsigned int mods = FbTk::KeyUtil::instance().cleanMods(fb->lastEvent().xkey.state); mods = FbTk::KeyUtil::instance().isolateModifierMask(mods); if (mods == 0) // can't stacked cycle unless there is a mod to grab - screen->nextFocus(m_option | BScreen::CYCLELINEAR); + screen->nextFocus(m_option | BScreen::CYCLELINEAR, m_pattern); else { // if stacked cycling, then set a watch for // the release of exactly these modifiers if (!fb->watchingScreen() && !(m_option & BScreen::CYCLELINEAR)) Fluxbox::instance()->watchKeyRelease(*screen, mods); - screen->nextFocus(m_option); + screen->nextFocus(m_option, m_pattern); } } else - screen->nextFocus(m_option); + screen->nextFocus(m_option, m_pattern); } } @@ -73,17 +73,17 @@ unsigned int mods = FbTk::KeyUtil::instance().cleanMods(fb->lastEvent().xkey.state); mods = FbTk::KeyUtil::instance().isolateModifierMask(mods); if (mods == 0) // can't stacked cycle unless there is a mod to grab - screen->prevFocus(m_option | BScreen::CYCLELINEAR); + screen->prevFocus(m_option | BScreen::CYCLELINEAR, m_pattern); else { // if stacked cycling, then set a watch for // the release of exactly these modifiers if (!fb->watchingScreen() && !(m_option & BScreen::CYCLELINEAR)) Fluxbox::instance()->watchKeyRelease(*screen, mods); - screen->prevFocus(m_option); + screen->prevFocus(m_option, m_pattern); } } else - screen->nextFocus(m_option); + screen->nextFocus(m_option, m_pattern); } } Index: src/ClientPattern.hh =================================================================== --- src/ClientPattern.hh (Revision 4071) +++ src/ClientPattern.hh (Arbeitskopie) @@ -30,7 +30,7 @@ #include "NotCopyable.hh" #include -#include +#include class WinClient; @@ -53,7 +53,24 @@ /// @return a string representation of this pattern std::string toString() const; - enum WinProperty { TITLE, CLASS, NAME, ROLE }; + enum WinProperty { + TITLE, // match 1st entry of WM_CLASS title= + CLASS, // match 2nd entry of WM_CLASS class= + NAME, // match WM_NAME name= + ROLE, // match WM_WINDOW_ROLE role= + + ICONIFIED, // match iconified windows icons="yes"|"no" + GROUPLEADER,// match the groupleader window groupleader="yes"|"no" + TABBED, // match a tabbed window tabbed="yes"|"no" + STUCK, // match a stucked window stuck="yes"|"no" + SHADED, // match a shaded window shaded="yes"|"no" + + // the current item has a @ at the end + LAYER, // match a layer layer=@ + WORKSPACE, // match a window on a workspace workspace=@ + + CURRENTWIN, // match the current window current="yes"|"no" <- just an idea + }; /// Does this client match this pattern? bool match(const WinClient &win) const; @@ -96,7 +113,7 @@ }; - typedef std::list Terms; + typedef std::map Terms; Terms m_terms; ///< our pattern is made up of a sequence of terms currently we "and" them all Index: src/FbTk/stringstream.hh =================================================================== --- src/FbTk/stringstream.hh (Revision 4071) +++ src/FbTk/stringstream.hh (Arbeitskopie) @@ -7,10 +7,12 @@ #ifdef HAVE_SSTREAM #include -#define FbTk_istringstream std::istringstream +typedef std::istringstream FbTk_istringstream; +typedef std::ostringstream FbTk_ostringstream; #elif HAVE_STRSTREAM #include -#define FbTk_istringstream std::istrstream +typedef std::istrstream FbTk_istringstream; +typedef std::ostrstream FBTk_ostringstream; #else #error "You dont have sstream or strstream headers!" #endif // HAVE_STRSTREAM Index: src/FbCommandFactory.cc =================================================================== --- src/FbCommandFactory.cc (Revision 4071) +++ src/FbCommandFactory.cc (Arbeitskopie) @@ -143,6 +143,7 @@ "workspace12", /* end note */ "workspacemenu", + "printpattern", 0 }; @@ -369,11 +370,54 @@ num = atoi(command.substr(9).c_str()); return new JumpToWorkspaceCmd(num-1); - } else if (command == "nextwindow") - return new NextWindowCmd(atoi(arguments.c_str())); - else if (command == "prevwindow") - return new PrevWindowCmd(atoi(arguments.c_str())); - else if (command == "focusup") + } else if (command == "nextwindow") { + int opts = 0; + FbTk_istringstream iss(arguments); + FbTk_ostringstream pattern; + + int tmp = 0; + iss >> tmp; + + pattern << "(icons=no) (workspace=.*@)"; + + if (!iss.bad()) { + if (tmp & BScreen::CYCLESKIPSHADED) + pattern << " (shaded=no)"; + if (tmp & BScreen::CYCLESKIPSTUCK) + pattern << " (stuck=no)"; + if (tmp & BScreen::CYCLEGROUPS) + pattern << " (groupleader=yes)"; + + opts = tmp; + } + + return new NextWindowCmd(opts, (pattern.str() + iss.str()).c_str()); + + } else if (command == "prevwindow") { + + int opts = 0; + FbTk_istringstream iss(arguments); + FbTk_ostringstream pattern; + + int tmp = 0; + iss >> tmp; + + pattern << "(icons=no) (workspace=.*@)"; + + if (!iss.bad()) { + if (tmp & BScreen::CYCLESKIPSHADED) + pattern << " (shaded=no)"; + if (tmp & BScreen::CYCLESKIPSTUCK) + pattern << " (stuck=no)"; + if (tmp & BScreen::CYCLEGROUPS) + pattern << " (groupleader=yes)"; + + opts = tmp; + } + + return new PrevWindowCmd(opts, (pattern.str() + iss.str()).c_str()); + + } else if (command == "focusup") return new DirFocusCmd(BScreen::FOCUSUP); else if (command == "focusdown") return new DirFocusCmd(BScreen::FOCUSDOWN); @@ -381,11 +425,54 @@ return new DirFocusCmd(BScreen::FOCUSLEFT); else if (command == "focusright") return new DirFocusCmd(BScreen::FOCUSRIGHT); - else if (command == "nextgroup") - return new NextWindowCmd(atoi(arguments.c_str()) ^ BScreen::CYCLEGROUPS); - else if (command == "prevgroup") - return new PrevWindowCmd(atoi(arguments.c_str()) ^ BScreen::CYCLEGROUPS); - else if (command == "arrangewindows") + else if (command == "nextgroup") { + + int opts = 0; + FbTk_istringstream iss(arguments); + FbTk_ostringstream pattern; + + int tmp = 0; + iss >> tmp; + + pattern << "(icons=no) (workspace=.*@)"; + + if (!iss.bad()) { + if (tmp & BScreen::CYCLESKIPSHADED) + pattern << " (shaded=no)"; + if (tmp & BScreen::CYCLESKIPSTUCK) + pattern << " (stuck=no)"; + + opts = tmp; + } + + pattern << " (groupleader=yes)"; + + return new NextWindowCmd(opts, pattern.str().c_str()); + + } else if (command == "prevgroup") { + + int opts = 0; + FbTk_istringstream iss(arguments); + FbTk_ostringstream pattern; + + int tmp = 0; + iss >> tmp; + + pattern << "(icons=no) (workspace=.*@)"; + + if (!iss.bad()) { + if (tmp & BScreen::CYCLESKIPSHADED) + pattern << " (shaded=no)"; + if (tmp & BScreen::CYCLESKIPSTUCK) + pattern << " (stuck=no)"; + + opts = tmp; + } + + pattern << " (groupleader=yes)"; + + return new PrevWindowCmd(opts, pattern.str().c_str()); + } else if (command == "arrangewindows") return new ArrangeWindowsCmd(); else if (command == "showdesktop") return new ShowDesktopCmd(); @@ -471,6 +558,10 @@ return macro; delete macro; + } else if (command == "printpattern") { + return new PrintPatternCmd(arguments.c_str()); + } + return 0; } Index: src/FbCommands.hh =================================================================== --- src/FbCommands.hh (Revision 4071) +++ src/FbCommands.hh (Arbeitskopie) @@ -26,7 +26,8 @@ #ifndef FBCOMMANDS_HH #define FBCOMMANDS_HH -#include "Command.hh" +#include "FbTk/Command.hh" +#include "ClientPattern.hh" #include @@ -168,6 +169,16 @@ Destination m_dest; }; + +class PrintPatternCmd : public FbTk::Command { +public: + PrintPatternCmd(const char* patterns); + void execute(); +private: + ClientPattern m_pattern; +}; + + } // end namespace FbCommands #endif // FBCOMMANDS_HH Index: src/Screen.hh =================================================================== --- src/Screen.hh (Revision 4071) +++ src/Screen.hh (Arbeitskopie) @@ -62,6 +62,7 @@ class Strut; class Slit; class HeadArea; +class ClientPattern; namespace FbTk { class Menu; @@ -278,10 +279,10 @@ bool changeworkspace=true); void reassociateWindow(FluxboxWindow *window, unsigned int workspace_id, bool ignore_sticky); - inline void prevFocus() { prevFocus(0); } - inline void nextFocus() { nextFocus(0); } - void prevFocus(int options); - void nextFocus(int options); + void prevFocus(); + void nextFocus(); + void prevFocus(int options, const ClientPattern& pattern); + void nextFocus(int options, const ClientPattern& pattern); void raiseFocus(); void setFocusedWindow(WinClient &winclient); Index: src/ClientPattern.cc =================================================================== --- src/ClientPattern.cc (Revision 4071) +++ src/ClientPattern.cc (Arbeitskopie) @@ -25,6 +25,7 @@ #include "ClientPattern.hh" #include "RegExp.hh" #include "WinClient.hh" +#include "Screen.hh" #include "FbTk/StringUtil.hh" #include "FbTk/App.hh" @@ -40,6 +41,7 @@ #include #include #include +#include #ifdef HAVE_CSTDIO #include #else @@ -105,6 +107,22 @@ prop = TITLE; } else if (strcasecmp(memstr.c_str(), "role") == 0) { prop = ROLE; + } else if (strcasecmp(memstr.c_str(), "icons") == 0) { + prop = ICONIFIED; + } else if (strcasecmp(memstr.c_str(), "tabbed") == 0) { + prop = TABBED; + } else if (strcasecmp(memstr.c_str(), "current") == 0) { + prop = CURRENTWIN; + } else if (strcasecmp(memstr.c_str(), "layer") == 0) { + prop = LAYER; + } else if (strcasecmp(memstr.c_str(), "workspace") == 0) { + prop = WORKSPACE; + } else if (strcasecmp(memstr.c_str(), "stuck") == 0) { + prop = STUCK; + } else if (strcasecmp(memstr.c_str(), "shaded") == 0) { + prop = SHADED; + } else if (strcasecmp(memstr.c_str(), "groupleader") == 0) { + prop = GROUPLEADER; } else { had_error = pos + match.find_first_of('(') + 1; break; @@ -147,20 +165,19 @@ if (had_error > 0) { m_matchlimit = had_error; // delete all the terms - while (!m_terms.empty()) { - Term * term = m_terms.back(); - delete term; - m_terms.pop_back(); + while(!m_terms.empty()) { + delete m_terms.begin()->second; + m_terms.erase(m_terms.begin()); } } } ClientPattern::~ClientPattern() { // delete all the terms - while (!m_terms.empty()) { - delete m_terms.back(); - m_terms.pop_back(); - } + while(!m_terms.empty()) { + delete m_terms.begin()->second; + m_terms.erase(m_terms.begin()); + } } // return a string representation of this pattern @@ -171,7 +188,9 @@ for (; it != it_end; ++it) { pat.append(" ("); - switch ((*it)->prop) { + Term* term = it->second; + + switch (term->prop) { case NAME: // do nothing -> this is the default break; @@ -183,9 +202,34 @@ break; case ROLE: pat.append("role="); - } + break; + case CURRENTWIN: + pat.append("current="); + break; + case LAYER: + pat.append("layer="); + break; + case WORKSPACE: + pat.append("workspace="); + break; + case ICONIFIED: + pat.append("icons="); + break; + case TABBED: + pat.append("tabbed="); + break; + case STUCK: + pat.append("stuck="); + break; + case SHADED: + pat.append("shaded="); + break; + case GROUPLEADER: + pat.append("groupleader="); + break; + }; - pat.append((*it)->orig); + pat.append(term->orig); pat.append(")"); } @@ -209,7 +253,9 @@ Terms::const_iterator it = m_terms.begin(); Terms::const_iterator it_end = m_terms.end(); for (; it != it_end; ++it) { - if (!(*it)->regexp.match(getProperty((*it)->prop, win))) + Term* term = it->second; + cout << " -> " << term->orig << ": " << getProperty(term->prop, win) << "\n"; + if (!term->regexp.match(getProperty(term->prop, win))) return false; } return true; @@ -220,6 +266,12 @@ // function that we wish to match against. bool ClientPattern::addTerm(const std::string &str, WinProperty prop) { + Terms::iterator it = m_terms.find(prop); + + if (it != m_terms.end()) { + m_terms.erase(it); + } + Term *term = new Term(str, true); term->orig = str; term->prop = prop; @@ -228,11 +280,14 @@ delete term; return false; } - m_terms.push_back(term); + m_terms[prop] = term; return true; } std::string ClientPattern::getProperty(WinProperty prop, const WinClient &client) const { + + FbTk_ostringstream oss; + switch (prop) { case TITLE: return client.title(); @@ -247,6 +302,61 @@ Atom wm_role = XInternAtom(FbTk::App::instance()->display(), "WM_WINDOW_ROLE", False); return client.textProperty(wm_role); break; + case WORKSPACE: + if (client.fbwindow()) { + oss << client.fbwindow()->workspaceNumber(); + if (client.fbwindow()->screen().currentWorkspaceID() == client.fbwindow()->workspaceNumber()) + oss << "@"; + return oss.str(); + } + break; + case LAYER: + if (client.fbwindow()) { + oss << client.fbwindow()->layerNum(); + return oss.str(); + } + break; + case ICONIFIED: + if (client.fbwindow()) { + if (client.fbwindow()->isIconic()) + return "yes"; + else + return "no"; + } + break; + case TABBED: + if (client.fbwindow()) { + if (client.fbwindow()->numClients() > 1) + return "yes"; + else + return "no"; + } + break; + case STUCK: + if (client.fbwindow()) { + if (client.fbwindow()->isStuck()) + return "yes"; + else + return "no"; + } + break; + case SHADED: + if (client.fbwindow()) { + if (client.fbwindow()->isShaded()) + return "yes"; + else + return "no"; + } + break; + case GROUPLEADER: + if (client.fbwindow()) { + if (client.fbwindow()->winClient().window() == client.window()) + return "yes"; + else + return "no"; + } + break; } - return client.getWMClassName(); + + return ""; //client.getWMClassName(); }