changeset 67478:d78d32605019

(bw-get-tree, bw-get-tree-1, bw-find-tree-sub) (bw-find-tree-sub-1, bw-l, bw-t, bw-r, bw-b, bw-dir, bw-eqdir) (bw-refresh-edges, bw-adjust-window, bw-balance-sub): New functions. (balance-windows): Rewrite using the above new functions.
author Eli Zaretskii <eliz@gnu.org>
date Sat, 10 Dec 2005 12:21:13 +0000
parents e4e7224e3873
children 677f6aabed5d
files lisp/window.el
diffstat 1 files changed, 193 insertions(+), 67 deletions(-) [+]
line wrap: on
line diff
--- a/lisp/window.el	Sat Dec 10 11:57:30 2005 +0000
+++ b/lisp/window.el	Sat Dec 10 12:21:13 2005 +0000
@@ -228,75 +228,201 @@
       (or (= (nth 2 edges) (nth 2 (window-edges (previous-window))))
 	  (= (nth 0 edges) (nth 0 (window-edges (next-window))))))))
 
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;; `balance-windows' subroutines using `window-tree'
 
-(defun balance-windows ()
-  "Make all visible windows the same height (approximately)."
-  (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))))
+;;; 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))))
 
-    ;; 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)
-					(unless (= (window-height) newsize)
-					  (setq done nil))))))
-			'nomini))))))
+(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 (
+        (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)))))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
 ;; I think this should be the default; I think people will prefer it--rms.
 (defcustom split-window-keep-point t