Mercurial > emacs
annotate lisp/windmove.el @ 65015:103b7190c29c
(pos_visible_p): Adjust X value if window is hscrolled.
author | Kim F. Storm <storm@cua.dk> |
---|---|
date | Wed, 17 Aug 2005 14:59:41 +0000 |
parents | 41bb365f41c4 |
children | 6c1c6712bea9 |
rev | line source |
---|---|
38436
b174db545cfd
Some fixes to follow coding conventions.
Pavel Janík <Pavel@Janik.cz>
parents:
30892
diff
changeset
|
1 ;;; windmove.el --- directional window-selection routines |
27545 | 2 ;; |
64762
41bb365f41c4
Update years in copyright notice; nfc.
Thien-Thi Nguyen <ttn@gnuvola.org>
parents:
64091
diff
changeset
|
3 ;; Copyright (C) 1998, 1999, 2000, 2002, 2003, 2004, |
41bb365f41c4
Update years in copyright notice; nfc.
Thien-Thi Nguyen <ttn@gnuvola.org>
parents:
64091
diff
changeset
|
4 ;; 2005 Free Software Foundation, Inc. |
27545 | 5 ;; |
6 ;; Author: Hovav Shacham (hovav@cs.stanford.edu) | |
7 ;; Created: 17 October 1998 | |
30892
495502641770
(windmove) <defgroup>: Add :version.
Dave Love <fx@gnu.org>
parents:
27545
diff
changeset
|
8 ;; Keywords: window, movement, convenience |
27545 | 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 2, 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 | |
64091 | 24 ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
25 ;; Boston, MA 02110-1301, USA. | |
27545 | 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. | |
30892
495502641770
(windmove) <defgroup>: Add :version.
Dave Love <fx@gnu.org>
parents:
27545
diff
changeset
|
55 ;; (3) Move to the window to the right of point in the selected |
27545 | 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 ;; | |
45166
8de5fa67b6cb
(windmove-default-keybindings): Add optional parameter to allow using a
Juanma Barranquero <lekktu@gmail.com>
parents:
39588
diff
changeset
|
102 ;; (windmove-default-keybindings) ; shifted arrow keys |
8de5fa67b6cb
(windmove-default-keybindings): Add optional parameter to allow using a
Juanma Barranquero <lekktu@gmail.com>
parents:
39588
diff
changeset
|
103 ;; |
8de5fa67b6cb
(windmove-default-keybindings): Add optional parameter to allow using a
Juanma Barranquero <lekktu@gmail.com>
parents:
39588
diff
changeset
|
104 ;; or |
8de5fa67b6cb
(windmove-default-keybindings): Add optional parameter to allow using a
Juanma Barranquero <lekktu@gmail.com>
parents:
39588
diff
changeset
|
105 ;; |
8de5fa67b6cb
(windmove-default-keybindings): Add optional parameter to allow using a
Juanma Barranquero <lekktu@gmail.com>
parents:
39588
diff
changeset
|
106 ;; (windmove-default-keybindings 'hyper) ; etc. |
8de5fa67b6cb
(windmove-default-keybindings): Add optional parameter to allow using a
Juanma Barranquero <lekktu@gmail.com>
parents:
39588
diff
changeset
|
107 ;; |
8de5fa67b6cb
(windmove-default-keybindings): Add optional parameter to allow using a
Juanma Barranquero <lekktu@gmail.com>
parents:
39588
diff
changeset
|
108 ;; to use another modifier key. |
27545 | 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: | |
45166
8de5fa67b6cb
(windmove-default-keybindings): Add optional parameter to allow using a
Juanma Barranquero <lekktu@gmail.com>
parents:
39588
diff
changeset
|
120 ;; |
27545 | 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-" | |
30892
495502641770
(windmove) <defgroup>: Add :version.
Dave Love <fx@gnu.org>
parents:
27545
diff
changeset
|
141 :version "21.1" |
27545 | 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', but to | |
211 ;; calculate the frame-based location of point, it calls the workhorse | |
212 ;; function `windmove-coordinates-of-position', which itself calls the | |
213 ;; incredibly hairy builtin `compute-motion'. There is a good deal of | |
214 ;; black magic in getting all the arguments to this function just right. | |
215 ;; | |
216 ;; The second step is more messy. Conceptually, it is fairly simple: | |
217 ;; if we know the reference location, and the coordinates of the | |
218 ;; current window, we can "throw" our reference point just over the | |
219 ;; appropriate edge of the window, and see what other window is | |
220 ;; there. More explicitly, consider this example from the user | |
221 ;; documentation above. | |
222 ;; | |
223 ;; ------------- | |
224 ;; | | A | | |
225 ;; | | | | |
226 ;; | |----- | |
227 ;; | * | | (* is point in the currently | |
228 ;; | | B | selected window) | |
229 ;; | | | | |
230 ;; ------------- | |
231 ;; | |
232 ;; The asterisk marks the reference point; we wish to move right. | |
233 ;; Since we are moving horizontally, the Y coordinate of the new | |
234 ;; location will be the same. The X coordinate can be such that it is | |
235 ;; just past the edge of the present window. Obviously, the new point | |
236 ;; will be inside window B. This in itself is fairly simple: using | |
237 ;; the result of `windmove-reference-loc' and `window-edges', all the | |
238 ;; necessary math can be performed. (Having said that, there is a | |
239 ;; good deal of room for off-by-one errors, and Emacs 19.34, at least, | |
240 ;; sometimes manifests a bug where two windows don't actually touch, | |
241 ;; so a larger skip is required.) The actual math here is done by | |
242 ;; `windmove-other-window-loc'. | |
243 ;; | |
244 ;; But we can't just pass the result of `windmove-other-window-loc' to | |
245 ;; `window-at' directly. Why not? Suppose a move would take us off | |
246 ;; the edge of the screen, say to the left. We want to give a | |
247 ;; descriptive error message to the user. Or, suppose that a move | |
248 ;; would place us in the minibuffer. What if the minibuffer is | |
249 ;; inactive? | |
250 ;; | |
251 ;; Actually, the whole subject of the minibuffer edge of the frame is | |
252 ;; rather messy. It turns out that with a sufficiently large delta, | |
253 ;; we can fly off the bottom edge of the frame and miss the minibuffer | |
254 ;; altogther. This, I think, is never right: if there's a minibuffer | |
255 ;; and you're not in it, and you move down, the minibuffer should be | |
256 ;; in your way. | |
257 ;; | |
258 ;; (By the way, I'm not totally sure that the code does the right | |
259 ;; thing in really weird cases, like a frame with no minibuffer.) | |
260 ;; | |
261 ;; So, what we need is some ways to do constraining and such. The | |
262 ;; early versions of windmove took a fairly simplistic approach to all | |
263 ;; this. When I added the wrap-around option, those internals had to | |
264 ;; be rewritten. After a *lot* of futzing around, I came up with a | |
265 ;; two-step process that I think is general enough to cover the | |
266 ;; relevant cases. (I'm not totally happy with having to pass the | |
267 ;; window variable as deep as I do, but we can't have everything.) | |
268 ;; | |
269 ;; In the first phase, we make sure that the new location is sane. | |
270 ;; "Sane" means that we can only fall of the edge of the frame in the | |
271 ;; direction we're moving in, and that we don't miss the minibuffer if | |
30892
495502641770
(windmove) <defgroup>: Add :version.
Dave Love <fx@gnu.org>
parents:
27545
diff
changeset
|
272 ;; we're moving down and not already in the minibuffer. The function |
27545 | 273 ;; `windmove-constrain-loc-for-movement' takes care of all this. |
274 ;; | |
275 ;; Then, we handle the wraparound, if it's enabled. The function | |
276 ;; `windmove-wrap-loc-for-movement' takes coordinate values (both X | |
277 ;; and Y) that fall off the edge of the frame, and replaces them with | |
278 ;; values on the other side of the frame. It also has special | |
279 ;; minibuffer-handling code again, because we want to wrap through the | |
280 ;; minibuffer if it's not enabled. | |
281 ;; | |
282 ;; So, that's it. Seems to work. All of this work is done by the fun | |
283 ;; function `windmove-find-other-window'. | |
284 ;; | |
285 ;; So, now we have a window to move to (or nil if something's gone | |
286 ;; wrong). The function `windmove-do-window-select' is the main | |
287 ;; driver function: it actually does the `select-window'. It is | |
288 ;; called by four little convenience wrappers, `windmove-left', | |
289 ;; `windmove-up', `windmove-right', and `windmove-down', which make | |
290 ;; for convenient keybinding. | |
291 | |
292 | |
293 ;; Quick & dirty utility function to add two (x . y) coords. | |
294 (defun windmove-coord-add (coord1 coord2) | |
295 "Add the two coordinates. | |
296 Both COORD1 and COORD2 are coordinate cons pairs, (HPOS . VPOS). The | |
297 result is another coordinate cons pair." | |
298 (cons (+ (car coord1) (car coord2)) | |
299 (+ (cdr coord1) (cdr coord2)))) | |
300 | |
301 | |
302 (defun windmove-constrain-to-range (n min-n max-n) | |
303 "Ensure that N is between MIN-N and MAX-N inclusive by constraining. | |
304 If N is less than MIN-N, return MIN-N; if greater than MAX-N, return | |
305 MAX-N." | |
306 (max min-n (min n max-n))) | |
307 | |
308 (defun windmove-constrain-around-range (n min-n max-n) | |
309 "Ensure that N is between MIN-N and MAX-N inclusive by wrapping. | |
310 If N is less than MIN-N, return MAX-N; if greater than MAX-N, return | |
311 MIN-N." | |
312 (cond | |
313 ((< n min-n) max-n) | |
314 ((> n max-n) min-n) | |
315 (t n))) | |
316 | |
317 (defun windmove-frame-edges (window) | |
318 "Return (X-MIN Y-MIN X-MAX Y-MAX) for the frame containing WINDOW. | |
319 If WINDOW is nil, return the edges for the selected frame. | |
39588 | 320 \(X-MIN, Y-MIN) is the zero-based coordinate of the top-left corner |
27545 | 321 of the frame; (X-MAX, Y-MAX) is the zero-based coordinate of the |
322 bottom-right corner of the frame. | |
323 For example, if a frame has 76 rows and 181 columns, the return value | |
324 from `windmove-frame-edges' will be the list (0 0 180 75)." | |
51424
26d468be5b3c
(windmove-reference-loc, windmove-frame-edges): Use window-inside-edges.
Richard M. Stallman <rms@gnu.org>
parents:
45166
diff
changeset
|
325 (let* ((frame (if window |
26d468be5b3c
(windmove-reference-loc, windmove-frame-edges): Use window-inside-edges.
Richard M. Stallman <rms@gnu.org>
parents:
45166
diff
changeset
|
326 (window-frame window) |
26d468be5b3c
(windmove-reference-loc, windmove-frame-edges): Use window-inside-edges.
Richard M. Stallman <rms@gnu.org>
parents:
45166
diff
changeset
|
327 (selected-frame))) |
57092
5f904f95d7fd
(windmove-frame-edges): Report coordinates of
Eli Zaretskii <eliz@gnu.org>
parents:
56587
diff
changeset
|
328 (top-left (window-edges (frame-first-window frame))) |
51424
26d468be5b3c
(windmove-reference-loc, windmove-frame-edges): Use window-inside-edges.
Richard M. Stallman <rms@gnu.org>
parents:
45166
diff
changeset
|
329 (x-min (nth 0 top-left)) |
26d468be5b3c
(windmove-reference-loc, windmove-frame-edges): Use window-inside-edges.
Richard M. Stallman <rms@gnu.org>
parents:
45166
diff
changeset
|
330 (y-min (nth 1 top-left)) |
57092
5f904f95d7fd
(windmove-frame-edges): Report coordinates of
Eli Zaretskii <eliz@gnu.org>
parents:
56587
diff
changeset
|
331 (x-max (1- (frame-width frame))) ; 1- for last row & col |
5f904f95d7fd
(windmove-frame-edges): Report coordinates of
Eli Zaretskii <eliz@gnu.org>
parents:
56587
diff
changeset
|
332 (y-max (1- (frame-height frame)))) |
51424
26d468be5b3c
(windmove-reference-loc, windmove-frame-edges): Use window-inside-edges.
Richard M. Stallman <rms@gnu.org>
parents:
45166
diff
changeset
|
333 (list x-min y-min x-max y-max))) |
27545 | 334 |
335 ;; it turns out that constraining is always a good thing, even when | |
336 ;; wrapping is going to happen. this is because: | |
337 ;; first, since we disallow exotic diagonal-around-a-corner type | |
338 ;; movements, so we can always fix the unimportant direction (the one | |
339 ;; we're not moving in). | |
340 ;; second, if we're moving down and we're not in the minibuffer, then | |
341 ;; constraining the y coordinate to max-y is okay, because if that | |
342 ;; falls in the minibuffer and the minibuffer isn't active, that y | |
343 ;; coordinate will still be off the bottom of the frame as the | |
344 ;; wrapping function sees it and so will get wrapped around anyway. | |
345 (defun windmove-constrain-loc-for-movement (coord window dir) | |
346 "Constrain COORD so that it is reasonable for the given movement. | |
347 This involves two things: first, make sure that the \"off\" coordinate | |
348 -- the one not being moved on, e.g., y for horizontal movement -- is | |
349 within frame boundaries; second, if the movement is down and we're not | |
350 moving from the minibuffer, make sure that the y coordinate does not | |
351 exceed the frame max-y, so that we don't overshoot the minibuffer | |
352 accidentally. WINDOW is the window that movement is relative to; DIR | |
353 is the direction of the movement, one of `left', `up', `right', | |
354 or `down'. | |
355 Returns the constrained coordinate." | |
356 (let ((frame-edges (windmove-frame-edges window)) | |
357 (in-minibuffer (window-minibuffer-p window))) | |
358 (let ((min-x (nth 0 frame-edges)) | |
359 (min-y (nth 1 frame-edges)) | |
360 (max-x (nth 2 frame-edges)) | |
361 (max-y (nth 3 frame-edges))) | |
362 (let ((new-x | |
363 (if (memq dir '(up down)) ; vertical movement | |
364 (windmove-constrain-to-range (car coord) min-x max-x) | |
365 (car coord))) | |
366 (new-y | |
367 (if (or (memq dir '(left right)) ; horizontal movement | |
368 (and (eq dir 'down) | |
369 (not in-minibuffer))) ; don't miss minibuffer | |
370 ;; (technically, we shouldn't constrain on min-y in the | |
371 ;; second case, but this shouldn't do any harm on a | |
372 ;; down movement.) | |
373 (windmove-constrain-to-range (cdr coord) min-y max-y) | |
374 (cdr coord)))) | |
375 (cons new-x new-y))))) | |
376 | |
377 ;; having constrained in the limited sense of windmove-constrain-loc- | |
378 ;; for-movement, the wrapping code is actually much simpler than it | |
379 ;; otherwise would be. the only complication is that we need to check | |
380 ;; if the minibuffer is active, and, if not, pretend that it's not | |
381 ;; even part of the frame. | |
382 (defun windmove-wrap-loc-for-movement (coord window dir) | |
383 "Takes the constrained COORD and wraps it around for the movement. | |
384 This makes an out-of-range x or y coordinate and wraps it around the | |
385 frame, giving a coordinate (hopefully) in the window on the other edge | |
386 of the frame. WINDOW is the window that movement is relative to (nil | |
387 means the currently selected window); DIR is the direction of the | |
388 movement, one of `left', `up', `right',or `down'. | |
389 Returns the wrapped coordinate." | |
390 (let* ((frame-edges (windmove-frame-edges window)) | |
391 (frame-minibuffer (minibuffer-window (if window | |
392 (window-frame window) | |
393 (selected-frame)))) | |
394 (minibuffer-active (minibuffer-window-active-p | |
395 frame-minibuffer))) | |
396 (let ((min-x (nth 0 frame-edges)) | |
397 (min-y (nth 1 frame-edges)) | |
398 (max-x (nth 2 frame-edges)) | |
399 (max-y (if (not minibuffer-active) | |
400 (- (nth 3 frame-edges) | |
401 (window-height frame-minibuffer)) | |
402 (nth 3 frame-edges)))) | |
403 (cons | |
404 (windmove-constrain-around-range (car coord) min-x max-x) | |
405 (windmove-constrain-around-range (cdr coord) min-y max-y))))) | |
406 | |
407 | |
408 | |
409 ;; `windmove-coordinates-of-position' is stolen and modified from the | |
57092
5f904f95d7fd
(windmove-frame-edges): Report coordinates of
Eli Zaretskii <eliz@gnu.org>
parents:
56587
diff
changeset
|
410 ;; Emacs 20 Lisp Reference Manual, section 27.2.5. It seems to work |
27545 | 411 ;; okay, although I am bothered by the fact that tab-offset (the cdr |
412 ;; of the next-to- last argument) is set to 0. On the other hand, I | |
413 ;; can't find a single usage of `compute-motion' anywhere that doesn't | |
414 ;; set this component to zero, and I'm too lazy to grovel through the | |
415 ;; C source to figure out what's happening in the background. there | |
416 ;; also seems to be a good deal of fun in calculating the correct | |
417 ;; width of lines for telling `compute-motion' about; in particular, | |
418 ;; it seems we need to subtract 1 (for the continuation column) from | |
419 ;; the number that `window-width' gives, or continuation lines aren't | |
420 ;; counted correctly. I haven't seen anyone doing this before, | |
421 ;; though. | |
57092
5f904f95d7fd
(windmove-frame-edges): Report coordinates of
Eli Zaretskii <eliz@gnu.org>
parents:
56587
diff
changeset
|
422 ;; |
5f904f95d7fd
(windmove-frame-edges): Report coordinates of
Eli Zaretskii <eliz@gnu.org>
parents:
56587
diff
changeset
|
423 ;; Now updated for Emacs 21, based on the Emacs 21 Lisp Reference |
5f904f95d7fd
(windmove-frame-edges): Report coordinates of
Eli Zaretskii <eliz@gnu.org>
parents:
56587
diff
changeset
|
424 ;; Manual, section 30.2.5. It is no longer necessary to subtract |
5f904f95d7fd
(windmove-frame-edges): Report coordinates of
Eli Zaretskii <eliz@gnu.org>
parents:
56587
diff
changeset
|
425 ;; 1 for the usable width of the window. |
5f904f95d7fd
(windmove-frame-edges): Report coordinates of
Eli Zaretskii <eliz@gnu.org>
parents:
56587
diff
changeset
|
426 ;; TODO: also handle minibuffer case, w/ `minibuffer-prompt-width'. |
5f904f95d7fd
(windmove-frame-edges): Report coordinates of
Eli Zaretskii <eliz@gnu.org>
parents:
56587
diff
changeset
|
427 (defun windmove-coordinates-of-position (pos) |
5f904f95d7fd
(windmove-frame-edges): Report coordinates of
Eli Zaretskii <eliz@gnu.org>
parents:
56587
diff
changeset
|
428 "Return the coordinates of position POS in the current window. |
27545 | 429 Return the window-based coodinates in a cons pair: (HPOS . VPOS), |
430 where HPOS and VPOS are the zero-based x and y components of the | |
57092
5f904f95d7fd
(windmove-frame-edges): Report coordinates of
Eli Zaretskii <eliz@gnu.org>
parents:
56587
diff
changeset
|
431 screen location of POS. |
27545 | 432 As an example, if point is in the top left corner of a window, then |
433 the return value from `windmove-coordinates-of-position' is (0 . 0) | |
434 regardless of the where point is in the buffer and where the window | |
435 is placed in the frame." | |
57092
5f904f95d7fd
(windmove-frame-edges): Report coordinates of
Eli Zaretskii <eliz@gnu.org>
parents:
56587
diff
changeset
|
436 (let ((big-hairy-result (compute-motion |
5f904f95d7fd
(windmove-frame-edges): Report coordinates of
Eli Zaretskii <eliz@gnu.org>
parents:
56587
diff
changeset
|
437 (window-start) |
5f904f95d7fd
(windmove-frame-edges): Report coordinates of
Eli Zaretskii <eliz@gnu.org>
parents:
56587
diff
changeset
|
438 '(0 . 0) |
5f904f95d7fd
(windmove-frame-edges): Report coordinates of
Eli Zaretskii <eliz@gnu.org>
parents:
56587
diff
changeset
|
439 pos |
5f904f95d7fd
(windmove-frame-edges): Report coordinates of
Eli Zaretskii <eliz@gnu.org>
parents:
56587
diff
changeset
|
440 nil ; (window-width window-height) |
5f904f95d7fd
(windmove-frame-edges): Report coordinates of
Eli Zaretskii <eliz@gnu.org>
parents:
56587
diff
changeset
|
441 nil ; window-width |
5f904f95d7fd
(windmove-frame-edges): Report coordinates of
Eli Zaretskii <eliz@gnu.org>
parents:
56587
diff
changeset
|
442 (cons (window-hscroll) |
5f904f95d7fd
(windmove-frame-edges): Report coordinates of
Eli Zaretskii <eliz@gnu.org>
parents:
56587
diff
changeset
|
443 0) ; why zero? |
5f904f95d7fd
(windmove-frame-edges): Report coordinates of
Eli Zaretskii <eliz@gnu.org>
parents:
56587
diff
changeset
|
444 (selected-window)))) |
5f904f95d7fd
(windmove-frame-edges): Report coordinates of
Eli Zaretskii <eliz@gnu.org>
parents:
56587
diff
changeset
|
445 (cons (nth 1 big-hairy-result) ; hpos, not vpos as documented |
5f904f95d7fd
(windmove-frame-edges): Report coordinates of
Eli Zaretskii <eliz@gnu.org>
parents:
56587
diff
changeset
|
446 (nth 2 big-hairy-result)))) ; vpos, not hpos as documented |
5f904f95d7fd
(windmove-frame-edges): Report coordinates of
Eli Zaretskii <eliz@gnu.org>
parents:
56587
diff
changeset
|
447 |
5f904f95d7fd
(windmove-frame-edges): Report coordinates of
Eli Zaretskii <eliz@gnu.org>
parents:
56587
diff
changeset
|
448 (defun windmove-coordinates-of-window-position (pos &optional window) |
5f904f95d7fd
(windmove-frame-edges): Report coordinates of
Eli Zaretskii <eliz@gnu.org>
parents:
56587
diff
changeset
|
449 "Return the coordinates of position POS in WINDOW. |
5f904f95d7fd
(windmove-frame-edges): Report coordinates of
Eli Zaretskii <eliz@gnu.org>
parents:
56587
diff
changeset
|
450 Return the window-based coodinates in a cons pair: (HPOS . VPOS), |
5f904f95d7fd
(windmove-frame-edges): Report coordinates of
Eli Zaretskii <eliz@gnu.org>
parents:
56587
diff
changeset
|
451 where HPOS and VPOS are the zero-based x and y components of the |
5f904f95d7fd
(windmove-frame-edges): Report coordinates of
Eli Zaretskii <eliz@gnu.org>
parents:
56587
diff
changeset
|
452 screen location of POS. If WINDOW is nil, return the coordinates in |
5f904f95d7fd
(windmove-frame-edges): Report coordinates of
Eli Zaretskii <eliz@gnu.org>
parents:
56587
diff
changeset
|
453 the currently selected window." |
5f904f95d7fd
(windmove-frame-edges): Report coordinates of
Eli Zaretskii <eliz@gnu.org>
parents:
56587
diff
changeset
|
454 (if (null window) |
5f904f95d7fd
(windmove-frame-edges): Report coordinates of
Eli Zaretskii <eliz@gnu.org>
parents:
56587
diff
changeset
|
455 (windmove-coordinates-of-position pos) |
5f904f95d7fd
(windmove-frame-edges): Report coordinates of
Eli Zaretskii <eliz@gnu.org>
parents:
56587
diff
changeset
|
456 (save-selected-window |
5f904f95d7fd
(windmove-frame-edges): Report coordinates of
Eli Zaretskii <eliz@gnu.org>
parents:
56587
diff
changeset
|
457 (select-window window) |
5f904f95d7fd
(windmove-frame-edges): Report coordinates of
Eli Zaretskii <eliz@gnu.org>
parents:
56587
diff
changeset
|
458 (windmove-coordinates-of-position pos)))) |
27545 | 459 |
460 ;; This calculates the reference location in the current window: the | |
461 ;; frame-based (x . y) of either point, the top-left, or the | |
462 ;; bottom-right of the window, depending on ARG. | |
463 (defun windmove-reference-loc (&optional arg window) | |
464 "Return the reference location for directional window selection. | |
465 Return a coordinate (HPOS . VPOS) that is frame-based. If ARG is nil | |
466 or not supplied, the reference point is the buffer's point in the | |
467 currently-selected window, or WINDOW if supplied; otherwise, it is the | |
468 top-left or bottom-right corner of the selected window, or WINDOW if | |
469 supplied, if ARG is greater or smaller than zero, respectively." | |
470 (let ((effective-arg (if (null arg) 0 (prefix-numeric-value arg))) | |
51424
26d468be5b3c
(windmove-reference-loc, windmove-frame-edges): Use window-inside-edges.
Richard M. Stallman <rms@gnu.org>
parents:
45166
diff
changeset
|
471 (edges (window-inside-edges window))) |
27545 | 472 (let ((top-left (cons (nth 0 edges) |
473 (nth 1 edges))) | |
51424
26d468be5b3c
(windmove-reference-loc, windmove-frame-edges): Use window-inside-edges.
Richard M. Stallman <rms@gnu.org>
parents:
45166
diff
changeset
|
474 ;; Subtracting 1 converts the edge to the last column or line |
26d468be5b3c
(windmove-reference-loc, windmove-frame-edges): Use window-inside-edges.
Richard M. Stallman <rms@gnu.org>
parents:
45166
diff
changeset
|
475 ;; within the window. |
27545 | 476 (bottom-right (cons (- (nth 2 edges) 1) |
51424
26d468be5b3c
(windmove-reference-loc, windmove-frame-edges): Use window-inside-edges.
Richard M. Stallman <rms@gnu.org>
parents:
45166
diff
changeset
|
477 (- (nth 3 edges) 1)))) |
27545 | 478 (cond |
479 ((> effective-arg 0) | |
480 top-left) | |
481 ((< effective-arg 0) | |
482 bottom-right) | |
483 ((= effective-arg 0) | |
484 (windmove-coord-add | |
485 top-left | |
57092
5f904f95d7fd
(windmove-frame-edges): Report coordinates of
Eli Zaretskii <eliz@gnu.org>
parents:
56587
diff
changeset
|
486 (windmove-coordinates-of-window-position |
5f904f95d7fd
(windmove-frame-edges): Report coordinates of
Eli Zaretskii <eliz@gnu.org>
parents:
56587
diff
changeset
|
487 (window-point window) |
5f904f95d7fd
(windmove-frame-edges): Report coordinates of
Eli Zaretskii <eliz@gnu.org>
parents:
56587
diff
changeset
|
488 window))))))) |
27545 | 489 |
490 ;; This uses the reference location in the current window (calculated | |
491 ;; by `windmove-reference-loc' above) to find a reference location | |
492 ;; that will hopefully be in the window we want to move to. | |
493 (defun windmove-other-window-loc (dir &optional arg window) | |
494 "Return a location in the window to be moved to. | |
495 Return value is a frame-based (HPOS . VPOS) value that should be moved | |
496 to. DIR is one of `left', `up', `right', or `down'; an optional ARG | |
497 is handled as by `windmove-reference-loc'; WINDOW is the window that | |
498 movement is relative to." | |
499 (let ((edges (window-edges window)) ; edges: (x0, y0, x1, y1) | |
500 (refpoint (windmove-reference-loc arg window))) ; (x . y) | |
501 (cond | |
502 ((eq dir 'left) | |
503 (cons (- (nth 0 edges) | |
504 windmove-window-distance-delta) | |
505 (cdr refpoint))) ; (x0-d, y) | |
506 ((eq dir 'up) | |
507 (cons (car refpoint) | |
508 (- (nth 1 edges) | |
509 windmove-window-distance-delta))) ; (x, y0-d) | |
510 ((eq dir 'right) | |
57092
5f904f95d7fd
(windmove-frame-edges): Report coordinates of
Eli Zaretskii <eliz@gnu.org>
parents:
56587
diff
changeset
|
511 (cons (+ (1- (nth 2 edges)) ; -1 to get actual max x |
27545 | 512 windmove-window-distance-delta) |
57092
5f904f95d7fd
(windmove-frame-edges): Report coordinates of
Eli Zaretskii <eliz@gnu.org>
parents:
56587
diff
changeset
|
513 (cdr refpoint))) ; (x1+d-1, y) |
5f904f95d7fd
(windmove-frame-edges): Report coordinates of
Eli Zaretskii <eliz@gnu.org>
parents:
56587
diff
changeset
|
514 ((eq dir 'down) ; -1 to get actual max y |
27545 | 515 (cons (car refpoint) |
57092
5f904f95d7fd
(windmove-frame-edges): Report coordinates of
Eli Zaretskii <eliz@gnu.org>
parents:
56587
diff
changeset
|
516 (+ (1- (nth 3 edges)) |
5f904f95d7fd
(windmove-frame-edges): Report coordinates of
Eli Zaretskii <eliz@gnu.org>
parents:
56587
diff
changeset
|
517 windmove-window-distance-delta))) ; (x, y1+d-1) |
27545 | 518 (t (error "Invalid direction of movement: %s" dir))))) |
519 | |
520 (defun windmove-find-other-window (dir &optional arg window) | |
521 "Return the window object in direction DIR. | |
522 DIR, ARG, and WINDOW are handled as by `windmove-other-window-loc'." | |
523 (let* ((actual-current-window (or window (selected-window))) | |
524 (raw-other-window-loc | |
525 (windmove-other-window-loc dir arg actual-current-window)) | |
526 (constrained-other-window-loc | |
527 (windmove-constrain-loc-for-movement raw-other-window-loc | |
528 actual-current-window | |
529 dir)) | |
530 (other-window-loc | |
531 (if windmove-wrap-around | |
532 (windmove-wrap-loc-for-movement constrained-other-window-loc | |
533 actual-current-window | |
534 dir) | |
535 constrained-other-window-loc))) | |
536 (window-at (car other-window-loc) | |
537 (cdr other-window-loc)))) | |
538 | |
539 | |
540 ;; Selects the window that's hopefully at the location returned by | |
541 ;; `windmove-other-window-loc', or screams if there's no window there. | |
542 (defun windmove-do-window-select (dir &optional arg window) | |
30892
495502641770
(windmove) <defgroup>: Add :version.
Dave Love <fx@gnu.org>
parents:
27545
diff
changeset
|
543 "Move to the window at direction DIR. |
27545 | 544 DIR, ARG, and WINDOW are handled as by `windmove-other-window-loc'. |
545 If no window is at direction DIR, an error is signaled." | |
546 (let ((other-window (windmove-find-other-window dir arg window))) | |
547 (cond ((null other-window) | |
51424
26d468be5b3c
(windmove-reference-loc, windmove-frame-edges): Use window-inside-edges.
Richard M. Stallman <rms@gnu.org>
parents:
45166
diff
changeset
|
548 (error "No window %s from selected window" dir)) |
27545 | 549 ((and (window-minibuffer-p other-window) |
550 (not (minibuffer-window-active-p other-window))) | |
51424
26d468be5b3c
(windmove-reference-loc, windmove-frame-edges): Use window-inside-edges.
Richard M. Stallman <rms@gnu.org>
parents:
45166
diff
changeset
|
551 (error "Minibuffer is inactive")) |
27545 | 552 (t |
553 (select-window other-window))))) | |
554 | |
555 | |
556 ;;; end-user functions | |
557 ;; these are all simple interactive wrappers to `windmove-do- | |
558 ;; window-select', meant to be bound to keys. | |
559 | |
560 ;;;###autoload | |
561 (defun windmove-left (&optional arg) | |
562 "Select the window to the left of the current one. | |
563 With no prefix argument, or with prefix argument equal to zero, | |
564 \"left\" is relative to the position of point in the window; otherwise | |
565 it is relative to the top edge (for positive ARG) or the bottom edge | |
39588 | 566 \(for negative ARG) of the current window. |
27545 | 567 If no window is at the desired location, an error is signaled." |
568 (interactive "P") | |
569 (windmove-do-window-select 'left arg)) | |
570 | |
571 ;;;###autoload | |
572 (defun windmove-up (&optional arg) | |
573 "Select the window above the current one. | |
574 With no prefix argument, or with prefix argument equal to zero, \"up\" | |
575 is relative to the position of point in the window; otherwise it is | |
576 relative to the left edge (for positive ARG) or the right edge (for | |
577 negative ARG) of the current window. | |
578 If no window is at the desired location, an error is signaled." | |
579 (interactive "P") | |
580 (windmove-do-window-select 'up arg)) | |
581 | |
582 ;;;###autoload | |
583 (defun windmove-right (&optional arg) | |
584 "Select the window to the right of the current one. | |
585 With no prefix argument, or with prefix argument equal to zero, | |
586 \"right\" is relative to the position of point in the window; | |
587 otherwise it is relative to the top edge (for positive ARG) or the | |
588 bottom edge (for negative ARG) of the current window. | |
589 If no window is at the desired location, an error is signaled." | |
590 (interactive "P") | |
591 (windmove-do-window-select 'right arg)) | |
592 | |
593 ;;;###autoload | |
594 (defun windmove-down (&optional arg) | |
595 "Select the window below the current one. | |
596 With no prefix argument, or with prefix argument equal to zero, | |
597 \"down\" is relative to the position of point in the window; otherwise | |
598 it is relative to the left edge (for positive ARG) or the right edge | |
39588 | 599 \(for negative ARG) of the current window. |
27545 | 600 If no window is at the desired location, an error is signaled." |
601 (interactive "P") | |
602 (windmove-do-window-select 'down arg)) | |
603 | |
604 | |
605 ;;; set up keybindings | |
606 ;; Idea for this function is from iswitchb.el, by Stephen Eglen | |
607 ;; (stephen@cns.ed.ac.uk). | |
608 ;; I don't think these bindings will work on non-X terminals; you | |
609 ;; probably want to use different bindings in that case. | |
610 | |
611 ;;;###autoload | |
45166
8de5fa67b6cb
(windmove-default-keybindings): Add optional parameter to allow using a
Juanma Barranquero <lekktu@gmail.com>
parents:
39588
diff
changeset
|
612 (defun windmove-default-keybindings (&optional modifier) |
8de5fa67b6cb
(windmove-default-keybindings): Add optional parameter to allow using a
Juanma Barranquero <lekktu@gmail.com>
parents:
39588
diff
changeset
|
613 "Set up keybindings for `windmove'. |
8de5fa67b6cb
(windmove-default-keybindings): Add optional parameter to allow using a
Juanma Barranquero <lekktu@gmail.com>
parents:
39588
diff
changeset
|
614 Keybindings are of the form MODIFIER-{left,right,up,down}. |
8de5fa67b6cb
(windmove-default-keybindings): Add optional parameter to allow using a
Juanma Barranquero <lekktu@gmail.com>
parents:
39588
diff
changeset
|
615 Default MODIFIER is 'shift." |
27545 | 616 (interactive) |
45166
8de5fa67b6cb
(windmove-default-keybindings): Add optional parameter to allow using a
Juanma Barranquero <lekktu@gmail.com>
parents:
39588
diff
changeset
|
617 (unless modifier (setq modifier 'shift)) |
8de5fa67b6cb
(windmove-default-keybindings): Add optional parameter to allow using a
Juanma Barranquero <lekktu@gmail.com>
parents:
39588
diff
changeset
|
618 (global-set-key (vector (list modifier 'left)) 'windmove-left) |
8de5fa67b6cb
(windmove-default-keybindings): Add optional parameter to allow using a
Juanma Barranquero <lekktu@gmail.com>
parents:
39588
diff
changeset
|
619 (global-set-key (vector (list modifier 'right)) 'windmove-right) |
8de5fa67b6cb
(windmove-default-keybindings): Add optional parameter to allow using a
Juanma Barranquero <lekktu@gmail.com>
parents:
39588
diff
changeset
|
620 (global-set-key (vector (list modifier 'up)) 'windmove-up) |
8de5fa67b6cb
(windmove-default-keybindings): Add optional parameter to allow using a
Juanma Barranquero <lekktu@gmail.com>
parents:
39588
diff
changeset
|
621 (global-set-key (vector (list modifier 'down)) 'windmove-down)) |
27545 | 622 |
623 | |
624 (provide 'windmove) | |
625 | |
52401 | 626 ;;; arch-tag: 56267432-bf1a-4296-a9a0-85c6bd9f2375 |
27545 | 627 ;;; windmove.el ends here |