;;; win-switch.el --- fast, dynamic bindings for window-switching/resizing ;; Copyright (C) 2011-2015 Christopher R. Genovese, all rights reserved. ;; Author: Christopher Genovese ;; Maintainer: Christopher R. Genovese ;; URL: http://www.stat.cmu.edu/~genovese/emacs/win-switch/ ;; Package-Version: 1.1.4 ;; Package-Commit: 954eb5e4c5737f0c06368c42a7f1c3dd374d782f ;; Version: 1.1.4 ;; Update#: 25 ;; Created: Wed 28 Jul 2011 at 00:27 EDT ;; Last-Updated: Sun 09 Oct 2016 at 12:20 EDT ;; By: Christopher R. Genovese ;; Keywords: window, switch, key bindings, ergonomic, efficient ;; Compatibility: GNU Emacs 22, GNU Emacs 23.4, Gnu Emacs 24.4.1 ;; Tested on these versions on Mac OS X 10.7.5. ;; Testing or feedback for other platforms/versions ;; would be very much appreciated. ;;; Commentary: ;; ;; If you use multiple windows in an Emacs frame, you may find yourself ;; moving through the window configuration using `other-window' (C-x o) ;; again and again. Because the order of windows in the window list ;; need not relate intuitively to windows' positions, moving ;; efficiently can require context-specific prefix arguments along the ;; way. The tiring outcome is that navigation through a complex window ;; configuration demands many keystrokes and nontrivial attention. ;; This package is designed to solve that problem. ;; ;; While the `windmove' package provides functions for moving ;; intuitively among windows, the natural key bindings for these ;; functions (e.g., the arrow keys with some modifier) require a ;; distant and thus inefficient hand movement. Moreover, one often ;; wants to mix a variety of window-based operations (other-window, ;; previous-window, directional movement, resizing) in rapid ;; succession. ;; ;; This package builds on the windmove functionality by defining a ;; command `win-switch-dispatch' that engages a dynamic, transient ;; keyboard override, allowing one to efficiently move among defined ;; windows (and frames) -- and even resize, split, delete them -- with ;; minimal fuss and effort. When the override is engaged, the movement ;; and resizing commands are bound to simple keys that can be pressed ;; quickly with one hand. The override ends either when the user exits ;; explicitly or after a configurable idle time threshold. The happy ;; outcome is fast and seamless navigation. ;; ;; To use the package, execute the following code either directly ;; or in your .emacs file: ;; ;; (require 'win-switch) ;; (global-set-key "\C-xo" 'win-switch-dispatch) ;; ;; or use whatever keybinding you ordinarily have set to `other-window'. ;; Alternatively, you can use one of a variety of predefined configuration ;; commands, as in ;; ;; (require 'win-switch) ;; (win-switch-setup-keys-ijkl "\C-xo") ;; ;; which has the same effect as the above. ;; ;; Now, when executing a window switch (i.e., hitting C-xo), Emacs enters ;; window switching mode, which lasts until either the user exits the ;; mode or the idle time exceeds the threshold `win-switch-idle-time'. ;; During this override, selected keys move among windows (or frames) ;; or resize the windows. The following keys are bound by default: ;; ;; + i select the window above the current window. ;; + k select the window below the current window. ;; + j select the window left of the current window. ;; + l select the window right of the current window. ;; + o cycle forward through the window list in the current frame. ;; + p cycle backward through the window list in the current frame. ;; + SPACE cycles among existing frames. ;; + u (and RETURN) exit window switching mode. ;; + I and K vertically enlarge and shrink the current window, respectively. ;; + L and J horizontally enlarge and shrink the current window, respectively. ;; + h and ; split the current window, horizontally and vertically, respectively. ;; + ESCAPE acts as an "emergency" exit ;; ;; All other keys exit window switching mode and execute their original function. ;; ;; By default, window selection wraps around when moving across a frame ;; edge and window switching mode is forgone when there are only two ;; windows. But these features, the key bindings, and other parameters ;; can all be customized, either with the customization facility or ;; with defvar and setter functions. ;; ;; The default keybindings are designed for fast and intuitve, ;; one-handed operation, but if desired the key bindings can be easily ;; adjusted or reset. Several alternative key configurations are pre-defined ;; (see `win-switch-setup-keys-ijkl', `win-switch-setup-keys-arrow-ctrl', ;; `win-switch-setup-keys-arrow-meta', and `win-switch-setup-keys-esdf' ;; below). The keys also can be rebound in groups via the variables ;; `win-switch--keys' where can be one of up, down, left, ;; right, next-window, previous-window, enlarge-vertically, ;; shrink-vertically, enlarge-horizontally, shrink-horizontally, ;; other-frame, exit, split-vertically, split-horizontally, delete-window, ;; or emergency-exit. These variables should not be set directly, ;; but rather should be set either by customize or by ;; using the functions `win-switch-add-key', `win-switch-delete-key', ;; and `win-switch-set-keys'. For example: ;; ;; (win-switch-add-key "O" 'previous-window) ;; (win-switch-delete-key "p" 'previous-window) ;; (win-switch-set-keys '(" " "," "m") 'other-frame) ;; ;; Note that the last arguments here are win-switch commands not elisp ;; functions. (Note also that the emergency-exit keys do a hard exit in ;; case of an unexpected error in user-defined code such as in ;; customized feedback functions. This command may be removed in future ;; versions.) At least one exit key must always be defined. Revised ;; bindings can be set in in the hook `win-switch-load-hook' before ;; loading the package. (Also see `win-switch-define-key' for setting ;; general commands in the win-switch keymap, and ;; `win-switch-set-once-key' for setting commands in the once only ;; keymap used by `win-switch-dispatch-once'.) ;; ;; Besides key bindings, the most important customization options are ;; the following: ;; ;; + `win-switch-idle-time' ;; + `win-switch-window-threshold' ;; + `win-switch-other-window-first' ;; + `win-switch-wrap-around' (set via `win-switch-set-wrap-around') ;; ;; The idle time should be set so that one does not have to either rush ;; or wait. (While explicit exit always works, it is nice to have ;; window-switching mode end on its own at just the right time.) This ;; may require some personalized fiddling to find a comfortable value, ;; though the default should be pretty good. The window-threshold and ;; other-window-first control when and if window switching mode is ;; entered. And wrap-around determines if moving across the edge of the ;; frame wraps around to the window on the other side. ;; ;; The other customizable parameters are as follows: ;; ;; + `win-switch-provide-visual-feedback' ;; + `win-switch-feedback-background-color' ;; + `win-switch-feedback-foreground-color' ;; + `win-switch-on-feedback-function' ;; + `win-switch-off-feedback-function' ;; + `win-switch-other-window-function' ;; ;; The feedback mechanisms are intended to make it salient when ;; window switching mode is on or off and can be customized at ;; several scales. The other-window-function replaces `other-window' ;; for moving between window; the primary motivation is to allow ;; `icicle-other-window-or-frame' under icicles. ;; ;; And four hooks can be set as well: ;; ;; + `win-switch-load-hook' ;; + `win-switch-on-hook' ;; + `win-switch-off-hook' ;; + `win-switch-abort-hook' ;; ;; The following functions are used to set options: ;; ;; + `win-switch-set-wrap-around' ;; + `win-switch-add-key' ;; + `win-switch-delete-key' ;; + `win-switch-set-keys' ;; ;; There are three main entry points for using this functionality ;; ;; + `win-switch-dispatch' (alias `win-switch-mode') ;; + `win-switch-dispatch-once' ;; + `win-switch-dispatch-with' ;; ;; The first is the main function, the second is a prefix command that ;; gives one switch only but allows easy maneuvering in up to five ;; windows with a single keystroke. (The `once' keys can be set using ;; the `win-switch-set-once-keys' command.) The last constructs ;; commands for keybindings that dispatch after some other command. ;; (See `win-switch-setup-keys-arrow' for a nice example of its use.) ;; ;; NOTE: win-switch is not a formal major or minor mode, more of an ;; overriding mode. This started as a way to explore dynamic ;; keybindings, an idea that is generalized considerably in ;; my packages `quick-nav' and `power-keys'. The latter ;; introduces some programming abstractions that can be used ;; to easily install dynamic keymaps of several flavors. ;; I plan to use the `power-keys' mechanisms for this package ;; in a later version. ;; ;; Code Contents ;; 1. (@> "User-Configurable Parameters") ;; 2. (@> "User-Configurable Key Bindings") ;; 3. (@> "Preventing Default Shadowing") ;; 4. (@> "Internal Configuration Data") ;; 5. (@> "Internal Functions and Macros") ;; 6. (@> "Actions and Key Commands") ;; 7. (@> "Customization Initializers and Option Setters") ;; 8. (@> "User Entry Points") ;; 9. (@> "Pre-defined Configurations") ;; ;;; Change Log: ;; ;; * 08 Oct 2016 -- (win-switch-on-feedback, win-switch-off-feedback) ;; Speed up by changing mode-line face only on ;; selected frame, and only when needed. ;; ;; (win-switch-exit-by-timeout) Streamlined pushback ;; of exit keys onto unread-command-events, matching ;; change to win-switch-exit-and-redo in last commit. ;; ;; * 10 Feb 2015 -- Added '--' to private function names, ;; streamlined handling of unread-command-events. ;; ;; * 09 Feb 2015 -- Added abort-hook to check for no-entry conditions, ;; cleaned up timer functions and dispatch checks. ;; ;; * 08 Feb 2015 -- Minor bug and doc fixes; fixed win-switch-dispatch-once, ;; ignores errors when there are no windows to move to. ;; ;; * 02 Feb 2013 -- Fixed customization type for win-switch-window-threshold ;; Fixed comment describing L and J keys' functionality ;; ;; * 02 Nov 2012 -- Changed the last stray 'first to 'car ;; ;; * 04 Aug 2012 -- Changed a few stray first/rest's to car/cdr's. ;; ;; * 17 Mar 2012 -- Fixed *two* silly typos in fset and in a string constant. ;; in win-switch-setup-keys-arrows. The former was causing ;; load failure from package.el. ;; ;; * 17 Jan 2012 -- Removed linkd minor mode and Package-Requires header ;; because they were causing problems with loading ;; the package through package.el. ;; ;; * 18 Dec 2011 -- Fixed error in keylist management functions that ;; masked changes to keys on other lists. This ;; affected `win-switch-custom-set-keys', ;; `win-switch-add-key', and `win-switch-delete-key'. ;; Also fixed a few documentation typos. ;; Thanks to Mark Hepburn and Brett Presnell for ;; finding the error. ;; ;; * 11 Sep 2011 -- Fixed typo in `win-switch-setup-keys-arrow-ctrl' ;; and `win-switch-setup-keys-arrow-meta'. ;; Thanks to mpdflaccuesupport. ;; ;; * 27 Aug 2011 -- Fixed missing hyphen in `win-switch-authors-configuration' ;; ;; * 20 Aug 2011 -- Updated documentation, moved split and delete ;; keys after the others in the file and added function ;; to suppress them if desired. Added timer to end ;; `win-switch-off-alert' message. ;; ;; * 19 Aug 2011 -- Adjusted for Emacs shadowing default bindings ;; in sub-keymaps. Whenever `win-switch-map' is ;; set or adjusted, the default binding is added ;; if necessary to the map and sub-keymaps. ;; Also, allowed `win-switch-other-window-first' ;; to be a function to allow context sensitive ;; behavior. Added `win-switch-delete-window-keys' ;; and put delete-window in command list. ;; ;; * 05 Aug 2011 -- Fixed setting functions for the once keys, ;; added author config FYI, and minor bug fixes. ;; ;; * 02 Aug 2011 -- Added setup-key functions, win-switch-dispatch-with, ;; win-switch-dispatch-once, minor bug fixes in ;; in setters, some clode clean up. ;; ;; * 01 Aug 2011 -- Added additional hooks, adjusted window-threshold ;; handling, added add-key and delete-key setters, ;; finalized the documentation, and ran tests. ;; ;; * 30 Jul 2011 -- Added customization framework, frame switching, and ;; custom feedback and other-window functions. ;; ;; * 29 Jul 2011 -- Adjusted keymap initialization, changed behavior ;; of `win-switch-dispatch' with prefix arguments ;; ;; * 28 Jul 2011 -- Completed and tested main functionality; rediscovered ;; several times that messing with overriding-loca-map ;; can be dangerous. ;; ;;; License: ;; ;; This program is free software; you can redistribute it and/or ;; modify it under the terms of the GNU General Public License as ;; published by the Free Software Foundation; either version 3, or ;; (at your option) any later version. ;; ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ;; General Public License for more details. ;; ;; You should have received a copy of the GNU General Public License ;; along with this program; see the file COPYING. If not, write to ;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth ;; Floor, Boston, MA 02110-1301, USA. ;; ;;; Code: (require 'windmove) ;; (@* "User-Configurable Parameters") (defgroup win-switch nil "All customization options for win-switch mode." :prefix "win-switch-" :group 'convenience :link `(url-link :tag "Send Bug Report" ,(concat "mailto:" "genovese@cmu.edu" "?subject=win-switch.el bug: \ &body=Describe bug here, starting with `emacs -q'. \ Be sure to include your platform and your Emacs and win-switch versions.")) :link `(url-link :tag "Download" "http://www.github.com/genovese/emacs-utils/win-switch") :link `(url-link :tag "Description" "http://www.emacsiki.org/cgi-bin/wiki/WinSwitch")) (defgroup win-switch-keys nil "Command key bindings for win-switch mode." :prefix "win-switch-" :group 'win-switch) ;;;###autoload (defcustom win-switch-idle-time 0.75 "Cancel window switching mode when idle time exceeds this threshold. The time is measured in seconds and can be an integer or floating-point number." :type 'number :group 'win-switch) ;;;###autoload (defcustom win-switch-window-threshold 2 "Number of windows above which dispatch always enters switching mode. When the current frame has more than this many windows, `win-switch-dispatch' enters window-switching mode unconditionally; otherwise, it acts like like `win-switch-other-window-function' (which is `other-window' by default). Besides its effect on window switching behavior, this option also affects how `win-switch-dispatch' interprets its prefix argument. See the documentation for `win-switch-dispatch' for details." :type 'integer :group 'win-switch) ;;;###autoload (defcustom win-switch-other-window-first t "Whether to move to next window before entering window switching mode. Should be either a boolean or a boolean function that takes no arguments. If equal to t or if a function and the function returns a non-nil value, `win-switch-dispatch' calls `win-switch-next-window' before changing window-switching modes." :type '(choice boolean function) :group 'win-switch) ;;;###autoload (defcustom win-switch-wrap-around t "Whether movement off the edge of the frame wraps around. To set this variable in Lisp code, do not set the variable directly but rather call the function `win-switch-set-wrap-around' with argument 1 to turn wrapping on and -1 to turn wrapping off." :type 'boolean :set (lambda (symbol value) (win-switch-set-wrap-around (if value 1 -1))) :initialize (lambda (symbol value) (setq windmove-wrap-around value) (custom-initialize-default symbol value)) :require 'windmove :group 'win-switch) ;;;###autoload (defcustom win-switch-provide-visual-feedback t "Whether to provide visual feedback during window switching mode." :type 'boolean :group 'win-switch) ;;;###autoload (defcustom win-switch-feedback-background-color "red" "Mode line background color of active window during switching mode." :type 'string :group 'win-switch) ;;;###autoload (defcustom win-switch-feedback-foreground-color "white" "Mode line foreground color of active window during switching mode." :type 'string :group 'win-switch) ;;;###autoload (defcustom win-switch-on-feedback-function nil "Function to turn on visual feedback, or nil for default behavior. This function of zero arguments is called when entering window switching mode, and it should set up conditions that make salient that window switching mode is turned on. Setting this function should usually be paired with setting `win-switch-off-feedback-function' to ensure that what is set on entry is unset on exit. See `win-switch-on-feedback' for the default behavior." :type '(choice (const :tag "Default" nil) ; nil here because other-window has distinct calling sequence function) :group 'win-switch) ;;;###autoload (defcustom win-switch-off-feedback-function nil "Function to turn off visual feedback, or nil for default behavior. This function of zero arguments is called when exiting window switching mode, and it should make salient that window switching mode is turned off and clear any conditions that were set on entry. Setting this function should usually be paired with setting `win-switch-on-feedback-function' to ensure that what is unset on exit had been set on entry. See the function `win-switch-off-feedback' for the default behavior." :type '(choice (const :tag "Default" nil) ; nil here because other-window has distinct calling sequence function) :group 'win-switch) ;;;###autoload (defcustom win-switch-other-window-function nil "Function to switch windows or nil for default, `other-window'." :type '(choice (const :tag "Default" nil) ; nil here because other-window has distinct calling sequence function) :group 'win-switch) ;;;###autoload (defvar win-switch-load-hook nil "List of functions to be called when win-switch module is loaded.") ;;;###autoload (defvar win-switch-on-hook nil "List of functions to be called as window switching mode is entered. These functions are called just before the overriding key map is set up and before the timer is started.") ;;;###autoload (defvar win-switch-off-hook nil "List of functions to be called after window switching mode is exited. These functions are called after the timer is cleared and the overriding key map is restored") ;;;###autoload (defvar win-switch-abort-hook nil "List of functions that check if `win-switch-dispatch' should be aborted. These functions are called in succession at `win-switch-dispatch' just before entering window-switching mode and after checking the usual conditions on the window threshold and prefix arguments. If any function in this list returns a non-nil value, window-switching mode is not entered. If the returned value is 'abort, then no action is taken; for any other non-nil value `win-switch-next-window' is still called. The primary purpose of these hooks is to allow for conditions where the persistent mode can cause conflicts or other problems.") ;; (@* "User-Configurable Key Bindings") ;;;###autoload (defcustom win-switch-up-keys '("i") "List of key sequences that select the window above the current one. To set this variable from Lisp code, do not just set it directly, but rather use the function `win-switch-set-keys' as follows: (win-switch-set-keys 'up) where is a list of key bindings." :type '(repeat (sexp :format "%v")) :set 'win-switch-custom-set-keys :initialize 'custom-initialize-default :group 'win-switch-keys) ;;;###autoload (defcustom win-switch-down-keys '("k") "List of key sequences that select the window below the current one. To set this variable from Lisp code, do not just set it directly, but rather use the function `win-switch-set-keys' as follows: (win-switch-set-keys 'down) where is a list of key bindings." :type '(repeat (sexp :format "%v")) :set 'win-switch-custom-set-keys :initialize 'custom-initialize-default :group 'win-switch-keys) ;;;###autoload (defcustom win-switch-left-keys '("j") "List of key sequences that select the window left of the current one. To set this variable from Lisp code, do not just set it directly, but rather use the function `win-switch-set-keys' as follows: (win-switch-set-keys 'left) where is a list of key bindings." :type '(repeat (sexp :format "%v")) :set 'win-switch-custom-set-keys :initialize 'custom-initialize-default :group 'win-switch-keys) ;;;###autoload (defcustom win-switch-right-keys '("l") "List of key sequences that select the window left of the current one. To set this variable from Lisp code, do not just set it directly, but rather use the function `win-switch-set-keys' as follows: (win-switch-set-keys 'right) where is a list of key bindings." :type '(repeat (sexp :format "%v")) :set 'win-switch-custom-set-keys :initialize 'custom-initialize-default :group 'win-switch-keys) ;;;###autoload (defcustom win-switch-next-window-keys '("o") "List of key sequences that select the next window. To set this variable from Lisp code, do not just set it directly, but rather use the function `win-switch-set-keys' as follows: (win-switch-set-keys 'next-window) where is a list of key bindings." :type '(repeat (sexp :format "%v")) :set 'win-switch-custom-set-keys :initialize 'custom-initialize-default :group 'win-switch-keys) ;;;###autoload (defcustom win-switch-previous-window-keys '("p") "List of key sequences that select the next window. To set this variable from Lisp code, do not just set it directly, but rather use the function `win-switch-set-keys' as follows: (win-switch-set-keys 'previous-window) where is a list of key bindings." :type '(repeat (sexp :format "%v")) :set 'win-switch-custom-set-keys :initialize 'custom-initialize-default :group 'win-switch-keys) ;;;###autoload (defcustom win-switch-enlarge-vertically-keys '("I") "List of key sequences that vertically enlarges current window. To set this variable from Lisp code, do not just set it directly, but rather use the function `win-switch-set-keys' as follows: (win-switch-set-keys 'enlarge-vertically) where is a list of key bindings." :type '(repeat (sexp :format "%v")) :set 'win-switch-custom-set-keys :initialize 'custom-initialize-default :group 'win-switch-keys) ;;;###autoload (defcustom win-switch-shrink-vertically-keys '("K") "List of key sequences that vertically shrinks current window. To set this variable from Lisp code, do not just set it directly, but rather use the function `win-switch-set-keys' as follows: (win-switch-set-keys 'shrink-vertically) where is a list of key bindings." :type '(repeat (sexp :format "%v")) :set 'win-switch-custom-set-keys :initialize 'custom-initialize-default :group 'win-switch-keys) ;;;###autoload (defcustom win-switch-shrink-horizontally-keys '("J") "List of key sequences that horizontally shrinks current window. To set this variable from Lisp code, do not just set it directly, but rather use the function `win-switch-set-keys' as follows: (win-switch-set-keys 'shrink-horizontally) where is a list of key bindings." :type '(repeat (sexp :format "%v")) :set 'win-switch-custom-set-keys :initialize 'custom-initialize-default :group 'win-switch-keys) ;;;###autoload (defcustom win-switch-enlarge-horizontally-keys '("L") "List of key sequences that horizontally enlarges current window. To set this variable from Lisp code, do not just set it directly, but rather use the function `win-switch-set-keys' as follows: (win-switch-set-keys 'enlarge-horizontally) where is a list of key bindings." :type '(repeat (sexp :format "%v")) :set 'win-switch-custom-set-keys :initialize 'custom-initialize-default :group 'win-switch-keys) ;;;###autoload (defcustom win-switch-other-frame-keys '(" ") "List of key sequences that select the next frame. To set this variable from Lisp code, do not just set it directly, but rather use the function `win-switch-set-keys' as follows: (win-switch-set-keys 'other-frame) where is a list of key bindings." :type '(repeat (sexp :format "%v")) :set 'win-switch-custom-set-keys :initialize 'custom-initialize-default :group 'win-switch-keys) ;;;###autoload (defcustom win-switch-exit-keys '("u" [return]) "List of key sequences that will exit window switching mode. To set this variable from Lisp code, do not just set it directly, but rather use the function `win-switch-set-keys' as follows: (win-switch-set-keys 'exit) where is a list of key bindings." :type '(repeat (sexp :format "%v")) :set 'win-switch-custom-set-keys :initialize 'custom-initialize-default :group 'win-switch-keys) ;;;###autoload (defcustom win-switch-split-horizontally-keys '(";") ; visual mnemonic "List of key sequences that horizontally splits current window. To set this variable from Lisp code, do not just set it directly, but rather use the function `win-switch-set-keys' as follows: (win-switch-set-keys 'split-horizontally) where is a list of key bindings." :type '(repeat (sexp :format "%v")) :set 'win-switch-custom-set-keys :initialize 'custom-initialize-default :group 'win-switch-keys) ;;;###autoload (defcustom win-switch-split-vertically-keys '("h") ; visual not letter mnemonic "List of key sequences that vertically splits current window. To set this variable from Lisp code, do not just set it directly, but rather use the function `win-switch-set-keys' as follows: (win-switch-set-keys 'split-vertically) where is a list of key bindings." :type '(repeat (sexp :format "%v")) :set 'win-switch-custom-set-keys :initialize 'custom-initialize-default :group 'win-switch-keys) ;;;###autoload (defcustom win-switch-delete-window-keys '("0") "List of key sequences that deletes current window. To set this variable from Lisp code, do not just set it directly, but rather use the function `win-switch-set-keys' as follows: (win-switch-set-keys 'delete-window) where is a list of key bindings." :type '(repeat (sexp :format "%v")) :set 'win-switch-custom-set-keys :initialize 'custom-initialize-default :group 'win-switch-keys) ;;;###autoload (defcustom win-switch-emergency-exit-keys '("\M-\C-g") "List of additional key sequences that will exit window switching mode. This exits window switching without any niceties, feedback, or hooks and so should be used only as a last resort. It is intended only as a precaution for cases in which an unexpected problem (e.g., in user defined hooks or function-valued options) makes it impossible to exit window switching mode by another way. This should not need really be necessary and may be removed in future versions. To set this variable from Lisp code, do not just set it directly, but rather use the function `win-switch-set-keys' as follows: (win-switch-set-keys 'emergency-exit) where is a list of key bindings." :type '(repeat (sexp :format "%v")) :set 'win-switch-custom-set-keys :initialize 'custom-initialize-default :group 'win-switch-keys) ;;;###autoload (defcustom win-switch-once-double-next-keys '("u") "List of keys that will advance two windows in `win-switch-dispatch-once'. To set this variable from Lisp code, do not just set it directly, but rather use the function `win-switch-set-once-keys' as follows: (win-switch-set-once-keys 'once-double-next) where is a list of key bindings." :type '(repeat (sexp :format "%v")) :set (lambda (sym value) (win-switch-set-once-keys value 'once-double-next)) :initialize 'custom-initialize-default :group 'win-switch-keys) ;;;###autoload (defcustom win-switch-once-double-prev-keys '("y") "List of keys that will move back two windows in `win-switch-dispatch-once'. To set this variable from Lisp code, do not just set it directly, but rather use the function `win-switch-set-once-keys' as follows: (win-switch-set-once-keys 'once-double-prev) where is a list of key bindings." :type '(repeat (sexp :format "%v")) :set (lambda (sym value) (win-switch-set-once-keys value 'once-double-prev)) :initialize 'custom-initialize-default :group 'win-switch-keys) ;; (@* "Preventing Default Shadowing") ;; Because of an issue (which I think should be considered a bug) in ;; Emacs, adding a sparse prefix key map can shadow the default binding ;; for unbound keys with the same prefix. For instance, if the user adds ;; a binding to a key involving the Meta modifier when no such binding ;; was included before, a sub-keymap is created under meta-prefix-char ;; (default escape or 27). But the default for `win-switch-map' will ;; *not* be found by the lookup process for any unbound meta key ;; sequence. This can cause problems because window-switching mode will ;; not be exited automatically in this case as it should. ;; ;; The function `win-switch--fix-keymap-defaults' adds the necessary ;; defaults in the keymap and sub-keymaps. It is used in the key setting ;; functions and in the `win-switch-map' keymap definition itself. ;; (defun win-switch--fix-keymap-defaults (map) "Adjust keymap MAP to include proper exit defaults. Returns a modified version of MAP that may share some structure with the original. In the modified map itself and in any sub-keymaps, a default binding is added, if a default is not already present. The default is bound to `win-switch-exit-and-redo'. This ensures that unbound key sequences exit from window switching mode. Without this, adding a sparse prefix key map can shadow the default binding for unbound keys with the same prefix." (let ((fixed-map (mapcar (lambda (entry) (if (and (consp entry) (keymapp (cdr entry)) (null (lookup-key (cdr entry) [t]))) (cons (car entry) (win-switch--fix-keymap-defaults (cdr entry))) entry)) map))) (unless (lookup-key fixed-map [t]) (define-key fixed-map [t] 'win-switch-exit-and-redo)) fixed-map)) ;; (@* "Internal Configuration Data") (defvar win-switch-commands '((win-switch-up-keys . win-switch-up) (win-switch-down-keys . win-switch-down) (win-switch-left-keys . win-switch-left) (win-switch-right-keys . win-switch-right) (win-switch-next-window-keys . win-switch-next-window) (win-switch-previous-window-keys . win-switch-previous-window) (win-switch-enlarge-vertically-keys . enlarge-window) (win-switch-shrink-vertically-keys . shrink-window) (win-switch-shrink-horizontally-keys . shrink-window-horizontally) (win-switch-enlarge-horizontally-keys . enlarge-window-horizontally) (win-switch-other-frame-keys . win-switch-other-frame) (win-switch-exit-keys . win-switch-exit) (win-switch-split-vertically-keys . split-window-vertically) (win-switch-split-horizontally-keys . split-window-horizontally) (win-switch-delete-window-keys . delete-window) (win-switch-emergency-exit-keys . win-switch-emergency-exit)) "Associates pre-defined key lists to window-switching mode commands.") (defvar win-switch-once-commands '((win-switch-once-double-next-keys . win-switch-double-next-window) (win-switch-once-double-prev-keys . win-switch-double-previous-window)) "Associates once-only key lists to dispatch-once commands. See `win-switch-dispatch-once'.") (defvar win-switch-map (let ((map (make-sparse-keymap))) ;; must have an exit or we will regret it (when (null win-switch-exit-keys) (error "The exit keys list for win-switch must remain non-empty")) ;; assign specified commands (dolist (cmdpair win-switch-commands) (dolist (key (symbol-value (car cmdpair))) (define-key map key (cdr cmdpair)))) ;; all other keys exit and then perform their original function ;; see comment above regarding the need for the `esc' submap (define-key map [t] 'win-switch-exit-and-redo) ;; fix up the defaults (win-switch--fix-keymap-defaults map)) "Keymap that is active during window switching mode. The functions `win-switch-set-keys', `win-switch-add-key', and `win-switch-delete-key', can be used to set parts of this keymap corresponding to the available commands. To add an arbitrary command to this keymap, use `win-switch-define-key' rather than changing this variable directly. If you do change this keymap directly, using `define-key' for instance, be very careful to leave an exit key available") (defvar win-switch-once-map (let ((map (make-sparse-keymap "Window Switching"))) (dolist (cmdpair (append win-switch-commands win-switch-once-commands nil)) (let ((keysym (car cmdpair)) (cmd (cdr cmdpair))) (dolist (key (symbol-value keysym)) (unless (or (eq keysym 'win-switch-exit-keys) (eq keysym 'win-switch-emergency-exit-keys)) (define-key map key cmd))))) map) "Keymap referenced by `win-switch-dispatch-once' to make simple moves. Commands using this keymap do *not* enter window-switching mode, so no exit keys or commands are required (or helpful). It is safe to assign directly to this keymap. See `win-switch-dispatch-once.'") (defvar win-switch-timer nil "When non-nil, measures the idle time until window switching mode expires.") (defvar win-switch-engaged nil "Non-nil when window switching mode is on, nil otherwise.") (defvar win-switch-overriding-map-stack nil "Stack to hold saved values of `overridiing-local-map'.") (defvar win-switch-saved-mode-line-faces nil "Holds cons with previous mode-line background and foreground.") (defvar win-switch-visited-frames-list nil "Holds list of frames visited during a single window-switching interval.") ;; (@* "Internal Functions and Macros") (defun win-switch--start-timer (secs func) "Run for SECS seconds before excecuting function FUNC, if SECS is non-nil. Uses `win-switch-timer', if valid, canceling it before restarting. If non-nil, SECS should be a number or time; FUNC should be a symbol or function." (when secs (when (and win-switch-timer (timerp win-switch-timer)) (cancel-timer win-switch-timer)) (setq win-switch-timer (run-with-idle-timer secs nil func)))) (defun win-switch--clear-timer () "Cancel and nullify `win-switch-timer', if valid." (when win-switch-timer (when (timerp win-switch-timer) (cancel-timer win-switch-timer)) (setq win-switch-timer nil))) (defmacro win-switch--override-map (map) "Save keymap bound to symbol MAP and set MAP to `win-switch-map'." `(progn (when (and ,map (not (eq ,map (car win-switch-overriding-map-stack)))) (push ,map win-switch-overriding-map-stack)) (setq ,map win-switch-map))) (defmacro win-switch--restore-map (map) "Reset symbol MAP's value to most recently saved keymap." `(setq ,map (pop win-switch-overriding-map-stack))) (defun win-switch--enough-windows-p (&optional maybe-frame) "Are there enough windows on MAYBE-FRAME to enter window-switching mode? Frame defaults to the selected frame if MAYBE-FRAME is nil." (let* ((frame (or maybe-frame (selected-frame)))) (or (<= win-switch-window-threshold 0) (nthcdr win-switch-window-threshold (window-list frame))))) ;; (@* "Actions and Key Commands") (defun win-switch-up (&optional arg) (interactive "P") (ignore-errors (windmove-do-window-select 'up arg))) (defun win-switch-down (&optional arg) (interactive "P") (ignore-errors (windmove-do-window-select 'down arg))) (defun win-switch-left (&optional arg) (interactive "P") (ignore-errors (windmove-do-window-select 'left arg))) (defun win-switch-right (&optional arg) (interactive "P") (ignore-errors (windmove-do-window-select 'right arg))) (defun win-switch-next-window (arg &optional interactive?) "Move to next window in window list. ARG is in raw prefix argument format, and INTERACTIVE? is non-nil if the function was called interactively. When `win-switch-other-window-function' is non-nil, call that function, either interactively when INTERACTIVE? is non-nil or passing arg otherwise. When it is nil, calls `other-window' with the numeric value of ARG. This is a wrapper that allows the user to override the standard window switching behavior, for instance when using icicles." (interactive "P\np") (if win-switch-other-window-function (if interactive? (call-interactively 'win-switch-other-window-function) (funcall win-switch-other-window-function arg)) (other-window (prefix-numeric-value arg)))) (defun win-switch-previous-window (arg &optional interactive?) "Move to previous window in window list." (interactive "p\np") (win-switch-next-window (- arg) interactive?)) (defun win-switch-double-next-window () "Advance two windows in window list." (interactive) (win-switch-next-window 2)) (defun win-switch-double-previous-window () "Move to second previous window in window list." (interactive) (win-switch-next-window -2)) ;; ATTN: This must be cleaned up. See comment below at win-switch-on-feedback (defun win-switch-other-frame (&optional arg) "Select the ARGth different visible frame on current display, and raise it. Like `other-frame' but correctly cleans up visual feedback settings." (interactive "p") (when (and win-switch-provide-visual-feedback (not win-switch-off-feedback-function)) (win-switch-off-feedback-mode-line)) (other-frame arg) (when (and win-switch-provide-visual-feedback (not win-switch-on-feedback-function)) (win-switch-on-feedback-mode-line))) (defun win-switch-on-alert () "Alert users, usually in echo area, that window switching is on." (message "Window Switching Mode On...")) (defun win-switch-off-alert () "Alert users, usually in echo area, that window switching is off." (message "Window Switching Mode Off.") (run-with-timer 0.5 nil (lambda () (message nil)))) ;; ATTN: Issue -- CRG 2016-10-09 ;; The change to the mode-line feedback in each frame as of v1.1.3 ;; gave noticeably improved responsiveness. In fact, with many frames, ;; changing the mode-line face in all frames at the beginning of window ;; switching was noticeably and annoyingly lagged. ;; ;; However, setting the background in each frame makes the coordination ;; difficult, as we don't want to leave any frames with the mode-line ;; changed after win-switching exits. In particular, setting it in each ;; frame required that we replace `other-frame' by ;; `win-switch-other-frame', which handls the mode-line changes if ;; required when a new frame is visited. This is leaky and not DRY, and ;; it requires decoupling the mode-line feedback change from the state ;; changs on beginning and end. It is clear now that the feedback ;; function should be a parameter with a default setting and pre- and ;; post- hooks rather than the way it is done here. Some care needs ;; to go into how that is arranged so that the main feedback functions ;; can be called at each frame change. ;; For example, *-(start|end)-feedback-functions can be those ;; called only at the beginning and end of a window-switching interval ;; whereas *-continuing-(on|off)-feedback-functions at a frame change, and ;; *-(pre|post)-cleanup-feedback-functions wrapped around the whole ;; thing for state cleanup. That is messy and confusing, though. ;; Maybe asthetically better to have an alist for the feedback functions ;; which is used to set the messier variables (pre-cleanup functions...) ;; (start functions...) (continuing functions...), et cetera. (defun win-switch-on-feedback () "Provide visual feedback for the start of window switching mode." (if win-switch-on-feedback-function (funcall win-switch-on-feedback-function) (win-switch-on-alert) (setq win-switch-saved-mode-line-faces (cons (face-attribute 'mode-line :background) (face-attribute 'mode-line :foreground))) (win-switch-on-feedback-mode-line))) (defun win-switch-on-feedback-mode-line () "Provide feedback via mode-line colors for start of window switching mode. This function only changes the mode-line foreground and background in the *current frame*, and only if they are different from the saved colors in `win-switch-saved-mode-line-faces', which should be set on entry into window-switching mode." (let ((this-frame (selected-frame)) (background win-switch-feedback-background-color) (foreground win-switch-feedback-foreground-color)) (unless (eq background (car win-switch-saved-mode-line-faces)) (set-face-background 'mode-line background this-frame)) (unless (eq foreground (cdr win-switch-saved-mode-line-faces)) (set-face-foreground 'mode-line foreground this-frame)))) (defun win-switch-off-feedback () "Provide visual feedback for the end of window switching mode." (if win-switch-off-feedback-function (funcall win-switch-off-feedback-function) (win-switch-off-alert) (win-switch-off-feedback-mode-line) (setq win-switch-saved-mode-line-faces nil))) (defun win-switch-off-feedback-mode-line () "Provide feedback via mode-line colors for end of window switching mode. This function only changes the mode-line foreground and background in the *current frame*, and only if `win-switch-saved-mode-line-faces' is non-nil and contains different colors than the current settings." (when win-switch-saved-mode-line-faces (let ((this-frame (selected-frame)) (background (car win-switch-saved-mode-line-faces)) (foreground (cdr win-switch-saved-mode-line-faces))) (unless (eq background (face-attribute 'mode-line :background)) (set-face-background 'mode-line background this-frame)) (unless (eq foreground (face-attribute 'mode-line :foreground)) (set-face-foreground 'mode-line foreground this-frame))))) (defun win-switch-begin-override () "Engage window switching interface." (when (not win-switch-engaged) (condition-case err-val (run-hooks 'win-switch-on-hook) (error (message "win-switch encountered error (%s) in on hook" err-val))) (win-switch--override-map overriding-local-map) (setq win-switch-engaged t) (win-switch--start-timer win-switch-idle-time 'win-switch-exit-by-timeout))) (defun win-switch-end-override () "Disengage window switching interface." (when win-switch-engaged (win-switch--clear-timer) (setq win-switch-engaged nil) (win-switch--restore-map overriding-local-map) (condition-case err-val (run-hooks 'win-switch-off-hook) (error (message "win-switch encountered error (%s) in off hook" err-val))))) (defun win-switch-enter () "Enter window switching mode with feedback." (interactive) (win-switch-begin-override) (when win-switch-provide-visual-feedback (win-switch-on-feedback))) (defun win-switch-exit () "Exit window switching mode with feedback." (interactive) (win-switch-end-override) (when win-switch-provide-visual-feedback (win-switch-off-feedback))) (defun win-switch-exit-by-timeout () "Exit window switching mode when timer expires. Because an idle timeout necessarily occurs when Emacs is waiting for an input event, with a stored copy of the active keymaps, ending the override does not take effect until *after* the next command. This functions issues an exit command event." (interactive) (when win-switch-engaged (let ((key (car win-switch-exit-keys))) (setq unread-command-events (append (listify-key-sequence key) unread-command-events))))) (defun win-switch-emergency-exit () "Exit from window switching mode without niceties." (interactive) (let ((win-switch-off-hook nil)) (win-switch-end-override) (win-switch-off-alert))) (defun win-switch-exit-and-redo () "End window switching mode and re-execute the last key event." (interactive) (let ((my-keys (this-command-keys-vector))) (win-switch-exit) (setq unread-command-events (append (listify-key-sequence my-keys) unread-command-events)))) ;; (@* "Customization Initializers and Option Setters") (defun win-switch-clear-from-keylists (key-list) "Remove each key in KEY-LIST from all predefined keylists. This is used to keep the predefined keylists synchronized with updates, additions, and deletions. KEY-LIST should be a list of keybindings. The predefined keylists are the customizable variables listed as the keys in `win-switch-commands'. No keymap changes are made by this function." (let ((keyvars (mapcar 'car win-switch-commands))) (dolist (keyvar keyvars) (let ((keys (symbol-value keyvar))) (dolist (key key-list) (when (member key keys) (set keyvar (delete key keys)))))))) ;;;###autoload (defun win-switch-custom-set-keys (key-sym key-list) "Set specified key list and adjust `win-switch-map' and other key lists. This is the most general function for changing the bindings of commands represented in the predefined key lists. See also `win-switch-set-keys', which is the API entry point to this function, along with `win-switch-add-key' and `win-switch-delete-key'. Note that changing `win-switch-map' directly need not be reliable, and also see `win-switch-define-key' for a safe way to make general bindings. KEY-SYM is a symbol that must be represented in the alist `win-switch-commands'. The corresponding list of keys will be set to KEY-LIST, which should be a list of keybindings, and each key in the list will be bound to the corresponding commands. An error is raised when trying to empty the exit list." (let ((cmd (cdr (assq key-sym win-switch-commands)))) (unless cmd (error "Symbol %s is not a valid win-switch key list" key-sym)) (when (and (null key-list) (eq key-sym 'win-switch-exit-keys)) (error "The exit keys list for win-switch must remain non-empty")) ;; Remove old bindings for the command before rebinding ;; | ATTN: Could use ;; | (substitute-key-definition cmd 'win-switch-exit-and-redo ;; | win-switch-map) ;; | here but that would also clear user bindings made directly by ;; | user and not in the corresponding key list. It might still be ;; | a good idea, though, but now keeping key lists in better sync. (dolist (oldkey (symbol-value key-sym)) (when (eq cmd (lookup-key win-switch-map oldkey)) (define-key win-switch-map oldkey 'win-switch-exit-and-redo))) ;; Remove keys in key-list from all key lists to keep lists in sync (win-switch-clear-from-keylists key-list) ;; Set new keylist and rebind (set key-sym key-list) (dolist (newkey key-list) (define-key win-switch-map newkey cmd))) (setq win-switch-map (win-switch--fix-keymap-defaults win-switch-map)) key-list) (defun win-switch-name-to-command-sym (name) "Convert abbreviated command name to a key list symbol. NAME can be either a symbol or a string, and can be either a full name of the form win-switch--keys or just the component, where for example is up, down, and so forth. Returns the associated win-switch--keys symbol." (let* ((name-str (if (stringp name) name (symbol-name name))) (name-sym (if (symbolp name) name (intern name)))) (if (assoc name-sym win-switch-commands) name-sym (intern (format "win-switch-%s-keys" name-str))))) ;;;###autoload (defun win-switch-set-keys (key-list name) "Bind specified keys to a command and adjust `win-switch-map'. KEY-LIST is a list of keys, each acceptable to `define-key'. NAME is either a string or a symbol, either of the form win-switch--keys or just , where is one of `up', `down', `left', `right', `next-window', `previous-window', `enlarge-vertically', `shrink-vertically', `enlarge-horizontally', `shrink-horizontally', `other-frame', `exit', `split-vertically', `split-horizontally', `delete-window', or `emergency-exit'. (Specifically, it must be on of the keys in the alist `win-switch-commands'.)" (interactive "XList of Key Sequences: \nSSet to win-switch command name: ") (win-switch-custom-set-keys (win-switch-name-to-command-sym name) key-list)) ;;;###autoload (defun win-switch-add-key (key name) "Add KEY to command list associated with NAME. KEY is a key binding in any form acceptable to `define-key'. NAME should be a symbol or string for which the variable `win-switch-NAME-keys' is defined." (interactive "KKey sequence: \nSAdd key %s to win-switch command name: ") (let* ((sym (win-switch-name-to-command-sym name)) (cmd (cdr (assoc sym win-switch-commands))) (lst (and cmd (symbol-value sym)))) (unless cmd (error "%s is not a valid win-switch key type" name)) (if (member key lst) lst ;; ATTN: Should probably use win-switch-custom-set-keys here instead ;; of what follows, even though it would do extra work. E.g., ;; (win-switch-custom-set-keys sym (cons key lst)) (define-key win-switch-map key cmd) (win-switch-clear-from-keylists (list key)) (set sym (cons key lst)))) (setq win-switch-map (win-switch--fix-keymap-defaults win-switch-map))) ;;;###autoload (defun win-switch-delete-key (key name) "Remove KEY from command list associated with NAME. NAME should be a symbol or string for which the variable `win-switch-NAME-keys' is defined. KEY is a key binding in any form acceptable to `define-key'. Removing the last exit key raises an error, and the last of any other key prompts an alert message." (interactive "KKey sequence: \nSDelete key %s from win-switch command name: ") (let* ((sym (win-switch-name-to-command-sym name)) (cmd (cdr (assoc sym win-switch-commands))) (lst (and cmd (symbol-value sym)))) (unless cmd (error "%s is not a valid win-switch key type" name)) (unless (member key lst) (error "Key %s is not associated with command %s" key name)) (when (null (cdr lst)) ; only one key remaining to be deleted (if (eq name 'exit) (error "The exit keys list for win-switch must remain non-empty") (message "Removing last key from command list win-switch-%s-keys" name))) (define-key win-switch-map key 'win-switch-exit-and-redo) (set sym (delete key lst)) (setq win-switch-map (win-switch--fix-keymap-defaults win-switch-map)))) ;;;###autoload (defun win-switch-define-key (key def &optional force-no-default) "Safely bind KEY to DEF in win-switch keymap. Attempting to bind the last exit key raises an error. KEY and DEF are a keybinding and definition, respectively, as would be acceptable to `define-key'. If DEF is a keymap, ensure that keymap (and all sub-keymaps) have an exit-inducing default (`win-switch-exit-and-redo'), unless FORCE-NO-DEFAULT is non-nil." (interactive "KKey sequence: \nCSet key %s to command: ") (when (and (null (cdr win-switch-exit-keys)) ; length <= 1 (member key win-switch-exit-keys) (not (eq def 'win-switch-exit))) (error "The exit keys list for win-switch must remain non-empty")) (win-switch-clear-from-keylists (list key)) (define-key win-switch-map key def) (unless force-no-default (setq win-switch-map (win-switch--fix-keymap-defaults win-switch-map)))) ;; Ideally, setting the once keys would be integrated into key setting ;; interface for the regular commands. But this would add nontrivial ;; complexity for limited gain, so we will just handle these keys ;; separately, with only one entry point. ;;;###autoload (defun win-switch-set-once-keys (key-list name) "Bind specified keys to a once-only command and adjust `win-switch-once-map'. KEY-LIST is a list of keys, each acceptable to `define-key'. NAME is either a string or a symbol, either of the form win-switch-once--keys or just , where is one of `double-next' or `double-prev'. (Specifically, it must be on of the keys in the alist `win-switch-once-commands'.)" (interactive "XList of Key Sequences: \nSSet to win-switch-once command name: ") (let* ((name-str (if (stringp name) name (symbol-name name))) (name-sym (if (symbolp name) name (intern name))) (cmd-list (append win-switch-commands win-switch-once-commands nil)) (cmdpair (or (assoc name-sym cmd-list) (assoc (intern (format "win-switch-%s-keys" name-str)) cmd-list))) (keysym (car cmdpair)) (cmd (cdr cmdpair))) (unless cmd (error "%s is not a valid win-switch-once-command" name)) (unless (or (eq keysym 'win-switch-exit-keys) (eq keysym 'win-switch-emergency-exit-keys)) (dolist (key (symbol-value keysym)) (define-key win-switch-once-map key nil)) (set keysym key-list) (dolist (key key-list) (define-key win-switch-once-map key cmd))))) ;;;###autoload (defun win-switch-set-wrap-around (&optional wrap) "Toggle or set window wrapping behavior. When WRAP is nil, toggle setting of `win-switch-wrap-around'. Otherwise, WRAP should be an integer, negative to turn off wrapping and non-negative to turn it on. (Value t also turns it on for convenience.) This function synchronizes `windmove-wrap-around' accordingly." (interactive "p") (setq win-switch-wrap-around (cond ((integerp wrap) (if (< wrap 0) nil t)) ((eq wrap t) t) (t (not win-switch-wrap-around)))) (setq windmove-wrap-around win-switch-wrap-around)) ;; (@* "User Entry Points") ;;;###autoload (defun win-switch-dispatch (&optional must-enter-or-prefix) "Enter window switching mode, or select next window in the frame. If the variable `win-switch-other-window-first' is non-nil, then `win-switch-next-window' is called *before* entering window switching mode. The variable `win-switch-window-threshold' determines both the switching behavior and how this function interprets its argument. * When `win-switch-window-threshold' is less than or equal to 0, window switching mode is always entered and the argument MUST-ENTER-OR-PREFIX is interpreted as a raw format prefix argument for any calls to `win-switch-next-window'. This only matters if `win-switch-other-window-first' is (or returns) a non-nil value. * When `win-switch-window-threshold' is greater than 0, entry to window switching mode occurs if either the number of windows in the current frame is above `win-switch-window-threshold' or if the prefix argument (MUST-ENTER-OR-PREFIX) is non-nil. Any calls to `win-switch-next-window' determined by the configuration are still made, and they are given nil as an argument. If window switching mode is not entered, `win-switch-next-window' is called. While more complicated than ideal, this dichotomy gives maximum flexibility for several common use cases." (interactive "P") (let* ((abort (run-hook-with-args-until-success win-switch-abort-hook)) (enter (and (not abort) (or must-enter-or-prefix (win-switch--enough-windows-p)))) (arg (and (<= win-switch-window-threshold 0) must-enter-or-prefix))) (when (and (not (eq abort 'abort)) (or (and win-switch-other-window-first (if (functionp win-switch-other-window-first) (funcall win-switch-other-window-first) t)) (not enter))) (win-switch-next-window arg)) (when enter (win-switch-enter)))) ;;;###autoload (defalias 'win-switch-mode 'win-switch-dispatch) ;;;###autoload (defmacro win-switch-dispatch-with (command &optional force-enter) ; would prefer a closure here "Produce a command to execute COMMAND and then `win-switch-dispatch'. COMMAND can be a (quoted) symbol, a lambda form, or a variable bound to a function. FORCE-ENTER, if non-nil, forces `win-switch-dispatch' to enter window switching mode. Note that the call to COMMAND replaces any automatic calls to `win-switch-next-window' or `win-switch-other-window-function' during dispatch." `(lambda (&optional arg) (interactive "P") (let ((win-switch-other-window-first t) (win-switch-other-window-function ,command) ,@(if force-enter (list '(win-switch-window-threshold 0)) nil)) (win-switch-dispatch arg)))) ;;;###autoload (defun win-switch-dispatch-once (&optional arg) "Prefix command to execute one window-switching operation. This command does not enter window-switching mode, nor does it require an exit. Except for the exit commands, which are excluded, the commands and keys are shared with `win-switch-dispatch'. In addition, this includes commands to move forward and backward by two windows. See `win-shift-once-double-next-keys' and `win-shift-once-double-prev-keys' for the associated keys. Taken together, these bindings make it convenient to use a single key sequence to navigate conveniently with up to five windows." (interactive "P") (let* ((keys (read-key-sequence-vector nil t)) (cmd (lookup-key win-switch-once-map keys))) (if cmd (call-interactively cmd) (message "%s is undefined" (key-description (this-command-keys-vector)))))) ;; (@* "Pre-defined Configurations") ;;;###autoload (defun win-switch-remove-split-and-delete-keys () "Eliminate window-splitting and deleting keys from win-switch mode." (win-switch-set-keys '() 'split-horizontally) (win-switch-set-keys '() 'split-vertically) (win-switch-set-keys '() 'delete-window)) ;;;###autoload (defun win-switch-setup-keys-ijkl (&rest dispatch-keys) "Restore default key commands and bind global dispatch keys. Under this setup, keys i, j, k, and l will switch windows, respectively, up, left, down, and right, with other functionality bound to nearby keys. The arguments DISPATCH-KEYS, if non-nil, should be a list of keys that will be bound globally to `win-switch-dispatch'." (interactive) (win-switch-set-keys '("i") 'up) (win-switch-set-keys '("k") 'down) (win-switch-set-keys '("j") 'left) (win-switch-set-keys '("l") 'right) (win-switch-set-keys '("o") 'next-window) (win-switch-set-keys '("p") 'previous-window) (win-switch-set-keys '("I") 'enlarge-vertically) (win-switch-set-keys '("K") 'shrink-vertically) (win-switch-set-keys '("J") 'shrink-horizontally) (win-switch-set-keys '("L") 'enlarge-horizontally) (win-switch-set-keys '(" ") 'other-frame) (win-switch-set-keys '("u" [return]) 'exit) (win-switch-set-keys '(";") 'split-horizontally) (win-switch-set-keys '("h") 'split-vertically) ; visual not letter mnemonic (win-switch-set-keys '("0") 'delete-window) (win-switch-set-keys '("\M-\C-g") 'emergency-exit) (dolist (key dispatch-keys) (global-set-key key 'win-switch-dispatch))) ;;;###autoload (defun win-switch-setup-keys-ijkl-minimal (&rest dispatch-keys) "Restore default key commands and bind global dispatch keys. Split and delete keys are excluded from the map for simplicity. Under this setup, keys i, j, k, and l will switch windows, respectively, up, left, down, and right, with other functionality bound to nearby keys. The arguments DISPATCH-KEYS, if non-nil, should be a list of keys that will be bound globally to `win-switch-dispatch'." (interactive) (apply 'win-switch-setup-keys-ijkl dispatch-keys) (win-switch-remove-split-and-delete-keys)) ;;;###autoload (defalias 'win-switch-setup-keys-default 'win-switch-setup-keys-ijkl) ;;;###autoload (defun win-switch-setup-keys-arrows (modifier &rest dispatch-keys) "Set arrow keys as both dispatch and direction control. Under this setup, pressing an arrow key with MODIFIER does a window switch in the corresponding direction and then calls `win-switch-dispatch'. When window-switching mode is engaged, the arrow keys continue to switch windows in the corresponding direction, with all the other functionality bound to nearby keys. MODIFIER is a symbol, one of control, meta, alt, hyper, super but *not* shift, which is used for enlarging. The arguments DISPATCH-KEYS, if non-nil, should be a list of keys that will be bound globally to `win-switch-dispatch'." (interactive "SModifier symbol: ") (when (eq modifier 'shift) (error "The shift modifier cannot be used for dispatch")) (win-switch-set-keys '([up]) 'up) (win-switch-set-keys '([down]) 'down) (win-switch-set-keys '([left]) 'left) (win-switch-set-keys '([right]) 'right) (win-switch-set-keys '("/") 'next-window) (win-switch-set-keys '(".") 'previous-window) (win-switch-set-keys '([(shift up)]) 'enlarge-vertically) (win-switch-set-keys '([(shift down)]) 'shrink-vertically) (win-switch-set-keys '([(shift left)]) 'shrink-horizontally) (win-switch-set-keys '([(shift right)]) 'enlarge-horizontally) (win-switch-set-keys '(" ") 'other-frame) (win-switch-set-keys '([return]) 'exit) (win-switch-set-keys '(";") 'split-horizontally) (win-switch-set-keys '("'") 'split-vertically) (win-switch-set-keys '("l") 'delete-window) (win-switch-set-keys '("\M-\C-g") 'emergency-exit) (dolist (key dispatch-keys) (global-set-key key 'win-switch-dispatch)) (global-set-key (vector (list modifier 'up)) (win-switch-dispatch-with 'win-switch-up)) (global-set-key (vector (list modifier 'down)) (win-switch-dispatch-with 'win-switch-down)) (global-set-key (vector (list modifier 'left)) (win-switch-dispatch-with 'win-switch-left)) (global-set-key (vector (list modifier 'right)) (win-switch-dispatch-with 'win-switch-right))) ;;;###autoload (defun win-switch-setup-keys-arrow-ctrl (&rest dispatch-keys) "Set arrow keys as both dispatch (w/control modifer) and direction. With a control modifier, Each arrow key causes a window switch in the corresponding direction and engages window-switching mode if the configuration parameters indicate so. When window-switching mode is engaged, the arrow keys theh continue to switch windows in the corresponding direction. The arguments DISPATCH-KEYS, if non-nil, should be a list of keys that will be bound globally to `win-switch-dispatch'." (interactive) (apply 'win-switch-setup-keys-arrows (cons 'control dispatch-keys))) ;;;###autoload (defun win-switch-setup-keys-arrow-meta (&rest dispatch-keys) "Set arrow keys as both dispatch (w/meta modifer) and direction. With a meta modifier, Each arrow key causes a window switch in the corresponding direction and engages window-switching mode if the configuration parameters indicate so. When window-switching mode is engaged, the arrow keys theh continue to switch windows in the corresponding direction. The arguments DISPATCH-KEYS, if non-nil, should be a list of keys that will be bound globally to `win-switch-dispatch'." (interactive) (apply 'win-switch-setup-keys-arrows (cons 'meta dispatch-keys))) ;;;###autoload (defun win-switch-setup-keys-esdf (&rest dispatch-keys) "Set left-handed keys mirroring defaults and bind global dispatch keys. Under this setup, keys e, s, d, and f will switch windows, respectively, up, left, down, and right, with other functionality bound to nearby keys. The arguments DISPATCH-KEYS, if non-nil, should be a list of keys that will be bound globally to `win-switch-dispatch'." (interactive) (win-switch-set-keys '("e") 'up) (win-switch-set-keys '("d") 'down) (win-switch-set-keys '("s") 'left) (win-switch-set-keys '("f") 'right) (win-switch-set-keys '("w") 'next-window) (win-switch-set-keys '("q") 'previous-window) (win-switch-set-keys '("E") 'enlarge-vertically) (win-switch-set-keys '("D") 'shrink-vertically) (win-switch-set-keys '("S") 'shrink-horizontally) (win-switch-set-keys '("F") 'enlarge-horizontally) (win-switch-set-keys '(" ") 'other-frame) (win-switch-set-keys '("r" [return]) 'exit) (win-switch-set-keys '("3") 'split-horizontally) (win-switch-set-keys '("2") 'split-vertically) (win-switch-set-keys '("4") 'delete-window) (win-switch-set-keys '("\M-\C-g") 'emergency-exit) (dolist (key dispatch-keys) (global-set-key key 'win-switch-dispatch))) ;;;###autoload (defun win-switch-authors-configuration () "Win-switch configuration previously preferred by the package author." (interactive) ;; Perhaps even a little shorter... (setq win-switch-idle-time 0.7) ;; For two windows, we can always use C-u to force entry (setq win-switch-window-threshold 2) ;; With more than 3 windows, doing an other-window first can be ;; confusing -- easier to just move where you want to go. ;; But with 3 or fewer windows, it's much more efficient. (setq win-switch-other-window-first (lambda () (null (nthcdr 3 (window-list))))) ;; Mode-line visual feedback is a potent cue (setq win-switch-provide-visual-feedback t) (setq win-switch-feedback-background-color "red") (setq win-switch-feedback-foreground-color "white") ;; No special functions, though icicles remaps other-window ;; which gets used here and whose argument is respected (setq win-switch-on-feedback-function nil) (setq win-switch-off-feedback-function nil) (setq win-switch-other-window-function nil) ;; Wrap around makes things easier (win-switch-set-wrap-around 1) ;; These two keys get easily confused when typing quickly ;; so it's easiest to not have to be too precise. ;; I just use both for window switching. (win-switch-setup-keys-ijkl "\C-xo" "\C-x\C-o")) ;;;###autoload (defun win-switch-authors-new-configuration () "Win-switch configuration currently preferred by the package author." (interactive) ;; Perhaps even a little shorter... (setq win-switch-idle-time 0.7) ;; I've gotten used to using it even with two windows (setq win-switch-window-threshold 1) ;; I've gotten used to just moving into the mode and going from there. (setq win-switch-other-window-first nil) ;; Mode-line visual feedback is a potent cue (setq win-switch-provide-visual-feedback t) (setq win-switch-feedback-background-color "red") (setq win-switch-feedback-foreground-color "white") ;; No special functions, though icicles remaps other-window ;; which gets used here and whose argument is respected (setq win-switch-on-feedback-function nil) (setq win-switch-off-feedback-function nil) (setq win-switch-other-window-function nil) ;; Wrap around makes things easier (win-switch-set-wrap-around 1) ;; These two keys get easily confused when typing quickly ;; so it's easiest to not have to be too precise. ;; I just use both for window switching. (win-switch-setup-keys-ijkl "\C-xo" "\C-x\C-o")) (run-hooks 'win-switch-load-hook) (provide 'win-switch) ;; Local Variables: ;; mode: emacs-lisp ;; End: ;;; win-switch.el ends here