;;; window.el --- GNU Emacs window commands aside from those written in C
;; Copyright (C) 1985, 1989, 1992, 1993, 1994, 2000, 2001, 2002,
-;; 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
+;; 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc.
;; Maintainer: FSF
;; Keywords: internal
(make-variable-buffer-local 'window-size-fixed)
(defmacro save-selected-window (&rest body)
- "Execute BODY, then select the window that was selected before BODY.
+ "Execute BODY, then select the previously selected window.
The value returned is the value of the last form in BODY.
+This macro saves and restores the selected window, as well as the
+selected window in each frame. If the previously selected window
+is no longer live, then whatever window is selected at the end of
+BODY remains selected. If the previously selected window of some
+frame is no longer live at the end of BODY, that frame's selected
+window is left alone.
+
This macro saves and restores the current buffer, since otherwise
-its normal operation could potentially make a different
-buffer current. It does not alter the buffer list ordering.
-
-This macro saves and restores the selected window, as well as
-the selected window in each frame. If the previously selected
-window of some frame is no longer live at the end of BODY, that
-frame's selected window is left alone. If the selected window is
-no longer live, then whatever window is selected at the end of
-BODY remains selected."
+its normal operation could make a different buffer current. The
+order of recently selected windows and the buffer list ordering
+are not altered by this macro (unless they are altered in BODY)."
`(let ((save-selected-window-window (selected-window))
;; It is necessary to save all of these, because calling
;; select-window changes frame-selected-window for whatever
(dolist (elt save-selected-window-alist)
(and (frame-live-p (car elt))
(window-live-p (cdr elt))
- (set-frame-selected-window (car elt) (cdr elt))))
- (if (window-live-p save-selected-window-window)
- (select-window save-selected-window-window))))))
+ (set-frame-selected-window (car elt) (cdr elt) 'norecord)))
+ (when (window-live-p save-selected-window-window)
+ (select-window save-selected-window-window 'norecord))))))
(defun window-body-height (&optional window)
- "Return number of lines in window WINDOW for actual buffer text.
-This does not include the mode line (if any) or the header line (if any)."
+ "Return number of lines in WINDOW available for actual buffer text.
+WINDOW defaults to the selected window.
+
+The return value does not include the mode line or the header
+line, if any. If a line at the bottom of the window is only
+partially visible, that line is included in the return value. If
+you do not want to include a partially visible bottom line in the
+return value, use `window-text-height' instead."
(or window (setq window (selected-window)))
(if (window-minibuffer-p window)
(window-height window)
(next-window base-window (if nomini 'arg) all-frames))))
(defun window-current-scroll-bars (&optional window)
- "Return the current scroll-bar settings in window WINDOW.
-Value is a cons (VERTICAL . HORIZONTAL) where VERTICAL specifies the
-current location of the vertical scroll-bars (left, right, or nil),
-and HORIZONTAL specifies the current location of the horizontal scroll
-bars (top, bottom, or nil)."
+ "Return the current scroll bar settings for WINDOW.
+WINDOW defaults to the selected window.
+
+The return value is a cons cell (VERTICAL . HORIZONTAL) where
+VERTICAL specifies the current location of the vertical scroll
+bars (`left', `right', or nil), and HORIZONTAL specifies the
+current location of the horizontal scroll bars (`top', `bottom',
+or nil).
+
+Unlike `window-scroll-bars', this function reports the scroll bar
+type actually used, once frame defaults and `scroll-bar-mode' are
+taken into account."
(let ((vert (nth 2 (window-scroll-bars window)))
(hor nil))
(when (or (eq vert t) (eq hor t))
(cons vert hor)))
(defun walk-windows (proc &optional minibuf all-frames)
- "Cycle through all visible windows, calling PROC for each one.
-PROC is called with a window as argument.
+ "Cycle through all windows, calling PROC for each one.
+PROC must specify a function with a window as its sole argument.
+The optional arguments MINIBUF and ALL-FRAMES specify the set of
+windows to include in the walk, see also `next-window'.
-Optional second arg MINIBUF t means count the minibuffer window even
-if not active. MINIBUF nil or omitted means count the minibuffer only if
-it is active. MINIBUF neither t nor nil means not to count the
-minibuffer even if it is active.
+MINIBUF t means include the minibuffer window even if the
+minibuffer is not active. MINIBUF nil or omitted means include
+the minibuffer window only if the minibuffer is active. Any
+other value means do not include the minibuffer window even if
+the minibuffer is active.
Several frames may share a single minibuffer; if the minibuffer
-counts, all windows on all frames that share that minibuffer count
-too. Therefore, if you are using a separate minibuffer frame
-and the minibuffer is active and MINIBUF says it counts,
-`walk-windows' includes the windows in the frame from which you
-entered the minibuffer, as well as the minibuffer window.
-
-ALL-FRAMES is the optional third argument.
-ALL-FRAMES nil or omitted means cycle within the frames as specified above.
-ALL-FRAMES = `visible' means include windows on all visible frames.
-ALL-FRAMES = 0 means include windows on all visible and iconified frames.
-ALL-FRAMES = t means include windows on all frames including invisible frames.
-If ALL-FRAMES is a frame, it means include windows on that frame.
-Anything else means restrict to the selected frame."
- ;; If we start from the minibuffer window, don't fail to come back to it.
- (if (window-minibuffer-p (selected-window))
- (setq minibuf t))
+is active, all windows on all frames that share that minibuffer
+are included too. Therefore, if you are using a separate
+minibuffer frame and the minibuffer is active and MINIBUF says it
+counts, `walk-windows' includes the windows in the frame from
+which you entered the minibuffer, as well as the minibuffer
+window.
+
+ALL-FRAMES nil or omitted means cycle through all windows on
+ WINDOW's frame, plus the minibuffer window if specified by the
+ MINIBUF argument, see above. If the minibuffer counts, cycle
+ through all windows on all frames that share that minibuffer
+ too.
+ALL-FRAMES t means cycle through all windows on all existing
+ frames.
+ALL-FRAMES `visible' means cycle through all windows on all
+ visible frames.
+ALL-FRAMES 0 means cycle through all windows on all visible and
+ iconified frames.
+ALL-FRAMES a frame means cycle through all windows on that frame
+ only.
+Anything else means cycle through all windows on WINDOW's frame
+ and no others.
+
+This function changes neither the order of recently selected
+windows nor the buffer list."
+ ;; If we start from the minibuffer window, don't fail to come
+ ;; back to it.
+ (when (window-minibuffer-p (selected-window))
+ (setq minibuf t))
+ ;; Make sure to not mess up the order of recently selected
+ ;; windows. Use `save-selected-window' and `select-window'
+ ;; with second argument non-nil for this purpose.
(save-selected-window
- (if (framep all-frames)
- (select-window (frame-first-window all-frames)))
+ (when (framep all-frames)
+ (select-window (frame-first-window all-frames) 'norecord))
(let* (walk-windows-already-seen
(walk-windows-current (selected-window)))
(while (progn
(defun get-window-with-predicate (predicate &optional minibuf
all-frames default)
"Return a window satisfying PREDICATE.
-
-This function cycles through all visible windows using `walk-windows',
-calling PREDICATE on each one. PREDICATE is called with a window as
-argument. The first window for which PREDICATE returns a non-nil
-value is returned. If no window satisfies PREDICATE, DEFAULT is
-returned.
-
-Optional second arg MINIBUF t means count the minibuffer window even
-if not active. MINIBUF nil or omitted means count the minibuffer only if
-it is active. MINIBUF neither t nor nil means not to count the
-minibuffer even if it is active.
-
-Several frames may share a single minibuffer; if the minibuffer
-counts, all windows on all frames that share that minibuffer count
-too. Therefore, if you are using a separate minibuffer frame
-and the minibuffer is active and MINIBUF says it counts,
-`walk-windows' includes the windows in the frame from which you
-entered the minibuffer, as well as the minibuffer window.
-
-ALL-FRAMES is the optional third argument.
-ALL-FRAMES nil or omitted means cycle within the frames as specified above.
-ALL-FRAMES = `visible' means include windows on all visible frames.
-ALL-FRAMES = 0 means include windows on all visible and iconified frames.
-ALL-FRAMES = t means include windows on all frames including invisible frames.
-If ALL-FRAMES is a frame, it means include windows on that frame.
-Anything else means restrict to the selected frame."
+More precisely, cycle through all windows using `walk-windows',
+calling the function PREDICATE on each one of them with the
+window as its sole argument. Return the first window for which
+PREDICATE returns non-nil. If no window satisfies PREDICATE,
+return DEFAULT.
+
+The optional arguments MINIBUF and ALL-FRAMES specify the set of
+windows to include. See `walk-windows' for the meaning of these
+arguments."
(catch 'found
(walk-windows #'(lambda (window)
(when (funcall predicate window)
(defalias 'some-window 'get-window-with-predicate)
;; This should probably be written in C (i.e., without using `walk-windows').
-(defun get-buffer-window-list (buffer &optional minibuf all-frames)
- "Return list of all windows displaying BUFFER, or nil if none.
-BUFFER can be a buffer or a buffer name.
-See `walk-windows' for the meaning of MINIBUF and ALL-FRAMES."
- (let ((buffer (if (bufferp buffer) buffer (get-buffer buffer))) windows)
+(defun get-buffer-window-list (&optional buffer-or-name minibuf all-frames)
+ "Return list of all windows displaying BUFFER-OR-NAME, or nil if none.
+BUFFER-OR-NAME may be a buffer or the name of an existing buffer
+and defaults to the current buffer.
+
+The optional arguments MINIBUF and ALL-FRAMES specify the set of
+windows to consider. See `walk-windows' for the precise meaning
+of these arguments."
+ (let ((buffer (cond
+ ((not buffer-or-name) (current-buffer))
+ ((bufferp buffer-or-name) buffer-or-name)
+ (t (get-buffer buffer-or-name))))
+ windows)
(walk-windows (function (lambda (window)
(if (eq (window-buffer window) buffer)
(setq windows (cons window windows)))))
windows))
(defun minibuffer-window-active-p (window)
- "Return t if WINDOW (a minibuffer window) is now active."
+ "Return t if WINDOW is the currently active minibuffer window."
(eq window (active-minibuffer-window)))
\f
(defun count-windows (&optional minibuf)
"Return the number of visible windows.
-This counts the windows in the selected frame and (if the minibuffer is
-to be counted) its minibuffer frame (if that's not the same frame).
-The optional arg MINIBUF non-nil means count the minibuffer
-even if it is inactive."
+The optional argument MINIBUF specifies whether the minibuffer
+window shall be counted. See `walk-windows' for the precise
+meaning of this argument."
(let ((count 0))
(walk-windows (lambda (w) (setq count (+ count 1)))
minibuf)
count))
-
-(defun window-safely-shrinkable-p (&optional window)
- "Non-nil if the WINDOW can be shrunk without shrinking other windows.
-If WINDOW is nil or omitted, it defaults to the currently selected window."
- (with-selected-window (or window (selected-window))
- (let ((edges (window-edges)))
- (or (= (nth 2 edges) (nth 2 (window-edges (previous-window))))
- (= (nth 0 edges) (nth 0 (window-edges (next-window))))))))
-
\f
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; `balance-windows' subroutines using `window-tree'
(dolist (c childs)
(bw-balance-sub c cw ch)))))
-;;; A different solution to balance-windows
-
(defun window-fixed-size-p (&optional window direction)
- "Non-nil if WINDOW cannot be resized in DIRECTION.
-DIRECTION can be nil (i.e. any), `height' or `width'."
+ "Return t if WINDOW cannot be resized in DIRECTION.
+WINDOW defaults to the selected window. DIRECTION can be
+nil (i.e. any), `height' or `width'."
(with-current-buffer (window-buffer window)
- (let ((fixed (and (boundp 'window-size-fixed) window-size-fixed)))
- (when fixed
- (not (and direction
- (member (cons direction window-size-fixed)
- '((height . width) (width . height)))))))))
+ (when (and (boundp 'window-size-fixed) window-size-fixed)
+ (not (and direction
+ (member (cons direction window-size-fixed)
+ '((height . width) (width . height))))))))
+
+;;; A different solution to balance-windows.
(defvar window-area-factor 1
"Factor by which the window area should be over-estimated.
(defun balance-windows-area ()
"Make all visible windows the same area (approximately).
-See also `window-area-factor' to change the relative size of specific buffers."
+See also `window-area-factor' to change the relative size of
+specific buffers."
(interactive)
(let* ((unchanged 0) (carry 0) (round 0)
;; Remove fixed-size windows.
BUFFER in any way it likes. All this is done by the function
found in `special-display-function'.
-If the specified frame parameters include (same-buffer . t), the
-buffer is displayed in the currently selected window. Otherwise, if
-they include (same-frame . t), the buffer is displayed in a new window
-in the currently selected frame.
+If the specified frame parameters include (same-window . t), the
+buffer is displayed in the currently selected window. Otherwise,
+if they include (same-frame . t), the buffer is displayed in a
+new window in the currently selected frame.
If this variable appears \"not to work\", because you add a name to it
but that buffer still appears in the selected window, look at the
the buffer in any way it likes. All this is done by the function
found in `special-display-function'.
-If the specified frame parameters include (same-buffer . t), the
+If the specified frame parameters include (same-window . t), the
buffer is displayed in the currently selected window. Otherwise,
if they include (same-frame . t), the buffer is displayed in a
new window in the currently selected frame.
using `special-display-frame-alist' to specify the frame
parameters.
-But if the buffer specific data includes (same-buffer . t) then
-the buffer is displayed in the current selected window.
-Otherwise if it includes (same-frame . t) then the buffer is
+But if the buffer specific data includes (same-window . t) then
+the buffer is displayed in the currently selected window.
+Otherwise, if it includes (same-frame . t) then the buffer is
displayed in a new window in the currently selected frame.
A buffer is special if it is listed in
:group 'windows)
(defcustom pop-up-frames nil
- "Non-nil means `display-buffer' should make a separate frame."
- :type 'boolean
+ "Whether `display-buffer' should make a separate frame.
+If nil, never make a seperate frame.
+If the value is `graphic-only', make a separate frame
+on graphic displays only.
+Any other non-nil value means always make a separate frame."
+ :type '(choice
+ (const :tag "Never" nil)
+ (const :tag "On graphic displays only" graphic-only)
+ (const :tag "Always" t))
:group 'windows)
(defcustom display-buffer-reuse-frames nil
:version "21.1"
:group 'windows)
-(defcustom pop-up-frame-function nil
- "Function to call to handle automatic new frame creation.
-It is called with no arguments and should return a newly created frame.
-
-A typical value might be
-
-`(lambda () (new-frame pop-up-frame-alist))'
-
-where `pop-up-frame-alist' would hold the default frame
-parameters."
- :type '(choice
- (const nil)
- (function :tag "function"))
- :group 'windows)
-
(defcustom pop-up-windows t
"Non-nil means `display-buffer' should make a new window."
:type 'boolean
If the window is the only window on its frame, `display-buffer'
can split it regardless of this value."
:type '(choice (const nil) (number :tag "lines"))
- :type 'number
:version "23.1"
:group 'windows)
:group 'windows)
(defun window--splittable-p (window &optional horizontal)
- "Return non-nil if window WINDOW can be split evenly.
+ "Return non-nil if WINDOW can be split evenly.
Optional argument HORIZONTAL non-nil means check whether WINDOW
can be split horizontally.
high as `split-height-threshold'.
- When WINDOW is split evenly, the emanating windows are at least
- `window-min-height' lines tall and can accomodate at least one
- line plus - if WINDOW has one - a modeline.
+ `window-min-height' lines tall and can accommodate at least one
+ line plus - if WINDOW has one - a mode line.
WINDOW can be split horizontally when the following conditions
hold:
(if mode-line-format 2 1))))))))))
(defun window--try-to-split-window (window)
- "Split window WINDOW if it is splittable.
+ "Split WINDOW if it is splittable.
See `window--splittable-p' for how to determine whether a window
is splittable. If WINDOW can be split, return the value returned
-by `split-window' or `split-window-preferred-function'."
+by `split-window' (or `split-window-preferred-function')."
(when (and (window-live-p window)
(not (frame-parameter (window-frame window) 'unsplittable)))
(if (functionp split-window-preferred-function)
;; minibuffer window, attempt to split it vertically
;; disregarding the value of `split-height-threshold'.
(let ((split-height-threshold 0))
- (window--splittable-p window)
- (split-window window)))))))
+ (and (window--splittable-p window)
+ (split-window window))))))))
(defun window--frame-usable-p (frame)
- "Return frame FRAME if it can be used to display another buffer."
- (when (framep frame)
+ "Return FRAME if it can be used to display a buffer."
+ (when (frame-live-p frame)
(let ((window (frame-root-window frame)))
;; `frame-root-window' may be an internal window which is considered
;; "dead" by `window-live-p'. Hence if `window' is not live we
;; implicitly know that `frame' has a visible window we can use.
- (when (or (not (window-live-p window))
- (and (not (window-minibuffer-p window))
- (not (window-dedicated-p window))))
+ (unless (and (window-live-p window)
+ (or (window-minibuffer-p window)
+ ;; If the window is soft-dedicated, the frame is usable.
+ ;; Actually, even if the window is really dedicated,
+ ;; the frame is still usable by splitting it.
+ ;; At least Emacs-22 allowed it, and it is desirable
+ ;; when displaying same-frame windows.
+ nil ; (eq t (window-dedicated-p window))
+ ))
frame))))
(defcustom even-window-heights t
:group 'windows)
(defun window--even-window-heights (window)
- "Even heights of window WINDOW and selected window.
+ "Even heights of WINDOW and selected window.
Do this only if these windows are vertically adjacent to each
other, `even-window-heights' is non-nil, and the selected window
is higher than WINDOW."
(error nil)))))
(defun window--display-buffer-1 (window)
- "Deiconify the frame containing the window WINDOW.
-Do not deiconify the selected frame. Return WINDOW."
+ "Raise the frame containing WINDOW.
+Do not raise the selected frame. Return WINDOW."
(let* ((frame (window-frame window))
(visible (frame-visible-p frame)))
(unless (or (not visible)
;; is visible.
(and (minibuffer-window-active-p (selected-window))
(eq frame (window-frame (minibuffer-selected-window)))))
- (when (eq visible 'icon)
- (make-frame-visible frame))
(raise-frame frame))
window))
(defun window--display-buffer-2 (buffer window)
- "Display buffer BUFFER in window WINDOW and make its frame visible.
+ "Display BUFFER in WINDOW and make its frame visible.
Return WINDOW."
(when (and (buffer-live-p buffer) (window-live-p window))
(set-window-buffer window buffer)
"Make buffer BUFFER-OR-NAME appear in some window but don't select it.
BUFFER-OR-NAME must be a buffer or the name of an existing
buffer. Return the window chosen to display BUFFER-OR-NAME or
-nil is no such window is found.
+nil if no such window is found.
Optional argument NOT-THIS-WINDOW non-nil means display the
buffer in a window other than the selected one, even if it is
nil - consider windows on the selected frame \(actually the
last non-minibuffer frame\) only. If, however, either
-`display-buffer-reuse-frames' or `pop-up-frames' is non-nil,
+`display-buffer-reuse-frames' or `pop-up-frames' is non-nil
+\(non-nil and not graphic-only on a text-only terminal),
consider all visible or iconified frames."
(interactive "BDisplay buffer:\nP")
(let* ((can-use-selected-window
buffer-or-name
(get-buffer buffer-or-name)))
(name-of-buffer (buffer-name buffer))
+ ;; On text-only terminals do not pop up a new frame when
+ ;; `pop-up-frames' equals graphic-only.
+ (use-pop-up-frames (if (eq pop-up-frames 'graphic-only)
+ (display-graphic-p)
+ pop-up-frames))
;; `frame-to-use' is the frame where to show `buffer' - either
;; the selected frame or the last nonminibuffer frame.
(frame-to-use
;; If the buffer's name tells us to use the selected window do so.
(window--display-buffer-2 buffer (selected-window)))
((let ((frames (or frame
- (and (or pop-up-frames display-buffer-reuse-frames
+ (and (or use-pop-up-frames
+ display-buffer-reuse-frames
(not (last-nonminibuffer-frame)))
0)
(last-nonminibuffer-frame))))
- (and (setq window-to-use (get-buffer-window buffer frames))
- (or can-use-selected-window
- (not (eq (selected-window) window-to-use)))))
- ;; If the buffer is already displayed in some window use that.
+ (setq window-to-use
+ (catch 'found
+ ;; Search frames for a window displaying BUFFER. Return
+ ;; the selected window only if we are allowed to do so.
+ (dolist (window (get-buffer-window-list buffer 'nomini frames))
+ (when (or can-use-selected-window
+ (not (eq (selected-window) window)))
+ (throw 'found window))))))
+ ;; The buffer is already displayed in some window; use that.
(window--display-buffer-1 window-to-use))
((and special-display-function
;; `special-display-p' returns either t or a list of frame
(when pars
(funcall special-display-function
buffer (if (listp pars) pars))))))
- ((or pop-up-frames (not frame-to-use))
+ ((or use-pop-up-frames (not frame-to-use))
;; We want or need a new frame.
(window--display-buffer-2
buffer (frame-selected-window (funcall pop-up-frame-function))))
(window--try-to-split-window
(get-lru-window frame-to-use t))))
(window--display-buffer-2 buffer window-to-use)))
- ((setq window-to-use
- ;; Reuse an existing window.
- (or (get-lru-window frame-to-use)
- (get-buffer-window buffer 'visible)
- (get-largest-window 'visible nil)
- (get-buffer-window buffer 0)
- (get-largest-window 0 nil)
- (frame-selected-window (funcall pop-up-frame-function))))
+ ((let ((window-to-undedicate
+ ;; When NOT-THIS-WINDOW is non-nil, temporarily dedicate
+ ;; the selected window to its buffer, to avoid that some of
+ ;; the `get-' routines below choose it. (Bug#1415)
+ (and not-this-window (not (window-dedicated-p))
+ (set-window-dedicated-p (selected-window) t)
+ (selected-window))))
+ (unwind-protect
+ (setq window-to-use
+ ;; Reuse an existing window.
+ (or (get-lru-window frame-to-use)
+ (let ((window (get-buffer-window buffer 'visible)))
+ (unless (and not-this-window
+ (eq window (selected-window)))
+ window))
+ (get-largest-window 'visible)
+ (let ((window (get-buffer-window buffer 0)))
+ (unless (and not-this-window
+ (eq window (selected-window)))
+ window))
+ (get-largest-window 0)
+ (frame-selected-window (funcall pop-up-frame-function))))
+ (when (window-live-p window-to-undedicate)
+ ;; Restore dedicated status of selected window.
+ (set-window-dedicated-p window-to-undedicate nil))))
(window--even-window-heights window-to-use)
(window--display-buffer-2 buffer window-to-use)))))
already visible in the selected window, and ignore
`same-window-regexps' and `same-window-buffer-names'.
+If the window to show BUFFER-OR-NAME is not on the selected
+frame, raise that window's frame and give it input focus.
+
This function returns the buffer it switched to. This uses the
function `display-buffer' as a subroutine; see the documentation
of `display-buffer' for additional customization information.
(or (get-buffer buffer-or-name)
(let ((buf (get-buffer-create buffer-or-name)))
(set-buffer-major-mode buf)
- buf)))))
+ buf))))
+ (old-window (selected-window))
+ (old-frame (selected-frame))
+ new-window new-frame)
(set-buffer buffer)
- (select-window (display-buffer buffer other-window) norecord)
+ (setq new-window (display-buffer buffer other-window))
+ (unless (eq new-window old-window)
+ ;; `display-buffer' has chosen another window, select it.
+ (select-window new-window norecord)
+ (setq new-frame (window-frame new-window))
+ (unless (eq new-frame old-frame)
+ ;; `display-buffer' has chosen another frame, make sure it gets
+ ;; input focus and is risen.
+ (select-frame-set-input-focus new-frame)))
buffer))
;; I think this should be the default; I think people will prefer it--rms.
:type 'boolean
:group 'windows)
-(defun split-window-vertically (&optional arg)
- "Split current window into two windows, one above the other.
-The uppermost window gets ARG lines and the other gets the rest.
-Negative ARG means select the size of the lowermost window instead.
-With no argument, split equally or close to it.
-Both windows display the same buffer now current.
+(defun split-window-vertically (&optional size)
+ "Split selected window into two windows, one above the other.
+The upper window gets SIZE lines and the lower one gets the rest.
+SIZE negative means the lower window gets -SIZE lines and the
+upper one the rest. With no argument, split windows equally or
+close to it. Both windows display the same buffer, now current.
-If the variable `split-window-keep-point' is non-nil, both new windows
-will get the same value of point as the current window. This is often
-more convenient for editing. The upper window is the selected window.
+If the variable `split-window-keep-point' is non-nil, both new
+windows will get the same value of point as the selected window.
+This is often more convenient for editing. The upper window is
+the selected window.
Otherwise, we choose window starts so as to minimize the amount of
redisplay; this is convenient on slow terminals. The new selected
window is the original one and the return value is the new, lower
window."
(interactive "P")
- (let ((old-w (selected-window))
+ (let ((old-window (selected-window))
(old-point (point))
- (size (and arg (prefix-numeric-value arg)))
- (window-full-p nil)
- new-w bottom moved)
- (and size (< size 0) (setq size (+ (window-height) size)))
- (setq new-w (split-window nil size))
- (or split-window-keep-point
- (progn
- (save-excursion
- (set-buffer (window-buffer))
- (goto-char (window-start))
- (setq moved (vertical-motion (window-height)))
- (set-window-start new-w (point))
- (if (> (point) (window-point new-w))
- (set-window-point new-w (point)))
- (and (= moved (window-height))
- (progn
- (setq window-full-p t)
- (vertical-motion -1)))
- (setq bottom (point)))
- (and window-full-p
- (<= bottom (point))
- (set-window-point old-w (1- bottom)))
- (and window-full-p
- (<= (window-start new-w) old-point)
- (progn
- (set-window-point new-w old-point)
- (select-window new-w)))))
- (split-window-save-restore-data new-w old-w)))
+ (size (and size (prefix-numeric-value size)))
+ moved-by-window-height moved new-window bottom)
+ (and size (< size 0)
+ ;; Handle negative SIZE value.
+ (setq size (+ (window-height) size)))
+ (setq new-window (split-window nil size))
+ (unless split-window-keep-point
+ (save-excursion
+ (set-buffer (window-buffer))
+ (goto-char (window-start))
+ (setq moved (vertical-motion (window-height)))
+ (set-window-start new-window (point))
+ (when (> (point) (window-point new-window))
+ (set-window-point new-window (point)))
+ (when (= moved (window-height))
+ (setq moved-by-window-height t)
+ (vertical-motion -1))
+ (setq bottom (point)))
+ (and moved-by-window-height
+ (<= bottom (point))
+ (set-window-point old-window (1- bottom)))
+ (and moved-by-window-height
+ (<= (window-start new-window) old-point)
+ (set-window-point new-window old-point)
+ (select-window new-window)))
+ (split-window-save-restore-data new-window old-window)))
;; This is to avoid compiler warnings.
(defvar view-return-to-alist)
-(defun split-window-save-restore-data (new-w old-w)
+(defun split-window-save-restore-data (new-window old-window)
(with-current-buffer (window-buffer)
- (if view-mode
- (let ((old-info (assq old-w view-return-to-alist)))
- (if old-info
- (push (cons new-w (cons (car (cdr old-info)) t))
- view-return-to-alist))))
- new-w))
-
-(defun split-window-horizontally (&optional arg)
- "Split current window into two windows side by side.
-This window becomes the leftmost of the two, and gets ARG columns.
-Negative ARG means select the size of the rightmost window instead.
-The argument includes the width of the window's scroll bar; if there
-are no scroll bars, it includes the width of the divider column
-to the window's right, if any. No ARG means split equally.
-
-The original, leftmost window remains selected.
-The return value is the new, rightmost window."
+ (when view-mode
+ (let ((old-info (assq old-window view-return-to-alist)))
+ (when old-info
+ (push (cons new-window (cons (car (cdr old-info)) t))
+ view-return-to-alist))))
+ new-window))
+
+(defun split-window-horizontally (&optional size)
+ "Split selected window into two windows side by side.
+The selected window becomes the left one and gets SIZE columns.
+SIZE negative means the right window gets -SIZE lines.
+
+SIZE includes the width of the window's scroll bar; if there are
+no scroll bars, it includes the width of the divider column to
+the window's right, if any. SIZE omitted or nil means split
+window equally.
+
+The selected window remains selected. Return the new window."
(interactive "P")
- (let ((old-w (selected-window))
- (size (and arg (prefix-numeric-value arg))))
+ (let ((old-window (selected-window))
+ (size (and size (prefix-numeric-value size))))
(and size (< size 0)
+ ;; Handle negative SIZE value.
(setq size (+ (window-width) size)))
- (split-window-save-restore-data (split-window nil size t) old-w)))
+ (split-window-save-restore-data (split-window nil size t) old-window)))
\f
(defun set-window-text-height (window height)
- "Sets the height in lines of the text display area of WINDOW to HEIGHT.
-This doesn't include the mode-line (or header-line if any) or any
-partial-height lines in the text display area.
-
-If WINDOW is nil, the selected window is used.
-
-Note that the current implementation of this function cannot always set
-the height exactly, but attempts to be conservative, by allocating more
-lines than are actually needed in the case where some error may be present."
+ "Set the height in lines of the text display area of WINDOW to HEIGHT.
+HEIGHT doesn't include the mode line or header line, if any, or
+any partial-height lines in the text display area.
+
+Note that the current implementation of this function cannot
+always set the height exactly, but attempts to be conservative,
+by allocating more lines than are actually needed in the case
+where some error may be present."
(let ((delta (- height (window-text-height window))))
(unless (zerop delta)
;; Setting window-min-height to a value like 1 can lead to very
;; bizarre displays because it also allows Emacs to make *other*
;; windows 1-line tall, which means that there's no more space for
;; the modeline.
- (let ((window-min-height (min 2 height))) ;One text line plus a modeline.
+ (let ((window-min-height (min 2 height))) ; One text line plus a modeline.
(if (and window (not (eq window (selected-window))))
(save-selected-window
- (select-window window)
+ (select-window window 'norecord)
(enlarge-window delta))
(enlarge-window delta))))))
\f
-(defun enlarge-window-horizontally (arg)
- "Make current window ARG columns wider."
+(defun enlarge-window-horizontally (columns)
+ "Make selected window COLUMNS wider.
+Interactively, if no argument is given, make selected window one
+column wider."
(interactive "p")
- (enlarge-window arg t))
+ (enlarge-window columns t))
-(defun shrink-window-horizontally (arg)
- "Make current window ARG columns narrower."
+(defun shrink-window-horizontally (columns)
+ "Make selected window COLUMNS narrower.
+Interactively, if no argument is given, make selected window one
+column narrower."
(interactive "p")
- (shrink-window arg t))
+ (shrink-window columns t))
(defun window-buffer-height (window)
"Return the height (in screen lines) of the buffer that WINDOW is displaying."
(1+ (vertical-motion (buffer-size) window))))))
(defun fit-window-to-buffer (&optional window max-height min-height)
- "Make WINDOW the right height to display its contents exactly.
-If WINDOW is omitted or nil, it defaults to the selected window.
-If the optional argument MAX-HEIGHT is supplied, it is the maximum height
- the window is allowed to be, defaulting to the frame height.
-If the optional argument MIN-HEIGHT is supplied, it is the minimum
- height the window is allowed to be, defaulting to `window-min-height'.
-
-The heights in MAX-HEIGHT and MIN-HEIGHT include the mode-line and/or
-header-line."
+ "Adjust height of WINDOW to display its buffer's contents exactly.
+WINDOW defaults to the selected window.
+Optional argument MAX-HEIGHT specifies the maximum height of the
+window and defaults to the maximum permissible height of a window
+on WINDOW's frame.
+Optional argument MIN-HEIGHT specifies the minimum height of the
+window and defaults to `window-min-height'.
+Both, MAX-HEIGHT and MIN-HEIGHT are specified in lines and
+include the mode line and header line, if any.
+
+Return non-nil if height was orderly adjusted, nil otherwise.
+
+Caution: This function can delete WINDOW and/or other windows
+when their height shrinks to less than MIN-HEIGHT."
(interactive)
+ ;; Do all the work in WINDOW and its buffer and restore the selected
+ ;; window and the current buffer when we're done.
+ (let ((old-buffer (current-buffer))
+ value)
+ (with-selected-window (or window (setq window (selected-window)))
+ (set-buffer (window-buffer))
+ ;; Use `condition-case' to handle any fixed-size windows and other
+ ;; pitfalls nearby.
+ (condition-case nil
+ (let* (;; MIN-HEIGHT must not be less than 1 and defaults to
+ ;; `window-min-height'.
+ (min-height (max (or min-height window-min-height) 1))
+ (max-window-height
+ ;; Maximum height of any window on this frame.
+ (min (window-height (frame-root-window)) (frame-height)))
+ ;; MAX-HEIGHT must not be larger than max-window-height and
+ ;; defaults to max-window-height.
+ (max-height
+ (min (or max-height max-window-height) max-window-height))
+ (desired-height
+ ;; The height necessary to show all of WINDOW's buffer,
+ ;; constrained by MIN-HEIGHT and MAX-HEIGHT.
+ (max
+ (min
+ ;; For an empty buffer `count-screen-lines' returns zero.
+ ;; Even in that case we need one line for the cursor.
+ (+ (max (count-screen-lines) 1)
+ ;; For non-minibuffers count the mode line, if any.
+ (if (and (not (window-minibuffer-p)) mode-line-format)
+ 1 0)
+ ;; Count the header line, if any.
+ (if header-line-format 1 0))
+ max-height)
+ min-height))
+ (delta
+ ;; How much the window height has to change.
+ (if (= (window-height) (window-height (frame-root-window)))
+ ;; Don't try to resize a full-height window.
+ 0
+ (- desired-height (window-height))))
+ ;; Do something reasonable so `enlarge-window' can make
+ ;; windows as small as MIN-HEIGHT.
+ (window-min-height (min min-height window-min-height)))
+ ;; Don't try to redisplay with the cursor at the end on its
+ ;; own line--that would force a scroll and spoil things.
+ (when (and (eobp) (bolp) (not (bobp)))
+ (set-window-point window (1- (window-point))))
+ ;; Adjust WINDOW's height to the nominally correct one
+ ;; (which may actually be slightly off because of variable
+ ;; height text, etc).
+ (unless (zerop delta)
+ (enlarge-window delta))
+ ;; `enlarge-window' might have deleted WINDOW, so make sure
+ ;; WINDOW's still alive for the remainder of this.
+ ;; Note: Deleting WINDOW is clearly counter-intuitive in
+ ;; this context, but we can't do much about it given the
+ ;; current semantics of `enlarge-window'.
+ (when (window-live-p window)
+ ;; Check if the last line is surely fully visible. If
+ ;; not, enlarge the window.
+ (let ((end (save-excursion
+ (goto-char (point-max))
+ (when (and (bolp) (not (bobp)))
+ ;; Don't include final newline.
+ (backward-char 1))
+ (when truncate-lines
+ ;; If line-wrapping is turned off, test the
+ ;; beginning of the last line for
+ ;; visibility instead of the end, as the
+ ;; end of the line could be invisible by
+ ;; virtue of extending past the edge of the
+ ;; window.
+ (forward-line 0))
+ (point))))
+ (set-window-vscroll window 0)
+ (while (and (< desired-height max-height)
+ (= desired-height (window-height))
+ (not (pos-visible-in-window-p end)))
+ (enlarge-window 1)
+ (setq desired-height (1+ desired-height))))
+ ;; Return non-nil only if nothing "bad" happened.
+ (setq value t)))
+ (error nil)))
+ (when (buffer-live-p old-buffer)
+ (set-buffer old-buffer))
+ value))
- (when (null window)
- (setq window (selected-window)))
- (when (null max-height)
- (setq max-height (frame-height (window-frame window))))
-
- (let* ((buf
- ;; Buffer that is displayed in WINDOW
- (window-buffer window))
- (window-height
- ;; The current height of WINDOW
- (window-height window))
- (desired-height
- ;; The height necessary to show the buffer displayed by WINDOW
- ;; (`count-screen-lines' always works on the current buffer).
- (with-current-buffer buf
- (+ (count-screen-lines)
- ;; If the buffer is empty, (count-screen-lines) is
- ;; zero. But, even in that case, we need one text line
- ;; for cursor.
- (if (= (point-min) (point-max))
- 1 0)
- ;; For non-minibuffers, count the mode-line, if any
- (if (and (not (window-minibuffer-p window))
- mode-line-format)
- 1 0)
- ;; Count the header-line, if any
- (if header-line-format 1 0))))
- (delta
- ;; Calculate how much the window height has to change to show
- ;; desired-height lines, constrained by MIN-HEIGHT and MAX-HEIGHT.
- (- (max (min desired-height max-height)
- (or min-height window-min-height))
- window-height)))
-
- ;; Don't try to redisplay with the cursor at the end
- ;; on its own line--that would force a scroll and spoil things.
- (when (with-current-buffer buf
- (and (eobp) (bolp) (not (bobp))))
- (set-window-point window (1- (window-point window))))
-
- (save-selected-window
- (select-window window)
-
- ;; Adjust WINDOW to the nominally correct size (which may actually
- ;; be slightly off because of variable height text, etc).
- (unless (zerop delta)
- (enlarge-window delta))
-
- ;; Check if the last line is surely fully visible. If not,
- ;; enlarge the window.
- (let ((end (with-current-buffer buf
- (save-excursion
- (goto-char (point-max))
- (when (and (bolp) (not (bobp)))
- ;; Don't include final newline
- (backward-char 1))
- (when truncate-lines
- ;; If line-wrapping is turned off, test the
- ;; beginning of the last line for visibility
- ;; instead of the end, as the end of the line
- ;; could be invisible by virtue of extending past
- ;; the edge of the window.
- (forward-line 0))
- (point)))))
- (set-window-vscroll window 0)
- (while (and (< desired-height max-height)
- (= desired-height (window-height window))
- (not (pos-visible-in-window-p end window)))
- (enlarge-window 1)
- (setq desired-height (1+ desired-height)))))))
+(defun window-safely-shrinkable-p (&optional window)
+ "Return t if WINDOW can be shrunk without shrinking other windows.
+WINDOW defaults to the selected window."
+ (with-selected-window (or window (selected-window))
+ (let ((edges (window-edges)))
+ (or (= (nth 2 edges) (nth 2 (window-edges (previous-window))))
+ (= (nth 0 edges) (nth 0 (window-edges (next-window))))))))
(defun shrink-window-if-larger-than-buffer (&optional window)
- "Shrink the WINDOW to be as small as possible to display its contents.
-If WINDOW is omitted or nil, it defaults to the selected window.
-Do not shrink to less than `window-min-height' lines.
-Do nothing if the buffer contains more lines than the present window height,
-or if some of the window's contents are scrolled out of view,
-or if shrinking this window would also shrink another window,
-or if the window is the only window of its frame."
+ "Shrink height of WINDOW if its buffer doesn't need so many lines.
+More precisely, shrink WINDOW vertically to be as small as
+possible, while still showing the full contents of its buffer.
+WINDOW defaults to the selected window.
+
+Do not shrink to less than `window-min-height' lines. Do nothing
+if the buffer contains more lines than the present window height,
+or if some of the window's contents are scrolled out of view, or
+if shrinking this window would also shrink another window, or if
+the window is the only window of its frame.
+
+Return non-nil if the window was shrunk, nil otherwise."
(interactive)
(when (null window)
(setq window (selected-window)))
(mini (frame-parameter frame 'minibuffer))
(edges (window-edges window)))
(if (and (not (eq window (frame-root-window frame)))
- (window-safely-shrinkable-p)
+ (window-safely-shrinkable-p window)
(pos-visible-in-window-p (point-min) window)
(not (eq mini 'only))
(or (not mini)
(error nil)))))
(defun quit-window (&optional kill window)
- "Quit the current buffer. Bury it, and maybe delete the selected frame.
-\(The frame is deleted if it contains a dedicated window for the buffer.)
-With a prefix argument, kill the buffer instead.
+ "Quit WINDOW and bury its buffer.
+With a prefix argument, kill the buffer instead. WINDOW defaults
+to the selected window.
-Noninteractively, if KILL is non-nil, then kill the current buffer,
-otherwise bury it.
+If WINDOW is non-nil, dedicated, or a minibuffer window, delete
+it and, if it's alone on its frame, its frame too. Otherwise, or
+if deleting WINDOW fails in any of the preceding cases, display
+another buffer in WINDOW using `switch-to-buffer'.
-If WINDOW is non-nil, it specifies a window; we delete that window,
-and the buffer that is killed or buried is the one in that window."
+Optional argument KILL non-nil means kill WINDOW's buffer.
+Otherwise, bury WINDOW's buffer, see `bury-buffer'."
(interactive "P")
- (let ((buffer (window-buffer window))
- (frame (window-frame (or window (selected-window))))
- (window-solitary
- (save-selected-window
- (if window
- (select-window window))
- (one-window-p t)))
- window-handled)
-
- (save-selected-window
- (if window
- (select-window window))
- (or (window-minibuffer-p)
- (window-dedicated-p (selected-window))
- (switch-to-buffer (other-buffer))))
-
- ;; Get rid of the frame, if it has just one dedicated window
- ;; and other visible frames exist.
- (and (or (window-minibuffer-p) (window-dedicated-p window))
- (delq frame (visible-frame-list))
- window-solitary
- (if (and (eq default-minibuffer-frame frame)
- (= 1 (length (minibuffer-frame-list))))
- (setq window nil)
- (delete-frame frame)
- (setq window-handled t)))
+ (let ((buffer (window-buffer window)))
+ (if (or window
+ (window-minibuffer-p window)
+ (window-dedicated-p window))
+ ;; WINDOW is either non-nil, a minibuffer window, or dedicated;
+ ;; try to delete it.
+ (let* ((window (or window (selected-window)))
+ (frame (window-frame window)))
+ (if (eq window (frame-root-window frame))
+ ;; WINDOW is alone on its frame. `delete-windows-on'
+ ;; knows how to handle that case.
+ (delete-windows-on buffer frame)
+ ;; There are other windows on its frame, delete WINDOW.
+ (delete-window window)))
+ ;; Otherwise, switch to another buffer in the selected window.
+ (switch-to-buffer nil))
;; Deal with the buffer.
(if kill
(kill-buffer buffer)
- (bury-buffer buffer))
-
- ;; Maybe get rid of the window.
- (and window (not window-handled) (not window-solitary)
- (delete-window window))))
+ (bury-buffer buffer))))
(defvar recenter-last-op nil
"Indicates the last recenter operation performed.
(defun recenter-top-bottom (&optional arg)
"Move current line to window center, top, and bottom, successively.
-With a prefix argument, this is the same as `recenter':
+With no prefix argument, the first call redraws the frame and
+ centers point vertically within the window. Successive calls
+ scroll the window, placing point on the top, bottom, and middle
+ consecutively. The cycling order is middle -> top -> bottom.
+
+A prefix argument is handled like `recenter':
With numeric prefix ARG, move current line to window-line ARG.
With plain `C-u', move current line to window center.
-Otherwise move current line to window center on first call, and to
-top, middle, or bottom on successive calls.
-
-The cycling order is: middle -> top -> bottom.
-
-Top and bottom destinations are actually `scroll-conservatively' lines
-from true window top and bottom."
+Top and bottom destinations are actually `scroll-margin' lines
+ the from true window top and bottom."
(interactive "P")
(cond
(arg (recenter arg)) ; Always respect ARG.
- ((not (eq this-command last-command))
- ;; First time - save mode and recenter.
+ ((or (not (eq this-command last-command))
+ (eq recenter-last-op 'bottom))
(setq recenter-last-op 'middle)
(recenter))
- (t ;; repeat: loop through various options.
- (setq recenter-last-op
- (cond ((eq recenter-last-op 'middle)
- (recenter scroll-conservatively)
- 'top)
- ((eq recenter-last-op 'top)
- (recenter (1- (- scroll-conservatively)))
- 'bottom)
- ((eq recenter-last-op 'bottom)
- (recenter)
- 'middle))))))
+ (t
+ (let ((this-scroll-margin
+ (min (max 0 scroll-margin)
+ (truncate (/ (window-body-height) 4.0)))))
+ (cond ((eq recenter-last-op 'middle)
+ (setq recenter-last-op 'top)
+ (recenter this-scroll-margin))
+ ((eq recenter-last-op 'top)
+ (setq recenter-last-op 'bottom)
+ (recenter (- -1 this-scroll-margin))))))))
(define-key global-map [?\C-l] 'recenter-top-bottom)
\f
(push w delenda))))
(mapc 'delete-window delenda)))
+(defun truncated-partial-width-window-p (&optional window)
+ "Return non-nil if lines in WINDOW are specifically truncated due to its width.
+WINDOW defaults to the selected window.
+Return nil if WINDOW is not a partial-width window
+ (regardless of the value of `truncate-lines').
+Otherwise, consult the value of `truncate-partial-width-windows'
+ for the buffer shown in WINDOW."
+ (unless window
+ (setq window (selected-window)))
+ (unless (window-full-width-p window)
+ (let ((t-p-w-w (buffer-local-value 'truncate-partial-width-windows
+ (window-buffer window))))
+ (if (integerp t-p-w-w)
+ (< (window-width window) t-p-w-w)
+ t-p-w-w))))
+
(define-key ctl-x-map "2" 'split-window-vertically)
(define-key ctl-x-map "3" 'split-window-horizontally)
(define-key ctl-x-map "}" 'enlarge-window-horizontally)