comparison lisp/window.el @ 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 edd04db0e098
children bde0adf72ba8 7beb78bc1f8e
comparison
equal deleted inserted replaced
67477:e4e7224e3873 67478:d78d32605019
226 (with-selected-window (or window (selected-window)) 226 (with-selected-window (or window (selected-window))
227 (let ((edges (window-edges))) 227 (let ((edges (window-edges)))
228 (or (= (nth 2 edges) (nth 2 (window-edges (previous-window)))) 228 (or (= (nth 2 edges) (nth 2 (window-edges (previous-window))))
229 (= (nth 0 edges) (nth 0 (window-edges (next-window)))))))) 229 (= (nth 0 edges) (nth 0 (window-edges (next-window))))))))
230 230
231 231
232 (defun balance-windows () 232 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
233 "Make all visible windows the same height (approximately)." 233 ;;; `balance-windows' subroutines using `window-tree'
234
235 ;;; Translate from internal window tree format
236
237 (defun bw-get-tree (&optional window-or-frame)
238 "Get a window split tree in our format.
239
240 WINDOW-OR-FRAME must be nil, a frame, or a window. If it is nil,
241 then the whole window split tree for `selected-frame' is returned.
242 If it is a frame, then this is used instead. If it is a window,
243 then the smallest tree containing that window is returned."
244 (when window-or-frame
245 (unless (or (framep window-or-frame)
246 (windowp window-or-frame))
247 (error "Not a frame or window: %s" window-or-frame)))
248 (let ((subtree (bw-find-tree-sub window-or-frame)))
249 (if (integerp subtree)
250 nil
251 (bw-get-tree-1 subtree))))
252
253 (defun bw-get-tree-1 (split)
254 (if (windowp split)
255 split
256 (let ((dir (car split))
257 (edges (car (cdr split)))
258 (childs (cdr (cdr split))))
259 (list
260 (cons 'dir (if dir 'ver 'hor))
261 (cons 'b (nth 3 edges))
262 (cons 'r (nth 2 edges))
263 (cons 't (nth 1 edges))
264 (cons 'l (nth 0 edges))
265 (cons 'childs (mapcar #'bw-get-tree-1 childs))))))
266
267 (defun bw-find-tree-sub (window-or-frame &optional get-parent)
268 (let* ((window (when (windowp window-or-frame) window-or-frame))
269 (frame (when (windowp window) (window-frame window)))
270 (wt (car (window-tree frame))))
271 (when (< 1 (length (window-list frame 0)))
272 (if window
273 (bw-find-tree-sub-1 wt window get-parent)
274 wt))))
275
276 (defun bw-find-tree-sub-1 (tree win &optional get-parent)
277 (unless (windowp win) (error "Not a window: %s" win))
278 (if (memq win tree)
279 (if get-parent
280 get-parent
281 tree)
282 (let ((childs (cdr (cdr tree)))
283 child
284 subtree)
285 (while (and childs (not subtree))
286 (setq child (car childs))
287 (setq childs (cdr childs))
288 (when (and child (listp child))
289 (setq subtree (bw-find-tree-sub-1 child win get-parent))))
290 (if (integerp subtree)
291 (progn
292 (if (= 1 subtree)
293 tree
294 (1- subtree)))
295 subtree
296 ))))
297
298 ;;; Window or object edges
299
300 (defun bw-l(obj)
301 "Left edge of OBJ."
302 (if (windowp obj) (nth 0 (window-edges obj)) (cdr (assq 'l obj))))
303 (defun bw-t(obj)
304 "Top edge of OBJ."
305 (if (windowp obj) (nth 1 (window-edges obj)) (cdr (assq 't obj))))
306 (defun bw-r(obj)
307 "Right edge of OBJ."
308 (if (windowp obj) (nth 2 (window-edges obj)) (cdr (assq 'r obj))))
309 (defun bw-b(obj)
310 "Bottom edge of OBJ."
311 (if (windowp obj) (nth 3 (window-edges obj)) (cdr (assq 'b obj))))
312
313 ;;; Split directions
314
315 (defun bw-dir(obj)
316 "Return window split tree direction if OBJ.
317 If OBJ is a window return 'both. If it is a window split tree
318 then return its direction."
319 (if (symbolp obj)
320 obj
321 (if (windowp obj)
322 'both
323 (let ((dir (cdr (assq 'dir obj))))
324 (unless (memq dir '(hor ver both))
325 (error "Can't find dir in %s" obj))
326 dir))))
327
328 (defun bw-eqdir(obj1 obj2)
329 "Return t if window split tree directions are equal.
330 OBJ1 and OBJ2 should be either windows or window split trees in
331 our format. The directions returned by `bw-dir' are compared and
332 t is returned if they are `eq' or one of them is 'both."
333 (let ((dir1 (bw-dir obj1))
334 (dir2 (bw-dir obj2)))
335 (or (eq dir1 dir2)
336 (eq dir1 'both)
337 (eq dir2 'both))))
338
339 ;;; Building split tree
340
341 (defun bw-refresh-edges(obj)
342 "Refresh the edge information of OBJ and return OBJ."
343 (unless (windowp obj)
344 (let ((childs (cdr (assq 'childs obj)))
345 (ol 1000)
346 (ot 1000)
347 (or -1)
348 (ob -1))
349 (dolist (o childs)
350 (when (> ol (bw-l o)) (setq ol (bw-l o)))
351 (when (> ot (bw-t o)) (setq ot (bw-t o)))
352 (when (< or (bw-r o)) (setq or (bw-r o)))
353 (when (< ob (bw-b o)) (setq ob (bw-b o))))
354 (setq obj (delq 'l obj))
355 (setq obj (delq 't obj))
356 (setq obj (delq 'r obj))
357 (setq obj (delq 'b obj))
358 (add-to-list 'obj (cons 'l ol))
359 (add-to-list 'obj (cons 't ot))
360 (add-to-list 'obj (cons 'r or))
361 (add-to-list 'obj (cons 'b ob))
362 ))
363 obj)
364
365 ;;; Balance windows
366
367 (defun balance-windows(&optional window-or-frame)
368 "Make windows the same heights or widths in window split subtrees.
369
370 When called non-interactively WINDOW-OR-FRAME may be either a
371 window or a frame. It then balances the windows on the implied
372 frame. If the parameter is a window only the corresponding window
373 subtree is balanced."
234 (interactive) 374 (interactive)
235 (let ((count -1) levels newsizes level-size 375 (let (
236 ;; Don't count the lines that are above the uppermost windows. 376 (wt (bw-get-tree window-or-frame))
237 ;; (These are the menu bar lines, if any.) 377 (w)
238 (mbl (nth 1 (window-edges (frame-first-window (selected-frame))))) 378 (h)
239 (last-window (previous-window (frame-first-window (selected-frame)))) 379 (tried-sizes)
240 ;; Don't count the lines that are past the lowest main window. 380 (last-sizes)
241 total) 381 (windows (window-list nil 0))
242 ;; Bottom edge of last window determines what size we have to work with. 382 (counter 0))
243 (setq total 383 (when wt
244 (+ (window-height last-window) 384 (while (not (member last-sizes tried-sizes))
245 (nth 1 (window-edges last-window)))) 385 (when last-sizes (setq tried-sizes (cons last-sizes tried-sizes)))
246 386 (setq last-sizes (mapcar (lambda(w)
247 ;; Find all the different vpos's at which windows start, 387 (window-edges w))
248 ;; then count them. But ignore levels that differ by only 1. 388 windows))
249 (let (tops (prev-top -2)) 389 (when (eq 'hor (bw-dir wt))
250 (walk-windows (function (lambda (w) 390 (setq w (- (bw-r wt) (bw-l wt))))
251 (setq tops (cons (nth 1 (window-edges w)) 391 (when (eq 'ver (bw-dir wt))
252 tops)))) 392 (setq h (- (bw-b wt) (bw-t wt))))
253 'nomini) 393 (bw-balance-sub wt w h)))))
254 (setq tops (sort tops '<)) 394
255 (while tops 395 (defun bw-adjust-window(window delta horizontal)
256 (if (> (car tops) (1+ prev-top)) 396 "Wrapper around `adjust-window-trailing-edge' with error checking.
257 (setq prev-top (car tops) 397 Arguments WINDOW, DELTA and HORIZONTAL are passed on to that function."
258 count (1+ count))) 398 (condition-case err
259 (setq levels (cons (cons (car tops) count) levels)) 399 (adjust-window-trailing-edge window delta horizontal)
260 (setq tops (cdr tops))) 400 (error
261 (setq count (1+ count))) 401 ;;(message "adjust: %s" (error-message-string err))
262 ;; Subdivide the frame into desired number of vertical levels. 402 )))
263 (setq level-size (/ (- total mbl) count)) 403
264 (save-selected-window 404 (defun bw-balance-sub(wt w h)
265 ;; Set up NEWSIZES to map windows to their desired sizes. 405 (setq wt (bw-refresh-edges wt))
266 ;; If a window ends at the bottom level, don't include 406 (unless w (setq w (- (bw-r wt) (bw-l wt))))
267 ;; it in NEWSIZES. Those windows get the right sizes 407 (unless h (setq h (- (bw-b wt) (bw-t wt))))
268 ;; by adjusting the ones above them. 408 (if (windowp wt)
269 (walk-windows (function 409 (progn
270 (lambda (w) 410 (when w
271 (let ((newtop (cdr (assq (nth 1 (window-edges w)) 411 (let ((dw (- w (- (bw-r wt) (bw-l wt)))))
272 levels))) 412 (when (/= 0 dw)
273 (newbot (cdr (assq (+ (window-height w) 413 (bw-adjust-window wt dw t))))
274 (nth 1 (window-edges w))) 414 (when h
275 levels)))) 415 (let ((dh (- h (- (bw-b wt) (bw-t wt)))))
276 (if newbot 416 (when (/= 0 dh)
277 (setq newsizes 417 (bw-adjust-window wt dh nil)))))
278 (cons (cons w (* level-size (- newbot newtop))) 418 (let* ((childs (cdr (assq 'childs wt)))
279 newsizes)))))) 419 (lastchild (car (last childs)))
280 'nomini) 420 (cw (when w (/ w (if (bw-eqdir 'hor wt) (length childs) 1))))
281 ;; Make walk-windows start with the topmost window. 421 (ch (when h (/ h (if (bw-eqdir 'ver wt) (length childs) 1)))))
282 (select-window (previous-window (frame-first-window (selected-frame)))) 422 (dolist (c childs)
283 (let (done (count 0)) 423 (bw-balance-sub c cw ch)))))
284 ;; Give each window its precomputed size, or at least try. 424
285 ;; Keep trying until they all get the intended sizes, 425 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
286 ;; but not more than 3 times (to prevent infinite loop).
287 (while (and (not done) (< count 3))
288 (setq done t)
289 (setq count (1+ count))
290 (walk-windows (function (lambda (w)
291 (select-window w)
292 (let ((newsize (cdr (assq w newsizes))))
293 (when newsize
294 (enlarge-window (- newsize
295 (window-height))
296 nil)
297 (unless (= (window-height) newsize)
298 (setq done nil))))))
299 'nomini))))))
300 426
301 ;; I think this should be the default; I think people will prefer it--rms. 427 ;; I think this should be the default; I think people will prefer it--rms.
302 (defcustom split-window-keep-point t 428 (defcustom split-window-keep-point t
303 "*If non-nil, \\[split-window-vertically] keeps the original point \ 429 "*If non-nil, \\[split-window-vertically] keeps the original point \
304 in both children. 430 in both children.