| 1 | ;;; windmove.el --- directional window-selection routines |
| 2 | ;; |
| 3 | ;; Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, |
| 4 | ;; 2005, 2006, 2007, 2008 Free Software Foundation, Inc. |
| 5 | ;; |
| 6 | ;; Author: Hovav Shacham (hovav@cs.stanford.edu) |
| 7 | ;; Created: 17 October 1998 |
| 8 | ;; Keywords: window, movement, convenience |
| 9 | ;; |
| 10 | ;; This file is part of GNU Emacs. |
| 11 | ;; |
| 12 | ;; GNU Emacs is free software; you can redistribute it and/or modify |
| 13 | ;; it under the terms of the GNU General Public License as published by |
| 14 | ;; the Free Software Foundation; either version 3, or (at your option) |
| 15 | ;; any later version. |
| 16 | ;; |
| 17 | ;; GNU Emacs is distributed in the hope that it will be useful, |
| 18 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 19 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 20 | ;; GNU General Public License for more details. |
| 21 | ;; |
| 22 | ;; You should have received a copy of the GNU General Public License |
| 23 | ;; along with GNU Emacs; see the file COPYING. If not, write to the |
| 24 | ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
| 25 | ;; Boston, MA 02110-1301, USA. |
| 26 | ;; |
| 27 | ;; -------------------------------------------------------------------- |
| 28 | |
| 29 | ;;; Commentary: |
| 30 | ;; |
| 31 | ;; This package defines a set of routines, windmove-{left,up,right, |
| 32 | ;; down}, for selection of windows in a frame geometrically. For |
| 33 | ;; example, `windmove-right' selects the window immediately to the |
| 34 | ;; right of the currently-selected one. This functionality is similar |
| 35 | ;; to the window-selection controls of the BRIEF editor of yore. |
| 36 | ;; |
| 37 | ;; One subtle point is what happens when the window to the right has |
| 38 | ;; been split vertically; for example, consider a call to |
| 39 | ;; `windmove-right' in this setup: |
| 40 | ;; |
| 41 | ;; ------------- |
| 42 | ;; | | A | |
| 43 | ;; | | | |
| 44 | ;; | |----- |
| 45 | ;; | * | | (* is point in the currently |
| 46 | ;; | | B | selected window) |
| 47 | ;; | | | |
| 48 | ;; ------------- |
| 49 | ;; |
| 50 | ;; There are (at least) three reasonable things to do: |
| 51 | ;; (1) Always move to the window to the right of the top edge of the |
| 52 | ;; selected window; in this case, this policy selects A. |
| 53 | ;; (2) Always move to the window to the right of the bottom edge of |
| 54 | ;; the selected window; in this case, this policy selects B. |
| 55 | ;; (3) Move to the window to the right of point in the selected |
| 56 | ;; window. This may select either A or B, depending on the |
| 57 | ;; position of point; in the illustrated example, it would select |
| 58 | ;; B. |
| 59 | ;; |
| 60 | ;; Similar issues arise for all the movement functions. Windmove |
| 61 | ;; resolves this problem by allowing the user to specify behavior |
| 62 | ;; through a prefix argument. The cases are thus: |
| 63 | ;; * if no argument is given to the movement functions, or the |
| 64 | ;; argument given is zero, movement is relative to point; |
| 65 | ;; * if a positive argument is given, movement is relative to the top |
| 66 | ;; or left edge of the selected window, depending on whether the |
| 67 | ;; movement is to be horizontal or vertical; |
| 68 | ;; * if a negative argument is given, movement is relative to the |
| 69 | ;; bottom or right edge of the selected window, depending on whether |
| 70 | ;; the movement is to be horizontal or vertical. |
| 71 | ;; |
| 72 | ;; |
| 73 | ;; Another feature enables wrap-around mode when the variable |
| 74 | ;; `windmove-wrap-around' is set to a non-nil value. In this mode, |
| 75 | ;; movement that falls off the edge of the frame will wrap around to |
| 76 | ;; find the window on the opposite side of the frame. Windmove does |
| 77 | ;; the Right Thing about the minibuffer; for example, consider: |
| 78 | ;; |
| 79 | ;; ------------- |
| 80 | ;; | * | |
| 81 | ;; |-----------| |
| 82 | ;; | A | |
| 83 | ;; |-----------| (* is point in the currently |
| 84 | ;; | B | C | selected window) |
| 85 | ;; | | | |
| 86 | ;; ------------- |
| 87 | ;; |
| 88 | ;; With wraparound enabled, windmove-down will move to A, while |
| 89 | ;; windmove-up will move to the minibuffer if it is active, or to |
| 90 | ;; either B or C depending on the prefix argument. |
| 91 | ;; |
| 92 | ;; |
| 93 | ;; A set of default keybindings is supplied: shift-{left,up,right,down} |
| 94 | ;; invoke the corresponding Windmove function. See the installation |
| 95 | ;; section if you wish to use these keybindings. |
| 96 | |
| 97 | |
| 98 | ;; Installation: |
| 99 | ;; |
| 100 | ;; Put the following line in your `.emacs' file: |
| 101 | ;; |
| 102 | ;; (windmove-default-keybindings) ; shifted arrow keys |
| 103 | ;; |
| 104 | ;; or |
| 105 | ;; |
| 106 | ;; (windmove-default-keybindings 'hyper) ; etc. |
| 107 | ;; |
| 108 | ;; to use another modifier key. |
| 109 | ;; |
| 110 | ;; |
| 111 | ;; If you wish to enable wrap-around, also add a line like: |
| 112 | ;; |
| 113 | ;; (setq windmove-wrap-around t) |
| 114 | ;; |
| 115 | ;; |
| 116 | ;; Note: If you have an Emacs that manifests a bug that sometimes |
| 117 | ;; causes the occasional creation of a "lost column" between windows, |
| 118 | ;; so that two adjacent windows do not actually touch, you may want to |
| 119 | ;; increase the value of `windmove-window-distance-delta' to 2 or 3: |
| 120 | ;; |
| 121 | ;; (setq windmove-window-distance-delta 2) |
| 122 | ;; |
| 123 | |
| 124 | ;; Acknowledgements: |
| 125 | ;; |
| 126 | ;; Special thanks to Julian Assange (proff@iq.org), whose |
| 127 | ;; change-windows-intuitively.el predates Windmove, and provided the |
| 128 | ;; inspiration for it. Kin Cho (kin@symmetrycomm.com) was the first |
| 129 | ;; to suggest wrap-around behavior. Thanks also to Gerd Moellmann |
| 130 | ;; (gerd@gnu.org) for his comments and suggestions. |
| 131 | |
| 132 | ;;; Code: |
| 133 | |
| 134 | |
| 135 | ;; User configurable variables: |
| 136 | |
| 137 | ;; For customize ... |
| 138 | (defgroup windmove nil |
| 139 | "Directional selection of windows in a frame." |
| 140 | :prefix "windmove-" |
| 141 | :version "21.1" |
| 142 | :group 'windows |
| 143 | :group 'convenience) |
| 144 | |
| 145 | |
| 146 | (defcustom windmove-wrap-around nil |
| 147 | "Whether movement off the edge of the frame wraps around. |
| 148 | If this variable is set to t, moving left from the leftmost window in |
| 149 | a frame will find the rightmost one, and similarly for the other |
| 150 | directions. The minibuffer is skipped over in up/down movements if it |
| 151 | is inactive." |
| 152 | :type 'boolean |
| 153 | :group 'windmove) |
| 154 | |
| 155 | ;; If your Emacs sometimes places an empty column between two adjacent |
| 156 | ;; windows, you may wish to set this delta to 2. |
| 157 | (defcustom windmove-window-distance-delta 1 |
| 158 | "How far away from the current window to look for an adjacent window. |
| 159 | Measured in characters either horizontally or vertically; setting this |
| 160 | to a value larger than 1 may be useful in getting around window- |
| 161 | placement bugs in old versions of Emacs." |
| 162 | :type 'number |
| 163 | :group 'windmove) |
| 164 | |
| 165 | |
| 166 | |
| 167 | ;; Implementation overview: |
| 168 | ;; |
| 169 | ;; The conceptual framework behind this code is all fairly simple. We |
| 170 | ;; are on one window; we wish to move to another. The correct window |
| 171 | ;; to move to is determined by the position of point in the current |
| 172 | ;; window as well as the overall window setup. |
| 173 | ;; |
| 174 | ;; Early on, I made the decision to base my implementation around the |
| 175 | ;; built-in function `window-at'. This function takes a frame-based |
| 176 | ;; coordinate, and returns the window that contains it. Using this |
| 177 | ;; function, the job of the various top-level windmove functions can |
| 178 | ;; be decomposed: first, find the current frame-based location of |
| 179 | ;; point; second, manipulate it in some way to give a new location, |
| 180 | ;; that hopefully falls in the window immediately at left (or right, |
| 181 | ;; etc.); third, use `window-at' and `select-window' to select the |
| 182 | ;; window at that new location. |
| 183 | ;; |
| 184 | ;; This is probably not the only possible architecture, and it turns |
| 185 | ;; out to have some inherent cruftiness. (Well, okay, the third step |
| 186 | ;; is pretty clean....) We will consider each step in turn. |
| 187 | ;; |
| 188 | ;; A quick digression about coordinate frames: most of the functions |
| 189 | ;; in the windmove package deal with screen coordinates in one way or |
| 190 | ;; another. These coordinates are always relative to some reference |
| 191 | ;; points. Window-based coordinates have their reference point in the |
| 192 | ;; upper-left-hand corner of whatever window is being talked about; |
| 193 | ;; frame-based coordinates have their reference point in the |
| 194 | ;; upper-left-hand corner of the entire frame (of which the current |
| 195 | ;; window is a component). |
| 196 | ;; |
| 197 | ;; All coordinates are zero-based, which simply means that the |
| 198 | ;; reference point (whatever it is) is assigned the value (x=0, y=0). |
| 199 | ;; X-coordinates grow down the screen, and Y-coordinates grow towards |
| 200 | ;; the right of the screen. |
| 201 | ;; |
| 202 | ;; Okay, back to work. The first step is to gather information about |
| 203 | ;; the frame-based coordinates of point, or rather, the reference |
| 204 | ;; location. The reference location can be point, or the upper-left, |
| 205 | ;; or the lower-right corner of the window; the particular one used is |
| 206 | ;; controlled by the prefix argument to `windmove-left' and all the |
| 207 | ;; rest. |
| 208 | ;; |
| 209 | ;; This work is done by `windmove-reference-loc'. It can figure out |
| 210 | ;; the locations of the corners by calling `window-edges' combined |
| 211 | ;; with the result of `posn-at-point'. |
| 212 | ;; |
| 213 | ;; The second step is more messy. Conceptually, it is fairly simple: |
| 214 | ;; if we know the reference location, and the coordinates of the |
| 215 | ;; current window, we can "throw" our reference point just over the |
| 216 | ;; appropriate edge of the window, and see what other window is |
| 217 | ;; there. More explicitly, consider this example from the user |
| 218 | ;; documentation above. |
| 219 | ;; |
| 220 | ;; ------------- |
| 221 | ;; | | A | |
| 222 | ;; | | | |
| 223 | ;; | |----- |
| 224 | ;; | * | | (* is point in the currently |
| 225 | ;; | | B | selected window) |
| 226 | ;; | | | |
| 227 | ;; ------------- |
| 228 | ;; |
| 229 | ;; The asterisk marks the reference point; we wish to move right. |
| 230 | ;; Since we are moving horizontally, the Y coordinate of the new |
| 231 | ;; location will be the same. The X coordinate can be such that it is |
| 232 | ;; just past the edge of the present window. Obviously, the new point |
| 233 | ;; will be inside window B. This in itself is fairly simple: using |
| 234 | ;; the result of `windmove-reference-loc' and `window-edges', all the |
| 235 | ;; necessary math can be performed. (Having said that, there is a |
| 236 | ;; good deal of room for off-by-one errors, and Emacs 19.34, at least, |
| 237 | ;; sometimes manifests a bug where two windows don't actually touch, |
| 238 | ;; so a larger skip is required.) The actual math here is done by |
| 239 | ;; `windmove-other-window-loc'. |
| 240 | ;; |
| 241 | ;; But we can't just pass the result of `windmove-other-window-loc' to |
| 242 | ;; `window-at' directly. Why not? Suppose a move would take us off |
| 243 | ;; the edge of the screen, say to the left. We want to give a |
| 244 | ;; descriptive error message to the user. Or, suppose that a move |
| 245 | ;; would place us in the minibuffer. What if the minibuffer is |
| 246 | ;; inactive? |
| 247 | ;; |
| 248 | ;; Actually, the whole subject of the minibuffer edge of the frame is |
| 249 | ;; rather messy. It turns out that with a sufficiently large delta, |
| 250 | ;; we can fly off the bottom edge of the frame and miss the minibuffer |
| 251 | ;; altogther. This, I think, is never right: if there's a minibuffer |
| 252 | ;; and you're not in it, and you move down, the minibuffer should be |
| 253 | ;; in your way. |
| 254 | ;; |
| 255 | ;; (By the way, I'm not totally sure that the code does the right |
| 256 | ;; thing in really weird cases, like a frame with no minibuffer.) |
| 257 | ;; |
| 258 | ;; So, what we need is some ways to do constraining and such. The |
| 259 | ;; early versions of windmove took a fairly simplistic approach to all |
| 260 | ;; this. When I added the wrap-around option, those internals had to |
| 261 | ;; be rewritten. After a *lot* of futzing around, I came up with a |
| 262 | ;; two-step process that I think is general enough to cover the |
| 263 | ;; relevant cases. (I'm not totally happy with having to pass the |
| 264 | ;; window variable as deep as I do, but we can't have everything.) |
| 265 | ;; |
| 266 | ;; In the first phase, we make sure that the new location is sane. |
| 267 | ;; "Sane" means that we can only fall of the edge of the frame in the |
| 268 | ;; direction we're moving in, and that we don't miss the minibuffer if |
| 269 | ;; we're moving down and not already in the minibuffer. The function |
| 270 | ;; `windmove-constrain-loc-for-movement' takes care of all this. |
| 271 | ;; |
| 272 | ;; Then, we handle the wraparound, if it's enabled. The function |
| 273 | ;; `windmove-wrap-loc-for-movement' takes coordinate values (both X |
| 274 | ;; and Y) that fall off the edge of the frame, and replaces them with |
| 275 | ;; values on the other side of the frame. It also has special |
| 276 | ;; minibuffer-handling code again, because we want to wrap through the |
| 277 | ;; minibuffer if it's not enabled. |
| 278 | ;; |
| 279 | ;; So, that's it. Seems to work. All of this work is done by the fun |
| 280 | ;; function `windmove-find-other-window'. |
| 281 | ;; |
| 282 | ;; So, now we have a window to move to (or nil if something's gone |
| 283 | ;; wrong). The function `windmove-do-window-select' is the main |
| 284 | ;; driver function: it actually does the `select-window'. It is |
| 285 | ;; called by four little convenience wrappers, `windmove-left', |
| 286 | ;; `windmove-up', `windmove-right', and `windmove-down', which make |
| 287 | ;; for convenient keybinding. |
| 288 | |
| 289 | |
| 290 | ;; Quick & dirty utility function to add two (x . y) coords. |
| 291 | (defun windmove-coord-add (coord1 coord2) |
| 292 | "Add the two coordinates. |
| 293 | Both COORD1 and COORD2 are coordinate cons pairs, (HPOS . VPOS). The |
| 294 | result is another coordinate cons pair." |
| 295 | (cons (+ (car coord1) (car coord2)) |
| 296 | (+ (cdr coord1) (cdr coord2)))) |
| 297 | |
| 298 | |
| 299 | (defun windmove-constrain-to-range (n min-n max-n) |
| 300 | "Ensure that N is between MIN-N and MAX-N inclusive by constraining. |
| 301 | If N is less than MIN-N, return MIN-N; if greater than MAX-N, return |
| 302 | MAX-N." |
| 303 | (max min-n (min n max-n))) |
| 304 | |
| 305 | (defun windmove-constrain-around-range (n min-n max-n) |
| 306 | "Ensure that N is between MIN-N and MAX-N inclusive by wrapping. |
| 307 | If N is less than MIN-N, return MAX-N; if greater than MAX-N, return |
| 308 | MIN-N." |
| 309 | (cond |
| 310 | ((< n min-n) max-n) |
| 311 | ((> n max-n) min-n) |
| 312 | (t n))) |
| 313 | |
| 314 | (defun windmove-frame-edges (window) |
| 315 | "Return (X-MIN Y-MIN X-MAX Y-MAX) for the frame containing WINDOW. |
| 316 | If WINDOW is nil, return the edges for the selected frame. |
| 317 | \(X-MIN, Y-MIN) is the zero-based coordinate of the top-left corner |
| 318 | of the frame; (X-MAX, Y-MAX) is the zero-based coordinate of the |
| 319 | bottom-right corner of the frame. |
| 320 | For example, if a frame has 76 rows and 181 columns, the return value |
| 321 | from `windmove-frame-edges' will be the list (0 0 180 75)." |
| 322 | (let* ((frame (if window |
| 323 | (window-frame window) |
| 324 | (selected-frame))) |
| 325 | (top-left (window-edges (frame-first-window frame))) |
| 326 | (x-min (nth 0 top-left)) |
| 327 | (y-min (nth 1 top-left)) |
| 328 | (x-max (1- (frame-width frame))) ; 1- for last row & col |
| 329 | (y-max (1- (frame-height frame)))) |
| 330 | (list x-min y-min x-max y-max))) |
| 331 | |
| 332 | ;; it turns out that constraining is always a good thing, even when |
| 333 | ;; wrapping is going to happen. this is because: |
| 334 | ;; first, since we disallow exotic diagonal-around-a-corner type |
| 335 | ;; movements, so we can always fix the unimportant direction (the one |
| 336 | ;; we're not moving in). |
| 337 | ;; second, if we're moving down and we're not in the minibuffer, then |
| 338 | ;; constraining the y coordinate to max-y is okay, because if that |
| 339 | ;; falls in the minibuffer and the minibuffer isn't active, that y |
| 340 | ;; coordinate will still be off the bottom of the frame as the |
| 341 | ;; wrapping function sees it and so will get wrapped around anyway. |
| 342 | (defun windmove-constrain-loc-for-movement (coord window dir) |
| 343 | "Constrain COORD so that it is reasonable for the given movement. |
| 344 | This involves two things: first, make sure that the \"off\" coordinate |
| 345 | -- the one not being moved on, e.g., y for horizontal movement -- is |
| 346 | within frame boundaries; second, if the movement is down and we're not |
| 347 | moving from the minibuffer, make sure that the y coordinate does not |
| 348 | exceed the frame max-y, so that we don't overshoot the minibuffer |
| 349 | accidentally. WINDOW is the window that movement is relative to; DIR |
| 350 | is the direction of the movement, one of `left', `up', `right', |
| 351 | or `down'. |
| 352 | Returns the constrained coordinate." |
| 353 | (let ((frame-edges (windmove-frame-edges window)) |
| 354 | (in-minibuffer (window-minibuffer-p window))) |
| 355 | (let ((min-x (nth 0 frame-edges)) |
| 356 | (min-y (nth 1 frame-edges)) |
| 357 | (max-x (nth 2 frame-edges)) |
| 358 | (max-y (nth 3 frame-edges))) |
| 359 | (let ((new-x |
| 360 | (if (memq dir '(up down)) ; vertical movement |
| 361 | (windmove-constrain-to-range (car coord) min-x max-x) |
| 362 | (car coord))) |
| 363 | (new-y |
| 364 | (if (or (memq dir '(left right)) ; horizontal movement |
| 365 | (and (eq dir 'down) |
| 366 | (not in-minibuffer))) ; don't miss minibuffer |
| 367 | ;; (technically, we shouldn't constrain on min-y in the |
| 368 | ;; second case, but this shouldn't do any harm on a |
| 369 | ;; down movement.) |
| 370 | (windmove-constrain-to-range (cdr coord) min-y max-y) |
| 371 | (cdr coord)))) |
| 372 | (cons new-x new-y))))) |
| 373 | |
| 374 | ;; having constrained in the limited sense of windmove-constrain-loc- |
| 375 | ;; for-movement, the wrapping code is actually much simpler than it |
| 376 | ;; otherwise would be. the only complication is that we need to check |
| 377 | ;; if the minibuffer is active, and, if not, pretend that it's not |
| 378 | ;; even part of the frame. |
| 379 | (defun windmove-wrap-loc-for-movement (coord window dir) |
| 380 | "Takes the constrained COORD and wraps it around for the movement. |
| 381 | This makes an out-of-range x or y coordinate and wraps it around the |
| 382 | frame, giving a coordinate (hopefully) in the window on the other edge |
| 383 | of the frame. WINDOW is the window that movement is relative to (nil |
| 384 | means the currently selected window); DIR is the direction of the |
| 385 | movement, one of `left', `up', `right',or `down'. |
| 386 | Returns the wrapped coordinate." |
| 387 | (let* ((frame-edges (windmove-frame-edges window)) |
| 388 | (frame-minibuffer (minibuffer-window (if window |
| 389 | (window-frame window) |
| 390 | (selected-frame)))) |
| 391 | (minibuffer-active (minibuffer-window-active-p |
| 392 | frame-minibuffer))) |
| 393 | (let ((min-x (nth 0 frame-edges)) |
| 394 | (min-y (nth 1 frame-edges)) |
| 395 | (max-x (nth 2 frame-edges)) |
| 396 | (max-y (if (not minibuffer-active) |
| 397 | (- (nth 3 frame-edges) |
| 398 | (window-height frame-minibuffer)) |
| 399 | (nth 3 frame-edges)))) |
| 400 | (cons |
| 401 | (windmove-constrain-around-range (car coord) min-x max-x) |
| 402 | (windmove-constrain-around-range (cdr coord) min-y max-y))))) |
| 403 | |
| 404 | |
| 405 | ;; This calculates the reference location in the current window: the |
| 406 | ;; frame-based (x . y) of either point, the top-left, or the |
| 407 | ;; bottom-right of the window, depending on ARG. |
| 408 | (defun windmove-reference-loc (&optional arg window) |
| 409 | "Return the reference location for directional window selection. |
| 410 | Return a coordinate (HPOS . VPOS) that is frame-based. If ARG is nil |
| 411 | or not supplied, the reference point is the buffer's point in the |
| 412 | currently-selected window, or WINDOW if supplied; otherwise, it is the |
| 413 | top-left or bottom-right corner of the selected window, or WINDOW if |
| 414 | supplied, if ARG is greater or smaller than zero, respectively." |
| 415 | (let ((effective-arg (if (null arg) 0 (prefix-numeric-value arg))) |
| 416 | (edges (window-inside-edges window))) |
| 417 | (let ((top-left (cons (nth 0 edges) |
| 418 | (nth 1 edges))) |
| 419 | ;; Subtracting 1 converts the edge to the last column or line |
| 420 | ;; within the window. |
| 421 | (bottom-right (cons (- (nth 2 edges) 1) |
| 422 | (- (nth 3 edges) 1)))) |
| 423 | (cond |
| 424 | ((> effective-arg 0) |
| 425 | top-left) |
| 426 | ((< effective-arg 0) |
| 427 | bottom-right) |
| 428 | ((= effective-arg 0) |
| 429 | (windmove-coord-add |
| 430 | top-left |
| 431 | (let ((col-row |
| 432 | (posn-col-row |
| 433 | (posn-at-point (window-point window) window)))) |
| 434 | (cons (- (car col-row) (window-hscroll window)) |
| 435 | (cdr col-row))))))))) |
| 436 | |
| 437 | ;; This uses the reference location in the current window (calculated |
| 438 | ;; by `windmove-reference-loc' above) to find a reference location |
| 439 | ;; that will hopefully be in the window we want to move to. |
| 440 | (defun windmove-other-window-loc (dir &optional arg window) |
| 441 | "Return a location in the window to be moved to. |
| 442 | Return value is a frame-based (HPOS . VPOS) value that should be moved |
| 443 | to. DIR is one of `left', `up', `right', or `down'; an optional ARG |
| 444 | is handled as by `windmove-reference-loc'; WINDOW is the window that |
| 445 | movement is relative to." |
| 446 | (let ((edges (window-edges window)) ; edges: (x0, y0, x1, y1) |
| 447 | (refpoint (windmove-reference-loc arg window))) ; (x . y) |
| 448 | (cond |
| 449 | ((eq dir 'left) |
| 450 | (cons (- (nth 0 edges) |
| 451 | windmove-window-distance-delta) |
| 452 | (cdr refpoint))) ; (x0-d, y) |
| 453 | ((eq dir 'up) |
| 454 | (cons (car refpoint) |
| 455 | (- (nth 1 edges) |
| 456 | windmove-window-distance-delta))) ; (x, y0-d) |
| 457 | ((eq dir 'right) |
| 458 | (cons (+ (1- (nth 2 edges)) ; -1 to get actual max x |
| 459 | windmove-window-distance-delta) |
| 460 | (cdr refpoint))) ; (x1+d-1, y) |
| 461 | ((eq dir 'down) ; -1 to get actual max y |
| 462 | (cons (car refpoint) |
| 463 | (+ (1- (nth 3 edges)) |
| 464 | windmove-window-distance-delta))) ; (x, y1+d-1) |
| 465 | (t (error "Invalid direction of movement: %s" dir))))) |
| 466 | |
| 467 | (defun windmove-find-other-window (dir &optional arg window) |
| 468 | "Return the window object in direction DIR. |
| 469 | DIR, ARG, and WINDOW are handled as by `windmove-other-window-loc'." |
| 470 | (let* ((actual-current-window (or window (selected-window))) |
| 471 | (raw-other-window-loc |
| 472 | (windmove-other-window-loc dir arg actual-current-window)) |
| 473 | (constrained-other-window-loc |
| 474 | (windmove-constrain-loc-for-movement raw-other-window-loc |
| 475 | actual-current-window |
| 476 | dir)) |
| 477 | (other-window-loc |
| 478 | (if windmove-wrap-around |
| 479 | (windmove-wrap-loc-for-movement constrained-other-window-loc |
| 480 | actual-current-window |
| 481 | dir) |
| 482 | constrained-other-window-loc))) |
| 483 | (window-at (car other-window-loc) |
| 484 | (cdr other-window-loc)))) |
| 485 | |
| 486 | |
| 487 | ;; Selects the window that's hopefully at the location returned by |
| 488 | ;; `windmove-other-window-loc', or screams if there's no window there. |
| 489 | (defun windmove-do-window-select (dir &optional arg window) |
| 490 | "Move to the window at direction DIR. |
| 491 | DIR, ARG, and WINDOW are handled as by `windmove-other-window-loc'. |
| 492 | If no window is at direction DIR, an error is signaled." |
| 493 | (let ((other-window (windmove-find-other-window dir arg window))) |
| 494 | (cond ((null other-window) |
| 495 | (error "No window %s from selected window" dir)) |
| 496 | ((and (window-minibuffer-p other-window) |
| 497 | (not (minibuffer-window-active-p other-window))) |
| 498 | (error "Minibuffer is inactive")) |
| 499 | (t |
| 500 | (select-window other-window))))) |
| 501 | |
| 502 | |
| 503 | ;;; end-user functions |
| 504 | ;; these are all simple interactive wrappers to `windmove-do- |
| 505 | ;; window-select', meant to be bound to keys. |
| 506 | |
| 507 | ;;;###autoload |
| 508 | (defun windmove-left (&optional arg) |
| 509 | "Select the window to the left of the current one. |
| 510 | With no prefix argument, or with prefix argument equal to zero, |
| 511 | \"left\" is relative to the position of point in the window; otherwise |
| 512 | it is relative to the top edge (for positive ARG) or the bottom edge |
| 513 | \(for negative ARG) of the current window. |
| 514 | If no window is at the desired location, an error is signaled." |
| 515 | (interactive "P") |
| 516 | (windmove-do-window-select 'left arg)) |
| 517 | |
| 518 | ;;;###autoload |
| 519 | (defun windmove-up (&optional arg) |
| 520 | "Select the window above the current one. |
| 521 | With no prefix argument, or with prefix argument equal to zero, \"up\" |
| 522 | is relative to the position of point in the window; otherwise it is |
| 523 | relative to the left edge (for positive ARG) or the right edge (for |
| 524 | negative ARG) of the current window. |
| 525 | If no window is at the desired location, an error is signaled." |
| 526 | (interactive "P") |
| 527 | (windmove-do-window-select 'up arg)) |
| 528 | |
| 529 | ;;;###autoload |
| 530 | (defun windmove-right (&optional arg) |
| 531 | "Select the window to the right of the current one. |
| 532 | With no prefix argument, or with prefix argument equal to zero, |
| 533 | \"right\" is relative to the position of point in the window; |
| 534 | otherwise it is relative to the top edge (for positive ARG) or the |
| 535 | bottom edge (for negative ARG) of the current window. |
| 536 | If no window is at the desired location, an error is signaled." |
| 537 | (interactive "P") |
| 538 | (windmove-do-window-select 'right arg)) |
| 539 | |
| 540 | ;;;###autoload |
| 541 | (defun windmove-down (&optional arg) |
| 542 | "Select the window below the current one. |
| 543 | With no prefix argument, or with prefix argument equal to zero, |
| 544 | \"down\" is relative to the position of point in the window; otherwise |
| 545 | it is relative to the left edge (for positive ARG) or the right edge |
| 546 | \(for negative ARG) of the current window. |
| 547 | If no window is at the desired location, an error is signaled." |
| 548 | (interactive "P") |
| 549 | (windmove-do-window-select 'down arg)) |
| 550 | |
| 551 | |
| 552 | ;;; set up keybindings |
| 553 | ;; Idea for this function is from iswitchb.el, by Stephen Eglen |
| 554 | ;; (stephen@cns.ed.ac.uk). |
| 555 | ;; I don't think these bindings will work on non-X terminals; you |
| 556 | ;; probably want to use different bindings in that case. |
| 557 | |
| 558 | ;;;###autoload |
| 559 | (defun windmove-default-keybindings (&optional modifier) |
| 560 | "Set up keybindings for `windmove'. |
| 561 | Keybindings are of the form MODIFIER-{left,right,up,down}. |
| 562 | Default MODIFIER is 'shift." |
| 563 | (interactive) |
| 564 | (unless modifier (setq modifier 'shift)) |
| 565 | (global-set-key (vector (list modifier 'left)) 'windmove-left) |
| 566 | (global-set-key (vector (list modifier 'right)) 'windmove-right) |
| 567 | (global-set-key (vector (list modifier 'up)) 'windmove-up) |
| 568 | (global-set-key (vector (list modifier 'down)) 'windmove-down)) |
| 569 | |
| 570 | |
| 571 | (provide 'windmove) |
| 572 | |
| 573 | ;;; arch-tag: 56267432-bf1a-4296-a9a0-85c6bd9f2375 |
| 574 | ;;; windmove.el ends here |