From 3ab6b48974c32c742bda5dc6c3f217f9d58e3d99 Mon Sep 17 00:00:00 2001 From: markt Date: Fri, 30 Mar 2007 20:29:46 +0000 Subject: pattern matching for window cycling --- ChangeLog | 17 ++++ src/ClientPattern.cc | 175 ++++++++++++++++++++++++++++++++--------- src/ClientPattern.hh | 16 ++-- src/FbCommandFactory.cc | 77 ++++++++++++++---- src/FocusControl.cc | 146 ++++++++++++++++++---------------- src/FocusControl.hh | 18 ++--- src/Focusable.hh | 8 +- src/Layer.hh | 49 ++++++++++++ src/Resources.cc | 40 +--------- src/Screen.cc | 15 ++-- src/Screen.hh | 15 ++-- src/WinClient.cc | 14 ++-- src/WinClient.hh | 7 +- src/Window.cc | 24 ++++-- src/Window.hh | 3 + src/WorkspaceCmd.cc | 8 +- src/WorkspaceCmd.hh | 17 +++- util/fluxbox-update_configs.cc | 18 ++--- 18 files changed, 445 insertions(+), 222 deletions(-) diff --git a/ChangeLog b/ChangeLog index 6e368be..3bd82eb 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,22 @@ (Format: Year/Month/Day) Changes for 1.1: + * Changed syntax for window cycling (Mark) + - Instead of a bitmask, the window cycling functions NextWindow, + PrevWindow, TypeAheadFocus, and GoToWindow now take a list of options + enclosed in {} followed by a pattern similar to those used in the apps + file. + - Examples: + * NextWindow {static groups} (shaded=yes) (name=xterm) + - cycles through all shaded xterms in creation order, only focusing + the active tab in the group + * GoToWindow 3 (title=[current]) + - focuses the third client in last-focused order with the same title + as the currently focused window + - The options are: name, class, title, role, maximized, minimized, shaded, + stuck, focushidden (can't be disabled), iconhidden, workspace (matches + workspace names, not numbers), head (numbers), and layer (names) + - Parsing is a pain, so you'll have to update your keys file yourself for + now. *07/03/29: * Removed groups file; entries will be added to the apps file automatically (Mark) diff --git a/src/ClientPattern.cc b/src/ClientPattern.cc index d644767..9324bee 100644 --- a/src/ClientPattern.cc +++ b/src/ClientPattern.cc @@ -24,7 +24,12 @@ #include "ClientPattern.hh" #include "RegExp.hh" + +#include "FocusControl.hh" +#include "Layer.hh" +#include "Screen.hh" #include "WinClient.hh" +#include "Workspace.hh" #include "FbTk/StringUtil.hh" #include "FbTk/App.hh" @@ -73,54 +78,67 @@ ClientPattern::ClientPattern(const char *str): If no limit is specified, no limit is applied (i.e. limit = infinity) */ - int had_error = 0; + bool had_error = false; int pos = 0; string match; int err = 1; // for starting first loop - while (had_error == 0 && err > 0) { + while (!had_error && err > 0) { err = FbTk::StringUtil::getStringBetween(match, str + pos, '(', ')', " \t\n", true); if (err > 0) { - size_t eq = match.find_first_of('='); + // need to determine the property used + string memstr, expr; + WinProperty prop; + string::size_type eq = match.find_first_of('='); if (eq == match.npos) { - if (!addTerm(match, NAME)) { - had_error = pos + match.find_first_of('(') + 1; - break; - } + memstr = match; + expr = "[current]"; } else { - // need to determine the property used - string memstr, expr; - WinProperty prop; memstr.assign(match, 0, eq); // memstr = our identifier expr.assign(match, eq+1, match.length()); - if (strcasecmp(memstr.c_str(), "name") == 0) { - prop = NAME; - } else if (strcasecmp(memstr.c_str(), "class") == 0) { - prop = CLASS; - } else if (strcasecmp(memstr.c_str(), "title") == 0) { - prop = TITLE; - } else if (strcasecmp(memstr.c_str(), "role") == 0) { - prop = ROLE; - } else { - had_error = pos + match.find_first_of('(') + 1; - break; - } - if (!addTerm(expr, prop)) { - had_error = pos + ((str+pos) - index(str+pos, '=')) + 1; - break; - } } + if (strcasecmp(memstr.c_str(), "name") == 0) { + prop = NAME; + } else if (strcasecmp(memstr.c_str(), "class") == 0) { + prop = CLASS; + } else if (strcasecmp(memstr.c_str(), "title") == 0) { + prop = TITLE; + } else if (strcasecmp(memstr.c_str(), "role") == 0) { + prop = ROLE; + } else if (strcasecmp(memstr.c_str(), "maximized") == 0) { + prop = MAXIMIZED; + } else if (strcasecmp(memstr.c_str(), "minimized") == 0) { + prop = MINIMIZED; + } else if (strcasecmp(memstr.c_str(), "shaded") == 0) { + prop = SHADED; + } else if (strcasecmp(memstr.c_str(), "stuck") == 0) { + prop = STUCK; + } else if (strcasecmp(memstr.c_str(), "focushidden") == 0) { + prop = FOCUSHIDDEN; + } else if (strcasecmp(memstr.c_str(), "iconhidden") == 0) { + prop = ICONHIDDEN; + } else if (strcasecmp(memstr.c_str(), "workspace") == 0) { + prop = WORKSPACE; + } else if (strcasecmp(memstr.c_str(), "head") == 0) { + prop = HEAD; + } else if (strcasecmp(memstr.c_str(), "layer") == 0) { + prop = LAYER; + } else { + prop = NAME; + expr = match; + } + had_error = !addTerm(expr, prop); pos += err; } } - if (pos == 0 && had_error == 0) { + if (pos == 0 && !had_error) { // no match terms given, this is not allowed - had_error = 1; + had_error = true; } - if (had_error == 0) { + if (!had_error) { // otherwise, we check for a number string number; err = FbTk::StringUtil::getStringBetween(number, @@ -139,12 +157,11 @@ ClientPattern::ClientPattern(const char *str): uerr = match.find_first_not_of(" \t\n", pos); if (uerr != match.npos) { // found something, not good - had_error++; + had_error = true; } } - if (had_error > 0) { - m_matchlimit = had_error; + if (had_error) { // delete all the terms while (!m_terms.empty()) { Term * term = m_terms.back(); @@ -183,6 +200,34 @@ string ClientPattern::toString() const { break; case ROLE: pat.append("role="); + break; + case MAXIMIZED: + pat.append("maximized="); + break; + case MINIMIZED: + pat.append("minimized="); + break; + case SHADED: + pat.append("shaded="); + break; + case STUCK: + pat.append("stuck="); + break; + case FOCUSHIDDEN: + pat.append("focushidden="); + break; + case ICONHIDDEN: + pat.append("iconhidden="); + break; + case WORKSPACE: + pat.append("workspace="); + break; + case HEAD: + pat.append("head="); + break; + case LAYER: + pat.append("layer="); + break; } pat.append((*it)->orig); @@ -198,9 +243,8 @@ string ClientPattern::toString() const { } // does this client match this pattern? -bool ClientPattern::match(const WinClient &win) const { - if (m_matchlimit != 0 && m_nummatches >= m_matchlimit || - m_terms.empty()) +bool ClientPattern::match(const Focusable &win) const { + if (m_matchlimit != 0 && m_nummatches >= m_matchlimit) return false; // already matched out // regmatch everything @@ -209,7 +253,20 @@ bool ClientPattern::match(const WinClient &win) const { 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))) + if ((*it)->orig == "[current]") { + // workspaces don't necessarily have unique names, so we want to + // compare numbers instead of strings + if ((*it)->prop == WORKSPACE && (!win.fbwindow() || + win.fbwindow()->workspaceNumber() != + win.screen().currentWorkspaceID())) + return false; + else { + WinClient *focused = FocusControl::focusedWindow(); + if (!focused || getProperty((*it)->prop, win) != + getProperty((*it)->prop, *focused)) + return false; + } + } else if (!(*it)->regexp.match(getProperty((*it)->prop, win))) return false; } return true; @@ -232,7 +289,11 @@ bool ClientPattern::addTerm(const string &str, WinProperty prop) { return true; } -string ClientPattern::getProperty(WinProperty prop, const WinClient &client) const { +string ClientPattern::getProperty(WinProperty prop, + const Focusable &client) const { + // we need this for some of the window properties + const FluxboxWindow *fbwin = client.fbwindow(); + switch (prop) { case TITLE: return client.title(); @@ -244,8 +305,44 @@ string ClientPattern::getProperty(WinProperty prop, const WinClient &client) con return client.getWMClassName(); break; case ROLE: - Atom wm_role = XInternAtom(FbTk::App::instance()->display(), "WM_WINDOW_ROLE", False); - return client.textProperty(wm_role); + return client.getWMRole(); + break; + case MAXIMIZED: + return (fbwin && fbwin->isMaximized()) ? "yes" : "no"; + break; + case MINIMIZED: + return (fbwin && fbwin->isIconic()) ? "yes" : "no"; + break; + case SHADED: + return (fbwin && fbwin->isShaded()) ? "yes" : "no"; + break; + case STUCK: + return (fbwin && fbwin->isStuck()) ? "yes" : "no"; + break; + case FOCUSHIDDEN: + return (fbwin && fbwin->isFocusHidden()) ? "yes" : "no"; + break; + case ICONHIDDEN: + return (fbwin && fbwin->isIconHidden()) ? "yes" : "no"; + break; + case WORKSPACE: { + if (!fbwin) + return ""; + const Workspace *w = client.screen().getWorkspace(fbwin->workspaceNumber()); + return w ? w->name() : ""; + break; + } + case HEAD: { + if (!fbwin) + return ""; + int head = client.screen().getHead(fbwin->fbWindow()); + char tmpstr[128]; + sprintf(tmpstr, "%d", head); + return std::string(tmpstr); + break; + } + case LAYER: + return fbwin ? ::Layer::getString(fbwin->layerNum()) : ""; break; } return client.getWMClassName(); diff --git a/src/ClientPattern.hh b/src/ClientPattern.hh index 7610e77..4a3cd0b 100644 --- a/src/ClientPattern.hh +++ b/src/ClientPattern.hh @@ -32,7 +32,7 @@ #include #include -class WinClient; +class Focusable; /** * This class represents a "pattern" that we can match against a @@ -53,10 +53,14 @@ public: /// @return a string representation of this pattern std::string toString() const; - enum WinProperty { TITLE, CLASS, NAME, ROLE }; + enum WinProperty { + TITLE, CLASS, NAME, ROLE, + MAXIMIZED, MINIMIZED, SHADED, STUCK, FOCUSHIDDEN, ICONHIDDEN, + WORKSPACE, HEAD, LAYER + }; /// Does this client match this pattern? - bool match(const WinClient &win) const; + bool match(const Focusable &win) const; /** * Add an expression to match against @@ -68,7 +72,7 @@ public: inline void addMatch() { ++m_nummatches; } - inline bool operator == (const WinClient &win) const { + inline bool operator == (const Focusable &win) const { return match(win); } @@ -79,9 +83,9 @@ public: * If there are no terms, then there is assumed to be an error * the column of the error is stored in m_matchlimit */ - inline int error() const { return m_terms.empty() ? m_matchlimit : 0; } + inline int error() const { return m_terms.empty() ? 1 : 0; } - std::string getProperty(WinProperty prop, const WinClient &winclient) const; + std::string getProperty(WinProperty prop, const Focusable &winclient) const; private: /** diff --git a/src/FbCommandFactory.cc b/src/FbCommandFactory.cc index 378a2b8..1ae8d44 100644 --- a/src/FbCommandFactory.cc +++ b/src/FbCommandFactory.cc @@ -52,11 +52,35 @@ using std::endl; // autoregister this module to command parser FbCommandFactory FbCommandFactory::s_autoreg; +namespace { + static int getint(const char *str, int defaultvalue) { sscanf(str, "%d", &defaultvalue); return defaultvalue; } +void parseNextWindowArgs(const string &in, int &opts, string &pat) { + string options; + int err = FbTk::StringUtil::getStringBetween(options, in.c_str(), '{', '}'); + + // the rest of the string is a ClientPattern + pat = in.c_str() + err; + + // now parse the options + vector args; + FbTk::StringUtil::stringtok(args, options); + vector::iterator it = args.begin(), it_end = args.end(); + opts = 0; + for (; it != it_end; ++it) { + if (strcasecmp((*it).c_str(), "static") == 0) + opts |= FocusControl::CYCLELINEAR; + else if (strcasecmp((*it).c_str(), "groups") == 0) + opts |= FocusControl::CYCLEGROUPS; + } +} + +}; // end anonymous namespace + FbCommandFactory::FbCommandFactory() { // setup commands that we can handle const char* commands[] = { @@ -417,17 +441,44 @@ FbTk::Command *FbCommandFactory::stringToCommand(const std::string &command, cerr<<"*** WARNING: 'Workspace' actions are deprecated! Use 'Workspace ' instead"<> num >> options; - return new GoToWindowCmd(num, options); + } else if (command == "nextwindow") { + int opts; + string pat; + parseNextWindowArgs(arguments, opts, pat); + return new NextWindowCmd(opts, pat); + } else if (command == "nextgroup") { + int opts; + string pat; + parseNextWindowArgs(arguments, opts, pat); + opts |= FocusControl::CYCLEGROUPS; + return new NextWindowCmd(opts, pat); + } else if (command == "prevwindow") { + int opts; + string pat; + parseNextWindowArgs(arguments, opts, pat); + return new PrevWindowCmd(opts, pat); + } else if (command == "prevgroup") { + int opts; + string pat; + parseNextWindowArgs(arguments, opts, pat); + opts |= FocusControl::CYCLEGROUPS; + return new PrevWindowCmd(opts, pat); + } else if (command == "typeaheadfocus") { + int opts; + string pat; + parseNextWindowArgs(arguments, opts, pat); + return new TypeAheadFocusCmd(opts, pat); + } else if (command == "gotowindow") { + int num, opts; + string args, pat; + FbTk_istringstream iss(arguments.c_str()); + iss >> num; + string::size_type pos = arguments.find_first_of("({"); + if (pos != string::npos && pos != arguments.size()) + args = arguments.c_str() + pos; +std::cerr << "GoToWindow args: " << args << std::endl; + parseNextWindowArgs(args, opts, pat); + return new GoToWindowCmd(num, opts, pat); } else if (command == "focusup") return new DirFocusCmd(FocusControl::FOCUSUP); else if (command == "focusdown") @@ -436,10 +487,6 @@ FbTk::Command *FbCommandFactory::stringToCommand(const std::string &command, return new DirFocusCmd(FocusControl::FOCUSLEFT); else if (command == "focusright") return new DirFocusCmd(FocusControl::FOCUSRIGHT); - else if (command == "nextgroup") - return new NextWindowCmd(atoi(arguments.c_str()) ^ FocusControl::CYCLEGROUPS); - else if (command == "prevgroup") - return new PrevWindowCmd(atoi(arguments.c_str()) ^ FocusControl::CYCLEGROUPS); else if (command == "arrangewindows") return new ArrangeWindowsCmd(); else if (command == "showdesktop") diff --git a/src/FocusControl.cc b/src/FocusControl.cc index c5ee939..c97b699 100644 --- a/src/FocusControl.cc +++ b/src/FocusControl.cc @@ -23,6 +23,7 @@ #include "FocusControl.hh" +#include "ClientPattern.hh" #include "Screen.hh" #include "Window.hh" #include "WinClient.hh" @@ -43,6 +44,22 @@ WinClient *FocusControl::s_focused_window = 0; FluxboxWindow *FocusControl::s_focused_fbwindow = 0; bool FocusControl::s_reverting = false; +namespace { + +bool doSkipWindow(const Focusable &win, const ClientPattern *pat) { + const FluxboxWindow *fbwin = win.fbwindow(); + if (!fbwin || fbwin->isFocusHidden()) + return true; // skip if no fbwindow or if focushidden + if (pat && !pat->match(win)) + return true; // skip if it doesn't match the pattern + if (fbwin->workspaceNumber() != win.screen().currentWorkspaceID() && + !fbwin->isStuck()) + return true; // for now, we only cycle through the current workspace + return false; // else don't skip +} + +}; // end anonymous namespace + FocusControl::FocusControl(BScreen &screen): m_screen(screen), m_focus_model(screen.resourceManager(), @@ -64,43 +81,30 @@ FocusControl::FocusControl(BScreen &screen): } -// true if the windows should be skiped else false -bool doSkipWindow(const Focusable &winclient, int opts) { - const FluxboxWindow *win = winclient.fbwindow(); - return (!win || - // skip if stuck - (opts & FocusControl::CYCLESKIPSTUCK) != 0 && win->isStuck() || - // skip if shaded - (opts & FocusControl::CYCLESKIPSHADED) != 0 && win->isShaded() || - // skip if iconic - (opts & FocusControl::CYCLESKIPICONIC) != 0 && win->isIconic() || - // skip if hidden - win->isFocusHidden() - ); -} - -void FocusControl::cycleFocus(FocusedWindows *window_list, int opts, bool cycle_reverse) { +void FocusControl::cycleFocus(FocusedWindows &window_list, + const ClientPattern *pat, bool cycle_reverse) { Focusables tmp_list; - FocusedWindows::iterator it = window_list->begin(); - FocusedWindows::iterator it_end = window_list->end(); + FocusedWindows::iterator it = window_list.begin(); + FocusedWindows::iterator it_end = window_list.end(); for (; it != it_end; ++it) tmp_list.push_back(*it); - cycleFocus(&tmp_list, opts, cycle_reverse); + cycleFocus(tmp_list, pat, cycle_reverse); } -void FocusControl::cycleFocus(Focusables *window_list, int opts, bool cycle_reverse) { +void FocusControl::cycleFocus(Focusables &window_list, const ClientPattern *pat, + bool cycle_reverse) { if (!m_cycling_list) { if (&m_screen == FbTk::EventManager::instance()->grabbingKeyboard()) // only set this when we're waiting for modifiers - m_cycling_list = window_list; + m_cycling_list = &window_list; m_was_iconic = 0; m_cycling_last = 0; - } else if (m_cycling_list != window_list) - m_cycling_list = window_list; + } else if (m_cycling_list != &window_list) + m_cycling_list = &window_list; - Focusables::iterator it_begin = window_list->begin(); - Focusables::iterator it_end = window_list->end(); + Focusables::iterator it_begin = window_list.begin(); + Focusables::iterator it_end = window_list.end(); // too many things can go wrong with remembering this m_cycling_window = find(it_begin, it_end, s_focused_window); @@ -108,6 +112,8 @@ void FocusControl::cycleFocus(Focusables *window_list, int opts, bool cycle_reve m_cycling_window = find(it_begin, it_end, s_focused_fbwindow); Focusables::iterator it = m_cycling_window; + FluxboxWindow *fbwin = 0; + WinClient *last_client = 0; // find the next window in the list that works while (true) { @@ -123,50 +129,54 @@ void FocusControl::cycleFocus(Focusables *window_list, int opts, bool cycle_reve if (it == it_end) continue; - FluxboxWindow *fbwin = (*it)->fbwindow(); - if (fbwin && (fbwin->isStuck() - || fbwin->workspaceNumber() == m_screen.currentWorkspaceID())) { - // either on this workspace, or stuck - - // keep track of the originally selected window in a set - WinClient &last_client = fbwin->winClient(); - - if (! (doSkipWindow(**it, opts) || !(*it)->focus()) ) { - // moved onto a new fbwin - if (!m_cycling_last || m_cycling_last->fbwindow() != fbwin) { - if (m_cycling_last) { - // already cycling, so restack to put windows back in - // their proper order - m_screen.layerManager().restack(); - - // set back to orig current Client in that fbwin - m_cycling_last->fbwindow()->setCurrentClient(*m_cycling_last, false); - if (m_was_iconic == m_cycling_last) { - s_reverting = true; // little hack - m_cycling_last->fbwindow()->iconify(); - s_reverting = false; - } - } - m_cycling_last = &last_client; - if (fbwin->isIconic()) - m_was_iconic = m_cycling_last; - if (m_cycling_list) - // else window will raise itself (if desired) on FocusIn - fbwin->tempRaise(); - } - break; - } - } + fbwin = (*it)->fbwindow(); + if (!fbwin) + continue; + + // keep track of the originally selected window in a group + last_client = &fbwin->winClient(); + + // now we actually try to focus the window + if (!doSkipWindow(**it, pat) && (*it)->focus()) + break; } + m_cycling_window = it; + + // if we're still in the same fbwin, there's nothing else to do + if (m_cycling_last && m_cycling_last->fbwindow() == fbwin) + return; + + // if we were already cycling, then restore the old state + if (m_cycling_last) { + m_screen.layerManager().restack(); + + // set back to originally selected window in that group + m_cycling_last->fbwindow()->setCurrentClient(*m_cycling_last, false); + + if (m_was_iconic == m_cycling_last) { + s_reverting = true; // little hack + m_cycling_last->fbwindow()->iconify(); + s_reverting = false; + } + } + + m_cycling_last = last_client; + if (fbwin->isIconic()) + m_was_iconic = m_cycling_last; + if (m_cycling_list) + // else window will raise itself (if desired) on FocusIn + fbwin->tempRaise(); + } -void FocusControl::goToWindowNumber(Focusables *winlist, int num, int options) { - if (num > 0 && winlist) { - Focusables::iterator it = winlist->begin(); - Focusables::iterator it_end = winlist->end(); +void FocusControl::goToWindowNumber(Focusables &winlist, int num, + const ClientPattern *pat) { + if (num > 0) { + Focusables::iterator it = winlist.begin(); + Focusables::iterator it_end = winlist.end(); for (; it != it_end; ++it) { - if (!doSkipWindow(**it, options) && (*it)->acceptsFocus()) { + if (!doSkipWindow(**it, pat) && (*it)->acceptsFocus()) { --num; if (!num) { if ((*it)->fbwindow() && (*it)->fbwindow()->isIconic()) @@ -176,11 +186,11 @@ void FocusControl::goToWindowNumber(Focusables *winlist, int num, int options) { } } } - } else if (num < 0 && winlist) { - Focusables::reverse_iterator it = winlist->rbegin(); - Focusables::reverse_iterator it_end = winlist->rend(); + } else if (num < 0) { + Focusables::reverse_iterator it = winlist.rbegin(); + Focusables::reverse_iterator it_end = winlist.rend(); for (; it != it_end; ++it) { - if (!doSkipWindow(**it, options) && (*it)->acceptsFocus()) { + if (!doSkipWindow(**it, pat) && (*it)->acceptsFocus()) { ++num; if (!num) { if ((*it)->fbwindow() && (*it)->fbwindow()->isIconic()) diff --git a/src/FocusControl.hh b/src/FocusControl.hh index f7c40d9..9ab19e2 100644 --- a/src/FocusControl.hh +++ b/src/FocusControl.hh @@ -28,6 +28,7 @@ #include "FbTk/Resource.hh" +class ClientPattern; class WinClient; class FluxboxWindow; class Focusable; @@ -61,20 +62,19 @@ public: // prevFocus/nextFocus option bits enum { CYCLEGROUPS = 0x01, - CYCLESKIPSTUCK = 0x02, - CYCLESKIPSHADED = 0x04, CYCLELINEAR = 0x08, - CYCLESKIPICONIC = 0x10, - CYCLEDEFAULT = 0x00 }; explicit FocusControl(BScreen &screen); - void prevFocus() { cycleFocus(&m_focused_list, 0, true); } - void nextFocus() { cycleFocus(&m_focused_list, 0, false); } - void cycleFocus(Focusables *winlist, int options, bool reverse = false); - void cycleFocus(FocusedWindows *winlist, int options, bool reverse = false); - void goToWindowNumber(Focusables *winlist, int num, int options); + void prevFocus() { cycleFocus(m_focused_list, 0, true); } + void nextFocus() { cycleFocus(m_focused_list, 0, false); } + void cycleFocus(Focusables &winlist, const ClientPattern *pat = 0, + bool reverse = false); + void cycleFocus(FocusedWindows &winlist, const ClientPattern *pat = 0, + bool reverse = false); + void goToWindowNumber(Focusables &winlist, int num, + const ClientPattern *pat = 0); void setScreenFocusedWindow(WinClient &win_client); void setFocusModel(FocusModel model); diff --git a/src/Focusable.hh b/src/Focusable.hh index d7c1ae8..852e1c8 100644 --- a/src/Focusable.hh +++ b/src/Focusable.hh @@ -38,6 +38,7 @@ class Focusable: public FbTk::ITypeAheadable { public: Focusable(BScreen &scr, FluxboxWindow *fbwin = 0): m_screen(scr), m_fbwin(fbwin), + m_instance_name("fluxbox"), m_class_name("fluxbox"), m_focused(false), m_titlesig(*this) { } virtual ~Focusable() { } @@ -53,6 +54,11 @@ public: inline const FluxboxWindow *fbwindow() const { return m_fbwin; } inline FluxboxWindow *fbwindow() { return m_fbwin; } + // for pattern matching + virtual const std::string &getWMClassClass() const { return m_class_name; } + virtual const std::string &getWMClassName() const { return m_instance_name; } + virtual std::string getWMRole() const { return "Focusable"; } + // so we can make nice buttons, menu entries, etc. virtual const FbTk::PixmapWithMask &icon() const { return m_icon; } virtual const std::string &title() const { return m_title; } @@ -75,8 +81,8 @@ protected: BScreen &m_screen; FluxboxWindow *m_fbwin; + std::string m_title, m_instance_name, m_class_name; bool m_focused; - std::string m_title; FbTk::PixmapWithMask m_icon; FocusSubject m_titlesig; diff --git a/src/Layer.hh b/src/Layer.hh index 2b15539..e86c090 100644 --- a/src/Layer.hh +++ b/src/Layer.hh @@ -22,6 +22,9 @@ #ifndef LAYER_HH #define LAYER_HH +#include +using std::string; + /** * (This is not the layer->raise/lower handling stuff, @see FbTk::Layer) * Class to store layer numbers (special Resource type) @@ -42,7 +45,53 @@ public: }; explicit Layer(int i) : m_num(i) {}; + + static int getNumFromString(string &str) { + int tempnum = 0; + if (sscanf(str.c_str(), "%d", &tempnum) == 1) + return tempnum; + if (strcasecmp(str.c_str(), "Menu") == 0) + return ::Layer::MENU; + if (strcasecmp(str.c_str(), "AboveDock") == 0) + return ::Layer::ABOVE_DOCK; + if (strcasecmp(str.c_str(), "Dock") == 0) + return ::Layer::DOCK; + if (strcasecmp(str.c_str(), "Top") == 0) + return ::Layer::TOP; + if (strcasecmp(str.c_str(), "Normal") == 0) + return ::Layer::NORMAL; + if (strcasecmp(str.c_str(), "Bottom") == 0) + return ::Layer::BOTTOM; + if (strcasecmp(str.c_str(), "Desktop") == 0) + return ::Layer::DESKTOP; + return -1; + } + + static string getString(int num) { + switch (num) { + case ::Layer::MENU: + return string("Menu"); + case ::Layer::ABOVE_DOCK: + return string("AboveDock"); + case ::Layer::DOCK: + return string("Dock"); + case ::Layer::TOP: + return string("Top"); + case ::Layer::NORMAL: + return string("Normal"); + case ::Layer::BOTTOM: + return string("Bottom"); + case ::Layer::DESKTOP: + return string("Desktop"); + default: + char tmpstr[128]; + sprintf(tmpstr, "%d", num); + return string(tmpstr); + } + } + int getNum() const { return m_num; } + string getString() const { return getString(m_num); } Layer &operator=(int num) { m_num = num; return *this; } diff --git a/src/Resources.cc b/src/Resources.cc index 9603523..92833b5 100644 --- a/src/Resources.cc +++ b/src/Resources.cc @@ -197,23 +197,10 @@ getString() const { template<> void FbTk::Resource:: setFromString(const char *strval) { - int tempnum = 0; - if (sscanf(strval, "%d", &tempnum) == 1) + string str(strval); + int tempnum = ::Layer::getNumFromString(str); + if (tempnum >= 0 && tempnum < ::Layer::NUM_LAYERS) m_value = tempnum; - else if (strcasecmp(strval, "Menu") == 0) - m_value = ::Layer::MENU; - else if (strcasecmp(strval, "AboveDock") == 0) - m_value = ::Layer::ABOVE_DOCK; - else if (strcasecmp(strval, "Dock") == 0) - m_value = ::Layer::DOCK; - else if (strcasecmp(strval, "Top") == 0) - m_value = ::Layer::TOP; - else if (strcasecmp(strval, "Normal") == 0) - m_value = ::Layer::NORMAL; - else if (strcasecmp(strval, "Bottom") == 0) - m_value = ::Layer::BOTTOM; - else if (strcasecmp(strval, "Desktop") == 0) - m_value = ::Layer::DESKTOP; else setDefaultValue(); } @@ -222,26 +209,7 @@ setFromString(const char *strval) { template<> string FbTk::Resource:: getString() const { - switch (m_value.getNum()) { - case Layer::MENU: - return string("Menu"); - case Layer::ABOVE_DOCK: - return string("AboveDock"); - case Layer::DOCK: - return string("Dock"); - case Layer::TOP: - return string("Top"); - case Layer::NORMAL: - return string("Normal"); - case Layer::BOTTOM: - return string("Bottom"); - case Layer::DESKTOP: - return string("Desktop"); - default: - char tmpstr[128]; - sprintf(tmpstr, "%d", m_value.getNum()); - return string(tmpstr); - } + return ::Layer::getString(m_value.getNum()); } template<> diff --git a/src/Screen.cc b/src/Screen.cc index 071d253..0cab124 100644 --- a/src/Screen.cc +++ b/src/Screen.cc @@ -810,7 +810,7 @@ void BScreen::keyPressEvent(XKeyEvent &ke) { case XK_Tab: case XK_ISO_Left_Tab: m_type_ahead.seek(); - focusControl().cycleFocus(&m_matches, m_cycle_opts, (bool)(ke.state & ShiftMask)); + focusControl().cycleFocus(m_matches, m_cycle_opts, (bool)(ke.state & ShiftMask)); break; default: m_matches = m_type_ahead.putCharacter(keychar[0]); @@ -818,7 +818,7 @@ void BScreen::keyPressEvent(XKeyEvent &ke) { if (!m_matches.empty() && std::find(m_matches.begin(), m_matches.end(), FocusControl::focusedWindow()) == m_matches.end()) - focusControl().cycleFocus(&m_matches, m_cycle_opts); + focusControl().cycleFocus(m_matches, m_cycle_opts); break; } } @@ -849,17 +849,18 @@ void BScreen::notifyUngrabKeyboard() { focusControl().stopCyclingFocus(); } -void BScreen::startTypeAheadFocus(std::list &winlist, int opts) { +void BScreen::startTypeAheadFocus(std::list &winlist, + const ClientPattern *pat) { m_type_ahead.init(winlist); m_matches = winlist; FbTk::EventManager *evm = FbTk::EventManager::instance(); if (!m_typing_ahead && !m_cycling) evm->grabKeyboard(*this, rootWindow().window()); - m_cycle_opts = opts; + m_cycle_opts = pat; m_typing_ahead = true; } -void BScreen::cycleFocus(int options, bool reverse) { +void BScreen::cycleFocus(int options, const ClientPattern *pat, bool reverse) { // get modifiers from event that causes this for focus order cycling XEvent ev = Fluxbox::instance()->lastEvent(); unsigned int mods = 0; @@ -887,7 +888,7 @@ void BScreen::cycleFocus(int options, bool reverse) { &focusControl().focusedOrderList(); } - focusControl().cycleFocus(win_list, options, reverse); + focusControl().cycleFocus(*win_list, pat, reverse); } @@ -2291,7 +2292,7 @@ int BScreen::getHead(int x, int y) const { return 0; } -int BScreen::getHead(FbTk::FbWindow &win) const { +int BScreen::getHead(const FbTk::FbWindow &win) const { if (hasXinerama()) return getHead(win.x() + win.width()/2, win.y() + win.height()/2); else diff --git a/src/Screen.hh b/src/Screen.hh index b1b5a7a..066ec74 100644 --- a/src/Screen.hh +++ b/src/Screen.hh @@ -55,6 +55,7 @@ #include #include +class ClientPattern; class Focusable; class FluxboxWindow; class Netizen; @@ -157,6 +158,9 @@ public: inline const Slit *slit() const { return m_slit.get(); } inline Workspace *getWorkspace(unsigned int w) { return ( w < m_workspaces_list.size() ? m_workspaces_list[w] : 0); } + inline const Workspace *getWorkspace(unsigned int w) const { + return (w < m_workspaces_list.size() ? m_workspaces_list[w] : 0); + } inline Workspace *currentWorkspace() { return m_current_workspace; } inline const Workspace *currentWorkspace() const { return m_current_workspace; } @@ -220,8 +224,9 @@ public: void buttonPressEvent(XButtonEvent &be); void notifyUngrabKeyboard(); - void startTypeAheadFocus(std::list &winlist, int opts); - void cycleFocus(int opts, bool reverse); + void startTypeAheadFocus(std::list &winlist, + const ClientPattern *pat = 0); + void cycleFocus(int opts = 0, const ClientPattern *pat = 0, bool reverse = false); FbTk::Menu *createMenu(const std::string &label); FbTk::Menu *createToggleMenu(const std::string &label); @@ -323,7 +328,7 @@ public: void initXinerama(); int getHead(int x, int y) const; - int getHead(FbTk::FbWindow &win) const; + int getHead(const FbTk::FbWindow &win) const; int getCurrHead() const; int getHeadX(int head) const; int getHeadY(int head) const; @@ -336,7 +341,7 @@ public: // magic to allow us to have "on head" placement (menu) without // the object really knowing about it. template - int getOnHead(OnHeadObject &obj); + int getOnHead(OnHeadObject &obj) const; template void setOnHead(OnHeadObject &obj, int head); @@ -493,7 +498,7 @@ private: Groupables m_expecting_groups; bool m_cycling, m_typing_ahead; - int m_cycle_opts; + const ClientPattern *m_cycle_opts; FbTk::TypeAhead, Focusable *> m_type_ahead; std::list m_matches; diff --git a/src/WinClient.cc b/src/WinClient.cc index 7e79dea..948a363 100644 --- a/src/WinClient.cc +++ b/src/WinClient.cc @@ -75,7 +75,6 @@ WinClient::WinClient(Window win, BScreen &screen, FluxboxWindow *fbwin): send_focus_message(false), send_close_message(false), m_win_gravity(0), - m_class_name(""), m_instance_name(""), m_title_override(false), m_icon_title_override(false), m_blackbox_hint(0), @@ -223,15 +222,13 @@ bool WinClient::getWMName(XTextProperty &textprop) const { } bool WinClient::getWMIconName(XTextProperty &textprop) const { - return XGetWMName(display(), window(), &textprop); -} - -const string &WinClient::getWMClassName() const { - return m_instance_name; + return XGetWMIconName(display(), window(), &textprop); } -const string &WinClient::getWMClassClass() const { - return m_class_name; +string WinClient::getWMRole() const { + Atom wm_role = XInternAtom(FbTk::App::instance()->display(), + "WM_WINDOW_ROLE", False); + return textProperty(wm_role); } const string &WinClient::title() const { @@ -246,6 +243,7 @@ void WinClient::updateWMClassHint() { #ifdef DEBUG cerr<<"WinClient: Failed to read class hint!"<title(); + return (m_client ? m_client->title() : m_title); +} + +const std::string &FluxboxWindow::getWMClassName() const { + return (m_client ? m_client->getWMClassName() : m_instance_name); +} + +const std::string &FluxboxWindow::getWMClassClass() const { + return (m_client ? m_client->getWMClassClass() : m_class_name); +} + +std::string FluxboxWindow::getWMRole() const { + return (m_client ? m_client->getWMRole() : "FluxboxWindow"); } int FluxboxWindow::initialState() const { return m_client->initial_state; } diff --git a/src/Window.hh b/src/Window.hh index 29f2274..090b4e8 100644 --- a/src/Window.hh +++ b/src/Window.hh @@ -378,6 +378,9 @@ public: bool acceptsFocus() const; const FbTk::PixmapWithMask &icon() const; const std::string &title() const; + const std::string &getWMClassName() const; + const std::string &getWMClassClass() const; + std::string getWMRole() const; inline int x() const { return frame().x(); } inline int y() const { return frame().y(); } diff --git a/src/WorkspaceCmd.cc b/src/WorkspaceCmd.cc index 93ea830..2d0f501 100644 --- a/src/WorkspaceCmd.cc +++ b/src/WorkspaceCmd.cc @@ -44,13 +44,13 @@ void NextWindowCmd::execute() { BScreen *screen = Fluxbox::instance()->keyScreen(); if (screen != 0) - screen->cycleFocus(m_option, false); + screen->cycleFocus(m_option, &m_pat, false); } void PrevWindowCmd::execute() { BScreen *screen = Fluxbox::instance()->keyScreen(); if (screen != 0) - screen->cycleFocus(m_option, true); + screen->cycleFocus(m_option, &m_pat, true); } void TypeAheadFocusCmd::execute() { @@ -67,7 +67,7 @@ void TypeAheadFocusCmd::execute() { &screen->focusControl().focusedOrderList(); } - screen->startTypeAheadFocus(*win_list, m_option); + screen->startTypeAheadFocus(*win_list, &m_pat); } } @@ -84,7 +84,7 @@ void GoToWindowCmd::execute() { &screen->focusControl().creationOrderList() : &screen->focusControl().focusedOrderList(); } - screen->focusControl().goToWindowNumber(win_list, m_num, m_option); + screen->focusControl().goToWindowNumber(*win_list, m_num, &m_pat); } } diff --git a/src/WorkspaceCmd.hh b/src/WorkspaceCmd.hh index 31ffb04..0e24a17 100644 --- a/src/WorkspaceCmd.hh +++ b/src/WorkspaceCmd.hh @@ -26,40 +26,49 @@ #define WORKSPACECMD_HH #include "Command.hh" +#include "ClientPattern.hh" #include "FocusControl.hh" class NextWindowCmd: public FbTk::Command { public: - explicit NextWindowCmd(int option):m_option(option) { } + explicit NextWindowCmd(int option, std::string &pat): + m_option(option), m_pat(pat.c_str()) { } void execute(); private: const int m_option; + const ClientPattern m_pat; }; class PrevWindowCmd: public FbTk::Command { public: - explicit PrevWindowCmd(int option):m_option(option) { } + explicit PrevWindowCmd(int option, std::string &pat): + m_option(option), m_pat(pat.c_str()) { } void execute(); private: const int m_option; + const ClientPattern m_pat; }; class TypeAheadFocusCmd: public FbTk::Command { public: - explicit TypeAheadFocusCmd(int option): m_option(option) { } + explicit TypeAheadFocusCmd(int option, std::string &pat): + m_option(option), m_pat(pat.c_str()) { } void execute(); private: const int m_option; + const ClientPattern m_pat; }; class GoToWindowCmd: public FbTk::Command { public: - GoToWindowCmd(int num, int option): m_num(num), m_option(option) { } + GoToWindowCmd(int num, int option, std::string &pat): + m_num(num), m_option(option), m_pat(pat.c_str()) { } void execute(); private: const int m_num; const int m_option; + const ClientPattern m_pat; }; class DirFocusCmd: public FbTk::Command { diff --git a/util/fluxbox-update_configs.cc b/util/fluxbox-update_configs.cc index 5aa0d6c..c3b5a85 100644 --- a/util/fluxbox-update_configs.cc +++ b/util/fluxbox-update_configs.cc @@ -67,10 +67,15 @@ void save_all_files(); int run_updates(int old_version, FbTk::ResourceManager rm) { int new_version = old_version; + FbTk::Resource rc_keyfile(rm, "~/.fluxbox/keys", + "session.keyFile", "Session.KeyFile"); + FbTk::Resource rc_appsfile(rm, "~/.fluxbox/apps", + "session.appsFile", "Session.AppsFile"); + + string appsfilename = FbTk::StringUtil::expandFilename(*rc_appsfile); + string keyfilename = FbTk::StringUtil::expandFilename(*rc_keyfile); + if (old_version < 1) { // add mouse events to keys file - FbTk::Resource rc_keyfile(rm, "~/.fluxbox/keys", - "session.keyFile", "Session.KeyFile"); - string keyfilename = FbTk::StringUtil::expandFilename(*rc_keyfile); string whole_keyfile = read_file(keyfilename); string new_keyfile = ""; @@ -111,12 +116,7 @@ int run_updates(int old_version, FbTk::ResourceManager rm) { "session.groupFile", "Session.GroupFile"); string groupfilename = FbTk::StringUtil::expandFilename(*rc_groupfile); string whole_groupfile = read_file(groupfilename); - - FbTk::Resource rc_appsfile(rm, "~/.fluxbox/apps", - "session.appsFile", "Session.AppsFile"); - string appsfilename = FbTk::StringUtil::expandFilename(*rc_appsfile); string whole_appsfile = read_file(appsfilename); - string new_appsfile = ""; list lines; @@ -125,7 +125,7 @@ int run_updates(int old_version, FbTk::ResourceManager rm) { list::iterator line_it = lines.begin(); list::iterator line_it_end = lines.end(); for (; line_it != line_it_end; ++line_it) { - new_appsfile += "[group] (workspace)\n"; + new_appsfile += "[group] (workspace=[current])\n"; list apps; FbTk::StringUtil::stringtok(apps, *line_it); -- cgit v0.11.2