aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMartin Rudalics2011-06-19 12:17:56 +0200
committerMartin Rudalics2011-06-19 12:17:56 +0200
commit9d89fec7458c978272a716c33df41f9958f7fe7f (patch)
treebd12fefafbf8461912c1d2d793247388baa213c9
parentfbf5b3ce9d95a61c06ebf09ee58c809469d71387 (diff)
downloademacs-9d89fec7458c978272a716c33df41f9958f7fe7f.tar.gz
emacs-9d89fec7458c978272a716c33df41f9958f7fe7f.zip
Provide functions for saving window configurations as Lisp objects.
* window.el (window-list-no-nils, window-state-ignored-parameters) (window-state-get-1, window-state-get, window-state-put-list) (window-state-put-1, window-state-put-2, window-state-put): New functions.
-rw-r--r--lisp/ChangeLog4
-rw-r--r--lisp/window.el305
2 files changed, 309 insertions, 0 deletions
diff --git a/lisp/ChangeLog b/lisp/ChangeLog
index 281c73528b2..8f3c0ea0572 100644
--- a/lisp/ChangeLog
+++ b/lisp/ChangeLog
@@ -19,6 +19,10 @@
19 display-buffer-normalize-options-inhibit is non-nil. 19 display-buffer-normalize-options-inhibit is non-nil.
20 (frame-auto-delete): New option. 20 (frame-auto-delete): New option.
21 (window-deletable-p): Use frame-auto-delete. 21 (window-deletable-p): Use frame-auto-delete.
22 (window-list-no-nils, window-state-ignored-parameters)
23 (window-state-get-1, window-state-get, window-state-put-list)
24 (window-state-put-1, window-state-put-2, window-state-put): New
25 functions.
22 26
232011-06-18 Chong Yidong <cyd@stupidchicken.com> 272011-06-18 Chong Yidong <cyd@stupidchicken.com>
24 28
diff --git a/lisp/window.el b/lisp/window.el
index 454aa6e2941..e79489e40b3 100644
--- a/lisp/window.el
+++ b/lisp/window.el
@@ -3500,6 +3500,311 @@ specific buffers."
3500 ;; (bw-finetune wins) 3500 ;; (bw-finetune wins)
3501 ;; (message "Done in %d rounds" round) 3501 ;; (message "Done in %d rounds" round)
3502 )) 3502 ))
3503
3504;;; Window states, how to get them and how to put them in a window.
3505(defsubst window-list-no-nils (&rest args)
3506 "Like LIST but do not add nil elements of ARGS."
3507 (delq nil (apply 'list args)))
3508
3509(defvar window-state-ignored-parameters '(quit-restore)
3510 "List of window parameters ignored by `window-state-get'.")
3511
3512(defun window-state-get-1 (window &optional markers)
3513 "Helper function for `window-state-get'."
3514 (let* ((type
3515 (cond
3516 ((window-vchild window) 'vc)
3517 ((window-hchild window) 'hc)
3518 (t 'leaf)))
3519 (buffer (window-buffer window))
3520 (selected (eq window (selected-window)))
3521 (head
3522 (window-list-no-nils
3523 type
3524 (unless (window-next window) (cons 'last t))
3525 (cons 'clone-number (window-clone-number window))
3526 (cons 'total-height (window-total-size window))
3527 (cons 'total-width (window-total-size window t))
3528 (cons 'normal-height (window-normal-size window))
3529 (cons 'normal-width (window-normal-size window t))
3530 (cons 'splits (window-splits window))
3531 (cons 'nest (window-nest window))
3532 (let (list)
3533 (dolist (parameter (window-parameters window))
3534 (unless (memq (car parameter)
3535 window-state-ignored-parameters)
3536 (setq list (cons parameter list))))
3537 (when list
3538 (cons 'parameters list)))
3539 (when buffer
3540 ;; All buffer related things go in here - make the buffer
3541 ;; current when retrieving `point' and `mark'.
3542 (with-current-buffer (window-buffer window)
3543 (let ((point (if selected (point) (window-point window)))
3544 (start (window-start window))
3545 (mark (mark)))
3546 (window-list-no-nils
3547 'buffer (buffer-name buffer)
3548 (cons 'selected selected)
3549 (when window-size-fixed (cons 'size-fixed window-size-fixed))
3550 (cons 'hscroll (window-hscroll window))
3551 (cons 'fringes (window-fringes window))
3552 (cons 'margins (window-margins window))
3553 (cons 'scroll-bars (window-scroll-bars window))
3554 (cons 'vscroll (window-vscroll window))
3555 (cons 'dedicated (window-dedicated-p window))
3556 (cons 'point (if markers (copy-marker point) point))
3557 (cons 'start (if markers (copy-marker start) start))
3558 (when mark
3559 (cons 'mark (if markers (copy-marker mark) mark)))))))))
3560 (tail
3561 (when (memq type '(vc hc))
3562 (let (list)
3563 (setq window (window-child window))
3564 (while window
3565 (setq list (cons (window-state-get-1 window markers) list))
3566 (setq window (window-right window)))
3567 (nreverse list)))))
3568 (append head tail)))
3569
3570(defun window-state-get (&optional window markers)
3571 "Return state of WINDOW as a Lisp object.
3572WINDOW can be any window and defaults to the root window of the
3573selected frame.
3574
3575Optional argument MARKERS non-nil means use markers for sampling
3576positions like `window-point' or `window-start'. MARKERS should
3577be non-nil only if the value is used for putting the state back
3578in the same session (note that markers slow down processing).
3579
3580The return value can be used as argument for `window-state-put'
3581to put the state recorded here into an arbitrary window. The
3582value can be also stored on disk and read back in a new session."
3583 (setq window
3584 (if window
3585 (if (window-any-p window)
3586 window
3587 (error "%s is not a live or internal window" window))
3588 (frame-root-window)))
3589 ;; The return value is a cons whose car specifies some constraints on
3590 ;; the size of WINDOW. The cdr lists the states of the subwindows of
3591 ;; WINDOW.
3592 (cons
3593 ;; Frame related things would go into a function, say `frame-state',
3594 ;; calling `window-state-get' to insert the frame's root window.
3595 (window-list-no-nils
3596 (cons 'min-height (window-min-size window))
3597 (cons 'min-width (window-min-size window t))
3598 (cons 'min-height-ignore (window-min-size window nil t))
3599 (cons 'min-width-ignore (window-min-size window t t))
3600 (cons 'min-height-safe (window-min-size window nil 'safe))
3601 (cons 'min-width-safe (window-min-size window t 'safe))
3602 ;; These are probably not needed.
3603 (when (window-size-fixed-p window) (cons 'fixed-height t))
3604 (when (window-size-fixed-p window t) (cons 'fixed-width t)))
3605 (window-state-get-1 window markers)))
3606
3607(defvar window-state-put-list nil
3608 "Helper variable for `window-state-put'.")
3609
3610(defun window-state-put-1 (state &optional window ignore totals)
3611 "Helper function for `window-state-put'."
3612 (let ((type (car state)))
3613 (setq state (cdr state))
3614 (cond
3615 ((eq type 'leaf)
3616 ;; For a leaf window just add unprocessed entries to
3617 ;; `window-state-put-list'.
3618 (setq window-state-put-list
3619 (cons (cons window state) window-state-put-list)))
3620 ((memq type '(vc hc))
3621 (let* ((horizontal (eq type 'hc))
3622 (total (window-total-size window horizontal))
3623 (first t)
3624 size new)
3625 (dolist (item state)
3626 ;; Find the next child window. WINDOW always points to the
3627 ;; real window that we want to fill with what we find here.
3628 (when (memq (car item) '(leaf vc hc))
3629 (if (assq 'last item)
3630 ;; The last child window. Below `window-state-put-1'
3631 ;; will put into it whatever ITEM has in store.
3632 (setq new nil)
3633 ;; Not the last child window, prepare for splitting
3634 ;; WINDOW. SIZE is the new (and final) size of the old
3635 ;; window.
3636 (setq size
3637 (if totals
3638 ;; Use total size.
3639 (cdr (assq (if horizontal 'total-width 'total-height) item))
3640 ;; Use normalized size and round.
3641 (round (* total
3642 (cdr (assq
3643 (if horizontal 'normal-width 'normal-height)
3644 item))))))
3645
3646 ;; Use safe sizes, we try to resize later.
3647 (setq size (max size (if horizontal
3648 window-safe-min-height
3649 window-safe-min-width)))
3650
3651 (if (window-sizable-p window (- size) horizontal 'safe)
3652 (let* ((window-nest (assq 'nest item)))
3653 ;; We must inherit the nesting, otherwise we might mess
3654 ;; up handling of atomic and side window.
3655 (setq new (split-window window size horizontal)))
3656 ;; Give up if we can't resize window down to safe sizes.
3657 (error "Cannot resize window %s" window))
3658
3659 (when first
3660 (setq first nil)
3661 ;; When creating the first child window add for parent
3662 ;; unprocessed entries to `window-state-put-list'.
3663 (setq window-state-put-list
3664 (cons (cons (window-parent window) state)
3665 window-state-put-list))))
3666
3667 ;; Now process the current window (either the one we've just
3668 ;; split or the last child of its parent).
3669 (window-state-put-1 item window ignore totals)
3670 ;; Continue with the last window split off.
3671 (setq window new))))))))
3672
3673(defun window-state-put-2 (ignore)
3674 "Helper function for `window-state-put'."
3675 (dolist (item window-state-put-list)
3676 (let ((window (car item))
3677 (clone-number (cdr (assq 'clone-number item)))
3678 (splits (cdr (assq 'splits item)))
3679 (nest (cdr (assq 'nest item)))
3680 (parameters (cdr (assq 'parameters item)))
3681 (state (cdr (assq 'buffer item))))
3682 ;; Put in clone-number.
3683 (when clone-number (set-window-clone-number window clone-number))
3684 (when splits (set-window-splits window splits))
3685 (when nest (set-window-nest window nest))
3686 ;; Process parameters.
3687 (when parameters
3688 (dolist (parameter parameters)
3689 (set-window-parameter window (car parameter) (cdr parameter))))
3690 ;; Process buffer related state.
3691 (when state
3692 ;; We don't want to raise an error here so we create a buffer if
3693 ;; there's none.
3694 (set-window-buffer window (get-buffer-create (car state)))
3695 (with-current-buffer (window-buffer window)
3696 (set-window-hscroll window (cdr (assq 'hscroll state)))
3697 (apply 'set-window-fringes
3698 (cons window (cdr (assq 'fringes state))))
3699 (let ((margins (cdr (assq 'margins state))))
3700 (set-window-margins window (car margins) (cdr margins)))
3701 (let ((scroll-bars (cdr (assq 'scroll-bars state))))
3702 (set-window-scroll-bars
3703 window (car scroll-bars) (nth 2 scroll-bars) (nth 3 scroll-bars)))
3704 (set-window-vscroll window (cdr (assq 'vscroll state)))
3705 ;; Adjust vertically.
3706 (if (memq window-size-fixed '(t height))
3707 ;; A fixed height window, try to restore the original size.
3708 (let ((delta (- (cdr (assq 'total-height item))
3709 (window-total-height window)))
3710 window-size-fixed)
3711 (when (window-resizable-p window delta)
3712 (resize-window window delta)))
3713 ;; Else check whether the window is not high enough.
3714 (let* ((min-size (window-min-size window nil ignore))
3715 (delta (- min-size (window-total-size window))))
3716 (when (and (> delta 0)
3717 (window-resizable-p window delta nil ignore))
3718 (resize-window window delta nil ignore))))
3719 ;; Adjust horizontally.
3720 (if (memq window-size-fixed '(t width))
3721 ;; A fixed width window, try to restore the original size.
3722 (let ((delta (- (cdr (assq 'total-width item))
3723 (window-total-width window)))
3724 window-size-fixed)
3725 (when (window-resizable-p window delta)
3726 (resize-window window delta)))
3727 ;; Else check whether the window is not wide enough.
3728 (let* ((min-size (window-min-size window t ignore))
3729 (delta (- min-size (window-total-size window t))))
3730 (when (and (> delta 0)
3731 (window-resizable-p window delta t ignore))
3732 (resize-window window delta t ignore))))
3733 ;; Set dedicated status.
3734 (set-window-dedicated-p window (cdr (assq 'dedicated state)))
3735 ;; Install positions (maybe we should do this after all windows
3736 ;; have been created and sized).
3737 (ignore-errors
3738 (set-window-start window (cdr (assq 'start state)))
3739 (set-window-point window (cdr (assq 'point state)))
3740 ;; I'm not sure whether we should set the mark here, but maybe
3741 ;; it can be used.
3742 (let ((mark (cdr (assq 'mark state))))
3743 (when mark (set-mark mark))))
3744 ;; Select window if it's the selected one.
3745 (when (cdr (assq 'selected state))
3746 (select-window window)))))))
3747
3748(defun window-state-put (state &optional window ignore)
3749 "Put window state STATE into WINDOW.
3750STATE should be the state of a window returned by an earlier
3751invocation of `window-state-get'. Optional argument WINDOW must
3752specify a live window and defaults to the selected one.
3753
3754Optional argument IGNORE non-nil means ignore minimum window
3755sizes and fixed size restrictions. IGNORE equal `safe' means
3756subwindows can get as small as `window-safe-min-height' and
3757`window-safe-min-width'."
3758 (setq window (normalize-live-window window))
3759 (let* ((frame (window-frame window))
3760 (head (car state))
3761 ;; We check here (1) whether the total sizes of root window of
3762 ;; STATE and that of WINDOW are equal so we can avoid
3763 ;; calculating new sizes, and (2) if we do have to resize
3764 ;; whether we can do so without violating size restrictions.
3765 (totals
3766 (and (= (window-total-size window)
3767 (cdr (assq 'total-height state)))
3768 (= (window-total-size window t)
3769 (cdr (assq 'total-width state)))))
3770 (min-height (cdr (assq 'min-height head)))
3771 (min-width (cdr (assq 'min-width head)))
3772 window-splits selected)
3773 (if (and (not totals)
3774 (or (> min-height (window-total-size window))
3775 (> min-width (window-total-size window t)))
3776 (or (not ignore)
3777 (and (setq min-height
3778 (cdr (assq 'min-height-ignore head)))
3779 (setq min-width
3780 (cdr (assq 'min-width-ignore head)))
3781 (or (> min-height (window-total-size window))
3782 (> min-width (window-total-size window t)))
3783 (or (not (eq ignore 'safe))
3784 (and (setq min-height
3785 (cdr (assq 'min-height-safe head)))
3786 (setq min-width
3787 (cdr (assq 'min-width-safe head)))
3788 (or (> min-height
3789 (window-total-size window))
3790 (> min-width
3791 (window-total-size window t))))))))
3792 ;; The check above might not catch all errors due to rounding
3793 ;; issues - so IGNORE equal 'safe might not always produce the
3794 ;; minimum possible state. But such configurations hardly make
3795 ;; sense anyway.
3796 (error "Window %s too small to accomodate state" window)
3797 (setq state (cdr state))
3798 (setq window-state-put-list nil)
3799 ;; Work on the windows of a temporary buffer to make sure that
3800 ;; splitting proceeds regardless of any buffer local values of
3801 ;; `window-size-fixed'. Release that buffer after the buffers of
3802 ;; all live windows have been set by `window-state-put-2'.
3803 (with-temp-buffer
3804 (set-window-buffer window (current-buffer))
3805 (window-state-put-1 state window nil totals)
3806 (window-state-put-2 ignore))
3807 (window-check frame))))
3503 3808
3504;;; Displaying buffers. 3809;;; Displaying buffers.
3505(defconst display-buffer-default-specifiers 3810(defconst display-buffer-default-specifiers