Mercurial > emacs
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. |