>From cd4bb24cc4d367af8c0972b1dddbefec04ab18d8 Mon Sep 17 00:00:00 2001 From: Jeremy Compostella Date: Mon, 16 Jan 2012 18:07:37 +0100 Subject: [PATCH] desktop.el: Add frames and windows configuration save&restore When desktop-save-frames is not nil (set to nil by default), the desktop-save functionn saves all the frame and their window configuration. All the frames will be automatically restored on the next Emacs session. When desktop-save-selected-frame-only is not nil only the current frame is saved and restored. Signed-off-by: Jeremy Compostella --- lisp/desktop.el | 151 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 files changed, 149 insertions(+), 2 deletions(-) diff --git a/lisp/desktop.el b/lisp/desktop.el index 2f79cc0..f7be80f 100644 --- a/lisp/desktop.el +++ b/lisp/desktop.el @@ -32,6 +32,7 @@ ;; - the mark & mark-active ;; - buffer-read-only ;; - some local variables +;; - the frames and their associated window configuration ;; To use this, use customize to turn on desktop-save-mode or add the ;; following line somewhere in your .emacs file: @@ -132,7 +133,7 @@ ;;; Code: -(defvar desktop-file-version "206" +(defvar desktop-file-version "207" "Version number of desktop file format. Written into the desktop file and used at desktop read to provide backward compatibility.") @@ -390,6 +391,52 @@ See `desktop-restore-eager'." :group 'desktop :version "22.1") +(defcustom desktop-save-frames nil + "If non-nil, offer to save and restore frames." + :type 'boolean + :group 'desktop + :version "24.1") + +(defcustom desktop-save-selected-frame-only nil + "If non-nil and `desktop-save-frames' non-nil, only the +current frame will be saved." + :type 'boolean + :group 'desktop + :version "24.1") + +(defcustom desktop-frame-parameters-to-save + '(name + top + left + width + height + modeline + fullscreen + minibuffer + horizontal-scroll-bars + font-parameter + font + font-backend) + "List of parameter to save for each frame." + :type '(repeat symbol) + :group 'desktop) + +(defcustom desktop-window-properties-to-save + '((set-window-start . window-start) + (set-window-point . window-point) + (set-window-dedicated-p . window-dedicated-p) + (set-window-margins . (lambda (w) `(,(car (window-margins w)) + ,(cdr (window-margins w))))) + (set-window-hscroll . window-hscroll) + (set-window-scroll-bars . (lambda (w) (let ((scroll (window-scroll-bars w))) + (cons (car scroll) (cddr scroll))))) + (set-window-fringes . window-fringes)) + "Associative list of window properties to save. Each property +is a cons cell of two functions \"restore\" and \"save\". The +\"restore\" function takes a window object and the returned value +of the \"save\" function. The \"save\" function takes a window object +as argument.") + ;;;###autoload (defvar desktop-save-buffer nil "When non-nil, save buffer status in desktop file. @@ -854,6 +901,92 @@ DIRNAME must be the directory in which the desktop file will be saved." ((eq desktop-file-name-format 'local) (file-relative-name filename dirname)) (t (expand-file-name filename)))) +;; ---------------------------------------------------------------------------- +;; Frame and window configuration +(defun desktop-window-info (window current-window) + "Return a list containing the WINDOW buffer-name, the WINDOW egdes, a +boolean to true when WINDOW is the CURRENT-WINDOW and the WINDOW +properties list as described by `desktop-window-properties-to-save'." + (let ((get-prop (lambda (fun) + (let ((prop (funcall fun window))) + (if (consp prop) prop (list prop)))))) + (with-current-buffer (window-buffer window) + (list (buffer-name) + (window-edges window) + (eq window current-window) + (mapcar (lambda (x) (cons (car x) (funcall get-prop (cdr x)))) + desktop-window-properties-to-save))))) + +;; ---------------------------------------------------------------------------- +(defun desktop-window-tree (frame) + "Return the window tree of frame FRAME replacing the window +objects by their save informations as returned by `desktop-window-info'" + (let* ((current-window (with-selected-frame frame (selected-window))) + (inner '(lambda (wtree) + (if (windowp wtree) + (desktop-window-info wtree current-window) + (setcdr (cdr wtree) (list (mapcar inner (cddr wtree)))) + wtree)))) + (funcall inner (car (window-tree frame))))) + +;; ---------------------------------------------------------------------------- +(defun desktop-frame-parameters (frame) + "Return the FRAME parameters associative list according to +`desktop-frame-parameters-to-save'." + (mapcar (lambda (param) + (cons param (let ((value (frame-parameter frame param))) + (if (windowp value) t value)))) + desktop-frame-parameters-to-save)) + +;; ---------------------------------------------------------------------------- +(defun desktop-restore-window (window-info) + "Restore the selected window properties from +WINDOW-INFO. WINDOW-INFO is a list as returned by +`desktop-window-info'." + (let* ((buf (get-buffer (car window-info))) + (window (selected-window)) + (current-window (when (nth 2 window-info) window))) + (when buf + (switch-to-buffer buf) + (dolist (elm (nth 3 window-info)) + (apply (car elm) (cons window (cdr elm))))) + (other-window 1) + current-window)) + +;; ---------------------------------------------------------------------------- +(defun desktop-restore-window-tree (window-tree) + "Restore the selected frame window tree from +WINDOW-TREE. WINDOW-TREE is a window tree as returned by +`desktop-window-tree'." + (let ((current-window nil)) + (if (stringp (car window-tree)) + (desktop-restore-window window-tree) + (let* ((direction (car window-tree)) + (subtree-list (car (cddr window-tree))) + (last-window (car (last subtree-list)))) + (dolist (subtree subtree-list) + (unless (eq last-window subtree) + (let* ((edges (nth 1 subtree)) + (size (if direction + (- (nth 3 edges) (nth 1 edges)) + (- (nth 2 edges) (nth 0 edges))))) + (split-window nil size (not direction)))) + (let ((window (desktop-restore-window-tree subtree))) + (when window (setq current-window window)))) + current-window)))) + +;; ---------------------------------------------------------------------------- +(defun desktop-restore-frame (use-current-frame frame-params window-tree) + "Restore a frame. If USE-CURRENT-FRAME is true, it uses the +selected frame otherwise it creates a new one. FRAME-PARAMS is a +frame parameters associative list as returned by +`desktop-frame-parameters' and WINDOW-TREE is a window tree as +returned by `desktop-window-tree'." + (when use-current-frame + (modify-frame-parameters (selected-frame) frame-params)) + (with-selected-frame (or (and use-current-frame (selected-frame)) + (make-frame frame-params)) + (select-window (desktop-restore-window-tree window-tree)))) ;; ---------------------------------------------------------------------------- ;;;###autoload @@ -920,6 +1053,17 @@ See also `desktop-base-file-name'." (insert "\n " (desktop-value-to-string e))) (insert ")\n\n")))) + (when (and desktop-save-frames (window-system)) + (insert "\n;; Frame and window configuration section:\n") + (let ((frames-to-save (if desktop-save-selected-frame-only + `(,(selected-frame)) + (frame-list)))) + (dolist (frame frames-to-save) + (insert (format "\n(desktop-restore-frame %s '%S '%S)\n" + (eq (selected-frame) frame) + (desktop-frame-parameters frame) + (desktop-window-tree frame)))))) + (setq default-directory desktop-dirname) (let ((coding-system-for-write 'emacs-mule)) (write-region (point-min) (point-max) (desktop-full-file-name) nil 'nomessage)) @@ -1008,7 +1152,10 @@ Using it may cause conflicts. Use it anyway? " owner))))) ;; move them here. (mapc 'bury-buffer (nreverse (cdr (memq desktop-first-buffer (nreverse (buffer-list)))))) - (switch-to-buffer (car (buffer-list))) + ;; If frames are saved/restored the current buffer is + ;; already restored + (unless desktop-save-frames + (switch-to-buffer (car (buffer-list)))) (run-hooks 'desktop-delay-hook) (setq desktop-delay-hook nil) (run-hooks 'desktop-after-read-hook) -- 1.7.2.5