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