From 174e62ff66fee78d83231b1f0f9ea083370ae55d Mon Sep 17 00:00:00 2001 From: Richard Tamin Date: Sun, 23 Aug 2020 00:01:43 -0400 Subject: Initial implementation of shortcut to windows [PURPOSE] In editors such as vi and emacs, a user can mark a line in a file with a shortcut key and afterwards jump back to that line using the shortcut. The idea is extended to opened windows. A user can assign a keyboard shortcut to an opened window. Afterwards, the shortcut can be used to switch focus back to the marked window. Such shortcuts save the user from pressing "alt+tab" multiple times to cycle through windows until the desired one is found. [EXAMPLE USAGE] The following binding is added to file "~/.fluxbox/keys": Mod1 m ARG :MarkWindow Mod1 g ARG :GotoMarkedWindow User enters "alt+m x" to mark the currently focused window with shortcut key 'x' User enters "alt+g x" to switch focus to the marked window [IMPLEMENTATION SUMMARY] - Two new commands were added :MarkWindow and :GotoMarkedWindow - Keys.cc was modified: - addBinding() method supports parsing an argument placeholder where the user can pass in a shortcut key - doAction() method forwards the shortcut key to the command to execute - Class Keys::t_key was modified to recognize a placeholder key - New class ShortcutManager was added to maintain mapping of shortcut keys to marked windows --- src/Keys.cc | 53 +++++++++++++++++++++++++++++++++++++++++++------- src/Makemodule.am | 2 ++ src/ShortcutManager.cc | 45 ++++++++++++++++++++++++++++++++++++++++++ src/ShortcutManager.hh | 32 ++++++++++++++++++++++++++++++ src/Window.cc | 1 + src/WorkspaceCmd.cc | 37 +++++++++++++++++++++++++++++++++++ src/WorkspaceCmd.hh | 10 ++++++++++ src/fluxbox.cc | 3 ++- src/fluxbox.hh | 3 +++ 9 files changed, 178 insertions(+), 8 deletions(-) create mode 100644 src/ShortcutManager.cc create mode 100644 src/ShortcutManager.hh diff --git a/src/Keys.cc b/src/Keys.cc index 774cd05..8659137 100644 --- a/src/Keys.cc +++ b/src/Keys.cc @@ -140,21 +140,35 @@ public: // constructor / destructor t_key(int type = 0, unsigned int mod = 0, unsigned int key = 0, const std::string &key_str = std::string(), int context = 0, - bool isdouble = false); + bool isdouble = false, bool isPlaceHolderArg = false); RefKey find(int type_, unsigned int mod_, unsigned int key_, int context_, bool isdouble_) { // t_key ctor sets context_ of 0 to GLOBAL, so we must here too context_ = context_ ? context_ : GLOBAL; + keylist_t::iterator itPlaceHolder = keylist.end(); keylist_t::iterator it = keylist.begin(), it_end = keylist.end(); for (; it != it_end; ++it) { + + if ((*it)->isPlaceHolderArg) + itPlaceHolder = it; + if (*it && (*it)->type == type_ && (*it)->key == key_ && ((*it)->context & context_) > 0 && isdouble_ == (*it)->isdouble && (*it)->mod == FbTk::KeyUtil::instance().isolateModifierMask(mod_)) return *it; } - return RefKey(); + + // Could not find any matching key. If a placeholder was located then user + // is trying to pass in a value for the placeholder. + if (itPlaceHolder == keylist.end()) { + return RefKey(); + } + else { + (*itPlaceHolder)->lastPlaceHolderArgValue = key_; + return *itPlaceHolder; + } } // member variables @@ -165,6 +179,8 @@ public: std::string key_str; // key-symbol, needed for regrab() int context; // ON_TITLEBAR, etc.: bitwise-or of all desired contexts bool isdouble; + bool isPlaceHolderArg; + unsigned int lastPlaceHolderArgValue; FbTk::RefCount > m_command; keylist_t keylist; @@ -172,13 +188,15 @@ public: Keys::t_key::t_key(int type_, unsigned int mod_, unsigned int key_, const std::string &key_str_, - int context_, bool isdouble_) : + int context_, bool isdouble_, bool isPlaceHolderArg_) : type(type_), mod(mod_), key(key_), key_str(key_str_), context(context_), isdouble(isdouble_), + isPlaceHolderArg(isPlaceHolderArg_), + lastPlaceHolderArgValue(0), m_command(0) { context = context_ ? context_ : GLOBAL; @@ -385,6 +403,8 @@ bool Keys::addBinding(const string &linebuffer) { std::string arg = FbTk::StringUtil::toLower(val[argc]); + bool isPlaceHolderArg = false; + if (arg[0] != ':') { // parse key(s) std::string key_str; @@ -461,7 +481,11 @@ bool Keys::addBinding(const string &linebuffer) { type = ButtonRelease; } else if (extractKeyFromString(arg, "move", key)) { type = MotionNotify; - + } else if (arg == "arg") { + isPlaceHolderArg = true; + key = 0; + mod = 0; + type = 0; } else if ((key = FbTk::KeyUtil::getKey(val[argc].c_str()))) { // convert from string symbol type = KeyPress; key_str = val[argc]; @@ -476,25 +500,31 @@ bool Keys::addBinding(const string &linebuffer) { type = KeyPress; } - if (key == 0 && (type == KeyPress || type == ButtonPress || type == ButtonRelease)) + if (key == 0 && (type == KeyPress || type == ButtonPress || type == ButtonRelease) && !isPlaceHolderArg) return false; if (type != ButtonPress) isdouble = false; + // Placeholder argument cannot be the first key + if (!first_new_key && isPlaceHolderArg) { + return false; + } + if (!first_new_key) { first_new_keylist = current_key; current_key = current_key->find(type, mod, key, context, isdouble); if (!current_key) { first_new_key.reset( new t_key(type, mod, key, key_str, context, - isdouble) ); + isdouble, isPlaceHolderArg) ); current_key = first_new_key; } else if (current_key->m_command) // already being used return false; } else { + RefKey temp_key( new t_key(type, mod, key, key_str, context, - isdouble) ); + isdouble, isPlaceHolderArg) ); current_key->keylist.push_back(temp_key); current_key = temp_key; } @@ -608,6 +638,15 @@ bool Keys::doAction(int type, unsigned int mods, unsigned int key, WinClient *old = WindowCmd::client(); WindowCmd::setClient(current); + + // The key is a placeholder, store the value of the entered key in the shortcut manager + // before executing the action + if (temp_key->isPlaceHolderArg) { + fbdbg << "Encountered placeholder key. Assign value[" << temp_key->lastPlaceHolderArgValue + << "] to the placeholder" << std::endl; + Fluxbox::instance()->shortcutManager().setLastPlaceHolderKey(temp_key->lastPlaceHolderArgValue); + } + temp_key->m_command->execute(); WindowCmd::setClient(old); diff --git a/src/Makemodule.am b/src/Makemodule.am index 0156c90..5be5692 100644 --- a/src/Makemodule.am +++ b/src/Makemodule.am @@ -233,6 +233,8 @@ fluxbox_SOURCES = \ src/ScreenResource.hh \ src/SendToMenu.cc \ src/SendToMenu.hh \ + src/ShortcutManager.cc \ + src/ShortcutManager.hh \ src/Strut.hh \ src/StyleMenuItem.cc \ src/StyleMenuItem.hh \ diff --git a/src/ShortcutManager.cc b/src/ShortcutManager.cc new file mode 100644 index 0000000..52e94af --- /dev/null +++ b/src/ShortcutManager.cc @@ -0,0 +1,45 @@ +#include "ShortcutManager.hh" +#include "Debug.hh" + +#include + +ShortcutManager::ShortcutManager() : m_last_placeholder_key(0) { } + +void ShortcutManager::setLastPlaceHolderKey(unsigned int lastPlaceHolderKey_) +{ + m_last_placeholder_key = lastPlaceHolderKey_; +} + +unsigned int ShortcutManager::getLastPlaceHolderKey() +{ + return m_last_placeholder_key; +} + +void ShortcutManager::mapKeyToWindow(unsigned int key, FluxboxWindow* window) +{ + m_key_to_window_map.insert(std::make_pair(key, window)); +} + +void ShortcutManager::removeWindow(FluxboxWindow* window) +{ + KeyToWindowMap::const_iterator it; + for (it = m_key_to_window_map.begin(); it != m_key_to_window_map.end(); ++it) { + if (it->second == window){ + fbdbg << "Remove mapping window[" << window + << "] key [" << it->first << "]" << std::endl; + m_key_to_window_map.erase(it); + return; + } + } +} + +FluxboxWindow* ShortcutManager::getWindowForKey(unsigned int key) +{ + KeyToWindowMap::const_iterator it = m_key_to_window_map.find(key); + if (it != m_key_to_window_map.end()) { + return it->second; + } + else { + return nullptr; + } +} diff --git a/src/ShortcutManager.hh b/src/ShortcutManager.hh new file mode 100644 index 0000000..792a66c --- /dev/null +++ b/src/ShortcutManager.hh @@ -0,0 +1,32 @@ +#ifndef SHORTCUTMANAGER_HH +#define SHORTCUTMANAGER_HH + +#include + +class FluxboxWindow; + +class ShortcutManager { + +public: + + ShortcutManager(); + + void setLastPlaceHolderKey(unsigned int lastPlaceHolderKey_); + + unsigned int getLastPlaceHolderKey(); + + void mapKeyToWindow(unsigned int key, FluxboxWindow* window); + + void removeWindow(FluxboxWindow* window); + + FluxboxWindow* getWindowForKey(unsigned int key); + +private: + + typedef std::map KeyToWindowMap; + + unsigned int m_last_placeholder_key; + KeyToWindowMap m_key_to_window_map; +}; + +#endif diff --git a/src/Window.cc b/src/Window.cc index d761016..342e488 100644 --- a/src/Window.cc +++ b/src/Window.cc @@ -353,6 +353,7 @@ FluxboxWindow::~FluxboxWindow() { // no longer a valid window to do stuff with Fluxbox::instance()->removeWindowSearchGroup(frame().window().window()); Fluxbox::instance()->removeWindowSearchGroup(frame().tabcontainer().window()); + Fluxbox::instance()->shortcutManager().removeWindow(this); Client2ButtonMap::iterator it = m_labelbuttons.begin(); Client2ButtonMap::iterator it_end = m_labelbuttons.end(); diff --git a/src/WorkspaceCmd.cc b/src/WorkspaceCmd.cc index 6d18b73..c2e4948 100644 --- a/src/WorkspaceCmd.cc +++ b/src/WorkspaceCmd.cc @@ -39,6 +39,8 @@ #include "FbTk/stringstream.hh" #include "FbTk/StringUtil.hh" +#include "Debug.hh" + #ifdef HAVE_CMATH #include #else @@ -751,3 +753,38 @@ FbTk::Command *RelabelButtonCmd::parse(const std::string &command, REGISTER_COMMAND_PARSER(relabelbutton, RelabelButtonCmd::parse, void); +void MarkWindowCmd::execute() { + BScreen *screen = Fluxbox::instance()->keyScreen(); + if (screen) { + + FluxboxWindow* window = screen->focusControl().focusedFbWindow(); + if (window) { + ShortcutManager &shortcutManager = Fluxbox::instance()->shortcutManager(); + unsigned int key = shortcutManager.getLastPlaceHolderKey(); + shortcutManager.mapKeyToWindow(key, window); + fbdbg << "Map window[" << window << "] to key[" << key << "]" << std::endl; + } + } +} + +REGISTER_COMMAND(markwindow, MarkWindowCmd, void); + +void GotoMarkedWindowCmd::execute() { + + ShortcutManager &shortcutManager = Fluxbox::instance()->shortcutManager(); + unsigned int key = shortcutManager.getLastPlaceHolderKey(); + + FluxboxWindow *window = shortcutManager.getWindowForKey(key); + if (window) { + + if (window->isIconic()) { + window->deiconify(false); + } + window->raiseAndFocus(); + + fbdbg << "Raise and focus window[" << window + << "] mapped to key[" << key << "]" << std::endl; + } +} + +REGISTER_COMMAND(gotomarkedwindow, GotoMarkedWindowCmd, void); diff --git a/src/WorkspaceCmd.hh b/src/WorkspaceCmd.hh index 4f294fe..8993e60 100644 --- a/src/WorkspaceCmd.hh +++ b/src/WorkspaceCmd.hh @@ -236,4 +236,14 @@ private: std::string m_button, m_label; }; +class MarkWindowCmd: public FbTk::Command { +public: + void execute(); +}; + +class GotoMarkedWindowCmd: public FbTk::Command { +public: + void execute(); +}; + #endif // WORKSPACECMD_HH diff --git a/src/fluxbox.cc b/src/fluxbox.cc index 468ca33..a55bd29 100644 --- a/src/fluxbox.cc +++ b/src/fluxbox.cc @@ -272,7 +272,8 @@ Fluxbox::Fluxbox(int argc, char **argv, m_masked_window(0), m_argv(argv), m_argc(argc), m_showing_dialog(false), - m_server_grabs(0) { + m_server_grabs(0), + m_shortcut_manager(new ShortcutManager) { _FB_USES_NLS; diff --git a/src/fluxbox.hh b/src/fluxbox.hh index 42739fb..d300102 100644 --- a/src/fluxbox.hh +++ b/src/fluxbox.hh @@ -32,6 +32,7 @@ #include "FbTk/MenuSearch.hh" #include "AttentionNoticeHandler.hh" +#include "ShortcutManager.hh" #include @@ -185,6 +186,7 @@ public: const XEvent &lastEvent() const { return m_last_event; } AttentionNoticeHandler &attentionHandler() { return m_attention_handler; } + ShortcutManager &shortcutManager() { return *m_shortcut_manager; } private: std::string getRcFilename(); @@ -306,6 +308,7 @@ private: } m_state; int m_server_grabs; + std::unique_ptr m_shortcut_manager; }; -- cgit v0.11.2