;;; window.el --- GNU Emacs window commands aside from those written in C
-;; Copyright (C) 1985, 1989, 1992, 1993, 1994, 2000, 2001, 2002, 2004
-;; Free Software Foundation, Inc.
+;; Copyright (C) 1985, 1989, 1992, 1993, 1994, 2000, 2001, 2002,
+;; 2003, 2004, 2005 Free Software Foundation, Inc.
;; Maintainer: FSF
;; Keywords: internal
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs; see the file COPYING. If not, write to the
-;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-;; Boston, MA 02111-1307, USA.
+;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+;; Boston, MA 02110-1301, USA.
;;; Commentary:
;;; Code:
+(defvar window-size-fixed nil
+ "*Non-nil in a buffer means windows displaying the buffer are fixed-size.
+If the value is `height', then only the window's height is fixed.
+If the value is `width', then only the window's width is fixed.
+Any other non-nil value fixes both the width and the height.
+Emacs won't change the size of any window displaying that buffer,
+unless you explicitly change the size, or Emacs has no other choice.")
+(make-variable-buffer-local 'window-size-fixed)
+
(defmacro save-selected-window (&rest body)
"Execute BODY, then select the window that was selected before BODY.
-Also restore the selected window of each frame as it was at the start
-of this construct.
-However, if a window has become dead, don't get an error,
-just refrain from reselecting it.
-Return the value of the last form in BODY."
+The value returned is the value of the last form in BODY.
+
+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."
`(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
(save-selected-window-alist
(mapcar (lambda (frame) (list frame (frame-selected-window frame)))
(frame-list))))
- (unwind-protect
- (progn ,@body)
- (dolist (elt save-selected-window-alist)
- (and (frame-live-p (car elt))
- (window-live-p (cadr elt))
- (set-frame-selected-window (car elt) (cadr elt))))
- (if (window-live-p save-selected-window-window)
- (select-window save-selected-window-window)))))
+ (save-current-buffer
+ (unwind-protect
+ (progn ,@body)
+ (dolist (elt save-selected-window-alist)
+ (and (frame-live-p (car elt))
+ (window-live-p (cadr elt))
+ (set-frame-selected-window (car elt) (cadr elt))))
+ (if (window-live-p save-selected-window-window)
+ (select-window save-selected-window-window))))))
(defun window-body-height (&optional window)
"Return number of lines in window WINDOW for actual buffer text.
(defun window-current-scroll-bars (&optional window)
"Return the current scroll-bar settings in window WINDOW.
-Value is a cons (VERTICAL . HORISONTAL) where VERTICAL specifies the
+Value is a cons (VERTICAL . HORIZONTAL) where VERTICAL specifies the
current location of the vertical scroll-bars (left, right, or nil),
-and HORISONTAL specifies the current location of the horisontal scroll
+and HORIZONTAL specifies the current location of the horizontal scroll
bars (top, bottom, or nil)."
(let ((vert (nth 2 (window-scroll-bars window)))
(hor nil))
(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 frame)
+ "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 FRAME."
+ (let ((buffer (if (bufferp buffer) buffer (get-buffer buffer))) windows)
+ (walk-windows (function (lambda (window)
+ (if (eq (window-buffer window) buffer)
+ (setq windows (cons window windows)))))
+ minibuf frame)
+ windows))
+
(defun minibuffer-window-active-p (window)
"Return t if WINDOW (a minibuffer window) is now active."
(eq window (active-minibuffer-window)))
(or (= (nth 2 edges) (nth 2 (window-edges (previous-window))))
(= (nth 0 edges) (nth 0 (window-edges (next-window))))))))
-
-(defun balance-windows ()
- "Make all visible windows the same height (approximately)."
+\f
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;; `balance-windows' subroutines using `window-tree'
+
+;;; Translate from internal window tree format
+
+(defun bw-get-tree (&optional window-or-frame)
+ "Get a window split tree in our format.
+
+WINDOW-OR-FRAME must be nil, a frame, or a window. If it is nil,
+then the whole window split tree for `selected-frame' is returned.
+If it is a frame, then this is used instead. If it is a window,
+then the smallest tree containing that window is returned."
+ (when window-or-frame
+ (unless (or (framep window-or-frame)
+ (windowp window-or-frame))
+ (error "Not a frame or window: %s" window-or-frame)))
+ (let ((subtree (bw-find-tree-sub window-or-frame)))
+ (if (integerp subtree)
+ nil
+ (bw-get-tree-1 subtree))))
+
+(defun bw-get-tree-1 (split)
+ (if (windowp split)
+ split
+ (let ((dir (car split))
+ (edges (car (cdr split)))
+ (childs (cdr (cdr split))))
+ (list
+ (cons 'dir (if dir 'ver 'hor))
+ (cons 'b (nth 3 edges))
+ (cons 'r (nth 2 edges))
+ (cons 't (nth 1 edges))
+ (cons 'l (nth 0 edges))
+ (cons 'childs (mapcar #'bw-get-tree-1 childs))))))
+
+(defun bw-find-tree-sub (window-or-frame &optional get-parent)
+ (let* ((window (when (windowp window-or-frame) window-or-frame))
+ (frame (when (windowp window) (window-frame window)))
+ (wt (car (window-tree frame))))
+ (when (< 1 (length (window-list frame 0)))
+ (if window
+ (bw-find-tree-sub-1 wt window get-parent)
+ wt))))
+
+(defun bw-find-tree-sub-1 (tree win &optional get-parent)
+ (unless (windowp win) (error "Not a window: %s" win))
+ (if (memq win tree)
+ (if get-parent
+ get-parent
+ tree)
+ (let ((childs (cdr (cdr tree)))
+ child
+ subtree)
+ (while (and childs (not subtree))
+ (setq child (car childs))
+ (setq childs (cdr childs))
+ (when (and child (listp child))
+ (setq subtree (bw-find-tree-sub-1 child win get-parent))))
+ (if (integerp subtree)
+ (progn
+ (if (= 1 subtree)
+ tree
+ (1- subtree)))
+ subtree
+ ))))
+
+;;; Window or object edges
+
+(defun bw-l(obj)
+ "Left edge of OBJ."
+ (if (windowp obj) (nth 0 (window-edges obj)) (cdr (assq 'l obj))))
+(defun bw-t(obj)
+ "Top edge of OBJ."
+ (if (windowp obj) (nth 1 (window-edges obj)) (cdr (assq 't obj))))
+(defun bw-r(obj)
+ "Right edge of OBJ."
+ (if (windowp obj) (nth 2 (window-edges obj)) (cdr (assq 'r obj))))
+(defun bw-b(obj)
+ "Bottom edge of OBJ."
+ (if (windowp obj) (nth 3 (window-edges obj)) (cdr (assq 'b obj))))
+
+;;; Split directions
+
+(defun bw-dir(obj)
+ "Return window split tree direction if OBJ.
+If OBJ is a window return 'both. If it is a window split tree
+then return its direction."
+ (if (symbolp obj)
+ obj
+ (if (windowp obj)
+ 'both
+ (let ((dir (cdr (assq 'dir obj))))
+ (unless (memq dir '(hor ver both))
+ (error "Can't find dir in %s" obj))
+ dir))))
+
+(defun bw-eqdir(obj1 obj2)
+ "Return t if window split tree directions are equal.
+OBJ1 and OBJ2 should be either windows or window split trees in
+our format. The directions returned by `bw-dir' are compared and
+t is returned if they are `eq' or one of them is 'both."
+ (let ((dir1 (bw-dir obj1))
+ (dir2 (bw-dir obj2)))
+ (or (eq dir1 dir2)
+ (eq dir1 'both)
+ (eq dir2 'both))))
+
+;;; Building split tree
+
+(defun bw-refresh-edges(obj)
+ "Refresh the edge information of OBJ and return OBJ."
+ (unless (windowp obj)
+ (let ((childs (cdr (assq 'childs obj)))
+ (ol 1000)
+ (ot 1000)
+ (or -1)
+ (ob -1))
+ (dolist (o childs)
+ (when (> ol (bw-l o)) (setq ol (bw-l o)))
+ (when (> ot (bw-t o)) (setq ot (bw-t o)))
+ (when (< or (bw-r o)) (setq or (bw-r o)))
+ (when (< ob (bw-b o)) (setq ob (bw-b o))))
+ (setq obj (delq 'l obj))
+ (setq obj (delq 't obj))
+ (setq obj (delq 'r obj))
+ (setq obj (delq 'b obj))
+ (add-to-list 'obj (cons 'l ol))
+ (add-to-list 'obj (cons 't ot))
+ (add-to-list 'obj (cons 'r or))
+ (add-to-list 'obj (cons 'b ob))
+ ))
+ obj)
+
+;;; Balance windows
+
+(defun balance-windows(&optional window-or-frame)
+ "Make windows the same heights or widths in window split subtrees.
+
+When called non-interactively WINDOW-OR-FRAME may be either a
+window or a frame. It then balances the windows on the implied
+frame. If the parameter is a window only the corresponding window
+subtree is balanced."
(interactive)
- (let ((count -1) levels newsizes level-size
- ;; Don't count the lines that are above the uppermost windows.
- ;; (These are the menu bar lines, if any.)
- (mbl (nth 1 (window-edges (frame-first-window (selected-frame)))))
- (last-window (previous-window (frame-first-window (selected-frame))))
- ;; Don't count the lines that are past the lowest main window.
- total)
- ;; Bottom edge of last window determines what size we have to work with.
- (setq total
- (+ (window-height last-window)
- (nth 1 (window-edges last-window))))
-
- ;; Find all the different vpos's at which windows start,
- ;; then count them. But ignore levels that differ by only 1.
- (let (tops (prev-top -2))
- (walk-windows (function (lambda (w)
- (setq tops (cons (nth 1 (window-edges w))
- tops))))
- 'nomini)
- (setq tops (sort tops '<))
- (while tops
- (if (> (car tops) (1+ prev-top))
- (setq prev-top (car tops)
- count (1+ count)))
- (setq levels (cons (cons (car tops) count) levels))
- (setq tops (cdr tops)))
- (setq count (1+ count)))
- ;; Subdivide the frame into desired number of vertical levels.
- (setq level-size (/ (- total mbl) count))
- (save-selected-window
- ;; Set up NEWSIZES to map windows to their desired sizes.
- ;; If a window ends at the bottom level, don't include
- ;; it in NEWSIZES. Those windows get the right sizes
- ;; by adjusting the ones above them.
- (walk-windows (function
- (lambda (w)
- (let ((newtop (cdr (assq (nth 1 (window-edges w))
- levels)))
- (newbot (cdr (assq (+ (window-height w)
- (nth 1 (window-edges w)))
- levels))))
- (if newbot
- (setq newsizes
- (cons (cons w (* level-size (- newbot newtop)))
- newsizes))))))
- 'nomini)
- ;; Make walk-windows start with the topmost window.
- (select-window (previous-window (frame-first-window (selected-frame))))
- (let (done (count 0))
- ;; Give each window its precomputed size, or at least try.
- ;; Keep trying until they all get the intended sizes,
- ;; but not more than 3 times (to prevent infinite loop).
- (while (and (not done) (< count 3))
- (setq done t)
- (setq count (1+ count))
- (walk-windows (function (lambda (w)
- (select-window w)
- (let ((newsize (cdr (assq w newsizes))))
- (when newsize
- (enlarge-window (- newsize
- (window-height))
- nil t)
- (unless (= (window-height) newsize)
- (setq done nil))))))
- 'nomini))))))
+ (let (
+ (wt (bw-get-tree window-or-frame))
+ (w)
+ (h)
+ (tried-sizes)
+ (last-sizes)
+ (windows (window-list nil 0))
+ (counter 0))
+ (when wt
+ (while (not (member last-sizes tried-sizes))
+ (when last-sizes (setq tried-sizes (cons last-sizes tried-sizes)))
+ (setq last-sizes (mapcar (lambda(w)
+ (window-edges w))
+ windows))
+ (when (eq 'hor (bw-dir wt))
+ (setq w (- (bw-r wt) (bw-l wt))))
+ (when (eq 'ver (bw-dir wt))
+ (setq h (- (bw-b wt) (bw-t wt))))
+ (bw-balance-sub wt w h)))))
+
+(defun bw-adjust-window(window delta horizontal)
+ "Wrapper around `adjust-window-trailing-edge' with error checking.
+Arguments WINDOW, DELTA and HORIZONTAL are passed on to that function."
+ (condition-case err
+ (adjust-window-trailing-edge window delta horizontal)
+ (error
+ ;;(message "adjust: %s" (error-message-string err))
+ )))
+
+(defun bw-balance-sub(wt w h)
+ (setq wt (bw-refresh-edges wt))
+ (unless w (setq w (- (bw-r wt) (bw-l wt))))
+ (unless h (setq h (- (bw-b wt) (bw-t wt))))
+ (if (windowp wt)
+ (progn
+ (when w
+ (let ((dw (- w (- (bw-r wt) (bw-l wt)))))
+ (when (/= 0 dw)
+ (bw-adjust-window wt dw t))))
+ (when h
+ (let ((dh (- h (- (bw-b wt) (bw-t wt)))))
+ (when (/= 0 dh)
+ (bw-adjust-window wt dh nil)))))
+ (let* ((childs (cdr (assq 'childs wt)))
+ (lastchild (car (last childs)))
+ (cw (when w (/ w (if (bw-eqdir 'hor wt) (length childs) 1))))
+ (ch (when h (/ h (if (bw-eqdir 'ver wt) (length childs) 1)))))
+ (dolist (c childs)
+ (bw-balance-sub c cw ch)))))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
\f
;; I think this should be the default; I think people will prefer it--rms.
(defcustom split-window-keep-point t
(defun window-buffer-height (window)
"Return the height (in screen lines) of the buffer that WINDOW is displaying."
- (save-excursion
- (set-buffer (window-buffer window))
- (goto-char (point-min))
- (let ((ignore-final-newline
- ;; If buffer ends with a newline, ignore it when counting height
- ;; unless point is after it.
- (and (not (eobp)) (eq ?\n (char-after (1- (point-max)))))))
- (+ 1 (nth 2 (compute-motion (point-min)
- '(0 . 0)
- (- (point-max) (if ignore-final-newline 1 0))
- (cons 0 100000000)
- (window-width window)
- nil
- window))))))
+ (with-current-buffer (window-buffer window)
+ (max 1
+ (count-screen-lines (point-min) (point-max)
+ ;; If buffer ends with a newline, ignore it when
+ ;; counting height unless point is after it.
+ (eobp)
+ window))))
(defun count-screen-lines (&optional beg end count-final-newline window)
"Return the number of screen lines in the region.
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."
+or if shrinking this window would also shrink another window,
+or if the window is the only window of its frame."
(interactive)
(when (null window)
(setq window (selected-window)))
(defun quit-window (&optional kill window)
"Quit the current buffer. Bury it, and maybe delete the selected frame.
-\(The frame is deleted if it is contains a dedicated window for the buffer.)
+\(The frame is deleted if it contains a dedicated window for the buffer.)
With a prefix argument, kill the buffer instead.
Noninteractively, if KILL is non-nil, then kill the current buffer,
(interactive "e")
(let ((window (posn-window (event-start event))))
(if (and (window-live-p window)
+ ;; Don't switch if we're currently in the minibuffer.
+ ;; This tries to work around problems where the minibuffer gets
+ ;; unselected unexpectedly, and where you then have to move
+ ;; your mouse all the way down to the minibuffer to select it.
+ (not (window-minibuffer-p (selected-window)))
+ ;; Don't switch to a minibuffer window unless it's active.
(or (not (window-minibuffer-p window))
(minibuffer-window-active-p window)))
(select-window window))))
(define-key ctl-x-map "+" 'balance-windows)
(define-key ctl-x-4-map "0" 'kill-buffer-and-window)
-;;; arch-tag: b508dfcc-c353-4c37-89fa-e773fe10cea9
+;; arch-tag: b508dfcc-c353-4c37-89fa-e773fe10cea9
;;; window.el ends here