]> code.delx.au - gnu-emacs/blob - lisp/window.el
*** empty log message ***
[gnu-emacs] / lisp / window.el
1 ;;; window.el --- GNU Emacs window commands aside from those written in C.
2
3 ;; Copyright (C) 1985, 1989, 1992, 1993, 1994, 2000
4 ;; Free Software Foundation, Inc.
5
6 ;; Maintainer: FSF
7
8 ;; This file is part of GNU Emacs.
9
10 ;; GNU Emacs is free software; you can redistribute it and/or modify
11 ;; it under the terms of the GNU General Public License as published by
12 ;; the Free Software Foundation; either version 2, or (at your option)
13 ;; any later version.
14
15 ;; GNU Emacs is distributed in the hope that it will be useful,
16 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 ;; GNU General Public License for more details.
19
20 ;; You should have received a copy of the GNU General Public License
21 ;; along with GNU Emacs; see the file COPYING. If not, write to the
22 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
23 ;; Boston, MA 02111-1307, USA.
24
25 ;;; Code:
26
27 ;;;; Window tree functions.
28
29 (defun one-window-p (&optional nomini all-frames)
30 "Returns non-nil if the selected window is the only window (in its frame).
31 Optional arg NOMINI non-nil means don't count the minibuffer
32 even if it is active.
33
34 The optional arg ALL-FRAMES t means count windows on all frames.
35 If it is `visible', count windows on all visible frames.
36 ALL-FRAMES nil or omitted means count only the selected frame,
37 plus the minibuffer it uses (which may be on another frame).
38 If ALL-FRAMES is neither nil nor t, count only the selected frame."
39 (let ((base-window (selected-window)))
40 (if (and nomini (eq base-window (minibuffer-window)))
41 (setq base-window (next-window base-window)))
42 (eq base-window
43 (next-window base-window (if nomini 'arg) all-frames))))
44
45 (defun walk-windows (proc &optional minibuf all-frames)
46 "Cycle through all visible windows, calling PROC for each one.
47 PROC is called with a window as argument.
48
49 Optional second arg MINIBUF t means count the minibuffer window even
50 if not active. MINIBUF nil or omitted means count the minibuffer iff
51 it is active. MINIBUF neither t nor nil means not to count the
52 minibuffer even if it is active.
53
54 Several frames may share a single minibuffer; if the minibuffer
55 counts, all windows on all frames that share that minibuffer count
56 too. Therefore, if you are using a separate minibuffer frame
57 and the minibuffer is active and MINIBUF says it counts,
58 `walk-windows' includes the windows in the frame from which you
59 entered the minibuffer, as well as the minibuffer window.
60
61 ALL-FRAMES is the optional third argument.
62 ALL-FRAMES nil or omitted means cycle within the frames as specified above.
63 ALL-FRAMES = `visible' means include windows on all visible frames.
64 ALL-FRAMES = 0 means include windows on all visible and iconified frames.
65 ALL-FRAMES = t means include windows on all frames including invisible frames.
66 If ALL-FRAMES is a frame, it means include windows on that frame.
67 Anything else means restrict to the selected frame."
68 ;; If we start from the minibuffer window, don't fail to come back to it.
69 (if (window-minibuffer-p (selected-window))
70 (setq minibuf t))
71 (save-selected-window
72 (if (framep all-frames)
73 (select-window (frame-first-window all-frames)))
74 (let* ((walk-windows-start (selected-window))
75 (walk-windows-current walk-windows-start))
76 (while (progn
77 (setq walk-windows-current
78 (next-window walk-windows-current minibuf all-frames))
79 (funcall proc walk-windows-current)
80 (not (eq walk-windows-current walk-windows-start)))))))
81
82 (defun minibuffer-window-active-p (window)
83 "Return t if WINDOW (a minibuffer window) is now active."
84 (eq window (active-minibuffer-window)))
85
86 (defmacro save-selected-window (&rest body)
87 "Execute BODY, then select the window that was selected before BODY."
88 (list 'let
89 '((save-selected-window-window (selected-window)))
90 (list 'unwind-protect
91 (cons 'progn body)
92 (list 'select-window 'save-selected-window-window))))
93 \f
94 (defun count-windows (&optional minibuf)
95 "Returns the number of visible windows.
96 This counts the windows in the selected frame and (if the minibuffer is
97 to be counted) its minibuffer frame (if that's not the same frame).
98 The optional arg MINIBUF non-nil means count the minibuffer
99 even if it is inactive."
100 (let ((count 0))
101 (walk-windows (function (lambda (w)
102 (setq count (+ count 1))))
103 minibuf)
104 count))
105
106 (defun balance-windows ()
107 "Makes all visible windows the same height (approximately)."
108 (interactive)
109 (let ((count -1) levels newsizes size
110 ;; Don't count the lines that are above the uppermost windows.
111 ;; (These are the menu bar lines, if any.)
112 (mbl (nth 1 (window-edges (frame-first-window (selected-frame))))))
113 ;; Find all the different vpos's at which windows start,
114 ;; then count them. But ignore levels that differ by only 1.
115 (save-window-excursion
116 (let (tops (prev-top -2))
117 (walk-windows (function (lambda (w)
118 (setq tops (cons (nth 1 (window-edges w))
119 tops))))
120 'nomini)
121 (setq tops (sort tops '<))
122 (while tops
123 (if (> (car tops) (1+ prev-top))
124 (setq prev-top (car tops)
125 count (1+ count)))
126 (setq levels (cons (cons (car tops) count) levels))
127 (setq tops (cdr tops)))
128 (setq count (1+ count))))
129 ;; Subdivide the frame into that many vertical levels.
130 (setq size (/ (- (frame-height) mbl) count))
131 (walk-windows (function
132 (lambda (w)
133 (select-window w)
134 (let ((newtop (cdr (assq (nth 1 (window-edges))
135 levels)))
136 (newbot (or (cdr (assq (+ (window-height)
137 (nth 1 (window-edges)))
138 levels))
139 count)))
140 (setq newsizes
141 (cons (cons w (* size (- newbot newtop)))
142 newsizes)))))
143 'nomini)
144 (walk-windows (function (lambda (w)
145 (select-window w)
146 (let ((newsize (cdr (assq w newsizes))))
147 (enlarge-window (- newsize
148 (window-height))))))
149 'nomini)))
150 \f
151 ;;; I think this should be the default; I think people will prefer it--rms.
152 (defcustom split-window-keep-point t
153 "*If non-nil, split windows keeps the original point in both children.
154 This is often more convenient for editing.
155 If nil, adjust point in each of the two windows to minimize redisplay.
156 This is convenient on slow terminals, but point can move strangely."
157 :type 'boolean
158 :group 'windows)
159
160 (defun split-window-vertically (&optional arg)
161 "Split current window into two windows, one above the other.
162 The uppermost window gets ARG lines and the other gets the rest.
163 Negative arg means select the size of the lowermost window instead.
164 With no argument, split equally or close to it.
165 Both windows display the same buffer now current.
166
167 If the variable `split-window-keep-point' is non-nil, both new windows
168 will get the same value of point as the current window. This is often
169 more convenient for editing.
170
171 Otherwise, we chose window starts so as to minimize the amount of
172 redisplay; this is convenient on slow terminals. The new selected
173 window is the one that the current value of point appears in. The
174 value of point can change if the text around point is hidden by the
175 new mode line."
176 (interactive "P")
177 (let ((old-w (selected-window))
178 (old-point (point))
179 (size (and arg (prefix-numeric-value arg)))
180 (window-full-p nil)
181 new-w bottom switch moved)
182 (and size (< size 0) (setq size (+ (window-height) size)))
183 (setq new-w (split-window nil size))
184 (or split-window-keep-point
185 (progn
186 (save-excursion
187 (set-buffer (window-buffer))
188 (goto-char (window-start))
189 (setq moved (vertical-motion (window-height)))
190 (set-window-start new-w (point))
191 (if (> (point) (window-point new-w))
192 (set-window-point new-w (point)))
193 (and (= moved (window-height))
194 (progn
195 (setq window-full-p t)
196 (vertical-motion -1)))
197 (setq bottom (point)))
198 (and window-full-p
199 (<= bottom (point))
200 (set-window-point old-w (1- bottom)))
201 (and window-full-p
202 (<= (window-start new-w) old-point)
203 (progn
204 (set-window-point new-w old-point)
205 (select-window new-w)))))
206 (split-window-save-restore-data new-w old-w)))
207
208 ;; This is to avoid compiler warnings.
209 (defvar view-return-to-alist)
210
211 (defun split-window-save-restore-data (new-w old-w)
212 (save-excursion
213 (set-buffer (window-buffer))
214 (if view-mode
215 (let ((old-info (assq old-w view-return-to-alist)))
216 (setq view-return-to-alist
217 (cons (cons new-w (cons (and old-info (car (cdr old-info))) t))
218 view-return-to-alist))))
219 new-w))
220
221 (defun split-window-horizontally (&optional arg)
222 "Split current window into two windows side by side.
223 This window becomes the leftmost of the two, and gets ARG columns.
224 Negative arg means select the size of the rightmost window instead.
225 The argument includes the width of the window's scroll bar; if there
226 are no scroll bars, it includes the width of the divider column
227 to the window's right, if any. No arg means split equally."
228 (interactive "P")
229 (let ((old-w (selected-window))
230 (size (and arg (prefix-numeric-value arg))))
231 (and size (< size 0)
232 (setq size (+ (window-width) size)))
233 (split-window-save-restore-data (split-window nil size t) old-w)))
234 \f
235 (defun enlarge-window-horizontally (arg)
236 "Make current window ARG columns wider."
237 (interactive "p")
238 (enlarge-window arg t))
239
240 (defun shrink-window-horizontally (arg)
241 "Make current window ARG columns narrower."
242 (interactive "p")
243 (shrink-window arg t))
244
245 (defun window-buffer-height (window)
246 "Return the height (in screen lines) of the buffer that WINDOW is displaying."
247 (save-excursion
248 (set-buffer (window-buffer window))
249 (goto-char (point-min))
250 (let ((ignore-final-newline
251 ;; If buffer ends with a newline, ignore it when counting height
252 ;; unless point is after it.
253 (and (not (eobp)) (eq ?\n (char-after (1- (point-max)))))))
254 (+ 1 (nth 2 (compute-motion (point-min)
255 '(0 . 0)
256 (- (point-max) (if ignore-final-newline 1 0))
257 (cons 0 100000000)
258 (window-width window)
259 nil
260 window))))))
261
262 (defun count-screen-lines (&optional beg end count-final-newline window)
263 "Return the number of screen lines in the region.
264 The number of screen lines may be different from the number of actual lines,
265 due to line breaking, display table, etc.
266
267 Optional arguments BEG and END default to `point-min' and `point-max'
268 respectively.
269
270 If region ends with a newline, ignore it unless optinal third argument
271 COUNT-FINAL-NEWLINE is non-nil.
272
273 The optional fourth argument WINDOW specifies the window used for obtaining
274 parameters such as width, horizontal scrolling, and so on. The default is
275 to use the selected window's parameters.
276
277 Like `vertical-motion', `count-screen-lines' always uses the current buffer,
278 regardless of which buffer is displayed in WINDOW. This makes possible to use
279 `count-screen-lines' in any buffer, whether or not it is currently displayed
280 in some window."
281 (unless beg
282 (setq beg (point-min)))
283 (unless end
284 (setq end (point-max)))
285 (if (= beg end)
286 0
287 (save-excursion
288 (save-restriction
289 (widen)
290 (narrow-to-region (min beg end)
291 (if (and (not count-final-newline)
292 (= ?\n (char-before (max beg end))))
293 (1- (max beg end))
294 (max beg end)))
295 (goto-char (point-min))
296 (1+ (vertical-motion (buffer-size) window))))))
297
298 (defun shrink-window-if-larger-than-buffer (&optional window)
299 "Shrink the WINDOW to be as small as possible to display its contents.
300 Do not shrink to less than `window-min-height' lines.
301 Do nothing if the buffer contains more lines than the present window height,
302 or if some of the window's contents are scrolled out of view,
303 or if the window is not the full width of the frame,
304 or if the window is the only window of its frame."
305 (interactive)
306 (save-selected-window
307 (if window
308 (select-window window)
309 (setq window (selected-window)))
310 (let* ((params (frame-parameters))
311 (mini (cdr (assq 'minibuffer params)))
312 (edges (window-edges)))
313 (if (and (< 1 (count-windows))
314 (= (window-width) (frame-width))
315 (pos-visible-in-window-p (point-min) window)
316 (not (eq mini 'only))
317 (or (not mini)
318 (< (nth 3 edges) (nth 1 (window-edges mini)))
319 (> (nth 1 edges) (cdr (assq 'menu-bar-lines params)))))
320 ;; `count-screen-lines' always works on the current buffer, so
321 ;; make sure it is the buffer displayed by WINDOW.
322 (let ((text-height (with-current-buffer (window-buffer window)
323 (count-screen-lines)))
324 (window-height (window-height)))
325 ;; Don't try to redisplay with the cursor at the end
326 ;; on its own line--that would force a scroll and spoil things.
327 (when (and (eobp) (bolp) (not (bobp)))
328 (forward-char -1))
329 (when (> window-height (1+ text-height))
330 (shrink-window
331 (- window-height (max (1+ text-height) window-min-height)))))))))
332
333 (defun kill-buffer-and-window ()
334 "Kill the current buffer and delete the selected window."
335 (interactive)
336 (if (yes-or-no-p (format "Kill buffer `%s'? " (buffer-name)))
337 (let ((buffer (current-buffer)))
338 (delete-window (selected-window))
339 (kill-buffer buffer))
340 (error "Aborted")))
341
342 (defun quit-window (&optional kill window)
343 "Quit the current buffer. Bury it, and maybe delete the selected frame.
344 \(The frame is deleted if it is contains a dedicated window for the buffer.)
345 With a prefix argument, kill the buffer instead.
346
347 Noninteractively, if KILL is non-nil, then kill the current buffer,
348 otherwise bury it.
349
350 If WINDOW is non-nil, it specifies a window; we delete that window,
351 and the buffer that is killed or buried is the one in that window."
352 (interactive "P")
353 (let ((buffer (window-buffer window))
354 (frame (window-frame (or window (selected-window))))
355 (window-solitary
356 (save-selected-window
357 (if window
358 (select-window window))
359 (one-window-p t)))
360 window-handled)
361
362 (save-selected-window
363 (if window
364 (select-window window))
365 (or (window-minibuffer-p)
366 (window-dedicated-p (selected-window))
367 (switch-to-buffer (other-buffer))))
368
369 ;; Get rid of the frame, if it has just one dedicated window
370 ;; and other visible frames exist.
371 (and (or (window-minibuffer-p) (window-dedicated-p window))
372 (delq frame (visible-frame-list))
373 window-solitary
374 (if (and (eq default-minibuffer-frame frame)
375 (= 1 (length (minibuffer-frame-list))))
376 (setq window nil)
377 (delete-frame frame)
378 (setq window-handled t)))
379
380 ;; Deal with the buffer.
381 (if kill
382 (kill-buffer buffer)
383 (bury-buffer buffer))
384
385 ;; Maybe get rid of the window.
386 (and window (not window-handled) (not window-solitary)
387 (delete-window window))))
388
389 (define-key ctl-x-map "2" 'split-window-vertically)
390 (define-key ctl-x-map "3" 'split-window-horizontally)
391 (define-key ctl-x-map "}" 'enlarge-window-horizontally)
392 (define-key ctl-x-map "{" 'shrink-window-horizontally)
393 (define-key ctl-x-map "-" 'shrink-window-if-larger-than-buffer)
394 (define-key ctl-x-map "+" 'balance-windows)
395 (define-key ctl-x-4-map "0" 'kill-buffer-and-window)
396
397 ;;; windows.el ends here