diff options
author | Richard Tamin <richard.tamin@gmail.com> | 2020-08-23 04:01:43 (GMT) |
---|---|---|
committer | Mathias Gumz <mgumz@users.noreply.github.com> | 2022-04-18 19:50:06 (GMT) |
commit | 174e62ff66fee78d83231b1f0f9ea083370ae55d (patch) | |
tree | 59168c7cc06e7a0d0ef0a3d55976db2fb733092c | |
parent | 8e32f098bd94cb14ae80c3621c3db74fbdfa7bb6 (diff) | |
download | fluxbox-174e62ff66fee78d83231b1f0f9ea083370ae55d.zip fluxbox-174e62ff66fee78d83231b1f0f9ea083370ae55d.tar.bz2 |
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
-rw-r--r-- | src/Keys.cc | 53 | ||||
-rw-r--r-- | src/Makemodule.am | 2 | ||||
-rw-r--r-- | src/ShortcutManager.cc | 45 | ||||
-rw-r--r-- | src/ShortcutManager.hh | 32 | ||||
-rw-r--r-- | src/Window.cc | 1 | ||||
-rw-r--r-- | src/WorkspaceCmd.cc | 37 | ||||
-rw-r--r-- | src/WorkspaceCmd.hh | 10 | ||||
-rw-r--r-- | src/fluxbox.cc | 3 | ||||
-rw-r--r-- | src/fluxbox.hh | 3 |
9 files changed, 178 insertions, 8 deletions
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: | |||
140 | // constructor / destructor | 140 | // constructor / destructor |
141 | t_key(int type = 0, unsigned int mod = 0, unsigned int key = 0, | 141 | t_key(int type = 0, unsigned int mod = 0, unsigned int key = 0, |
142 | const std::string &key_str = std::string(), int context = 0, | 142 | const std::string &key_str = std::string(), int context = 0, |
143 | bool isdouble = false); | 143 | bool isdouble = false, bool isPlaceHolderArg = false); |
144 | 144 | ||
145 | RefKey find(int type_, unsigned int mod_, unsigned int key_, | 145 | RefKey find(int type_, unsigned int mod_, unsigned int key_, |
146 | int context_, bool isdouble_) { | 146 | int context_, bool isdouble_) { |
147 | // t_key ctor sets context_ of 0 to GLOBAL, so we must here too | 147 | // t_key ctor sets context_ of 0 to GLOBAL, so we must here too |
148 | context_ = context_ ? context_ : GLOBAL; | 148 | context_ = context_ ? context_ : GLOBAL; |
149 | keylist_t::iterator itPlaceHolder = keylist.end(); | ||
149 | keylist_t::iterator it = keylist.begin(), it_end = keylist.end(); | 150 | keylist_t::iterator it = keylist.begin(), it_end = keylist.end(); |
150 | for (; it != it_end; ++it) { | 151 | for (; it != it_end; ++it) { |
152 | |||
153 | if ((*it)->isPlaceHolderArg) | ||
154 | itPlaceHolder = it; | ||
155 | |||
151 | if (*it && (*it)->type == type_ && (*it)->key == key_ && | 156 | if (*it && (*it)->type == type_ && (*it)->key == key_ && |
152 | ((*it)->context & context_) > 0 && | 157 | ((*it)->context & context_) > 0 && |
153 | isdouble_ == (*it)->isdouble && (*it)->mod == | 158 | isdouble_ == (*it)->isdouble && (*it)->mod == |
154 | FbTk::KeyUtil::instance().isolateModifierMask(mod_)) | 159 | FbTk::KeyUtil::instance().isolateModifierMask(mod_)) |
155 | return *it; | 160 | return *it; |
156 | } | 161 | } |
157 | return RefKey(); | 162 | |
163 | // Could not find any matching key. If a placeholder was located then user | ||
164 | // is trying to pass in a value for the placeholder. | ||
165 | if (itPlaceHolder == keylist.end()) { | ||
166 | return RefKey(); | ||
167 | } | ||
168 | else { | ||
169 | (*itPlaceHolder)->lastPlaceHolderArgValue = key_; | ||
170 | return *itPlaceHolder; | ||
171 | } | ||
158 | } | 172 | } |
159 | 173 | ||
160 | // member variables | 174 | // member variables |
@@ -165,6 +179,8 @@ public: | |||
165 | std::string key_str; // key-symbol, needed for regrab() | 179 | std::string key_str; // key-symbol, needed for regrab() |
166 | int context; // ON_TITLEBAR, etc.: bitwise-or of all desired contexts | 180 | int context; // ON_TITLEBAR, etc.: bitwise-or of all desired contexts |
167 | bool isdouble; | 181 | bool isdouble; |
182 | bool isPlaceHolderArg; | ||
183 | unsigned int lastPlaceHolderArgValue; | ||
168 | FbTk::RefCount<FbTk::Command<void> > m_command; | 184 | FbTk::RefCount<FbTk::Command<void> > m_command; |
169 | 185 | ||
170 | keylist_t keylist; | 186 | keylist_t keylist; |
@@ -172,13 +188,15 @@ public: | |||
172 | 188 | ||
173 | Keys::t_key::t_key(int type_, unsigned int mod_, unsigned int key_, | 189 | Keys::t_key::t_key(int type_, unsigned int mod_, unsigned int key_, |
174 | const std::string &key_str_, | 190 | const std::string &key_str_, |
175 | int context_, bool isdouble_) : | 191 | int context_, bool isdouble_, bool isPlaceHolderArg_) : |
176 | type(type_), | 192 | type(type_), |
177 | mod(mod_), | 193 | mod(mod_), |
178 | key(key_), | 194 | key(key_), |
179 | key_str(key_str_), | 195 | key_str(key_str_), |
180 | context(context_), | 196 | context(context_), |
181 | isdouble(isdouble_), | 197 | isdouble(isdouble_), |
198 | isPlaceHolderArg(isPlaceHolderArg_), | ||
199 | lastPlaceHolderArgValue(0), | ||
182 | m_command(0) { | 200 | m_command(0) { |
183 | 201 | ||
184 | context = context_ ? context_ : GLOBAL; | 202 | context = context_ ? context_ : GLOBAL; |
@@ -385,6 +403,8 @@ bool Keys::addBinding(const string &linebuffer) { | |||
385 | 403 | ||
386 | std::string arg = FbTk::StringUtil::toLower(val[argc]); | 404 | std::string arg = FbTk::StringUtil::toLower(val[argc]); |
387 | 405 | ||
406 | bool isPlaceHolderArg = false; | ||
407 | |||
388 | if (arg[0] != ':') { // parse key(s) | 408 | if (arg[0] != ':') { // parse key(s) |
389 | 409 | ||
390 | std::string key_str; | 410 | std::string key_str; |
@@ -461,7 +481,11 @@ bool Keys::addBinding(const string &linebuffer) { | |||
461 | type = ButtonRelease; | 481 | type = ButtonRelease; |
462 | } else if (extractKeyFromString(arg, "move", key)) { | 482 | } else if (extractKeyFromString(arg, "move", key)) { |
463 | type = MotionNotify; | 483 | type = MotionNotify; |
464 | 484 | } else if (arg == "arg") { | |
485 | isPlaceHolderArg = true; | ||
486 | key = 0; | ||
487 | mod = 0; | ||
488 | type = 0; | ||
465 | } else if ((key = FbTk::KeyUtil::getKey(val[argc].c_str()))) { // convert from string symbol | 489 | } else if ((key = FbTk::KeyUtil::getKey(val[argc].c_str()))) { // convert from string symbol |
466 | type = KeyPress; | 490 | type = KeyPress; |
467 | key_str = val[argc]; | 491 | key_str = val[argc]; |
@@ -476,25 +500,31 @@ bool Keys::addBinding(const string &linebuffer) { | |||
476 | type = KeyPress; | 500 | type = KeyPress; |
477 | } | 501 | } |
478 | 502 | ||
479 | if (key == 0 && (type == KeyPress || type == ButtonPress || type == ButtonRelease)) | 503 | if (key == 0 && (type == KeyPress || type == ButtonPress || type == ButtonRelease) && !isPlaceHolderArg) |
480 | return false; | 504 | return false; |
481 | 505 | ||
482 | if (type != ButtonPress) | 506 | if (type != ButtonPress) |
483 | isdouble = false; | 507 | isdouble = false; |
484 | 508 | ||
509 | // Placeholder argument cannot be the first key | ||
510 | if (!first_new_key && isPlaceHolderArg) { | ||
511 | return false; | ||
512 | } | ||
513 | |||
485 | if (!first_new_key) { | 514 | if (!first_new_key) { |
486 | first_new_keylist = current_key; | 515 | first_new_keylist = current_key; |
487 | current_key = current_key->find(type, mod, key, context, | 516 | current_key = current_key->find(type, mod, key, context, |
488 | isdouble); | 517 | isdouble); |
489 | if (!current_key) { | 518 | if (!current_key) { |
490 | first_new_key.reset( new t_key(type, mod, key, key_str, context, | 519 | first_new_key.reset( new t_key(type, mod, key, key_str, context, |
491 | isdouble) ); | 520 | isdouble, isPlaceHolderArg) ); |
492 | current_key = first_new_key; | 521 | current_key = first_new_key; |
493 | } else if (current_key->m_command) // already being used | 522 | } else if (current_key->m_command) // already being used |
494 | return false; | 523 | return false; |
495 | } else { | 524 | } else { |
525 | |||
496 | RefKey temp_key( new t_key(type, mod, key, key_str, context, | 526 | RefKey temp_key( new t_key(type, mod, key, key_str, context, |
497 | isdouble) ); | 527 | isdouble, isPlaceHolderArg) ); |
498 | current_key->keylist.push_back(temp_key); | 528 | current_key->keylist.push_back(temp_key); |
499 | current_key = temp_key; | 529 | current_key = temp_key; |
500 | } | 530 | } |
@@ -608,6 +638,15 @@ bool Keys::doAction(int type, unsigned int mods, unsigned int key, | |||
608 | 638 | ||
609 | WinClient *old = WindowCmd<void>::client(); | 639 | WinClient *old = WindowCmd<void>::client(); |
610 | WindowCmd<void>::setClient(current); | 640 | WindowCmd<void>::setClient(current); |
641 | |||
642 | // The key is a placeholder, store the value of the entered key in the shortcut manager | ||
643 | // before executing the action | ||
644 | if (temp_key->isPlaceHolderArg) { | ||
645 | fbdbg << "Encountered placeholder key. Assign value[" << temp_key->lastPlaceHolderArgValue | ||
646 | << "] to the placeholder" << std::endl; | ||
647 | Fluxbox::instance()->shortcutManager().setLastPlaceHolderKey(temp_key->lastPlaceHolderArgValue); | ||
648 | } | ||
649 | |||
611 | temp_key->m_command->execute(); | 650 | temp_key->m_command->execute(); |
612 | WindowCmd<void>::setClient(old); | 651 | WindowCmd<void>::setClient(old); |
613 | 652 | ||
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 = \ | |||
233 | src/ScreenResource.hh \ | 233 | src/ScreenResource.hh \ |
234 | src/SendToMenu.cc \ | 234 | src/SendToMenu.cc \ |
235 | src/SendToMenu.hh \ | 235 | src/SendToMenu.hh \ |
236 | src/ShortcutManager.cc \ | ||
237 | src/ShortcutManager.hh \ | ||
236 | src/Strut.hh \ | 238 | src/Strut.hh \ |
237 | src/StyleMenuItem.cc \ | 239 | src/StyleMenuItem.cc \ |
238 | src/StyleMenuItem.hh \ | 240 | 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 @@ | |||
1 | #include "ShortcutManager.hh" | ||
2 | #include "Debug.hh" | ||
3 | |||
4 | #include <iostream> | ||
5 | |||
6 | ShortcutManager::ShortcutManager() : m_last_placeholder_key(0) { } | ||
7 | |||
8 | void ShortcutManager::setLastPlaceHolderKey(unsigned int lastPlaceHolderKey_) | ||
9 | { | ||
10 | m_last_placeholder_key = lastPlaceHolderKey_; | ||
11 | } | ||
12 | |||
13 | unsigned int ShortcutManager::getLastPlaceHolderKey() | ||
14 | { | ||
15 | return m_last_placeholder_key; | ||
16 | } | ||
17 | |||
18 | void ShortcutManager::mapKeyToWindow(unsigned int key, FluxboxWindow* window) | ||
19 | { | ||
20 | m_key_to_window_map.insert(std::make_pair(key, window)); | ||
21 | } | ||
22 | |||
23 | void ShortcutManager::removeWindow(FluxboxWindow* window) | ||
24 | { | ||
25 | KeyToWindowMap::const_iterator it; | ||
26 | for (it = m_key_to_window_map.begin(); it != m_key_to_window_map.end(); ++it) { | ||
27 | if (it->second == window){ | ||
28 | fbdbg << "Remove mapping window[" << window | ||
29 | << "] key [" << it->first << "]" << std::endl; | ||
30 | m_key_to_window_map.erase(it); | ||
31 | return; | ||
32 | } | ||
33 | } | ||
34 | } | ||
35 | |||
36 | FluxboxWindow* ShortcutManager::getWindowForKey(unsigned int key) | ||
37 | { | ||
38 | KeyToWindowMap::const_iterator it = m_key_to_window_map.find(key); | ||
39 | if (it != m_key_to_window_map.end()) { | ||
40 | return it->second; | ||
41 | } | ||
42 | else { | ||
43 | return nullptr; | ||
44 | } | ||
45 | } | ||
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 @@ | |||
1 | #ifndef SHORTCUTMANAGER_HH | ||
2 | #define SHORTCUTMANAGER_HH | ||
3 | |||
4 | #include <map> | ||
5 | |||
6 | class FluxboxWindow; | ||
7 | |||
8 | class ShortcutManager { | ||
9 | |||
10 | public: | ||
11 | |||
12 | ShortcutManager(); | ||
13 | |||
14 | void setLastPlaceHolderKey(unsigned int lastPlaceHolderKey_); | ||
15 | |||
16 | unsigned int getLastPlaceHolderKey(); | ||
17 | |||
18 | void mapKeyToWindow(unsigned int key, FluxboxWindow* window); | ||
19 | |||
20 | void removeWindow(FluxboxWindow* window); | ||
21 | |||
22 | FluxboxWindow* getWindowForKey(unsigned int key); | ||
23 | |||
24 | private: | ||
25 | |||
26 | typedef std::map<unsigned int, FluxboxWindow*> KeyToWindowMap; | ||
27 | |||
28 | unsigned int m_last_placeholder_key; | ||
29 | KeyToWindowMap m_key_to_window_map; | ||
30 | }; | ||
31 | |||
32 | #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() { | |||
353 | // no longer a valid window to do stuff with | 353 | // no longer a valid window to do stuff with |
354 | Fluxbox::instance()->removeWindowSearchGroup(frame().window().window()); | 354 | Fluxbox::instance()->removeWindowSearchGroup(frame().window().window()); |
355 | Fluxbox::instance()->removeWindowSearchGroup(frame().tabcontainer().window()); | 355 | Fluxbox::instance()->removeWindowSearchGroup(frame().tabcontainer().window()); |
356 | Fluxbox::instance()->shortcutManager().removeWindow(this); | ||
356 | 357 | ||
357 | Client2ButtonMap::iterator it = m_labelbuttons.begin(); | 358 | Client2ButtonMap::iterator it = m_labelbuttons.begin(); |
358 | Client2ButtonMap::iterator it_end = m_labelbuttons.end(); | 359 | 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 @@ | |||
39 | #include "FbTk/stringstream.hh" | 39 | #include "FbTk/stringstream.hh" |
40 | #include "FbTk/StringUtil.hh" | 40 | #include "FbTk/StringUtil.hh" |
41 | 41 | ||
42 | #include "Debug.hh" | ||
43 | |||
42 | #ifdef HAVE_CMATH | 44 | #ifdef HAVE_CMATH |
43 | #include <cmath> | 45 | #include <cmath> |
44 | #else | 46 | #else |
@@ -751,3 +753,38 @@ FbTk::Command<void> *RelabelButtonCmd::parse(const std::string &command, | |||
751 | 753 | ||
752 | REGISTER_COMMAND_PARSER(relabelbutton, RelabelButtonCmd::parse, void); | 754 | REGISTER_COMMAND_PARSER(relabelbutton, RelabelButtonCmd::parse, void); |
753 | 755 | ||
756 | void MarkWindowCmd::execute() { | ||
757 | BScreen *screen = Fluxbox::instance()->keyScreen(); | ||
758 | if (screen) { | ||
759 | |||
760 | FluxboxWindow* window = screen->focusControl().focusedFbWindow(); | ||
761 | if (window) { | ||
762 | ShortcutManager &shortcutManager = Fluxbox::instance()->shortcutManager(); | ||
763 | unsigned int key = shortcutManager.getLastPlaceHolderKey(); | ||
764 | shortcutManager.mapKeyToWindow(key, window); | ||
765 | fbdbg << "Map window[" << window << "] to key[" << key << "]" << std::endl; | ||
766 | } | ||
767 | } | ||
768 | } | ||
769 | |||
770 | REGISTER_COMMAND(markwindow, MarkWindowCmd, void); | ||
771 | |||
772 | void GotoMarkedWindowCmd::execute() { | ||
773 | |||
774 | ShortcutManager &shortcutManager = Fluxbox::instance()->shortcutManager(); | ||
775 | unsigned int key = shortcutManager.getLastPlaceHolderKey(); | ||
776 | |||
777 | FluxboxWindow *window = shortcutManager.getWindowForKey(key); | ||
778 | if (window) { | ||
779 | |||
780 | if (window->isIconic()) { | ||
781 | window->deiconify(false); | ||
782 | } | ||
783 | window->raiseAndFocus(); | ||
784 | |||
785 | fbdbg << "Raise and focus window[" << window | ||
786 | << "] mapped to key[" << key << "]" << std::endl; | ||
787 | } | ||
788 | } | ||
789 | |||
790 | 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: | |||
236 | std::string m_button, m_label; | 236 | std::string m_button, m_label; |
237 | }; | 237 | }; |
238 | 238 | ||
239 | class MarkWindowCmd: public FbTk::Command<void> { | ||
240 | public: | ||
241 | void execute(); | ||
242 | }; | ||
243 | |||
244 | class GotoMarkedWindowCmd: public FbTk::Command<void> { | ||
245 | public: | ||
246 | void execute(); | ||
247 | }; | ||
248 | |||
239 | #endif // WORKSPACECMD_HH | 249 | #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, | |||
272 | m_masked_window(0), | 272 | m_masked_window(0), |
273 | m_argv(argv), m_argc(argc), | 273 | m_argv(argv), m_argc(argc), |
274 | m_showing_dialog(false), | 274 | m_showing_dialog(false), |
275 | m_server_grabs(0) { | 275 | m_server_grabs(0), |
276 | m_shortcut_manager(new ShortcutManager) { | ||
276 | 277 | ||
277 | _FB_USES_NLS; | 278 | _FB_USES_NLS; |
278 | 279 | ||
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 @@ | |||
32 | #include "FbTk/MenuSearch.hh" | 32 | #include "FbTk/MenuSearch.hh" |
33 | 33 | ||
34 | #include "AttentionNoticeHandler.hh" | 34 | #include "AttentionNoticeHandler.hh" |
35 | #include "ShortcutManager.hh" | ||
35 | 36 | ||
36 | #include <X11/Xresource.h> | 37 | #include <X11/Xresource.h> |
37 | 38 | ||
@@ -185,6 +186,7 @@ public: | |||
185 | const XEvent &lastEvent() const { return m_last_event; } | 186 | const XEvent &lastEvent() const { return m_last_event; } |
186 | 187 | ||
187 | AttentionNoticeHandler &attentionHandler() { return m_attention_handler; } | 188 | AttentionNoticeHandler &attentionHandler() { return m_attention_handler; } |
189 | ShortcutManager &shortcutManager() { return *m_shortcut_manager; } | ||
188 | 190 | ||
189 | private: | 191 | private: |
190 | std::string getRcFilename(); | 192 | std::string getRcFilename(); |
@@ -306,6 +308,7 @@ private: | |||
306 | } m_state; | 308 | } m_state; |
307 | 309 | ||
308 | int m_server_grabs; | 310 | int m_server_grabs; |
311 | std::unique_ptr<ShortcutManager> m_shortcut_manager; | ||
309 | }; | 312 | }; |
310 | 313 | ||
311 | 314 | ||