In fit-window-to-buffer fix argument in window-size call.
[bpt/emacs.git] / lisp / window.el
CommitLineData
3c448ab6
MR
1;;; window.el --- GNU Emacs window commands aside from those written in C
2
ba318903
PE
3;; Copyright (C) 1985, 1989, 1992-1994, 2000-2014 Free Software
4;; Foundation, Inc.
3c448ab6 5
34dc21db 6;; Maintainer: emacs-devel@gnu.org
3c448ab6 7;; Keywords: internal
bd78fa1d 8;; Package: emacs
3c448ab6
MR
9
10;; This file is part of GNU Emacs.
11
12;; GNU Emacs is free software: you can redistribute it and/or modify
13;; it under the terms of the GNU General Public License as published by
14;; the Free Software Foundation, either version 3 of the License, or
15;; (at your option) 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. If not, see <http://www.gnu.org/licenses/>.
24
25;;; Commentary:
26
27;; Window tree functions.
28
29;;; Code:
30
662a9d0e
SM
31(defun internal--before-save-selected-window ()
32 (cons (selected-window)
33 ;; We save and restore all frames' selected windows, because
34 ;; `select-window' can change the frame-selected-window of
35 ;; whatever frame that window is in. Each text terminal's
36 ;; top-frame is preserved by putting it last in the list.
37 (apply #'append
38 (mapcar (lambda (terminal)
39 (let ((frames (frames-on-display-list terminal))
40 (top-frame (tty-top-frame terminal))
41 alist)
42 (if top-frame
43 (setq frames
44 (cons top-frame
45 (delq top-frame frames))))
46 (dolist (f frames)
47 (push (cons f (frame-selected-window f))
48 alist))
49 alist))
50 (terminal-list)))))
51
52(defun internal--after-save-selected-window (state)
53 (dolist (elt (cdr state))
54 (and (frame-live-p (car elt))
55 (window-live-p (cdr elt))
56 (set-frame-selected-window (car elt) (cdr elt) 'norecord)))
57 (when (window-live-p (car state))
58 (select-window (car state) 'norecord)))
59
3c448ab6
MR
60(defmacro save-selected-window (&rest body)
61 "Execute BODY, then select the previously selected window.
62The value returned is the value of the last form in BODY.
63
64This macro saves and restores the selected window, as well as the
65selected window in each frame. If the previously selected window
66is no longer live, then whatever window is selected at the end of
67BODY remains selected. If the previously selected window of some
68frame is no longer live at the end of BODY, that frame's selected
69window is left alone.
70
71This macro saves and restores the current buffer, since otherwise
72its normal operation could make a different buffer current. The
73order of recently selected windows and the buffer list ordering
74are not altered by this macro (unless they are altered in BODY)."
f291fe60 75 (declare (indent 0) (debug t))
662a9d0e 76 `(let ((save-selected-window--state (internal--before-save-selected-window)))
3c448ab6
MR
77 (save-current-buffer
78 (unwind-protect
79 (progn ,@body)
662a9d0e 80 (internal--after-save-selected-window save-selected-window--state)))))
3c448ab6 81
c5e28e39
MR
82(defvar temp-buffer-window-setup-hook nil
83 "Normal hook run by `with-temp-buffer-window' before buffer display.
84This hook is run by `with-temp-buffer-window' with the buffer to be
85displayed current.")
86
87(defvar temp-buffer-window-show-hook nil
88 "Normal hook run by `with-temp-buffer-window' after buffer display.
89This hook is run by `with-temp-buffer-window' with the buffer
90displayed and current and its window selected.")
91
92(defun temp-buffer-window-setup (buffer-or-name)
42019c2e 93 "Set up temporary buffer specified by BUFFER-OR-NAME.
c5e28e39
MR
94Return the buffer."
95 (let ((old-dir default-directory)
96 (buffer (get-buffer-create buffer-or-name)))
97 (with-current-buffer buffer
98 (kill-all-local-variables)
99 (setq default-directory old-dir)
100 (delete-all-overlays)
101 (setq buffer-read-only nil)
102 (setq buffer-file-name nil)
103 (setq buffer-undo-list t)
104 (let ((inhibit-read-only t)
105 (inhibit-modification-hooks t))
106 (erase-buffer)
107 (run-hooks 'temp-buffer-window-setup-hook))
108 ;; Return the buffer.
109 buffer)))
110
111(defun temp-buffer-window-show (&optional buffer action)
112 "Show temporary buffer BUFFER in a window.
113Return the window showing BUFFER. Pass ACTION as action argument
114to `display-buffer'."
115 (let (window frame)
116 (with-current-buffer buffer
117 (set-buffer-modified-p nil)
118 (setq buffer-read-only t)
119 (goto-char (point-min))
8e17c9ba
MR
120 (when (let ((window-combination-limit
121 ;; When `window-combination-limit' equals
122 ;; `temp-buffer' or `temp-buffer-resize' and
123 ;; `temp-buffer-resize-mode' is enabled in this
124 ;; buffer bind it to t so resizing steals space
125 ;; preferably from the window that was split.
126 (if (or (eq window-combination-limit 'temp-buffer)
127 (and (eq window-combination-limit
128 'temp-buffer-resize)
129 temp-buffer-resize-mode))
130 t
131 window-combination-limit)))
132 (setq window (display-buffer buffer action)))
c5e28e39
MR
133 (setq frame (window-frame window))
134 (unless (eq frame (selected-frame))
135 (raise-frame frame))
136 (setq minibuffer-scroll-window window)
137 (set-window-hscroll window 0)
138 (with-selected-window window
139 (run-hooks 'temp-buffer-window-show-hook)
140 (when temp-buffer-resize-mode
141 (resize-temp-buffer-window window)))
142 ;; Return the window.
143 window))))
144
145(defmacro with-temp-buffer-window (buffer-or-name action quit-function &rest body)
9dda75ad
MR
146 "Evaluate BODY in a buffer BUFFER-OR-NAME and show that buffer.
147BUFFER-OR-NAME must specify either a live buffer, or the name of
148a buffer (if it does not exist, this macro creates it).
149
150Make the buffer specified by BUFFER-OR-NAME empty before running
151BODY and make that buffer current for running the forms in BODY.
152In addition, bind `standard-output' to that buffer, so that
153output generated with `prin1' and similar functions in BODY goes
154into that buffer.
155
156At the end of BODY, mark the specified buffer unmodified and
157read-only, and display it in a window (but do not select it).
158The display happens by calling `display-buffer' passing it the
159ACTION argument. If `temp-buffer-resize-mode' is enabled, the
160corresponding window may shrink automatically.
161
162Return the value returned by BODY, unless QUIT-FUNCTION specifies
163a function. In that case, run that function with two arguments -
c5e28e39 164the window showing the specified buffer and the value returned by
9dda75ad 165BODY - and return the value returned by that function.
c5e28e39
MR
166
167If the buffer is displayed on a new frame, the window manager may
168decide to select that frame. In that case, it's usually a good
65463c40
GM
169strategy if QUIT-FUNCTION selects the window showing the buffer
170before reading any value from the minibuffer; for example, when
171asking a `yes-or-no-p' question.
38785e75 172
95f0501e 173This runs the hook `temp-buffer-window-setup-hook' before BODY,
9dda75ad
MR
174with the specified buffer temporarily current. It runs the hook
175`temp-buffer-window-show-hook' after displaying the buffer, with
176that buffer temporarily current, and the window that was used to
95f0501e
GM
177display it temporarily selected.
178
9dda75ad
MR
179This construct is similar to `with-output-to-temp-buffer' but,
180unlike that, makes BUFFER-OR-NAME current when running BODY.
181Also, it neither runs `temp-buffer-setup-hook' which usually puts
182the buffer in Help mode, nor `temp-buffer-show-function' (the
183ACTION argument replaces this)."
c5e28e39
MR
184 (declare (debug t))
185 (let ((buffer (make-symbol "buffer"))
186 (window (make-symbol "window"))
187 (value (make-symbol "value")))
188 `(let* ((,buffer (temp-buffer-window-setup ,buffer-or-name))
189 (standard-output ,buffer)
190 ,window ,value)
191 (with-current-buffer ,buffer
9dda75ad 192 (setq ,value (progn ,@body))
c5e28e39
MR
193 (setq ,window (temp-buffer-window-show ,buffer ,action)))
194
195 (if (functionp ,quit-function)
196 (funcall ,quit-function ,window ,value)
197 ,value))))
198
d68443dc
MR
199;; The following two functions are like `window-next-sibling' and
200;; `window-prev-sibling' but the WINDOW argument is _not_ optional (so
201;; they don't substitute the selected window for nil), and they return
202;; nil when WINDOW doesn't have a parent (like a frame's root window or
203;; a minibuffer window).
4b0d61e3 204(defun window-right (window)
85cc1f11
MR
205 "Return WINDOW's right sibling.
206Return nil if WINDOW is the root window of its frame. WINDOW can
207be any window."
d68443dc 208 (and window (window-parent window) (window-next-sibling window)))
85cc1f11 209
4b0d61e3 210(defun window-left (window)
85cc1f11
MR
211 "Return WINDOW's left sibling.
212Return nil if WINDOW is the root window of its frame. WINDOW can
213be any window."
d68443dc 214 (and window (window-parent window) (window-prev-sibling window)))
85cc1f11 215
4b0d61e3 216(defun window-child (window)
85c2386b
MR
217 "Return WINDOW's first child window.
218WINDOW can be any window."
d68443dc 219 (or (window-top-child window) (window-left-child window)))
85cc1f11
MR
220
221(defun window-child-count (window)
85c2386b
MR
222 "Return number of WINDOW's child windows.
223WINDOW can be any window."
85cc1f11
MR
224 (let ((count 0))
225 (when (and (windowp window) (setq window (window-child window)))
226 (while window
227 (setq count (1+ count))
d68443dc 228 (setq window (window-next-sibling window))))
85cc1f11
MR
229 count))
230
231(defun window-last-child (window)
85c2386b
MR
232 "Return last child window of WINDOW.
233WINDOW can be any window."
85cc1f11 234 (when (and (windowp window) (setq window (window-child window)))
d68443dc
MR
235 (while (window-next-sibling window)
236 (setq window (window-next-sibling window))))
85cc1f11
MR
237 window)
238
4b0d61e3 239(defun window-normalize-buffer (buffer-or-name)
85cc1f11
MR
240 "Return buffer specified by BUFFER-OR-NAME.
241BUFFER-OR-NAME must be either a buffer or a string naming a live
242buffer and defaults to the current buffer."
243 (cond
244 ((not buffer-or-name)
245 (current-buffer))
246 ((bufferp buffer-or-name)
247 (if (buffer-live-p buffer-or-name)
248 buffer-or-name
249 (error "Buffer %s is not a live buffer" buffer-or-name)))
250 ((get-buffer buffer-or-name))
251 (t
252 (error "No such buffer %s" buffer-or-name))))
253
4b0d61e3 254(defun window-normalize-frame (frame)
85cc1f11
MR
255 "Return frame specified by FRAME.
256FRAME must be a live frame and defaults to the selected frame."
257 (if frame
258 (if (frame-live-p frame)
259 frame
260 (error "%s is not a live frame" frame))
261 (selected-frame)))
262
4b0d61e3 263(defun window-normalize-window (window &optional live-only)
85c2386b
MR
264 "Return the window specified by WINDOW.
265If WINDOW is nil, return the selected window. Otherwise, if
266WINDOW is a live or an internal window, return WINDOW; if
267LIVE-ONLY is non-nil, return WINDOW for a live window only.
447f16b8 268Otherwise, signal an error."
85c2386b
MR
269 (cond
270 ((null window)
271 (selected-window))
272 (live-only
273 (if (window-live-p window)
274 window
275 (error "%s is not a live window" window)))
276 ((window-valid-p window)
277 window)
278 (t
279 (error "%s is not a valid window" window))))
85cc1f11 280
880e6158
MR
281;; Maybe this should go to frame.el.
282(defun frame-char-size (&optional window-or-frame horizontal)
283 "Return the value of `frame-char-height' for WINDOW-OR-FRAME.
284If WINDOW-OR-FRAME is a live frame, return the value of
285`frame-char-height' for that frame. If WINDOW-OR-FRAME is a
286valid window, return the value of `frame-char-height' for that
287window's frame. In any other case, return the value of
288`frame-char-height' for the selected frame.
289
290Optional argument HORIZONTAL non-nil means to return the value of
291`frame-char-width' for WINDOW-OR-FRAME."
292 (let ((frame
293 (cond
294 ((window-valid-p window-or-frame)
295 (window-frame window-or-frame))
296 ((frame-live-p window-or-frame)
297 window-or-frame)
298 (t (selected-frame)))))
299 (if horizontal
300 (frame-char-width frame)
301 (frame-char-height frame))))
302
85cc1f11
MR
303(defvar ignore-window-parameters nil
304 "If non-nil, standard functions ignore window parameters.
305The functions currently affected by this are `split-window',
306`delete-window', `delete-other-windows' and `other-window'.
307
308An application may bind this to a non-nil value around calls to
309these functions to inhibit processing of window parameters.")
310
880e6158 311;; This must go to C, finally (or get removed).
a1511caf 312(defconst window-safe-min-height 1
880e6158 313 "The absolute minimum number of lines of any window.
a1511caf
MR
314Anything less might crash Emacs.")
315
880e6158
MR
316(defun window-safe-min-pixel-height (&optional window)
317 "Return the absolute minimum pixel height of WINDOW."
318 (* window-safe-min-height
319 (frame-char-size (window-normalize-window window))))
320
562dd5e9
MR
321(defcustom window-min-height 4
322 "The minimum number of lines of any window.
9173deec
JB
323The value has to accommodate a mode- or header-line if present.
324A value less than `window-safe-min-height' is ignored. The value
562dd5e9
MR
325of this variable is honored when windows are resized or split.
326
327Applications should never rebind this variable. To resize a
328window to a height less than the one specified here, an
d615d6d2 329application should instead call `window-resize' with a non-nil
562dd5e9 330IGNORE argument. In order to have `split-window' make a window
e4769531 331shorter, explicitly specify the SIZE argument of that function."
562dd5e9
MR
332 :type 'integer
333 :version "24.1"
334 :group 'windows)
335
880e6158
MR
336(defun window-min-pixel-height (&optional window)
337 "Return the minimum pixel height of window WINDOW."
338 (* (max window-min-height window-safe-min-height)
339 (frame-char-size window)))
340
341;; This must go to C, finally (or get removed).
a1511caf 342(defconst window-safe-min-width 2
8da11505 343 "The absolute minimum number of columns of a window.
a1511caf
MR
344Anything less might crash Emacs.")
345
880e6158
MR
346(defun window-safe-min-pixel-width (&optional window)
347 "Return the absolute minimum pixel width of WINDOW."
348 (* window-safe-min-width
349 (frame-char-size (window-normalize-window window) t)))
350
562dd5e9
MR
351(defcustom window-min-width 10
352 "The minimum number of columns of any window.
a91adc7e 353The value has to accommodate margins, fringes, or scrollbars if
562dd5e9
MR
354present. A value less than `window-safe-min-width' is ignored.
355The value of this variable is honored when windows are resized or
356split.
357
358Applications should never rebind this variable. To resize a
359window to a width less than the one specified here, an
d615d6d2 360application should instead call `window-resize' with a non-nil
562dd5e9 361IGNORE argument. In order to have `split-window' make a window
e4769531 362narrower, explicitly specify the SIZE argument of that function."
562dd5e9
MR
363 :type 'integer
364 :version "24.1"
365 :group 'windows)
366
880e6158
MR
367(defun window-min-pixel-width (&optional window)
368 "Return the minimum pixel width of window WINDOW."
369 (* (max window-min-width window-safe-min-width)
370 (frame-char-size window t)))
371
372(defun window-safe-min-pixel-size (&optional window horizontal)
373 "Return the absolute minimum pixel height of WINDOW.
374Optional argument HORIZONTAL non-nil means return the absolute
375minimum pixel width of WINDOW."
376 (if horizontal
377 (window-safe-min-pixel-width window)
378 (window-safe-min-pixel-height window)))
379
4b0d61e3 380(defun window-combined-p (&optional window horizontal)
49745b39 381 "Return non-nil if WINDOW has siblings in a given direction.
85c2386b 382WINDOW must be a valid window and defaults to the selected one.
49745b39 383
880e6158
MR
384HORIZONTAL determines a direction for the window combination. If
385HORIZONTAL is omitted or nil, return non-nil if WINDOW is part of
386a vertical window combination. If HORIZONTAL is non-nil, return
387non-nil if WINDOW is part of a horizontal window combination."
447f16b8 388 (setq window (window-normalize-window window))
85cc1f11 389 (let ((parent (window-parent window)))
49745b39
CY
390 (and parent
391 (if horizontal
392 (window-left-child parent)
393 (window-top-child parent)))))
85cc1f11 394
880e6158
MR
395(defun window-combination-p (&optional window horizontal)
396 "Return WINDOW's first child if WINDOW is a vertical combination.
397WINDOW can be any window and defaults to the selected one.
398Optional argument HORIZONTAL non-nil means return WINDOW's first
399child if WINDOW is a horizontal combination."
400 (setq window (window-normalize-window window))
401 (if horizontal
402 (window-left-child window)
403 (window-top-child window)))
404
be7f5545
MR
405(defun window-combinations (window &optional horizontal)
406 "Return largest number of windows vertically arranged within WINDOW.
85c2386b 407WINDOW must be a valid window and defaults to the selected one.
49745b39 408If HORIZONTAL is non-nil, return the largest number of
be7f5545 409windows horizontally arranged within WINDOW."
447f16b8 410 (setq window (window-normalize-window window))
85cc1f11
MR
411 (cond
412 ((window-live-p window)
413 ;; If WINDOW is live, return 1.
414 1)
49745b39
CY
415 ((if horizontal
416 (window-left-child window)
417 (window-top-child window))
85cc1f11 418 ;; If WINDOW is iso-combined, return the sum of the values for all
be7f5545 419 ;; child windows of WINDOW.
85cc1f11
MR
420 (let ((child (window-child window))
421 (count 0))
422 (while child
423 (setq count
3d8daefe 424 (+ (window-combinations child horizontal)
85cc1f11
MR
425 count))
426 (setq child (window-right child)))
427 count))
428 (t
429 ;; If WINDOW is not iso-combined, return the maximum value of any
be7f5545 430 ;; child window of WINDOW.
85cc1f11
MR
431 (let ((child (window-child window))
432 (count 1))
433 (while child
434 (setq count
3d8daefe 435 (max (window-combinations child horizontal)
85cc1f11
MR
436 count))
437 (setq child (window-right child)))
438 count))))
439
c7635a97 440(defun walk-window-tree-1 (fun walk-window-tree-window any &optional sub-only)
85cc1f11
MR
441 "Helper function for `walk-window-tree' and `walk-window-subtree'."
442 (let (walk-window-tree-buffer)
443 (while walk-window-tree-window
444 (setq walk-window-tree-buffer
445 (window-buffer walk-window-tree-window))
446 (when (or walk-window-tree-buffer any)
c7635a97 447 (funcall fun walk-window-tree-window))
85cc1f11
MR
448 (unless walk-window-tree-buffer
449 (walk-window-tree-1
c7635a97 450 fun (window-left-child walk-window-tree-window) any)
85cc1f11 451 (walk-window-tree-1
c7635a97 452 fun (window-top-child walk-window-tree-window) any))
85cc1f11
MR
453 (if sub-only
454 (setq walk-window-tree-window nil)
455 (setq walk-window-tree-window
456 (window-right walk-window-tree-window))))))
457
ea95074e 458(defun walk-window-tree (fun &optional frame any minibuf)
c7635a97
CY
459 "Run function FUN on each live window of FRAME.
460FUN must be a function with one argument - a window. FRAME must
85cc1f11 461be a live frame and defaults to the selected one. ANY, if
ea95074e 462non-nil, means to run FUN on all live and internal windows of
85cc1f11
MR
463FRAME.
464
ea95074e
MR
465Optional argument MINIBUF t means run FUN on FRAME's minibuffer
466window even if it isn't active. MINIBUF nil or omitted means run
467FUN on FRAME's minibuffer window only if it's active. In both
468cases the minibuffer window must be part of FRAME. MINIBUF
469neither nil nor t means never run FUN on the minibuffer window.
470
85cc1f11 471This function performs a pre-order, depth-first traversal of the
c7635a97 472window tree. If FUN changes the window tree, the result is
85cc1f11 473unpredictable."
ea95074e
MR
474 (setq frame (window-normalize-frame frame))
475 (walk-window-tree-1 fun (frame-root-window frame) any)
476 (when (memq minibuf '(nil t))
477 ;; Run FUN on FRAME's minibuffer window if requested.
478 (let ((minibuffer-window (minibuffer-window frame)))
479 (when (and (window-live-p minibuffer-window)
480 (eq (window-frame minibuffer-window) frame)
481 (or (eq minibuf t)
482 (minibuffer-window-active-p minibuffer-window)))
483 (funcall fun minibuffer-window)))))
85cc1f11 484
c7635a97
CY
485(defun walk-window-subtree (fun &optional window any)
486 "Run function FUN on the subtree of windows rooted at WINDOW.
487WINDOW defaults to the selected window. FUN must be a function
488with one argument - a window. By default, run FUN only on live
be7f5545 489windows of the subtree. If the optional argument ANY is non-nil,
c7635a97
CY
490run FUN on all live and internal windows of the subtree. If
491WINDOW is live, run FUN on WINDOW only.
85cc1f11
MR
492
493This function performs a pre-order, depth-first traversal of the
c7635a97 494subtree rooted at WINDOW. If FUN changes that tree, the result
be7f5545 495is unpredictable."
447f16b8 496 (setq window (window-normalize-window window))
c7635a97 497 (walk-window-tree-1 fun window any t))
85cc1f11 498
ea95074e 499(defun window-with-parameter (parameter &optional value frame any minibuf)
85cc1f11
MR
500 "Return first window on FRAME with PARAMETER non-nil.
501FRAME defaults to the selected frame. Optional argument VALUE
502non-nil means only return a window whose window-parameter value
382c953b 503for PARAMETER equals VALUE (comparison is done with `equal').
85cc1f11 504Optional argument ANY non-nil means consider internal windows
ea95074e
MR
505too.
506
507Optional argument MINIBUF t means consider FRAME's minibuffer
508window even if it isn't active. MINIBUF nil or omitted means
509consider FRAME's minibuffer window only if it's active. In both
510cases the minibuffer window must be part of FRAME. MINIBUF
511neither nil nor t means never consider the minibuffer window."
cb882333 512 (let (this-value)
85cc1f11
MR
513 (catch 'found
514 (walk-window-tree
515 (lambda (window)
516 (when (and (setq this-value (window-parameter window parameter))
517 (or (not value) (equal value this-value)))
518 (throw 'found window)))
ea95074e 519 frame any minibuf))))
85cc1f11
MR
520
521;;; Atomic windows.
522(defun window-atom-root (&optional window)
523 "Return root of atomic window WINDOW is a part of.
85c2386b 524WINDOW must be a valid window and defaults to the selected one.
382c953b 525Return nil if WINDOW is not part of an atomic window."
447f16b8 526 (setq window (window-normalize-window window))
85cc1f11
MR
527 (let (root)
528 (while (and window (window-parameter window 'window-atom))
529 (setq root window)
530 (setq window (window-parent window)))
531 root))
532
5386012d 533(defun window-make-atom (window)
85cc1f11
MR
534 "Make WINDOW an atomic window.
535WINDOW must be an internal window. Return WINDOW."
536 (if (not (window-child window))
537 (error "Window %s is not an internal window" window)
538 (walk-window-subtree
539 (lambda (window)
c660a885
MR
540 (unless (window-parameter window 'window-atom)
541 (set-window-parameter window 'window-atom t)))
85cc1f11
MR
542 window t)
543 window))
544
caceae25
MR
545(defun display-buffer-in-atom-window (buffer alist)
546 "Display BUFFER in an atomic window.
547This function displays BUFFER in a new window that will be
548combined with an existing window to form an atomic window. If
549the existing window is already part of an atomic window, add the
550new window to that atomic window. Operations like `split-window'
551or `delete-window', when applied to a constituent of an atomic
552window, are applied atomically to the root of that atomic window.
553
554ALIST is an association list of symbols and values. The
555following symbols can be used.
556
557`window' specifies the existing window the new window shall be
558 combined with. Use `window-atom-root' to make the new window a
559 sibling of an atomic window's root. If an internal window is
560 specified here, all children of that window become part of the
561 atomic window too. If no window is specified, the new window
c660a885
MR
562 becomes a sibling of the selected window. By default, the
563 `window-atom' parameter of the existing window is set to `main'
564 provided it is live and was not set before.
caceae25
MR
565
566`side' denotes the side of the existing window where the new
567 window shall be located. Valid values are `below', `right',
c660a885
MR
568 `above' and `left'. The default is `below'. By default, the
569 `window-atom' parameter of the new window is set to this value.
caceae25
MR
570
571The return value is the new window, nil when creating that window
572failed."
c660a885
MR
573 (let* ((ignore-window-parameters t)
574 (window-combination-limit t)
575 (window-combination-resize 'atom)
576 (window (cdr (assq 'window alist)))
577 (side (cdr (assq 'side alist)))
578 (atom (when window (window-parameter window 'window-atom)))
579 root new)
caceae25 580 (setq window (window-normalize-window window))
c660a885
MR
581 (setq root (window-atom-root window))
582 ;; Split off new window.
caceae25 583 (when (setq new (split-window window nil side))
c660a885
MR
584 (window-make-atom
585 (if (and root (not (eq root window)))
586 ;; When WINDOW was part of an atomic window and we did not
587 ;; split its root, root atomic window at old root.
588 root
589 ;; Otherwise, root atomic window at WINDOW's new parent.
590 (window-parent window)))
591 ;; Assign `window-atom' parameters, if needed.
592 (when (and (not atom) (window-live-p window))
593 (set-window-parameter window 'window-atom 'main))
594 (set-window-parameter new 'window-atom side)
caceae25
MR
595 ;; Display BUFFER in NEW and return NEW.
596 (window--display-buffer
5938d519 597 buffer new 'window alist display-buffer-mark-dedicated))))
caceae25 598
54f9154c
MR
599(defun window--atom-check-1 (window)
600 "Subroutine of `window--atom-check'."
85cc1f11
MR
601 (when window
602 (if (window-parameter window 'window-atom)
603 (let ((count 0))
604 (when (or (catch 'reset
605 (walk-window-subtree
606 (lambda (window)
607 (if (window-parameter window 'window-atom)
608 (setq count (1+ count))
609 (throw 'reset t)))
610 window t))
611 ;; count >= 1 must hold here. If there's no other
612 ;; window around dissolve this atomic window.
613 (= count 1))
614 ;; Dissolve atomic window.
615 (walk-window-subtree
616 (lambda (window)
617 (set-window-parameter window 'window-atom nil))
618 window t)))
619 ;; Check children.
620 (unless (window-buffer window)
54f9154c
MR
621 (window--atom-check-1 (window-left-child window))
622 (window--atom-check-1 (window-top-child window))))
85cc1f11 623 ;; Check right sibling
54f9154c 624 (window--atom-check-1 (window-right window))))
85cc1f11 625
54f9154c 626(defun window--atom-check (&optional frame)
85cc1f11
MR
627 "Check atomicity of all windows on FRAME.
628FRAME defaults to the selected frame. If an atomic window is
be7f5545
MR
629wrongly configured, reset the atomicity of all its windows on
630FRAME to nil. An atomic window is wrongly configured if it has
631no child windows or one of its child windows is not atomic."
54f9154c 632 (window--atom-check-1 (frame-root-window frame)))
85cc1f11
MR
633
634;; Side windows.
635(defvar window-sides '(left top right bottom)
636 "Window sides.")
637
638(defcustom window-sides-vertical nil
639 "If non-nil, left and right side windows are full height.
640Otherwise, top and bottom side windows are full width."
641 :type 'boolean
642 :group 'windows
643 :version "24.1")
644
645(defcustom window-sides-slots '(nil nil nil nil)
646 "Maximum number of side window slots.
647The value is a list of four elements specifying the number of
382c953b 648side window slots on (in this order) the left, top, right and
85cc1f11
MR
649bottom side of each frame. If an element is a number, this means
650to display at most that many side windows on the corresponding
651side. If an element is nil, this means there's no bound on the
652number of slots on that side."
2bed3f04 653 :version "24.1"
85cc1f11
MR
654 :risky t
655 :type
656 '(list
657 :value (nil nil nil nil)
658 (choice
659 :tag "Left"
660 :help-echo "Maximum slots of left side window."
661 :value nil
662 :format "%[Left%] %v\n"
663 (const :tag "Unlimited" :format "%t" nil)
664 (integer :tag "Number" :value 2 :size 5))
665 (choice
666 :tag "Top"
667 :help-echo "Maximum slots of top side window."
668 :value nil
669 :format "%[Top%] %v\n"
670 (const :tag "Unlimited" :format "%t" nil)
671 (integer :tag "Number" :value 3 :size 5))
672 (choice
673 :tag "Right"
674 :help-echo "Maximum slots of right side window."
675 :value nil
676 :format "%[Right%] %v\n"
677 (const :tag "Unlimited" :format "%t" nil)
678 (integer :tag "Number" :value 2 :size 5))
679 (choice
680 :tag "Bottom"
681 :help-echo "Maximum slots of bottom side window."
682 :value nil
683 :format "%[Bottom%] %v\n"
684 (const :tag "Unlimited" :format "%t" nil)
685 (integer :tag "Number" :value 3 :size 5)))
686 :group 'windows)
687
caceae25
MR
688(defun window--major-non-side-window (&optional frame)
689 "Return the major non-side window of frame FRAME.
690The optional argument FRAME must be a live frame and defaults to
691the selected one.
692
693If FRAME has at least one side window, the major non-side window
694is either an internal non-side window such that all other
695non-side windows on FRAME descend from it, or the single live
696non-side window of FRAME. If FRAME has no side windows, return
697its root window."
698 (let ((frame (window-normalize-frame frame))
699 major sibling)
700 ;; Set major to the _last_ window found by `walk-window-tree' that
701 ;; is not a side window but has a side window as its sibling.
702 (walk-window-tree
703 (lambda (window)
704 (and (not (window-parameter window 'window-side))
705 (or (and (setq sibling (window-prev-sibling window))
706 (window-parameter sibling 'window-side))
707 (and (setq sibling (window-next-sibling window))
708 (window-parameter sibling 'window-side)))
709 (setq major window)))
c660a885 710 frame t 'nomini)
caceae25
MR
711 (or major (frame-root-window frame))))
712
713(defun window--major-side-window (side)
714 "Return major side window on SIDE.
715SIDE must be one of the symbols `left', `top', `right' or
716`bottom'. Return nil if no such window exists."
717 (let ((root (frame-root-window))
718 window)
719 ;; (1) If a window on the opposite side exists, return that window's
720 ;; sibling.
721 ;; (2) If the new window shall span the entire side, return the
722 ;; frame's root window.
723 ;; (3) If a window on an orthogonal side exists, return that
724 ;; window's sibling.
725 ;; (4) Otherwise return the frame's root window.
726 (cond
727 ((or (and (eq side 'left)
728 (setq window (window-with-parameter 'window-side 'right nil t)))
729 (and (eq side 'top)
730 (setq window (window-with-parameter 'window-side 'bottom nil t))))
731 (window-prev-sibling window))
732 ((or (and (eq side 'right)
733 (setq window (window-with-parameter 'window-side 'left nil t)))
734 (and (eq side 'bottom)
735 (setq window (window-with-parameter 'window-side 'top nil t))))
736 (window-next-sibling window))
737 ((memq side '(left right))
738 (cond
739 (window-sides-vertical
740 root)
741 ((setq window (window-with-parameter 'window-side 'top nil t))
742 (window-next-sibling window))
743 ((setq window (window-with-parameter 'window-side 'bottom nil t))
744 (window-prev-sibling window))
745 (t root)))
746 ((memq side '(top bottom))
747 (cond
748 ((not window-sides-vertical)
749 root)
750 ((setq window (window-with-parameter 'window-side 'left nil t))
751 (window-next-sibling window))
752 ((setq window (window-with-parameter 'window-side 'right nil t))
753 (window-prev-sibling window))
754 (t root))))))
755
756(defun display-buffer-in-major-side-window (buffer side slot &optional alist)
757 "Display BUFFER in a new window on SIDE of the selected frame.
758SIDE must be one of `left', `top', `right' or `bottom'. SLOT
759specifies the slot to use. ALIST is an association list of
760symbols and values as passed to `display-buffer-in-side-window'.
761This function may be called only if no window on SIDE exists yet.
762The new window automatically becomes the \"major\" side window on
763SIDE. Return the new window, nil if its creation window failed."
9d3aa82c 764 (let* ((left-or-right (memq side '(left right)))
caceae25 765 (major (window--major-side-window side))
caceae25
MR
766 (on-side (cond
767 ((eq side 'top) 'above)
768 ((eq side 'bottom) 'below)
769 (t side)))
770 ;; The following two bindings will tell `split-window' to take
771 ;; the space for the new window from `major' and not make a new
772 ;; parent window unless needed.
773 (window-combination-resize 'side)
774 (window-combination-limit nil)
9d3aa82c 775 (new (split-window major nil on-side)))
caceae25
MR
776 (when new
777 ;; Initialize `window-side' parameter of new window to SIDE.
778 (set-window-parameter new 'window-side side)
779 ;; Install `window-slot' parameter of new window.
780 (set-window-parameter new 'window-slot slot)
781 ;; Install `delete-window' parameter thus making sure that when
782 ;; the new window is deleted, a side window on the opposite side
783 ;; does not get resized.
784 (set-window-parameter new 'delete-window 'delete-side-window)
5938d519
MR
785 ;; Auto-adjust height/width of new window unless a size has been
786 ;; explicitly requested.
735135f9 787 (unless (if left-or-right
5938d519
MR
788 (cdr (assq 'window-width alist))
789 (cdr (assq 'window-height alist)))
790 (setq alist
791 (cons
792 (cons
793 (if left-or-right 'window-width 'window-height)
794 (/ (window-total-size (frame-root-window) left-or-right)
880e6158
MR
795 ;; By default use a fourth of the size of the frame's
796 ;; root window.
5938d519
MR
797 4))
798 alist)))
caceae25 799 ;; Install BUFFER in new window and return NEW.
5938d519 800 (window--display-buffer buffer new 'window alist 'side))))
caceae25
MR
801
802(defun delete-side-window (window)
803 "Delete side window WINDOW."
804 (let ((window-combination-resize
805 (window-parameter (window-parent window) 'window-side))
806 (ignore-window-parameters t))
807 (delete-window window)))
808
809(defun display-buffer-in-side-window (buffer alist)
3c29190f 810 "Display BUFFER in a side window of the selected frame.
caceae25 811ALIST is an association list of symbols and values. The
3c29190f 812following special symbols can be used in ALIST.
caceae25 813
3c29190f
MR
814`side' denotes the side of the frame where the new window shall
815 be located. Valid values are `bottom', `right', `top' and
816 `left'. The default is `bottom'.
caceae25
MR
817
818`slot' if non-nil, specifies the window slot where to display
819 BUFFER. A value of zero or nil means use the middle slot on
820 the specified side. A negative value means use a slot
821 preceding (that is, above or on the left of) the middle slot.
822 A positive value means use a slot following (that is, below or
823 on the right of) the middle slot. The default is zero."
824 (let ((side (or (cdr (assq 'side alist)) 'bottom))
9d3aa82c 825 (slot (or (cdr (assq 'slot alist)) 0)))
caceae25
MR
826 (cond
827 ((not (memq side '(top bottom left right)))
828 (error "Invalid side %s specified" side))
829 ((not (numberp slot))
830 (error "Invalid slot %s specified" slot)))
831
832 (let* ((major (window-with-parameter 'window-side side nil t))
833 ;; `major' is the major window on SIDE, `windows' the list of
834 ;; life windows on SIDE.
835 (windows
836 (when major
837 (let (windows)
838 (walk-window-tree
839 (lambda (window)
840 (when (eq (window-parameter window 'window-side) side)
c660a885
MR
841 (setq windows (cons window windows))))
842 nil nil 'nomini)
caceae25
MR
843 (nreverse windows))))
844 (slots (when major (max 1 (window-child-count major))))
845 (max-slots
846 (nth (cond
847 ((eq side 'left) 0)
848 ((eq side 'top) 1)
849 ((eq side 'right) 2)
850 ((eq side 'bottom) 3))
851 window-sides-slots))
caceae25 852 window this-window this-slot prev-window next-window
9d3aa82c 853 best-window best-slot abs-slot)
caceae25
MR
854
855 (cond
856 ((and (numberp max-slots) (<= max-slots 0))
857 ;; No side-slots available on this side. Don't create an error,
858 ;; just return nil.
859 nil)
860 ((not windows)
861 ;; No major window exists on this side, make one.
862 (display-buffer-in-major-side-window buffer side slot alist))
863 (t
864 ;; Scan windows on SIDE.
865 (catch 'found
866 (dolist (window windows)
867 (setq this-slot (window-parameter window 'window-slot))
868 (cond
869 ;; The following should not happen and probably be checked
870 ;; by window--side-check.
871 ((not (numberp this-slot)))
872 ((= this-slot slot)
873 ;; A window with a matching slot has been found.
874 (setq this-window window)
875 (throw 'found t))
876 (t
877 ;; Check if this window has a better slot value wrt the
878 ;; slot of the window we want.
879 (setq abs-slot
880 (if (or (and (> this-slot 0) (> slot 0))
881 (and (< this-slot 0) (< slot 0)))
882 (abs (- slot this-slot))
883 (+ (abs slot) (abs this-slot))))
884 (unless (and best-slot (<= best-slot abs-slot))
885 (setq best-window window)
886 (setq best-slot abs-slot))
887 (cond
888 ((<= this-slot slot)
889 (setq prev-window window))
890 ((not next-window)
891 (setq next-window window)))))))
892
893 ;; `this-window' is the first window with the same SLOT.
894 ;; `prev-window' is the window with the largest slot < SLOT. A new
895 ;; window will be created after it.
896 ;; `next-window' is the window with the smallest slot > SLOT. A new
897 ;; window will be created before it.
898 ;; `best-window' is the window with the smallest absolute difference
899 ;; of its slot and SLOT.
900
901 ;; Note: We dedicate the window used softly to its buffer to
902 ;; avoid that "other" (non-side) buffer display functions steal
903 ;; it from us. This must eventually become customizable via
904 ;; ALIST (or, better, avoided in the "other" functions).
905 (or (and this-window
906 ;; Reuse `this-window'.
5938d519 907 (window--display-buffer buffer this-window 'reuse alist 'side))
caceae25
MR
908 (and (or (not max-slots) (< slots max-slots))
909 (or (and next-window
910 ;; Make new window before `next-window'.
911 (let ((next-side
912 (if (memq side '(left right)) 'above 'left))
913 (window-combination-resize 'side))
914 (setq window (split-window next-window nil next-side))
915 ;; When the new window is deleted, its space
916 ;; is returned to other side windows.
917 (set-window-parameter
918 window 'delete-window 'delete-side-window)
919 window))
920 (and prev-window
921 ;; Make new window after `prev-window'.
922 (let ((prev-side
923 (if (memq side '(left right)) 'below 'right))
924 (window-combination-resize 'side))
925 (setq window (split-window prev-window nil prev-side))
926 ;; When the new window is deleted, its space
927 ;; is returned to other side windows.
928 (set-window-parameter
929 window 'delete-window 'delete-side-window)
930 window)))
931 (set-window-parameter window 'window-slot slot)
5938d519 932 (window--display-buffer buffer window 'window alist 'side))
caceae25
MR
933 (and best-window
934 ;; Reuse `best-window'.
935 (progn
936 ;; Give best-window the new slot value.
937 (set-window-parameter best-window 'window-slot slot)
5938d519
MR
938 (window--display-buffer
939 buffer best-window 'reuse alist 'side)))))))))
caceae25 940
54f9154c 941(defun window--side-check (&optional frame)
caceae25
MR
942 "Check the side window configuration of FRAME.
943FRAME defaults to the selected frame.
944
945A valid side window configuration preserves the following two
946invariants:
947
948- If there exists a window whose window-side parameter is
949 non-nil, there must exist at least one live window whose
950 window-side parameter is nil.
951
952- If a window W has a non-nil window-side parameter (i) it must
953 have a parent window and that parent's window-side parameter
954 must be either nil or the same as for W, and (ii) any child
955 window of W must have the same window-side parameter as W.
956
957If the configuration is invalid, reset the window-side parameters
958of all windows on FRAME to nil."
959 (let (left top right bottom none side parent parent-side)
85cc1f11
MR
960 (when (or (catch 'reset
961 (walk-window-tree
962 (lambda (window)
963 (setq side (window-parameter window 'window-side))
964 (setq parent (window-parent window))
965 (setq parent-side
966 (and parent (window-parameter parent 'window-side)))
967 ;; The following `cond' seems a bit tedious, but I'd
968 ;; rather stick to using just the stack.
969 (cond
970 (parent-side
971 (when (not (eq parent-side side))
972 ;; A parent whose window-side is non-nil must
973 ;; have a child with the same window-side.
974 (throw 'reset t)))
caceae25
MR
975 ((not side)
976 (when (window-buffer window)
977 ;; Record that we have at least one non-side,
978 ;; live window.
85cc1f11 979 (setq none t)))
caceae25
MR
980 ((if (memq side '(left top))
981 (window-prev-sibling window)
982 (window-next-sibling window))
983 ;; Left and top major side windows must not have a
984 ;; previous sibling, right and bottom major side
985 ;; windows must not have a next sibling.
986 (throw 'reset t))
987 ;; Now check that there's no more than one major
988 ;; window for any of left, top, right and bottom.
85cc1f11 989 ((eq side 'left)
caceae25 990 (if left (throw 'reset t) (setq left t)))
85cc1f11 991 ((eq side 'top)
caceae25 992 (if top (throw 'reset t) (setq top t)))
85cc1f11 993 ((eq side 'right)
caceae25 994 (if right (throw 'reset t) (setq right t)))
85cc1f11 995 ((eq side 'bottom)
caceae25 996 (if bottom (throw 'reset t) (setq bottom t)))
42019c2e 997 (t
caceae25 998 (throw 'reset t))))
c660a885 999 frame t 'nomini))
caceae25
MR
1000 ;; If there's a side window, there must be at least one
1001 ;; non-side window.
1002 (and (or left top right bottom) (not none)))
85cc1f11
MR
1003 (walk-window-tree
1004 (lambda (window)
1005 (set-window-parameter window 'window-side nil))
c660a885 1006 frame t 'nomini))))
85cc1f11 1007
54f9154c 1008(defun window--check (&optional frame)
85cc1f11
MR
1009 "Check atomic and side windows on FRAME.
1010FRAME defaults to the selected frame."
54f9154c
MR
1011 (window--side-check frame)
1012 (window--atom-check frame))
85cc1f11 1013
8dd3e94f
MR
1014;; Dumping frame/window contents.
1015(defun window--dump-window (&optional window erase)
1016 "Dump WINDOW to buffer *window-frame-dump*.
1017WINDOW must be a valid window and defaults to the selected one.
1018Optional argument ERASE non-nil means erase *window-frame-dump*
1019before writing to it."
1020 (setq window (window-normalize-window window))
1021 (with-current-buffer (get-buffer-create "*window-frame-dump*")
1022 (when erase (erase-buffer))
1023 (insert
1024 (format "%s parent: %s\n" window (window-parent window))
1025 (format "pixel left: %s top: %s size: %s x %s new: %s\n"
1026 (window-pixel-left window) (window-pixel-top window)
1027 (window-size window t t) (window-size window nil t)
1028 (window-new-pixel window))
1029 (format "char left: %s top: %s size: %s x %s new: %s\n"
1030 (window-left-column window) (window-top-line window)
1031 (window-total-size window t) (window-total-size window)
1032 (window-new-total window))
1033 (format "normal: %s x %s new: %s\n"
1034 (window-normal-size window t) (window-normal-size window)
1035 (window-new-normal window)))
1036 (when (window-live-p window)
1037 (let ((fringes (window-fringes window))
1038 (margins (window-margins window)))
1039 (insert
1040 (format "body pixel: %s x %s char: %s x %s\n"
1041 (window-body-width window t) (window-body-height window t)
1042 (window-body-width window) (window-body-height window))
1043 (format "width left fringe: %s left margin: %s right margin: %s\n"
1044 (car fringes) (or (car margins) 0) (or (cdr margins) 0))
1045 (format "width right fringe: %s scroll-bar: %s divider: %s\n"
1046 (cadr fringes)
1047 (window-scroll-bar-width window)
1048 (window-right-divider-width window))
1049 (format "height header-line: %s mode-line: %s divider: %s\n"
1050 (window-header-line-height window)
1051 (window-mode-line-height window)
1052 (window-bottom-divider-width window)))))
1053 (insert "\n")))
1054
1055(defun window--dump-frame (&optional window-or-frame)
1056 "Dump WINDOW-OR-FRAME to buffer *window-frame-dump*.
1057WINDOW-OR-FRAME can be a frame or a window and defaults to the
1058selected frame. When WINDOW-OR-FRAME is a window, dump that
1059window's frame. The buffer *window-frame-dump* is erased before
1060dumping to it."
1061 (interactive)
1062 (let* ((window
1063 (cond
1064 ((or (not window-or-frame)
1065 (frame-live-p window-or-frame))
1066 (frame-root-window window-or-frame))
1067 ((or (window-live-p window-or-frame)
1068 (window-child window-or-frame))
1069 window-or-frame)
1070 (t
1071 (frame-root-window))))
1072 (frame (window-frame window)))
1073 (with-current-buffer (get-buffer-create "*window-frame-dump*")
1074 (erase-buffer)
1075 (insert
1076 (format "frame pixel: %s x %s cols/lines: %s x %s units: %s x %s\n"
1077 (frame-pixel-width frame) (frame-pixel-height frame)
1078 (frame-total-cols frame) (frame-text-lines frame) ; (frame-total-lines frame)
1079 (frame-char-width frame) (frame-char-height frame))
1080 (format "frame text pixel: %s x %s cols/lines: %s x %s\n"
1081 (frame-text-width frame) (frame-text-height frame)
1082 (frame-text-cols frame) (frame-text-lines frame))
1083 (format "tool: %s scroll: %s fringe: %s border: %s right: %s bottom: %s\n\n"
1084 (tool-bar-height frame t)
1085 (frame-scroll-bar-width frame)
1086 (frame-fringe-width frame)
1087 (frame-border-width frame)
1088 (frame-right-divider-width frame)
1089 (frame-bottom-divider-width frame)))
1090 (walk-window-tree 'window--dump-window frame t t))))
1091
85cc1f11 1092;;; Window sizes.
6cb4da45 1093(defun window-total-size (&optional window horizontal round)
880e6158
MR
1094 "Return the total height or width of WINDOW.
1095WINDOW must be a valid window and defaults to the selected one.
1096
1097If HORIZONTAL is omitted or nil, return the total height of
1098WINDOW, in lines, like `window-total-height'. Otherwise return
6cb4da45
MR
1099the total width, in columns, like `window-total-width'.
1100
1101Optional argument ROUND is handled as for `window-total-height'
1102and `window-total-width'."
880e6158 1103 (if horizontal
6cb4da45
MR
1104 (window-total-width window round)
1105 (window-total-height window round)))
880e6158 1106
6cb4da45 1107(defun window-size (&optional window horizontal pixelwise round)
880e6158
MR
1108 "Return the height or width of WINDOW.
1109WINDOW must be a valid window and defaults to the selected one.
1110
1111If HORIZONTAL is omitted or nil, return the total height of
1112WINDOW, in lines, like `window-total-height'. Otherwise return
1113the total width, in columns, like `window-total-width'.
1114
1115Optional argument PIXELWISE means return the pixel size of WINDOW
6cb4da45
MR
1116like `window-pixel-height' and `window-pixel-width'.
1117
1118Optional argument ROUND is ignored if PIXELWISE is non-nil and
1119handled as for `window-total-height' and `window-total-width'
1120otherwise."
880e6158
MR
1121 (if horizontal
1122 (if pixelwise
1123 (window-pixel-width window)
6cb4da45 1124 (window-total-width window round))
880e6158
MR
1125 (if pixelwise
1126 (window-pixel-height window)
6cb4da45 1127 (window-total-height window round))))
880e6158 1128
85cc1f11
MR
1129(defvar window-size-fixed nil
1130 "Non-nil in a buffer means windows displaying the buffer are fixed-size.
1131If the value is `height', then only the window's height is fixed.
1132If the value is `width', then only the window's width is fixed.
1133Any other non-nil value fixes both the width and the height.
1134
1135Emacs won't change the size of any window displaying that buffer,
382c953b 1136unless it has no other choice (like when deleting a neighboring
85cc1f11
MR
1137window).")
1138(make-variable-buffer-local 'window-size-fixed)
1139
842e3a93 1140(defun window--size-ignore-p (window ignore)
a1511caf 1141 "Return non-nil if IGNORE says to ignore size restrictions for WINDOW."
24300f5f 1142 (if (window-valid-p ignore) (eq window ignore) ignore))
a1511caf 1143
880e6158
MR
1144(defun window-safe-min-size (&optional window horizontal pixelwise)
1145 "Return safe minimum size of WINDOW.
1146WINDOW must be a valid window and defaults to the selected one.
1147Optional argument HORIZONTAL non-nil means return the minimum
1148number of columns of WINDOW; otherwise return the minimum number
1149of WINDOW's lines.
1150
1151Optional argument PIXELWISE non-nil means return the minimum pixel-size
1152of WINDOW."
1153 (setq window (window-normalize-window window))
1154 (if pixelwise
1155 (if horizontal
1156 (* window-safe-min-width
1157 (frame-char-width (window-frame window)))
1158 (* window-safe-min-height
1159 (frame-char-height (window-frame window))))
1160 (if horizontal window-safe-min-width window-safe-min-height)))
1161
1162(defun window-min-size (&optional window horizontal ignore pixelwise)
2116e93c 1163 "Return the minimum size of WINDOW.
85c2386b
MR
1164WINDOW must be a valid window and defaults to the selected one.
1165Optional argument HORIZONTAL non-nil means return the minimum
1166number of columns of WINDOW; otherwise return the minimum number
1167of WINDOW's lines.
a1511caf 1168
2116e93c 1169Optional argument IGNORE, if non-nil, means ignore restrictions
a1511caf 1170imposed by fixed size windows, `window-min-height' or
2116e93c 1171`window-min-width' settings. If IGNORE equals `safe', live
a1511caf 1172windows may get as small as `window-safe-min-height' lines and
2116e93c
EZ
1173`window-safe-min-width' columns. If IGNORE is a window, ignore
1174restrictions for that window only. Any other non-nil value
880e6158
MR
1175means ignore all of the above restrictions for all windows.
1176
1177Optional argument PIXELWISE non-nil means return the minimum pixel-size
1178of WINDOW."
c56cad4a 1179 (window--min-size-1
880e6158 1180 (window-normalize-window window) horizontal ignore pixelwise))
a1511caf 1181
880e6158 1182(defun window--min-size-1 (window horizontal ignore pixelwise)
a1511caf
MR
1183 "Internal function of `window-min-size'."
1184 (let ((sub (window-child window)))
1185 (if sub
1186 (let ((value 0))
1187 ;; WINDOW is an internal window.
3d8daefe 1188 (if (window-combined-p sub horizontal)
a1511caf 1189 ;; The minimum size of an iso-combination is the sum of
be7f5545 1190 ;; the minimum sizes of its child windows.
a1511caf
MR
1191 (while sub
1192 (setq value (+ value
880e6158
MR
1193 (window--min-size-1
1194 sub horizontal ignore pixelwise)))
a1511caf 1195 (setq sub (window-right sub)))
c660a885
MR
1196 ;; The minimum size of an ortho-combination is the maximum
1197 ;; of the minimum sizes of its child windows.
a1511caf
MR
1198 (while sub
1199 (setq value (max value
880e6158
MR
1200 (window--min-size-1
1201 sub horizontal ignore pixelwise)))
a1511caf
MR
1202 (setq sub (window-right sub))))
1203 value)
1204 (with-current-buffer (window-buffer window)
1205 (cond
842e3a93 1206 ((and (not (window--size-ignore-p window ignore))
a1511caf
MR
1207 (window-size-fixed-p window horizontal))
1208 ;; The minimum size of a fixed size window is its size.
880e6158 1209 (window-size window horizontal pixelwise))
a1511caf
MR
1210 ((or (eq ignore 'safe) (eq ignore window))
1211 ;; If IGNORE equals `safe' or WINDOW return the safe values.
880e6158 1212 (window-safe-min-size window horizontal pixelwise))
a1511caf
MR
1213 (horizontal
1214 ;; For the minimum width of a window take fringes and
1215 ;; scroll-bars into account. This is questionable and should
1216 ;; be removed as soon as we are able to split (and resize)
1217 ;; windows such that the new (or resized) windows can get a
1218 ;; size less than the user-specified `window-min-height' and
1219 ;; `window-min-width'.
8dd3e94f
MR
1220 (let* ((char-size (frame-char-size window t))
1221 (fringes (window-fringes window))
1222 (pixel-width
1223 (+ (window-safe-min-size window t t)
1224 (car fringes) (cadr fringes)
1225 (window-scroll-bar-width window)
1226 (window-right-divider-width window))))
880e6158
MR
1227 (if pixelwise
1228 (max
8dd3e94f
MR
1229 (if window-resize-pixelwise
1230 pixel-width
1231 ;; Round up to next integral of columns.
1232 (* (ceiling pixel-width char-size) char-size))
880e6158
MR
1233 (if (window--size-ignore-p window ignore)
1234 0
1235 (window-min-pixel-width)))
1236 (max
8dd3e94f 1237 (ceiling pixel-width char-size)
880e6158
MR
1238 (if (window--size-ignore-p window ignore)
1239 0
1240 window-min-width)))))
8dd3e94f
MR
1241 ((let ((char-size (frame-char-size window))
1242 (pixel-height
1243 (+ (window-safe-min-size window nil t)
1244 (window-header-line-height window)
1245 (window-mode-line-height window)
1246 (window-bottom-divider-width window))))
1247 (if pixelwise
1248 (max
1249 (if window-resize-pixelwise
1250 pixel-height
1251 ;; Round up to next integral of lines.
1252 (* (ceiling pixel-height char-size) char-size))
1253 (if (window--size-ignore-p window ignore)
1254 0
1255 (window-min-pixel-height)))
1256 (max (ceiling pixel-height char-size)
1257 (if (window--size-ignore-p window ignore)
1258 0
1259 window-min-height))))))))))
a1511caf 1260
880e6158 1261(defun window-sizable (window delta &optional horizontal ignore pixelwise)
a1511caf 1262 "Return DELTA if DELTA lines can be added to WINDOW.
85c2386b 1263WINDOW must be a valid window and defaults to the selected one.
a1511caf
MR
1264Optional argument HORIZONTAL non-nil means return DELTA if DELTA
1265columns can be added to WINDOW. A return value of zero means
1266that no lines (or columns) can be added to WINDOW.
1267
be7f5545
MR
1268This function looks only at WINDOW and, recursively, its child
1269windows. The function `window-resizable' looks at other windows
1270as well.
a1511caf
MR
1271
1272DELTA positive means WINDOW shall be enlarged by DELTA lines or
1273columns. If WINDOW cannot be enlarged by DELTA lines or columns
1274return the maximum value in the range 0..DELTA by which WINDOW
1275can be enlarged.
1276
1277DELTA negative means WINDOW shall be shrunk by -DELTA lines or
1278columns. If WINDOW cannot be shrunk by -DELTA lines or columns,
1279return the minimum value in the range DELTA..0 by which WINDOW
1280can be shrunk.
1281
2116e93c 1282Optional argument IGNORE non-nil means ignore restrictions
a1511caf 1283imposed by fixed size windows, `window-min-height' or
2116e93c 1284`window-min-width' settings. If IGNORE equals `safe', live
a1511caf 1285windows may get as small as `window-safe-min-height' lines and
2116e93c
EZ
1286`window-safe-min-width' columns. If IGNORE is a window, ignore
1287restrictions for that window only. Any other non-nil value means
880e6158
MR
1288ignore all of the above restrictions for all windows.
1289
1290Optional argument PIXELWISE non-nil means interpret DELTA as
1291pixels."
447f16b8 1292 (setq window (window-normalize-window window))
a1511caf
MR
1293 (cond
1294 ((< delta 0)
880e6158
MR
1295 (max (- (window-min-size window horizontal ignore pixelwise)
1296 (window-size window horizontal pixelwise))
a1511caf 1297 delta))
842e3a93 1298 ((window--size-ignore-p window ignore)
a1511caf
MR
1299 delta)
1300 ((> delta 0)
1301 (if (window-size-fixed-p window horizontal)
1302 0
1303 delta))
1304 (t 0)))
1305
880e6158 1306(defun window-sizable-p (window delta &optional horizontal ignore pixelwise)
a1511caf 1307 "Return t if WINDOW can be resized by DELTA lines.
85c2386b 1308WINDOW must be a valid window and defaults to the selected one.
a1511caf
MR
1309For the meaning of the arguments of this function see the
1310doc-string of `window-sizable'."
447f16b8 1311 (setq window (window-normalize-window window))
a1511caf 1312 (if (> delta 0)
880e6158
MR
1313 (>= (window-sizable window delta horizontal ignore pixelwise)
1314 delta)
1315 (<= (window-sizable window delta horizontal ignore pixelwise)
1316 delta)))
a1511caf 1317
5e92ca23 1318(defun window--size-fixed-1 (window horizontal)
a1511caf
MR
1319 "Internal function for `window-size-fixed-p'."
1320 (let ((sub (window-child window)))
1321 (catch 'fixed
1322 (if sub
1323 ;; WINDOW is an internal window.
3d8daefe 1324 (if (window-combined-p sub horizontal)
be7f5545
MR
1325 ;; An iso-combination is fixed size if all its child
1326 ;; windows are fixed-size.
a1511caf
MR
1327 (progn
1328 (while sub
5e92ca23 1329 (unless (window--size-fixed-1 sub horizontal)
be7f5545
MR
1330 ;; We found a non-fixed-size child window, so
1331 ;; WINDOW's size is not fixed.
a1511caf
MR
1332 (throw 'fixed nil))
1333 (setq sub (window-right sub)))
be7f5545 1334 ;; All child windows are fixed-size, so WINDOW's size is
a1511caf
MR
1335 ;; fixed.
1336 (throw 'fixed t))
1337 ;; An ortho-combination is fixed-size if at least one of its
be7f5545 1338 ;; child windows is fixed-size.
a1511caf 1339 (while sub
5e92ca23 1340 (when (window--size-fixed-1 sub horizontal)
be7f5545
MR
1341 ;; We found a fixed-size child window, so WINDOW's size
1342 ;; is fixed.
a1511caf
MR
1343 (throw 'fixed t))
1344 (setq sub (window-right sub))))
1345 ;; WINDOW is a live window.
1346 (with-current-buffer (window-buffer window)
1347 (if horizontal
1348 (memq window-size-fixed '(width t))
1349 (memq window-size-fixed '(height t))))))))
1350
1351(defun window-size-fixed-p (&optional window horizontal)
1352 "Return non-nil if WINDOW's height is fixed.
85c2386b
MR
1353WINDOW must be a valid window and defaults to the selected one.
1354Optional argument HORIZONTAL non-nil means return non-nil if
1355WINDOW's width is fixed.
a1511caf
MR
1356
1357If this function returns nil, this does not necessarily mean that
2cffd681
MR
1358WINDOW can be resized in the desired direction. The function
1359`window-resizable' can tell that."
5e92ca23 1360 (window--size-fixed-1
447f16b8 1361 (window-normalize-window window) horizontal))
a1511caf 1362
880e6158 1363(defun window--min-delta-1 (window delta &optional horizontal ignore trail noup pixelwise)
a1511caf
MR
1364 "Internal function for `window-min-delta'."
1365 (if (not (window-parent window))
1366 ;; If we can't go up, return zero.
1367 0
1368 ;; Else try to find a non-fixed-size sibling of WINDOW.
1369 (let* ((parent (window-parent window))
1370 (sub (window-child parent)))
1371 (catch 'done
3d8daefe 1372 (if (window-combined-p sub horizontal)
a1511caf 1373 ;; In an iso-combination throw DELTA if we find at least one
be7f5545
MR
1374 ;; child window and that window is either not fixed-size or
1375 ;; we can ignore fixed-sizeness.
a1511caf
MR
1376 (let ((skip (eq trail 'after)))
1377 (while sub
1378 (cond
1379 ((eq sub window)
1380 (setq skip (eq trail 'before)))
1381 (skip)
842e3a93 1382 ((and (not (window--size-ignore-p window ignore))
a1511caf
MR
1383 (window-size-fixed-p sub horizontal)))
1384 (t
be7f5545 1385 ;; We found a non-fixed-size child window.
a1511caf
MR
1386 (throw 'done delta)))
1387 (setq sub (window-right sub))))
1388 ;; In an ortho-combination set DELTA to the minimum value by
be7f5545 1389 ;; which other child windows can shrink.
a1511caf
MR
1390 (while sub
1391 (unless (eq sub window)
1392 (setq delta
1393 (min delta
8dd3e94f
MR
1394 (max (- (window-size sub horizontal pixelwise 'ceiling)
1395 (window-min-size
1396 sub horizontal ignore pixelwise))
1397 0))))
a1511caf
MR
1398 (setq sub (window-right sub))))
1399 (if noup
1400 delta
880e6158
MR
1401 (window--min-delta-1
1402 parent delta horizontal ignore trail nil pixelwise))))))
a1511caf 1403
880e6158 1404(defun window-min-delta (&optional window horizontal ignore trail noup nodown pixelwise)
a1511caf 1405 "Return number of lines by which WINDOW can be shrunk.
85c2386b
MR
1406WINDOW must be a valid window and defaults to the selected one.
1407Return zero if WINDOW cannot be shrunk.
a1511caf
MR
1408
1409Optional argument HORIZONTAL non-nil means return number of
1410columns by which WINDOW can be shrunk.
1411
2116e93c 1412Optional argument IGNORE non-nil means ignore restrictions
a1511caf 1413imposed by fixed size windows, `window-min-height' or
2116e93c
EZ
1414`window-min-width' settings. If IGNORE is a window, ignore
1415restrictions for that window only. If IGNORE equals `safe',
a1511caf 1416live windows may get as small as `window-safe-min-height' lines
2116e93c
EZ
1417and `window-safe-min-width' columns. Any other non-nil value
1418means ignore all of the above restrictions for all windows.
a1511caf 1419
2116e93c
EZ
1420Optional argument TRAIL restricts the windows that can be enlarged.
1421If its value is `before', only windows to the left of or above WINDOW
1422can be enlarged. If it is `after', only windows to the right of or
1423below WINDOW can be enlarged.
a1511caf
MR
1424
1425Optional argument NOUP non-nil means don't go up in the window
2116e93c 1426tree, but try to enlarge windows within WINDOW's combination only.
a1511caf
MR
1427
1428Optional argument NODOWN non-nil means don't check whether WINDOW
382c953b 1429itself (and its child windows) can be shrunk; check only whether
880e6158
MR
1430at least one other window can be enlarged appropriately.
1431
1432Optional argument PIXELWISE non-nil means return number of pixels
1433by which WINDOW can be shrunk."
447f16b8 1434 (setq window (window-normalize-window window))
6cb4da45 1435 (let ((size (window-size window horizontal pixelwise 'floor))
880e6158 1436 (minimum (window-min-size window horizontal ignore pixelwise)))
a1511caf
MR
1437 (cond
1438 (nodown
1439 ;; If NODOWN is t, try to recover the entire size of WINDOW.
880e6158
MR
1440 (window--min-delta-1
1441 window size horizontal ignore trail noup pixelwise))
1442 ((<= size minimum)
a1511caf
MR
1443 ;; If NODOWN is nil and WINDOW's size is already at its minimum,
1444 ;; there's nothing to recover.
1445 0)
1446 (t
1447 ;; Otherwise, try to recover whatever WINDOW is larger than its
1448 ;; minimum size.
c56cad4a 1449 (window--min-delta-1
880e6158 1450 window (- size minimum) horizontal ignore trail noup pixelwise)))))
a1511caf 1451
880e6158 1452(defun window--max-delta-1 (window delta &optional horizontal ignore trail noup pixelwise)
a1511caf
MR
1453 "Internal function of `window-max-delta'."
1454 (if (not (window-parent window))
1455 ;; Can't go up. Return DELTA.
1456 delta
1457 (let* ((parent (window-parent window))
1458 (sub (window-child parent)))
1459 (catch 'fixed
3d8daefe 1460 (if (window-combined-p sub horizontal)
a1511caf 1461 ;; For an iso-combination calculate how much we can get from
be7f5545 1462 ;; other child windows.
a1511caf
MR
1463 (let ((skip (eq trail 'after)))
1464 (while sub
1465 (cond
1466 ((eq sub window)
1467 (setq skip (eq trail 'before)))
1468 (skip)
1469 (t
1470 (setq delta
1471 (+ delta
8dd3e94f 1472 (max
04b577b6 1473 (- (window-size sub horizontal pixelwise 'floor)
8dd3e94f
MR
1474 (window-min-size
1475 sub horizontal ignore pixelwise))
1476 0)))))
a1511caf
MR
1477 (setq sub (window-right sub))))
1478 ;; For an ortho-combination throw DELTA when at least one
be7f5545 1479 ;; child window is fixed-size.
a1511caf
MR
1480 (while sub
1481 (when (and (not (eq sub window))
842e3a93 1482 (not (window--size-ignore-p sub ignore))
a1511caf
MR
1483 (window-size-fixed-p sub horizontal))
1484 (throw 'fixed delta))
1485 (setq sub (window-right sub))))
1486 (if noup
1487 ;; When NOUP is nil, DELTA is all we can get.
1488 delta
1489 ;; Else try with parent of WINDOW, passing the DELTA we
1490 ;; recovered so far.
880e6158
MR
1491 (window--max-delta-1
1492 parent delta horizontal ignore trail nil pixelwise))))))
a1511caf 1493
880e6158 1494(defun window-max-delta (&optional window horizontal ignore trail noup nodown pixelwise)
2116e93c 1495 "Return maximum number of lines by which WINDOW can be enlarged.
85c2386b
MR
1496WINDOW must be a valid window and defaults to the selected one.
1497The return value is zero if WINDOW cannot be enlarged.
a1511caf
MR
1498
1499Optional argument HORIZONTAL non-nil means return maximum number
1500of columns by which WINDOW can be enlarged.
1501
2116e93c 1502Optional argument IGNORE non-nil means ignore restrictions
a1511caf 1503imposed by fixed size windows, `window-min-height' or
2116e93c
EZ
1504`window-min-width' settings. If IGNORE is a window, ignore
1505restrictions for that window only. If IGNORE equals `safe',
a1511caf 1506live windows may get as small as `window-safe-min-height' lines
2116e93c
EZ
1507and `window-safe-min-width' columns. Any other non-nil value means
1508ignore all of the above restrictions for all windows.
a1511caf 1509
2116e93c
EZ
1510Optional argument TRAIL restricts the windows that can be enlarged.
1511If its value is `before', only windows to the left of or above WINDOW
1512can be enlarged. If it is `after', only windows to the right of or
1513below WINDOW can be enlarged.
a1511caf
MR
1514
1515Optional argument NOUP non-nil means don't go up in the window
1516tree but try to obtain the entire space from windows within
1517WINDOW's combination.
1518
1519Optional argument NODOWN non-nil means do not check whether
382c953b 1520WINDOW itself (and its child windows) can be enlarged; check
880e6158
MR
1521only whether other windows can be shrunk appropriately.
1522
1523Optional argument PIXELWISE non-nil means return number of
1524pixels by which WINDOW can be enlarged."
447f16b8 1525 (setq window (window-normalize-window window))
842e3a93 1526 (if (and (not (window--size-ignore-p window ignore))
a1511caf
MR
1527 (not nodown) (window-size-fixed-p window horizontal))
1528 ;; With IGNORE and NOWDON nil return zero if WINDOW has fixed
1529 ;; size.
1530 0
1531 ;; WINDOW has no fixed size.
880e6158 1532 (window--max-delta-1 window 0 horizontal ignore trail noup pixelwise)))
a1511caf
MR
1533
1534;; Make NOUP also inhibit the min-size check.
880e6158 1535(defun window--resizable (window delta &optional horizontal ignore trail noup nodown pixelwise)
a1511caf 1536 "Return DELTA if WINDOW can be resized vertically by DELTA lines.
85c2386b 1537WINDOW must be a valid window and defaults to the selected one.
a1511caf
MR
1538Optional argument HORIZONTAL non-nil means return DELTA if WINDOW
1539can be resized horizontally by DELTA columns. A return value of
1540zero means that WINDOW is not resizable.
1541
1542DELTA positive means WINDOW shall be enlarged by DELTA lines or
2cffd681 1543columns. If WINDOW cannot be enlarged by DELTA lines or columns,
a1511caf
MR
1544return the maximum value in the range 0..DELTA by which WINDOW
1545can be enlarged.
1546
1547DELTA negative means WINDOW shall be shrunk by -DELTA lines or
1548columns. If WINDOW cannot be shrunk by -DELTA lines or columns,
1549return the minimum value in the range DELTA..0 that can be used
1550for shrinking WINDOW.
1551
2116e93c 1552Optional argument IGNORE non-nil means ignore restrictions
a1511caf 1553imposed by fixed size windows, `window-min-height' or
2116e93c
EZ
1554`window-min-width' settings. If IGNORE is a window, ignore
1555restrictions for that window only. If IGNORE equals `safe',
a1511caf 1556live windows may get as small as `window-safe-min-height' lines
2116e93c
EZ
1557and `window-safe-min-width' columns. Any other non-nil value
1558means ignore all of the above restrictions for all windows.
a1511caf
MR
1559
1560Optional argument TRAIL `before' means only windows to the left
1561of or below WINDOW can be shrunk. Optional argument TRAIL
1562`after' means only windows to the right of or above WINDOW can be
1563shrunk.
1564
1565Optional argument NOUP non-nil means don't go up in the window
2cffd681
MR
1566tree but check only whether space can be obtained from (or given
1567to) WINDOW's siblings.
a1511caf 1568
2cffd681
MR
1569Optional argument NODOWN non-nil means don't go down in the
1570window tree. This means do not check whether resizing would
880e6158
MR
1571violate size restrictions of WINDOW or its child windows.
1572
1573Optional argument PIXELWISE non-nil means interpret DELTA as
1574number of pixels."
447f16b8 1575 (setq window (window-normalize-window window))
a1511caf
MR
1576 (cond
1577 ((< delta 0)
880e6158
MR
1578 (max (- (window-min-delta
1579 window horizontal ignore trail noup nodown pixelwise))
a1511caf
MR
1580 delta))
1581 ((> delta 0)
880e6158
MR
1582 (min (window-max-delta
1583 window horizontal ignore trail noup nodown pixelwise)
a1511caf
MR
1584 delta))
1585 (t 0)))
1586
880e6158 1587(defun window--resizable-p (window delta &optional horizontal ignore trail noup nodown pixelwise)
a1511caf 1588 "Return t if WINDOW can be resized vertically by DELTA lines.
85c2386b 1589WINDOW must be a valid window and defaults to the selected one.
a1511caf 1590For the meaning of the arguments of this function see the
880e6158
MR
1591doc-string of `window--resizable'.
1592
1593Optional argument PIXELWISE non-nil means interpret DELTA as
1594pixels."
447f16b8 1595 (setq window (window-normalize-window window))
a1511caf 1596 (if (> delta 0)
880e6158
MR
1597 (>= (window--resizable
1598 window delta horizontal ignore trail noup nodown pixelwise)
a1511caf 1599 delta)
880e6158
MR
1600 (<= (window--resizable
1601 window delta horizontal ignore trail noup nodown pixelwise)
a1511caf
MR
1602 delta)))
1603
880e6158 1604(defun window-resizable (window delta &optional horizontal ignore pixelwise)
2cffd681 1605 "Return DELTA if WINDOW can be resized vertically by DELTA lines.
85c2386b 1606WINDOW must be a valid window and defaults to the selected one.
2cffd681
MR
1607Optional argument HORIZONTAL non-nil means return DELTA if WINDOW
1608can be resized horizontally by DELTA columns. A return value of
1609zero means that WINDOW is not resizable.
1610
1611DELTA positive means WINDOW shall be enlarged by DELTA lines or
1612columns. If WINDOW cannot be enlarged by DELTA lines or columns
1613return the maximum value in the range 0..DELTA by which WINDOW
1614can be enlarged.
1615
1616DELTA negative means WINDOW shall be shrunk by -DELTA lines or
1617columns. If WINDOW cannot be shrunk by -DELTA lines or columns,
1618return the minimum value in the range DELTA..0 that can be used
1619for shrinking WINDOW.
1620
2116e93c 1621Optional argument IGNORE non-nil means ignore restrictions
2cffd681 1622imposed by fixed size windows, `window-min-height' or
2116e93c
EZ
1623`window-min-width' settings. If IGNORE is a window, ignore
1624restrictions for that window only. If IGNORE equals `safe',
2cffd681 1625live windows may get as small as `window-safe-min-height' lines
2116e93c 1626and `window-safe-min-width' columns. Any other non-nil value
880e6158
MR
1627means ignore all of the above restrictions for all windows.
1628
1629Optional argument PIXELWISE non-nil means interpret DELTA as
1630pixels."
2cffd681 1631 (setq window (window-normalize-window window))
880e6158 1632 (window--resizable window delta horizontal ignore nil nil nil pixelwise))
2cffd681 1633
880e6158
MR
1634(defun window-resizable-p (window delta &optional horizontal ignore pixelwise)
1635 "Return t if WINDOW can be resized vertically by DELTA lines.
85c2386b 1636WINDOW must be a valid window and defaults to the selected one.
880e6158
MR
1637For the meaning of the arguments of this function see the
1638doc-string of `window-resizable'."
1639 (setq window (window-normalize-window window))
1640 (if (> delta 0)
1641 (>= (window--resizable
1642 window delta horizontal ignore nil nil nil pixelwise)
1643 delta)
1644 (<= (window--resizable
1645 window delta horizontal ignore nil nil nil pixelwise)
1646 delta)))
105216ed 1647
880e6158 1648;; Aliases of functions defined in window.c.
f3d1777e 1649(defalias 'window-height 'window-total-height)
880e6158
MR
1650(defalias 'window-width 'window-body-width)
1651
1652;; Eventually the following two should work pixelwise.
f3d1777e 1653
ccafbf06 1654;; See discussion in bug#4543.
4b0d61e3 1655(defun window-full-height-p (&optional window)
2116e93c 1656 "Return t if WINDOW is as high as its containing frame.
a1511caf
MR
1657More precisely, return t if and only if the total height of
1658WINDOW equals the total height of the root window of WINDOW's
85c2386b
MR
1659frame. WINDOW must be a valid window and defaults to the
1660selected one."
447f16b8 1661 (setq window (window-normalize-window window))
880e6158
MR
1662 (= (window-pixel-height window)
1663 (window-pixel-height (frame-root-window window))))
a1511caf 1664
4b0d61e3 1665(defun window-full-width-p (&optional window)
2116e93c 1666 "Return t if WINDOW is as wide as its containing frame.
a1511caf
MR
1667More precisely, return t if and only if the total width of WINDOW
1668equals the total width of the root window of WINDOW's frame.
85c2386b 1669WINDOW must be a valid window and defaults to the selected one."
447f16b8 1670 (setq window (window-normalize-window window))
880e6158
MR
1671 (= (window-pixel-width window)
1672 (window-pixel-width (frame-root-window window))))
a1511caf 1673
105216ed
CY
1674(defun window-body-size (&optional window horizontal)
1675 "Return the height or width of WINDOW's text area.
85c2386b 1676WINDOW must be a live window and defaults to the selected one.
105216ed
CY
1677
1678If HORIZONTAL is omitted or nil, return the height of the text
1679area, like `window-body-height'. Otherwise, return the width of
1680the text area, like `window-body-width'."
1681 (if horizontal
1682 (window-body-width window)
1683 (window-body-height window)))
02c6f098 1684
3c448ab6
MR
1685(defun window-current-scroll-bars (&optional window)
1686 "Return the current scroll bar settings for WINDOW.
387522b2 1687WINDOW must be a live window and defaults to the selected one.
3c448ab6
MR
1688
1689The return value is a cons cell (VERTICAL . HORIZONTAL) where
1690VERTICAL specifies the current location of the vertical scroll
1691bars (`left', `right', or nil), and HORIZONTAL specifies the
1692current location of the horizontal scroll bars (`top', `bottom',
1693or nil).
1694
1695Unlike `window-scroll-bars', this function reports the scroll bar
1696type actually used, once frame defaults and `scroll-bar-mode' are
1697taken into account."
447f16b8 1698 (setq window (window-normalize-window window t))
3c448ab6
MR
1699 (let ((vert (nth 2 (window-scroll-bars window)))
1700 (hor nil))
1701 (when (or (eq vert t) (eq hor t))
387522b2 1702 (let ((fcsb (frame-current-scroll-bars (window-frame window))))
3c448ab6
MR
1703 (if (eq vert t)
1704 (setq vert (car fcsb)))
1705 (if (eq hor t)
1706 (setq hor (cdr fcsb)))))
1707 (cons vert hor)))
1708
c7635a97
CY
1709(defun walk-windows (fun &optional minibuf all-frames)
1710 "Cycle through all live windows, calling FUN for each one.
1711FUN must specify a function with a window as its sole argument.
3c448ab6 1712The optional arguments MINIBUF and ALL-FRAMES specify the set of
387522b2 1713windows to include in the walk.
3c448ab6
MR
1714
1715MINIBUF t means include the minibuffer window even if the
1716minibuffer is not active. MINIBUF nil or omitted means include
1717the minibuffer window only if the minibuffer is active. Any
1718other value means do not include the minibuffer window even if
1719the minibuffer is active.
1720
387522b2
MR
1721ALL-FRAMES nil or omitted means consider all windows on the
1722selected frame, plus the minibuffer window if specified by the
1723MINIBUF argument. If the minibuffer counts, consider all windows
1724on all frames that share that minibuffer too. The following
1725non-nil values of ALL-FRAMES have special meanings:
1726
1727- t means consider all windows on all existing frames.
1728
1729- `visible' means consider all windows on all visible frames on
1730 the current terminal.
1731
1732- 0 (the number zero) means consider all windows on all visible
1733 and iconified frames on the current terminal.
1734
1735- A frame means consider all windows on that frame only.
1736
1737Anything else means consider all windows on the selected frame
1738and no others.
3c448ab6
MR
1739
1740This function changes neither the order of recently selected
1741windows nor the buffer list."
1742 ;; If we start from the minibuffer window, don't fail to come
1743 ;; back to it.
290d5b58 1744 (when (window-minibuffer-p)
3c448ab6
MR
1745 (setq minibuf t))
1746 ;; Make sure to not mess up the order of recently selected
1747 ;; windows. Use `save-selected-window' and `select-window'
1748 ;; with second argument non-nil for this purpose.
1749 (save-selected-window
1750 (when (framep all-frames)
1751 (select-window (frame-first-window all-frames) 'norecord))
387522b2 1752 (dolist (walk-windows-window (window-list-1 nil minibuf all-frames))
c7635a97 1753 (funcall fun walk-windows-window))))
387522b2 1754
e07b9a6d
MR
1755(defun window-at-side-p (&optional window side)
1756 "Return t if WINDOW is at SIDE of its containing frame.
85c2386b
MR
1757WINDOW must be a valid window and defaults to the selected one.
1758SIDE can be any of the symbols `left', `top', `right' or
1759`bottom'. The default value nil is handled like `bottom'."
447f16b8 1760 (setq window (window-normalize-window window))
e07b9a6d
MR
1761 (let ((edge
1762 (cond
1763 ((eq side 'left) 0)
1764 ((eq side 'top) 1)
1765 ((eq side 'right) 2)
1766 ((memq side '(bottom nil)) 3))))
880e6158
MR
1767 (= (nth edge (window-pixel-edges window))
1768 (nth edge (window-pixel-edges (frame-root-window window))))))
e07b9a6d 1769
54f9154c 1770(defun window-at-side-list (&optional frame side)
e07b9a6d
MR
1771 "Return list of all windows on SIDE of FRAME.
1772FRAME must be a live frame and defaults to the selected frame.
1773SIDE can be any of the symbols `left', `top', `right' or
1774`bottom'. The default value nil is handled like `bottom'."
1775 (setq frame (window-normalize-frame frame))
1776 (let (windows)
1777 (walk-window-tree
1778 (lambda (window)
1779 (when (window-at-side-p window side)
1780 (setq windows (cons window windows))))
ea95074e 1781 frame nil 'nomini)
e07b9a6d
MR
1782 (nreverse windows)))
1783
5e92ca23 1784(defun window--in-direction-2 (window posn &optional horizontal)
387522b2
MR
1785 "Support function for `window-in-direction'."
1786 (if horizontal
880e6158 1787 (let ((top (window-pixel-top window)))
387522b2
MR
1788 (if (> top posn)
1789 (- top posn)
880e6158
MR
1790 (- posn top (window-pixel-height window))))
1791 (let ((left (window-pixel-left window)))
387522b2
MR
1792 (if (> left posn)
1793 (- left posn)
880e6158 1794 (- posn left (window-pixel-width window))))))
387522b2 1795
ea95074e
MR
1796;; Predecessors to the below have been devised by Julian Assange in
1797;; change-windows-intuitively.el and Hovav Shacham in windmove.el.
1798;; Neither of these allow to selectively ignore specific windows
1799;; (windows whose `no-other-window' parameter is non-nil) as targets of
1800;; the movement.
71e6691e 1801(defun window-in-direction (direction &optional window ignore sign wrap mini)
387522b2 1802 "Return window in DIRECTION as seen from WINDOW.
ea95074e
MR
1803More precisely, return the nearest window in direction DIRECTION
1804as seen from the position of `window-point' in window WINDOW.
387522b2
MR
1805DIRECTION must be one of `above', `below', `left' or `right'.
1806WINDOW must be a live window and defaults to the selected one.
ea95074e
MR
1807
1808Do not return a window whose `no-other-window' parameter is
1809non-nil. If the nearest window's `no-other-window' parameter is
1810non-nil, try to find another window in the indicated direction.
1811If, however, the optional argument IGNORE is non-nil, return that
1812window even if its `no-other-window' parameter is non-nil.
1813
71e6691e
MR
1814Optional argument SIGN a negative number means to use the right
1815or bottom edge of WINDOW as reference position instead of
1816`window-point'. SIGN a positive number means to use the left or
1817top edge of WINDOW as reference position.
1818
1819Optional argument WRAP non-nil means to wrap DIRECTION around
1820frame borders. This means to return for a WINDOW a the top of
1821the frame and DIRECTION `above' to return the minibuffer window
1822if the frame has one, and a window at the bottom of the frame
1823otherwise.
1824
1825Optional argument MINI nil means to return the minibuffer window
1826if and only if it is currently active. MINI non-nil means to
1827return the minibuffer window even when it's not active. However,
1828if WRAP non-nil, always act as if MINI were nil.
1829
ea95074e 1830Return nil if no suitable window can be found."
447f16b8 1831 (setq window (window-normalize-window window t))
387522b2
MR
1832 (unless (memq direction '(above below left right))
1833 (error "Wrong direction %s" direction))
1834 (let* ((frame (window-frame window))
1835 (hor (memq direction '(left right)))
1836 (first (if hor
880e6158
MR
1837 (window-pixel-left window)
1838 (window-pixel-top window)))
1839 (last (+ first (window-size window hor t)))
387522b2
MR
1840 ;; The column / row value of `posn-at-point' can be nil for the
1841 ;; mini-window, guard against that.
71e6691e
MR
1842 (posn
1843 (cond
1844 ((and (numberp sign) (< sign 0))
1845 (if hor
1846 (1- (+ (window-pixel-top window) (window-pixel-height window)))
1847 (1- (+ (window-pixel-left window) (window-pixel-width window)))))
1848 ((and (numberp sign) (> sign 0))
1849 (if hor
1850 (window-pixel-top window)
1851 (window-pixel-left window)))
1852 ((let ((posn-cons (nth 2 (posn-at-point (window-point window) window))))
1853 (if hor
1854 (+ (or (cdr posn-cons) 1) (window-pixel-top window))
1855 (+ (or (car posn-cons) 1) (window-pixel-left window)))))))
387522b2
MR
1856 (best-edge
1857 (cond
880e6158
MR
1858 ((eq direction 'below) (frame-pixel-height frame))
1859 ((eq direction 'right) (frame-pixel-width frame))
387522b2
MR
1860 (t -1)))
1861 (best-edge-2 best-edge)
880e6158 1862 (best-diff-2 (if hor (frame-pixel-height frame) (frame-pixel-width frame)))
387522b2
MR
1863 best best-2 best-diff-2-new)
1864 (walk-window-tree
1865 (lambda (w)
880e6158
MR
1866 (let* ((w-top (window-pixel-top w))
1867 (w-left (window-pixel-left w)))
387522b2
MR
1868 (cond
1869 ((or (eq window w)
1870 ;; Ignore ourselves.
1871 (and (window-parameter w 'no-other-window)
1872 ;; Ignore W unless IGNORE is non-nil.
1873 (not ignore))))
1874 (hor
1875 (cond
1876 ((and (<= w-top posn)
880e6158 1877 (< posn (+ w-top (window-pixel-height w))))
387522b2
MR
1878 ;; W is to the left or right of WINDOW and covers POSN.
1879 (when (or (and (eq direction 'left)
71e6691e
MR
1880 (or (and (<= w-left first) (> w-left best-edge))
1881 (and wrap
1882 (window-at-side-p window 'left)
1883 (window-at-side-p w 'right))))
387522b2 1884 (and (eq direction 'right)
71e6691e
MR
1885 (or (and (>= w-left last) (< w-left best-edge))
1886 (and wrap
1887 (window-at-side-p window 'right)
1888 (window-at-side-p w 'left)))))
387522b2
MR
1889 (setq best-edge w-left)
1890 (setq best w)))
1891 ((and (or (and (eq direction 'left)
880e6158 1892 (<= (+ w-left (window-pixel-width w)) first))
387522b2
MR
1893 (and (eq direction 'right) (<= last w-left)))
1894 ;; W is to the left or right of WINDOW but does not
1895 ;; cover POSN.
1896 (setq best-diff-2-new
5e92ca23 1897 (window--in-direction-2 w posn hor))
387522b2
MR
1898 (or (< best-diff-2-new best-diff-2)
1899 (and (= best-diff-2-new best-diff-2)
1900 (if (eq direction 'left)
1901 (> w-left best-edge-2)
1902 (< w-left best-edge-2)))))
1903 (setq best-edge-2 w-left)
1904 (setq best-diff-2 best-diff-2-new)
1905 (setq best-2 w))))
71e6691e
MR
1906 ((and (<= w-left posn)
1907 (< posn (+ w-left (window-pixel-width w))))
1908 ;; W is above or below WINDOW and covers POSN.
1909 (when (or (and (eq direction 'above)
1910 (or (and (<= w-top first) (> w-top best-edge))
1911 (and wrap
1912 (window-at-side-p window 'top)
1913 (if (active-minibuffer-window)
1914 (minibuffer-window-active-p w)
1915 (window-at-side-p w 'bottom)))))
1916 (and (eq direction 'below)
1917 (or (and (>= w-top first) (< w-top best-edge))
1918 (and wrap
1919 (if (active-minibuffer-window)
1920 (minibuffer-window-active-p window)
1921 (window-at-side-p window 'bottom))
1922 (window-at-side-p w 'top)))))
1923 (setq best-edge w-top)
1924 (setq best w)))
1925 ((and (or (and (eq direction 'above)
1926 (<= (+ w-top (window-pixel-height w)) first))
1927 (and (eq direction 'below) (<= last w-top)))
1928 ;; W is above or below WINDOW but does not cover POSN.
1929 (setq best-diff-2-new
1930 (window--in-direction-2 w posn hor))
1931 (or (< best-diff-2-new best-diff-2)
1932 (and (= best-diff-2-new best-diff-2)
1933 (if (eq direction 'above)
1934 (> w-top best-edge-2)
1935 (< w-top best-edge-2)))))
1936 (setq best-edge-2 w-top)
1937 (setq best-diff-2 best-diff-2-new)
1938 (setq best-2 w)))))
1939 frame nil (and mini t))
387522b2 1940 (or best best-2)))
3c448ab6 1941
4c43d97b 1942(defun get-window-with-predicate (predicate &optional minibuf all-frames default)
387522b2
MR
1943 "Return a live window satisfying PREDICATE.
1944More precisely, cycle through all windows calling the function
1945PREDICATE on each one of them with the window as its sole
1946argument. Return the first window for which PREDICATE returns
4c43d97b 1947non-nil. Windows are scanned starting with the window following
c80e3b4a 1948the selected window. If no window satisfies PREDICATE, return
4c43d97b
MR
1949DEFAULT.
1950
1951MINIBUF t means include the minibuffer window even if the
1952minibuffer is not active. MINIBUF nil or omitted means include
1953the minibuffer window only if the minibuffer is active. Any
1954other value means do not include the minibuffer window even if
1955the minibuffer is active.
387522b2
MR
1956
1957ALL-FRAMES nil or omitted means consider all windows on the selected
1958frame, plus the minibuffer window if specified by the MINIBUF
1959argument. If the minibuffer counts, consider all windows on all
1960frames that share that minibuffer too. The following non-nil
1961values of ALL-FRAMES have special meanings:
3c448ab6 1962
387522b2
MR
1963- t means consider all windows on all existing frames.
1964
1965- `visible' means consider all windows on all visible frames on
1966 the current terminal.
1967
1968- 0 (the number zero) means consider all windows on all visible
1969 and iconified frames on the current terminal.
1970
1971- A frame means consider all windows on that frame only.
1972
1973Anything else means consider all windows on the selected frame
1974and no others."
3c448ab6 1975 (catch 'found
4c43d97b
MR
1976 (dolist (window (window-list-1
1977 (next-window nil minibuf all-frames)
1978 minibuf all-frames))
387522b2
MR
1979 (when (funcall predicate window)
1980 (throw 'found window)))
3c448ab6
MR
1981 default))
1982
1983(defalias 'some-window 'get-window-with-predicate)
1984
51a5f9d8 1985(defun get-lru-window (&optional all-frames dedicated not-selected)
190b47e6
MR
1986 "Return the least recently used window on frames specified by ALL-FRAMES.
1987Return a full-width window if possible. A minibuffer window is
1988never a candidate. A dedicated window is never a candidate
1989unless DEDICATED is non-nil, so if all windows are dedicated, the
1990value is nil. Avoid returning the selected window if possible.
51a5f9d8
MR
1991Optional argument NOT-SELECTED non-nil means never return the
1992selected window.
190b47e6
MR
1993
1994The following non-nil values of the optional argument ALL-FRAMES
1995have special meanings:
1996
1997- t means consider all windows on all existing frames.
1998
1999- `visible' means consider all windows on all visible frames on
2000 the current terminal.
2001
2002- 0 (the number zero) means consider all windows on all visible
2003 and iconified frames on the current terminal.
2004
2005- A frame means consider all windows on that frame only.
2006
2007Any other value of ALL-FRAMES means consider all windows on the
2008selected frame and no others."
2009 (let (best-window best-time second-best-window second-best-time time)
02cfc6d6 2010 (dolist (window (window-list-1 nil 'nomini all-frames))
51a5f9d8
MR
2011 (when (and (or dedicated (not (window-dedicated-p window)))
2012 (or (not not-selected) (not (eq window (selected-window)))))
190b47e6
MR
2013 (setq time (window-use-time window))
2014 (if (or (eq window (selected-window))
2015 (not (window-full-width-p window)))
2016 (when (or (not second-best-time) (< time second-best-time))
2017 (setq second-best-time time)
2018 (setq second-best-window window))
2019 (when (or (not best-time) (< time best-time))
2020 (setq best-time time)
2021 (setq best-window window)))))
2022 (or best-window second-best-window)))
2023
51a5f9d8 2024(defun get-mru-window (&optional all-frames dedicated not-selected)
387522b2 2025 "Return the most recently used window on frames specified by ALL-FRAMES.
51a5f9d8
MR
2026A minibuffer window is never a candidate. A dedicated window is
2027never a candidate unless DEDICATED is non-nil, so if all windows
2028are dedicated, the value is nil. Optional argument NOT-SELECTED
2029non-nil means never return the selected window.
387522b2
MR
2030
2031The following non-nil values of the optional argument ALL-FRAMES
2032have special meanings:
2033
2034- t means consider all windows on all existing frames.
2035
2036- `visible' means consider all windows on all visible frames on
2037 the current terminal.
2038
2039- 0 (the number zero) means consider all windows on all visible
2040 and iconified frames on the current terminal.
2041
2042- A frame means consider all windows on that frame only.
2043
2044Any other value of ALL-FRAMES means consider all windows on the
2045selected frame and no others."
2046 (let (best-window best-time time)
02cfc6d6 2047 (dolist (window (window-list-1 nil 'nomini all-frames))
387522b2 2048 (setq time (window-use-time window))
51a5f9d8
MR
2049 (when (and (or dedicated (not (window-dedicated-p window)))
2050 (or (not not-selected) (not (eq window (selected-window))))
2051 (or (not best-time) (> time best-time)))
387522b2
MR
2052 (setq best-time time)
2053 (setq best-window window)))
2054 best-window))
2055
51a5f9d8 2056(defun get-largest-window (&optional all-frames dedicated not-selected)
190b47e6
MR
2057 "Return the largest window on frames specified by ALL-FRAMES.
2058A minibuffer window is never a candidate. A dedicated window is
2059never a candidate unless DEDICATED is non-nil, so if all windows
51a5f9d8
MR
2060are dedicated, the value is nil. Optional argument NOT-SELECTED
2061non-nil means never return the selected window.
190b47e6
MR
2062
2063The following non-nil values of the optional argument ALL-FRAMES
2064have special meanings:
2065
2066- t means consider all windows on all existing frames.
2067
2068- `visible' means consider all windows on all visible frames on
2069 the current terminal.
2070
2071- 0 (the number zero) means consider all windows on all visible
2072 and iconified frames on the current terminal.
2073
2074- A frame means consider all windows on that frame only.
2075
2076Any other value of ALL-FRAMES means consider all windows on the
2077selected frame and no others."
2078 (let ((best-size 0)
2079 best-window size)
02cfc6d6 2080 (dolist (window (window-list-1 nil 'nomini all-frames))
51a5f9d8
MR
2081 (when (and (or dedicated (not (window-dedicated-p window)))
2082 (or (not not-selected) (not (eq window (selected-window)))))
880e6158
MR
2083 (setq size (* (window-pixel-height window)
2084 (window-pixel-width window)))
190b47e6
MR
2085 (when (> size best-size)
2086 (setq best-size size)
2087 (setq best-window window))))
2088 best-window))
2089
3c448ab6
MR
2090(defun get-buffer-window-list (&optional buffer-or-name minibuf all-frames)
2091 "Return list of all windows displaying BUFFER-OR-NAME, or nil if none.
2092BUFFER-OR-NAME may be a buffer or the name of an existing buffer
4c43d97b
MR
2093and defaults to the current buffer. Windows are scanned starting
2094with the selected window.
190b47e6
MR
2095
2096MINIBUF t means include the minibuffer window even if the
2097minibuffer is not active. MINIBUF nil or omitted means include
2098the minibuffer window only if the minibuffer is active. Any
2099other value means do not include the minibuffer window even if
2100the minibuffer is active.
2101
2102ALL-FRAMES nil or omitted means consider all windows on the
2103selected frame, plus the minibuffer window if specified by the
2104MINIBUF argument. If the minibuffer counts, consider all windows
2105on all frames that share that minibuffer too. The following
2106non-nil values of ALL-FRAMES have special meanings:
2107
2108- t means consider all windows on all existing frames.
2109
2110- `visible' means consider all windows on all visible frames on
2111 the current terminal.
2112
2113- 0 (the number zero) means consider all windows on all visible
2114 and iconified frames on the current terminal.
2115
2116- A frame means consider all windows on that frame only.
2117
2118Anything else means consider all windows on the selected frame
2119and no others."
5386012d 2120 (let ((buffer (window-normalize-buffer buffer-or-name))
3c448ab6 2121 windows)
4c43d97b 2122 (dolist (window (window-list-1 (selected-window) minibuf all-frames))
190b47e6
MR
2123 (when (eq (window-buffer window) buffer)
2124 (setq windows (cons window windows))))
2125 (nreverse windows)))
3c448ab6
MR
2126
2127(defun minibuffer-window-active-p (window)
2128 "Return t if WINDOW is the currently active minibuffer window."
2129 (eq window (active-minibuffer-window)))
387522b2 2130
3c448ab6 2131(defun count-windows (&optional minibuf)
387522b2 2132 "Return the number of live windows on the selected frame.
3c448ab6
MR
2133The optional argument MINIBUF specifies whether the minibuffer
2134window shall be counted. See `walk-windows' for the precise
2135meaning of this argument."
387522b2 2136 (length (window-list-1 nil minibuf)))
9aab8e0d
MR
2137\f
2138;;; Resizing windows.
880e6158
MR
2139(defun window--size-to-pixel (window size &optional horizontal pixelwise round-maybe)
2140 "For WINDOW convert SIZE lines to pixels.
2141SIZE is supposed to specify a height of WINDOW in terms of text
2142lines. The return value is the number of pixels specifying that
2143height.
2144
2145WINDOW must be a valid window. Optional argument HORIZONTAL
2146non-nil means convert SIZE columns to pixels.
2147
2148Optional argument PIXELWISE non-nil means SIZE already specifies
2149pixels but may have to be adjusted to a multiple of the character
2150size of WINDOW's frame. Optional argument ROUND-MAYBE non-nil
2151means round to the nearest multiple of the character size of
2152WINDOW's frame if the option `window-resize-pixelwise' is nil."
2153 (setq window (window-normalize-window window))
2154 (let ((char-size (frame-char-size window horizontal)))
2155 (if pixelwise
2156 (if (and round-maybe (not window-resize-pixelwise))
2157 (* (round size char-size) char-size)
2158 size)
2159 (* size char-size))))
2160
880e6158
MR
2161(defun window--pixel-to-total-1 (window horizontal char-size)
2162 "Subroutine of `window--pixel-to-total'."
2163 (let ((child (window-child window)))
2164 (if (window-combination-p window horizontal)
2165 ;; In an iso-combination distribute sizes proportionally.
2166 (let ((remainder (window-new-total window))
c44de18d 2167 size best-child rem best-rem)
880e6158
MR
2168 ;; Initialize total sizes to each child's floor.
2169 (while child
c44de18d 2170 (setq size (max (/ (window-size child horizontal t) char-size) 1))
880e6158
MR
2171 (set-window-new-total child size)
2172 (setq remainder (- remainder size))
2173 (setq child (window-next-sibling child)))
2174 ;; Distribute remainder.
2175 (while (> remainder 0)
2176 (setq child (window-last-child window))
2177 (setq best-child nil)
c44de18d 2178 (setq best-rem 0)
880e6158 2179 (while child
c44de18d
MR
2180 (when (and (<= (window-new-total child)
2181 (/ (window-size child horizontal t) char-size))
2182 (> (setq rem (% (window-size child horizontal t)
2183 char-size))
2184 best-rem))
2185 (setq best-child child)
2186 (setq best-rem rem))
880e6158
MR
2187 (setq child (window-prev-sibling child)))
2188 ;; We MUST have a best-child here.
2189 (set-window-new-total best-child 1 t)
2190 (setq remainder (1- remainder)))
2191 ;; Recurse.
2192 (setq child (window-child window))
2193 (while child
2194 (window--pixel-to-total-1 child horizontal char-size)
2195 (setq child (window-next-sibling child))))
2196 ;; In an ortho-combination assign new sizes directly.
2197 (let ((size (window-new-total window)))
2198 (while child
2199 (set-window-new-total child size)
2200 (window--pixel-to-total-1 child horizontal char-size)
2201 (setq child (window-next-sibling child)))))))
2202
2203(defun window--pixel-to-total (&optional frame horizontal)
2204 "On FRAME assign new total window heights from pixel heights.
2205FRAME must be a live frame and defaults to the selected frame.
2206
2207Optional argument HORIZONTAL non-nil means assign new total
2208window widths from pixel widths."
2209 (setq frame (window-normalize-frame frame))
c44de18d
MR
2210 (let* ((char-size (frame-char-size frame horizontal))
2211 (root (frame-root-window))
2212 (root-size (window-size root horizontal t))
2213 ;; We have to care about the minibuffer window only if it
2214 ;; appears together with the root window on this frame.
2215 (mini (let ((mini (minibuffer-window frame)))
2216 (and (eq (window-frame mini) frame)
2217 (not (eq mini root)) mini)))
2218 (mini-size (and mini (window-size mini horizontal t))))
2219 ;; We round the line/column sizes of windows here to the nearest
2220 ;; integer. In some cases this can make windows appear _larger_
2221 ;; than the containing frame (line/column-wise) because the latter's
2222 ;; sizes are not (yet) rounded. We might eventually fix that.
2223 (if (and mini (not horizontal))
2224 (let (lines)
2225 (set-window-new-total root (max (/ root-size char-size) 1))
2226 (set-window-new-total mini (max (/ mini-size char-size) 1))
2227 (setq lines (- (round (+ root-size mini-size) char-size)
2228 (+ (window-new-total root) (window-new-total mini))))
2229 (while (> lines 0)
2230 (if (>= (% root-size (window-new-total root))
2231 (% mini-size (window-new-total mini)))
2232 (set-window-new-total root 1 t)
2233 (set-window-new-total mini 1 t))
2234 (setq lines (1- lines))))
2235 (set-window-new-total root (round root-size char-size))
2236 (when mini
2237 ;; This is taken in the horizontal case only.
2238 (set-window-new-total mini (round mini-size char-size))))
880e6158 2239 (unless (window-buffer root)
c44de18d
MR
2240 (window--pixel-to-total-1 root horizontal char-size))
2241 ;; Apply the new sizes.
2242 (window-resize-apply-total frame horizontal)))
880e6158 2243
5386012d 2244(defun window--resize-reset (&optional frame horizontal)
9aab8e0d
MR
2245 "Reset resize values for all windows on FRAME.
2246FRAME defaults to the selected frame.
2247
880e6158 2248This function stores the current value of `window-size' applied
9aab8e0d
MR
2249with argument HORIZONTAL in the new total size of all windows on
2250FRAME. It also resets the new normal size of each of these
2251windows."
5386012d
MR
2252 (window--resize-reset-1
2253 (frame-root-window (window-normalize-frame frame)) horizontal))
9aab8e0d 2254
5386012d
MR
2255(defun window--resize-reset-1 (window horizontal)
2256 "Internal function of `window--resize-reset'."
9aab8e0d 2257 ;; Register old size in the new total size.
880e6158
MR
2258 (set-window-new-pixel window (window-size window horizontal t))
2259 (set-window-new-total window (window-size window horizontal))
9aab8e0d
MR
2260 ;; Reset new normal size.
2261 (set-window-new-normal window)
2262 (when (window-child window)
5386012d 2263 (window--resize-reset-1 (window-child window) horizontal))
9aab8e0d 2264 (when (window-right window)
5386012d 2265 (window--resize-reset-1 (window-right window) horizontal)))
9aab8e0d 2266
562dd5e9
MR
2267;; The following routine is used to manually resize the minibuffer
2268;; window and is currently used, for example, by ispell.el.
5386012d 2269(defun window--resize-mini-window (window delta)
880e6158
MR
2270 "Resize minibuffer window WINDOW by DELTA pixels.
2271If WINDOW cannot be resized by DELTA pixels make it as large (or
2116e93c 2272as small) as possible, but don't signal an error."
562dd5e9
MR
2273 (when (window-minibuffer-p window)
2274 (let* ((frame (window-frame window))
2275 (root (frame-root-window frame))
880e6158 2276 (height (window-pixel-height window))
562dd5e9 2277 (min-delta
880e6158
MR
2278 (- (window-pixel-height root)
2279 (window-min-size root nil nil t))))
562dd5e9
MR
2280 ;; Sanitize DELTA.
2281 (cond
2282 ((<= (+ height delta) 0)
880e6158 2283 (setq delta (- (frame-char-height (window-frame window)) height)))
562dd5e9
MR
2284 ((> delta min-delta)
2285 (setq delta min-delta)))
2286
880e6158
MR
2287 (unless (zerop delta)
2288 ;; Resize now.
2289 (window--resize-reset frame)
2290 ;; Ideally we should be able to resize just the last child of root
2291 ;; here. See the comment in `resize-root-window-vertically' for
2292 ;; why we do not do that.
2293 (window--resize-this-window root (- delta) nil nil t)
2294 (set-window-new-pixel window (+ height delta))
2295 ;; The following routine catches the case where we want to resize
2296 ;; a minibuffer-only frame.
2297 (when (resize-mini-window-internal window)
2298 (window--pixel-to-total frame)
2299 (run-window-configuration-change-hook frame))))))
2300
2301(defun window--resize-apply-p (frame &optional horizontal)
2302 "Return t when a window on FRAME shall be resized vertically.
2303Optional argument HORIZONTAL non-nil means return t when a window
2304shall be resized horizontally."
2305(catch 'apply
2306 (walk-window-tree
2307 (lambda (window)
2308 (unless (= (window-new-pixel window)
2309 (window-size window horizontal t))
2310 (throw 'apply t)))
2311 frame t)
2312 nil))
2313
2314(defun window-resize (window delta &optional horizontal ignore pixelwise)
562dd5e9
MR
2315 "Resize WINDOW vertically by DELTA lines.
2316WINDOW can be an arbitrary window and defaults to the selected
2317one. An attempt to resize the root window of a frame will raise
2318an error though.
2319
2320DELTA a positive number means WINDOW shall be enlarged by DELTA
2321lines. DELTA negative means WINDOW shall be shrunk by -DELTA
2322lines.
2323
2324Optional argument HORIZONTAL non-nil means resize WINDOW
2325horizontally by DELTA columns. In this case a positive DELTA
2326means enlarge WINDOW by DELTA columns. DELTA negative means
2327WINDOW shall be shrunk by -DELTA columns.
2328
2116e93c 2329Optional argument IGNORE non-nil means ignore restrictions
562dd5e9 2330imposed by fixed size windows, `window-min-height' or
2116e93c
EZ
2331`window-min-width' settings. If IGNORE is a window, ignore
2332restrictions for that window only. If IGNORE equals `safe',
562dd5e9 2333live windows may get as small as `window-safe-min-height' lines
2116e93c
EZ
2334and `window-safe-min-width' columns. Any other non-nil value
2335means ignore all of the above restrictions for all windows.
562dd5e9 2336
880e6158
MR
2337Optional argument PIXELWISE non-nil means resize WINDOW by DELTA
2338pixels.
2339
562dd5e9
MR
2340This function resizes other windows proportionally and never
2341deletes any windows. If you want to move only the low (right)
2342edge of WINDOW consider using `adjust-window-trailing-edge'
2343instead."
447f16b8 2344 (setq window (window-normalize-window window))
562dd5e9 2345 (let* ((frame (window-frame window))
feeb6f53 2346 (minibuffer-window (minibuffer-window frame))
562dd5e9 2347 sibling)
880e6158
MR
2348 (setq delta (window--size-to-pixel
2349 window delta horizontal pixelwise t))
562dd5e9
MR
2350 (cond
2351 ((eq window (frame-root-window frame))
2352 (error "Cannot resize the root window of a frame"))
2353 ((window-minibuffer-p window)
41cfe0cb
MR
2354 (if horizontal
2355 (error "Cannot resize minibuffer window horizontally")
2356 (window--resize-mini-window window delta)))
feeb6f53
MR
2357 ((and (not horizontal)
2358 (window-full-height-p window)
2359 (eq (window-frame minibuffer-window) frame)
2360 (or (not resize-mini-windows)
2361 (eq minibuffer-window (active-minibuffer-window))))
2362 ;; If WINDOW is full height and either `resize-mini-windows' is
2363 ;; nil or the minibuffer window is active, resize the minibuffer
2364 ;; window.
2365 (window--resize-mini-window minibuffer-window (- delta)))
880e6158
MR
2366 ((window--resizable-p
2367 window delta horizontal ignore nil nil nil t)
5386012d
MR
2368 (window--resize-reset frame horizontal)
2369 (window--resize-this-window window delta horizontal ignore t)
a0c2d0ae 2370 (if (and (not window-combination-resize)
3d8daefe 2371 (window-combined-p window horizontal)
562dd5e9 2372 (setq sibling (or (window-right window) (window-left window)))
880e6158
MR
2373 (window-sizable-p
2374 sibling (- delta) horizontal ignore t))
a0c2d0ae 2375 ;; If window-combination-resize is nil, WINDOW is part of an
89d61221 2376 ;; iso-combination, and WINDOW's neighboring right or left
562dd5e9
MR
2377 ;; sibling can be resized as requested, resize that sibling.
2378 (let ((normal-delta
2379 (/ (float delta)
880e6158 2380 (window-size (window-parent window) horizontal t))))
5386012d 2381 (window--resize-this-window sibling (- delta) horizontal nil t)
562dd5e9
MR
2382 (set-window-new-normal
2383 window (+ (window-normal-size window horizontal)
2384 normal-delta))
2385 (set-window-new-normal
2386 sibling (- (window-normal-size sibling horizontal)
2387 normal-delta)))
2388 ;; Otherwise, resize all other windows in the same combination.
5386012d 2389 (window--resize-siblings window delta horizontal ignore))
880e6158 2390 (when (window--resize-apply-p frame horizontal)
8dd3e94f
MR
2391 (if (window-resize-apply frame horizontal)
2392 (progn
2393 (window--pixel-to-total frame horizontal)
2394 (run-window-configuration-change-hook frame))
2395 (error "Failed to apply resizing %s" window))))
562dd5e9
MR
2396 (t
2397 (error "Cannot resize window %s" window)))))
2398
880e6158 2399(defun window-resize-no-error (window delta &optional horizontal ignore pixelwise)
27fcfe31
MR
2400 "Resize WINDOW vertically if it is resizable by DELTA lines.
2401This function is like `window-resize' but does not signal an
2402error when WINDOW cannot be resized. For the meaning of the
880e6158
MR
2403optional arguments see the documentation of `window-resize'.
2404
2405Optional argument PIXELWISE non-nil means interpret DELTA as
2406pixels."
2407 (when (window--resizable-p
2408 window delta horizontal ignore nil nil nil pixelwise)
2409 (window-resize window delta horizontal ignore pixelwise)))
27fcfe31 2410
4b0d61e3 2411(defun window--resize-child-windows-skip-p (window)
9aab8e0d
MR
2412 "Return non-nil if WINDOW shall be skipped by resizing routines."
2413 (memq (window-new-normal window) '(ignore stuck skip)))
2414
be7f5545
MR
2415(defun window--resize-child-windows-normal (parent horizontal window this-delta &optional trail other-delta)
2416 "Recursively set new normal height of child windows of window PARENT.
9aab8e0d 2417HORIZONTAL non-nil means set the new normal width of these
be7f5545 2418windows. WINDOW specifies a child window of PARENT that has been
382c953b 2419resized by THIS-DELTA lines (columns).
9aab8e0d 2420
2116e93c
EZ
2421Optional argument TRAIL either `before' or `after' means set values
2422only for windows before or after WINDOW. Optional argument
2423OTHER-DELTA, a number, specifies that this many lines (columns)
382c953b 2424have been obtained from (or returned to) an ancestor window of
9aab8e0d
MR
2425PARENT in order to resize WINDOW."
2426 (let* ((delta-normal
880e6158
MR
2427 (if (and (= (- this-delta)
2428 (window-size window horizontal t))
9aab8e0d
MR
2429 (zerop other-delta))
2430 ;; When WINDOW gets deleted and we can return its entire
2431 ;; space to its siblings, use WINDOW's normal size as the
2432 ;; normal delta.
2433 (- (window-normal-size window horizontal))
2434 ;; In any other case calculate the normal delta from the
2435 ;; relation of THIS-DELTA to the total size of PARENT.
880e6158
MR
2436 (/ (float this-delta)
2437 (window-size parent horizontal t))))
9aab8e0d
MR
2438 (sub (window-child parent))
2439 (parent-normal 0.0)
2440 (skip (eq trail 'after)))
2441
be7f5545
MR
2442 ;; Set parent-normal to the sum of the normal sizes of all child
2443 ;; windows of PARENT that shall be resized, excluding only WINDOW
9aab8e0d
MR
2444 ;; and any windows specified by the optional TRAIL argument.
2445 (while sub
2446 (cond
2447 ((eq sub window)
2448 (setq skip (eq trail 'before)))
2449 (skip)
2450 (t
2451 (setq parent-normal
2452 (+ parent-normal (window-normal-size sub horizontal)))))
2453 (setq sub (window-right sub)))
2454
be7f5545 2455 ;; Set the new normal size of all child windows of PARENT from what
9aab8e0d
MR
2456 ;; they should have contributed for recovering THIS-DELTA lines
2457 ;; (columns).
2458 (setq sub (window-child parent))
2459 (setq skip (eq trail 'after))
2460 (while sub
2461 (cond
2462 ((eq sub window)
2463 (setq skip (eq trail 'before)))
2464 (skip)
2465 (t
2466 (let ((old-normal (window-normal-size sub horizontal)))
2467 (set-window-new-normal
2468 sub (min 1.0 ; Don't get larger than 1.
2469 (max (- old-normal
2470 (* (/ old-normal parent-normal)
2471 delta-normal))
2472 ;; Don't drop below 0.
2473 0.0))))))
2474 (setq sub (window-right sub)))
2475
2476 (when (numberp other-delta)
2477 ;; Set the new normal size of windows from what they should have
2478 ;; contributed for recovering OTHER-DELTA lines (columns).
880e6158
MR
2479 (setq delta-normal (/ (float (window-size parent horizontal t))
2480 (+ (window-size parent horizontal t)
9aab8e0d
MR
2481 other-delta)))
2482 (setq sub (window-child parent))
2483 (setq skip (eq trail 'after))
2484 (while sub
2485 (cond
2486 ((eq sub window)
2487 (setq skip (eq trail 'before)))
2488 (skip)
2489 (t
2490 (set-window-new-normal
2491 sub (min 1.0 ; Don't get larger than 1.
2492 (max (* (window-new-normal sub) delta-normal)
2493 ;; Don't drop below 0.
2494 0.0)))))
2495 (setq sub (window-right sub))))
2496
2497 ;; Set the new normal size of WINDOW to what is left by the sum of
2498 ;; the normal sizes of its siblings.
2499 (set-window-new-normal
2500 window
2501 (let ((sum 0))
2502 (setq sub (window-child parent))
2503 (while sub
2504 (cond
2505 ((eq sub window))
2506 ((not (numberp (window-new-normal sub)))
2507 (setq sum (+ sum (window-normal-size sub horizontal))))
2508 (t
2509 (setq sum (+ sum (window-new-normal sub)))))
2510 (setq sub (window-right sub)))
2511 ;; Don't get larger than 1 or smaller than 0.
2512 (min 1.0 (max (- 1.0 sum) 0.0))))))
2513
880e6158
MR
2514(defun window--resize-child-windows (parent delta &optional horizontal window ignore trail edge char-size)
2515 "Resize child windows of window PARENT vertically by DELTA pixels.
9aab8e0d
MR
2516PARENT must be a vertically combined internal window.
2517
880e6158
MR
2518Optional argument HORIZONTAL non-nil means resize child windows
2519of PARENT horizontally by DELTA pixels. In this case PARENT must
9aab8e0d
MR
2520be a horizontally combined internal window.
2521
2522WINDOW, if specified, must denote a child window of PARENT that
880e6158 2523is resized by DELTA pixels.
9aab8e0d 2524
2116e93c 2525Optional argument IGNORE non-nil means ignore restrictions
9aab8e0d 2526imposed by fixed size windows, `window-min-height' or
2116e93c 2527`window-min-width' settings. If IGNORE equals `safe', live
9aab8e0d 2528windows may get as small as `window-safe-min-height' lines and
2116e93c
EZ
2529`window-safe-min-width' columns. If IGNORE is a window, ignore
2530restrictions for that window only. Any other non-nil value means
2531ignore all of the above restrictions for all windows.
9aab8e0d
MR
2532
2533Optional arguments TRAIL and EDGE, when non-nil, restrict the set
2534of windows that shall be resized. If TRAIL equals `before',
2535resize only windows on the left or above EDGE. If TRAIL equals
2536`after', resize only windows on the right or below EDGE. Also,
2537preferably only resize windows adjacent to EDGE.
2538
880e6158
MR
2539If the optional argument CHAR-SIZE is a positive integer, it specifies
2540the number of pixels by which windows are incrementally resized.
2541If CHAR-SIZE is nil, this means to use the value of
2542`frame-char-height' or `frame-char-width' of WINDOW's frame.
2543
9aab8e0d
MR
2544Return the symbol `normalized' if new normal sizes have been
2545already set by this routine."
2546 (let* ((first (window-child parent))
9c52dd5a 2547 (last (window-last-child parent))
880e6158
MR
2548 (parent-total (+ (window-size parent horizontal t)
2549 delta))
2550 (char-size (or char-size
2551 (and window-resize-pixelwise 1)
2552 (frame-char-size window horizontal)))
2553 sub best-window best-value best-delta)
9aab8e0d
MR
2554
2555 (if (and edge (memq trail '(before after))
2556 (progn
2557 (setq sub first)
2558 (while (and (window-right sub)
2559 (or (and (eq trail 'before)
be7f5545 2560 (not (window--resize-child-windows-skip-p
9aab8e0d
MR
2561 (window-right sub))))
2562 (and (eq trail 'after)
be7f5545 2563 (window--resize-child-windows-skip-p sub))))
9aab8e0d
MR
2564 (setq sub (window-right sub)))
2565 sub)
2566 (if horizontal
2567 (if (eq trail 'before)
880e6158 2568 (= (+ (window-pixel-left sub) (window-pixel-width sub))
9aab8e0d 2569 edge)
880e6158 2570 (= (window-pixel-left sub) edge))
9aab8e0d 2571 (if (eq trail 'before)
880e6158 2572 (= (+ (window-pixel-top sub) (window-pixel-height sub))
9aab8e0d 2573 edge)
880e6158
MR
2574 (= (window-pixel-top sub) edge)))
2575 (window-sizable-p sub delta horizontal ignore t))
9aab8e0d
MR
2576 ;; Resize only windows adjacent to EDGE.
2577 (progn
5386012d
MR
2578 (window--resize-this-window
2579 sub delta horizontal ignore t trail edge)
9aab8e0d
MR
2580 (if (and window (eq (window-parent sub) parent))
2581 (progn
2582 ;; Assign new normal sizes.
2583 (set-window-new-normal
880e6158 2584 sub (/ (float (window-new-pixel sub)) parent-total))
9aab8e0d
MR
2585 (set-window-new-normal
2586 window (- (window-normal-size window horizontal)
2587 (- (window-new-normal sub)
2588 (window-normal-size sub horizontal)))))
be7f5545 2589 (window--resize-child-windows-normal
5386012d
MR
2590 parent horizontal sub 0 trail delta))
2591 ;; Return 'normalized to notify `window--resize-siblings' that
9aab8e0d
MR
2592 ;; normal sizes have been already set.
2593 'normalized)
2594 ;; Resize all windows proportionally.
9c52dd5a 2595 (setq sub last)
9aab8e0d
MR
2596 (while sub
2597 (cond
be7f5545
MR
2598 ((or (window--resize-child-windows-skip-p sub)
2599 ;; Ignore windows to skip and fixed-size child windows -
2600 ;; in the latter case make it a window to skip.
9aab8e0d
MR
2601 (and (not ignore)
2602 (window-size-fixed-p sub horizontal)
2603 (set-window-new-normal sub 'ignore))))
2604 ((< delta 0)
2605 ;; When shrinking store the number of lines/cols we can get
2606 ;; from this window here together with the total/normal size
2607 ;; factor.
2608 (set-window-new-normal
2609 sub
2610 (cons
2611 ;; We used to call this with NODOWN t, "fixed" 2011-05-11.
880e6158
MR
2612 (window-min-delta sub horizontal ignore trail t nil t)
2613 (- (/ (float (window-size sub horizontal t))
9aab8e0d
MR
2614 parent-total)
2615 (window-normal-size sub horizontal)))))
2616 ((> delta 0)
2617 ;; When enlarging store the total/normal size factor only
2618 (set-window-new-normal
2619 sub
880e6158 2620 (- (/ (float (window-size sub horizontal t))
9aab8e0d
MR
2621 parent-total)
2622 (window-normal-size sub horizontal)))))
2623
9c52dd5a 2624 (setq sub (window-left sub)))
9aab8e0d
MR
2625
2626 (cond
2627 ((< delta 0)
2628 ;; Shrink windows by delta.
2629 (setq best-window t)
2630 (while (and best-window (not (zerop delta)))
9c52dd5a 2631 (setq sub last)
9aab8e0d
MR
2632 (setq best-window nil)
2633 (setq best-value most-negative-fixnum)
2634 (while sub
2635 (when (and (consp (window-new-normal sub))
8dd3e94f 2636 (not (<= (car (window-new-normal sub)) 0))
9aab8e0d
MR
2637 (> (cdr (window-new-normal sub)) best-value))
2638 (setq best-window sub)
2639 (setq best-value (cdr (window-new-normal sub))))
2640
9c52dd5a 2641 (setq sub (window-left sub)))
9aab8e0d
MR
2642
2643 (when best-window
880e6158
MR
2644 (setq best-delta (min (car (window-new-normal best-window))
2645 char-size (- delta)))
2646 (setq delta (+ delta best-delta))
2647 (set-window-new-pixel best-window (- best-delta) t)
2648 (set-window-new-normal
2649 best-window
2650 (if (= (car (window-new-normal best-window)) best-delta)
2651 'skip ; We can't shrink best-window any further.
8dd3e94f 2652 (cons (- (car (window-new-normal best-window)) best-delta)
880e6158
MR
2653 (- (/ (float (window-new-pixel best-window))
2654 parent-total)
2655 (window-normal-size best-window horizontal))))))))
9aab8e0d
MR
2656 ((> delta 0)
2657 ;; Enlarge windows by delta.
2658 (setq best-window t)
2659 (while (and best-window (not (zerop delta)))
9c52dd5a 2660 (setq sub last)
9aab8e0d
MR
2661 (setq best-window nil)
2662 (setq best-value most-positive-fixnum)
2663 (while sub
2664 (when (and (numberp (window-new-normal sub))
2665 (< (window-new-normal sub) best-value))
2666 (setq best-window sub)
2667 (setq best-value (window-new-normal sub)))
2668
9c52dd5a 2669 (setq sub (window-left sub)))
9aab8e0d
MR
2670
2671 (when best-window
880e6158
MR
2672 (setq best-delta (min delta char-size))
2673 (setq delta (- delta best-delta))
2674 (set-window-new-pixel best-window best-delta t)
2675 (set-window-new-normal
2676 best-window
2677 (- (/ (float (window-new-pixel best-window))
2678 parent-total)
2679 (window-normal-size best-window horizontal)))))))
9aab8e0d
MR
2680
2681 (when best-window
9c52dd5a 2682 (setq sub last)
9aab8e0d
MR
2683 (while sub
2684 (when (or (consp (window-new-normal sub))
2685 (numberp (window-new-normal sub)))
d615d6d2 2686 ;; Reset new normal size fields so `window-resize-apply'
9aab8e0d
MR
2687 ;; won't use them to apply new sizes.
2688 (set-window-new-normal sub))
2689
2690 (unless (eq (window-new-normal sub) 'ignore)
be7f5545 2691 ;; Resize this window's child windows (back-engineering
9aab8e0d 2692 ;; delta from sub's old and new total sizes).
880e6158
MR
2693 (let ((delta (- (window-new-pixel sub)
2694 (window-size sub horizontal t))))
9aab8e0d
MR
2695 (unless (and (zerop delta) (not trail))
2696 ;; For the TRAIL non-nil case we have to resize SUB
2697 ;; recursively even if it's size does not change.
5386012d 2698 (window--resize-this-window
9aab8e0d 2699 sub delta horizontal ignore nil trail edge))))
9c52dd5a 2700 (setq sub (window-left sub)))))))
9aab8e0d 2701
880e6158
MR
2702(defun window--resize-siblings (window delta &optional horizontal ignore trail edge char-size)
2703 "Resize other windows when WINDOW is resized vertically by DELTA pixels.
9aab8e0d 2704Optional argument HORIZONTAL non-nil means resize other windows
880e6158 2705when WINDOW is resized horizontally by DELTA pixels. WINDOW
9aab8e0d
MR
2706itself is not resized by this function.
2707
2116e93c 2708Optional argument IGNORE non-nil means ignore restrictions
9aab8e0d 2709imposed by fixed size windows, `window-min-height' or
2116e93c 2710`window-min-width' settings. If IGNORE equals `safe', live
9aab8e0d 2711windows may get as small as `window-safe-min-height' lines and
2116e93c
EZ
2712`window-safe-min-width' columns. If IGNORE is a window, ignore
2713restrictions for that window only. Any other non-nil value means
2714ignore all of the above restrictions for all windows.
9aab8e0d
MR
2715
2716Optional arguments TRAIL and EDGE, when non-nil, refine the set
2717of windows that shall be resized. If TRAIL equals `before',
2718resize only windows on the left or above EDGE. If TRAIL equals
2719`after', resize only windows on the right or below EDGE. Also,
2720preferably only resize windows adjacent to EDGE."
2721 (when (window-parent window)
2722 (let* ((parent (window-parent window))
2723 (sub (window-child parent)))
3d8daefe 2724 (if (window-combined-p sub horizontal)
9aab8e0d
MR
2725 ;; In an iso-combination try to extract DELTA from WINDOW's
2726 ;; siblings.
cb882333 2727 (let ((skip (eq trail 'after))
9aab8e0d
MR
2728 this-delta other-delta)
2729 ;; Decide which windows shall be left alone.
2730 (while sub
2731 (cond
2732 ((eq sub window)
2733 ;; Make sure WINDOW is left alone when
2734 ;; resizing its siblings.
2735 (set-window-new-normal sub 'ignore)
2736 (setq skip (eq trail 'before)))
2737 (skip
2738 ;; Make sure this sibling is left alone when
2739 ;; resizing its siblings.
2740 (set-window-new-normal sub 'ignore))
842e3a93 2741 ((or (window--size-ignore-p sub ignore)
9aab8e0d
MR
2742 (not (window-size-fixed-p sub horizontal)))
2743 ;; Set this-delta to t to signal that we found a sibling
2744 ;; of WINDOW whose size is not fixed.
2745 (setq this-delta t)))
2746
2747 (setq sub (window-right sub)))
2748
2749 ;; Set this-delta to what we can get from WINDOW's siblings.
880e6158 2750 (if (= (- delta) (window-size window horizontal t))
9aab8e0d 2751 ;; A deletion, presumably. We must handle this case
2cffd681 2752 ;; specially since `window--resizable' can't be used.
9aab8e0d
MR
2753 (if this-delta
2754 ;; There's at least one resizable sibling we can
2755 ;; give WINDOW's size to.
2756 (setq this-delta delta)
2757 ;; No resizable sibling exists.
2758 (setq this-delta 0))
2759 ;; Any other form of resizing.
2760 (setq this-delta
880e6158
MR
2761 (window--resizable
2762 window delta horizontal ignore trail t nil t)))
9aab8e0d
MR
2763
2764 ;; Set other-delta to what we still have to get from
2765 ;; ancestor windows of parent.
2766 (setq other-delta (- delta this-delta))
2767 (unless (zerop other-delta)
2768 ;; Unless we got everything from WINDOW's siblings, PARENT
2769 ;; must be resized by other-delta lines or columns.
880e6158 2770 (set-window-new-pixel parent other-delta 'add))
9aab8e0d
MR
2771
2772 (if (zerop this-delta)
2773 ;; We haven't got anything from WINDOW's siblings but we
2774 ;; must update the normal sizes to respect other-delta.
be7f5545 2775 (window--resize-child-windows-normal
9aab8e0d
MR
2776 parent horizontal window this-delta trail other-delta)
2777 ;; We did get something from WINDOW's siblings which means
be7f5545
MR
2778 ;; we have to resize their child windows.
2779 (unless (eq (window--resize-child-windows
5386012d 2780 parent (- this-delta) horizontal
880e6158 2781 window ignore trail edge char-size)
be7f5545 2782 ;; If `window--resize-child-windows' returns
5386012d
MR
2783 ;; 'normalized, this means it has set the
2784 ;; normal sizes already.
9aab8e0d
MR
2785 'normalized)
2786 ;; Set the normal sizes.
be7f5545 2787 (window--resize-child-windows-normal
9aab8e0d
MR
2788 parent horizontal window this-delta trail other-delta))
2789 ;; Set DELTA to what we still have to get from ancestor
2790 ;; windows.
2791 (setq delta other-delta)))
2792
2793 ;; In an ortho-combination all siblings of WINDOW must be
2794 ;; resized by DELTA.
880e6158 2795 (set-window-new-pixel parent delta 'add)
9aab8e0d
MR
2796 (while sub
2797 (unless (eq sub window)
880e6158
MR
2798 (window--resize-this-window
2799 sub delta horizontal ignore t))
9aab8e0d
MR
2800 (setq sub (window-right sub))))
2801
2802 (unless (zerop delta)
2803 ;; "Go up."
5386012d 2804 (window--resize-siblings
880e6158 2805 parent delta horizontal ignore trail edge char-size)))))
9aab8e0d 2806
880e6158
MR
2807(defun window--resize-this-window (window delta &optional horizontal ignore add trail edge char-size)
2808 "Resize WINDOW vertically by DELTA pixels.
9aab8e0d 2809Optional argument HORIZONTAL non-nil means resize WINDOW
880e6158 2810horizontally by DELTA pixels.
9aab8e0d 2811
2116e93c 2812Optional argument IGNORE non-nil means ignore restrictions
9aab8e0d 2813imposed by fixed size windows, `window-min-height' or
2116e93c 2814`window-min-width' settings. If IGNORE equals `safe', live
9aab8e0d 2815windows may get as small as `window-safe-min-height' lines and
2116e93c
EZ
2816`window-safe-min-width' columns. If IGNORE is a window, ignore
2817restrictions for that window only. Any other non-nil value
2818means ignore all of the above restrictions for all windows.
9aab8e0d
MR
2819
2820Optional argument ADD non-nil means add DELTA to the new total
2821size of WINDOW.
2822
2823Optional arguments TRAIL and EDGE, when non-nil, refine the set
2824of windows that shall be resized. If TRAIL equals `before',
2825resize only windows on the left or above EDGE. If TRAIL equals
2826`after', resize only windows on the right or below EDGE. Also,
2827preferably only resize windows adjacent to EDGE.
2828
880e6158
MR
2829If the optional argument CHAR-SIZE is a positive integer, it specifies
2830the number of pixels by which windows are incrementally resized.
2831If CHAR-SIZE is nil, this means to use the value of
2832`frame-char-height' or `frame-char-width' of WINDOW's frame.
2833
be7f5545 2834This function recursively resizes WINDOW's child windows to fit the
2cffd681 2835new size. Make sure that WINDOW is `window--resizable' before
9aab8e0d
MR
2836calling this function. Note that this function does not resize
2837siblings of WINDOW or WINDOW's parent window. You have to
d615d6d2 2838eventually call `window-resize-apply' in order to make resizing
9aab8e0d
MR
2839actually take effect."
2840 (when add
2841 ;; Add DELTA to the new total size of WINDOW.
880e6158 2842 (set-window-new-pixel window delta t))
387522b2 2843
9aab8e0d
MR
2844 (let ((sub (window-child window)))
2845 (cond
2846 ((not sub))
3d8daefe 2847 ((window-combined-p sub horizontal)
be7f5545 2848 ;; In an iso-combination resize child windows according to their
9aab8e0d 2849 ;; normal sizes.
be7f5545 2850 (window--resize-child-windows
880e6158 2851 window delta horizontal nil ignore trail edge char-size))
be7f5545 2852 ;; In an ortho-combination resize each child window by DELTA.
9aab8e0d
MR
2853 (t
2854 (while sub
5386012d 2855 (window--resize-this-window
880e6158 2856 sub delta horizontal ignore t trail edge char-size)
9aab8e0d
MR
2857 (setq sub (window-right sub)))))))
2858
880e6158 2859(defun window--resize-root-window (window delta horizontal ignore pixelwise)
9aab8e0d
MR
2860 "Resize root window WINDOW vertically by DELTA lines.
2861HORIZONTAL non-nil means resize root window WINDOW horizontally
2862by DELTA columns.
2863
2864IGNORE non-nil means ignore any restrictions imposed by fixed
2865size windows, `window-min-height' or `window-min-width' settings.
2866
2867This function is only called by the frame resizing routines. It
2868resizes windows proportionally and never deletes any windows."
880e6158
MR
2869 (when (and (windowp window) (numberp delta))
2870 (let ((pixel-delta
2871 (if pixelwise
2872 delta
2873 (window--size-to-pixel window delta horizontal))))
2874 (when (window-sizable-p window pixel-delta horizontal ignore t)
2875 (window--resize-reset (window-frame window) horizontal)
2876 (window--resize-this-window
2877 window pixel-delta horizontal ignore t)))))
9aab8e0d 2878
880e6158 2879(defun window--resize-root-window-vertically (window delta pixelwise)
9aab8e0d
MR
2880 "Resize root window WINDOW vertically by DELTA lines.
2881If DELTA is less than zero and we can't shrink WINDOW by DELTA
2882lines, shrink it as much as possible. If DELTA is greater than
be7f5545 2883zero, this function can resize fixed-size windows in order to
880e6158
MR
2884recover the necessary lines. Return the number of lines that
2885were recovered.
9aab8e0d 2886
f224e500 2887Third argument PIXELWISE non-nil means to interpret DELTA as
880e6158 2888pixels and return the number of pixels that were recovered.
9aab8e0d 2889
880e6158
MR
2890This function is called by the minibuffer window resizing
2891routines."
2892 (let* ((frame (window-frame window))
2893 (pixel-delta
2894 (cond
2895 (pixelwise
2896 delta)
2897 ((numberp delta)
2898 (* (frame-char-height frame) delta))
2899 (t 0)))
2900 ignore)
9c52dd5a 2901 (cond
880e6158
MR
2902 ((zerop pixel-delta))
2903 ((< pixel-delta 0)
2904 (setq pixel-delta (window-sizable window pixel-delta nil nil pixelwise))
9c52dd5a
MR
2905 (window--resize-reset frame)
2906 ;; When shrinking the root window, emulate an edge drag in order
2907 ;; to not resize other windows if we can avoid it (Bug#12419).
2908 (window--resize-this-window
880e6158
MR
2909 window pixel-delta nil ignore t 'before
2910 (+ (window-pixel-top window) (window-pixel-height window)))
9c52dd5a
MR
2911 ;; Don't record new normal sizes to make sure that shrinking back
2912 ;; proportionally works as intended.
2913 (walk-window-tree
2914 (lambda (window) (set-window-new-normal window 'ignore)) frame t))
880e6158 2915 ((> pixel-delta 0)
9c52dd5a 2916 (window--resize-reset frame)
880e6158 2917 (unless (window-sizable window pixel-delta nil nil pixelwise)
9c52dd5a
MR
2918 (setq ignore t))
2919 ;; When growing the root window, resize proportionally. This
2920 ;; should give windows back their original sizes (hopefully).
880e6158
MR
2921 (window--resize-this-window
2922 window pixel-delta nil ignore t)))
9c52dd5a 2923 ;; Return the possibly adjusted DELTA.
880e6158
MR
2924 (if pixelwise
2925 pixel-delta
2926 (/ pixel-delta (frame-char-height frame)))))
9aab8e0d 2927
880e6158 2928(defun adjust-window-trailing-edge (window delta &optional horizontal pixelwise)
562dd5e9
MR
2929 "Move WINDOW's bottom edge by DELTA lines.
2930Optional argument HORIZONTAL non-nil means move WINDOW's right
85c2386b
MR
2931edge by DELTA columns. WINDOW must be a valid window and
2932defaults to the selected one.
562dd5e9 2933
880e6158
MR
2934Optional argument PIXELWISE non-nil means interpret DELTA as
2935number of pixels.
2936
2116e93c 2937If DELTA is greater than zero, move the edge downwards or to the
562dd5e9
MR
2938right. If DELTA is less than zero, move the edge upwards or to
2939the left. If the edge can't be moved by DELTA lines or columns,
2940move it as far as possible in the desired direction."
447f16b8 2941 (setq window (window-normalize-window window))
41cfe0cb
MR
2942 (let* ((frame (window-frame window))
2943 (minibuffer-window (minibuffer-window frame))
2944 (right window)
2945 left this-delta min-delta max-delta)
880e6158
MR
2946
2947 (unless pixelwise
2948 (setq pixelwise t)
2949 (setq delta (* delta (frame-char-size window horizontal))))
2950
562dd5e9 2951 ;; Find the edge we want to move.
3d8daefe 2952 (while (and (or (not (window-combined-p right horizontal))
562dd5e9
MR
2953 (not (window-right right)))
2954 (setq right (window-parent right))))
2955 (cond
41cfe0cb
MR
2956 ((and (not right) (not horizontal)
2957 ;; Resize the minibuffer window if it's on the same frame as
2958 ;; and immediately below WINDOW and it's either active or
2959 ;; `resize-mini-windows' is nil.
2960 (eq (window-frame minibuffer-window) frame)
880e6158
MR
2961 (= (nth 1 (window-pixel-edges minibuffer-window))
2962 (nth 3 (window-pixel-edges window)))
41cfe0cb
MR
2963 (or (not resize-mini-windows)
2964 (eq minibuffer-window (active-minibuffer-window))))
2965 (window--resize-mini-window minibuffer-window (- delta)))
562dd5e9
MR
2966 ((or (not (setq left right)) (not (setq right (window-right right))))
2967 (if horizontal
2968 (error "No window on the right of this one")
2969 (error "No window below this one")))
2970 (t
2971 ;; Set LEFT to the first resizable window on the left. This step is
2972 ;; needed to handle fixed-size windows.
2973 (while (and left (window-size-fixed-p left horizontal))
2974 (setq left
2975 (or (window-left left)
2976 (progn
2977 (while (and (setq left (window-parent left))
3d8daefe 2978 (not (window-combined-p left horizontal))))
562dd5e9
MR
2979 (window-left left)))))
2980 (unless left
2981 (if horizontal
2982 (error "No resizable window on the left of this one")
2983 (error "No resizable window above this one")))
2984
2985 ;; Set RIGHT to the first resizable window on the right. This step
2986 ;; is needed to handle fixed-size windows.
2987 (while (and right (window-size-fixed-p right horizontal))
2988 (setq right
2989 (or (window-right right)
2990 (progn
2991 (while (and (setq right (window-parent right))
3d8daefe 2992 (not (window-combined-p right horizontal))))
562dd5e9
MR
2993 (window-right right)))))
2994 (unless right
2995 (if horizontal
2996 (error "No resizable window on the right of this one")
2997 (error "No resizable window below this one")))
2998
2999 ;; LEFT and RIGHT (which might be both internal windows) are now the
3000 ;; two windows we want to resize.
3001 (cond
3002 ((> delta 0)
880e6158
MR
3003 (setq max-delta
3004 (window--max-delta-1
3005 left 0 horizontal nil 'after nil pixelwise))
3006 (setq min-delta
3007 (window--min-delta-1
3008 right (- delta) horizontal nil 'before nil pixelwise))
562dd5e9
MR
3009 (when (or (< max-delta delta) (> min-delta (- delta)))
3010 ;; We can't get the whole DELTA - move as far as possible.
3011 (setq delta (min max-delta (- min-delta))))
3012 (unless (zerop delta)
3013 ;; Start resizing.
5386012d 3014 (window--resize-reset frame horizontal)
562dd5e9 3015 ;; Try to enlarge LEFT first.
880e6158 3016 (setq this-delta (window--resizable
8dd3e94f 3017 left delta horizontal nil 'after nil nil pixelwise))
562dd5e9 3018 (unless (zerop this-delta)
5386012d 3019 (window--resize-this-window
562dd5e9
MR
3020 left this-delta horizontal nil t 'before
3021 (if horizontal
880e6158
MR
3022 (+ (window-pixel-left left) (window-pixel-width left))
3023 (+ (window-pixel-top left) (window-pixel-height left)))))
562dd5e9 3024 ;; Shrink windows on right of LEFT.
5386012d 3025 (window--resize-siblings
562dd5e9
MR
3026 left delta horizontal nil 'after
3027 (if horizontal
880e6158
MR
3028 (window-pixel-left right)
3029 (window-pixel-top right)))))
562dd5e9 3030 ((< delta 0)
880e6158
MR
3031 (setq max-delta
3032 (window--max-delta-1
3033 right 0 horizontal nil 'before nil pixelwise))
3034 (setq min-delta
3035 (window--min-delta-1
3036 left delta horizontal nil 'after nil pixelwise))
562dd5e9
MR
3037 (when (or (< max-delta (- delta)) (> min-delta delta))
3038 ;; We can't get the whole DELTA - move as far as possible.
3039 (setq delta (max (- max-delta) min-delta)))
3040 (unless (zerop delta)
3041 ;; Start resizing.
5386012d 3042 (window--resize-reset frame horizontal)
562dd5e9 3043 ;; Try to enlarge RIGHT.
880e6158
MR
3044 (setq this-delta
3045 (window--resizable
8dd3e94f 3046 right (- delta) horizontal nil 'before nil nil pixelwise))
562dd5e9 3047 (unless (zerop this-delta)
5386012d 3048 (window--resize-this-window
562dd5e9
MR
3049 right this-delta horizontal nil t 'after
3050 (if horizontal
880e6158
MR
3051 (window-pixel-left right)
3052 (window-pixel-top right))))
562dd5e9 3053 ;; Shrink windows on left of RIGHT.
5386012d 3054 (window--resize-siblings
562dd5e9
MR
3055 right (- delta) horizontal nil 'before
3056 (if horizontal
880e6158
MR
3057 (+ (window-pixel-left left) (window-pixel-width left))
3058 (+ (window-pixel-top left) (window-pixel-height left)))))))
562dd5e9
MR
3059 (unless (zerop delta)
3060 ;; Don't report an error in the standard case.
880e6158
MR
3061 (when (window--resize-apply-p frame horizontal)
3062 (if (window-resize-apply frame horizontal)
3063 (progn
3064 (window--pixel-to-total frame horizontal)
3065 (run-window-configuration-change-hook frame))
3066 ;; But do report an error if applying the changes fails.
3067 (error "Failed adjusting window %s" window))))))))
562dd5e9
MR
3068
3069(defun enlarge-window (delta &optional horizontal)
2116e93c 3070 "Make the selected window DELTA lines taller.
562dd5e9
MR
3071Interactively, if no argument is given, make the selected window
3072one line taller. If optional argument HORIZONTAL is non-nil,
3073make selected window wider by DELTA columns. If DELTA is
0ff7851c 3074negative, shrink selected window by -DELTA lines or columns."
562dd5e9 3075 (interactive "p")
feeb6f53
MR
3076 (let ((minibuffer-window (minibuffer-window)))
3077 (cond
3078 ((zerop delta))
3079 ((window-size-fixed-p nil horizontal)
3080 (error "Selected window has fixed size"))
3081 ((window-minibuffer-p)
3082 (if horizontal
3083 (error "Cannot resize minibuffer window horizontally")
3084 (window--resize-mini-window (selected-window) delta)))
3085 ((and (not horizontal)
3086 (window-full-height-p)
3087 (eq (window-frame minibuffer-window) (selected-frame))
3088 (not resize-mini-windows))
3089 ;; If the selected window is full height and `resize-mini-windows'
3090 ;; is nil, resize the minibuffer window.
3091 (window--resize-mini-window minibuffer-window (- delta)))
880e6158 3092 ((window--resizable-p nil delta horizontal)
feeb6f53
MR
3093 (window-resize nil delta horizontal))
3094 (t
3095 (window-resize
3096 nil (if (> delta 0)
3097 (window-max-delta nil horizontal)
3098 (- (window-min-delta nil horizontal)))
3099 horizontal)))))
562dd5e9
MR
3100
3101(defun shrink-window (delta &optional horizontal)
2116e93c 3102 "Make the selected window DELTA lines smaller.
562dd5e9
MR
3103Interactively, if no argument is given, make the selected window
3104one line smaller. If optional argument HORIZONTAL is non-nil,
3105make selected window narrower by DELTA columns. If DELTA is
3106negative, enlarge selected window by -DELTA lines or columns.
0ff7851c 3107Also see the `window-min-height' variable."
562dd5e9 3108 (interactive "p")
feeb6f53
MR
3109 (let ((minibuffer-window (minibuffer-window)))
3110 (cond
3111 ((zerop delta))
3112 ((window-size-fixed-p nil horizontal)
3113 (error "Selected window has fixed size"))
3114 ((window-minibuffer-p)
3115 (if horizontal
3116 (error "Cannot resize minibuffer window horizontally")
3117 (window--resize-mini-window (selected-window) (- delta))))
3118 ((and (not horizontal)
3119 (window-full-height-p)
3120 (eq (window-frame minibuffer-window) (selected-frame))
3121 (not resize-mini-windows))
3122 ;; If the selected window is full height and `resize-mini-windows'
3123 ;; is nil, resize the minibuffer window.
3124 (window--resize-mini-window minibuffer-window delta))
880e6158 3125 ((window--resizable-p nil (- delta) horizontal)
feeb6f53
MR
3126 (window-resize nil (- delta) horizontal))
3127 (t
3128 (window-resize
3129 nil (if (> delta 0)
3130 (- (window-min-delta nil horizontal))
3131 (window-max-delta nil horizontal))
3132 horizontal)))))
562dd5e9 3133
2d6b6005 3134(defun maximize-window (&optional window)
562dd5e9
MR
3135 "Maximize WINDOW.
3136Make WINDOW as large as possible without deleting any windows.
880e6158
MR
3137WINDOW must be a valid window and defaults to the selected one.
3138
3139If the option `window-resize-pixelwise' is non-nil maximize
3140WINDOW pixelwise."
562dd5e9 3141 (interactive)
447f16b8 3142 (setq window (window-normalize-window window))
880e6158
MR
3143 (window-resize
3144 window (window-max-delta window nil nil nil nil nil window-resize-pixelwise)
3145 nil nil window-resize-pixelwise)
3146 (window-resize
3147 window (window-max-delta window t nil nil nil nil window-resize-pixelwise)
3148 t nil window-resize-pixelwise))
3149
2d6b6005 3150(defun minimize-window (&optional window)
562dd5e9
MR
3151 "Minimize WINDOW.
3152Make WINDOW as small as possible without deleting any windows.
880e6158
MR
3153WINDOW must be a valid window and defaults to the selected one.
3154
3155If the option `window-resize-pixelwise' is non-nil minimize
3156WINDOW pixelwise."
562dd5e9 3157 (interactive)
447f16b8 3158 (setq window (window-normalize-window window))
880e6158
MR
3159 (window-resize
3160 window
3161 (- (window-min-delta window nil nil nil nil nil window-resize-pixelwise))
3162 nil nil window-resize-pixelwise)
3163 (window-resize
3164 window
3165 (- (window-min-delta window t nil nil nil nil window-resize-pixelwise))
3166 t nil window-resize-pixelwise))
562dd5e9 3167\f
4b0d61e3 3168(defun frame-root-window-p (window)
9aab8e0d
MR
3169 "Return non-nil if WINDOW is the root window of its frame."
3170 (eq window (frame-root-window window)))
6198ccd0 3171
5e92ca23
MR
3172(defun window--subtree (window &optional next)
3173 "Return window subtree rooted at WINDOW.
3174Optional argument NEXT non-nil means include WINDOW's right
6198ccd0
MR
3175siblings in the return value.
3176
3177See the documentation of `window-tree' for a description of the
3178return value."
3179 (let (list)
3180 (while window
3181 (setq list
3182 (cons
3183 (cond
d68443dc 3184 ((window-top-child window)
6198ccd0 3185 (cons t (cons (window-edges window)
5e92ca23 3186 (window--subtree (window-top-child window) t))))
d68443dc 3187 ((window-left-child window)
6198ccd0 3188 (cons nil (cons (window-edges window)
5e92ca23 3189 (window--subtree (window-left-child window) t))))
6198ccd0
MR
3190 (t window))
3191 list))
d68443dc 3192 (setq window (when next (window-next-sibling window))))
6198ccd0
MR
3193 (nreverse list)))
3194
3195(defun window-tree (&optional frame)
3196 "Return the window tree of frame FRAME.
3197FRAME must be a live frame and defaults to the selected frame.
3198The return value is a list of the form (ROOT MINI), where ROOT
3199represents the window tree of the frame's root window, and MINI
3200is the frame's minibuffer window.
3201
3202If the root window is not split, ROOT is the root window itself.
3203Otherwise, ROOT is a list (DIR EDGES W1 W2 ...) where DIR is nil
3204for a horizontal split, and t for a vertical split. EDGES gives
be7f5545
MR
3205the combined size and position of the child windows in the split,
3206and the rest of the elements are the child windows in the split.
3207Each of the child windows may again be a window or a list
382c953b 3208representing a window split, and so on. EDGES is a list (LEFT
6198ccd0 3209TOP RIGHT BOTTOM) as returned by `window-edges'."
5386012d 3210 (setq frame (window-normalize-frame frame))
5e92ca23 3211 (window--subtree (frame-root-window frame) t))
9aab8e0d 3212\f
9397e56f
MR
3213(defun other-window (count &optional all-frames)
3214 "Select another window in cyclic ordering of windows.
3215COUNT specifies the number of windows to skip, starting with the
3216selected window, before making the selection. If COUNT is
3217positive, skip COUNT windows forwards. If COUNT is negative,
3218skip -COUNT windows backwards. COUNT zero means do not skip any
3219window, so select the selected window. In an interactive call,
3220COUNT is the numeric prefix argument. Return nil.
3221
9a9e9ef0
MR
3222If the `other-window' parameter of the selected window is a
3223function and `ignore-window-parameters' is nil, call that
3224function with the arguments COUNT and ALL-FRAMES.
9397e56f
MR
3225
3226This function does not select a window whose `no-other-window'
3227window parameter is non-nil.
3228
3229This function uses `next-window' for finding the window to
3230select. The argument ALL-FRAMES has the same meaning as in
3231`next-window', but the MINIBUF argument of `next-window' is
3232always effectively nil."
3233 (interactive "p")
3234 (let* ((window (selected-window))
3235 (function (and (not ignore-window-parameters)
3236 (window-parameter window 'other-window)))
3237 old-window old-count)
3238 (if (functionp function)
3239 (funcall function count all-frames)
3240 ;; `next-window' and `previous-window' may return a window we are
3241 ;; not allowed to select. Hence we need an exit strategy in case
3242 ;; all windows are non-selectable.
3243 (catch 'exit
3244 (while (> count 0)
3245 (setq window (next-window window nil all-frames))
3246 (cond
3247 ((eq window old-window)
3248 (when (= count old-count)
3249 ;; Keep out of infinite loops. When COUNT has not changed
3250 ;; since we last looked at `window' we're probably in one.
3251 (throw 'exit nil)))
3252 ((window-parameter window 'no-other-window)
3253 (unless old-window
3254 ;; The first non-selectable window `next-window' got us:
3255 ;; Remember it and the current value of COUNT.
3256 (setq old-window window)
3257 (setq old-count count)))
3258 (t
3259 (setq count (1- count)))))
3260 (while (< count 0)
3261 (setq window (previous-window window nil all-frames))
3262 (cond
3263 ((eq window old-window)
3264 (when (= count old-count)
3265 ;; Keep out of infinite loops. When COUNT has not changed
3266 ;; since we last looked at `window' we're probably in one.
3267 (throw 'exit nil)))
3268 ((window-parameter window 'no-other-window)
3269 (unless old-window
3270 ;; The first non-selectable window `previous-window' got
3271 ;; us: Remember it and the current value of COUNT.
3272 (setq old-window window)
3273 (setq old-count count)))
3274 (t
3275 (setq count (1+ count)))))
3276
3277 (select-window window)
3278 ;; Always return nil.
3279 nil))))
3280
387522b2
MR
3281;; This should probably return non-nil when the selected window is part
3282;; of an atomic window whose root is the frame's root window.
3283(defun one-window-p (&optional nomini all-frames)
3284 "Return non-nil if the selected window is the only window.
3285Optional arg NOMINI non-nil means don't count the minibuffer
3286even if it is active. Otherwise, the minibuffer is counted
3287when it is active.
3288
3289Optional argument ALL-FRAMES specifies the set of frames to
3290consider, see also `next-window'. ALL-FRAMES nil or omitted
3291means consider windows on the selected frame only, plus the
3292minibuffer window if specified by the NOMINI argument. If the
3293minibuffer counts, consider all windows on all frames that share
3294that minibuffer too. The remaining non-nil values of ALL-FRAMES
3295with a special meaning are:
3296
3297- t means consider all windows on all existing frames.
3298
3299- `visible' means consider all windows on all visible frames on
3300 the current terminal.
3301
3302- 0 (the number zero) means consider all windows on all visible
3303 and iconified frames on the current terminal.
3304
3305- A frame means consider all windows on that frame only.
3306
3307Anything else means consider all windows on the selected frame
3308and no others."
3309 (let ((base-window (selected-window)))
3310 (if (and nomini (eq base-window (minibuffer-window)))
3311 (setq base-window (next-window base-window)))
3312 (eq base-window
3313 (next-window base-window (if nomini 'arg) all-frames))))
3c448ab6 3314\f
9aab8e0d 3315;;; Deleting windows.
1b36ed6a 3316(defun window-deletable-p (&optional window)
9aab8e0d 3317 "Return t if WINDOW can be safely deleted from its frame.
85c2386b 3318WINDOW must be a valid window and defaults to the selected one.
c660a885 3319Return 'frame if deleting WINDOW should also delete its frame."
447f16b8 3320 (setq window (window-normalize-window window))
8b0874b5 3321
c660a885 3322 (unless (or ignore-window-parameters
f224e500 3323 (eq (window-parameter window 'delete-window) t))
9aab8e0d
MR
3324 ;; Handle atomicity.
3325 (when (window-parameter window 'window-atom)
3326 (setq window (window-atom-root window))))
8b0874b5 3327
caceae25 3328 (let ((frame (window-frame window)))
9aab8e0d
MR
3329 (cond
3330 ((frame-root-window-p window)
85e9c04b 3331 ;; WINDOW's frame can be deleted only if there are other frames
46b7967e
TN
3332 ;; on the same terminal, and it does not contain the active
3333 ;; minibuffer.
3334 (unless (or (eq frame (next-frame frame 0))
c660a885
MR
3335 ;; We can delete our frame only if no other frame
3336 ;; currently uses our minibuffer window.
3337 (catch 'other
3338 (dolist (other (frame-list))
3339 (when (and (not (eq other frame))
3340 (eq (window-frame (minibuffer-window other))
3341 frame))
3342 (throw 'other t))))
46b7967e
TN
3343 (let ((minibuf (active-minibuffer-window)))
3344 (and minibuf (eq frame (window-frame minibuf)))))
85e9c04b 3345 'frame))
1b36ed6a 3346 ((or ignore-window-parameters
caceae25
MR
3347 (not (eq window (window--major-non-side-window frame))))
3348 ;; WINDOW can be deleted unless it is the major non-side window of
3349 ;; its frame.
1f3c99ca 3350 t))))
9aab8e0d 3351
be7f5545
MR
3352(defun window--in-subtree-p (window root)
3353 "Return t if WINDOW is either ROOT or a member of ROOT's subtree."
3354 (or (eq window root)
3355 (let ((parent (window-parent window)))
9aab8e0d
MR
3356 (catch 'done
3357 (while parent
be7f5545 3358 (if (eq parent root)
9aab8e0d
MR
3359 (throw 'done t)
3360 (setq parent (window-parent parent))))))))
3361
562dd5e9
MR
3362(defun delete-window (&optional window)
3363 "Delete WINDOW.
85c2386b
MR
3364WINDOW must be a valid window and defaults to the selected one.
3365Return nil.
562dd5e9
MR
3366
3367If the variable `ignore-window-parameters' is non-nil or the
3368`delete-window' parameter of WINDOW equals t, do not process any
3369parameters of WINDOW. Otherwise, if the `delete-window'
3370parameter of WINDOW specifies a function, call that function with
3371WINDOW as its sole argument and return the value returned by that
3372function.
3373
3374Otherwise, if WINDOW is part of an atomic window, call
3375`delete-window' with the root of the atomic window as its
85c2386b
MR
3376argument. Signal an error if WINDOW is either the only window on
3377its frame, the last non-side window, or part of an atomic window
3378that is its frame's root window."
562dd5e9 3379 (interactive)
447f16b8 3380 (setq window (window-normalize-window window))
562dd5e9
MR
3381 (let* ((frame (window-frame window))
3382 (function (window-parameter window 'delete-window))
3383 (parent (window-parent window))
3384 atom-root)
54f9154c 3385 (window--check frame)
562dd5e9
MR
3386 (catch 'done
3387 ;; Handle window parameters.
3388 (cond
3389 ;; Ignore window parameters if `ignore-window-parameters' tells
3390 ;; us so or `delete-window' equals t.
3391 ((or ignore-window-parameters (eq function t)))
3392 ((functionp function)
3393 ;; The `delete-window' parameter specifies the function to call.
3394 ;; If that function is `ignore' nothing is done. It's up to the
3395 ;; function called here to avoid infinite recursion.
3396 (throw 'done (funcall function window)))
3397 ((and (window-parameter window 'window-atom)
3398 (setq atom-root (window-atom-root window))
3399 (not (eq atom-root window)))
caceae25
MR
3400 (if (eq atom-root (frame-root-window frame))
3401 (error "Root of atomic window is root window of its frame")
3402 (throw 'done (delete-window atom-root))))
562dd5e9 3403 ((not parent)
caceae25
MR
3404 (error "Attempt to delete minibuffer or sole ordinary window"))
3405 ((eq window (window--major-non-side-window frame))
3406 (error "Attempt to delete last non-side window")))
562dd5e9 3407
d68443dc 3408 (let* ((horizontal (window-left-child parent))
880e6158 3409 (size (window-size window horizontal t))
562dd5e9 3410 (frame-selected
be7f5545 3411 (window--in-subtree-p (frame-selected-window frame) window))
562dd5e9
MR
3412 ;; Emacs 23 preferably gives WINDOW's space to its left
3413 ;; sibling.
3414 (sibling (or (window-left window) (window-right window))))
5386012d 3415 (window--resize-reset frame horizontal)
562dd5e9 3416 (cond
a0c2d0ae 3417 ((and (not window-combination-resize)
880e6158 3418 sibling (window-sizable-p sibling size horizontal nil t))
562dd5e9 3419 ;; Resize WINDOW's sibling.
5386012d 3420 (window--resize-this-window sibling size horizontal nil t)
562dd5e9
MR
3421 (set-window-new-normal
3422 sibling (+ (window-normal-size sibling horizontal)
3423 (window-normal-size window horizontal))))
880e6158 3424 ((window--resizable-p window (- size) horizontal nil nil nil t t)
562dd5e9 3425 ;; Can do without resizing fixed-size windows.
5386012d 3426 (window--resize-siblings window (- size) horizontal))
562dd5e9
MR
3427 (t
3428 ;; Can't do without resizing fixed-size windows.
5386012d 3429 (window--resize-siblings window (- size) horizontal t)))
562dd5e9
MR
3430 ;; Actually delete WINDOW.
3431 (delete-window-internal window)
880e6158 3432 (window--pixel-to-total frame horizontal)
562dd5e9
MR
3433 (when (and frame-selected
3434 (window-parameter
3435 (frame-selected-window frame) 'no-other-window))
3436 ;; `delete-window-internal' has selected a window that should
3437 ;; not be selected, fix this here.
3438 (other-window -1 frame))
3439 (run-window-configuration-change-hook frame)
54f9154c 3440 (window--check frame)
562dd5e9
MR
3441 ;; Always return nil.
3442 nil))))
3443
3444(defun delete-other-windows (&optional window)
3445 "Make WINDOW fill its frame.
85c2386b 3446WINDOW must be a valid window and defaults to the selected one.
562dd5e9
MR
3447Return nil.
3448
3449If the variable `ignore-window-parameters' is non-nil or the
3450`delete-other-windows' parameter of WINDOW equals t, do not
3451process any parameters of WINDOW. Otherwise, if the
3452`delete-other-windows' parameter of WINDOW specifies a function,
3453call that function with WINDOW as its sole argument and return
3454the value returned by that function.
3455
3456Otherwise, if WINDOW is part of an atomic window, call this
3457function with the root of the atomic window as its argument. If
3458WINDOW is a non-side window, make WINDOW the only non-side window
382c953b 3459on the frame. Side windows are not deleted. If WINDOW is a side
562dd5e9
MR
3460window signal an error."
3461 (interactive)
447f16b8 3462 (setq window (window-normalize-window window))
562dd5e9
MR
3463 (let* ((frame (window-frame window))
3464 (function (window-parameter window 'delete-other-windows))
3465 (window-side (window-parameter window 'window-side))
3466 atom-root side-main)
54f9154c 3467 (window--check frame)
562dd5e9
MR
3468 (catch 'done
3469 (cond
3470 ;; Ignore window parameters if `ignore-window-parameters' is t or
3471 ;; `delete-other-windows' is t.
3472 ((or ignore-window-parameters (eq function t)))
3473 ((functionp function)
3474 ;; The `delete-other-windows' parameter specifies the function
3475 ;; to call. If the function is `ignore' no windows are deleted.
3476 ;; It's up to the function called to avoid infinite recursion.
3477 (throw 'done (funcall function window)))
3478 ((and (window-parameter window 'window-atom)
3479 (setq atom-root (window-atom-root window))
3480 (not (eq atom-root window)))
caceae25
MR
3481 (if (eq atom-root (frame-root-window frame))
3482 (error "Root of atomic window is root window of its frame")
3483 (throw 'done (delete-other-windows atom-root))))
562dd5e9 3484 ((memq window-side window-sides)
caceae25
MR
3485 (error "Cannot make side window the only window"))
3486 ((and (window-minibuffer-p window)
3487 (not (eq window (frame-root-window window))))
3488 (error "Can't expand minibuffer to full frame")))
3489
3490 ;; If WINDOW is the major non-side window, do nothing.
3491 (if (window-with-parameter 'window-side)
3492 (setq side-main (window--major-non-side-window frame))
3493 (setq side-main (frame-root-window frame)))
562dd5e9
MR
3494 (unless (eq window side-main)
3495 (delete-other-windows-internal window side-main)
3496 (run-window-configuration-change-hook frame)
54f9154c 3497 (window--check frame))
562dd5e9
MR
3498 ;; Always return nil.
3499 nil)))
9397e56f
MR
3500
3501(defun delete-other-windows-vertically (&optional window)
3502 "Delete the windows in the same column with WINDOW, but not WINDOW itself.
3503This may be a useful alternative binding for \\[delete-other-windows]
3504 if you often split windows horizontally."
3505 (interactive)
3506 (let* ((window (or window (selected-window)))
3507 (edges (window-edges window))
3508 (w window) delenda)
3509 (while (not (eq (setq w (next-window w 1)) window))
3510 (let ((e (window-edges w)))
3511 (when (and (= (car e) (car edges))
36cec983 3512 (= (nth 2 e) (nth 2 edges)))
9397e56f
MR
3513 (push w delenda))))
3514 (mapc 'delete-window delenda)))
3515
3516;;; Windows and buffers.
3517
3518;; `prev-buffers' and `next-buffers' are two reserved window slots used
3519;; for (1) determining which buffer to show in the window when its
3520;; buffer shall be buried or killed and (2) which buffer to show for
3521;; `switch-to-prev-buffer' and `switch-to-next-buffer'.
3522
3523;; `prev-buffers' consists of <buffer, window-start, window-point>
3524;; triples. The entries on this list are ordered by the time their
3525;; buffer has been removed from the window, the most recently removed
3526;; buffer's entry being first. The window-start and window-point
3527;; components are `window-start' and `window-point' at the time the
3528;; buffer was removed from the window which implies that the entry must
3529;; be added when `set-window-buffer' removes the buffer from the window.
3530
3531;; `next-buffers' is the list of buffers that have been replaced
3532;; recently by `switch-to-prev-buffer'. These buffers are the least
3533;; preferred candidates of `switch-to-prev-buffer' and the preferred
3534;; candidates of `switch-to-next-buffer' to switch to. This list is
3535;; reset to nil by any action changing the window's buffer with the
3536;; exception of `switch-to-prev-buffer' and `switch-to-next-buffer'.
3537;; `switch-to-prev-buffer' pushes the buffer it just replaced on it,
3538;; `switch-to-next-buffer' pops the last pushed buffer from it.
3539
3540;; Both `prev-buffers' and `next-buffers' may reference killed buffers
3541;; if such a buffer was killed while the window was hidden within a
3542;; window configuration. Such killed buffers get removed whenever
3543;; `switch-to-prev-buffer' or `switch-to-next-buffer' encounter them.
3544
3545;; The following function is called by `set-window-buffer' _before_ it
3546;; replaces the buffer of the argument window with the new buffer.
3547(defun record-window-buffer (&optional window)
3548 "Record WINDOW's buffer.
3549WINDOW must be a live window and defaults to the selected one."
447f16b8 3550 (let* ((window (window-normalize-window window t))
9397e56f
MR
3551 (buffer (window-buffer window))
3552 (entry (assq buffer (window-prev-buffers window))))
3553 ;; Reset WINDOW's next buffers. If needed, they are resurrected by
3554 ;; `switch-to-prev-buffer' and `switch-to-next-buffer'.
3555 (set-window-next-buffers window nil)
3556
3557 (when entry
3558 ;; Remove all entries for BUFFER from WINDOW's previous buffers.
3559 (set-window-prev-buffers
3560 window (assq-delete-all buffer (window-prev-buffers window))))
3561
3562 ;; Don't record insignificant buffers.
3563 (unless (eq (aref (buffer-name buffer) 0) ?\s)
3564 ;; Add an entry for buffer to WINDOW's previous buffers.
3565 (with-current-buffer buffer
3566 (let ((start (window-start window))
5481664a 3567 (point (window-point window)))
9397e56f
MR
3568 (setq entry
3569 (cons buffer
3570 (if entry
3571 ;; We have an entry, update marker positions.
3572 (list (set-marker (nth 1 entry) start)
3573 (set-marker (nth 2 entry) point))
3574 ;; Make new markers.
3575 (list (copy-marker start)
92346275
MR
3576 (copy-marker
3577 ;; Preserve window-point-insertion-type
3578 ;; (Bug#12588).
3579 point window-point-insertion-type)))))
9397e56f 3580 (set-window-prev-buffers
c660a885
MR
3581 window (cons entry (window-prev-buffers window)))))
3582
3583 (run-hooks 'buffer-list-update-hook))))
9397e56f
MR
3584
3585(defun unrecord-window-buffer (&optional window buffer)
3586 "Unrecord BUFFER in WINDOW.
3587WINDOW must be a live window and defaults to the selected one.
3588BUFFER must be a live buffer and defaults to the buffer of
3589WINDOW."
447f16b8 3590 (let* ((window (window-normalize-window window t))
9397e56f
MR
3591 (buffer (or buffer (window-buffer window))))
3592 (set-window-prev-buffers
3593 window (assq-delete-all buffer (window-prev-buffers window)))
3594 (set-window-next-buffers
3595 window (delq buffer (window-next-buffers window)))))
3596
3597(defun set-window-buffer-start-and-point (window buffer &optional start point)
3598 "Set WINDOW's buffer to BUFFER.
85c2386b 3599WINDOW must be a live window and defaults to the selected one.
9397e56f
MR
3600Optional argument START non-nil means set WINDOW's start position
3601to START. Optional argument POINT non-nil means set WINDOW's
3602point to POINT. If WINDOW is selected this also sets BUFFER's
3603`point' to POINT. If WINDOW is selected and the buffer it showed
3604before was current this also makes BUFFER the current buffer."
85c2386b 3605 (setq window (window-normalize-window window t))
9397e56f
MR
3606 (let ((selected (eq window (selected-window)))
3607 (current (eq (window-buffer window) (current-buffer))))
3608 (set-window-buffer window buffer)
3609 (when (and selected current)
3610 (set-buffer buffer))
3611 (when start
cf4eacfd
MR
3612 ;; Don't force window-start here (even if POINT is nil).
3613 (set-window-start window start t))
9397e56f 3614 (when point
5481664a 3615 (set-window-point window point))))
9397e56f 3616
dcb6e7b3
MR
3617(defcustom switch-to-visible-buffer t
3618 "If non-nil, allow switching to an already visible buffer.
3619If this variable is non-nil, `switch-to-prev-buffer' and
3620`switch-to-next-buffer' may switch to an already visible buffer
43bcfda6
MR
3621provided the buffer was shown before in the window specified as
3622argument to those functions. If this variable is nil,
3623`switch-to-prev-buffer' and `switch-to-next-buffer' always try to
3624avoid switching to a buffer that is already visible in another
3625window on the same frame."
dcb6e7b3
MR
3626 :type 'boolean
3627 :version "24.1"
3628 :group 'windows)
3629
9397e56f
MR
3630(defun switch-to-prev-buffer (&optional window bury-or-kill)
3631 "In WINDOW switch to previous buffer.
3632WINDOW must be a live window and defaults to the selected one.
502e3f89
MR
3633Return the buffer switched to, nil if no suitable buffer could be
3634found.
9397e56f
MR
3635
3636Optional argument BURY-OR-KILL non-nil means the buffer currently
3637shown in WINDOW is about to be buried or killed and consequently
78dd6ab1
MR
3638shall not be switched to in future invocations of this command.
3639
3640As a special case, if BURY-OR-KILL equals `append', this means to
3641move the buffer to the end of WINDOW's previous buffers list so a
3642future invocation of `switch-to-prev-buffer' less likely switches
3643to it."
9397e56f 3644 (interactive)
447f16b8 3645 (let* ((window (window-normalize-window window t))
dcb6e7b3 3646 (frame (window-frame window))
9397e56f
MR
3647 (old-buffer (window-buffer window))
3648 ;; Save this since it's destroyed by `set-window-buffer'.
3649 (next-buffers (window-next-buffers window))
862382df 3650 (pred (frame-parameter frame 'buffer-predicate))
78dd6ab1
MR
3651 entry new-buffer killed-buffers visible)
3652 (when (window-minibuffer-p window)
3653 ;; Don't switch in minibuffer window.
3654 (unless (setq window (minibuffer-selected-window))
3655 (error "Window %s is a minibuffer window" window)))
3656
1b36ed6a 3657 (when (window-dedicated-p window)
78dd6ab1 3658 ;; Don't switch in dedicated window.
1b36ed6a
MR
3659 (error "Window %s is dedicated to buffer %s" window old-buffer))
3660
3661 (catch 'found
3662 ;; Scan WINDOW's previous buffers first, skipping entries of next
3663 ;; buffers.
3664 (dolist (entry (window-prev-buffers window))
78dd6ab1
MR
3665 (when (and (setq new-buffer (car entry))
3666 (or (buffer-live-p new-buffer)
1b36ed6a 3667 (not (setq killed-buffers
78dd6ab1
MR
3668 (cons new-buffer killed-buffers))))
3669 (not (eq new-buffer old-buffer))
3670 (or (null pred) (funcall pred new-buffer))
3671 ;; When BURY-OR-KILL is nil, avoid switching to a
3672 ;; buffer in WINDOW's next buffers list.
3673 (or bury-or-kill (not (memq new-buffer next-buffers))))
dcb6e7b3 3674 (if (and (not switch-to-visible-buffer)
78dd6ab1
MR
3675 (get-buffer-window new-buffer frame))
3676 ;; Try to avoid showing a buffer visible in some other
3677 ;; window.
3678 (setq visible new-buffer)
502e3f89
MR
3679 (set-window-buffer-start-and-point
3680 window new-buffer (nth 1 entry) (nth 2 entry))
3681 (throw 'found t))))
1b36ed6a
MR
3682 ;; Scan reverted buffer list of WINDOW's frame next, skipping
3683 ;; entries of next buffers. Note that when we bury or kill a
3684 ;; buffer we don't reverse the global buffer list to avoid showing
3685 ;; a buried buffer instead. Otherwise, we must reverse the global
3686 ;; buffer list in order to make sure that switching to the
3687 ;; previous/next buffer traverse it in opposite directions.
3688 (dolist (buffer (if bury-or-kill
dcb6e7b3
MR
3689 (buffer-list frame)
3690 (nreverse (buffer-list frame))))
1b36ed6a
MR
3691 (when (and (buffer-live-p buffer)
3692 (not (eq buffer old-buffer))
862382df 3693 (or (null pred) (funcall pred buffer))
78dd6ab1 3694 (not (eq (aref (buffer-name buffer) 0) ?\s))
1b36ed6a 3695 (or bury-or-kill (not (memq buffer next-buffers))))
dcb6e7b3 3696 (if (get-buffer-window buffer frame)
1b36ed6a 3697 ;; Try to avoid showing a buffer visible in some other window.
dcb6e7b3
MR
3698 (unless visible
3699 (setq visible buffer))
1b36ed6a
MR
3700 (setq new-buffer buffer)
3701 (set-window-buffer-start-and-point window new-buffer)
3702 (throw 'found t))))
3703 (unless bury-or-kill
3704 ;; Scan reverted next buffers last (must not use nreverse
3705 ;; here!).
3706 (dolist (buffer (reverse next-buffers))
3707 ;; Actually, buffer _must_ be live here since otherwise it
3708 ;; would have been caught in the scan of previous buffers.
3709 (when (and (or (buffer-live-p buffer)
9397e56f 3710 (not (setq killed-buffers
1b36ed6a 3711 (cons buffer killed-buffers))))
9397e56f 3712 (not (eq buffer old-buffer))
862382df 3713 (or (null pred) (funcall pred buffer))
1b36ed6a 3714 (setq entry (assq buffer (window-prev-buffers window))))
9397e56f 3715 (setq new-buffer buffer)
1b36ed6a
MR
3716 (set-window-buffer-start-and-point
3717 window new-buffer (nth 1 entry) (nth 2 entry))
9397e56f 3718 (throw 'found t))))
1b36ed6a
MR
3719
3720 ;; Show a buffer visible in another window.
3721 (when visible
3722 (setq new-buffer visible)
3723 (set-window-buffer-start-and-point window new-buffer)))
3724
3725 (if bury-or-kill
78dd6ab1
MR
3726 (let ((entry (and (eq bury-or-kill 'append)
3727 (assq old-buffer (window-prev-buffers window)))))
3728 ;; Remove `old-buffer' from WINDOW's previous and (restored list
3729 ;; of) next buffers.
1b36ed6a
MR
3730 (set-window-prev-buffers
3731 window (assq-delete-all old-buffer (window-prev-buffers window)))
78dd6ab1
MR
3732 (set-window-next-buffers window (delq old-buffer next-buffers))
3733 (when entry
3734 ;; Append old-buffer's entry to list of WINDOW's previous
3735 ;; buffers so it's less likely to get switched to soon but
3736 ;; `display-buffer-in-previous-window' can nevertheless find
3737 ;; it.
3738 (set-window-prev-buffers
3739 window (append (window-prev-buffers window) (list entry)))))
1b36ed6a
MR
3740 ;; Move `old-buffer' to head of WINDOW's restored list of next
3741 ;; buffers.
3742 (set-window-next-buffers
3743 window (cons old-buffer (delq old-buffer next-buffers))))
9397e56f
MR
3744
3745 ;; Remove killed buffers from WINDOW's previous and next buffers.
3746 (when killed-buffers
3747 (dolist (buffer killed-buffers)
3748 (set-window-prev-buffers
3749 window (assq-delete-all buffer (window-prev-buffers window)))
3750 (set-window-next-buffers
3751 window (delq buffer (window-next-buffers window)))))
3752
3753 ;; Return new-buffer.
3754 new-buffer))
3755
3756(defun switch-to-next-buffer (&optional window)
3757 "In WINDOW switch to next buffer.
502e3f89
MR
3758WINDOW must be a live window and defaults to the selected one.
3759Return the buffer switched to, nil if no suitable buffer could be
3760found."
9397e56f 3761 (interactive)
447f16b8 3762 (let* ((window (window-normalize-window window t))
dcb6e7b3 3763 (frame (window-frame window))
9397e56f
MR
3764 (old-buffer (window-buffer window))
3765 (next-buffers (window-next-buffers window))
862382df 3766 (pred (frame-parameter frame 'buffer-predicate))
78dd6ab1
MR
3767 new-buffer entry killed-buffers visible)
3768 (when (window-minibuffer-p window)
3769 ;; Don't switch in minibuffer window.
3770 (unless (setq window (minibuffer-selected-window))
3771 (error "Window %s is a minibuffer window" window)))
3772
9397e56f 3773 (when (window-dedicated-p window)
78dd6ab1 3774 ;; Don't switch in dedicated window.
9397e56f
MR
3775 (error "Window %s is dedicated to buffer %s" window old-buffer))
3776
3777 (catch 'found
3778 ;; Scan WINDOW's next buffers first.
3779 (dolist (buffer next-buffers)
3780 (when (and (or (buffer-live-p buffer)
3781 (not (setq killed-buffers
3782 (cons buffer killed-buffers))))
3783 (not (eq buffer old-buffer))
862382df 3784 (or (null pred) (funcall pred buffer))
9397e56f
MR
3785 (setq entry (assq buffer (window-prev-buffers window))))
3786 (setq new-buffer buffer)
3787 (set-window-buffer-start-and-point
3788 window new-buffer (nth 1 entry) (nth 2 entry))
3789 (throw 'found t)))
3790 ;; Scan the buffer list of WINDOW's frame next, skipping previous
3791 ;; buffers entries.
dcb6e7b3 3792 (dolist (buffer (buffer-list frame))
78dd6ab1
MR
3793 (when (and (buffer-live-p buffer)
3794 (not (eq buffer old-buffer))
862382df 3795 (or (null pred) (funcall pred buffer))
78dd6ab1 3796 (not (eq (aref (buffer-name buffer) 0) ?\s))
9397e56f 3797 (not (assq buffer (window-prev-buffers window))))
dcb6e7b3 3798 (if (get-buffer-window buffer frame)
9397e56f
MR
3799 ;; Try to avoid showing a buffer visible in some other window.
3800 (setq visible buffer)
3801 (setq new-buffer buffer)
3802 (set-window-buffer-start-and-point window new-buffer)
3803 (throw 'found t))))
3804 ;; Scan WINDOW's reverted previous buffers last (must not use
3805 ;; nreverse here!)
3806 (dolist (entry (reverse (window-prev-buffers window)))
78dd6ab1
MR
3807 (when (and (setq new-buffer (car entry))
3808 (or (buffer-live-p new-buffer)
9397e56f 3809 (not (setq killed-buffers
78dd6ab1
MR
3810 (cons new-buffer killed-buffers))))
3811 (not (eq new-buffer old-buffer))
3812 (or (null pred) (funcall pred new-buffer)))
dcb6e7b3 3813 (if (and (not switch-to-visible-buffer)
78dd6ab1 3814 (get-buffer-window new-buffer frame))
dcb6e7b3
MR
3815 ;; Try to avoid showing a buffer visible in some other window.
3816 (unless visible
78dd6ab1 3817 (setq visible new-buffer))
dcb6e7b3
MR
3818 (set-window-buffer-start-and-point
3819 window new-buffer (nth 1 entry) (nth 2 entry))
3820 (throw 'found t))))
9397e56f
MR
3821
3822 ;; Show a buffer visible in another window.
3823 (when visible
3824 (setq new-buffer visible)
3825 (set-window-buffer-start-and-point window new-buffer)))
3826
3827 ;; Remove `new-buffer' from and restore WINDOW's next buffers.
3828 (set-window-next-buffers window (delq new-buffer next-buffers))
3829
3830 ;; Remove killed buffers from WINDOW's previous and next buffers.
3831 (when killed-buffers
3832 (dolist (buffer killed-buffers)
3833 (set-window-prev-buffers
3834 window (assq-delete-all buffer (window-prev-buffers window)))
3835 (set-window-next-buffers
3836 window (delq buffer (window-next-buffers window)))))
3837
3838 ;; Return new-buffer.
3839 new-buffer))
3840
3841(defun get-next-valid-buffer (list &optional buffer visible-ok frame)
3842 "Search LIST for a valid buffer to display in FRAME.
3843Return nil when all buffers in LIST are undesirable for display,
3844otherwise return the first suitable buffer in LIST.
3845
3846Buffers not visible in windows are preferred to visible buffers,
3847unless VISIBLE-OK is non-nil.
3848If the optional argument FRAME is nil, it defaults to the selected frame.
3849If BUFFER is non-nil, ignore occurrences of that buffer in LIST."
3850 ;; This logic is more or less copied from other-buffer.
3851 (setq frame (or frame (selected-frame)))
3852 (let ((pred (frame-parameter frame 'buffer-predicate))
3853 found buf)
3854 (while (and (not found) list)
3855 (setq buf (car list))
3856 (if (and (not (eq buffer buf))
3857 (buffer-live-p buf)
3858 (or (null pred) (funcall pred buf))
3859 (not (eq (aref (buffer-name buf) 0) ?\s))
3860 (or visible-ok (null (get-buffer-window buf 'visible))))
3861 (setq found buf)
3862 (setq list (cdr list))))
3863 (car list)))
3864
3865(defun last-buffer (&optional buffer visible-ok frame)
3866 "Return the last buffer in FRAME's buffer list.
3867If BUFFER is the last buffer, return the preceding buffer
3868instead. Buffers not visible in windows are preferred to visible
3869buffers, unless optional argument VISIBLE-OK is non-nil.
3870Optional third argument FRAME nil or omitted means use the
3871selected frame's buffer list. If no such buffer exists, return
3872the buffer `*scratch*', creating it if necessary."
3873 (setq frame (or frame (selected-frame)))
3874 (or (get-next-valid-buffer (nreverse (buffer-list frame))
3875 buffer visible-ok frame)
3876 (get-buffer "*scratch*")
3877 (let ((scratch (get-buffer-create "*scratch*")))
3878 (set-buffer-major-mode scratch)
3879 scratch)))
3880
5a4cf282
MR
3881(defcustom frame-auto-hide-function #'iconify-frame
3882 "Function called to automatically hide frames.
3883The function is called with one argument - a frame.
3884
3885Functions affected by this option are those that bury a buffer
3886shown in a separate frame like `quit-window' and `bury-buffer'."
3887 :type '(choice (const :tag "Iconify" iconify-frame)
3888 (const :tag "Delete" delete-frame)
3889 (const :tag "Do nothing" ignore)
3890 function)
3891 :group 'windows
49677495
MR
3892 :group 'frames
3893 :version "24.1")
0e2070b5
MR
3894
3895(defun window--delete (&optional window dedicated-only kill)
3896 "Delete WINDOW if possible.
3897WINDOW must be a live window and defaults to the selected one.
3898Optional argument DEDICATED-ONLY non-nil means to delete WINDOW
3899only if it's dedicated to its buffer. Optional argument KILL
3900means the buffer shown in window will be killed. Return non-nil
c557cd6b 3901if WINDOW gets deleted or its frame is auto-hidden."
447f16b8 3902 (setq window (window-normalize-window window t))
0e2070b5 3903 (unless (and dedicated-only (not (window-dedicated-p window)))
cb882333 3904 (let ((deletable (window-deletable-p window)))
0e2070b5
MR
3905 (cond
3906 ((eq deletable 'frame)
3907 (let ((frame (window-frame window)))
c557cd6b
MR
3908 (cond
3909 (kill
3910 (delete-frame frame))
3911 ((functionp frame-auto-hide-function)
3912 (funcall frame-auto-hide-function frame))))
0e2070b5
MR
3913 'frame)
3914 (deletable
3915 (delete-window window)
3916 t)))))
3917
9397e56f
MR
3918(defun bury-buffer (&optional buffer-or-name)
3919 "Put BUFFER-OR-NAME at the end of the list of all buffers.
3920There it is the least likely candidate for `other-buffer' to
3921return; thus, the least likely buffer for \\[switch-to-buffer] to
3922select by default.
3923
3924You can specify a buffer name as BUFFER-OR-NAME, or an actual
3925buffer object. If BUFFER-OR-NAME is nil or omitted, bury the
3926current buffer. Also, if BUFFER-OR-NAME is nil or omitted,
3927remove the current buffer from the selected window if it is
3928displayed there."
3929 (interactive)
5386012d 3930 (let* ((buffer (window-normalize-buffer buffer-or-name)))
9397e56f
MR
3931 ;; If `buffer-or-name' is not on the selected frame we unrecord it
3932 ;; although it's not "here" (call it a feature).
e4ed06f1 3933 (bury-buffer-internal buffer)
9397e56f
MR
3934 ;; Handle case where `buffer-or-name' is nil and the current buffer
3935 ;; is shown in the selected window.
3936 (cond
3937 ((or buffer-or-name (not (eq buffer (window-buffer)))))
0e2070b5
MR
3938 ((window--delete nil t))
3939 (t
3940 ;; Switch to another buffer in window.
3941 (set-window-dedicated-p nil nil)
1885e5b8 3942 (switch-to-prev-buffer nil 'bury)))
1b36ed6a 3943
9397e56f
MR
3944 ;; Always return nil.
3945 nil))
3946
3947(defun unbury-buffer ()
3948 "Switch to the last buffer in the buffer list."
3949 (interactive)
3950 (switch-to-buffer (last-buffer)))
3951
3952(defun next-buffer ()
3953 "In selected window switch to next buffer."
3954 (interactive)
85c2386b
MR
3955 (cond
3956 ((window-minibuffer-p)
3957 (error "Cannot switch buffers in minibuffer window"))
3958 ((eq (window-dedicated-p) t)
3959 (error "Window is strongly dedicated to its buffer"))
3960 (t
3961 (switch-to-next-buffer))))
9397e56f
MR
3962
3963(defun previous-buffer ()
3964 "In selected window switch to previous buffer."
3965 (interactive)
85c2386b
MR
3966 (cond
3967 ((window-minibuffer-p)
3968 (error "Cannot switch buffers in minibuffer window"))
3969 ((eq (window-dedicated-p) t)
3970 (error "Window is strongly dedicated to its buffer"))
3971 (t
3972 (switch-to-prev-buffer))))
9397e56f
MR
3973
3974(defun delete-windows-on (&optional buffer-or-name frame)
3975 "Delete all windows showing BUFFER-OR-NAME.
3976BUFFER-OR-NAME may be a buffer or the name of an existing buffer
3977and defaults to the current buffer.
3978
3979The following non-nil values of the optional argument FRAME
3980have special meanings:
3981
3982- t means consider all windows on the selected frame only.
3983
3984- `visible' means consider all windows on all visible frames on
3985 the current terminal.
3986
3987- 0 (the number zero) means consider all windows on all visible
3988 and iconified frames on the current terminal.
3989
3990- A frame means consider all windows on that frame only.
3991
3992Any other value of FRAME means consider all windows on all
3993frames.
3994
3995When a window showing BUFFER-OR-NAME is dedicated and the only
3996window of its frame, that frame is deleted when there are other
3997frames left."
3998 (interactive "BDelete windows on (buffer):\nP")
5386012d 3999 (let ((buffer (window-normalize-buffer buffer-or-name))
9397e56f
MR
4000 ;; Handle the "inverted" meaning of the FRAME argument wrt other
4001 ;; `window-list-1' based function.
4002 (all-frames (cond ((not frame) t) ((eq frame t) nil) (t frame))))
4003 (dolist (window (window-list-1 nil nil all-frames))
4004 (if (eq (window-buffer window) buffer)
1b36ed6a 4005 (let ((deletable (window-deletable-p window)))
9397e56f 4006 (cond
1b36ed6a
MR
4007 ((and (eq deletable 'frame) (window-dedicated-p window))
4008 ;; Delete frame if and only if window is dedicated.
9397e56f 4009 (delete-frame (window-frame window)))
1b36ed6a
MR
4010 ((eq deletable t)
4011 ;; Delete window.
9397e56f
MR
4012 (delete-window window))
4013 (t
4014 ;; In window switch to previous buffer.
4015 (set-window-dedicated-p window nil)
4016 (switch-to-prev-buffer window 'bury))))
4017 ;; If a window doesn't show BUFFER, unrecord BUFFER in it.
4018 (unrecord-window-buffer window buffer)))))
4019
4020(defun replace-buffer-in-windows (&optional buffer-or-name)
4021 "Replace BUFFER-OR-NAME with some other buffer in all windows showing it.
4022BUFFER-OR-NAME may be a buffer or the name of an existing buffer
4023and defaults to the current buffer.
4024
0e2070b5
MR
4025When a window showing BUFFER-OR-NAME is dedicated, that window is
4026deleted. If that window is the only window on its frame, the
4027frame is deleted too when there are other frames left. If there
4028are no other frames left, some other buffer is displayed in that
4029window.
9397e56f
MR
4030
4031This function removes the buffer denoted by BUFFER-OR-NAME from
4032all window-local buffer lists."
24901d61 4033 (interactive "bBuffer to replace: ")
5386012d 4034 (let ((buffer (window-normalize-buffer buffer-or-name)))
9397e56f
MR
4035 (dolist (window (window-list-1 nil nil t))
4036 (if (eq (window-buffer window) buffer)
0e2070b5
MR
4037 (unless (window--delete window t t)
4038 ;; Switch to another buffer in window.
4039 (set-window-dedicated-p window nil)
4040 (switch-to-prev-buffer window 'kill))
9397e56f
MR
4041 ;; Unrecord BUFFER in WINDOW.
4042 (unrecord-window-buffer window buffer)))))
4043
78dd6ab1
MR
4044(defun quit-restore-window (&optional window bury-or-kill)
4045 "Quit WINDOW and deal with its buffer.
cf4eacfd 4046WINDOW must be a live window and defaults to the selected one.
9397e56f
MR
4047
4048According to information stored in WINDOW's `quit-restore' window
382c953b
JB
4049parameter either (1) delete WINDOW and its frame, (2) delete
4050WINDOW, (3) restore the buffer previously displayed in WINDOW,
4051or (4) make WINDOW display some other buffer than the present
78dd6ab1
MR
4052one. If non-nil, reset `quit-restore' parameter to nil.
4053
4054Optional second argument BURY-OR-KILL tells how to proceed with
4055the buffer of WINDOW. The following values are handled:
4056
4057`nil' means to not handle the buffer in a particular way. This
4058 means that if WINDOW is not deleted by this function, invoking
4059 `switch-to-prev-buffer' will usually show the buffer again.
4060
4061`append' means that if WINDOW is not deleted, move its buffer to
4062 the end of WINDOW's previous buffers so it's less likely that a
4063 future invocation of `switch-to-prev-buffer' will switch to it.
4064 Also, move the buffer to the end of the frame's buffer list.
4065
4066`bury' means that if WINDOW is not deleted, remove its buffer
4067 from WINDOW'S list of previous buffers. Also, move the buffer
4068 to the end of the frame's buffer list. This value provides the
4069 most reliable remedy to not have `switch-to-prev-buffer' switch
4070 to this buffer again without killing the buffer.
4071
4072`kill' means to kill WINDOW's buffer."
447f16b8 4073 (setq window (window-normalize-window window t))
cf4eacfd
MR
4074 (let* ((buffer (window-buffer window))
4075 (quit-restore (window-parameter window 'quit-restore))
4076 (prev-buffer
4077 (let* ((prev-buffers (window-prev-buffers window))
4078 (prev-buffer (caar prev-buffers)))
4079 (and (or (not (eq prev-buffer buffer))
4080 (and (cdr prev-buffers)
4081 (not (eq (setq prev-buffer (cadr prev-buffers))
4082 buffer))))
4083 prev-buffer)))
78dd6ab1 4084 quad entry)
9397e56f 4085 (cond
cf4eacfd 4086 ((and (not prev-buffer)
218e997a
MR
4087 (or (eq (nth 1 quit-restore) 'frame)
4088 (and (eq (nth 1 quit-restore) 'window)
4089 ;; If the window has been created on an existing
4090 ;; frame and ended up as the sole window on that
4091 ;; frame, do not delete it (Bug#12764).
4092 (not (eq window (frame-root-window window)))))
0e2070b5
MR
4093 (eq (nth 3 quit-restore) buffer)
4094 ;; Delete WINDOW if possible.
78dd6ab1 4095 (window--delete window nil (eq bury-or-kill 'kill)))
9397e56f
MR
4096 ;; If the previously selected window is still alive, select it.
4097 (when (window-live-p (nth 2 quit-restore))
4098 (select-window (nth 2 quit-restore))))
cf4eacfd
MR
4099 ((and (listp (setq quad (nth 1 quit-restore)))
4100 (buffer-live-p (car quad))
4101 (eq (nth 3 quit-restore) buffer))
4102 ;; Show another buffer stored in quit-restore parameter.
78dd6ab1 4103 (when (and (integerp (nth 3 quad))
880e6158 4104 (/= (nth 3 quad) (window-total-height window)))
cf4eacfd
MR
4105 ;; Try to resize WINDOW to its old height but don't signal an
4106 ;; error.
4107 (condition-case nil
880e6158 4108 (window-resize window (- (nth 3 quad) (window-total-height window)))
cf4eacfd 4109 (error nil)))
78dd6ab1 4110 (set-window-dedicated-p window nil)
1885e5b8 4111 ;; Restore WINDOW's previous buffer, start and point position.
cf4eacfd
MR
4112 (set-window-buffer-start-and-point
4113 window (nth 0 quad) (nth 1 quad) (nth 2 quad))
78dd6ab1
MR
4114 ;; Deal with the buffer we just removed from WINDOW.
4115 (setq entry (and (eq bury-or-kill 'append)
4116 (assq buffer (window-prev-buffers window))))
4117 (when bury-or-kill
4118 ;; Remove buffer from WINDOW's previous and next buffers.
4119 (set-window-prev-buffers
4120 window (assq-delete-all buffer (window-prev-buffers window)))
4121 (set-window-next-buffers
4122 window (delq buffer (window-next-buffers window))))
4123 (when entry
4124 ;; Append old buffer's entry to list of WINDOW's previous
4125 ;; buffers so it's less likely to get switched to soon but
4126 ;; `display-buffer-in-previous-window' can nevertheless find it.
4127 (set-window-prev-buffers
4128 window (append (window-prev-buffers window) (list entry))))
9397e56f
MR
4129 ;; Reset the quit-restore parameter.
4130 (set-window-parameter window 'quit-restore nil)
cf4eacfd
MR
4131 ;; Select old window.
4132 (when (window-live-p (nth 2 quit-restore))
4133 (select-window (nth 2 quit-restore))))
9397e56f 4134 (t
cf4eacfd
MR
4135 ;; Show some other buffer in WINDOW and reset the quit-restore
4136 ;; parameter.
9397e56f 4137 (set-window-parameter window 'quit-restore nil)
b4d72fcf
MR
4138 ;; Make sure that WINDOW is no more dedicated.
4139 (set-window-dedicated-p window nil)
78dd6ab1
MR
4140 (switch-to-prev-buffer window bury-or-kill)))
4141
4142 ;; Deal with the buffer.
4143 (cond
4144 ((not (buffer-live-p buffer)))
4145 ((eq bury-or-kill 'kill)
4146 (kill-buffer buffer))
4147 (bury-or-kill
4148 (bury-buffer-internal buffer)))))
9397e56f 4149
78dd6ab1
MR
4150(defun quit-window (&optional kill window)
4151 "Quit WINDOW and bury its buffer.
4152WINDOW must be a live window and defaults to the selected one.
4153With prefix argument KILL non-nil, kill the buffer instead of
4154burying it.
4155
4156According to information stored in WINDOW's `quit-restore' window
4157parameter either (1) delete WINDOW and its frame, (2) delete
4158WINDOW, (3) restore the buffer previously displayed in WINDOW,
4159or (4) make WINDOW display some other buffer than the present
4160one. If non-nil, reset `quit-restore' parameter to nil."
4161 (interactive "P")
4162 (quit-restore-window window (if kill 'kill 'bury)))
366ca7f3
MR
4163
4164(defun quit-windows-on (&optional buffer-or-name kill frame)
4165 "Quit all windows showing BUFFER-OR-NAME.
4166BUFFER-OR-NAME may be a buffer or the name of an existing buffer
4167and defaults to the current buffer. Optional argument KILL
4168non-nil means to kill BUFFER-OR-NAME. KILL nil means to bury
4169BUFFER-OR-NAME. Optional argument FRAME is handled as by
4170`delete-windows-on'.
4171
4172This function calls `quit-window' on all candidate windows
4173showing BUFFER-OR-NAME."
4174 (interactive "BQuit windows on (buffer):\nP")
4175 (let ((buffer (window-normalize-buffer buffer-or-name))
4176 ;; Handle the "inverted" meaning of the FRAME argument wrt other
4177 ;; `window-list-1' based function.
4178 (all-frames (cond ((not frame) t) ((eq frame t) nil) (t frame))))
4179 (dolist (window (window-list-1 nil nil all-frames))
4180 (if (eq (window-buffer window) buffer)
4181 (quit-window kill window)
4182 ;; If a window doesn't show BUFFER, unrecord BUFFER in it.
4183 (unrecord-window-buffer window buffer)))))
562dd5e9
MR
4184\f
4185;;; Splitting windows.
880e6158 4186(defun window-split-min-size (&optional horizontal pixelwise)
562dd5e9
MR
4187 "Return minimum height of any window when splitting windows.
4188Optional argument HORIZONTAL non-nil means return minimum width."
880e6158
MR
4189 (cond
4190 (pixelwise
4191 (if horizontal
4192 (window-min-pixel-width)
4193 (window-min-pixel-height)))
4194 (horizontal
4195 (max window-min-width window-safe-min-width))
4196 (t
4197 (max window-min-height window-safe-min-height))))
562dd5e9 4198
880e6158 4199(defun split-window (&optional window size side pixelwise)
562dd5e9 4200 "Make a new window adjacent to WINDOW.
85c2386b 4201WINDOW must be a valid window and defaults to the selected one.
562dd5e9
MR
4202Return the new window which is always a live window.
4203
4204Optional argument SIZE a positive number means make WINDOW SIZE
4205lines or columns tall. If SIZE is negative, make the new window
4206-SIZE lines or columns tall. If and only if SIZE is non-nil, its
4207absolute value can be less than `window-min-height' or
4208`window-min-width'; so this command can make a new window as
4209small as one line or two columns. SIZE defaults to half of
0aa3616e 4210WINDOW's size.
562dd5e9
MR
4211
4212Optional third argument SIDE nil (or `below') specifies that the
4213new window shall be located below WINDOW. SIDE `above' means the
4214new window shall be located above WINDOW. In both cases SIZE
382c953b 4215specifies the new number of lines for WINDOW (or the new window
562dd5e9
MR
4216if SIZE is negative) including space reserved for the mode and/or
4217header line.
4218
4219SIDE t (or `right') specifies that the new window shall be
4220located on the right side of WINDOW. SIDE `left' means the new
4221window shall be located on the left of WINDOW. In both cases
382c953b 4222SIZE specifies the new number of columns for WINDOW (or the new
562dd5e9
MR
4223window provided SIZE is negative) including space reserved for
4224fringes and the scrollbar or a divider column. Any other non-nil
4225value for SIDE is currently handled like t (or `right').
4226
f224e500 4227PIXELWISE, if non-nil, means to interpret SIZE pixelwise.
880e6158 4228
562dd5e9
MR
4229If the variable `ignore-window-parameters' is non-nil or the
4230`split-window' parameter of WINDOW equals t, do not process any
4231parameters of WINDOW. Otherwise, if the `split-window' parameter
4232of WINDOW specifies a function, call that function with all three
4233arguments and return the value returned by that function.
4234
4235Otherwise, if WINDOW is part of an atomic window, \"split\" the
4236root of that atomic window. The new window does not become a
4237member of that atomic window.
4238
4239If WINDOW is live, properties of the new window like margins and
4240scrollbars are inherited from WINDOW. If WINDOW is an internal
4241window, these properties as well as the buffer displayed in the
4242new window are inherited from the window selected on WINDOW's
4243frame. The selected window is not changed by this function."
447f16b8 4244 (setq window (window-normalize-window window))
130e3e11
MR
4245 (let* ((side (cond
4246 ((not side) 'below)
4247 ((memq side '(below above right left)) side)
4248 (t 'right)))
caceae25 4249 (horizontal (not (memq side '(below above))))
562dd5e9
MR
4250 (frame (window-frame window))
4251 (parent (window-parent window))
4252 (function (window-parameter window 'split-window))
4253 (window-side (window-parameter window 'window-side))
8e17c9ba
MR
4254 ;; Rebind the following two variables since in some cases we
4255 ;; have to override their value.
b6f67890 4256 (window-combination-limit window-combination-limit)
caceae25 4257 (window-combination-resize window-combination-resize)
880e6158
MR
4258 (char-size (frame-char-size window horizontal))
4259 (pixel-size
4260 (when (numberp size)
4261 (window--size-to-pixel window size horizontal pixelwise t)))
562dd5e9 4262 atom-root)
54f9154c 4263 (window--check frame)
562dd5e9
MR
4264 (catch 'done
4265 (cond
4266 ;; Ignore window parameters if either `ignore-window-parameters'
4267 ;; is t or the `split-window' parameter equals t.
4268 ((or ignore-window-parameters (eq function t)))
4269 ((functionp function)
4270 ;; The `split-window' parameter specifies the function to call.
4271 ;; If that function is `ignore', do nothing.
4272 (throw 'done (funcall function window size side)))
be7f5545
MR
4273 ;; If WINDOW is part of an atomic window, split the root window
4274 ;; of that atomic window instead.
562dd5e9
MR
4275 ((and (window-parameter window 'window-atom)
4276 (setq atom-root (window-atom-root window))
4277 (not (eq atom-root window)))
880e6158 4278 (throw 'done (split-window atom-root size side pixelwise)))
caceae25
MR
4279 ;; If WINDOW is a side window or its first or last child is a
4280 ;; side window, throw an error unless `window-combination-resize'
4281 ;; equals 'side.
4282 ((and (not (eq window-combination-resize 'side))
4283 (or (window-parameter window 'window-side)
4284 (and (window-child window)
4285 (or (window-parameter
4286 (window-child window) 'window-side)
4287 (window-parameter
4288 (window-last-child window) 'window-side)))))
4289 (error "Cannot split side window or parent of side window"))
4290 ;; If `window-combination-resize' is 'side and window has a side
4291 ;; window sibling, bind `window-combination-limit' to t.
4292 ((and (not (eq window-combination-resize 'side))
4293 (or (and (window-prev-sibling window)
4294 (window-parameter
4295 (window-prev-sibling window) 'window-side))
4296 (and (window-next-sibling window)
4297 (window-parameter
4298 (window-next-sibling window) 'window-side))))
4299 (setq window-combination-limit t)))
4300
4301 ;; If `window-combination-resize' is t and SIZE is non-negative,
4302 ;; bind `window-combination-limit' to t.
880e6158
MR
4303 (when (and (eq window-combination-resize t)
4304 pixel-size (> pixel-size 0))
b6f67890 4305 (setq window-combination-limit t))
562dd5e9 4306
880e6158
MR
4307 (let* ((parent-pixel-size
4308 ;; `parent-pixel-size' is the pixel size of WINDOW's
4309 ;; parent, provided it has one.
4310 (when parent (window-size parent horizontal t)))
562dd5e9
MR
4311 ;; `resize' non-nil means we are supposed to resize other
4312 ;; windows in WINDOW's combination.
4313 (resize
caceae25
MR
4314 (and window-combination-resize
4315 (or (window-parameter window 'window-side)
4316 (not (eq window-combination-resize 'side)))
8e17c9ba 4317 (not (eq window-combination-limit t))
562dd5e9 4318 ;; Resize makes sense in iso-combinations only.
3d8daefe 4319 (window-combined-p window horizontal)))
880e6158
MR
4320 ;; `old-pixel-size' is the current pixel size of WINDOW.
4321 (old-pixel-size (window-size window horizontal t))
562dd5e9
MR
4322 ;; `new-size' is the specified or calculated size of the
4323 ;; new window.
2d6b6005 4324 new-pixel-size new-parent new-normal)
880e6158
MR
4325 (cond
4326 ((not pixel-size)
4327 (setq new-pixel-size
4328 (if resize
4329 ;; When resizing try to give the new window the
4330 ;; average size of a window in its combination.
4331 (min (- parent-pixel-size
4332 (window-min-size parent horizontal nil t))
4333 (/ parent-pixel-size
4334 (1+ (window-combinations parent horizontal))))
4335 ;; Else try to give the new window half the size
4336 ;; of WINDOW (plus an eventual odd pixel).
4337 (/ old-pixel-size 2)))
4338 (unless window-resize-pixelwise
4339 ;; Round to nearest char-size multiple.
4340 (setq new-pixel-size
4341 (* char-size (round new-pixel-size char-size)))))
4342 ((>= pixel-size 0)
4343 ;; SIZE non-negative specifies the new size of WINDOW.
4344
4345 ;; Note: Specifying a non-negative SIZE is practically
4346 ;; always done as workaround for making the new window
4347 ;; appear above or on the left of the new window (the
4348 ;; ispell window is a typical example of that). In all
4349 ;; these cases the SIDE argument should be set to 'above
4350 ;; or 'left in order to support the 'resize option.
4351 ;; Here we have to nest the windows instead, see above.
4352 (setq new-pixel-size (- old-pixel-size pixel-size)))
4353 (t
4354 ;; SIZE negative specifies the size of the new window.
4355 (setq new-pixel-size (- pixel-size))))
562dd5e9
MR
4356
4357 ;; Check SIZE.
4358 (cond
880e6158 4359 ((not pixel-size)
562dd5e9
MR
4360 (cond
4361 (resize
4362 ;; SIZE unspecified, resizing.
880e6158
MR
4363 (when (and (not (window-sizable-p
4364 parent (- new-pixel-size) horizontal nil t))
562dd5e9 4365 ;; Try again with minimum split size.
880e6158
MR
4366 (setq new-pixel-size
4367 (max new-pixel-size
4368 (window-split-min-size horizontal t)))
4369 (not (window-sizable-p
4370 parent (- new-pixel-size) horizontal nil t)))
4371 (error "Window %s too small for splitting 1" parent)))
4372 ((> (+ new-pixel-size (window-min-size window horizontal nil t))
4373 old-pixel-size)
562dd5e9 4374 ;; SIZE unspecified, no resizing.
880e6158
MR
4375 (error "Window %s too small for splitting 2" window))))
4376 ((and (>= pixel-size 0)
4377 (or (>= pixel-size old-pixel-size)
4378 (< new-pixel-size
4379 (window-safe-min-pixel-size window horizontal))))
562dd5e9
MR
4380 ;; SIZE specified as new size of old window. If the new size
4381 ;; is larger than the old size or the size of the new window
4382 ;; would be less than the safe minimum, signal an error.
880e6158 4383 (error "Window %s too small for splitting 3" window))
562dd5e9
MR
4384 (resize
4385 ;; SIZE specified, resizing.
880e6158
MR
4386 (unless (window-sizable-p
4387 parent (- new-pixel-size) horizontal nil t)
562dd5e9 4388 ;; If we cannot resize the parent give up.
880e6158
MR
4389 (error "Window %s too small for splitting 4" parent)))
4390 ((or (< new-pixel-size
4391 (window-safe-min-pixel-size window horizontal))
4392 (< (- old-pixel-size new-pixel-size)
4393 (window-safe-min-pixel-size window horizontal)))
562dd5e9 4394 ;; SIZE specification violates minimum size restrictions.
880e6158 4395 (error "Window %s too small for splitting 5" window)))
562dd5e9 4396
5386012d 4397 (window--resize-reset frame horizontal)
562dd5e9
MR
4398
4399 (setq new-parent
4400 ;; Make new-parent non-nil if we need a new parent window;
4401 ;; either because we want to nest or because WINDOW is not
4402 ;; iso-combined.
8e17c9ba 4403 (or (eq window-combination-limit t)
b6f67890 4404 (not (window-combined-p window horizontal))))
562dd5e9
MR
4405 (setq new-normal
4406 ;; Make new-normal the normal size of the new window.
4407 (cond
880e6158
MR
4408 (pixel-size (/ (float new-pixel-size)
4409 (if new-parent old-pixel-size parent-pixel-size)))
562dd5e9 4410 (new-parent 0.5)
3d8daefe 4411 (resize (/ 1.0 (1+ (window-combinations parent horizontal))))
562dd5e9
MR
4412 (t (/ (window-normal-size window horizontal) 2.0))))
4413
4414 (if resize
4415 ;; Try to get space from OLD's siblings. We could go "up" and
4416 ;; try getting additional space from surrounding windows but
4417 ;; we won't be able to return space to those windows when we
4418 ;; delete the one we create here. Hence we do not go up.
4419 (progn
880e6158
MR
4420 (window--resize-child-windows
4421 parent (- new-pixel-size) horizontal)
562dd5e9
MR
4422 (let* ((normal (- 1.0 new-normal))
4423 (sub (window-child parent)))
4424 (while sub
4425 (set-window-new-normal
4426 sub (* (window-normal-size sub horizontal) normal))
4427 (setq sub (window-right sub)))))
4428 ;; Get entire space from WINDOW.
880e6158
MR
4429 (set-window-new-pixel
4430 window (- old-pixel-size new-pixel-size))
4431;; (set-window-new-pixel window (- old-pixel-size new-pixel-size))
4432;; (set-window-new-total
4433;; window (- old-size new-size))
4434 (window--resize-this-window window (- new-pixel-size) horizontal)
562dd5e9
MR
4435 (set-window-new-normal
4436 window (- (if new-parent 1.0 (window-normal-size window horizontal))
4437 new-normal)))
4438
880e6158
MR
4439 (let* ((new (split-window-internal window new-pixel-size side new-normal)))
4440 (window--pixel-to-total frame horizontal)
caceae25 4441 ;; Assign window-side parameters, if any.
c660a885
MR
4442 (cond
4443 ((eq window-combination-resize 'side)
caceae25
MR
4444 (let ((window-side
4445 (cond
4446 (window-side window-side)
4447 ((eq side 'above) 'top)
4448 ((eq side 'below) 'bottom)
4449 (t side))))
4450 ;; We made a new side window.
4451 (set-window-parameter new 'window-side window-side)
4452 (when (and new-parent (window-parameter window 'window-side))
4453 ;; We've been splitting a side root window. Give the
4454 ;; new parent the same window-side parameter.
4455 (set-window-parameter
4456 (window-parent new) 'window-side window-side))))
c660a885
MR
4457 ((eq window-combination-resize 'atom)
4458 ;; Make sure `window--check-frame' won't destroy an existing
4459 ;; atomic window in case the new window gets nested inside.
4460 (unless (window-parameter window 'window-atom)
4461 (set-window-parameter window 'window-atom t))
4462 (when new-parent
4463 (set-window-parameter (window-parent new) 'window-atom t))
4464 (set-window-parameter new 'window-atom t)))
562dd5e9
MR
4465
4466 (run-window-configuration-change-hook frame)
880e6158 4467 (run-window-scroll-functions new)
54f9154c 4468 (window--check frame)
562dd5e9
MR
4469 ;; Always return the new window.
4470 new)))))
4471
4472;; I think this should be the default; I think people will prefer it--rms.
4473(defcustom split-window-keep-point t
2d197ffb
CY
4474 "If non-nil, \\[split-window-below] preserves point in the new window.
4475If nil, adjust point in the two windows to minimize redisplay.
4476This option applies only to `split-window-below' and functions
4477that call it. The low-level `split-window' function always keeps
4478the original point in both windows."
562dd5e9
MR
4479 :type 'boolean
4480 :group 'windows)
4481
2d197ffb
CY
4482(defun split-window-below (&optional size)
4483 "Split the selected window into two windows, one above the other.
4484The selected window is above. The newly split-off window is
4485below, and displays the same buffer. Return the new window.
4486
4487If optional argument SIZE is omitted or nil, both windows get the
4488same height, or close to it. If SIZE is positive, the upper
4489\(selected) window gets SIZE lines. If SIZE is negative, the
4490lower (new) window gets -SIZE lines.
4491
4492If the variable `split-window-keep-point' is non-nil, both
4493windows get the same value of point as the selected window.
4494Otherwise, the window starts are chosen so as to minimize the
4495amount of redisplay; this is convenient on slow terminals."
562dd5e9
MR
4496 (interactive "P")
4497 (let ((old-window (selected-window))
5481664a 4498 (old-point (window-point))
562dd5e9
MR
4499 (size (and size (prefix-numeric-value size)))
4500 moved-by-window-height moved new-window bottom)
4501 (when (and size (< size 0) (< (- size) window-min-height))
4502 ;; `split-window' would not signal an error here.
4503 (error "Size of new window too small"))
4504 (setq new-window (split-window nil size))
4505 (unless split-window-keep-point
4506 (with-current-buffer (window-buffer)
c491fa41
MR
4507 ;; Use `save-excursion' around vertical movements below
4508 ;; (Bug#10971). Note: When the selected window's buffer has a
4509 ;; header line, up to two lines of the buffer may not show up
4510 ;; in the resulting configuration.
4511 (save-excursion
4512 (goto-char (window-start))
4513 (setq moved (vertical-motion (window-height)))
4514 (set-window-start new-window (point))
4515 (when (> (point) (window-point new-window))
4516 (set-window-point new-window (point)))
4517 (when (= moved (window-height))
4518 (setq moved-by-window-height t)
4519 (vertical-motion -1))
4520 (setq bottom (point)))
4521 (and moved-by-window-height
4522 (<= bottom (point))
5481664a 4523 (set-window-point old-window (1- bottom)))
c491fa41
MR
4524 (and moved-by-window-height
4525 (<= (window-start new-window) old-point)
4526 (set-window-point new-window old-point)
4527 (select-window new-window))))
9397e56f
MR
4528 ;; Always copy quit-restore parameter in interactive use.
4529 (let ((quit-restore (window-parameter old-window 'quit-restore)))
4530 (when quit-restore
4531 (set-window-parameter new-window 'quit-restore quit-restore)))
4532 new-window))
562dd5e9 4533
2d197ffb 4534(defalias 'split-window-vertically 'split-window-below)
562dd5e9 4535
2d197ffb
CY
4536(defun split-window-right (&optional size)
4537 "Split the selected window into two side-by-side windows.
4538The selected window is on the left. The newly split-off window
4539is on the right, and displays the same buffer. Return the new
4540window.
562dd5e9 4541
2d197ffb
CY
4542If optional argument SIZE is omitted or nil, both windows get the
4543same width, or close to it. If SIZE is positive, the left-hand
4544\(selected) window gets SIZE columns. If SIZE is negative, the
4545right-hand (new) window gets -SIZE columns. Here, SIZE includes
4546the width of the window's scroll bar; if there are no scroll
4547bars, it includes the width of the divider column to the window's
4548right, if any."
562dd5e9
MR
4549 (interactive "P")
4550 (let ((old-window (selected-window))
4551 (size (and size (prefix-numeric-value size)))
4552 new-window)
4553 (when (and size (< size 0) (< (- size) window-min-width))
4554 ;; `split-window' would not signal an error here.
4555 (error "Size of new window too small"))
9397e56f
MR
4556 (setq new-window (split-window nil size t))
4557 ;; Always copy quit-restore parameter in interactive use.
4558 (let ((quit-restore (window-parameter old-window 'quit-restore)))
4559 (when quit-restore
4560 (set-window-parameter new-window 'quit-restore quit-restore)))
4561 new-window))
562dd5e9 4562
2d197ffb 4563(defalias 'split-window-horizontally 'split-window-right)
562dd5e9 4564\f
6198ccd0
MR
4565;;; Balancing windows.
4566
4567;; The following routine uses the recycled code from an old version of
be7f5545
MR
4568;; `window--resize-child-windows'. It's not very pretty, but coding it the way the
4569;; new `window--resize-child-windows' code does would hardly make it any shorter or
6198ccd0
MR
4570;; more readable (FWIW we'd need three loops - one to calculate the
4571;; minimum sizes per window, one to enlarge or shrink windows until the
4572;; new parent-size matches, and one where we shrink the largest/enlarge
4573;; the smallest window).
4574(defun balance-windows-2 (window horizontal)
4575 "Subroutine of `balance-windows-1'.
3d8daefe 4576WINDOW must be a vertical combination (horizontal if HORIZONTAL
85c2386b 4577is non-nil)."
880e6158
MR
4578 (let* ((char-size (if window-resize-pixelwise
4579 1
4580 (frame-char-size window horizontal)))
4581 (first (window-child window))
6198ccd0
MR
4582 (sub first)
4583 (number-of-children 0)
880e6158 4584 (parent-size (window-new-pixel window))
6198ccd0 4585 (total-sum parent-size)
cb882333 4586 failed size sub-total sub-delta sub-amount rest)
6198ccd0
MR
4587 (while sub
4588 (setq number-of-children (1+ number-of-children))
4589 (when (window-size-fixed-p sub horizontal)
4590 (setq total-sum
880e6158 4591 (- total-sum (window-size sub horizontal t)))
6198ccd0
MR
4592 (set-window-new-normal sub 'ignore))
4593 (setq sub (window-right sub)))
3c448ab6 4594
6198ccd0
MR
4595 (setq failed t)
4596 (while (and failed (> number-of-children 0))
4597 (setq size (/ total-sum number-of-children))
4598 (setq failed nil)
4599 (setq sub first)
4600 (while (and sub (not failed))
be7f5545
MR
4601 ;; Ignore child windows that should be ignored or are stuck.
4602 (unless (window--resize-child-windows-skip-p sub)
880e6158 4603 (setq sub-total (window-size sub horizontal t))
6198ccd0
MR
4604 (setq sub-delta (- size sub-total))
4605 (setq sub-amount
880e6158 4606 (window-sizable sub sub-delta horizontal nil t))
be7f5545 4607 ;; Register the new total size for this child window.
880e6158 4608 (set-window-new-pixel sub (+ sub-total sub-amount))
6198ccd0
MR
4609 (unless (= sub-amount sub-delta)
4610 (setq total-sum (- total-sum sub-total sub-amount))
4611 (setq number-of-children (1- number-of-children))
4612 ;; We failed and need a new round.
4613 (setq failed t)
4614 (set-window-new-normal sub 'skip)))
4615 (setq sub (window-right sub))))
3c448ab6 4616
880e6158 4617 ;; How can we be sure that `number-of-children' is NOT zero here ?
6198ccd0
MR
4618 (setq rest (% total-sum number-of-children))
4619 ;; Fix rounding by trying to enlarge non-stuck windows by one line
4620 ;; (column) until `rest' is zero.
4621 (setq sub first)
4622 (while (and sub (> rest 0))
be7f5545 4623 (unless (window--resize-child-windows-skip-p window)
7e1899d7 4624 (set-window-new-pixel sub (min rest char-size) t)
880e6158 4625 (setq rest (- rest char-size)))
6198ccd0 4626 (setq sub (window-right sub)))
3c448ab6 4627
6198ccd0
MR
4628 ;; Fix rounding by trying to enlarge stuck windows by one line
4629 ;; (column) until `rest' equals zero.
4630 (setq sub first)
4631 (while (and sub (> rest 0))
4632 (unless (eq (window-new-normal sub) 'ignore)
7e1899d7 4633 (set-window-new-pixel sub (min rest char-size) t)
880e6158 4634 (setq rest (- rest char-size)))
6198ccd0
MR
4635 (setq sub (window-right sub)))
4636
4637 (setq sub first)
4638 (while sub
4639 ;; Record new normal sizes.
4640 (set-window-new-normal
4641 sub (/ (if (eq (window-new-normal sub) 'ignore)
880e6158
MR
4642 (window-size sub horizontal t)
4643 (window-new-pixel sub))
6198ccd0 4644 (float parent-size)))
be7f5545 4645 ;; Recursively balance each window's child windows.
6198ccd0
MR
4646 (balance-windows-1 sub horizontal)
4647 (setq sub (window-right sub)))))
4648
4649(defun balance-windows-1 (window &optional horizontal)
4650 "Subroutine of `balance-windows'."
4651 (if (window-child window)
4652 (let ((sub (window-child window)))
3d8daefe 4653 (if (window-combined-p sub horizontal)
6198ccd0 4654 (balance-windows-2 window horizontal)
880e6158 4655 (let ((size (window-new-pixel window)))
6198ccd0 4656 (while sub
880e6158 4657 (set-window-new-pixel sub size)
6198ccd0
MR
4658 (balance-windows-1 sub horizontal)
4659 (setq sub (window-right sub))))))))
4660
4661(defun balance-windows (&optional window-or-frame)
be7f5545 4662 "Balance the sizes of windows of WINDOW-OR-FRAME.
6198ccd0
MR
4663WINDOW-OR-FRAME is optional and defaults to the selected frame.
4664If WINDOW-OR-FRAME denotes a frame, balance the sizes of all
4c36be58 4665windows of that frame. If WINDOW-OR-FRAME denotes a window,
be7f5545 4666recursively balance the sizes of all child windows of that
6198ccd0
MR
4667window."
4668 (interactive)
4669 (let* ((window
4670 (cond
4671 ((or (not window-or-frame)
4672 (frame-live-p window-or-frame))
4673 (frame-root-window window-or-frame))
4674 ((or (window-live-p window-or-frame)
4675 (window-child window-or-frame))
4676 window-or-frame)
4677 (t
4678 (error "Not a window or frame %s" window-or-frame))))
4679 (frame (window-frame window)))
4680 ;; Balance vertically.
5386012d 4681 (window--resize-reset (window-frame window))
6198ccd0 4682 (balance-windows-1 window)
880e6158
MR
4683 (when (window--resize-apply-p frame)
4684 (window-resize-apply frame)
4685 (window--pixel-to-total frame)
4686 (run-window-configuration-change-hook frame))
6198ccd0 4687 ;; Balance horizontally.
5386012d 4688 (window--resize-reset (window-frame window) t)
6198ccd0 4689 (balance-windows-1 window t)
880e6158 4690 (when (window--resize-apply-p frame t)
3669b636 4691 (window-resize-apply frame t)
880e6158
MR
4692 (window--pixel-to-total frame t)
4693 (run-window-configuration-change-hook frame))))
3c448ab6
MR
4694
4695(defun window-fixed-size-p (&optional window direction)
4696 "Return t if WINDOW cannot be resized in DIRECTION.
4697WINDOW defaults to the selected window. DIRECTION can be
4698nil (i.e. any), `height' or `width'."
4699 (with-current-buffer (window-buffer window)
4700 (when (and (boundp 'window-size-fixed) window-size-fixed)
4701 (not (and direction
4702 (member (cons direction window-size-fixed)
4703 '((height . width) (width . height))))))))
4704
4705;;; A different solution to balance-windows.
3c448ab6
MR
4706(defvar window-area-factor 1
4707 "Factor by which the window area should be over-estimated.
4708This is used by `balance-windows-area'.
4709Changing this globally has no effect.")
4710(make-variable-buffer-local 'window-area-factor)
4711
880e6158 4712(defun balance-windows-area-adjust (window delta horizontal pixelwise)
d615d6d2 4713 "Wrapper around `window-resize' with error checking.
6198ccd0 4714Arguments WINDOW, DELTA and HORIZONTAL are passed on to that function."
d615d6d2 4715 ;; `window-resize' may fail if delta is too large.
6198ccd0
MR
4716 (while (>= (abs delta) 1)
4717 (condition-case nil
4718 (progn
880e6158
MR
4719 ;; It was wrong to use `window-resize' here. Somehow
4720 ;; `balance-windows-area' depends on resizing windows
4721 ;; asymmetrically.
4722 (adjust-window-trailing-edge window delta horizontal pixelwise)
6198ccd0
MR
4723 (setq delta 0))
4724 (error
4725 ;;(message "adjust: %s" (error-message-string err))
4726 (setq delta (/ delta 2))))))
4727
3c448ab6
MR
4728(defun balance-windows-area ()
4729 "Make all visible windows the same area (approximately).
4730See also `window-area-factor' to change the relative size of
4731specific buffers."
4732 (interactive)
4733 (let* ((unchanged 0) (carry 0) (round 0)
4734 ;; Remove fixed-size windows.
4735 (wins (delq nil (mapcar (lambda (win)
4736 (if (not (window-fixed-size-p win)) win))
4737 (window-list nil 'nomini))))
4738 (changelog nil)
880e6158
MR
4739 (pixelwise window-resize-pixelwise)
4740 next)
3c448ab6
MR
4741 ;; Resizing a window changes the size of surrounding windows in complex
4742 ;; ways, so it's difficult to balance them all. The introduction of
4743 ;; `adjust-window-trailing-edge' made it a bit easier, but it is still
4744 ;; very difficult to do. `balance-window' above takes an off-line
4745 ;; approach: get the whole window tree, then balance it, then try to
4746 ;; adjust the windows so they fit the result.
4747 ;; Here, instead, we take a "local optimization" approach, where we just
4748 ;; go through all the windows several times until nothing needs to be
4749 ;; changed. The main problem with this approach is that it's difficult
4750 ;; to make sure it terminates, so we use some heuristic to try and break
4751 ;; off infinite loops.
4752 ;; After a round without any change, we allow a second, to give a chance
4753 ;; to the carry to propagate a minor imbalance from the end back to
4754 ;; the beginning.
4755 (while (< unchanged 2)
4756 ;; (message "New round")
4757 (setq unchanged (1+ unchanged) round (1+ round))
4758 (dolist (win wins)
4759 (setq next win)
4760 (while (progn (setq next (next-window next))
4761 (window-fixed-size-p next)))
4762 ;; (assert (eq next (or (cadr (member win wins)) (car wins))))
4763 (let* ((horiz
880e6158 4764 (< (car (window-pixel-edges win)) (car (window-pixel-edges next))))
18cee9ec
MR
4765 (areadiff (/ (- (* (window-size next nil pixelwise)
4766 (window-size next t pixelwise)
3c448ab6
MR
4767 (buffer-local-value 'window-area-factor
4768 (window-buffer next)))
18cee9ec
MR
4769 (* (window-size win nil pixelwise)
4770 (window-size win t pixelwise)
3c448ab6
MR
4771 (buffer-local-value 'window-area-factor
4772 (window-buffer win))))
4773 (max (buffer-local-value 'window-area-factor
4774 (window-buffer win))
4775 (buffer-local-value 'window-area-factor
4776 (window-buffer next)))))
4777 (edgesize (if horiz
18cee9ec
MR
4778 (+ (window-size win nil pixelwise)
4779 (window-size next nil pixelwise))
4780 (+ (window-size win t pixelwise)
4781 (window-size next t pixelwise))))
3c448ab6
MR
4782 (diff (/ areadiff edgesize)))
4783 (when (zerop diff)
4784 ;; Maybe diff is actually closer to 1 than to 0.
4785 (setq diff (/ (* 3 areadiff) (* 2 edgesize))))
4786 (when (and (zerop diff) (not (zerop areadiff)))
4787 (setq diff (/ (+ areadiff carry) edgesize))
4788 ;; Change things smoothly.
4789 (if (or (> diff 1) (< diff -1)) (setq diff (/ diff 2))))
4790 (if (zerop diff)
4791 ;; Make sure negligible differences don't accumulate to
4792 ;; become significant.
4793 (setq carry (+ carry areadiff))
6198ccd0 4794 ;; This used `adjust-window-trailing-edge' before and uses
d615d6d2 4795 ;; `window-resize' now. Error wrapping is still needed.
880e6158 4796 (balance-windows-area-adjust win diff horiz pixelwise)
3c448ab6 4797 ;; (sit-for 0.5)
880e6158 4798 (let ((change (cons win (window-pixel-edges win))))
3c448ab6
MR
4799 ;; If the same change has been seen already for this window,
4800 ;; we're most likely in an endless loop, so don't count it as
4801 ;; a change.
4802 (unless (member change changelog)
4803 (push change changelog)
4804 (setq unchanged 0 carry 0)))))))
4805 ;; We've now basically balanced all the windows.
4806 ;; But there may be some minor off-by-one imbalance left over,
4807 ;; so let's do some fine tuning.
4808 ;; (bw-finetune wins)
4809 ;; (message "Done in %d rounds" round)
4810 ))
9d89fec7
MR
4811
4812;;; Window states, how to get them and how to put them in a window.
34a02f46 4813(defun window--state-get-1 (window &optional writable)
9d89fec7
MR
4814 "Helper function for `window-state-get'."
4815 (let* ((type
4816 (cond
d68443dc
MR
4817 ((window-top-child window) 'vc)
4818 ((window-left-child window) 'hc)
9d89fec7
MR
4819 (t 'leaf)))
4820 (buffer (window-buffer window))
4821 (selected (eq window (selected-window)))
4822 (head
4b0d61e3
SM
4823 `(,type
4824 ,@(unless (window-next-sibling window) `((last . t)))
880e6158
MR
4825 (pixel-width . ,(window-pixel-width window))
4826 (pixel-height . ,(window-pixel-height window))
4827 (total-width . ,(window-total-width window))
4828 (total-height . ,(window-total-height window))
4b0d61e3
SM
4829 (normal-height . ,(window-normal-size window))
4830 (normal-width . ,(window-normal-size window t))
8bbdea0f 4831 ,@(unless (window-live-p window)
ab5340fe 4832 `((combination-limit . ,(window-combination-limit window))))
34a02f46
MR
4833 ,@(let ((parameters (window-parameters window))
4834 list)
4835 ;; Make copies of those window parameters whose
4836 ;; persistence property is `writable' if WRITABLE is
4837 ;; non-nil and non-nil if WRITABLE is nil.
4838 (dolist (par parameters)
4839 (let ((pers (cdr (assq (car par)
4840 window-persistent-parameters))))
4841 (when (and pers (or (not writable) (eq pers 'writable)))
4842 (setq list (cons (cons (car par) (cdr par)) list)))))
4843 ;; Add `clone-of' parameter if necessary.
4844 (let ((pers (cdr (assq 'clone-of
4845 window-persistent-parameters))))
4846 (when (and pers (or (not writable) (eq pers 'writable))
4847 (not (assq 'clone-of list)))
4848 (setq list (cons (cons 'clone-of window) list))))
4b0d61e3
SM
4849 (when list
4850 `((parameters . ,list))))
4851 ,@(when buffer
1edf595d 4852 ;; All buffer related things go in here.
5481664a 4853 (let ((point (window-point window))
1edf595d
MR
4854 (start (window-start window)))
4855 `((buffer
4856 ,(buffer-name buffer)
4857 (selected . ,selected)
4858 (hscroll . ,(window-hscroll window))
4859 (fringes . ,(window-fringes window))
4860 (margins . ,(window-margins window))
4861 (scroll-bars . ,(window-scroll-bars window))
4862 (vscroll . ,(window-vscroll window))
4863 (dedicated . ,(window-dedicated-p window))
de8c03dc
SM
4864 (point . ,(if writable point
4865 (copy-marker point
4866 (buffer-local-value
4867 'window-point-insertion-type
4868 buffer))))
1edf595d 4869 (start . ,(if writable start (copy-marker start)))))))))
9d89fec7
MR
4870 (tail
4871 (when (memq type '(vc hc))
4872 (let (list)
4873 (setq window (window-child window))
4874 (while window
34a02f46 4875 (setq list (cons (window--state-get-1 window writable) list))
9d89fec7
MR
4876 (setq window (window-right window)))
4877 (nreverse list)))))
4878 (append head tail)))
4879
34a02f46 4880(defun window-state-get (&optional window writable)
9d89fec7
MR
4881 "Return state of WINDOW as a Lisp object.
4882WINDOW can be any window and defaults to the root window of the
4883selected frame.
4884
34a02f46
MR
4885Optional argument WRITABLE non-nil means do not use markers for
4886sampling `window-point' and `window-start'. Together, WRITABLE
4887and the variable `window-persistent-parameters' specify which
4888window parameters are saved by this function. WRITABLE should be
4889non-nil when the return value shall be written to a file and read
4890back in another session. Otherwise, an application may run into
4891an `invalid-read-syntax' error while attempting to read back the
4892value from file.
9d89fec7
MR
4893
4894The return value can be used as argument for `window-state-put'
4895to put the state recorded here into an arbitrary window. The
4896value can be also stored on disk and read back in a new session."
4897 (setq window
4898 (if window
24300f5f 4899 (if (window-valid-p window)
9d89fec7
MR
4900 window
4901 (error "%s is not a live or internal window" window))
4902 (frame-root-window)))
4903 ;; The return value is a cons whose car specifies some constraints on
be7f5545
MR
4904 ;; the size of WINDOW. The cdr lists the states of the child windows
4905 ;; of WINDOW.
9d89fec7
MR
4906 (cons
4907 ;; Frame related things would go into a function, say `frame-state',
4908 ;; calling `window-state-get' to insert the frame's root window.
4b0d61e3
SM
4909 `((min-height . ,(window-min-size window))
4910 (min-width . ,(window-min-size window t))
4911 (min-height-ignore . ,(window-min-size window nil t))
4912 (min-width-ignore . ,(window-min-size window t t))
4913 (min-height-safe . ,(window-min-size window nil 'safe))
880e6158
MR
4914 (min-width-safe . ,(window-min-size window t 'safe))
4915 (min-pixel-height . ,(window-min-size window nil nil t))
4916 (min-pixel-width . ,(window-min-size window t nil t))
4917 (min-pixel-height-ignore . ,(window-min-size window nil t t))
4918 (min-pixel-width-ignore . ,(window-min-size window t t t))
4919 (min-pixel-height-safe . ,(window-min-size window nil 'safe t))
4920 (min-pixel-width-safe . ,(window-min-size window t 'safe t)))
34a02f46 4921 (window--state-get-1 window writable)))
9d89fec7
MR
4922
4923(defvar window-state-put-list nil
4924 "Helper variable for `window-state-put'.")
4925
34ada5f4
MR
4926(defvar window-state-put-stale-windows nil
4927 "Helper variable for `window-state-put'.")
4928
880e6158 4929(defun window--state-put-1 (state &optional window ignore totals pixelwise)
9d89fec7
MR
4930 "Helper function for `window-state-put'."
4931 (let ((type (car state)))
4932 (setq state (cdr state))
4933 (cond
4934 ((eq type 'leaf)
4935 ;; For a leaf window just add unprocessed entries to
4936 ;; `window-state-put-list'.
c7635a97 4937 (push (cons window state) window-state-put-list))
9d89fec7
MR
4938 ((memq type '(vc hc))
4939 (let* ((horizontal (eq type 'hc))
880e6158 4940 (total (window-size window horizontal pixelwise))
9d89fec7
MR
4941 (first t)
4942 size new)
4943 (dolist (item state)
4944 ;; Find the next child window. WINDOW always points to the
4945 ;; real window that we want to fill with what we find here.
4946 (when (memq (car item) '(leaf vc hc))
4947 (if (assq 'last item)
c56cad4a 4948 ;; The last child window. Below `window--state-put-1'
9d89fec7
MR
4949 ;; will put into it whatever ITEM has in store.
4950 (setq new nil)
4951 ;; Not the last child window, prepare for splitting
4952 ;; WINDOW. SIZE is the new (and final) size of the old
4953 ;; window.
4954 (setq size
4955 (if totals
4956 ;; Use total size.
880e6158
MR
4957 (if pixelwise
4958 (cdr (assq (if horizontal
4959 'pixel-width
4960 'pixel-height)
4961 item))
4962 (cdr (assq (if horizontal
4963 'total-width
4964 'total-height)
4965 item)))
9d89fec7 4966 ;; Use normalized size and round.
880e6158
MR
4967 (round
4968 (* total
4969 (cdr (assq (if horizontal 'normal-width 'normal-height)
4970 item))))))
9d89fec7
MR
4971
4972 ;; Use safe sizes, we try to resize later.
880e6158
MR
4973 (setq size (max size
4974 (if horizontal
4975 (* window-safe-min-width
4976 (if pixelwise
4977 (frame-char-width (window-frame window))
4978 1))
4979 (* window-safe-min-height
4980 (if pixelwise
4981 (frame-char-height (window-frame window))
4982 1)))))
4983 (if (window-sizable-p window (- size) horizontal 'safe pixelwise)
b6f67890
MR
4984 (let* ((window-combination-limit
4985 (assq 'combination-limit item)))
301b181a 4986 ;; We must inherit the combination limit, otherwise
b6f67890
MR
4987 ;; we might mess up handling of atomic and side
4988 ;; window.
880e6158 4989 (setq new (split-window window size horizontal pixelwise)))
9d89fec7
MR
4990 ;; Give up if we can't resize window down to safe sizes.
4991 (error "Cannot resize window %s" window))
4992
4993 (when first
4994 (setq first nil)
4995 ;; When creating the first child window add for parent
4996 ;; unprocessed entries to `window-state-put-list'.
4997 (setq window-state-put-list
4998 (cons (cons (window-parent window) state)
4999 window-state-put-list))))
5000
5001 ;; Now process the current window (either the one we've just
5002 ;; split or the last child of its parent).
c56cad4a 5003 (window--state-put-1 item window ignore totals)
9d89fec7
MR
5004 ;; Continue with the last window split off.
5005 (setq window new))))))))
5006
880e6158 5007(defun window--state-put-2 (ignore pixelwise)
9d89fec7
MR
5008 "Helper function for `window-state-put'."
5009 (dolist (item window-state-put-list)
5010 (let ((window (car item))
b6f67890 5011 (combination-limit (cdr (assq 'combination-limit item)))
9d89fec7
MR
5012 (parameters (cdr (assq 'parameters item)))
5013 (state (cdr (assq 'buffer item))))
b6f67890
MR
5014 (when combination-limit
5015 (set-window-combination-limit window combination-limit))
34a02f46
MR
5016 ;; Reset window's parameters and assign saved ones (we might want
5017 ;; a `remove-window-parameters' function here).
5018 (dolist (parameter (window-parameters window))
5019 (set-window-parameter window (car parameter) nil))
9d89fec7
MR
5020 (when parameters
5021 (dolist (parameter parameters)
34a02f46 5022 (set-window-parameter window (car parameter) (cdr parameter))))
9d89fec7
MR
5023 ;; Process buffer related state.
5024 (when state
34ada5f4
MR
5025 (let ((buffer (get-buffer (car state))))
5026 (if buffer
c089653d
MR
5027 (with-current-buffer buffer
5028 (set-window-buffer window buffer)
5029 (set-window-hscroll window (cdr (assq 'hscroll state)))
5030 (apply 'set-window-fringes
5031 (cons window (cdr (assq 'fringes state))))
5032 (let ((margins (cdr (assq 'margins state))))
5033 (set-window-margins window (car margins) (cdr margins)))
5034 (let ((scroll-bars (cdr (assq 'scroll-bars state))))
5035 (set-window-scroll-bars
5036 window (car scroll-bars) (nth 2 scroll-bars)
5037 (nth 3 scroll-bars)))
5038 (set-window-vscroll window (cdr (assq 'vscroll state)))
5039 ;; Adjust vertically.
5040 (if (memq window-size-fixed '(t height))
5041 ;; A fixed height window, try to restore the
5042 ;; original size.
880e6158
MR
5043 (let ((delta
5044 (- (cdr (assq
5045 (if pixelwise 'pixel-height 'total-height)
5046 item))
5047 (window-size window nil pixelwise)))
c089653d 5048 window-size-fixed)
880e6158
MR
5049 (when (window--resizable-p
5050 window delta nil nil nil nil nil pixelwise)
5051 (window-resize window delta nil nil pixelwise)))
c089653d 5052 ;; Else check whether the window is not high enough.
880e6158
MR
5053 (let* ((min-size
5054 (window-min-size window nil ignore pixelwise))
5055 (delta
5056 (- min-size (window-size window nil pixelwise))))
c089653d 5057 (when (and (> delta 0)
880e6158
MR
5058 (window--resizable-p
5059 window delta nil ignore nil nil nil pixelwise))
5060 (window-resize window delta nil ignore pixelwise))))
c089653d
MR
5061 ;; Adjust horizontally.
5062 (if (memq window-size-fixed '(t width))
5063 ;; A fixed width window, try to restore the original
5064 ;; size.
880e6158
MR
5065 (let ((delta
5066 (- (cdr (assq
5067 (if pixelwise 'pixel-width 'total-width)
5068 item))
5069 (window-size window t pixelwise)))
c089653d 5070 window-size-fixed)
880e6158
MR
5071 (when (window--resizable-p
5072 window delta nil nil nil nil nil pixelwise)
5073 (window-resize window delta nil nil pixelwise)))
c089653d 5074 ;; Else check whether the window is not wide enough.
880e6158
MR
5075 (let* ((min-size (window-min-size window t ignore pixelwise))
5076 (delta (- min-size (window-size window t pixelwise))))
c089653d 5077 (when (and (> delta 0)
880e6158
MR
5078 (window--resizable-p
5079 window delta t ignore nil nil nil pixelwise))
5080 (window-resize window delta t ignore pixelwise))))
c089653d
MR
5081 ;; Set dedicated status.
5082 (set-window-dedicated-p window (cdr (assq 'dedicated state)))
5083 ;; Install positions (maybe we should do this after all
5084 ;; windows have been created and sized).
5085 (ignore-errors
5086 (set-window-start window (cdr (assq 'start state)))
5087 (set-window-point window (cdr (assq 'point state))))
5088 ;; Select window if it's the selected one.
5089 (when (cdr (assq 'selected state))
5090 (select-window window)))
5091 ;; We don't want to raise an error in case the buffer does
5092 ;; not exist anymore, so we switch to a previous one and
5093 ;; save the window with the intention of deleting it later
5094 ;; if possible.
34ada5f4 5095 (switch-to-prev-buffer window)
c089653d 5096 (push window window-state-put-stale-windows)))))))
9d89fec7
MR
5097
5098(defun window-state-put (state &optional window ignore)
5099 "Put window state STATE into WINDOW.
5100STATE should be the state of a window returned by an earlier
5101invocation of `window-state-get'. Optional argument WINDOW must
8e009b78
MR
5102specify a valid window and defaults to the selected one. If
5103WINDOW is not live, replace WINDOW by a live one before putting
5104STATE into it.
9d89fec7
MR
5105
5106Optional argument IGNORE non-nil means ignore minimum window
5107sizes and fixed size restrictions. IGNORE equal `safe' means
be7f5545 5108windows can get as small as `window-safe-min-height' and
9d89fec7 5109`window-safe-min-width'."
34ada5f4 5110 (setq window-state-put-stale-windows nil)
8e009b78
MR
5111 (setq window (window-normalize-window window))
5112
5113 ;; When WINDOW is internal, reduce it to a live one to put STATE into,
5114 ;; see Bug#16793.
5115 (unless (window-live-p window)
5116 (let ((root (frame-root-window window)))
5117 (if (eq window root)
5118 (setq window (frame-first-window root))
5119 (setq root window)
5120 (setq window (catch 'live
5121 (walk-window-subtree
5122 (lambda (window)
5123 (when (window-live-p window)
5124 (throw 'live window)))
5125 root))))
5126 (delete-other-windows-internal window root)))
5127
9d89fec7
MR
5128 (let* ((frame (window-frame window))
5129 (head (car state))
5130 ;; We check here (1) whether the total sizes of root window of
5131 ;; STATE and that of WINDOW are equal so we can avoid
5132 ;; calculating new sizes, and (2) if we do have to resize
5133 ;; whether we can do so without violating size restrictions.
880e6158
MR
5134 (pixelwise (and (cdr (assq 'pixel-width state))
5135 (cdr (assq 'pixel-height state))))
5136 (totals (or (and pixelwise
5137 (= (window-pixel-width window)
5138 (cdr (assq 'pixel-width state)))
5139 (= (window-pixel-height window)
5140 (cdr (assq 'pixel-height state))))
5141 (and (= (window-total-width window)
5142 (cdr (assq 'total-width state)))
5143 (= (window-total-height window)
5144 (cdr (assq 'total-height state))))))
5145 (min-height (cdr (assq
5146 (if pixelwise 'min-pixel-height 'min-height)
5147 head)))
5148 (min-width (cdr (assq
5149 (if pixelwise 'min-pixel-width 'min-weight)
5150 head))))
9d89fec7 5151 (if (and (not totals)
880e6158
MR
5152 (or (> min-height (window-size window nil pixelwise))
5153 (> min-width (window-size window t pixelwise)))
9d89fec7
MR
5154 (or (not ignore)
5155 (and (setq min-height
880e6158
MR
5156 (cdr (assq
5157 (if pixelwise
5158 'min-pixel-height-ignore
5159 'min-height-ignore)
5160 head)))
9d89fec7 5161 (setq min-width
880e6158
MR
5162 (cdr (assq
5163 (if pixelwise
5164 'min-pixel-width-ignore
5165 'min-width-ignore)
5166 head)))
5167 (or (> min-height
5168 (window-size window nil pixelwise))
5169 (> min-width
5170 (window-size window t pixelwise)))
9d89fec7
MR
5171 (or (not (eq ignore 'safe))
5172 (and (setq min-height
880e6158
MR
5173 (cdr (assq
5174 (if pixelwise
5175 'min-pixel-height-safe
5176 'min-height-safe)
5177 head)))
9d89fec7 5178 (setq min-width
880e6158
MR
5179 (cdr (assq
5180 (if pixelwise
5181 'min-pixel-width-safe
5182 'min-width-safe)
5183 head)))
9d89fec7 5184 (or (> min-height
880e6158 5185 (window-size window nil pixelwise))
9d89fec7 5186 (> min-width
880e6158 5187 (window-size window t pixelwise))))))))
9d89fec7
MR
5188 ;; The check above might not catch all errors due to rounding
5189 ;; issues - so IGNORE equal 'safe might not always produce the
5190 ;; minimum possible state. But such configurations hardly make
5191 ;; sense anyway.
a91adc7e 5192 (error "Window %s too small to accommodate state" window)
9d89fec7
MR
5193 (setq state (cdr state))
5194 (setq window-state-put-list nil)
5195 ;; Work on the windows of a temporary buffer to make sure that
5196 ;; splitting proceeds regardless of any buffer local values of
5197 ;; `window-size-fixed'. Release that buffer after the buffers of
c56cad4a 5198 ;; all live windows have been set by `window--state-put-2'.
9d89fec7
MR
5199 (with-temp-buffer
5200 (set-window-buffer window (current-buffer))
880e6158
MR
5201 (window--state-put-1 state window nil totals pixelwise)
5202 (window--state-put-2 ignore pixelwise))
34ada5f4
MR
5203 (while window-state-put-stale-windows
5204 (let ((window (pop window-state-put-stale-windows)))
5205 (when (eq (window-deletable-p window) t)
5206 (delete-window window))))
54f9154c 5207 (window--check frame))))
9481c002 5208\f
f818cd2a
MR
5209(defun display-buffer-record-window (type window buffer)
5210 "Record information for window used by `display-buffer'.
cf4eacfd 5211TYPE specifies the type of the calling operation and must be one
382c953b
JB
5212of the symbols 'reuse (when WINDOW existed already and was
5213reused for displaying BUFFER), 'window (when WINDOW was created
5214on an already existing frame), or 'frame (when WINDOW was
5215created on a new frame). WINDOW is the window used for or created
cf4eacfd
MR
5216by the `display-buffer' routines. BUFFER is the buffer that
5217shall be displayed.
5218
5219This function installs or updates the quit-restore parameter of
5220WINDOW. The quit-restore parameter is a list of four elements:
5221The first element is one of the symbols 'window, 'frame, 'same or
5222'other. The second element is either one of the symbols 'window
5223or 'frame or a list whose elements are the buffer previously
5224shown in the window, that buffer's window start and window point,
5225and the window's height. The third element is the window
5226selected at the time the parameter was created. The fourth
5227element is BUFFER."
f818cd2a 5228 (cond
cf4eacfd
MR
5229 ((eq type 'reuse)
5230 (if (eq (window-buffer window) buffer)
5231 ;; WINDOW shows BUFFER already.
5232 (when (consp (window-parameter window 'quit-restore))
5233 ;; If WINDOW has a quit-restore parameter, reset its car.
5234 (setcar (window-parameter window 'quit-restore) 'same))
5235 ;; WINDOW shows another buffer.
92346275
MR
5236 (with-current-buffer (window-buffer window)
5237 (set-window-parameter
5238 window 'quit-restore
5239 (list 'other
5240 ;; A quadruple of WINDOW's buffer, start, point and height.
5241 (list (current-buffer) (window-start window)
5242 ;; Preserve window-point-insertion-type (Bug#12588).
5243 (copy-marker
5244 (window-point window) window-point-insertion-type)
880e6158 5245 (window-total-height window))
92346275 5246 (selected-window) buffer)))))
cf4eacfd
MR
5247 ((eq type 'window)
5248 ;; WINDOW has been created on an existing frame.
f818cd2a 5249 (set-window-parameter
cf4eacfd
MR
5250 window 'quit-restore
5251 (list 'window 'window (selected-window) buffer)))
5252 ((eq type 'frame)
5253 ;; WINDOW has been created on a new frame.
f818cd2a 5254 (set-window-parameter
cf4eacfd
MR
5255 window 'quit-restore
5256 (list 'frame 'frame (selected-window) buffer)))))
9481c002 5257
f818cd2a
MR
5258(defcustom display-buffer-function nil
5259 "If non-nil, function to call to handle `display-buffer'.
5260It will receive two args, the buffer and a flag which if non-nil
5261means that the currently selected window is not acceptable. It
5262should choose or create a window, display the specified buffer in
5263it, and return the window.
5264
e1c2c6f2
MR
5265The specified function should call `display-buffer-record-window'
5266with corresponding arguments to set up the quit-restore parameter
5267of the window used."
f818cd2a
MR
5268 :type '(choice
5269 (const nil)
5270 (function :tag "function"))
3c448ab6 5271 :group 'windows)
9481c002 5272
71ce58e7
CY
5273(make-obsolete-variable 'display-buffer-function
5274 'display-buffer-alist "24.3")
5275
d97af5a0
CY
5276;; Eventually, we want to turn this into a defvar; instead of
5277;; customizing this, the user should use a `pop-up-frame-parameters'
5278;; alist entry in `display-buffer-base-action'.
f818cd2a
MR
5279(defcustom pop-up-frame-alist nil
5280 "Alist of parameters for automatically generated new frames.
f818cd2a
MR
5281If non-nil, the value you specify here is used by the default
5282`pop-up-frame-function' for the creation of new frames.
9481c002 5283
f818cd2a
MR
5284Since `pop-up-frame-function' is used by `display-buffer' for
5285making new frames, any value specified here by default affects
5286the automatic generation of new frames via `display-buffer' and
5287all functions based on it. The behavior of `make-frame' is not
5288affected by this variable."
9481c002 5289 :type '(repeat (cons :format "%v"
f818cd2a
MR
5290 (symbol :tag "Parameter")
5291 (sexp :tag "Value")))
9481c002 5292 :group 'frames)
9481c002 5293
f818cd2a
MR
5294(defcustom pop-up-frame-function
5295 (lambda () (make-frame pop-up-frame-alist))
5296 "Function used by `display-buffer' for creating a new frame.
5297This function is called with no arguments and should return a new
5298frame. The default value calls `make-frame' with the argument
5299`pop-up-frame-alist'."
9481c002 5300 :type 'function
9481c002 5301 :group 'frames)
3c448ab6 5302
56f31926
MR
5303(defcustom special-display-buffer-names nil
5304 "List of names of buffers that should be displayed specially.
5305Displaying a buffer with `display-buffer' or `pop-to-buffer', if
5306its name is in this list, displays the buffer in a way specified
5307by `special-display-function'. `special-display-popup-frame'
5308\(the default for `special-display-function') usually displays
5309the buffer in a separate frame made with the parameters specified
5310by `special-display-frame-alist'. If `special-display-function'
5311has been set to some other function, that function is called with
5312the buffer as first, and nil as second argument.
5313
5314Alternatively, an element of this list can be specified as
5315\(BUFFER-NAME FRAME-PARAMETERS), where BUFFER-NAME is a buffer
382c953b 5316name and FRAME-PARAMETERS an alist of (PARAMETER . VALUE) pairs.
56f31926
MR
5317`special-display-popup-frame' will interpret such pairs as frame
5318parameters when it creates a special frame, overriding the
5319corresponding values from `special-display-frame-alist'.
5320
5321As a special case, if FRAME-PARAMETERS contains (same-window . t)
5322`special-display-popup-frame' displays that buffer in the
5323selected window. If FRAME-PARAMETERS contains (same-frame . t),
5324it displays that buffer in a window on the selected frame.
5325
5326If `special-display-function' specifies some other function than
5327`special-display-popup-frame', that function is called with the
5328buffer named BUFFER-NAME as first, and FRAME-PARAMETERS as second
5329argument.
5330
5331Finally, an element of this list can be also specified as
5332\(BUFFER-NAME FUNCTION OTHER-ARGS). In that case,
5333`special-display-popup-frame' will call FUNCTION with the buffer
5334named BUFFER-NAME as first argument, and OTHER-ARGS as the
0563dae9
MR
5335second.
5336
5337Any alternative function specified here is responsible for
5338setting up the quit-restore parameter of the window used.
56f31926
MR
5339
5340If this variable appears \"not to work\", because you added a
5341name to it but the corresponding buffer is displayed in the
5342selected window, look at the values of `same-window-buffer-names'
5343and `same-window-regexps'. Those variables take precedence over
5344this one.
5345
5346See also `special-display-regexps'."
5347 :type '(repeat
5348 (choice :tag "Buffer"
5349 :value ""
5350 (string :format "%v")
5351 (cons :tag "With parameters"
5352 :format "%v"
5353 :value ("" . nil)
5354 (string :format "%v")
5355 (repeat :tag "Parameters"
5356 (cons :format "%v"
5357 (symbol :tag "Parameter")
5358 (sexp :tag "Value"))))
5359 (list :tag "With function"
5360 :format "%v"
5361 :value ("" . nil)
5362 (string :format "%v")
5363 (function :tag "Function")
5364 (repeat :tag "Arguments" (sexp)))))
5365 :group 'windows
5366 :group 'frames)
77f1f99c 5367(make-obsolete-variable 'special-display-buffer-names 'display-buffer-alist "24.3")
ac549fa5
GM
5368(put 'special-display-buffer-names 'risky-local-variable t)
5369
56f31926
MR
5370(defcustom special-display-regexps nil
5371 "List of regexps saying which buffers should be displayed specially.
5372Displaying a buffer with `display-buffer' or `pop-to-buffer', if
5373any regexp in this list matches its name, displays it specially
5374using `special-display-function'. `special-display-popup-frame'
5375\(the default for `special-display-function') usually displays
5376the buffer in a separate frame made with the parameters specified
5377by `special-display-frame-alist'. If `special-display-function'
5378has been set to some other function, that function is called with
5379the buffer as first, and nil as second argument.
5380
5381Alternatively, an element of this list can be specified as
5382\(REGEXP FRAME-PARAMETERS), where REGEXP is a regexp as above and
5383FRAME-PARAMETERS an alist of (PARAMETER . VALUE) pairs.
5384`special-display-popup-frame' will then interpret these pairs as
5385frame parameters when creating a special frame for a buffer whose
5386name matches REGEXP, overriding the corresponding values from
5387`special-display-frame-alist'.
5388
5389As a special case, if FRAME-PARAMETERS contains (same-window . t)
5390`special-display-popup-frame' displays buffers matching REGEXP in
382c953b 5391the selected window. (same-frame . t) in FRAME-PARAMETERS means
56f31926
MR
5392to display such buffers in a window on the selected frame.
5393
5394If `special-display-function' specifies some other function than
5395`special-display-popup-frame', that function is called with the
5396buffer whose name matched REGEXP as first, and FRAME-PARAMETERS
5397as second argument.
5398
5399Finally, an element of this list can be also specified as
5400\(REGEXP FUNCTION OTHER-ARGS). `special-display-popup-frame'
5401will then call FUNCTION with the buffer whose name matched
0563dae9
MR
5402REGEXP as first, and OTHER-ARGS as second argument.
5403
5404Any alternative function specified here is responsible for
5405setting up the quit-restore parameter of the window used.
56f31926
MR
5406
5407If this variable appears \"not to work\", because you added a
5408name to it but the corresponding buffer is displayed in the
5409selected window, look at the values of `same-window-buffer-names'
5410and `same-window-regexps'. Those variables take precedence over
5411this one.
5412
5413See also `special-display-buffer-names'."
5414 :type '(repeat
5415 (choice :tag "Buffer"
5416 :value ""
5417 (regexp :format "%v")
5418 (cons :tag "With parameters"
5419 :format "%v"
5420 :value ("" . nil)
5421 (regexp :format "%v")
5422 (repeat :tag "Parameters"
5423 (cons :format "%v"
5424 (symbol :tag "Parameter")
5425 (sexp :tag "Value"))))
5426 (list :tag "With function"
5427 :format "%v"
5428 :value ("" . nil)
5429 (regexp :format "%v")
5430 (function :tag "Function")
5431 (repeat :tag "Arguments" (sexp)))))
5432 :group 'windows
5433 :group 'frames)
77f1f99c
CY
5434(make-obsolete-variable 'special-display-regexps 'display-buffer-alist "24.3")
5435(put 'special-display-regexps 'risky-local-variable t)
56f31926 5436
3c448ab6
MR
5437(defun special-display-p (buffer-name)
5438 "Return non-nil if a buffer named BUFFER-NAME gets a special frame.
56f31926
MR
5439More precisely, return t if `special-display-buffer-names' or
5440`special-display-regexps' contain a string entry equaling or
5441matching BUFFER-NAME. If `special-display-buffer-names' or
5442`special-display-regexps' contain a list entry whose car equals
5443or matches BUFFER-NAME, the return value is the cdr of that
5444entry."
f818cd2a 5445 (let (tmp)
98722073 5446 (cond
f818cd2a 5447 ((member buffer-name special-display-buffer-names)
98722073 5448 t)
f818cd2a 5449 ((setq tmp (assoc buffer-name special-display-buffer-names))
98722073
MR
5450 (cdr tmp))
5451 ((catch 'found
f818cd2a 5452 (dolist (regexp special-display-regexps)
98722073
MR
5453 (cond
5454 ((stringp regexp)
5455 (when (string-match-p regexp buffer-name)
5456 (throw 'found t)))
5457 ((and (consp regexp) (stringp (car regexp))
5458 (string-match-p (car regexp) buffer-name))
5459 (throw 'found (cdr regexp))))))))))
9481c002 5460
f818cd2a
MR
5461(defcustom special-display-frame-alist
5462 '((height . 14) (width . 80) (unsplittable . t))
5463 "Alist of parameters for special frames.
5464Special frames are used for buffers whose names are listed in
5465`special-display-buffer-names' and for buffers whose names match
5466one of the regular expressions in `special-display-regexps'.
9481c002 5467
f818cd2a 5468This variable can be set in your init file, like this:
9481c002 5469
f818cd2a
MR
5470 (setq special-display-frame-alist '((width . 80) (height . 20)))
5471
5472These supersede the values given in `default-frame-alist'."
5473 :type '(repeat (cons :format "%v"
5474 (symbol :tag "Parameter")
5475 (sexp :tag "Value")))
5476 :group 'frames)
77f1f99c 5477(make-obsolete-variable 'special-display-frame-alist 'display-buffer-alist "24.3")
f818cd2a
MR
5478
5479(defun special-display-popup-frame (buffer &optional args)
31cd32c9 5480 "Pop up a frame displaying BUFFER and return its window.
f818cd2a
MR
5481If BUFFER is already displayed in a visible or iconified frame,
5482raise that frame. Otherwise, display BUFFER in a new frame.
5483
5484Optional argument ARGS is a list specifying additional
5485information.
5486
5487If ARGS is an alist, use it as a list of frame parameters. If
382c953b
JB
5488these parameters contain (same-window . t), display BUFFER in
5489the selected window. If they contain (same-frame . t), display
f818cd2a
MR
5490BUFFER in a window of the selected frame.
5491
5492If ARGS is a list whose car is a symbol, use (car ARGS) as a
fdc2806d
CY
5493function to do the work. Pass it BUFFER as first argument, and
5494pass the elements of (cdr ARGS) as the remaining arguments."
f818cd2a
MR
5495 (if (and args (symbolp (car args)))
5496 (apply (car args) buffer (cdr args))
5497 (let ((window (get-buffer-window buffer 0)))
5498 (or
5499 ;; If we have a window already, make it visible.
5500 (when window
5501 (let ((frame (window-frame window)))
5502 (make-frame-visible frame)
5503 (raise-frame frame)
cf4eacfd 5504 (display-buffer-record-window 'reuse window buffer)
f818cd2a
MR
5505 window))
5506 ;; Reuse the current window if the user requested it.
5507 (when (cdr (assq 'same-window args))
5508 (condition-case nil
5509 (progn (switch-to-buffer buffer nil t) (selected-window))
5510 (error nil)))
5511 ;; Stay on the same frame if requested.
5512 (when (or (cdr (assq 'same-frame args)) (cdr (assq 'same-window args)))
5513 (let* ((pop-up-windows t)
5514 pop-up-frames
5515 special-display-buffer-names special-display-regexps)
5516 (display-buffer buffer)))
5517 ;; If no window yet, make one in a new frame.
e75852fd
MR
5518 (let* ((frame
5519 (with-current-buffer buffer
5520 (make-frame (append args special-display-frame-alist))))
5521 (window (frame-selected-window frame)))
5522 (display-buffer-record-window 'frame window buffer)
c5e28e39
MR
5523 (unless (eq buffer (window-buffer window))
5524 (set-window-buffer window buffer)
5525 (set-window-prev-buffers window nil))
e75852fd
MR
5526 (set-window-dedicated-p window t)
5527 window)))))
f818cd2a
MR
5528
5529(defcustom special-display-function 'special-display-popup-frame
5530 "Function to call for displaying special buffers.
5531This function is called with two arguments - the buffer and,
5532optionally, a list - and should return a window displaying that
5533buffer. The default value usually makes a separate frame for the
5534buffer using `special-display-frame-alist' to specify the frame
5535parameters. See the definition of `special-display-popup-frame'
5536for how to specify such a function.
5537
5538A buffer is special when its name is either listed in
5539`special-display-buffer-names' or matches a regexp in
5540`special-display-regexps'.
5541
e1c2c6f2
MR
5542The specified function should call `display-buffer-record-window'
5543with corresponding arguments to set up the quit-restore parameter
5544of the window used."
f818cd2a
MR
5545 :type 'function
5546 :group 'frames)
77f1f99c 5547(make-obsolete-variable 'special-display-function 'display-buffer-alist "24.3")
f818cd2a
MR
5548
5549(defcustom same-window-buffer-names nil
5550 "List of names of buffers that should appear in the \"same\" window.
5551`display-buffer' and `pop-to-buffer' show a buffer whose name is
5552on this list in the selected rather than some other window.
5553
5554An element of this list can be a cons cell instead of just a
5555string. In that case, the cell's car must be a string specifying
5556the buffer name. This is for compatibility with
5557`special-display-buffer-names'; the cdr of the cons cell is
5558ignored.
5559
5560See also `same-window-regexps'."
5561 :type '(repeat (string :format "%v"))
5562 :group 'windows)
5563
5564(defcustom same-window-regexps nil
5565 "List of regexps saying which buffers should appear in the \"same\" window.
5566`display-buffer' and `pop-to-buffer' show a buffer whose name
5567matches a regexp on this list in the selected rather than some
5568other window.
5569
5570An element of this list can be a cons cell instead of just a
5571string. In that case, the cell's car must be a regexp matching
5572the buffer name. This is for compatibility with
5573`special-display-regexps'; the cdr of the cons cell is ignored.
9481c002 5574
f818cd2a
MR
5575See also `same-window-buffer-names'."
5576 :type '(repeat (regexp :format "%v"))
5577 :group 'windows)
9481c002 5578
f818cd2a
MR
5579(defun same-window-p (buffer-name)
5580 "Return non-nil if a buffer named BUFFER-NAME would be shown in the \"same\" window.
5581This function returns non-nil if `display-buffer' or
5582`pop-to-buffer' would show a buffer named BUFFER-NAME in the
382c953b 5583selected rather than (as usual) some other window. See
f818cd2a
MR
5584`same-window-buffer-names' and `same-window-regexps'."
5585 (cond
5586 ((not (stringp buffer-name)))
5587 ;; The elements of `same-window-buffer-names' can be buffer
5588 ;; names or cons cells whose cars are buffer names.
5589 ((member buffer-name same-window-buffer-names))
5590 ((assoc buffer-name same-window-buffer-names))
5591 ((catch 'found
5592 (dolist (regexp same-window-regexps)
5593 ;; The elements of `same-window-regexps' can be regexps
5594 ;; or cons cells whose cars are regexps.
5595 (when (or (and (stringp regexp)
cb882333 5596 (string-match-p regexp buffer-name))
f818cd2a
MR
5597 (and (consp regexp) (stringp (car regexp))
5598 (string-match-p (car regexp) buffer-name)))
5599 (throw 'found t)))))))
3c448ab6 5600
d1067961 5601(defcustom pop-up-frames nil
3c448ab6 5602 "Whether `display-buffer' should make a separate frame.
d1f18ec0 5603If nil, never make a separate frame.
3c448ab6
MR
5604If the value is `graphic-only', make a separate frame
5605on graphic displays only.
5606Any other non-nil value means always make a separate frame."
5607 :type '(choice
5608 (const :tag "Never" nil)
5609 (const :tag "On graphic displays only" graphic-only)
5610 (const :tag "Always" t))
f818cd2a 5611 :group 'windows)
3c448ab6 5612
d1067961 5613(defcustom display-buffer-reuse-frames nil
f818cd2a 5614 "Non-nil means `display-buffer' should reuse frames.
3c448ab6
MR
5615If the buffer in question is already displayed in a frame, raise
5616that frame."
5617 :type 'boolean
d1067961 5618 :version "21.1"
f818cd2a 5619 :group 'windows)
24777832
CY
5620
5621(make-obsolete-variable
5622 'display-buffer-reuse-frames
5623 "use a `reusable-frames' alist entry in `display-buffer-alist'."
5624 "24.3")
3c448ab6 5625
4dc2a129
MR
5626(defcustom pop-up-windows t
5627 "Non-nil means `display-buffer' should make a new window."
3c448ab6
MR
5628 :type 'boolean
5629 :group 'windows)
5630
8b10a2d1 5631(defcustom split-window-preferred-function 'split-window-sensibly
f818cd2a 5632 "Function called by `display-buffer' routines to split a window.
8b10a2d1
MR
5633This function is called with a window as single argument and is
5634supposed to split that window and return the new window. If the
5635window can (or shall) not be split, it is supposed to return nil.
5636The default is to call the function `split-window-sensibly' which
5637tries to split the window in a way which seems most suitable.
5638You can customize the options `split-height-threshold' and/or
5639`split-width-threshold' in order to have `split-window-sensibly'
5640prefer either vertical or horizontal splitting.
5641
f818cd2a
MR
5642If you set this to any other function, bear in mind that the
5643`display-buffer' routines may call this function two times. The
5644argument of the first call is the largest window on its frame.
5645If that call fails to return a live window, the function is
5646called again with the least recently used window as argument. If
5647that call fails too, `display-buffer' will use an existing window
5648to display its buffer.
8b10a2d1
MR
5649
5650The window selected at the time `display-buffer' was invoked is
5651still selected when this function is called. Hence you can
5652compare the window argument with the value of `selected-window'
5653if you intend to split the selected window instead or if you do
5654not want to split the selected window."
5655 :type 'function
3c448ab6
MR
5656 :version "23.1"
5657 :group 'windows)
5658
8b10a2d1 5659(defcustom split-height-threshold 80
f818cd2a
MR
5660 "Minimum height for splitting windows sensibly.
5661If this is an integer, `split-window-sensibly' may split a window
8b10a2d1 5662vertically only if it has at least this many lines. If this is
f818cd2a
MR
5663nil, `split-window-sensibly' is not allowed to split a window
5664vertically. If, however, a window is the only window on its
5665frame, `split-window-sensibly' may split it vertically
5666disregarding the value of this variable."
8b10a2d1 5667 :type '(choice (const nil) (integer :tag "lines"))
3c448ab6
MR
5668 :version "23.1"
5669 :group 'windows)
5670
8b10a2d1 5671(defcustom split-width-threshold 160
f818cd2a
MR
5672 "Minimum width for splitting windows sensibly.
5673If this is an integer, `split-window-sensibly' may split a window
8b10a2d1 5674horizontally only if it has at least this many columns. If this
f818cd2a
MR
5675is nil, `split-window-sensibly' is not allowed to split a window
5676horizontally."
8b10a2d1 5677 :type '(choice (const nil) (integer :tag "columns"))
3c448ab6
MR
5678 :version "23.1"
5679 :group 'windows)
9481c002 5680
8b10a2d1
MR
5681(defun window-splittable-p (window &optional horizontal)
5682 "Return non-nil if `split-window-sensibly' may split WINDOW.
5683Optional argument HORIZONTAL nil or omitted means check whether
5684`split-window-sensibly' may split WINDOW vertically. HORIZONTAL
5685non-nil means check whether WINDOW may be split horizontally.
3c448ab6 5686
8b10a2d1 5687WINDOW may be split vertically when the following conditions
3c448ab6 5688hold:
3c448ab6
MR
5689- `window-size-fixed' is either nil or equals `width' for the
5690 buffer of WINDOW.
8b10a2d1 5691- `split-height-threshold' is an integer and WINDOW is at least as
3c448ab6 5692 high as `split-height-threshold'.
3c448ab6
MR
5693- When WINDOW is split evenly, the emanating windows are at least
5694 `window-min-height' lines tall and can accommodate at least one
5695 line plus - if WINDOW has one - a mode line.
5696
8b10a2d1 5697WINDOW may be split horizontally when the following conditions
3c448ab6 5698hold:
3c448ab6
MR
5699- `window-size-fixed' is either nil or equals `height' for the
5700 buffer of WINDOW.
8b10a2d1 5701- `split-width-threshold' is an integer and WINDOW is at least as
3c448ab6 5702 wide as `split-width-threshold'.
3c448ab6
MR
5703- When WINDOW is split evenly, the emanating windows are at least
5704 `window-min-width' or two (whichever is larger) columns wide."
5705 (when (window-live-p window)
5706 (with-current-buffer (window-buffer window)
5707 (if horizontal
5708 ;; A window can be split horizontally when its width is not
5709 ;; fixed, it is at least `split-width-threshold' columns wide
5710 ;; and at least twice as wide as `window-min-width' and 2 (the
5711 ;; latter value is hardcoded).
5712 (and (memq window-size-fixed '(nil height))
5713 ;; Testing `window-full-width-p' here hardly makes any
5714 ;; sense nowadays. This can be done more intuitively by
5715 ;; setting up `split-width-threshold' appropriately.
5716 (numberp split-width-threshold)
5717 (>= (window-width window)
5718 (max split-width-threshold
5719 (* 2 (max window-min-width 2)))))
5720 ;; A window can be split vertically when its height is not
5721 ;; fixed, it is at least `split-height-threshold' lines high,
5722 ;; and it is at least twice as high as `window-min-height' and 2
37269466 5723 ;; if it has a mode line or 1.
3c448ab6
MR
5724 (and (memq window-size-fixed '(nil width))
5725 (numberp split-height-threshold)
5726 (>= (window-height window)
5727 (max split-height-threshold
5728 (* 2 (max window-min-height
5729 (if mode-line-format 2 1))))))))))
5730
2dc2a609 5731(defun split-window-sensibly (&optional window)
8b10a2d1 5732 "Split WINDOW in a way suitable for `display-buffer'.
2dc2a609 5733WINDOW defaults to the currently selected window.
8b10a2d1
MR
5734If `split-height-threshold' specifies an integer, WINDOW is at
5735least `split-height-threshold' lines tall and can be split
5736vertically, split WINDOW into two windows one above the other and
5737return the lower window. Otherwise, if `split-width-threshold'
5738specifies an integer, WINDOW is at least `split-width-threshold'
5739columns wide and can be split horizontally, split WINDOW into two
5740windows side by side and return the window on the right. If this
5741can't be done either and WINDOW is the only window on its frame,
5742try to split WINDOW vertically disregarding any value specified
5743by `split-height-threshold'. If that succeeds, return the lower
5744window. Return nil otherwise.
5745
5746By default `display-buffer' routines call this function to split
5747the largest or least recently used window. To change the default
5748customize the option `split-window-preferred-function'.
5749
5750You can enforce this function to not split WINDOW horizontally,
382c953b 5751by setting (or binding) the variable `split-width-threshold' to
8b10a2d1
MR
5752nil. If, in addition, you set `split-height-threshold' to zero,
5753chances increase that this function does split WINDOW vertically.
5754
382c953b 5755In order to not split WINDOW vertically, set (or bind) the
8b10a2d1
MR
5756variable `split-height-threshold' to nil. Additionally, you can
5757set `split-width-threshold' to zero to make a horizontal split
5758more likely to occur.
5759
5760Have a look at the function `window-splittable-p' if you want to
5761know how `split-window-sensibly' determines whether WINDOW can be
5762split."
2dc2a609
TH
5763 (let ((window (or window (selected-window))))
5764 (or (and (window-splittable-p window)
5765 ;; Split window vertically.
5766 (with-selected-window window
5767 (split-window-below)))
5768 (and (window-splittable-p window t)
5769 ;; Split window horizontally.
5770 (with-selected-window window
5771 (split-window-right)))
5772 (and (eq window (frame-root-window (window-frame window)))
5773 (not (window-minibuffer-p window))
5774 ;; If WINDOW is the only window on its frame and is not the
5775 ;; minibuffer window, try to split it vertically disregarding
5776 ;; the value of `split-height-threshold'.
5777 (let ((split-height-threshold 0))
5778 (when (window-splittable-p window)
5779 (with-selected-window window
5780 (split-window-below))))))))
9481c002 5781
5938d519 5782(defun window--try-to-split-window (window &optional alist)
f818cd2a
MR
5783 "Try to split WINDOW.
5784Return value returned by `split-window-preferred-function' if it
5785represents a live window, nil otherwise."
5786 (and (window-live-p window)
5787 (not (frame-parameter (window-frame window) 'unsplittable))
8e17c9ba
MR
5788 (let* ((window-combination-limit
5789 ;; When `window-combination-limit' equals
5938d519
MR
5790 ;; `display-buffer' or equals `resize-window' and a
5791 ;; `window-height' or `window-width' alist entry are
5792 ;; present, bind it to t so resizing steals space
5793 ;; preferably from the window that was split.
5794 (if (or (eq window-combination-limit 'display-buffer)
5795 (and (eq window-combination-limit 'window-size)
5796 (or (cdr (assq 'window-height alist))
5797 (cdr (assq 'window-width alist)))))
8e17c9ba
MR
5798 t
5799 window-combination-limit))
5800 (new-window
5801 ;; Since `split-window-preferred-function' might
5802 ;; throw an error use `condition-case'.
5803 (condition-case nil
5804 (funcall split-window-preferred-function window)
5805 (error nil))))
f818cd2a
MR
5806 (and (window-live-p new-window) new-window))))
5807
5808(defun window--frame-usable-p (frame)
5809 "Return FRAME if it can be used to display a buffer."
5810 (when (frame-live-p frame)
5811 (let ((window (frame-root-window frame)))
5812 ;; `frame-root-window' may be an internal window which is considered
5813 ;; "dead" by `window-live-p'. Hence if `window' is not live we
5814 ;; implicitly know that `frame' has a visible window we can use.
5815 (unless (and (window-live-p window)
5816 (or (window-minibuffer-p window)
5817 ;; If the window is soft-dedicated, the frame is usable.
5818 ;; Actually, even if the window is really dedicated,
5819 ;; the frame is still usable by splitting it.
5820 ;; At least Emacs-22 allowed it, and it is desirable
5821 ;; when displaying same-frame windows.
5822 nil ; (eq t (window-dedicated-p window))
5823 ))
5824 frame))))
5825
5826(defcustom even-window-heights t
5827 "If non-nil `display-buffer' will try to even window heights.
5828Otherwise `display-buffer' will leave the window configuration
5829alone. Heights are evened only when `display-buffer' chooses a
5830window that appears above or below the selected window."
5831 :type 'boolean
5832 :group 'windows)
5833
5834(defun window--even-window-heights (window)
5835 "Even heights of WINDOW and selected window.
5836Do this only if these windows are vertically adjacent to each
5837other, `even-window-heights' is non-nil, and the selected window
5838is higher than WINDOW."
5839 (when (and even-window-heights
9aba119d
MR
5840 ;; Even iff WINDOW forms a vertical combination with the
5841 ;; selected window, and WINDOW's height exceeds that of the
5842 ;; selected window, see also bug#11880.
5843 (window-combined-p window)
5844 (= (window-child-count (window-parent window)) 2)
5845 (eq (window-parent) (window-parent window))
5846 (> (window-total-height) (window-total-height window)))
5847 ;; Don't throw an error if we can't even window heights for
5848 ;; whatever reason.
5849 (condition-case nil
5850 (enlarge-window
5851 (/ (- (window-total-height window) (window-total-height)) 2))
5852 (error nil))))
f818cd2a 5853
5938d519 5854(defun window--display-buffer (buffer window type &optional alist dedicated)
0ff7851c 5855 "Display BUFFER in WINDOW.
51a5f9d8 5856TYPE must be one of the symbols `reuse', `window' or `frame' and
0ff7851c
MR
5857is passed unaltered to `display-buffer-record-window'. ALIST is
5858the alist argument of `display-buffer'. Set `window-dedicated-p'
5859to DEDICATED if non-nil. Return WINDOW if BUFFER and WINDOW are
5860live."
f818cd2a 5861 (when (and (buffer-live-p buffer) (window-live-p window))
caceae25 5862 (display-buffer-record-window type window buffer)
90749b53
CY
5863 (unless (eq buffer (window-buffer window))
5864 (set-window-dedicated-p window nil)
90749b53
CY
5865 (set-window-buffer window buffer)
5866 (when dedicated
5867 (set-window-dedicated-p window dedicated))
5868 (when (memq type '(window frame))
5869 (set-window-prev-buffers window nil)))
2d6b6005 5870 (let ((parameter (window-parameter window 'quit-restore))
5938d519 5871 (height (cdr (assq 'window-height alist)))
880e6158
MR
5872 (width (cdr (assq 'window-width alist)))
5873 (size (cdr (assq 'window-size alist))))
5874 (cond
5875 ((or (eq type 'frame)
5876 (and (eq (car parameter) 'same)
5877 (eq (nth 1 parameter) 'frame)))
5878 ;; Adjust size of frame if asked for.
5879 (cond
5880 ((not size))
5881 ((consp size)
5882 (let ((width (car size))
5883 (height (cdr size))
2d6b6005 5884 (frame (window-frame window)))
880e6158
MR
5885 (when (and (numberp width) (numberp height))
5886 (set-frame-height
5887 frame (+ (frame-height frame)
5888 (- height (window-total-height window))))
5889 (set-frame-width
5890 frame (+ (frame-width frame)
5891 (- width (window-total-width window)))))))
5892 ((functionp size)
5893 (ignore-errors (funcall size window)))))
5894 ((or (eq type 'window)
5895 (and (eq (car parameter) 'same)
5896 (eq (nth 1 parameter) 'window)))
0ff7851c 5897 ;; Adjust height of window if asked for.
5938d519
MR
5898 (cond
5899 ((not height))
5900 ((numberp height)
5901 (let* ((new-height
5902 (if (integerp height)
5903 height
5904 (round
880e6158 5905 (* (window-total-height (frame-root-window window))
5938d519 5906 height))))
880e6158
MR
5907 (delta (- new-height (window-total-height window))))
5908 (when (and (window--resizable-p window delta nil 'safe)
0ff7851c
MR
5909 (window-combined-p window))
5910 (window-resize window delta nil 'safe))))
5938d519
MR
5911 ((functionp height)
5912 (ignore-errors (funcall height window))))
0ff7851c 5913 ;; Adjust width of window if asked for.
5938d519
MR
5914 (cond
5915 ((not width))
5916 ((numberp width)
5917 (let* ((new-width
5918 (if (integerp width)
5919 width
5920 (round
880e6158 5921 (* (window-total-width (frame-root-window window))
5938d519 5922 width))))
880e6158
MR
5923 (delta (- new-width (window-total-width window))))
5924 (when (and (window--resizable-p window delta t 'safe)
0ff7851c
MR
5925 (window-combined-p window t))
5926 (window-resize window delta t 'safe))))
5938d519 5927 ((functionp width)
880e6158 5928 (ignore-errors (funcall width window)))))))
0ff7851c 5929
90749b53
CY
5930 window))
5931
5932(defun window--maybe-raise-frame (frame)
5933 (let ((visible (frame-visible-p frame)))
5934 (unless (or (not visible)
5935 ;; Assume the selected frame is already visible enough.
5936 (eq frame (selected-frame))
5937 ;; Assume the frame from which we invoked the
5938 ;; minibuffer is visible.
5939 (and (minibuffer-window-active-p (selected-window))
5940 (eq frame (window-frame (minibuffer-selected-window)))))
5941 (raise-frame frame))))
f818cd2a 5942
24510c22
SM
5943;; FIXME: Not implemented.
5944;; FIXME: By the way, there could be more levels of dedication:
5945;; - `barely' dedicated doesn't prevent reuse of the window, only records that
5946;; the window hasn't been used for something else yet.
5947;; - `softly' dedicated only allows reuse when asked explicitly.
5948;; - `strongly' never allows reuse.
f818cd2a
MR
5949(defvar display-buffer-mark-dedicated nil
5950 "If non-nil, `display-buffer' marks the windows it creates as dedicated.
5951The actual non-nil value of this variable will be copied to the
5952`window-dedicated-p' flag.")
5953
fa5660f9
CY
5954(defconst display-buffer--action-function-custom-type
5955 '(choice :tag "Function"
5956 (const :tag "--" ignore) ; default for insertion
fa5660f9 5957 (const display-buffer-reuse-window)
d9558cad 5958 (const display-buffer-pop-up-window)
fa5660f9
CY
5959 (const display-buffer-same-window)
5960 (const display-buffer-pop-up-frame)
880e6158 5961 (const display-buffer-in-previous-window)
fa5660f9
CY
5962 (const display-buffer-use-some-window)
5963 (function :tag "Other function"))
5964 "Custom type for `display-buffer' action functions.")
5965
5966(defconst display-buffer--action-custom-type
5967 `(cons :tag "Action"
5968 (choice :tag "Action functions"
5969 ,display-buffer--action-function-custom-type
5970 (repeat
5971 :tag "List of functions"
5972 ,display-buffer--action-function-custom-type))
5973 (alist :tag "Action arguments"
5974 :key-type symbol
5975 :value-type (sexp :tag "Value")))
5976 "Custom type for `display-buffer' actions.")
5977
cbb0f9ab
CY
5978(defvar display-buffer-overriding-action '(nil . nil)
5979 "Overriding action to perform to display a buffer.
5980It should be a cons cell (FUNCTION . ALIST), where FUNCTION is a
382c953b 5981function or a list of functions. Each function should accept two
cbb0f9ab
CY
5982arguments: a buffer to display and an alist similar to ALIST.
5983See `display-buffer' for details.")
5984(put 'display-buffer-overriding-action 'risky-local-variable t)
5985
fa5660f9 5986(defcustom display-buffer-alist nil
89894cd8
CY
5987 "Alist of conditional actions for `display-buffer'.
5988This is a list of elements (CONDITION . ACTION), where:
5989
0ff7851c
MR
5990 CONDITION is either a regexp matching buffer names, or a
5991 function that takes two arguments - a buffer name and the
5992 ACTION argument of `display-buffer' - and returns a boolean.
89894cd8 5993
8319e0bf
CY
5994 ACTION is a cons cell (FUNCTION . ALIST), where FUNCTION is a
5995 function or a list of functions. Each such function should
382c953b 5996 accept two arguments: a buffer to display and an alist of the
f0cfa5fe 5997 same form as ALIST. See `display-buffer' for details.
0ff7851c
MR
5998
5999`display-buffer' scans this alist until it either finds a
6000matching regular expression or the function specified by a
9139632a
JL
6001condition returns non-nil. In any of these cases, it adds the
6002associated action to the list of actions it will try."
fa5660f9
CY
6003 :type `(alist :key-type
6004 (choice :tag "Condition"
6005 regexp
6006 (function :tag "Matcher function"))
6007 :value-type ,display-buffer--action-custom-type)
6008 :risky t
6009 :version "24.1"
6010 :group 'windows)
89894cd8 6011
cbb0f9ab
CY
6012(defcustom display-buffer-base-action '(nil . nil)
6013 "User-specified default action for `display-buffer'.
8319e0bf 6014It should be a cons cell (FUNCTION . ALIST), where FUNCTION is a
382c953b 6015function or a list of functions. Each function should accept two
fa5660f9
CY
6016arguments: a buffer to display and an alist similar to ALIST.
6017See `display-buffer' for details."
6018 :type display-buffer--action-custom-type
6019 :risky t
6020 :version "24.1"
6021 :group 'windows)
89894cd8 6022
cbb0f9ab 6023(defconst display-buffer-fallback-action
0a9f9ab5 6024 '((display-buffer--maybe-same-window ;FIXME: why isn't this redundant?
cbb0f9ab 6025 display-buffer-reuse-window
cbb0f9ab 6026 display-buffer--maybe-pop-up-frame-or-window
880e6158 6027 display-buffer-in-previous-window
cbb0f9ab
CY
6028 display-buffer-use-some-window
6029 ;; If all else fails, pop up a new frame.
6030 display-buffer-pop-up-frame))
6031 "Default fallback action for `display-buffer'.
6032This is the action used by `display-buffer' if no other actions
6033specified, e.g. by the user options `display-buffer-alist' or
6034`display-buffer-base-action'. See `display-buffer'.")
6035(put 'display-buffer-fallback-action 'risky-local-variable t)
f818cd2a 6036
0ff7851c
MR
6037(defun display-buffer-assq-regexp (buffer-name alist action)
6038 "Retrieve ALIST entry corresponding to BUFFER-NAME.
6039ACTION is the action argument passed to `display-buffer'."
f818cd2a
MR
6040 (catch 'match
6041 (dolist (entry alist)
cb882333 6042 (let ((key (car entry)))
f818cd2a
MR
6043 (when (or (and (stringp key)
6044 (string-match-p key buffer-name))
0ff7851c
MR
6045 (and (functionp key)
6046 (funcall key buffer-name action)))
f818cd2a
MR
6047 (throw 'match (cdr entry)))))))
6048
8319e0bf
CY
6049(defvar display-buffer--same-window-action
6050 '(display-buffer-same-window
6051 (inhibit-same-window . nil))
6052 "A `display-buffer' action for displaying in the same window.")
6053(put 'display-buffer--same-window-action 'risky-local-variable t)
6054
6055(defvar display-buffer--other-frame-action
6056 '((display-buffer-reuse-window
8319e0bf
CY
6057 display-buffer-pop-up-frame)
6058 (reusable-frames . 0)
6059 (inhibit-same-window . t))
6060 "A `display-buffer' action for displaying in another frame.")
6061(put 'display-buffer--other-frame-action 'risky-local-variable t)
6062
d45ba96b 6063(defun display-buffer (buffer-or-name &optional action frame)
3199b96f 6064 "Display BUFFER-OR-NAME in some window, without selecting it.
89894cd8
CY
6065BUFFER-OR-NAME must be a buffer or the name of an existing
6066buffer. Return the window chosen for displaying BUFFER-OR-NAME,
6067or nil if no such window is found.
6068
7b9abf24
EZ
6069Optional argument ACTION, if non-nil, should specify a display
6070action. Its form is described below.
6071
6072Optional argument FRAME, if non-nil, acts like an additional
6073ALIST entry (reusable-frames . FRAME) to the action list of ACTION,
6074specifying the frame(s) to search for a window that is already
6075displaying the buffer. See `display-buffer-reuse-window'
6076
6077If ACTION is non-nil, it should have the form (FUNCTION . ALIST),
6078where FUNCTION is either a function or a list of functions, and
d4bd55e7
CY
6079ALIST is an arbitrary association list (alist).
6080
6081Each such FUNCTION should accept two arguments: the buffer to
f130cb76
LL
6082display and an alist. Based on those arguments, it should
6083display the buffer and return the window. If the caller is
9139632a
JL
6084prepared to handle the case of not displaying the buffer
6085and returning nil from `display-buffer' it should pass
6086\(allow-no-window . t) as an element of the ALIST.
89894cd8 6087
cbb0f9ab 6088The `display-buffer' function builds a function list and an alist
d4bd55e7
CY
6089by combining the functions and alists specified in
6090`display-buffer-overriding-action', `display-buffer-alist', the
6091ACTION argument, `display-buffer-base-action', and
6092`display-buffer-fallback-action' (in order). Then it calls each
6093function in the combined function list in turn, passing the
cbb0f9ab
CY
6094buffer as the first argument and the combined alist as the second
6095argument, until one of the functions returns non-nil.
8319e0bf 6096
7b9abf24
EZ
6097If ACTION is nil, the function list and the alist are built using
6098only the other variables mentioned above.
6099
8319e0bf
CY
6100Available action functions include:
6101 `display-buffer-same-window'
8319e0bf
CY
6102 `display-buffer-reuse-window'
6103 `display-buffer-pop-up-frame'
6104 `display-buffer-pop-up-window'
880e6158 6105 `display-buffer-in-previous-window'
8319e0bf
CY
6106 `display-buffer-use-some-window'
6107
6108Recognized alist entries include:
6109
6110 `inhibit-same-window' -- A non-nil value prevents the same
6111 window from being used for display.
6112
90749b53
CY
6113 `inhibit-switch-frame' -- A non-nil value prevents any other
6114 frame from being raised or selected,
6115 even if the window is displayed there.
6116
8319e0bf
CY
6117 `reusable-frames' -- Value specifies frame(s) to search for a
6118 window that already displays the buffer.
6119 See `display-buffer-reuse-window'.
2a7bdc1a 6120
d97af5a0
CY
6121 `pop-up-frame-parameters' -- Value specifies an alist of frame
6122 parameters to give a new frame, if
6123 one is created.
6124
df171c23
MR
6125 `window-height' -- Value specifies either an integer (the number
6126 of lines of a new window), a floating point number (the
6127 fraction of a new window with respect to the height of the
6128 frame's root window) or a function to be called with one
6129 argument - a new window. The function is supposed to adjust
6130 the height of the window; its return value is ignored.
6131 Suitable functions are `shrink-window-if-larger-than-buffer'
6132 and `fit-window-to-buffer'.
6133
6134 `window-width' -- Value specifies either an integer (the number
6135 of columns of a new window), a floating point number (the
6136 fraction of a new window with respect to the width of the
6137 frame's root window) or a function to be called with one
6138 argument - a new window. The function is supposed to adjust
6139 the width of the window; its return value is ignored.
6140
9139632a
JL
6141 `allow-no-window' -- A non-nil value indicates readiness for the case
6142 of not displaying the buffer and FUNCTION can safely return
6143 a non-window value to suppress displaying.
6144
2a7bdc1a
CY
6145The ACTION argument to `display-buffer' can also have a non-nil
6146and non-list value. This means to display the buffer in a window
6147other than the selected one, even if it is already displayed in
6148the selected window. If called interactively with a prefix
7b9abf24 6149argument, ACTION is t."
c3313451
CY
6150 (interactive (list (read-buffer "Display buffer: " (other-buffer))
6151 (if current-prefix-arg t)))
d45ba96b
MR
6152 (let ((buffer (if (bufferp buffer-or-name)
6153 buffer-or-name
6154 (get-buffer buffer-or-name)))
f47ad11b
MR
6155 ;; Make sure that when we split windows the old window keeps
6156 ;; point, bug#14829.
6157 (split-window-keep-point t)
89894cd8
CY
6158 ;; Handle the old form of the first argument.
6159 (inhibit-same-window (and action (not (listp action)))))
6160 (unless (listp action) (setq action nil))
6161 (if display-buffer-function
6162 ;; If `display-buffer-function' is defined, let it do the job.
6163 (funcall display-buffer-function buffer inhibit-same-window)
6164 ;; Otherwise, use the defined actions.
6165 (let* ((user-action
0ff7851c
MR
6166 (display-buffer-assq-regexp
6167 (buffer-name buffer) display-buffer-alist action))
0a9f9ab5 6168 (special-action (display-buffer--special-action buffer))
89894cd8
CY
6169 ;; Extra actions from the arguments to this function:
6170 (extra-action
6171 (cons nil (append (if inhibit-same-window
6172 '((inhibit-same-window . t)))
6173 (if frame
8319e0bf 6174 `((reusable-frames . ,frame))))))
89894cd8
CY
6175 ;; Construct action function list and action alist.
6176 (actions (list display-buffer-overriding-action
0a9f9ab5 6177 user-action special-action action extra-action
cbb0f9ab
CY
6178 display-buffer-base-action
6179 display-buffer-fallback-action))
89894cd8
CY
6180 (functions (apply 'append
6181 (mapcar (lambda (x)
6182 (setq x (car x))
c3313451 6183 (if (functionp x) (list x) x))
89894cd8
CY
6184 actions)))
6185 (alist (apply 'append (mapcar 'cdr actions)))
6186 window)
6187 (unless (buffer-live-p buffer)
6188 (error "Invalid buffer"))
6189 (while (and functions (not window))
6190 (setq window (funcall (car functions) buffer alist)
6191 functions (cdr functions)))
f130cb76 6192 (and (windowp window) window)))))
f818cd2a
MR
6193
6194(defun display-buffer-other-frame (buffer)
e6f759f9 6195 "Display buffer BUFFER preferably in another frame.
f818cd2a
MR
6196This uses the function `display-buffer' as a subroutine; see
6197its documentation for additional customization information."
6198 (interactive "BDisplay buffer in other frame: ")
8319e0bf 6199 (display-buffer buffer display-buffer--other-frame-action t))
f818cd2a 6200
89894cd8 6201;;; `display-buffer' action functions:
437014c8 6202
89894cd8 6203(defun display-buffer-same-window (buffer alist)
8319e0bf
CY
6204 "Display BUFFER in the selected window.
6205This fails if ALIST has a non-nil `inhibit-same-window' entry, or
6206if the selected window is a minibuffer window or is dedicated to
6207another buffer; in that case, return nil. Otherwise, return the
6208selected window."
89894cd8
CY
6209 (unless (or (cdr (assq 'inhibit-same-window alist))
6210 (window-minibuffer-p)
6211 (window-dedicated-p))
5938d519 6212 (window--display-buffer buffer (selected-window) 'reuse alist)))
89894cd8 6213
0d3ff375 6214(defun display-buffer--maybe-same-window (buffer alist)
8319e0bf
CY
6215 "Conditionally display BUFFER in the selected window.
6216If `same-window-p' returns non-nil for BUFFER's name, call
6217`display-buffer-same-window' and return its value. Otherwise,
6218return nil."
89894cd8
CY
6219 (and (same-window-p (buffer-name buffer))
6220 (display-buffer-same-window buffer alist)))
6221
6222(defun display-buffer-reuse-window (buffer alist)
6223 "Return a window that is already displaying BUFFER.
8319e0bf
CY
6224Return nil if no usable window is found.
6225
6226If ALIST has a non-nil `inhibit-same-window' entry, the selected
6227window is not eligible for reuse.
6228
6229If ALIST contains a `reusable-frames' entry, its value determines
6230which frames to search for a reusable window:
6231 nil -- the selected frame (actually the last non-minibuffer frame)
6232 A frame -- just that frame
6233 `visible' -- all visible frames
6234 0 -- all frames on the current terminal
6235 t -- all frames.
6236
6237If ALIST contains no `reusable-frames' entry, search just the
6238selected frame if `display-buffer-reuse-frames' and
6239`pop-up-frames' are both nil; search all frames on the current
90749b53
CY
6240terminal if either of those variables is non-nil.
6241
6242If ALIST has a non-nil `inhibit-switch-frame' entry, then in the
6243event that a window on another frame is chosen, avoid raising
6244that frame."
8319e0bf
CY
6245 (let* ((alist-entry (assq 'reusable-frames alist))
6246 (frames (cond (alist-entry (cdr alist-entry))
6247 ((if (eq pop-up-frames 'graphic-only)
6248 (display-graphic-p)
6249 pop-up-frames)
6250 0)
6251 (display-buffer-reuse-frames 0)
6252 (t (last-nonminibuffer-frame))))
6253 (window (if (and (eq buffer (window-buffer))
6254 (not (cdr (assq 'inhibit-same-window alist))))
6255 (selected-window)
6256 (car (delq (selected-window)
6257 (get-buffer-window-list buffer 'nomini
6258 frames))))))
90749b53 6259 (when (window-live-p window)
5938d519 6260 (prog1 (window--display-buffer buffer window 'reuse alist)
90749b53
CY
6261 (unless (cdr (assq 'inhibit-switch-frame alist))
6262 (window--maybe-raise-frame (window-frame window)))))))
89894cd8 6263
0a9f9ab5 6264(defun display-buffer--special-action (buffer)
4ad3bc2a
CY
6265 "Return special display action for BUFFER, if any.
6266If `special-display-p' returns non-nil for BUFFER, return an
6267appropriate display action involving `special-display-function'.
6268See `display-buffer' for the format of display actions."
8319e0bf
CY
6269 (and special-display-function
6270 ;; `special-display-p' returns either t or a list of frame
6271 ;; parameters to pass to `special-display-function'.
6272 (let ((pars (special-display-p (buffer-name buffer))))
6273 (when pars
0a9f9ab5
SM
6274 (list (list #'display-buffer-reuse-window
6275 `(lambda (buffer _alist)
6276 (funcall special-display-function
6277 buffer ',(if (listp pars) pars)))))))))
8319e0bf 6278
90749b53 6279(defun display-buffer-pop-up-frame (buffer alist)
89894cd8 6280 "Display BUFFER in a new frame.
8319e0bf 6281This works by calling `pop-up-frame-function'. If successful,
90749b53
CY
6282return the window used; otherwise return nil.
6283
6284If ALIST has a non-nil `inhibit-switch-frame' entry, avoid
d97af5a0
CY
6285raising the new frame.
6286
6287If ALIST has a non-nil `pop-up-frame-parameters' entry, the
6288corresponding value is an alist of frame parameters to give the
6289new frame."
6290 (let* ((params (cdr (assq 'pop-up-frame-parameters alist)))
6291 (pop-up-frame-alist (append params pop-up-frame-alist))
6292 (fun pop-up-frame-function)
6293 frame window)
89894cd8 6294 (when (and fun
7fd5f65e
MR
6295 ;; Make BUFFER current so `make-frame' will use it as the
6296 ;; new frame's buffer (Bug#15133).
6297 (with-current-buffer buffer
6298 (setq frame (funcall fun)))
89894cd8 6299 (setq window (frame-selected-window frame)))
5938d519
MR
6300 (prog1 (window--display-buffer
6301 buffer window 'frame alist display-buffer-mark-dedicated)
90749b53
CY
6302 (unless (cdr (assq 'inhibit-switch-frame alist))
6303 (window--maybe-raise-frame frame))))))
89894cd8 6304
90749b53 6305(defun display-buffer-pop-up-window (buffer alist)
89894cd8
CY
6306 "Display BUFFER by popping up a new window.
6307The new window is created on the selected frame, or in
6308`last-nonminibuffer-frame' if no windows can be created there.
90749b53
CY
6309If successful, return the new window; otherwise return nil.
6310
6311If ALIST has a non-nil `inhibit-switch-frame' entry, then in the
6312event that the new window is created on another frame, avoid
6313raising the frame."
89894cd8
CY
6314 (let ((frame (or (window--frame-usable-p (selected-frame))
6315 (window--frame-usable-p (last-nonminibuffer-frame))))
6316 window)
6317 (when (and (or (not (frame-parameter frame 'unsplittable))
6318 ;; If the selected frame cannot be split, look at
6319 ;; `last-nonminibuffer-frame'.
6320 (and (eq frame (selected-frame))
6321 (setq frame (last-nonminibuffer-frame))
6322 (window--frame-usable-p frame)
6323 (not (frame-parameter frame 'unsplittable))))
6324 ;; Attempt to split largest or least recently used window.
6325 (setq window (or (window--try-to-split-window
5938d519 6326 (get-largest-window frame t) alist)
89894cd8 6327 (window--try-to-split-window
5938d519
MR
6328 (get-lru-window frame t) alist))))
6329 (prog1 (window--display-buffer
6330 buffer window 'window alist display-buffer-mark-dedicated)
90749b53
CY
6331 (unless (cdr (assq 'inhibit-switch-frame alist))
6332 (window--maybe-raise-frame (window-frame window)))))))
89894cd8 6333
8319e0bf
CY
6334(defun display-buffer--maybe-pop-up-frame-or-window (buffer alist)
6335 "Try displaying BUFFER based on `pop-up-frames' or `pop-up-windows'.
6336
6337If `pop-up-frames' is non-nil (and not `graphic-only' on a
6338text-only terminal), try with `display-buffer-pop-up-frame'.
6339
6340If that cannot be done, and `pop-up-windows' is non-nil, try
6341again with `display-buffer-pop-up-window'."
6342 (or (and (if (eq pop-up-frames 'graphic-only)
6343 (display-graphic-p)
6344 pop-up-frames)
6345 (display-buffer-pop-up-frame buffer alist))
6346 (and pop-up-windows
6347 (display-buffer-pop-up-window buffer alist))))
89894cd8 6348
5938d519 6349(defun display-buffer-below-selected (buffer alist)
78dd6ab1
MR
6350 "Try displaying BUFFER in a window below the selected window.
6351This either splits the selected window or reuses the window below
6352the selected one."
6353 (let (window)
6354 (or (and (not (frame-parameter nil 'unsplittable))
d144ef06
MR
6355 (let ((split-height-threshold 0)
6356 split-width-threshold)
f93cc74f 6357 (setq window (window--try-to-split-window (selected-window) alist)))
78dd6ab1 6358 (window--display-buffer
5938d519 6359 buffer window 'window alist display-buffer-mark-dedicated))
78dd6ab1
MR
6360 (and (setq window (window-in-direction 'below))
6361 (not (window-dedicated-p window))
6362 (window--display-buffer
5938d519 6363 buffer window 'reuse alist display-buffer-mark-dedicated)))))
8e17c9ba 6364
5938d519 6365(defun display-buffer-at-bottom (buffer alist)
735135f9 6366 "Try displaying BUFFER in a window at the bottom of the selected frame.
8e17c9ba
MR
6367This either splits the window at the bottom of the frame or the
6368frame's root window, or reuses an existing window at the bottom
6369of the selected frame."
6370 (let (bottom-window window)
c660a885
MR
6371 (walk-window-tree
6372 (lambda (window) (setq bottom-window window)) nil nil 'nomini)
8e17c9ba 6373 (or (and (not (frame-parameter nil 'unsplittable))
00139435
MR
6374 (let (split-width-threshold)
6375 (setq window (window--try-to-split-window bottom-window alist)))
8e17c9ba 6376 (window--display-buffer
5938d519 6377 buffer window 'window alist display-buffer-mark-dedicated))
8e17c9ba
MR
6378 (and (not (frame-parameter nil 'unsplittable))
6379 (setq window
6380 (condition-case nil
c660a885 6381 (split-window (window--major-non-side-window))
8e17c9ba
MR
6382 (error nil)))
6383 (window--display-buffer
5938d519 6384 buffer window 'window alist display-buffer-mark-dedicated))
8e17c9ba
MR
6385 (and (setq window bottom-window)
6386 (not (window-dedicated-p window))
6387 (window--display-buffer
5938d519 6388 buffer window 'reuse alist display-buffer-mark-dedicated)))))
78dd6ab1 6389
fa2bcf43
MR
6390(defun display-buffer-in-previous-window (buffer alist)
6391 "Display BUFFER in a window previously showing it.
6392If ALIST has a non-nil `inhibit-same-window' entry, the selected
6393window is not eligible for reuse.
6394
6395If ALIST contains a `reusable-frames' entry, its value determines
6396which frames to search for a reusable window:
6397 nil -- the selected frame (actually the last non-minibuffer frame)
6398 A frame -- just that frame
6399 `visible' -- all visible frames
6400 0 -- all frames on the current terminal
6401 t -- all frames.
6402
6403If ALIST contains no `reusable-frames' entry, search just the
6404selected frame if `display-buffer-reuse-frames' and
6405`pop-up-frames' are both nil; search all frames on the current
6406terminal if either of those variables is non-nil.
6407
6408If ALIST has a `previous-window' entry, the window specified by
6409that entry will override any other window found by the methods
6410above, even if that window never showed BUFFER before."
6411 (let* ((alist-entry (assq 'reusable-frames alist))
6412 (inhibit-same-window
6413 (cdr (assq 'inhibit-same-window alist)))
6414 (frames (cond
6415 (alist-entry (cdr alist-entry))
6416 ((if (eq pop-up-frames 'graphic-only)
6417 (display-graphic-p)
6418 pop-up-frames)
6419 0)
6420 (display-buffer-reuse-frames 0)
6421 (t (last-nonminibuffer-frame))))
9d3aa82c 6422 best-window second-best-window window)
fa2bcf43
MR
6423 ;; Scan windows whether they have shown the buffer recently.
6424 (catch 'best
6425 (dolist (window (window-list-1 (frame-first-window) 'nomini frames))
6426 (when (and (assq buffer (window-prev-buffers window))
6427 (not (window-dedicated-p window)))
6428 (if (eq window (selected-window))
6429 (unless inhibit-same-window
6430 (setq second-best-window window))
6431 (setq best-window window)
6432 (throw 'best t)))))
6433 ;; When ALIST has a `previous-window' entry, that entry may override
6434 ;; anything we found so far.
6435 (when (and (setq window (cdr (assq 'previous-window alist)))
6436 (window-live-p window)
6437 (not (window-dedicated-p window)))
6438 (if (eq window (selected-window))
6439 (unless inhibit-same-window
6440 (setq second-best-window window))
6441 (setq best-window window)))
6442 ;; Return best or second best window found.
6443 (when (setq window (or best-window second-best-window))
5938d519 6444 (window--display-buffer buffer window 'reuse alist))))
fa2bcf43 6445
89894cd8
CY
6446(defun display-buffer-use-some-window (buffer alist)
6447 "Display BUFFER in an existing window.
6448Search for a usable window, set that window to the buffer, and
90749b53
CY
6449return the window. If no suitable window is found, return nil.
6450
6451If ALIST has a non-nil `inhibit-switch-frame' entry, then in the
6452event that a window in another frame is chosen, avoid raising
6453that frame."
89894cd8 6454 (let* ((not-this-window (cdr (assq 'inhibit-same-window alist)))
89894cd8
CY
6455 (frame (or (window--frame-usable-p (selected-frame))
6456 (window--frame-usable-p (last-nonminibuffer-frame))))
51a5f9d8
MR
6457 (window
6458 ;; Reuse an existing window.
6459 (or (get-lru-window frame nil not-this-window)
6460 (let ((window (get-buffer-window buffer 'visible)))
6461 (unless (and not-this-window
6462 (eq window (selected-window)))
6463 window))
6464 (get-largest-window 'visible nil not-this-window)
6465 (let ((window (get-buffer-window buffer 0)))
6466 (unless (and not-this-window
6467 (eq window (selected-window)))
6468 window))
880e6158
MR
6469 (get-largest-window 0 nil not-this-window)))
6470 (quit-restore (and (window-live-p window)
6471 (window-parameter window 'quit-restore)))
6472 (quad (nth 1 quit-restore)))
90749b53 6473 (when (window-live-p window)
880e6158
MR
6474 ;; If the window was used by `display-buffer' before, try to
6475 ;; resize it to its old height but don't signal an error.
6476 (when (and (listp quad)
6477 (integerp (nth 3 quad))
6478 (/= (nth 3 quad) (window-total-height window)))
6479 (condition-case nil
6480 (window-resize window (- (nth 3 quad) (window-total-height window)))
6481 (error nil)))
6482
9aba119d 6483 (prog1
5938d519 6484 (window--display-buffer buffer window 'reuse alist)
9aba119d 6485 (window--even-window-heights window)
90749b53
CY
6486 (unless (cdr (assq 'inhibit-switch-frame alist))
6487 (window--maybe-raise-frame (window-frame window)))))))
437014c8 6488
2d6b6005 6489(defun display-buffer-no-window (_buffer alist)
9139632a
JL
6490 "Display BUFFER in no window.
6491If ALIST has a non-nil `allow-no-window' entry, then don't display
6492a window at all. This makes possible to override the default action
6493and avoid displaying the buffer. It is assumed that when the caller
6494specifies a non-nil `allow-no-window' then it can handle a nil value
6495returned from `display-buffer' in this case."
6496 (when (cdr (assq 'allow-no-window alist))
6497 'fail))
6498
437014c8 6499;;; Display + selection commands:
c3313451
CY
6500(defun pop-to-buffer (buffer &optional action norecord)
6501 "Select buffer BUFFER in some window, preferably a different one.
6502BUFFER may be a buffer, a string (a buffer name), or nil. If it
6503is a string not naming an existent buffer, create a buffer with
6504that name. If BUFFER is nil, choose some other buffer. Return
6505the buffer.
6506
6507This uses `display-buffer' as a subroutine. The optional ACTION
6508argument is passed to `display-buffer' as its ACTION argument.
6509See `display-buffer' for more information. ACTION is t if called
6510interactively with a prefix argument, which means to pop to a
6511window other than the selected one even if the buffer is already
6512displayed in the selected window.
6513
6514If the window to show BUFFER is not on the selected
f818cd2a
MR
6515frame, raise that window's frame and give it input focus.
6516
f818cd2a
MR
6517Optional third arg NORECORD non-nil means do not put this buffer
6518at the front of the list of recently selected ones."
c3313451
CY
6519 (interactive (list (read-buffer "Pop to buffer: " (other-buffer))
6520 (if current-prefix-arg t)))
8319e0bf 6521 (setq buffer (window-normalize-buffer-to-switch-to buffer))
c660a885
MR
6522 ;; This should be done by `select-window' below.
6523 ;; (set-buffer buffer)
cb882333 6524 (let* ((old-frame (selected-frame))
8319e0bf 6525 (window (display-buffer buffer action))
c3313451 6526 (frame (window-frame window)))
97adfb97
CY
6527 ;; If we chose another frame, make sure it gets input focus.
6528 (unless (eq frame old-frame)
c3313451 6529 (select-frame-set-input-focus frame norecord))
97adfb97
CY
6530 ;; Make sure new window is selected (Bug#8615), (Bug#6954).
6531 (select-window window norecord)
c3313451 6532 buffer))
f818cd2a 6533
72258fe5
CY
6534(defun pop-to-buffer-same-window (buffer &optional norecord)
6535 "Select buffer BUFFER in some window, preferably the same one.
72258fe5
CY
6536BUFFER may be a buffer, a string (a buffer name), or nil. If it
6537is a string not naming an existent buffer, create a buffer with
6538that name. If BUFFER is nil, choose some other buffer. Return
6539the buffer.
6540
6c8413fc
MR
6541Optional argument NORECORD, if non-nil means do not put this
6542buffer at the front of the list of recently selected ones.
6543
6544Unlike `pop-to-buffer', this function prefers using the selected
6545window over popping up a new window or frame."
24510c22 6546 (pop-to-buffer buffer display-buffer--same-window-action norecord))
72258fe5 6547
f818cd2a
MR
6548(defun read-buffer-to-switch (prompt)
6549 "Read the name of a buffer to switch to, prompting with PROMPT.
e1dbe924 6550Return the name of the buffer as a string.
f818cd2a
MR
6551
6552This function is intended for the `switch-to-buffer' family of
6553commands since these need to omit the name of the current buffer
6554from the list of completions and default values."
6555 (let ((rbts-completion-table (internal-complete-buffer-except)))
6556 (minibuffer-with-setup-hook
6557 (lambda ()
6558 (setq minibuffer-completion-table rbts-completion-table)
6559 ;; Since rbts-completion-table is built dynamically, we
6560 ;; can't just add it to the default value of
6561 ;; icomplete-with-completion-tables, so we add it
6562 ;; here manually.
6563 (if (and (boundp 'icomplete-with-completion-tables)
6564 (listp icomplete-with-completion-tables))
6565 (set (make-local-variable 'icomplete-with-completion-tables)
6566 (cons rbts-completion-table
6567 icomplete-with-completion-tables))))
6568 (read-buffer prompt (other-buffer (current-buffer))
6569 (confirm-nonexistent-file-or-buffer)))))
6570
6571(defun window-normalize-buffer-to-switch-to (buffer-or-name)
6572 "Normalize BUFFER-OR-NAME argument of buffer switching functions.
6573If BUFFER-OR-NAME is nil, return the buffer returned by
6574`other-buffer'. Else, if a buffer specified by BUFFER-OR-NAME
6575exists, return that buffer. If no such buffer exists, create a
6576buffer with the name BUFFER-OR-NAME and return that buffer."
6577 (if buffer-or-name
6578 (or (get-buffer buffer-or-name)
6579 (let ((buffer (get-buffer-create buffer-or-name)))
6580 (set-buffer-major-mode buffer)
6581 buffer))
6582 (other-buffer)))
6583
9d7f027b
MR
6584(defcustom switch-to-buffer-preserve-window-point nil
6585 "If non-nil, `switch-to-buffer' tries to preserve `window-point'.
6586If this is nil, `switch-to-buffer' displays the buffer at that
6587buffer's `point'. If this is `already-displayed', it tries to
791ef5f8 6588display the buffer at its previous position in the selected
9d7f027b
MR
6589window, provided the buffer is currently displayed in some other
6590window on any visible or iconified frame. If this is t, it
6591unconditionally tries to display the buffer at its previous
6592position in the selected window.
6593
43bcfda6
MR
6594This variable is ignored if the buffer is already displayed in
6595the selected window or never appeared in it before, or if
9d7f027b
MR
6596`switch-to-buffer' calls `pop-to-buffer' to display the buffer."
6597 :type '(choice
6598 (const :tag "Never" nil)
6599 (const :tag "If already displayed elsewhere" already-displayed)
6600 (const :tag "Always" t))
6601 :group 'windows
6602 :version "24.3")
6603
f818cd2a 6604(defun switch-to-buffer (buffer-or-name &optional norecord force-same-window)
d1c0cddf
SM
6605 "Display buffer BUFFER-OR-NAME in the selected window.
6606
6607WARNING: This is NOT the way to work on another buffer temporarily
6608within a Lisp program! Use `set-buffer' instead. That avoids
6609messing with the window-buffer correspondences.
6610
cee2e90d
CY
6611If the selected window cannot display the specified
6612buffer (e.g. if it is a minibuffer window or strongly dedicated
6613to another buffer), call `pop-to-buffer' to select the buffer in
6614another window.
6615
6616If called interactively, read the buffer name using the
f818cd2a
MR
6617minibuffer. The variable `confirm-nonexistent-file-or-buffer'
6618determines whether to request confirmation before creating a new
6619buffer.
6620
cee2e90d
CY
6621BUFFER-OR-NAME may be a buffer, a string (a buffer name), or nil.
6622If BUFFER-OR-NAME is a string that does not identify an existing
6623buffer, create a buffer with that name. If BUFFER-OR-NAME is
6624nil, switch to the buffer returned by `other-buffer'.
6625
6626If optional argument NORECORD is non-nil, do not put the buffer
6627at the front of the buffer list, and do not make the window
6628displaying it the most recently selected one.
6629
6630If optional argument FORCE-SAME-WINDOW is non-nil, the buffer
6631must be displayed in the selected window; if that is impossible,
6632signal an error rather than calling `pop-to-buffer'.
f818cd2a 6633
9d7f027b
MR
6634The option `switch-to-buffer-preserve-window-point' can be used
6635to make the buffer appear at its last position in the selected
6636window.
6637
f818cd2a
MR
6638Return the buffer switched to."
6639 (interactive
acc825c5 6640 (list (read-buffer-to-switch "Switch to buffer: ") nil 'force-same-window))
f818cd2a 6641 (let ((buffer (window-normalize-buffer-to-switch-to buffer-or-name)))
24510c22
SM
6642 (cond
6643 ;; Don't call set-window-buffer if it's not needed since it
6644 ;; might signal an error (e.g. if the window is dedicated).
6645 ((eq buffer (window-buffer)))
6646 ((window-minibuffer-p)
6647 (if force-same-window
71873e2b 6648 (user-error "Cannot switch buffers in minibuffer window")
24510c22
SM
6649 (pop-to-buffer buffer norecord)))
6650 ((eq (window-dedicated-p) t)
6651 (if force-same-window
71873e2b 6652 (user-error "Cannot switch buffers in a dedicated window")
24510c22 6653 (pop-to-buffer buffer norecord)))
9d7f027b
MR
6654 (t
6655 (let* ((entry (assq buffer (window-prev-buffers)))
6656 (displayed (and (eq switch-to-buffer-preserve-window-point
6657 'already-displayed)
6658 (get-buffer-window buffer 0))))
6659 (set-window-buffer nil buffer)
6660 (when (and entry
6661 (or (eq switch-to-buffer-preserve-window-point t)
6662 displayed))
6663 ;; Try to restore start and point of buffer in the selected
6664 ;; window (Bug#4041).
6665 (set-window-start (selected-window) (nth 1 entry) t)
6666 (set-window-point nil (nth 2 entry))))))
24510c22
SM
6667
6668 (unless norecord
6669 (select-window (selected-window)))
6670 (set-buffer buffer)))
f818cd2a
MR
6671
6672(defun switch-to-buffer-other-window (buffer-or-name &optional norecord)
6673 "Select the buffer specified by BUFFER-OR-NAME in another window.
382c953b 6674BUFFER-OR-NAME may be a buffer, a string (a buffer name), or
f818cd2a
MR
6675nil. Return the buffer switched to.
6676
6677If called interactively, prompt for the buffer name using the
6678minibuffer. The variable `confirm-nonexistent-file-or-buffer'
6679determines whether to request confirmation before creating a new
6680buffer.
6681
6682If BUFFER-OR-NAME is a string and does not identify an existing
6683buffer, create a new buffer with that name. If BUFFER-OR-NAME is
6684nil, switch to the buffer returned by `other-buffer'.
6685
6686Optional second argument NORECORD non-nil means do not put this
6687buffer at the front of the list of recently selected ones.
6688
6689This uses the function `display-buffer' as a subroutine; see its
6690documentation for additional customization information."
6691 (interactive
6692 (list (read-buffer-to-switch "Switch to buffer in other window: ")))
8319e0bf
CY
6693 (let ((pop-up-windows t))
6694 (pop-to-buffer buffer-or-name t norecord)))
f818cd2a
MR
6695
6696(defun switch-to-buffer-other-frame (buffer-or-name &optional norecord)
6697 "Switch to buffer BUFFER-OR-NAME in another frame.
382c953b 6698BUFFER-OR-NAME may be a buffer, a string (a buffer name), or
f818cd2a
MR
6699nil. Return the buffer switched to.
6700
6701If called interactively, prompt for the buffer name using the
6702minibuffer. The variable `confirm-nonexistent-file-or-buffer'
6703determines whether to request confirmation before creating a new
6704buffer.
6705
6706If BUFFER-OR-NAME is a string and does not identify an existing
6707buffer, create a new buffer with that name. If BUFFER-OR-NAME is
6708nil, switch to the buffer returned by `other-buffer'.
6709
6710Optional second arg NORECORD non-nil means do not put this
6711buffer at the front of the list of recently selected ones.
6712
6713This uses the function `display-buffer' as a subroutine; see its
6714documentation for additional customization information."
6715 (interactive
6716 (list (read-buffer-to-switch "Switch to buffer in other frame: ")))
8319e0bf 6717 (pop-to-buffer buffer-or-name display-buffer--other-frame-action norecord))
3c448ab6
MR
6718\f
6719(defun set-window-text-height (window height)
9992ea0c 6720 "Set the height in lines of the text display area of WINDOW to HEIGHT.
85c2386b
MR
6721WINDOW must be a live window and defaults to the selected one.
6722HEIGHT doesn't include the mode line or header line, if any, or
6723any partial-height lines in the text display area.
3c448ab6
MR
6724
6725Note that the current implementation of this function cannot
6726always set the height exactly, but attempts to be conservative,
6727by allocating more lines than are actually needed in the case
6728where some error may be present."
447f16b8 6729 (setq window (window-normalize-window window t))
3c448ab6
MR
6730 (let ((delta (- height (window-text-height window))))
6731 (unless (zerop delta)
6732 ;; Setting window-min-height to a value like 1 can lead to very
6733 ;; bizarre displays because it also allows Emacs to make *other*
37269466
CY
6734 ;; windows one line tall, which means that there's no more space
6735 ;; for the mode line.
6736 (let ((window-min-height (min 2 height)))
d615d6d2 6737 (window-resize window delta)))))
3c448ab6 6738
6198ccd0
MR
6739(defun enlarge-window-horizontally (delta)
6740 "Make selected window DELTA columns wider.
3c448ab6
MR
6741Interactively, if no argument is given, make selected window one
6742column wider."
6743 (interactive "p")
6198ccd0 6744 (enlarge-window delta t))
3c448ab6 6745
6198ccd0
MR
6746(defun shrink-window-horizontally (delta)
6747 "Make selected window DELTA columns narrower.
3c448ab6
MR
6748Interactively, if no argument is given, make selected window one
6749column narrower."
6750 (interactive "p")
6198ccd0 6751 (shrink-window delta t))
3c448ab6
MR
6752
6753(defun count-screen-lines (&optional beg end count-final-newline window)
6754 "Return the number of screen lines in the region.
6755The number of screen lines may be different from the number of actual lines,
6756due to line breaking, display table, etc.
6757
6758Optional arguments BEG and END default to `point-min' and `point-max'
6759respectively.
6760
6761If region ends with a newline, ignore it unless optional third argument
6762COUNT-FINAL-NEWLINE is non-nil.
6763
6764The optional fourth argument WINDOW specifies the window used for obtaining
6765parameters such as width, horizontal scrolling, and so on. The default is
6766to use the selected window's parameters.
6767
6768Like `vertical-motion', `count-screen-lines' always uses the current buffer,
6769regardless of which buffer is displayed in WINDOW. This makes possible to use
6770`count-screen-lines' in any buffer, whether or not it is currently displayed
6771in some window."
6772 (unless beg
6773 (setq beg (point-min)))
6774 (unless end
6775 (setq end (point-max)))
6776 (if (= beg end)
6777 0
6778 (save-excursion
6779 (save-restriction
6780 (widen)
6781 (narrow-to-region (min beg end)
6782 (if (and (not count-final-newline)
6783 (= ?\n (char-before (max beg end))))
6784 (1- (max beg end))
6785 (max beg end)))
6786 (goto-char (point-min))
6787 (1+ (vertical-motion (buffer-size) window))))))
6788
6198ccd0 6789(defun window-buffer-height (window)
85c2386b
MR
6790 "Return the height (in screen lines) of the buffer that WINDOW is displaying.
6791WINDOW must be a live window and defaults to the selected one."
6792 (setq window (window-normalize-window window t))
6198ccd0
MR
6793 (with-current-buffer (window-buffer window)
6794 (max 1
6795 (count-screen-lines (point-min) (point-max)
6796 ;; If buffer ends with a newline, ignore it when
6797 ;; counting height unless point is after it.
6798 (eobp)
6799 window))))
6800
880e6158
MR
6801;;; Resizing windows and frames to fit their contents exactly.
6802(defcustom fit-window-to-buffer-horizontally nil
6803 "Non-nil means `fit-window-to-buffer' can resize windows horizontally.
6804If this is nil, `fit-window-to-buffer' never resizes windows
6805horizontally. If this is `only', it can resize windows
6806horizontally only. Any other value means `fit-window-to-buffer'
6807can resize windows in both dimensions."
6808 :type 'boolean
6809 :version "24.4"
6810 :group 'help)
6811
6812;; `fit-frame-to-buffer' eventually wants to know the real frame sizes
6813;; counting title bar and outer borders.
5938d519 6814(defcustom fit-frame-to-buffer nil
880e6158
MR
6815 "Non-nil means `fit-frame-to-buffer' can fit a frame to its buffer.
6816A frame is fit if and only if its root window is a live window
6817and this option is non-nil. If this is `horizontally', frames
6818are resized horizontally only. If this is `vertically', frames
6819are resized vertically only. Any other non-nil value means
6820frames can be resized in both dimensions. See also
6821`fit-frame-to-buffer-margins' and `fit-frame-to-buffer-sizes'.
6822
6823If this is non-nil and a window is the only window of its frame,
6824`fit-window-to-buffer' will invoke `fit-frame-to-buffer' to fit
6825the frame to its buffer."
5938d519 6826 :type 'boolean
880e6158 6827 :version "24.4"
5938d519
MR
6828 :group 'help)
6829
880e6158
MR
6830(defcustom fit-frame-to-buffer-margins '(nil nil nil nil)
6831 "Margins around frame for `fit-frame-to-buffer'.
6832This list specifies the numbers of pixels to be left free on the
6833left, above, the right, and below a frame that shall be fit to
6834its buffer. The value specified here can be overridden for a
6835specific frame by that frame's `fit-frame-to-buffer-margins'
6836parameter, if present.
6837
6838This variable controls how fitting a frame to the size of its
6839buffer coordinates with the size of your display. If you don't
6840specify a value here, the size of the display's workarea is used.
6841
6842See also `fit-frame-to-buffer-sizes'."
6843 :version "24.4"
6844 :type '(list
6845 (choice
6846 :tag "Left"
6847 :value nil
6848 :format "%[LeftMargin%] %v "
6849 (const :tag "None" :format "%t" nil)
6850 (integer :tag "Pixels" :size 5))
6851 (choice
6852 :tag "Top"
6853 :value nil
6854 :format "%[TopMargin%] %v "
6855 (const :tag "None" :format "%t" nil)
6856 (integer :tag "Pixels" :size 5))
6857 (choice
6858 :tag "Right"
6859 :value nil
6860 :format "%[RightMargin%] %v "
6861 (const :tag "None" :format "%t" nil)
6862 (integer :tag "Pixels" :size 5))
6863 (choice
6864 :tag "Bottom"
6865 :value nil
6866 :format "%[BottomMargin%] %v "
6867 (const :tag "None" :format "%t" nil)
6868 (integer :tag "Pixels" :size 5)))
6869 :group 'help)
6870
6871(defcustom fit-frame-to-buffer-sizes '(nil nil nil nil)
6872 "Size boundaries of frame for `fit-frame-to-buffer'.
6873This list specifies the total maximum and minimum lines and
6874maximum and minimum columns of the root window of any frame that
6875shall be fit to its buffer. If any of these values is non-nil,
6876it overrides the corresponding argument of `fit-frame-to-buffer'.
6877
6878On window systems where the menubar can wrap, fitting a frame to
6879its buffer may swallow the last line(s). Specifying an
6880appropriate minimum width value here can avoid such wrapping.
6881
6882See also `fit-frame-to-buffer-margins'."
6883 :version "24.4"
6884 :type '(list
6885 (choice
6886 :tag "Maximum Height"
6887 :value nil
6888 :format "%[MaxHeight%] %v "
6889 (const :tag "None" :format "%t" nil)
6890 (integer :tag "Lines" :size 5))
6891 (choice
6892 :tag "Minimum Height"
6893 :value nil
6894 :format "%[MinHeight%] %v "
6895 (const :tag "None" :format "%t" nil)
6896 (integer :tag "Lines" :size 5))
6897 (choice
6898 :tag "Maximum Width"
6899 :value nil
6900 :format "%[MaxWidth%] %v "
6901 (const :tag "None" :format "%t" nil)
6902 (integer :tag "Columns" :size 5))
6903 (choice
6904 :tag "Minimum Width"
6905 :value nil
6906 :format "%[MinWidth%] %v\n"
6907 (const :tag "None" :format "%t" nil)
6908 (integer :tag "Columns" :size 5)))
6909 :group 'help)
5938d519 6910
e740f9d2 6911(declare-function x-display-pixel-height "xfns.c" (&optional terminal))
e740f9d2 6912
880e6158
MR
6913(defun window--sanitize-margin (margin left right)
6914 "Return MARGIN if it's a number between LEFT and RIGHT."
6915 (when (and (numberp margin)
6916 (<= left (- right margin)) (<= margin right))
6917 margin))
5938d519 6918
880e6158
MR
6919(defun fit-frame-to-buffer (&optional frame max-height min-height max-width min-width)
6920 "Adjust size of FRAME to display the contents of its buffer exactly.
6921FRAME can be any live frame and defaults to the selected one.
6922Fit only if FRAME's root window is live. MAX-HEIGHT, MIN-HEIGHT,
6923MAX-WIDTH and MIN-WIDTH specify bounds on the new total size of
25a700d0
MR
6924FRAME's root window. MIN-HEIGHT and MIN-WIDTH default to the values of
6925`window-min-height' and `window-min-width' respectively.
880e6158
MR
6926
6927The option `fit-frame-to-buffer' controls whether this function
6928has any effect. New position and size of FRAME are additionally
6929determined by the options `fit-frame-to-buffer-sizes' and
6930`fit-frame-to-buffer-margins' or the corresponding parameters of
6931FRAME."
5938d519 6932 (interactive)
25a700d0
MR
6933 (unless (and (fboundp 'x-display-pixel-height)
6934 ;; We need the respective sizes now.
6935 (fboundp 'display-monitor-attributes-list))
6936 (user-error "Cannot resize frame in non-graphic Emacs"))
5938d519 6937 (setq frame (window-normalize-frame frame))
880e6158
MR
6938 (when (and (window-live-p (frame-root-window frame))
6939 fit-frame-to-buffer
6940 (or (not window-size-fixed)
6941 (and (eq window-size-fixed 'height)
6942 (not (eq fit-frame-to-buffer 'vertically)))
6943 (and (eq window-size-fixed 'width)
6944 (not (eq fit-frame-to-buffer 'horizontally)))))
6945 (with-selected-window (frame-root-window frame)
6946 (let* ((window (frame-root-window frame))
6947 (char-width (frame-char-width))
6948 (char-height (frame-char-height))
6949 (monitor-attributes (car (display-monitor-attributes-list
6950 (frame-parameter frame 'display))))
6951 (geometry (cdr (assq 'geometry monitor-attributes)))
6952 (display-width (- (nth 2 geometry) (nth 0 geometry)))
6953 (display-height (- (nth 3 geometry) (nth 1 geometry)))
6954 (workarea (cdr (assq 'workarea monitor-attributes)))
6955 ;; Handle margins.
6956 (margins (or (frame-parameter frame 'fit-frame-to-buffer-margins)
6957 fit-frame-to-buffer-margins))
1dcb169c
MR
6958 (left-margin (if (nth 0 margins)
6959 (or (window--sanitize-margin
6960 (nth 0 margins) 0 display-width)
6961 0)
6962 (nth 0 workarea)))
6963 (top-margin (if (nth 1 margins)
6964 (or (window--sanitize-margin
6965 (nth 1 margins) 0 display-height)
6966 0)
6967 (nth 1 workarea)))
880e6158 6968 (workarea-width (nth 2 workarea))
1dcb169c
MR
6969 (right-margin (if (nth 2 margins)
6970 (- display-width
6971 (or (window--sanitize-margin
6972 (nth 2 margins) left-margin display-width)
6973 0))
6974 (nth 2 workarea)))
880e6158 6975 (workarea-height (nth 3 workarea))
1dcb169c
MR
6976 (bottom-margin (if (nth 3 margins)
6977 (- display-height
6978 (or (window--sanitize-margin
6979 (nth 3 margins) top-margin display-height)
6980 0))
6981 (nth 3 workarea)))
880e6158
MR
6982 ;; The pixel width of FRAME (which does not include the
6983 ;; window manager's decorations).
6984 (frame-width (frame-pixel-width))
6985 ;; The pixel width of the body of FRAME's root window.
6986 (window-body-width (window-body-width nil t))
6987 ;; The difference in pixels between total and body width of
6988 ;; FRAME's window.
6989 (window-extra-width (- (window-pixel-width) window-body-width))
6990 ;; The difference in pixels between the frame's pixel width
6991 ;; and the window's body width. This is the space we can't
6992 ;; use for fitting.
6993 (extra-width (- frame-width window-body-width))
6994 ;; The maximum width we can use for fitting.
6995 (fit-width (- workarea-width extra-width))
6996 ;; The pixel position of FRAME's left border. We usually
6997 ;; try to leave this alone.
6998 (left
6999 (let ((left (frame-parameter nil 'left)))
7000 (if (consp left)
7001 (funcall (car left) (cadr left))
7002 left)))
7003 ;; The pixel height of FRAME (which does not include title
7004 ;; line, decorations, and sometimes neither the menu nor
7005 ;; the toolbar).
7006 (frame-height (frame-pixel-height))
7007 ;; The pixel height of FRAME's root window (we don't care
7008 ;; about the window's body height since the return value of
7009 ;; `window-text-pixel-size' includes header and mode line).
7010 (window-height (window-pixel-height))
7011 ;; The difference in pixels between the frame's pixel
7012 ;; height and the window's height.
7013 (extra-height (- frame-height window-height))
7014 ;; When tool-bar-mode is enabled and we just created a new
7015 ;; frame, reserve lines for toolbar resizing. Needed
7016 ;; because for reasons unknown to me Emacs (1) reserves one
7017 ;; line for the toolbar when making the initial frame and
7018 ;; toolbars are enabled, and (2) later adds the remaining
7019 ;; lines needed. Our code runs IN BETWEEN (1) and (2).
7020 ;; YMMV when you're on a system that behaves differently.
7021 (toolbar-extra-height
7022 (let ((quit-restore (window-parameter window 'quit-restore))
7023 ;; This may have to change when we allow arbitrary
7024 ;; pixel height toolbars.
7025 (lines (tool-bar-height)))
7026 (* char-height
7027 (if (and quit-restore (eq (car quit-restore) 'frame)
7028 (not (zerop lines)))
7029 (1- lines)
7030 0))))
1dcb169c 7031 ;; The pixel position of FRAME's top border.
880e6158
MR
7032 (top
7033 (let ((top (frame-parameter nil 'top)))
7034 (if (consp top)
7035 (funcall (car top) (cadr top))
7036 top)))
7037 ;; Sanitize minimum and maximum sizes.
7038 (sizes (or (frame-parameter frame 'fit-frame-to-buffer-sizes)
7039 fit-frame-to-buffer-sizes))
7040 (max-height
7041 (cond
7042 ((numberp (nth 0 sizes)) (* (nth 0 sizes) char-height))
1dcb169c
MR
7043 ((numberp max-height) (* max-height char-height))
7044 (t display-height)))
880e6158
MR
7045 (min-height
7046 (cond
7047 ((numberp (nth 1 sizes)) (* (nth 1 sizes) char-height))
25a700d0
MR
7048 ((numberp min-height) (* min-height char-height))
7049 (t (* window-min-height char-height))))
880e6158
MR
7050 (max-width
7051 (cond
7052 ((numberp (nth 2 sizes))
7053 (- (* (nth 2 sizes) char-width) window-extra-width))
7054 ((numberp max-width)
1dcb169c
MR
7055 (- (* max-width char-width) window-extra-width))
7056 (t display-height)))
880e6158
MR
7057 (min-width
7058 (cond
7059 ((numberp (nth 3 sizes))
7060 (- (* (nth 3 sizes) char-width) window-extra-width))
7061 ((numberp min-width)
25a700d0
MR
7062 (- (* min-width char-width) window-extra-width))
7063 (t (* window-min-width char-width))))
880e6158
MR
7064 ;; Note: Currently, for a new frame the sizes of the header
7065 ;; and mode line may be estimated incorrectly
7066 (value (window-text-pixel-size
7067 nil t t workarea-width workarea-height t))
7068 (width (+ (car value) (window-right-divider-width)))
1dcb169c 7069 (height (+ (cdr value) (window-bottom-divider-width))))
880e6158
MR
7070 ;; Don't change height or width when the window's size is fixed
7071 ;; in either direction.
5938d519 7072 (cond
880e6158 7073 ((eq window-size-fixed 'width)
1dcb169c
MR
7074 (setq width nil))
7075 ((eq window-size-fixed 'height)
880e6158 7076 (setq height nil)))
1dcb169c 7077 ;; Fit width to constraints.
880e6158 7078 (when width
1dcb169c
MR
7079 (unless frame-resize-pixelwise
7080 ;; Round to character sizes.
7081 (setq width (* (/ (+ width char-width -1) char-width)
7082 char-width)))
880e6158 7083 ;; Fit to maximum and minimum widths.
1dcb169c 7084 (setq width (max (min width max-width) min-width))
880e6158
MR
7085 ;; Add extra width.
7086 (setq width (+ width extra-width))
1dcb169c
MR
7087 ;; Preserve margins.
7088 (let ((right (+ left width)))
880e6158 7089 (cond
25a700d0
MR
7090 ((> right right-margin)
7091 ;; Move frame to left (we don't know its real width).
1dcb169c 7092 (setq left (max left-margin (- left (- right right-margin)))))
880e6158
MR
7093 ((< left left-margin)
7094 ;; Move frame to right.
7095 (setq left left-margin)))))
1dcb169c 7096 ;; Fit height to constraints.
880e6158 7097 (when height
1dcb169c
MR
7098 (unless frame-resize-pixelwise
7099 (setq height (* (/ (+ height char-height -1) char-height)
7100 char-height)))
880e6158 7101 ;; Fit to maximum and minimum heights.
1dcb169c 7102 (setq height (max (min height max-height) min-height))
880e6158
MR
7103 ;; Add extra height.
7104 (setq height (+ height extra-height))
1dcb169c
MR
7105 ;; Preserve margins.
7106 (let ((bottom (+ top height)))
880e6158 7107 (cond
25a700d0
MR
7108 ((> bottom bottom-margin)
7109 ;; Move frame up (we don't know its real height).
1dcb169c 7110 (setq top (max top-margin (- top (- bottom bottom-margin)))))
880e6158
MR
7111 ((< top top-margin)
7112 ;; Move frame down.
7113 (setq top top-margin)))))
7114 ;; Apply changes.
7115 (set-frame-position frame left top)
7116 ;; Clumsily try to translate our calculations to what
7117 ;; `set-frame-size' wants.
7118 (when width
7119 (setq width (- (+ (frame-text-width) width)
7120 extra-width window-body-width)))
7121 (when height
7122 (setq height (- (+ (frame-text-height) height)
7123 extra-height window-height)))
7124 (set-frame-size
7125 frame
7126 (if width
25a700d0 7127 (if frame-resize-pixelwise
880e6158
MR
7128 width
7129 (/ width char-width))
7130 (frame-text-width))
7131 (if height
25a700d0 7132 (if frame-resize-pixelwise
880e6158
MR
7133 height
7134 (/ height char-height))
7135 (frame-text-height))
25a700d0 7136 frame-resize-pixelwise)))))
880e6158
MR
7137
7138(defun fit-window-to-buffer (&optional window max-height min-height max-width min-width)
7139 "Adjust size of WINDOW to display its buffer's contents exactly.
85c2386b 7140WINDOW must be a live window and defaults to the selected one.
6198ccd0 7141
880e6158
MR
7142If WINDOW is part of a vertical combination, adjust WINDOW's
7143height. The new height is calculated from the number of lines of
7144the accessible portion of its buffer. The optional argument
7145MAX-HEIGHT specifies a maximum height and defaults to the height
7146of WINDOW's frame. The optional argument MIN-HEIGHT specifies a
7147minimum height and defaults to `window-min-height'. Both
7148MAX-HEIGHT and MIN-HEIGHT are specified in lines and include the
7149mode line and header line, if any.
7150
7151If WINDOW is part of a horizontal combination and the value of
7152the option `fit-window-to-buffer-horizontally' is non-nil, adjust
7153WINDOW's height. The new width of WINDOW is calculated from the
7154maximum length of its buffer's lines that follow the current
7155start position of WINDOW. The optional argument MAX-WIDTH
7156specifies a maximum width and defaults to the width of WINDOW's
7157frame. The optional argument MIN-WIDTH specifies a minimum width
7158and defaults to `window-min-width'. Both MAX-WIDTH and MIN-WIDTH
7159are specified in columns and include fringes, margins and
7160scrollbars, if any.
7161
7162Fit pixelwise if the option `window-resize-pixelwise' is non-nil.
7163If WINDOW is its frame's root window, then if the option
7164`fit-frame-to-buffer' is non-nil, call `fit-frame-to-buffer' to
7165adjust the frame's size.
6198ccd0
MR
7166
7167Note that even if this function makes WINDOW large enough to show
880e6158
MR
7168_all_ parts of its buffer you might not see the first part when
7169WINDOW was scrolled. If WINDOW is resized horizontally, you will
7170not see the top of its buffer unless WINDOW starts at its minimum
7171accessible position."
f7baca20 7172 (interactive)
447f16b8 7173 (setq window (window-normalize-window window t))
880e6158
MR
7174 (if (eq window (frame-root-window window))
7175 (when fit-frame-to-buffer
7176 ;; Fit WINDOW's frame to buffer.
7177 (fit-frame-to-buffer
7178 (window-frame window)
7179 max-height min-height max-width min-width))
6198ccd0 7180 (with-selected-window window
880e6158 7181 (let* ((pixelwise window-resize-pixelwise)
880e6158
MR
7182 (char-height (frame-char-height))
7183 (char-width (frame-char-width))
880e6158
MR
7184 (total-height (window-size window nil pixelwise))
7185 (body-height (window-body-height window pixelwise))
7186 (body-width (window-body-width window pixelwise))
6198ccd0 7187 (min-height
880e6158 7188 ;; Sanitize MIN-HEIGHT.
c5e28e39
MR
7189 (if (numberp min-height)
7190 ;; Can't get smaller than `window-safe-min-height'.
880e6158
MR
7191 (max (if pixelwise
7192 (* char-height min-height)
7193 min-height)
7194 (if pixelwise
7195 (window-safe-min-pixel-height window)
7196 window-safe-min-height))
c5e28e39 7197 ;; Preserve header and mode line if present.
25a700d0
MR
7198 (max (if pixelwise
7199 (* char-height window-min-height)
7200 window-min-height)
7201 (window-min-size nil nil t pixelwise))))
6198ccd0 7202 (max-height
880e6158 7203 ;; Sanitize MAX-HEIGHT.
c5e28e39 7204 (if (numberp max-height)
880e6158
MR
7205 (min
7206 (+ total-height
7207 (window-max-delta
7208 window nil nil nil nil nil pixelwise))
7209 (if pixelwise
7210 (* char-height max-height)
7211 max-height))
7212 (+ total-height (window-max-delta
7213 window nil nil nil nil nil pixelwise))))
7214 height)
7215 (cond
7216 ;; If WINDOW is vertically combined, try to resize it
7217 ;; vertically.
7218 ((and (not (eq fit-window-to-buffer-horizontally 'only))
7219 (not (window-size-fixed-p window))
7220 (window-combined-p))
7221 ;; Vertically we always want to fit the entire buffer.
7222 ;; WINDOW'S height can't get larger than its frame's pixel
7223 ;; height. Its width remains fixed.
7224 (setq height (+ (cdr (window-text-pixel-size
7225 nil nil t nil (frame-pixel-height) t))
7226 (window-bottom-divider-width)))
7227 ;; Round height.
7228 (unless pixelwise
6cb4da45 7229 (setq height (/ (+ height char-height -1) char-height)))
880e6158
MR
7230 (unless (= height total-height)
7231 (window-resize-no-error
7232 window
7233 (- (max min-height (min max-height height)) total-height)
7234 nil window pixelwise)))
7235 ;; If WINDOW is horizontally combined, try to resize it
7236 ;; horizontally.
7237 ((and fit-window-to-buffer-horizontally
7238 (not (window-size-fixed-p window t))
7239 (window-combined-p nil t))
b923819c 7240 (let* ((total-width (window-size window t pixelwise))
880e6158
MR
7241 (min-width
7242 ;; Sanitize MIN-WIDTH.
7243 (if (numberp min-width)
7244 ;; Can't get smaller than `window-safe-min-width'.
7245 (max (if pixelwise
7246 (* char-width min-width)
7247 min-width)
7248 (if pixelwise
7249 (window-safe-min-pixel-width)
7250 window-safe-min-width))
f224e500 7251 ;; Preserve fringes, margins, scrollbars if present.
25a700d0
MR
7252 (max (if pixelwise
7253 (* char-width window-min-width)
7254 window-min-width)
7255 (window-min-size nil nil t pixelwise))))
880e6158
MR
7256 (max-width
7257 ;; Sanitize MAX-WIDTH.
7258 (if (numberp max-width)
7259 (min (+ total-width
7260 (window-max-delta
7261 nil t nil nil nil nil pixelwise))
7262 (if pixelwise
7263 (* char-width max-width)
7264 max-width))
7265 (+ total-width (window-max-delta
7266 nil t nil nil nil nil pixelwise))))
7267 ;; When fitting vertically, assume that WINDOW's start
7268 ;; position remains unaltered. WINDOW can't get wider
7269 ;; than its frame's pixel width, its height remains
7270 ;; unaltered.
7271 (width (+ (car (window-text-pixel-size
7272 nil (window-start) (point-max)
7273 (frame-pixel-width)
7274 ;; Add one char-height to assure that
7275 ;; we're on the safe side. This
7276 ;; overshoots when the first line below
7277 ;; the bottom is wider than the window.
7278 (* body-height
7279 (if pixelwise char-height 1))))
7280 (window-right-divider-width))))
7281 (unless pixelwise
6cb4da45 7282 (setq width (/ (+ width char-width -1) char-width)))
880e6158
MR
7283 (unless (= width body-width)
7284 (window-resize-no-error
7285 window
7286 (- (max min-width
7287 (min max-width
7288 (+ total-width (- width body-width))))
7289 total-width)
7290 t window pixelwise)))))))))
ef654460 7291
39cffb44
MR
7292(defun window-safely-shrinkable-p (&optional window)
7293 "Return t if WINDOW can be shrunk without shrinking other windows.
7294WINDOW defaults to the selected window."
7295 (with-selected-window (or window (selected-window))
7296 (let ((edges (window-edges)))
39cffb44
MR
7297 (or (= (nth 2 edges) (nth 2 (window-edges (previous-window))))
7298 (= (nth 0 edges) (nth 0 (window-edges (next-window))))))))
39cffb44 7299
3c448ab6
MR
7300(defun shrink-window-if-larger-than-buffer (&optional window)
7301 "Shrink height of WINDOW if its buffer doesn't need so many lines.
7302More precisely, shrink WINDOW vertically to be as small as
7303possible, while still showing the full contents of its buffer.
85c2386b 7304WINDOW must be a live window and defaults to the selected one.
3c448ab6 7305
6198ccd0
MR
7306Do not shrink WINDOW to less than `window-min-height' lines. Do
7307nothing if the buffer contains more lines than the present window
7308height, or if some of the window's contents are scrolled out of
7309view, or if shrinking this window would also shrink another
7310window, or if the window is the only window of its frame.
3c448ab6
MR
7311
7312Return non-nil if the window was shrunk, nil otherwise."
7313 (interactive)
447f16b8 7314 (setq window (window-normalize-window window t))
6198ccd0
MR
7315 ;; Make sure that WINDOW is vertically combined and `point-min' is
7316 ;; visible (for whatever reason that's needed). The remaining issues
7317 ;; should be taken care of by `fit-window-to-buffer'.
3d8daefe 7318 (when (and (window-combined-p window)
6198ccd0 7319 (pos-visible-in-window-p (point-min) window))
880e6158 7320 (fit-window-to-buffer window (window-total-height window))))
6198ccd0 7321\f
3c448ab6
MR
7322(defun kill-buffer-and-window ()
7323 "Kill the current buffer and delete the selected window."
7324 (interactive)
7325 (let ((window-to-delete (selected-window))
7326 (buffer-to-kill (current-buffer))
6198ccd0 7327 (delete-window-hook (lambda () (ignore-errors (delete-window)))))
3c448ab6
MR
7328 (unwind-protect
7329 (progn
7330 (add-hook 'kill-buffer-hook delete-window-hook t t)
7331 (if (kill-buffer (current-buffer))
7332 ;; If `delete-window' failed before, we rerun it to regenerate
7333 ;; the error so it can be seen in the echo area.
7334 (when (eq (selected-window) window-to-delete)
7335 (delete-window))))
7336 ;; If the buffer is not dead for some reason (probably because
7337 ;; of a `quit' signal), remove the hook again.
6198ccd0
MR
7338 (ignore-errors
7339 (with-current-buffer buffer-to-kill
7340 (remove-hook 'kill-buffer-hook delete-window-hook t))))))
3c448ab6 7341
74f806a1 7342\f
3c448ab6
MR
7343(defvar recenter-last-op nil
7344 "Indicates the last recenter operation performed.
0116abbd
JL
7345Possible values: `top', `middle', `bottom', integer or float numbers.")
7346
7347(defcustom recenter-positions '(middle top bottom)
7348 "Cycling order for `recenter-top-bottom'.
7349A list of elements with possible values `top', `middle', `bottom',
7350integer or float numbers that define the cycling order for
7351the command `recenter-top-bottom'.
7352
382c953b 7353Top and bottom destinations are `scroll-margin' lines from the true
0116abbd
JL
7354window top and bottom. Middle redraws the frame and centers point
7355vertically within the window. Integer number moves current line to
7356the specified absolute window-line. Float number between 0.0 and 1.0
7357means the percentage of the screen space from the top. The default
7358cycling order is middle -> top -> bottom."
7359 :type '(repeat (choice
7360 (const :tag "Top" top)
7361 (const :tag "Middle" middle)
7362 (const :tag "Bottom" bottom)
7363 (integer :tag "Line number")
7364 (float :tag "Percentage")))
7365 :version "23.2"
7366 :group 'windows)
3c448ab6
MR
7367
7368(defun recenter-top-bottom (&optional arg)
0116abbd
JL
7369 "Move current buffer line to the specified window line.
7370With no prefix argument, successive calls place point according
7371to the cycling order defined by `recenter-positions'.
3c448ab6
MR
7372
7373A prefix argument is handled like `recenter':
7374 With numeric prefix ARG, move current line to window-line ARG.
0116abbd 7375 With plain `C-u', move current line to window center."
3c448ab6
MR
7376 (interactive "P")
7377 (cond
0116abbd 7378 (arg (recenter arg)) ; Always respect ARG.
3c448ab6 7379 (t
0116abbd
JL
7380 (setq recenter-last-op
7381 (if (eq this-command last-command)
7382 (car (or (cdr (member recenter-last-op recenter-positions))
7383 recenter-positions))
7384 (car recenter-positions)))
3c448ab6
MR
7385 (let ((this-scroll-margin
7386 (min (max 0 scroll-margin)
7387 (truncate (/ (window-body-height) 4.0)))))
7388 (cond ((eq recenter-last-op 'middle)
0116abbd 7389 (recenter))
3c448ab6 7390 ((eq recenter-last-op 'top)
0116abbd
JL
7391 (recenter this-scroll-margin))
7392 ((eq recenter-last-op 'bottom)
7393 (recenter (- -1 this-scroll-margin)))
7394 ((integerp recenter-last-op)
7395 (recenter recenter-last-op))
7396 ((floatp recenter-last-op)
7397 (recenter (round (* recenter-last-op (window-height))))))))))
3c448ab6
MR
7398
7399(define-key global-map [?\C-l] 'recenter-top-bottom)
216349f8 7400
216349f8
SM
7401(defun move-to-window-line-top-bottom (&optional arg)
7402 "Position point relative to window.
7403
0f202d5d 7404With a prefix argument ARG, acts like `move-to-window-line'.
216349f8
SM
7405
7406With no argument, positions point at center of window.
0116abbd
JL
7407Successive calls position point at positions defined
7408by `recenter-positions'."
216349f8
SM
7409 (interactive "P")
7410 (cond
0116abbd 7411 (arg (move-to-window-line arg)) ; Always respect ARG.
216349f8 7412 (t
0116abbd
JL
7413 (setq recenter-last-op
7414 (if (eq this-command last-command)
7415 (car (or (cdr (member recenter-last-op recenter-positions))
7416 recenter-positions))
7417 (car recenter-positions)))
216349f8
SM
7418 (let ((this-scroll-margin
7419 (min (max 0 scroll-margin)
7420 (truncate (/ (window-body-height) 4.0)))))
0f202d5d 7421 (cond ((eq recenter-last-op 'middle)
0116abbd 7422 (call-interactively 'move-to-window-line))
0f202d5d 7423 ((eq recenter-last-op 'top)
0116abbd
JL
7424 (move-to-window-line this-scroll-margin))
7425 ((eq recenter-last-op 'bottom)
7426 (move-to-window-line (- -1 this-scroll-margin)))
7427 ((integerp recenter-last-op)
7428 (move-to-window-line recenter-last-op))
7429 ((floatp recenter-last-op)
7430 (move-to-window-line (round (* recenter-last-op (window-height))))))))))
216349f8
SM
7431
7432(define-key global-map [?\M-r] 'move-to-window-line-top-bottom)
3c448ab6 7433\f
74f806a1
JL
7434;;; Scrolling commands.
7435
0a9f9ab5
SM
7436;;; Scrolling commands which do not signal errors at top/bottom
7437;;; of buffer at first key-press (instead move to top/bottom
74f806a1
JL
7438;;; of buffer).
7439
7440(defcustom scroll-error-top-bottom nil
8350f087 7441 "Move point to top/bottom of buffer before signaling a scrolling error.
74f806a1
JL
7442A value of nil means just signal an error if no more scrolling possible.
7443A value of t means point moves to the beginning or the end of the buffer
7444\(depending on scrolling direction) when no more scrolling possible.
7445When point is already on that position, then signal an error."
7446 :type 'boolean
60efac0f 7447 :group 'windows
74f806a1
JL
7448 :version "24.1")
7449
7450(defun scroll-up-command (&optional arg)
7451 "Scroll text of selected window upward ARG lines; or near full screen if no ARG.
7452If `scroll-error-top-bottom' is non-nil and `scroll-up' cannot
7453scroll window further, move cursor to the bottom line.
7454When point is already on that position, then signal an error.
7455A near full screen is `next-screen-context-lines' less than a full screen.
7456Negative ARG means scroll downward.
7457If ARG is the atom `-', scroll downward by nearly full screen."
7458 (interactive "^P")
7459 (cond
7460 ((null scroll-error-top-bottom)
7461 (scroll-up arg))
7462 ((eq arg '-)
7463 (scroll-down-command nil))
7464 ((< (prefix-numeric-value arg) 0)
7465 (scroll-down-command (- (prefix-numeric-value arg))))
7466 ((eobp)
7467 (scroll-up arg)) ; signal error
7468 (t
7469 (condition-case nil
7470 (scroll-up arg)
7471 (end-of-buffer
7472 (if arg
7473 ;; When scrolling by ARG lines can't be done,
7474 ;; move by ARG lines instead.
7475 (forward-line arg)
7476 ;; When ARG is nil for full-screen scrolling,
7477 ;; move to the bottom of the buffer.
7478 (goto-char (point-max))))))))
7479
7480(put 'scroll-up-command 'scroll-command t)
7481
7482(defun scroll-down-command (&optional arg)
7483 "Scroll text of selected window down ARG lines; or near full screen if no ARG.
7484If `scroll-error-top-bottom' is non-nil and `scroll-down' cannot
7485scroll window further, move cursor to the top line.
7486When point is already on that position, then signal an error.
7487A near full screen is `next-screen-context-lines' less than a full screen.
7488Negative ARG means scroll upward.
7489If ARG is the atom `-', scroll upward by nearly full screen."
7490 (interactive "^P")
7491 (cond
7492 ((null scroll-error-top-bottom)
7493 (scroll-down arg))
7494 ((eq arg '-)
7495 (scroll-up-command nil))
7496 ((< (prefix-numeric-value arg) 0)
7497 (scroll-up-command (- (prefix-numeric-value arg))))
7498 ((bobp)
7499 (scroll-down arg)) ; signal error
7500 (t
7501 (condition-case nil
7502 (scroll-down arg)
7503 (beginning-of-buffer
7504 (if arg
7505 ;; When scrolling by ARG lines can't be done,
7506 ;; move by ARG lines instead.
7507 (forward-line (- arg))
7508 ;; When ARG is nil for full-screen scrolling,
7509 ;; move to the top of the buffer.
7510 (goto-char (point-min))))))))
7511
7512(put 'scroll-down-command 'scroll-command t)
7513
7514;;; Scrolling commands which scroll a line instead of full screen.
7515
7516(defun scroll-up-line (&optional arg)
7517 "Scroll text of selected window upward ARG lines; or one line if no ARG.
7518If ARG is omitted or nil, scroll upward by one line.
7519This is different from `scroll-up-command' that scrolls a full screen."
7520 (interactive "p")
7521 (scroll-up (or arg 1)))
7522
7523(put 'scroll-up-line 'scroll-command t)
7524
7525(defun scroll-down-line (&optional arg)
7526 "Scroll text of selected window down ARG lines; or one line if no ARG.
7527If ARG is omitted or nil, scroll down by one line.
7528This is different from `scroll-down-command' that scrolls a full screen."
7529 (interactive "p")
7530 (scroll-down (or arg 1)))
7531
7532(put 'scroll-down-line 'scroll-command t)
7533
7534\f
011474aa 7535(defun scroll-other-window-down (&optional lines)
74f806a1
JL
7536 "Scroll the \"other window\" down.
7537For more details, see the documentation for `scroll-other-window'."
7538 (interactive "P")
7539 (scroll-other-window
7540 ;; Just invert the argument's meaning.
7541 ;; We can do that without knowing which window it will be.
7542 (if (eq lines '-) nil
7543 (if (null lines) '-
7544 (- (prefix-numeric-value lines))))))
7545
7546(defun beginning-of-buffer-other-window (arg)
7547 "Move point to the beginning of the buffer in the other window.
7548Leave mark at previous position.
7549With arg N, put point N/10 of the way from the true beginning."
7550 (interactive "P")
7551 (let ((orig-window (selected-window))
7552 (window (other-window-for-scrolling)))
7553 ;; We use unwind-protect rather than save-window-excursion
7554 ;; because the latter would preserve the things we want to change.
7555 (unwind-protect
7556 (progn
7557 (select-window window)
7558 ;; Set point and mark in that window's buffer.
7559 (with-no-warnings
7560 (beginning-of-buffer arg))
7561 ;; Set point accordingly.
7562 (recenter '(t)))
7563 (select-window orig-window))))
7564
7565(defun end-of-buffer-other-window (arg)
7566 "Move point to the end of the buffer in the other window.
7567Leave mark at previous position.
7568With arg N, put point N/10 of the way from the true end."
7569 (interactive "P")
7570 ;; See beginning-of-buffer-other-window for comments.
7571 (let ((orig-window (selected-window))
7572 (window (other-window-for-scrolling)))
7573 (unwind-protect
7574 (progn
7575 (select-window window)
7576 (with-no-warnings
7577 (end-of-buffer arg))
7578 (recenter '(t)))
7579 (select-window orig-window))))
74f806a1 7580\f
3c448ab6
MR
7581(defvar mouse-autoselect-window-timer nil
7582 "Timer used by delayed window autoselection.")
7583
7584(defvar mouse-autoselect-window-position nil
7585 "Last mouse position recorded by delayed window autoselection.")
7586
7587(defvar mouse-autoselect-window-window nil
7588 "Last window recorded by delayed window autoselection.")
7589
7590(defvar mouse-autoselect-window-state nil
7591 "When non-nil, special state of delayed window autoselection.
382c953b
JB
7592Possible values are `suspend' (suspend autoselection after a menu or
7593scrollbar interaction) and `select' (the next invocation of
7594`handle-select-window' shall select the window immediately).")
3c448ab6
MR
7595
7596(defun mouse-autoselect-window-cancel (&optional force)
7597 "Cancel delayed window autoselection.
7598Optional argument FORCE means cancel unconditionally."
7599 (unless (and (not force)
7600 ;; Don't cancel for select-window or select-frame events
7601 ;; or when the user drags a scroll bar.
7602 (or (memq this-command
7603 '(handle-select-window handle-switch-frame))
7604 (and (eq this-command 'scroll-bar-toolkit-scroll)
7605 (memq (nth 4 (event-end last-input-event))
7606 '(handle end-scroll)))))
7607 (setq mouse-autoselect-window-state nil)
7608 (when (timerp mouse-autoselect-window-timer)
7609 (cancel-timer mouse-autoselect-window-timer))
7610 (remove-hook 'pre-command-hook 'mouse-autoselect-window-cancel)))
7611
7612(defun mouse-autoselect-window-start (mouse-position &optional window suspend)
7613 "Start delayed window autoselection.
7614MOUSE-POSITION is the last position where the mouse was seen as returned
7615by `mouse-position'. Optional argument WINDOW non-nil denotes the
7616window where the mouse was seen. Optional argument SUSPEND non-nil
7617means suspend autoselection."
7618 ;; Record values for MOUSE-POSITION, WINDOW, and SUSPEND.
7619 (setq mouse-autoselect-window-position mouse-position)
7620 (when window (setq mouse-autoselect-window-window window))
7621 (setq mouse-autoselect-window-state (when suspend 'suspend))
7622 ;; Install timer which runs `mouse-autoselect-window-select' after
7623 ;; `mouse-autoselect-window' seconds.
7624 (setq mouse-autoselect-window-timer
7625 (run-at-time
7626 (abs mouse-autoselect-window) nil 'mouse-autoselect-window-select)))
7627
7628(defun mouse-autoselect-window-select ()
7629 "Select window with delayed window autoselection.
7630If the mouse position has stabilized in a non-selected window, select
382c953b
JB
7631that window. The minibuffer window is selected only if the minibuffer
7632is active. This function is run by `mouse-autoselect-window-timer'."
6198ccd0
MR
7633 (ignore-errors
7634 (let* ((mouse-position (mouse-position))
7635 (window
7636 (ignore-errors
7637 (window-at (cadr mouse-position) (cddr mouse-position)
7638 (car mouse-position)))))
7639 (cond
e740f9d2 7640 ((or (and (fboundp 'menu-or-popup-active-p) (menu-or-popup-active-p))
6198ccd0 7641 (and window
3dfc5cd6
MR
7642 (let ((coords (coordinates-in-window-p
7643 (cdr mouse-position) window)))
7644 (and (not (consp coords))
7645 (not (memq coords '(left-margin right-margin)))))))
c660a885
MR
7646 ;; A menu / popup dialog is active or the mouse is not on the
7647 ;; text region of WINDOW: Suspend autoselection temporarily.
6198ccd0
MR
7648 (mouse-autoselect-window-start mouse-position nil t))
7649 ((eq mouse-autoselect-window-state 'suspend)
7650 ;; Delayed autoselection was temporarily suspended, reenable it.
7651 (mouse-autoselect-window-start mouse-position))
7652 ((and window (not (eq window (selected-window)))
7653 (or (not (numberp mouse-autoselect-window))
7654 (and (> mouse-autoselect-window 0)
7655 ;; If `mouse-autoselect-window' is positive, select
7656 ;; window if the window is the same as before.
7657 (eq window mouse-autoselect-window-window))
7658 ;; Otherwise select window if the mouse is at the same
7659 ;; position as before. Observe that the first test after
7660 ;; starting autoselection usually fails since the value of
7661 ;; `mouse-autoselect-window-position' recorded there is the
7662 ;; position where the mouse has entered the new window and
7663 ;; not necessarily where the mouse has stopped moving.
7664 (equal mouse-position mouse-autoselect-window-position))
7665 ;; The minibuffer is a candidate window if it's active.
7666 (or (not (window-minibuffer-p window))
7667 (eq window (active-minibuffer-window))))
7668 ;; Mouse position has stabilized in non-selected window: Cancel
7669 ;; delayed autoselection and try to select that window.
7670 (mouse-autoselect-window-cancel t)
7671 ;; Select window where mouse appears unless the selected window is the
7672 ;; minibuffer. Use `unread-command-events' in order to execute pre-
7673 ;; and post-command hooks and trigger idle timers. To avoid delaying
7674 ;; autoselection again, set `mouse-autoselect-window-state'."
290d5b58 7675 (unless (window-minibuffer-p)
6198ccd0
MR
7676 (setq mouse-autoselect-window-state 'select)
7677 (setq unread-command-events
7678 (cons (list 'select-window (list window))
7679 unread-command-events))))
7680 ((or (and window (eq window (selected-window)))
7681 (not (numberp mouse-autoselect-window))
7682 (equal mouse-position mouse-autoselect-window-position))
7683 ;; Mouse position has either stabilized in the selected window or at
7684 ;; `mouse-autoselect-window-position': Cancel delayed autoselection.
7685 (mouse-autoselect-window-cancel t))
7686 (t
7687 ;; Mouse position has not stabilized yet, resume delayed
7688 ;; autoselection.
7689 (mouse-autoselect-window-start mouse-position window))))))
3c448ab6
MR
7690
7691(defun handle-select-window (event)
7692 "Handle select-window events."
7693 (interactive "e")
7694 (let ((window (posn-window (event-start event))))
7695 (unless (or (not (window-live-p window))
7696 ;; Don't switch if we're currently in the minibuffer.
7697 ;; This tries to work around problems where the
7698 ;; minibuffer gets unselected unexpectedly, and where
7699 ;; you then have to move your mouse all the way down to
7700 ;; the minibuffer to select it.
290d5b58 7701 (window-minibuffer-p)
3c448ab6
MR
7702 ;; Don't switch to minibuffer window unless it's active.
7703 (and (window-minibuffer-p window)
7704 (not (minibuffer-window-active-p window)))
7705 ;; Don't switch when autoselection shall be delayed.
7706 (and (numberp mouse-autoselect-window)
7707 (not (zerop mouse-autoselect-window))
7708 (not (eq mouse-autoselect-window-state 'select))
7709 (progn
7710 ;; Cancel any delayed autoselection.
7711 (mouse-autoselect-window-cancel t)
7712 ;; Start delayed autoselection from current mouse
7713 ;; position and window.
7714 (mouse-autoselect-window-start (mouse-position) window)
7715 ;; Executing a command cancels delayed autoselection.
7716 (add-hook
7717 'pre-command-hook 'mouse-autoselect-window-cancel))))
7718 (when mouse-autoselect-window
7719 ;; Reset state of delayed autoselection.
7720 (setq mouse-autoselect-window-state nil)
7721 ;; Run `mouse-leave-buffer-hook' when autoselecting window.
7722 (run-hooks 'mouse-leave-buffer-hook))
b1bac16e
MR
7723 ;; Clear echo area.
7724 (message nil)
3c448ab6
MR
7725 (select-window window))))
7726
3c448ab6
MR
7727(defun truncated-partial-width-window-p (&optional window)
7728 "Return non-nil if lines in WINDOW are specifically truncated due to its width.
85c2386b 7729WINDOW must be a live window and defaults to the selected one.
3c448ab6
MR
7730Return nil if WINDOW is not a partial-width window
7731 (regardless of the value of `truncate-lines').
7732Otherwise, consult the value of `truncate-partial-width-windows'
7733 for the buffer shown in WINDOW."
85c2386b 7734 (setq window (window-normalize-window window t))
3c448ab6
MR
7735 (unless (window-full-width-p window)
7736 (let ((t-p-w-w (buffer-local-value 'truncate-partial-width-windows
7737 (window-buffer window))))
7738 (if (integerp t-p-w-w)
7739 (< (window-width window) t-p-w-w)
7740 t-p-w-w))))
562dd5e9 7741\f
59ee0542
GM
7742;; Some of these are in tutorial--default-keys, so update that if you
7743;; change these.
562dd5e9
MR
7744(define-key ctl-x-map "0" 'delete-window)
7745(define-key ctl-x-map "1" 'delete-other-windows)
2d197ffb
CY
7746(define-key ctl-x-map "2" 'split-window-below)
7747(define-key ctl-x-map "3" 'split-window-right)
9397e56f 7748(define-key ctl-x-map "o" 'other-window)
562dd5e9 7749(define-key ctl-x-map "^" 'enlarge-window)
3c448ab6
MR
7750(define-key ctl-x-map "}" 'enlarge-window-horizontally)
7751(define-key ctl-x-map "{" 'shrink-window-horizontally)
7752(define-key ctl-x-map "-" 'shrink-window-if-larger-than-buffer)
7753(define-key ctl-x-map "+" 'balance-windows)
7754(define-key ctl-x-4-map "0" 'kill-buffer-and-window)
7755
3c448ab6 7756;;; window.el ends here