27545
|
1 ;; windmove.el -- directional window-selection routines.
|
|
2 ;;
|
|
3 ;; Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc.
|
|
4 ;;
|
|
5 ;; Author: Hovav Shacham (hovav@cs.stanford.edu)
|
|
6 ;; Created: 17 October 1998
|
|
7 ;; Keywords: window, movement
|
|
8 ;;
|
|
9 ;; This file is part of GNU Emacs.
|
|
10 ;;
|
|
11 ;; GNU Emacs is free software; you can redistribute it and/or modify
|
|
12 ;; it under the terms of the GNU General Public License as published by
|
|
13 ;; the Free Software Foundation; either version 2, or (at your option)
|
|
14 ;; any later version.
|
|
15 ;;
|
|
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.
|
|
20 ;;
|
|
21 ;; You should have received a copy of the GNU General Public License
|
|
22 ;; along with GNU Emacs; see the file COPYING. If not, write to the
|
|
23 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
24 ;; Boston, MA 02111-1307, USA.
|
|
25 ;;
|
|
26 ;; --------------------------------------------------------------------
|
|
27
|
|
28 ;;; Commentary:
|
|
29 ;;
|
|
30 ;; This package defines a set of routines, windmove-{left,up,right,
|
|
31 ;; down}, for selection of windows in a frame geometrically. For
|
|
32 ;; example, `windmove-right' selects the window immediately to the
|
|
33 ;; right of the currently-selected one. This functionality is similar
|
|
34 ;; to the window-selection controls of the BRIEF editor of yore.
|
|
35 ;;
|
|
36 ;; One subtle point is what happens when the window to the right has
|
|
37 ;; been split vertically; for example, consider a call to
|
|
38 ;; `windmove-right' in this setup:
|
|
39 ;;
|
|
40 ;; -------------
|
|
41 ;; | | A |
|
|
42 ;; | | |
|
|
43 ;; | |-----
|
|
44 ;; | * | | (* is point in the currently
|
|
45 ;; | | B | selected window)
|
|
46 ;; | | |
|
|
47 ;; -------------
|
|
48 ;;
|
|
49 ;; There are (at least) three reasonable things to do:
|
|
50 ;; (1) Always move to the window to the right of the top edge of the
|
|
51 ;; selected window; in this case, this policy selects A.
|
|
52 ;; (2) Always move to the window to the right of the bottom edge of
|
|
53 ;; the selected window; in this case, this policy selects B.
|
|
54 ;; (3) Move to the window to the right of point in the slected
|
|
55 ;; window. This may select either A or B, depending on the
|
|
56 ;; position of point; in the illustrated example, it would select
|
|
57 ;; B.
|
|
58 ;;
|
|
59 ;; Similar issues arise for all the movement functions. Windmove
|
|
60 ;; resolves this problem by allowing the user to specify behavior
|
|
61 ;; through a prefix argument. The cases are thus:
|
|
62 ;; * if no argument is given to the movement functions, or the
|
|
63 ;; argument given is zero, movement is relative to point;
|
|
64 ;; * if a positive argument is given, movement is relative to the top
|
|
65 ;; or left edge of the selected window, depending on whether the
|
|
66 ;; movement is to be horizontal or vertical;
|
|
67 ;; * if a negative argument is given, movement is relative to the
|
|
68 ;; bottom or right edge of the selected window, depending on whether
|
|
69 ;; the movement is to be horizontal or vertical.
|
|
70 ;;
|
|
71 ;;
|
|
72 ;; Another feature enables wrap-around mode when the variable
|
|
73 ;; `windmove-wrap-around' is set to a non-nil value. In this mode,
|
|
74 ;; movement that falls off the edge of the frame will wrap around to
|
|
75 ;; find the window on the opposite side of the frame. Windmove does
|
|
76 ;; the Right Thing about the minibuffer; for example, consider:
|
|
77 ;;
|
|
78 ;; -------------
|
|
79 ;; | * |
|
|
80 ;; |-----------|
|
|
81 ;; | A |
|
|
82 ;; |-----------| (* is point in the currently
|
|
83 ;; | B | C | selected window)
|
|
84 ;; | | |
|
|
85 ;; -------------
|
|
86 ;;
|
|
87 ;; With wraparound enabled, windmove-down will move to A, while
|
|
88 ;; windmove-up will move to the minibuffer if it is active, or to
|
|
89 ;; either B or C depending on the prefix argument.
|
|
90 ;;
|
|
91 ;;
|
|
92 ;; A set of default keybindings is supplied: shift-{left,up,right,down}
|
|
93 ;; invoke the corresponding Windmove function. See the installation
|
|
94 ;; section if you wish to use these keybindings.
|
|
95
|
|
96
|
|
97 ;; Installation:
|
|
98 ;;
|
|
99 ;; Put the following line in your `.emacs' file:
|
|
100 ;;
|
|
101 ;; (windmove-default-keybindings) ; default keybindings
|
|
102 ;;
|
|
103 ;;
|
|
104 ;; If you wish to enable wrap-around, also add a line like:
|
|
105 ;;
|
|
106 ;; (setq windmove-wrap-around t)
|
|
107 ;;
|
|
108 ;;
|
|
109 ;; Note: If you have an Emacs that manifests a bug that sometimes
|
|
110 ;; causes the occasional creation of a "lost column" between windows,
|
|
111 ;; so that two adjacent windows do not actually touch, you may want to
|
|
112 ;; increase the value of `windmove-window-distance-delta' to 2 or 3:
|
|
113 ;;
|
|
114 ;; (setq windmove-window-distance-delta 2)
|
|
115 ;;
|
|
116
|
|
117 ;; Acknowledgements:
|
|
118 ;;
|
|
119 ;; Special thanks to Julian Assange (proff@iq.org), whose
|
|
120 ;; change-windows-intuitively.el predates Windmove, and provided the
|
|
121 ;; inspiration for it. Kin Cho (kin@symmetrycomm.com) was the first
|
|
122 ;; to suggest wrap-around behavior. Thanks also to Gerd Moellmann
|
|
123 ;; (gerd@gnu.org) for his comments and suggestions.
|
|
124
|
|
125 ;; --------------------------------------------------------------------
|
|
126
|
|
127 ;;; Code:
|
|
128
|
|
129
|
|
130 ;; User configurable variables:
|
|
131
|
|
132 ;; For customize ...
|
|
133 (defgroup windmove nil
|
|
134 "Directional selection of windows in a frame."
|
|
135 :prefix "windmove-"
|
|
136 :group 'windows
|
|
137 :group 'convenience)
|
|
138
|
|
139
|
|
140 (defcustom windmove-wrap-around nil
|
|
141 "Whether movement off the edge of the frame wraps around.
|
|
142 If this variable is set to t, moving left from the leftmost window in
|
|
143 a frame will find the rightmost one, and similarly for the other
|
|
144 directions. The minibuffer is skipped over in up/down movements if it
|
|
145 is inactive."
|
|
146 :type 'boolean
|
|
147 :group 'windmove)
|
|
148
|
|
149 ;; If your Emacs sometimes places an empty column between two adjacent
|
|
150 ;; windows, you may wish to set this delta to 2.
|
|
151 (defcustom windmove-window-distance-delta 1
|
|
152 "How far away from the current window to look for an adjacent window.
|
|
153 Measured in characters either horizontally or vertically; setting this
|
|
154 to a value larger than 1 may be useful in getting around window-
|
|
155 placement bugs in old versions of Emacs."
|
|
156 :type 'number
|
|
157 :group 'windmove)
|
|
158
|
|
159
|
|
160
|
|
161 ;; Implementation overview:
|
|
162 ;;
|
|
163 ;; The conceptual framework behind this code is all fairly simple. We
|
|
164 ;; are on one window; we wish to move to another. The correct window
|
|
165 ;; to move to is determined by the position of point in the current
|
|
166 ;; window as well as the overall window setup.
|
|
167 ;;
|
|
168 ;; Early on, I made the decision to base my implementation around the
|
|
169 ;; built-in function `window-at'. This function takes a frame-based
|
|
170 ;; coordinate, and returns the window that contains it. Using this
|
|
171 ;; function, the job of the various top-level windmove functions can
|
|
172 ;; be decomposed: first, find the current frame-based location of
|
|
173 ;; point; second, manipulate it in some way to give a new location,
|
|
174 ;; that hopefully falls in the window immediately at left (or right,
|
|
175 ;; etc.); third, use `window-at' and `select-window' to select the
|
|
176 ;; window at that new location.
|
|
177 ;;
|
|
178 ;; This is probably not the only possible architecture, and it turns
|
|
179 ;; out to have some inherent cruftiness. (Well, okay, the third step
|
|
180 ;; is pretty clean....) We will consider each step in turn.
|
|
181 ;;
|
|
182 ;; A quick digression about coordinate frames: most of the functions
|
|
183 ;; in the windmove package deal with screen coordinates in one way or
|
|
184 ;; another. These coordinates are always relative to some reference
|
|
185 ;; points. Window-based coordinates have their reference point in the
|
|
186 ;; upper-left-hand corner of whatever window is being talked about;
|
|
187 ;; frame-based coordinates have their reference point in the
|
|
188 ;; upper-left-hand corner of the entire frame (of which the current
|
|
189 ;; window is a component).
|
|
190 ;;
|
|
191 ;; All coordinates are zero-based, which simply means that the
|
|
192 ;; reference point (whatever it is) is assigned the value (x=0, y=0).
|
|
193 ;; X-coordinates grow down the screen, and Y-coordinates grow towards
|
|
194 ;; the right of the screen.
|
|
195 ;;
|
|
196 ;; Okay, back to work. The first step is to gather information about
|
|
197 ;; the frame-based coordinates of point, or rather, the reference
|
|
198 ;; location. The reference location can be point, or the upper-left,
|
|
199 ;; or the lower-right corner of the window; the particular one used is
|
|
200 ;; controlled by the prefix argument to `windmove-left' and all the
|
|
201 ;; rest.
|
|
202 ;;
|
|
203 ;; This work is done by `windmove-reference-loc'. It can figure out
|
|
204 ;; the locations of the corners by calling `window-edges', but to
|
|
205 ;; calculate the frame-based location of point, it calls the workhorse
|
|
206 ;; function `windmove-coordinates-of-position', which itself calls the
|
|
207 ;; incredibly hairy builtin `compute-motion'. There is a good deal of
|
|
208 ;; black magic in getting all the arguments to this function just right.
|
|
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
|
|
266 ;; we're moving down and not already in the minibuffer. The function
|
|
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.
|
|
314 (X-MIN, Y-MIN) is the zero-based coordinate of the top-left corner
|
|
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)."
|
|
319 (let ((frame (if window
|
|
320 (window-frame window)
|
|
321 (selected-frame))))
|
|
322 (let ((x-min 0)
|
|
323 (y-min 0)
|
|
324 (x-max (1- (frame-width frame))) ; 1- for last row & col here
|
|
325 (y-max (1- (frame-height frame))))
|
|
326 (list x-min y-min x-max y-max))))
|
|
327
|
|
328 ;; it turns out that constraining is always a good thing, even when
|
|
329 ;; wrapping is going to happen. this is because:
|
|
330 ;; first, since we disallow exotic diagonal-around-a-corner type
|
|
331 ;; movements, so we can always fix the unimportant direction (the one
|
|
332 ;; we're not moving in).
|
|
333 ;; second, if we're moving down and we're not in the minibuffer, then
|
|
334 ;; constraining the y coordinate to max-y is okay, because if that
|
|
335 ;; falls in the minibuffer and the minibuffer isn't active, that y
|
|
336 ;; coordinate will still be off the bottom of the frame as the
|
|
337 ;; wrapping function sees it and so will get wrapped around anyway.
|
|
338 (defun windmove-constrain-loc-for-movement (coord window dir)
|
|
339 "Constrain COORD so that it is reasonable for the given movement.
|
|
340 This involves two things: first, make sure that the \"off\" coordinate
|
|
341 -- the one not being moved on, e.g., y for horizontal movement -- is
|
|
342 within frame boundaries; second, if the movement is down and we're not
|
|
343 moving from the minibuffer, make sure that the y coordinate does not
|
|
344 exceed the frame max-y, so that we don't overshoot the minibuffer
|
|
345 accidentally. WINDOW is the window that movement is relative to; DIR
|
|
346 is the direction of the movement, one of `left', `up', `right',
|
|
347 or `down'.
|
|
348 Returns the constrained coordinate."
|
|
349 (let ((frame-edges (windmove-frame-edges window))
|
|
350 (in-minibuffer (window-minibuffer-p window)))
|
|
351 (let ((min-x (nth 0 frame-edges))
|
|
352 (min-y (nth 1 frame-edges))
|
|
353 (max-x (nth 2 frame-edges))
|
|
354 (max-y (nth 3 frame-edges)))
|
|
355 (let ((new-x
|
|
356 (if (memq dir '(up down)) ; vertical movement
|
|
357 (windmove-constrain-to-range (car coord) min-x max-x)
|
|
358 (car coord)))
|
|
359 (new-y
|
|
360 (if (or (memq dir '(left right)) ; horizontal movement
|
|
361 (and (eq dir 'down)
|
|
362 (not in-minibuffer))) ; don't miss minibuffer
|
|
363 ;; (technically, we shouldn't constrain on min-y in the
|
|
364 ;; second case, but this shouldn't do any harm on a
|
|
365 ;; down movement.)
|
|
366 (windmove-constrain-to-range (cdr coord) min-y max-y)
|
|
367 (cdr coord))))
|
|
368 (cons new-x new-y)))))
|
|
369
|
|
370 ;; having constrained in the limited sense of windmove-constrain-loc-
|
|
371 ;; for-movement, the wrapping code is actually much simpler than it
|
|
372 ;; otherwise would be. the only complication is that we need to check
|
|
373 ;; if the minibuffer is active, and, if not, pretend that it's not
|
|
374 ;; even part of the frame.
|
|
375 (defun windmove-wrap-loc-for-movement (coord window dir)
|
|
376 "Takes the constrained COORD and wraps it around for the movement.
|
|
377 This makes an out-of-range x or y coordinate and wraps it around the
|
|
378 frame, giving a coordinate (hopefully) in the window on the other edge
|
|
379 of the frame. WINDOW is the window that movement is relative to (nil
|
|
380 means the currently selected window); DIR is the direction of the
|
|
381 movement, one of `left', `up', `right',or `down'.
|
|
382 Returns the wrapped coordinate."
|
|
383 (let* ((frame-edges (windmove-frame-edges window))
|
|
384 (frame-minibuffer (minibuffer-window (if window
|
|
385 (window-frame window)
|
|
386 (selected-frame))))
|
|
387 (minibuffer-active (minibuffer-window-active-p
|
|
388 frame-minibuffer)))
|
|
389 (let ((min-x (nth 0 frame-edges))
|
|
390 (min-y (nth 1 frame-edges))
|
|
391 (max-x (nth 2 frame-edges))
|
|
392 (max-y (if (not minibuffer-active)
|
|
393 (- (nth 3 frame-edges)
|
|
394 (window-height frame-minibuffer))
|
|
395 (nth 3 frame-edges))))
|
|
396 (cons
|
|
397 (windmove-constrain-around-range (car coord) min-x max-x)
|
|
398 (windmove-constrain-around-range (cdr coord) min-y max-y)))))
|
|
399
|
|
400
|
|
401
|
|
402 ;; `windmove-coordinates-of-position' is stolen and modified from the
|
|
403 ;; Emacs Lisp Reference Manual, section 27.2.5. It seems to work
|
|
404 ;; okay, although I am bothered by the fact that tab-offset (the cdr
|
|
405 ;; of the next-to- last argument) is set to 0. On the other hand, I
|
|
406 ;; can't find a single usage of `compute-motion' anywhere that doesn't
|
|
407 ;; set this component to zero, and I'm too lazy to grovel through the
|
|
408 ;; C source to figure out what's happening in the background. there
|
|
409 ;; also seems to be a good deal of fun in calculating the correct
|
|
410 ;; width of lines for telling `compute-motion' about; in particular,
|
|
411 ;; it seems we need to subtract 1 (for the continuation column) from
|
|
412 ;; the number that `window-width' gives, or continuation lines aren't
|
|
413 ;; counted correctly. I haven't seen anyone doing this before,
|
|
414 ;; though.
|
|
415 (defun windmove-coordinates-of-position (pos &optional window)
|
|
416 "Return the coordinates of position POS in window WINDOW.
|
|
417 Return the window-based coodinates in a cons pair: (HPOS . VPOS),
|
|
418 where HPOS and VPOS are the zero-based x and y components of the
|
|
419 screen location of POS. If WINDOW is nil, return the coordinates in
|
|
420 the currently selected window.
|
|
421 As an example, if point is in the top left corner of a window, then
|
|
422 the return value from `windmove-coordinates-of-position' is (0 . 0)
|
|
423 regardless of the where point is in the buffer and where the window
|
|
424 is placed in the frame."
|
|
425 (let* ((wind (if (null window) (selected-window) window))
|
|
426 (usable-width (1- (window-width wind))) ; 1- for cont. column
|
|
427 (usable-height (1- (window-height wind))) ; 1- for mode line
|
|
428 (big-hairy-result (compute-motion
|
|
429 (window-start)
|
|
430 '(0 . 0)
|
|
431 pos
|
|
432 (cons usable-width usable-height)
|
|
433 usable-width
|
|
434 (cons (window-hscroll)
|
|
435 0) ; why zero?
|
|
436 wind)))
|
|
437 (cons (nth 1 big-hairy-result) ; hpos, not vpos as documented
|
|
438 (nth 2 big-hairy-result)))) ; vpos, not hpos as documented
|
|
439
|
|
440 ;; This calculates the reference location in the current window: the
|
|
441 ;; frame-based (x . y) of either point, the top-left, or the
|
|
442 ;; bottom-right of the window, depending on ARG.
|
|
443 (defun windmove-reference-loc (&optional arg window)
|
|
444 "Return the reference location for directional window selection.
|
|
445 Return a coordinate (HPOS . VPOS) that is frame-based. If ARG is nil
|
|
446 or not supplied, the reference point is the buffer's point in the
|
|
447 currently-selected window, or WINDOW if supplied; otherwise, it is the
|
|
448 top-left or bottom-right corner of the selected window, or WINDOW if
|
|
449 supplied, if ARG is greater or smaller than zero, respectively."
|
|
450 (let ((effective-arg (if (null arg) 0 (prefix-numeric-value arg)))
|
|
451 (edges (window-edges window)))
|
|
452 (let ((top-left (cons (nth 0 edges)
|
|
453 (nth 1 edges)))
|
|
454 ;; if 1-'s are not there, windows actually extend too far.
|
|
455 ;; actually, -2 is necessary for bottom: (nth 3 edges) is
|
|
456 ;; the height of the window; -1 because we want 0-based max,
|
|
457 ;; -1 to get rid of mode line
|
|
458 (bottom-right (cons (- (nth 2 edges) 1)
|
|
459 (- (nth 3 edges) 2))))
|
|
460 (cond
|
|
461 ((> effective-arg 0)
|
|
462 top-left)
|
|
463 ((< effective-arg 0)
|
|
464 bottom-right)
|
|
465 ((= effective-arg 0)
|
|
466 (windmove-coord-add
|
|
467 top-left
|
|
468 (windmove-coordinates-of-position (window-point window)
|
|
469 window)))))))
|
|
470
|
|
471 ;; This uses the reference location in the current window (calculated
|
|
472 ;; by `windmove-reference-loc' above) to find a reference location
|
|
473 ;; that will hopefully be in the window we want to move to.
|
|
474 (defun windmove-other-window-loc (dir &optional arg window)
|
|
475 "Return a location in the window to be moved to.
|
|
476 Return value is a frame-based (HPOS . VPOS) value that should be moved
|
|
477 to. DIR is one of `left', `up', `right', or `down'; an optional ARG
|
|
478 is handled as by `windmove-reference-loc'; WINDOW is the window that
|
|
479 movement is relative to."
|
|
480 (let ((edges (window-edges window)) ; edges: (x0, y0, x1, y1)
|
|
481 (refpoint (windmove-reference-loc arg window))) ; (x . y)
|
|
482 (cond
|
|
483 ((eq dir 'left)
|
|
484 (cons (- (nth 0 edges)
|
|
485 windmove-window-distance-delta)
|
|
486 (cdr refpoint))) ; (x0-d, y)
|
|
487 ((eq dir 'up)
|
|
488 (cons (car refpoint)
|
|
489 (- (nth 1 edges)
|
|
490 windmove-window-distance-delta))) ; (x, y0-d)
|
|
491 ((eq dir 'right)
|
|
492 (cons (+ (nth 2 edges)
|
|
493 windmove-window-distance-delta)
|
|
494 (cdr refpoint))) ; (x1+d, y)
|
|
495 ((eq dir 'down)
|
|
496 (cons (car refpoint)
|
|
497 (+ (nth 3 edges)
|
|
498 windmove-window-distance-delta))) ; (x, y1+d)
|
|
499 (t (error "Invalid direction of movement: %s" dir)))))
|
|
500
|
|
501 (defun windmove-find-other-window (dir &optional arg window)
|
|
502 "Return the window object in direction DIR.
|
|
503 DIR, ARG, and WINDOW are handled as by `windmove-other-window-loc'."
|
|
504 (let* ((actual-current-window (or window (selected-window)))
|
|
505 (raw-other-window-loc
|
|
506 (windmove-other-window-loc dir arg actual-current-window))
|
|
507 (constrained-other-window-loc
|
|
508 (windmove-constrain-loc-for-movement raw-other-window-loc
|
|
509 actual-current-window
|
|
510 dir))
|
|
511 (other-window-loc
|
|
512 (if windmove-wrap-around
|
|
513 (windmove-wrap-loc-for-movement constrained-other-window-loc
|
|
514 actual-current-window
|
|
515 dir)
|
|
516 constrained-other-window-loc)))
|
|
517 (window-at (car other-window-loc)
|
|
518 (cdr other-window-loc))))
|
|
519
|
|
520
|
|
521 ;; Selects the window that's hopefully at the location returned by
|
|
522 ;; `windmove-other-window-loc', or screams if there's no window there.
|
|
523 (defun windmove-do-window-select (dir &optional arg window)
|
|
524 "Moves to the window at direction DIR.
|
|
525 DIR, ARG, and WINDOW are handled as by `windmove-other-window-loc'.
|
|
526 If no window is at direction DIR, an error is signaled."
|
|
527 (let ((other-window (windmove-find-other-window dir arg window)))
|
|
528 (cond ((null other-window)
|
|
529 (error "No window at %s" dir))
|
|
530 ((and (window-minibuffer-p other-window)
|
|
531 (not (minibuffer-window-active-p other-window)))
|
|
532 (error "Can't move to inactive minibuffer"))
|
|
533 (t
|
|
534 (select-window other-window)))))
|
|
535
|
|
536
|
|
537 ;;; end-user functions
|
|
538 ;; these are all simple interactive wrappers to `windmove-do-
|
|
539 ;; window-select', meant to be bound to keys.
|
|
540
|
|
541 ;;;###autoload
|
|
542 (defun windmove-left (&optional arg)
|
|
543 "Select the window to the left of the current one.
|
|
544 With no prefix argument, or with prefix argument equal to zero,
|
|
545 \"left\" is relative to the position of point in the window; otherwise
|
|
546 it is relative to the top edge (for positive ARG) or the bottom edge
|
|
547 (for negative ARG) of the current window.
|
|
548 If no window is at the desired location, an error is signaled."
|
|
549 (interactive "P")
|
|
550 (windmove-do-window-select 'left arg))
|
|
551
|
|
552 ;;;###autoload
|
|
553 (defun windmove-up (&optional arg)
|
|
554 "Select the window above the current one.
|
|
555 With no prefix argument, or with prefix argument equal to zero, \"up\"
|
|
556 is relative to the position of point in the window; otherwise it is
|
|
557 relative to the left edge (for positive ARG) or the right edge (for
|
|
558 negative ARG) of the current window.
|
|
559 If no window is at the desired location, an error is signaled."
|
|
560 (interactive "P")
|
|
561 (windmove-do-window-select 'up arg))
|
|
562
|
|
563 ;;;###autoload
|
|
564 (defun windmove-right (&optional arg)
|
|
565 "Select the window to the right of the current one.
|
|
566 With no prefix argument, or with prefix argument equal to zero,
|
|
567 \"right\" is relative to the position of point in the window;
|
|
568 otherwise it is relative to the top edge (for positive ARG) or the
|
|
569 bottom edge (for negative ARG) of the current window.
|
|
570 If no window is at the desired location, an error is signaled."
|
|
571 (interactive "P")
|
|
572 (windmove-do-window-select 'right arg))
|
|
573
|
|
574 ;;;###autoload
|
|
575 (defun windmove-down (&optional arg)
|
|
576 "Select the window below the current one.
|
|
577 With no prefix argument, or with prefix argument equal to zero,
|
|
578 \"down\" is relative to the position of point in the window; otherwise
|
|
579 it is relative to the left edge (for positive ARG) or the right edge
|
|
580 (for negative ARG) of the current window.
|
|
581 If no window is at the desired location, an error is signaled."
|
|
582 (interactive "P")
|
|
583 (windmove-do-window-select 'down arg))
|
|
584
|
|
585
|
|
586 ;;; set up keybindings
|
|
587 ;; Idea for this function is from iswitchb.el, by Stephen Eglen
|
|
588 ;; (stephen@cns.ed.ac.uk).
|
|
589 ;; I don't think these bindings will work on non-X terminals; you
|
|
590 ;; probably want to use different bindings in that case.
|
|
591
|
|
592 ;;;###autoload
|
|
593 (defun windmove-default-keybindings ()
|
|
594 "Set up default keybindings for `windmove'."
|
|
595 (interactive)
|
|
596 (global-set-key [(shift left)] 'windmove-left)
|
|
597 (global-set-key [(shift up)] 'windmove-up)
|
|
598 (global-set-key [(shift right)] 'windmove-right)
|
|
599 (global-set-key [(shift down)] 'windmove-down))
|
|
600
|
|
601
|
|
602 (provide 'windmove)
|
|
603
|
|
604 ;;; windmove.el ends here
|