From: Martin Rudalics Date: Mon, 6 Jun 2011 15:21:07 +0000 (+0200) Subject: Add window-tree based, atomic and side window functions to window.el. X-Git-Tag: emacs-pretest-24.0.90~104^2~618^2~13 X-Git-Url: http://git.eshelyaron.com/gitweb/?a=commitdiff_plain;h=85cc1f119546380ffaa903de15a7fd6a51d1d198;p=emacs.git Add window-tree based, atomic and side window functions to window.el. * window.el (window-right, window-left, window-child) (window-child-count, window-last-child, window-any-p) (normalize-live-buffer, normalize-live-frame) (normalize-any-window, normalize-live-window) (window-iso-combination-p, window-iso-combined-p) (window-iso-combinations) (walk-window-tree-1, walk-window-tree, walk-window-subtree) (windows-with-parameter, window-with-parameter) (window-atom-root, make-window-atom, window-atom-check-1) (window-atom-check, window-side-check, window-check): New functions. (ignore-window-parameters, window-sides, window-sides-vertical) (window-sides-slots): New variables. (window-size-fixed): Move down in code. Minor doc-string fix. --- diff --git a/lisp/ChangeLog b/lisp/ChangeLog index e11149f1689..8cc1c3f1e26 100644 --- a/lisp/ChangeLog +++ b/lisp/ChangeLog @@ -1,3 +1,20 @@ +2011-06-06 Martin Rudalics + + * window.el (window-right, window-left, window-child) + (window-child-count, window-last-child, window-any-p) + (normalize-live-buffer, normalize-live-frame) + (normalize-any-window, normalize-live-window) + (window-iso-combination-p, window-iso-combined-p) + (window-iso-combinations) + (walk-window-tree-1, walk-window-tree, walk-window-subtree) + (windows-with-parameter, window-with-parameter) + (window-atom-root, make-window-atom, window-atom-check-1) + (window-atom-check, window-side-check, window-check): New + functions. + (ignore-window-parameters, window-sides, window-sides-vertical) + (window-sides-slots): New variables. + (window-size-fixed): Move down in code. Minor doc-string fix. + 2011-06-05 Andreas Schwab * comint.el (comint-dynamic-complete-as-filename) diff --git a/lisp/window.el b/lisp/window.el index 9ea00442628..0da3f5ae1de 100644 --- a/lisp/window.el +++ b/lisp/window.el @@ -30,15 +30,6 @@ (eval-when-compile (require 'cl)) -(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 previously selected window. The value returned is the value of the last form in BODY. @@ -72,6 +63,434 @@ are not altered by this macro (unless they are altered in BODY)." (when (window-live-p save-selected-window-window) (select-window save-selected-window-window 'norecord)))))) +;; The following two functions are like `window-next' and `window-prev' +;; but the WINDOW argument is _not_ optional (so they don't substitute +;; the selected window for nil), and they return nil when WINDOW doesn't +;; have a parent (like a frame's root window or a minibuffer window). +(defsubst window-right (window) + "Return WINDOW's right sibling. +Return nil if WINDOW is the root window of its frame. WINDOW can +be any window." + (and window (window-parent window) (window-next window))) + +(defsubst window-left (window) + "Return WINDOW's left sibling. +Return nil if WINDOW is the root window of its frame. WINDOW can +be any window." + (and window (window-parent window) (window-prev window))) + +(defsubst window-child (window) + "Return WINDOW's first child window." + (or (window-vchild window) (window-hchild window))) + +(defun window-child-count (window) + "Return number of WINDOW's child windows." + (let ((count 0)) + (when (and (windowp window) (setq window (window-child window))) + (while window + (setq count (1+ count)) + (setq window (window-next window)))) + count)) + +(defun window-last-child (window) + "Return last child window of WINDOW." + (when (and (windowp window) (setq window (window-child window))) + (while (window-next window) + (setq window (window-next window)))) + window) + +(defsubst window-any-p (object) + "Return t if OBJECT denotes a live or internal window." + (and (windowp object) + (or (window-buffer object) (window-child object)) + t)) + +;; The following four functions should probably go to subr.el. +(defsubst normalize-live-buffer (buffer-or-name) + "Return buffer specified by BUFFER-OR-NAME. +BUFFER-OR-NAME must be either a buffer or a string naming a live +buffer and defaults to the current buffer." + (cond + ((not buffer-or-name) + (current-buffer)) + ((bufferp buffer-or-name) + (if (buffer-live-p buffer-or-name) + buffer-or-name + (error "Buffer %s is not a live buffer" buffer-or-name))) + ((get-buffer buffer-or-name)) + (t + (error "No such buffer %s" buffer-or-name)))) + +(defsubst normalize-live-frame (frame) + "Return frame specified by FRAME. +FRAME must be a live frame and defaults to the selected frame." + (if frame + (if (frame-live-p frame) + frame + (error "%s is not a live frame" frame)) + (selected-frame))) + +(defsubst normalize-any-window (window) + "Return window specified by WINDOW. +WINDOW must be a window that has not been deleted and defaults to +the selected window." + (if window + (if (window-any-p window) + window + (error "%s is not a window" window)) + (selected-window))) + +(defsubst normalize-live-window (window) + "Return live window specified by WINDOW. +WINDOW must be a live window and defaults to the selected one." + (if window + (if (and (windowp window) (window-buffer window)) + window + (error "%s is not a live window" window)) + (selected-window))) + +(defvar ignore-window-parameters nil + "If non-nil, standard functions ignore window parameters. +The functions currently affected by this are `split-window', +`delete-window', `delete-other-windows' and `other-window'. + +An application may bind this to a non-nil value around calls to +these functions to inhibit processing of window parameters.") + +(defun window-iso-combination-p (&optional window horizontal) + "If WINDOW is a vertical combination return WINDOW's first child. +WINDOW can be any window and defaults to the selected one. +Optional argument HORIZONTAL non-nil means return WINDOW's first +child if WINDOW is a horizontal combination." + (setq window (normalize-any-window window)) + (if horizontal + (window-hchild window) + (window-vchild window))) + +(defsubst window-iso-combined-p (&optional window horizontal) + "Return non-nil if and only if WINDOW is vertically combined. +WINDOW can be any window and defaults to the selected one. +Optional argument HORIZONTAL non-nil means return non-nil if and +only if WINDOW is horizontally combined." + (setq window (normalize-any-window window)) + (let ((parent (window-parent window))) + (and parent (window-iso-combination-p parent horizontal)))) + +(defun window-iso-combinations (&optional window horizontal) + "Return largest number of vertically arranged subwindows of WINDOW. +WINDOW can be any window and defaults to the selected one. +Optional argument HORIZONTAL non-nil means to return the largest +number of horizontally arranged subwindows of WINDOW." + (setq window (normalize-any-window window)) + (cond + ((window-live-p window) + ;; If WINDOW is live, return 1. + 1) + ((window-iso-combination-p window horizontal) + ;; If WINDOW is iso-combined, return the sum of the values for all + ;; subwindows of WINDOW. + (let ((child (window-child window)) + (count 0)) + (while child + (setq count + (+ (window-iso-combinations child horizontal) + count)) + (setq child (window-right child))) + count)) + (t + ;; If WINDOW is not iso-combined, return the maximum value of any + ;; subwindow of WINDOW. + (let ((child (window-child window)) + (count 1)) + (while child + (setq count + (max (window-iso-combinations child horizontal) + count)) + (setq child (window-right child))) + count)))) + +(defun walk-window-tree-1 (proc walk-window-tree-window any &optional sub-only) + "Helper function for `walk-window-tree' and `walk-window-subtree'." + (let (walk-window-tree-buffer) + (while walk-window-tree-window + (setq walk-window-tree-buffer + (window-buffer walk-window-tree-window)) + (when (or walk-window-tree-buffer any) + (funcall proc walk-window-tree-window)) + (unless walk-window-tree-buffer + (walk-window-tree-1 + proc (window-hchild walk-window-tree-window) any) + (walk-window-tree-1 + proc (window-vchild walk-window-tree-window) any)) + (if sub-only + (setq walk-window-tree-window nil) + (setq walk-window-tree-window + (window-right walk-window-tree-window)))))) + +(defun walk-window-tree (proc &optional frame any) + "Run function PROC on each live window of FRAME. +PROC must be a function with one argument - a window. FRAME must +be a live frame and defaults to the selected one. ANY, if +non-nil means to run PROC on all live and internal windows of +FRAME. + +This function performs a pre-order, depth-first traversal of the +window tree. If PROC changes the window tree, the result is +unpredictable." + (let ((walk-window-tree-frame (normalize-live-frame frame))) + (walk-window-tree-1 + proc (frame-root-window walk-window-tree-frame) any))) + +(defun walk-window-subtree (proc &optional window any) + "Run function PROC on each live subwindow of WINDOW. +WINDOW defaults to the selected window. PROC must be a function +with one argument - a window. ANY, if non-nil means to run PROC +on all live and internal subwindows of WINDOW. + +This function performs a pre-order, depth-first traversal of the +window tree rooted at WINDOW. If PROC changes that window tree, +the result is unpredictable." + (setq window (normalize-any-window window)) + (walk-window-tree-1 proc window any t)) + +(defun windows-with-parameter (parameter &optional value frame any values) + "Return a list of all windows on FRAME with PARAMETER non-nil. +FRAME defaults to the selected frame. Optional argument VALUE +non-nil means only return windows whose window-parameter value of +PARAMETER equals VALUE \(comparison is done using `equal'). +Optional argument ANY non-nil means consider internal windows +too. Optional argument VALUES non-nil means return a list of cons +cells whose car is the value of the parameter and whose cdr is +the window." + (let (this-value windows) + (walk-window-tree + (lambda (window) + (when (and (setq this-value (window-parameter window parameter)) + (or (not value) (or (equal value this-value)))) + (setq windows + (if values + (cons (cons this-value window) windows) + (cons window windows))))) + frame any) + + (nreverse windows))) + +(defun window-with-parameter (parameter &optional value frame any) + "Return first window on FRAME with PARAMETER non-nil. +FRAME defaults to the selected frame. Optional argument VALUE +non-nil means only return a window whose window-parameter value +for PARAMETER equals VALUE \(comparison is done with `equal'). +Optional argument ANY non-nil means consider internal windows +too." + (let (this-value windows) + (catch 'found + (walk-window-tree + (lambda (window) + (when (and (setq this-value (window-parameter window parameter)) + (or (not value) (equal value this-value))) + (throw 'found window))) + frame any)))) + +;;; Atomic windows. +(defun window-atom-root (&optional window) + "Return root of atomic window WINDOW is a part of. +WINDOW can be any window and defaults to the selected one. +Return nil if WINDOW is not part of a atomic window." + (setq window (normalize-any-window window)) + (let (root) + (while (and window (window-parameter window 'window-atom)) + (setq root window) + (setq window (window-parent window))) + root)) + +(defun make-window-atom (window) + "Make WINDOW an atomic window. +WINDOW must be an internal window. Return WINDOW." + (if (not (window-child window)) + (error "Window %s is not an internal window" window) + (walk-window-subtree + (lambda (window) + (set-window-parameter window 'window-atom t)) + window t) + window)) + +(defun window-atom-check-1 (window) + "Subroutine of `window-atom-check'." + (when window + (if (window-parameter window 'window-atom) + (let ((count 0)) + (when (or (catch 'reset + (walk-window-subtree + (lambda (window) + (if (window-parameter window 'window-atom) + (setq count (1+ count)) + (throw 'reset t))) + window t)) + ;; count >= 1 must hold here. If there's no other + ;; window around dissolve this atomic window. + (= count 1)) + ;; Dissolve atomic window. + (walk-window-subtree + (lambda (window) + (set-window-parameter window 'window-atom nil)) + window t))) + ;; Check children. + (unless (window-buffer window) + (window-atom-check-1 (window-hchild window)) + (window-atom-check-1 (window-vchild window)))) + ;; Check right sibling + (window-atom-check-1 (window-right window)))) + +(defun window-atom-check (&optional frame) + "Check atomicity of all windows on FRAME. +FRAME defaults to the selected frame. If an atomic window is +wrongly configured, reset the atomicity of all its subwindows to +nil. An atomic window is wrongly configured if it has no +subwindows or one of its subwindows is not atomic." + (window-atom-check-1 (frame-root-window frame))) + +;; Side windows. +(defvar window-sides '(left top right bottom) + "Window sides.") + +(defcustom window-sides-vertical nil + "If non-nil, left and right side windows are full height. +Otherwise, top and bottom side windows are full width." + :type 'boolean + :group 'windows + :version "24.1") + +(defcustom window-sides-slots '(nil nil nil nil) + "Maximum number of side window slots. +The value is a list of four elements specifying the number of +side window slots on \(in this order) the left, top, right and +bottom side of each frame. If an element is a number, this means +to display at most that many side windows on the corresponding +side. If an element is nil, this means there's no bound on the +number of slots on that side." + :risky t + :type + '(list + :value (nil nil nil nil) + (choice + :tag "Left" + :help-echo "Maximum slots of left side window." + :value nil + :format "%[Left%] %v\n" + (const :tag "Unlimited" :format "%t" nil) + (integer :tag "Number" :value 2 :size 5)) + (choice + :tag "Top" + :help-echo "Maximum slots of top side window." + :value nil + :format "%[Top%] %v\n" + (const :tag "Unlimited" :format "%t" nil) + (integer :tag "Number" :value 3 :size 5)) + (choice + :tag "Right" + :help-echo "Maximum slots of right side window." + :value nil + :format "%[Right%] %v\n" + (const :tag "Unlimited" :format "%t" nil) + (integer :tag "Number" :value 2 :size 5)) + (choice + :tag "Bottom" + :help-echo "Maximum slots of bottom side window." + :value nil + :format "%[Bottom%] %v\n" + (const :tag "Unlimited" :format "%t" nil) + (integer :tag "Number" :value 3 :size 5))) + :group 'windows) + +(defun window-side-check (&optional frame) + "Check the window-side parameter of all windows on FRAME. +FRAME defaults to the selected frame. If the configuration is +invalid, reset all window-side parameters to nil. + +A valid configuration has to preserve the following invariant: + +- If a window has a non-nil window-side parameter, it must have a + parent window and the parent window's window-side parameter + must be either nil or the same as for window. + +- If windows with non-nil window-side parameters exist, there + must be at most one window of each side and non-side with a + parent whose window-side parameter is nil and there must be no + leaf window whose window-side parameter is nil." + (let (normal none left top right bottom + side parent parent-side code) + (when (or (catch 'reset + (walk-window-tree + (lambda (window) + (setq side (window-parameter window 'window-side)) + (setq parent (window-parent window)) + (setq parent-side + (and parent (window-parameter parent 'window-side))) + ;; The following `cond' seems a bit tedious, but I'd + ;; rather stick to using just the stack. + (cond + (parent-side + (when (not (eq parent-side side)) + ;; A parent whose window-side is non-nil must + ;; have a child with the same window-side. + (throw 'reset t))) + ;; Now check that there's more than one main window + ;; for any of none, left, top, right and bottom. + ((eq side 'none) + (if none + (throw 'reset t) + (setq none t))) + ((eq side 'left) + (if left + (throw 'reset t) + (setq left t))) + ((eq side 'top) + (if top + (throw 'reset t) + (setq top t))) + ((eq side 'right) + (if right + (throw 'reset t) + (setq right t))) + ((eq side 'bottom) + (if bottom + (throw 'reset t) + (setq bottom t))) + ((window-buffer window) + ;; A leaf window without window-side parameter, + ;; record its existence. + (setq normal t)))) + frame t)) + (if none + ;; At least one non-side window exists, so there must + ;; be at least one side-window and no normal window. + (or (not (or left top right bottom)) normal) + ;; No non-side window exists, so there must be no side + ;; window either. + (or left top right bottom))) + (walk-window-tree + (lambda (window) + (set-window-parameter window 'window-side nil)) + frame t)))) + +(defun window-check (&optional frame) + "Check atomic and side windows on FRAME. +FRAME defaults to the selected frame." + (window-side-check frame) + (window-atom-check frame)) + +;;; Window sizes. +(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 it has no other choice \(like when deleting a neighboring +window).") +(make-variable-buffer-local 'window-size-fixed) + (defun window-body-height (&optional window) "Return number of lines in WINDOW available for actual buffer text. WINDOW defaults to the selected window.