X-Git-Url: https://git.hcoop.net/bpt/emacs.git/blobdiff_plain/0e8ade71d7d1091e099d1c6dc8a3cee9707ce022..9478502212bb8eea71a5d96805fe40f3c9d29561:/lisp/window.el diff --git a/lisp/window.el b/lisp/window.el index b3ec25e1f6..41a5d17321 100644 --- a/lisp/window.el +++ b/lisp/window.el @@ -1,7 +1,8 @@ ;;; 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, 2009 Free Software Foundation, Inc. +;; 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 +;; Free Software Foundation, Inc. ;; Maintainer: FSF ;; Keywords: internal @@ -76,9 +77,9 @@ 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." +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) @@ -87,6 +88,16 @@ return value, use `window-text-height' instead." (if mode-line-format 1 0) (if header-line-format 1 0)))))) +;; See discussion in bug#4543. +(defun window-full-height-p (&optional window) + "Return non-nil if WINDOW is not the result of a vertical split. +WINDOW defaults to the selected window. (This function is not +appropriate for minibuffers.)" + (unless window + (setq window (selected-window))) + (= (window-height window) + (window-height (frame-root-window (window-frame window))))) + (defun one-window-p (&optional nomini all-frames) "Return non-nil if the selected window is the only window. Optional arg NOMINI non-nil means don't count the minibuffer @@ -612,6 +623,9 @@ See also `special-display-regexps'." :group 'windows :group 'frames) +;;;###autoload +(put 'special-display-buffer-names 'risky-local-variable t) + (defcustom special-display-regexps nil "List of regexps saying which buffers should be displayed specially. Displaying a buffer with `display-buffer' or `pop-to-buffer', if @@ -771,7 +785,7 @@ selected rather than \(as usual\) some other window. See (defcustom pop-up-frames nil "Whether `display-buffer' should make a separate frame. -If nil, never make a seperate frame. +If nil, never make a separate 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." @@ -794,66 +808,78 @@ that frame." :type 'boolean :group 'windows) -(defcustom split-height-threshold 80 - "Minimum height of window to be split vertically. -If the value is a number, `display-buffer' can split a window -only if it has at least as many lines. If the value is nil, -`display-buffer' cannot split a window vertically. - -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")) +(defcustom split-window-preferred-function 'split-window-sensibly + "Function called by `display-buffer' routines to split a window. +This function is called with a window as single argument and is +supposed to split that window and return the new window. If the +window can (or shall) not be split, it is supposed to return nil. +The default is to call the function `split-window-sensibly' which +tries to split the window in a way which seems most suitable. +You can customize the options `split-height-threshold' and/or +`split-width-threshold' in order to have `split-window-sensibly' +prefer either vertical or horizontal splitting. + +If you set this to any other function, bear in mind that the +`display-buffer' routines may call this function two times. The +argument of the first call is the largest window on its frame. +If that call fails to return a live window, the function is +called again with the least recently used window as argument. If +that call fails too, `display-buffer' will use an existing window +to display its buffer. + +The window selected at the time `display-buffer' was invoked is +still selected when this function is called. Hence you can +compare the window argument with the value of `selected-window' +if you intend to split the selected window instead or if you do +not want to split the selected window." + :type 'function :version "23.1" :group 'windows) -(defcustom split-width-threshold 160 - "Minimum width of window to be split horizontally. -If the value is a number, `display-buffer' can split a window -only if it has at least as many columns. If the value is nil, -`display-buffer' cannot split a window horizontally." - :type '(choice (const nil) (number :tag "columns")) +(defcustom split-height-threshold 80 + "Minimum height for splitting windows sensibly. +If this is an integer, `split-window-sensibly' may split a window +vertically only if it has at least this many lines. If this is +nil, `split-window-sensibly' is not allowed to split a window +vertically. If, however, a window is the only window on its +frame, `split-window-sensibly' may split it vertically +disregarding the value of this variable." + :type '(choice (const nil) (integer :tag "lines")) :version "23.1" :group 'windows) -(defcustom split-window-preferred-function nil - "Function used by `display-buffer' to split windows. -If non-nil, a function called with a window as single argument -supposed to split that window and return the new window. If the -function returns nil the window is not split. - -If nil, `display-buffer' will split the window respecting the -values of `split-height-threshold' and `split-width-threshold'." - :type '(choice (const nil) (function :tag "Function")) +(defcustom split-width-threshold 160 + "Minimum width for splitting windows sensibly. +If this is an integer, `split-window-sensibly' may split a window +horizontally only if it has at least this many columns. If this +is nil, `split-window-sensibly' is not allowed to split a window +horizontally." + :type '(choice (const nil) (integer :tag "columns")) :version "23.1" :group 'windows) -(defun window--splittable-p (window &optional horizontal) - "Return non-nil if WINDOW can be split evenly. -Optional argument HORIZONTAL non-nil means check whether WINDOW -can be split horizontally. +(defun window-splittable-p (window &optional horizontal) + "Return non-nil if `split-window-sensibly' may split WINDOW. +Optional argument HORIZONTAL nil or omitted means check whether +`split-window-sensibly' may split WINDOW vertically. HORIZONTAL +non-nil means check whether WINDOW may be split horizontally. -WINDOW can be split vertically when the following conditions +WINDOW may be split vertically when the following conditions hold: - - `window-size-fixed' is either nil or equals `width' for the buffer of WINDOW. - -- `split-height-threshold' is a number and WINDOW is at least as +- `split-height-threshold' is an integer and WINDOW is at least as high as `split-height-threshold'. - - When WINDOW is split evenly, the emanating windows are at least `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 +WINDOW may be split horizontally when the following conditions hold: - - `window-size-fixed' is either nil or equals `height' for the buffer of WINDOW. - -- `split-width-threshold' is a number and WINDOW is at least as +- `split-width-threshold' is an integer and WINDOW is at least as wide as `split-width-threshold'. - - When WINDOW is split evenly, the emanating windows are at least `window-min-width' or two (whichever is larger) columns wide." (when (window-live-p window) @@ -882,30 +908,68 @@ hold: (* 2 (max window-min-height (if mode-line-format 2 1)))))))))) +(defun split-window-sensibly (window) + "Split WINDOW in a way suitable for `display-buffer'. +If `split-height-threshold' specifies an integer, WINDOW is at +least `split-height-threshold' lines tall and can be split +vertically, split WINDOW into two windows one above the other and +return the lower window. Otherwise, if `split-width-threshold' +specifies an integer, WINDOW is at least `split-width-threshold' +columns wide and can be split horizontally, split WINDOW into two +windows side by side and return the window on the right. If this +can't be done either and WINDOW is the only window on its frame, +try to split WINDOW vertically disregarding any value specified +by `split-height-threshold'. If that succeeds, return the lower +window. Return nil otherwise. + +By default `display-buffer' routines call this function to split +the largest or least recently used window. To change the default +customize the option `split-window-preferred-function'. + +You can enforce this function to not split WINDOW horizontally, +by setting \(or binding) the variable `split-width-threshold' to +nil. If, in addition, you set `split-height-threshold' to zero, +chances increase that this function does split WINDOW vertically. + +In order to not split WINDOW vertically, set \(or bind) the +variable `split-height-threshold' to nil. Additionally, you can +set `split-width-threshold' to zero to make a horizontal split +more likely to occur. + +Have a look at the function `window-splittable-p' if you want to +know how `split-window-sensibly' determines whether WINDOW can be +split." + (or (and (window-splittable-p window) + ;; Split window vertically. + (with-selected-window window + (split-window-vertically))) + (and (window-splittable-p window t) + ;; Split window horizontally. + (with-selected-window window + (split-window-horizontally))) + (and (eq window (frame-root-window (window-frame window))) + (not (window-minibuffer-p window)) + ;; If WINDOW is the only window on its frame and is not the + ;; minibuffer window, try to split it vertically disregarding + ;; the value of `split-height-threshold'. + (let ((split-height-threshold 0)) + (when (window-splittable-p window) + (with-selected-window window + (split-window-vertically))))))) + (defun window--try-to-split-window (window) - "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')." - (when (and (window-live-p window) - (not (frame-parameter (window-frame window) 'unsplittable))) - (if (functionp split-window-preferred-function) - ;; `split-window-preferred-function' is specified, so use it. - (funcall split-window-preferred-function window) - (or (and (window--splittable-p window) - ;; Split window vertically. - (split-window window)) - (and (window--splittable-p window t) - ;; Split window horizontally. - (split-window window nil t)) - (and (eq window (frame-root-window (window-frame window))) - (not (window-minibuffer-p window)) - ;; If WINDOW is the only window on its frame and not the - ;; minibuffer window, attempt to split it vertically - ;; disregarding the value of `split-height-threshold'. - (let ((split-height-threshold 0)) - (and (window--splittable-p window) - (split-window window)))))))) + "Try to split WINDOW. +Return value returned by `split-window-preferred-function' if it +represents a live window, nil otherwise." + (and (window-live-p window) + (not (frame-parameter (window-frame window) 'unsplittable)) + (let ((new-window + ;; Since `split-window-preferred-function' might + ;; throw an error use `condition-case'. + (condition-case nil + (funcall split-window-preferred-function window) + (error nil)))) + (and (window-live-p new-window) new-window)))) (defun window--frame-usable-p (frame) "Return FRAME if it can be used to display a buffer." @@ -942,7 +1006,7 @@ is higher than WINDOW." (not (eq window (selected-window))) ;; Don't resize minibuffer windows. (not (window-minibuffer-p (selected-window))) - (> (window-height (selected-window)) (window-height window)) + (> (window-height (selected-window)) (window-height window)) (eq (window-frame window) (window-frame (selected-window))) (let ((sel-edges (window-edges (selected-window))) (win-edges (window-edges window))) @@ -972,13 +1036,21 @@ Do not raise the selected frame. Return WINDOW." (raise-frame frame)) window)) -(defun window--display-buffer-2 (buffer window) +(defun window--display-buffer-2 (buffer window &optional dedicated) "Display BUFFER in WINDOW and make its frame visible. +Set `window-dedicated-p' to DEDICATED if non-nil. Return WINDOW." (when (and (buffer-live-p buffer) (window-live-p window)) (set-window-buffer window buffer) + (when dedicated + (set-window-dedicated-p window dedicated)) (window--display-buffer-1 window))) +(defvar display-buffer-mark-dedicated nil + "If non-nil, `display-buffer' marks the windows it creates as dedicated. +The actual non-nil value of this variable will be copied to the +`window-dedicated-p' flag.") + (defun display-buffer (buffer-or-name &optional not-this-window frame) "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 @@ -1070,8 +1142,8 @@ consider all visible or iconified frames." buffer (if (listp pars) pars)))))) ((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)))) + (let ((win (frame-selected-window (funcall pop-up-frame-function)))) + (window--display-buffer-2 buffer win display-buffer-mark-dedicated))) ((and pop-up-windows ;; Make a new window. (or (not (frame-parameter frame-to-use 'unsplittable)) @@ -1086,8 +1158,9 @@ consider all visible or iconified frames." (or (window--try-to-split-window (get-largest-window frame-to-use t)) (window--try-to-split-window - (get-lru-window frame-to-use t)))) - (window--display-buffer-2 buffer window-to-use))) + (get-lru-window frame-to-use t))))) + (window--display-buffer-2 buffer window-to-use + display-buffer-mark-dedicated)) ((let ((window-to-undedicate ;; When NOT-THIS-WINDOW is non-nil, temporarily dedicate ;; the selected window to its buffer, to avoid that some of @@ -1207,8 +1280,7 @@ window." (setq size (+ (window-height) size))) (setq new-window (split-window nil size)) (unless split-window-keep-point - (save-excursion - (set-buffer (window-buffer)) + (with-current-buffer (window-buffer) (goto-char (window-start)) (setq moved (vertical-motion (window-height))) (set-window-start new-window (point)) @@ -1547,40 +1619,95 @@ Otherwise, bury WINDOW's buffer, see `bury-buffer'." (defvar recenter-last-op nil "Indicates the last recenter operation performed. -Possible values: `top', `middle', `bottom'.") +Possible values: `top', `middle', `bottom', integer or float numbers.") + +(defcustom recenter-positions '(middle top bottom) + "Cycling order for `recenter-top-bottom'. +A list of elements with possible values `top', `middle', `bottom', +integer or float numbers that define the cycling order for +the command `recenter-top-bottom'. + +Top and bottom destinations are `scroll-margin' lines the from true +window top and bottom. Middle redraws the frame and centers point +vertically within the window. Integer number moves current line to +the specified absolute window-line. Float number between 0.0 and 1.0 +means the percentage of the screen space from the top. The default +cycling order is middle -> top -> bottom." + :type '(repeat (choice + (const :tag "Top" top) + (const :tag "Middle" middle) + (const :tag "Bottom" bottom) + (integer :tag "Line number") + (float :tag "Percentage"))) + :version "23.2" + :group 'windows) (defun recenter-top-bottom (&optional arg) - "Move current line to window center, top, and bottom, successively. -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. + "Move current buffer line to the specified window line. +With no prefix argument, successive calls place point according +to the cycling order defined by `recenter-positions'. 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. - -Top and bottom destinations are actually `scroll-margin' lines - the from true window top and bottom." + With plain `C-u', move current line to window center." (interactive "P") (cond - (arg (recenter arg)) ; Always respect ARG. - ((or (not (eq this-command last-command)) - (eq recenter-last-op 'bottom)) - (setq recenter-last-op 'middle) - (recenter)) + (arg (recenter arg)) ; Always respect ARG. (t + (setq recenter-last-op + (if (eq this-command last-command) + (car (or (cdr (member recenter-last-op recenter-positions)) + recenter-positions)) + (car recenter-positions))) (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)) + (recenter)) ((eq recenter-last-op 'top) - (setq recenter-last-op 'bottom) - (recenter (- -1 this-scroll-margin)))))))) + (recenter this-scroll-margin)) + ((eq recenter-last-op 'bottom) + (recenter (- -1 this-scroll-margin))) + ((integerp recenter-last-op) + (recenter recenter-last-op)) + ((floatp recenter-last-op) + (recenter (round (* recenter-last-op (window-height)))))))))) (define-key global-map [?\C-l] 'recenter-top-bottom) + +(defun move-to-window-line-top-bottom (&optional arg) + "Position point relative to window. + +With a prefix argument ARG, acts like `move-to-window-line'. + +With no argument, positions point at center of window. +Successive calls position point at positions defined +by `recenter-positions'." + (interactive "P") + (cond + (arg (move-to-window-line arg)) ; Always respect ARG. + (t + (setq recenter-last-op + (if (eq this-command last-command) + (car (or (cdr (member recenter-last-op recenter-positions)) + recenter-positions)) + (car recenter-positions))) + (let ((this-scroll-margin + (min (max 0 scroll-margin) + (truncate (/ (window-body-height) 4.0))))) + (cond ((eq recenter-last-op 'middle) + (call-interactively 'move-to-window-line)) + ((eq recenter-last-op 'top) + (move-to-window-line this-scroll-margin)) + ((eq recenter-last-op 'bottom) + (move-to-window-line (- -1 this-scroll-margin))) + ((integerp recenter-last-op) + (move-to-window-line recenter-last-op)) + ((floatp recenter-last-op) + (move-to-window-line (round (* recenter-last-op (window-height)))))))))) + +(define-key global-map [?\M-r] 'move-to-window-line-top-bottom) + (defvar mouse-autoselect-window-timer nil "Timer used by delayed window autoselection.")