;;; 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 Free Software Foundation, Inc.
+;; 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
;; Maintainer: FSF
;; Keywords: internal
;; GNU Emacs is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation; either version 2, or (at your option)
+;; the Free Software Foundation; either version 3, or (at your option)
;; any later version.
;; GNU Emacs is distributed in the hope that it will be useful,
;; select-window changes frame-selected-window for whatever
;; frame that window is in.
(save-selected-window-alist
- (mapcar (lambda (frame) (list frame (frame-selected-window frame)))
+ (mapcar (lambda (frame) (cons frame (frame-selected-window frame)))
(frame-list))))
(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))))
+ (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))))))
PROC is called with a window as argument.
Optional second arg MINIBUF t means count the minibuffer window even
-if not active. MINIBUF nil or omitted means count the minibuffer iff
+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.
returned.
Optional second arg MINIBUF t means count the minibuffer window even
-if not active. MINIBUF nil or omitted means count the minibuffer iff
+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.
(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))))
+ (when subtree
+ (if (integerp subtree)
+ nil
+ (bw-get-tree-1 subtree)))))
(defun bw-get-tree-1 (split)
(if (windowp split)
(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))
- )))
+ ;; `adjust-window-trailing-edge' may fail if delta is too large.
+ (while (>= (abs delta) 1)
+ (condition-case err
+ (progn
+ (adjust-window-trailing-edge window delta horizontal)
+ (setq delta 0))
+ (error
+ ;;(message "adjust: %s" (error-message-string err))
+ (setq delta (/ delta 2))))))
(defun bw-balance-sub (wt w h)
(setq wt (bw-refresh-edges wt))
(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'."
+ (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)))))))))
+
+(defvar window-area-factor 1
+ "Factor by which the window area should be over-estimated.
+This is used by `balance-windows-area'.
+Changing this globally has no effect.")
+
+(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."
+ (interactive)
+ (let* ((unchanged 0) (carry 0) (round 0)
+ ;; Remove fixed-size windows.
+ (wins (delq nil (mapcar (lambda (win)
+ (if (not (window-fixed-size-p win)) win))
+ (window-list nil 'nomini))))
+ (changelog nil)
+ next)
+ ;; Resizing a window changes the size of surrounding windows in complex
+ ;; ways, so it's difficult to balance them all. The introduction of
+ ;; `adjust-window-trailing-edge' made it a bit easier, but it is still
+ ;; very difficult to do. `balance-window' above takes an off-line
+ ;; approach: get the whole window tree, then balance it, then try to
+ ;; adjust the windows so they fit the result.
+ ;; Here, instead, we take a "local optimization" approach, where we just
+ ;; go through all the windows several times until nothing needs to be
+ ;; changed. The main problem with this approach is that it's difficult
+ ;; to make sure it terminates, so we use some heuristic to try and break
+ ;; off infinite loops.
+ ;; After a round without any change, we allow a second, to give a chance
+ ;; to the carry to propagate a minor imbalance from the end back to
+ ;; the beginning.
+ (while (< unchanged 2)
+ ;; (message "New round")
+ (setq unchanged (1+ unchanged) round (1+ round))
+ (dolist (win wins)
+ (setq next win)
+ (while (progn (setq next (next-window next))
+ (window-fixed-size-p next)))
+ ;; (assert (eq next (or (cadr (member win wins)) (car wins))))
+ (let* ((horiz
+ (< (car (window-edges win)) (car (window-edges next))))
+ (areadiff (/ (- (* (window-height next) (window-width next)
+ (buffer-local-value 'window-area-factor
+ (window-buffer next)))
+ (* (window-height win) (window-width win)
+ (buffer-local-value 'window-area-factor
+ (window-buffer win))))
+ (max (buffer-local-value 'window-area-factor
+ (window-buffer win))
+ (buffer-local-value 'window-area-factor
+ (window-buffer next)))))
+ (edgesize (if horiz
+ (+ (window-height win) (window-height next))
+ (+ (window-width win) (window-width next))))
+ (diff (/ areadiff edgesize)))
+ (when (zerop diff)
+ ;; Maybe diff is actually closer to 1 than to 0.
+ (setq diff (/ (* 3 areadiff) (* 2 edgesize))))
+ (when (and (zerop diff) (not (zerop areadiff)))
+ (setq diff (/ (+ areadiff carry) edgesize))
+ ;; Change things smoothly.
+ (if (or (> diff 1) (< diff -1)) (setq diff (/ diff 2))))
+ (if (zerop diff)
+ ;; Make sure negligible differences don't accumulate to
+ ;; become significant.
+ (setq carry (+ carry areadiff))
+ (bw-adjust-window win diff horiz)
+ ;; (sit-for 0.5)
+ (let ((change (cons win (window-edges win))))
+ ;; If the same change has been seen already for this window,
+ ;; we're most likely in an endless loop, so don't count it as
+ ;; a change.
+ (unless (member change changelog)
+ (push change changelog)
+ (setq unchanged 0 carry 0)))))))
+ ;; We've now basically balanced all the windows.
+ ;; But there may be some minor off-by-one imbalance left over,
+ ;; so let's do some fine tuning.
+ ;; (bw-finetune wins)
+ ;; (message "Done in %d rounds" round)
+ ))
+
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
\f
;; I think this should be the default; I think people will prefer it--rms.
lines than are actually needed in the case where some error may be present."
(let ((delta (- height (window-text-height window))))
(unless (zerop delta)
- (let ((window-min-height 1))
+ ;; 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.
(if (and window (not (eq window (selected-window))))
(save-selected-window
(select-window window)
(1+ (vertical-motion (buffer-size) window))))))
(defun fit-window-to-buffer (&optional window max-height min-height)
- "Make WINDOW the right size to display its contents exactly.
+ "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.
;; desired-height lines, constrained by MIN-HEIGHT and MAX-HEIGHT.
(- (max (min desired-height max-height)
(or min-height window-min-height))
- window-height))
- ;; We do our own height checking, so avoid any restrictions due to
- ;; window-min-height.
- (window-min-height 1))
+ 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.
"Kill the current buffer and delete the selected window."
(interactive)
(let ((window-to-delete (selected-window))
+ (buffer-to-kill (current-buffer))
(delete-window-hook (lambda ()
(condition-case nil
(delete-window)
(error nil)))))
- (add-hook 'kill-buffer-hook delete-window-hook t t)
- (if (kill-buffer (current-buffer))
- ;; If `delete-window' failed before, we rerun it to regenerate
- ;; the error so it can be seen in the minibuffer.
- (when (eq (selected-window) window-to-delete)
- (delete-window))
- (remove-hook 'kill-buffer-hook delete-window-hook t))))
+ (unwind-protect
+ (progn
+ (add-hook 'kill-buffer-hook delete-window-hook t t)
+ (if (kill-buffer (current-buffer))
+ ;; If `delete-window' failed before, we rerun it to regenerate
+ ;; the error so it can be seen in the echo area.
+ (when (eq (selected-window) window-to-delete)
+ (delete-window))))
+ ;; If the buffer is not dead for some reason (probably because
+ ;; of a `quit' signal), remove the hook again.
+ (condition-case nil
+ (with-current-buffer buffer-to-kill
+ (remove-hook 'kill-buffer-hook delete-window-hook t))
+ (error nil)))))
(defun quit-window (&optional kill window)
"Quit the current buffer. Bury it, and maybe delete the selected frame.
;; Maybe get rid of the window.
(and window (not window-handled) (not window-solitary)
(delete-window window))))
+\f
+(defvar mouse-autoselect-window-timer nil
+ "Timer used by delayed window autoselection.")
+
+(defvar mouse-autoselect-window-position nil
+ "Last mouse position recorded by delayed window autoselection.")
+
+(defvar mouse-autoselect-window-window nil
+ "Last window recorded by delayed window autoselection.")
+
+(defvar mouse-autoselect-window-state nil
+ "When non-nil, special state of delayed window autoselection.
+Possible values are `suspend' \(suspend autoselection after a menu or
+scrollbar interaction\) and `select' \(the next invocation of
+'handle-select-window' shall select the window immediately\).")
+
+(defun mouse-autoselect-window-cancel (&optional force)
+ "Cancel delayed window autoselection.
+Optional argument FORCE means cancel unconditionally."
+ (unless (and (not force)
+ ;; Don't cancel while the user drags a scroll bar.
+ (eq this-command 'scroll-bar-toolkit-scroll)
+ (memq (nth 4 (event-end last-input-event))
+ '(handle end-scroll)))
+ (setq mouse-autoselect-window-state nil)
+ (when (timerp mouse-autoselect-window-timer)
+ (cancel-timer mouse-autoselect-window-timer))
+ (remove-hook 'pre-command-hook 'mouse-autoselect-window-cancel)))
+
+(defun mouse-autoselect-window-start (mouse-position &optional window suspend)
+ "Start delayed window autoselection.
+MOUSE-POSITION is the last position where the mouse was seen as returned
+by `mouse-position'. Optional argument WINDOW non-nil denotes the
+window where the mouse was seen. Optional argument SUSPEND non-nil
+means suspend autoselection."
+ ;; Record values for MOUSE-POSITION, WINDOW, and SUSPEND.
+ (setq mouse-autoselect-window-position mouse-position)
+ (when window (setq mouse-autoselect-window-window window))
+ (setq mouse-autoselect-window-state (when suspend 'suspend))
+ ;; Install timer which runs `mouse-autoselect-window-select' after
+ ;; `mouse-autoselect-window' seconds.
+ (setq mouse-autoselect-window-timer
+ (run-at-time
+ (abs mouse-autoselect-window) nil 'mouse-autoselect-window-select)))
+
+(defun mouse-autoselect-window-select ()
+ "Select window with delayed window autoselection.
+If the mouse position has stabilized in a non-selected window, select
+that window. The minibuffer window is selected only if the minibuffer is
+active. This function is run by `mouse-autoselect-window-timer'."
+ (condition-case nil
+ (let* ((mouse-position (mouse-position))
+ (window
+ (condition-case nil
+ (window-at (cadr mouse-position) (cddr mouse-position)
+ (car mouse-position))
+ (error nil))))
+ (cond
+ ((or (menu-or-popup-active-p)
+ (and window
+ (not (coordinates-in-window-p (cdr mouse-position) window))))
+ ;; A menu / popup dialog is active or the mouse is on the scroll-bar
+ ;; of WINDOW, temporarily suspend delayed autoselection.
+ (mouse-autoselect-window-start mouse-position nil t))
+ ((eq mouse-autoselect-window-state 'suspend)
+ ;; Delayed autoselection was temporarily suspended, reenable it.
+ (mouse-autoselect-window-start mouse-position))
+ ((and window (not (eq window (selected-window)))
+ (or (not (numberp mouse-autoselect-window))
+ (and (> mouse-autoselect-window 0)
+ ;; If `mouse-autoselect-window' is positive, select
+ ;; window if the window is the same as before.
+ (eq window mouse-autoselect-window-window))
+ ;; Otherwise select window if the mouse is at the same
+ ;; position as before. Observe that the first test after
+ ;; starting autoselection usually fails since the value of
+ ;; `mouse-autoselect-window-position' recorded there is the
+ ;; position where the mouse has entered the new window and
+ ;; not necessarily where the mouse has stopped moving.
+ (equal mouse-position mouse-autoselect-window-position))
+ ;; The minibuffer is a candidate window if it's active.
+ (or (not (window-minibuffer-p window))
+ (eq window (active-minibuffer-window))))
+ ;; Mouse position has stabilized in non-selected window: Cancel
+ ;; delayed autoselection and try to select that window.
+ (mouse-autoselect-window-cancel t)
+ ;; Select window where mouse appears unless the selected window is the
+ ;; minibuffer. Use `unread-command-events' in order to execute pre-
+ ;; and post-command hooks and trigger idle timers. To avoid delaying
+ ;; autoselection again, set `mouse-autoselect-window-state'."
+ (unless (window-minibuffer-p (selected-window))
+ (setq mouse-autoselect-window-state 'select)
+ (setq unread-command-events
+ (cons (list 'select-window (list window))
+ unread-command-events))))
+ ((or (and window (eq window (selected-window)))
+ (not (numberp mouse-autoselect-window))
+ (equal mouse-position mouse-autoselect-window-position))
+ ;; Mouse position has either stabilized in the selected window or at
+ ;; `mouse-autoselect-window-position': Cancel delayed autoselection.
+ (mouse-autoselect-window-cancel t))
+ (t
+ ;; Mouse position has not stabilized yet, resume delayed
+ ;; autoselection.
+ (mouse-autoselect-window-start mouse-position window))))
+ (error nil)))
(defun handle-select-window (event)
"Handle select-window events."
(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))))
+ (when (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)))
+ (unless (and (numberp mouse-autoselect-window)
+ (not (zerop mouse-autoselect-window))
+ (not (eq mouse-autoselect-window-state 'select))
+ (progn
+ ;; Cancel any delayed autoselection.
+ (mouse-autoselect-window-cancel t)
+ ;; Start delayed autoselection from current mouse position
+ ;; and window.
+ (mouse-autoselect-window-start (mouse-position) window)
+ ;; Executing a command cancels delayed autoselection.
+ (add-hook
+ 'pre-command-hook 'mouse-autoselect-window-cancel)))
+ ;; Reset state of delayed autoselection.
+ (setq mouse-autoselect-window-state nil)
+ (when mouse-autoselect-window
+ ;; Run `mouse-leave-buffer-hook' when autoselecting window.
+ (run-hooks 'mouse-leave-buffer-hook))
+ (select-window window)))))
(define-key ctl-x-map "2" 'split-window-vertically)
(define-key ctl-x-map "3" 'split-window-horizontally)