;;; winsav.el --- Save and restore window structure ;; ;; Author: Lennart Borgman ;; Created: Sun Jan 14 2007 (defconst winsav:version "0.77") ;;Version: 0.77 ;; Last-Updated: 2009-08-04 Tue ;; Keywords: ;; Compatibility: ;; ;; Features that might be required by this library: ;; ;; None ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;;; Commentary: ;; ;; This library contains both user level commands and options and ;; functions for use in other elisp libraries. ;; ;;;; User level commands and options ;; ;; The user level commands and options are for saving frame, windows ;; and buffers between Emacs sessions. To do that you can customize ;; the options `desktop-save-mode' and `winsav-save-mode' or put this ;; at the end of your .emacs: ;; ;; (desktop-save-mode 1) ;; (winsav-save-mode 1) ;; ;; You can also save configurations that you later switch between. ;; For more information see the command `winsav-save-mode'. ;; ;; (There is also a command in this library for rotating window ;; borders in a frame, `winsav-rotate'. It is here just because the ;; needed support functions lives here.) ;; ;; ;; ;;;; Commands for other elisp libraries ;; ;; This library was orignally written to solve the problem of adding a ;; window to the left of some windows in a frame like the one below ;; ;; ___________ ;; | | | ;; | 1 | 2 | ;; |____|____| ;; | | ;; | 3 | ;; |_________| ;; ;; so that the window structure on the frame becomes ;; ;; ___________ ;; | | | | ;; | | 1| 2 | ;; | B|__|___| ;; | A| | ;; | R| 3 | ;; |__|______| ;; ;; ;; This problem can be solved by this library. However the solution in ;; this library is a bit more general: You first copy the window ;; structure and then restore that into another window. To do the ;; above you first copy the window structure in the first frame above ;; with `winsav-get-window-tree'. Then you create windows like this: ;; ;; ___________ ;; | | | ;; | | | ;; | B| | ;; | A| | ;; | R| | ;; |__|______| ;; ;; ;; Finally you use `winsav-put-window-tree' to put the window ;; structure into the right window. (Of course you could have put BAR ;; above, under etc.) ;; ;; ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; Bugs and limitations: ;; ;; Juanma Barranquero has pointed out there is a serious limitation in ;; this way of doing it when overlays with 'window properties are ;; used. The problem is that any pointers to windows are made invalid ;; since they are deleted. So in fact any code that relies on saved ;; pointers to windows will have problem if the window is one of those ;; that are involved here. ;; ;; To overcome this problem when doing something like inserting a BAR ;; window (see above) a new window has to be inserted in the existing ;; window tree on a frame in a way that is currently not supported in ;; Emacs. ;; ;; It would be nice to be have primitives to manipulate the window ;; tree more generally from elisp. That requires implementation of ;; them at the C level of course. ;; ;; However it is probably much easier to implement it quite a bit less ;; general. The concept of splitting is maybe then the right level to ;; search for primitives at. ;; ;; My conclusion is that it will take some time to find suitable ;; primitives for this. ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;;; Change log: ;; ;; Version 0.72: ;; ;; - Format of window structure changed in Emacs 23. Adopted to that. ;; - Added save and restore of frame/window configurations between ;; Emacs sessions. ;; - Added named winsav configurations for save and restore of frames, ;; windows, buffers and files. ;; ;; Version 0.71: ;; ;; - Added rotation of window structure. ;; ;; Version 0.70: ;; ;; - Support for save and restore from file. ;; ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation; either version 2, or (at your option) ;; any later version. ;; ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; ;; You should have received a copy of the GNU General Public License ;; along with this program; see the file COPYING. If not, write to the ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, ;; Boston, MA 02111-1307, USA. ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;;; Code: (eval-when-compile (require 'cl)) (eval-and-compile (require 'desktop)) ;; (defun winsav-upper-left-window(&optional frame w) ;; (let* ((tree (if w w (car (window-tree frame)))) ;; (is-split (not (windowp tree)))) ;; (if (not is-split) ;; tree ;; (winsav-upper-left-window frame (nth 2 tree))))) (defcustom winsav-after-get-hook nil "Hook to run after at the end of `winsav-get-window-tree'. The functions in this hook are called with one parameter which is the same as the return value from the function above." :type 'hook :group 'winsav) (defcustom winsav-after-put-hook nil "Hook to run after at the end of `winsav-put-window-tree'. The functions in this hook are called with one parameter which is a list where each element is a list \(old-win new-win) where OLD-WIN are the window from `winsav-get-window-tree' and NEW-WIN is the newly created corresponding window. This list is the same as the return value from the function above." :type 'hook :group 'winsav) (defun winsav-get-window-tree(&optional frame) "Get window structure. This returns an object with current windows with values, buffers, points and the selected window. FRAME is the frame to save structure from. If nil use selected. At the very end of this function the hook `winsav-after-get' is run." ;;(let* ((upper-left (winsav-upper-left-window frame)) (let* ((upper-left (frame-first-window frame)) (num -1) sel-num) (dolist (w (window-list frame nil upper-left)) (setq num (1+ num)) (when (eq w (selected-window)) (setq sel-num num))) (let ((ret (list sel-num (winsav-get-window-tree-1 frame nil)))) (run-hook-with-args 'winsav-after-get-hook ret) ret))) ;; Fix-me: add window-hscroll (defun winsav-get-window-tree-1(frame w) (let ((tree (if w w (car (window-tree frame))))) (if (windowp tree) ;; Note: Desktop is used for saving buffers. (with-current-buffer (window-buffer tree) (list (window-buffer tree) ;; buffer (buffer-name) (buffer-file-name) ;;buffer-read-only ;;(if mumamo-multi-major-mode mumamo-multi-major-mode major-mode) ;;minor-modes ;;buffer locals ;;(cons (+ 0 (mark-marker)) (mark-active)) ;; window (window-point tree) (window-edges tree) (window-scroll-bars tree) (window-fringes tree) (window-margins tree) (window-hscroll tree) ;; misc (window-dedicated-p tree) (when (fboundp 'window-redisplay-end-trigger) (window-redisplay-end-trigger tree)) (window-start tree) tree)) (let* ((dir (nth 0 tree)) (split (nth 1 tree)) (wt (cddr tree)) (wsubs (mapcar (lambda(wc) (winsav-get-window-tree-1 nil wc)) wt))) (append (list dir split) wsubs))))) ;;;###autoload (defun winsav-put-window-tree (saved-tree window &optional copy-win-ovl win-ovl-all-bufs) "Put window structure SAVED-TREE into WINDOW. Restore a structure SAVED-TREE returned from `winsav-get-window-tree' into window WINDOW. If COPY-WIN-OVL is non-nil then overlays having a 'window property pointing to one of the windows in SAVED-TREE where this window still is shown will be copied to a new overlay with 'window property pointing to the corresponding new window. If WIN-OVL-ALL-BUFS is non-nil then all buffers will be searched for overlays with a 'window property of the kind above. At the very end of this function the hook `winsav-after-put' is run." (let* ((sel-num (nth 0 saved-tree)) (tree (nth 1 saved-tree)) nsiz nh nw osiz oh ow scale-w scale-h first-win winsav-put-return) (unless (or (bufferp (car tree)) (eq 'buffer (car tree))) (setq nsiz (window-edges window)) (setq nh (- (nth 3 nsiz) (nth 1 nsiz))) (setq nw (- (nth 2 nsiz) (nth 0 nsiz))) (setq osiz (cadr tree)) (setq oh (- (nth 3 osiz) (nth 1 osiz))) (setq ow (- (nth 2 osiz) (nth 0 osiz))) (setq scale-w (unless (= ow nw) (/ nw (float ow)))) (setq scale-h (unless (= oh nh) (/ nh (float oh))))) (setq first-win (winsav-put-window-tree-1 tree window scale-w scale-h t 1)) (select-window first-win) (when sel-num (other-window sel-num)) (winsav-fix-win-ovl winsav-put-return copy-win-ovl win-ovl-all-bufs) (run-hook-with-args 'winsav-after-put-hook winsav-put-return) winsav-put-return)) (defun winsav-put-window-tree-1 (saved-tree window scale-w scale-h first-call level) "Helper for `winsav-put-window-tree'. For the arguments SAVED-TREE and WINDOW see that function. The arguments SCALE-W and SCALE-H are used to make the saved window config fit into its new place. FIRST-CALL is a state variable telling if this is the first round. LEVEL helps debugging by tells how far down we are in the call chain." (if (or (bufferp (car saved-tree)) ;;(not (car saved-tree)) (eq 'buffer (car saved-tree)) ) (let ((buffer (nth 0 saved-tree)) ;; buffer (bufnam (nth 1 saved-tree)) (filnam (nth 2 saved-tree)) ;;(mark (nth 3 saved-tree)) ;; window (point (nth 3 saved-tree)) (edges (nth 4 saved-tree)) (scroll (nth 5 saved-tree)) (fringe (nth 6 saved-tree)) (margs (nth 7 saved-tree)) (hscroll (nth 8 saved-tree)) (dedic (nth 9 saved-tree)) (trigger (nth 10 saved-tree)) (start (nth 11 saved-tree)) (ovlwin (nth 12 saved-tree)) scr2 (misbuf " *Winsav information: Buffer is gone*")) (or (windowp ovlwin) (not ovlwin) (error "Parameter mismatch, ovlwin not window: %s" ovlwin)) (when first-call (add-to-list 'winsav-put-return (list ovlwin window)) (when (eq 'buffer buffer) (when filnam (setq buffer (winsav-find-file-noselect filnam))) (if (buffer-live-p buffer) (or (string= bufnam (buffer-name buffer)) (eq (string-to-char bufnam) 32) ;; Avoid system buffer names (rename-buffer bufnam)) (when (eq (string-to-char bufnam) 32) (setq bufnam " *Winsav dummy buffer*")) ;; Fix-me, this might need some tweaking: Don't restore ;; buffers without a file name and without ;; content. (desktop-mode will make that when ;; necessary.) Just show the scratch buffer instead. (setq buffer (get-buffer bufnam)) (unless (and buffer (< 0 (buffer-size buffer))) (setq buffer (get-buffer-create "*scratch*"))))) (set-window-buffer window buffer) (set-window-dedicated-p window dedic) ;; Strange incompatibility in scroll args: (setq scr2 (list (nth 0 scroll) (nth 2 scroll) (nth 3 scroll))) (apply 'set-window-scroll-bars (append (list window) scr2)) (apply 'set-window-fringes (append (list window) fringe)) (set-window-margins window (car margs) (cdr margs)) (set-window-hscroll window hscroll) (unless (>= emacs-major-version 23) (with-no-warnings (set-window-redisplay-end-trigger window trigger)))) (let* ((nsiz (window-edges window)) (nh (- (nth 3 nsiz) (nth 1 nsiz))) (nw (- (nth 2 nsiz) (nth 0 nsiz))) (osiz edges) ;(nth 2 saved-tree)) (oh (- (nth 3 osiz) (nth 1 osiz))) (ow (- (nth 2 osiz) (nth 0 osiz))) (diff-w (- (if scale-w (round (* scale-w ow)) ow) nw)) (diff-h (- (if scale-h (round (* scale-h oh)) oh) nh))) ;; Avoid rounding naggings: (when (> (abs diff-h) 1) (bw-adjust-window window diff-h nil)) (when (> (abs diff-w) 1) (bw-adjust-window window diff-w t))) ;; Fix-me: there were some problems getting point correctly. Don't know why... (with-selected-window window (with-current-buffer (window-buffer window) (goto-char point)) (set-window-point window point) ;;(unless (buffer-live-p buffer) (setq point 1) (setq start 1)) (set-window-start window start) ;; Maybe point got off screen? (when (/= point (window-point window)) (set-window-point window point))) window) (let* ((ver (car saved-tree)) (wtree (list (cons window (caddr saved-tree)))) (nwin window) pwin pdelta (first-win nwin)) ;; First split to get it in correct order (when first-call (dolist (subtree (cdddr saved-tree)) (setq pwin nwin) ;;(message "nwin edges=%s, ver=%s" (window-edges nwin) ver) (let ((split-err nil) (window-min-height 1) (window-min-width 1)) (setq nwin (split-window nwin nil (not ver)))) ;; Make the previous window as small as permitted to allow ;; splitting as many times as possible (setq pdelta (- (if ver window-min-height window-min-width) (if ver (window-width pwin) (window-height pwin)))) ;;(message "pwin=%s, edges=%s, pdelta=%s, ver=%s" pwin (window-edges pwin) pdelta ver) ;; No reason to fail here: (condition-case err (adjust-window-trailing-edge pwin pdelta (not ver)) (error ;;(message "awt=>%s" (error-message-string err)) nil )) ;; Add to traverse (add-to-list 'wtree (cons nwin subtree) t))) ;; Now traverse. Sizing is a bit tricky, multiple runs have to ;; be done (as in balance-windows). (let (tried-sizes last-sizes (windows (window-list (selected-frame)))) (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)) (dolist (wsub (reverse wtree)) (select-window (car wsub)) (winsav-put-window-tree-1 (cdr wsub) (selected-window) scale-w scale-h first-call (1+ level) )) (setq first-call nil) )) first-win))) (defun winsav-fix-win-ovl(win-list copy-win-ovl win-ovl-all-bufs) (let ((oldwins (mapcar (lambda(elt) (car elt)) win-list)) ovlwin window) (let (buffers) (if win-ovl-all-bufs (setq buffers (buffer-list)) (mapc (lambda(w) (when (window-live-p w) (add-to-list 'buffers (window-buffer w)))) oldwins)) (dolist (buf buffers) (with-current-buffer buf (save-restriction (widen) (dolist (overlay (overlays-in (point-min) (point-max))) (when (setq ovlwin (car (memq (overlay-get overlay 'window) oldwins))) (setq window (cadr (assoc ovlwin win-list))) ;; If the old window is still alive then maybe copy ;; overlay, otherwise change the 'window prop. However ;; copy only if COPY-WIN-OVL is non-nil. (if (not (and (window-live-p ovlwin) (window-frame ovlwin))) (overlay-put overlay 'window window) (when copy-win-ovl (let* ((props (overlay-properties overlay)) (start (overlay-start overlay)) (end (overlay-end overlay)) ;; Fix-me: start and end marker props (newovl (make-overlay start end))) (while props (let ((key (car props)) (val (cadr props))) (setq props (cddr props)) (when (eq key 'window) (setq val window)) (overlay-put newovl key val)))))))))))))) (defun winsav-transform-edges (edges) "Just rotate the arguments in EDGES to make them fit next function." (let ((le (nth 0 edges)) (te (nth 1 edges)) (re (nth 2 edges)) (be (nth 3 edges))) (list te le be re))) (defun winsav-transform-1 (tree mirror transpose) "Mirroring of the window tree TREE. MIRROR could be 'mirror-top-bottom or 'mirror-left-right which I think explain what it does here. TRANSPOSE shifts the tree between a horisontal and vertical tree." (let* ((vertical (nth 0 tree)) (edges (nth 1 tree)) (subtrees (nthcdr 2 tree)) ) ;;(winsav-log "tree 1" tree) (when transpose (cond ((eq vertical nil) (setcar tree t)) ((eq vertical t) (setcar tree nil)) (t (error "Uh? vertical=%S" vertical)))) (setcar (nthcdr 1 tree) (winsav-transform-edges edges)) (dolist (subtree subtrees) (if (bufferp (car subtree)) (when transpose (let ((edges (nth 4 subtree))) ;;(winsav-log "subtree 1" subtree) (setcar (nthcdr 4 subtree) (winsav-transform-edges edges)) ;;(winsav-log "subtree 2" subtree) )) (winsav-transform-1 subtree mirror transpose))) (when (case mirror ('mirror-top-bottom vertical) ('mirror-left-right (not vertical)) (nil) ;; Don't mirror (t (error "Uh? mirror=%s" mirror))) (setcdr (nthcdr 1 tree) (reverse subtrees)) ) )) (defun winsav-find-file-noselect (filename) "Read file FILENAME into a buffer and return the buffer. Like `find-file-noselect', but if file is not find then creates a buffer with a message about that." (let ((buf (find-file-noselect filename))) (unless buf (setq buf (generate-new-buffer filename)) (with-current-buffer buf (insert "Winsav could not find the file " filename) (set-buffer-modified-p nil))) buf)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Session saving and restore etc ;;;###autoload (defgroup winsav nil "Save frames and windows when you exit Emacs." :group 'frames) ;;;###autoload (define-minor-mode winsav-save-mode "Toggle winsav configuration saving mode. With numeric ARG, turn winsav saving on if ARG is positive, off otherwise. When this mode is turned on, winsav configurations are saved from one session to another. A winsav configuration consists of frames, windows and visible buffers configurations plus optionally buffers and files managed by the functions used by option `desktop-save-mode' By default this is integrated with `desktop-save-mode'. If `desktop-save-mode' is on and `winsav-handle-also-desktop' is non-nil then save and restore also desktop. See the command `winsav-switch-config' for more information and other possibilities. Note: If you want to avoid saving when you exit just turn off this minor mode. For information about what is saved and restored and how to save and restore additional information see the function `winsav-save-configuration'." :global t :group 'winsav) (defun winsav-save-mode-on () "Ensable option `winsav-save-mode'. Provided for use in hooks." (winsav-save-mode 1)) (defun winsav-save-mode-off () "Disable option `winsav-save-mode'. Provided for use in hooks." (winsav-save-mode -1)) (defcustom winsav-save 'ask-if-new "Specifies whether the winsav config should be saved when it is killed. A winsav config \(winsav frame configuration) is killed when the user changes winsav directory or quits Emacs. Possible values are: t -- always save. ask -- always ask. ask-if-new -- ask if no winsav file exists, otherwise just save. ask-if-exists -- ask if winsav file exists, otherwise don't save. if-exists -- save if winsav file exists, otherwise don't save. nil -- never save. The winsav config is never saved when the option `winsav-save-mode' is nil. The variables `winsav-dirname' and `winsav-base-file-name' determine where the winsav config is saved." :type '(choice (const :tag "Always save" t) (const :tag "Always ask" ask) (const :tag "Ask if winsav file is new, else do save" ask-if-new) (const :tag "Ask if winsav file exists, else don't save" ask-if-exists) (const :tag "Save if winsav file exists, else don't" if-exists) (const :tag "Never save" nil)) :group 'winsav) (defcustom winsav-handle-also-desktop t "If this is non-nil then desktop is also saved and restored. See option `winsav-save-mode' for more information." :type 'boolean :group 'winsav) (defcustom winsav-base-file-name (convert-standard-filename ".emacs.winsav") "Base name of file for Emacs winsav, excluding directory part. The actual file name will have a system identifier added too." :type 'file :group 'winsav) (defvar winsav-dirname nil "The directory in which the winsav file should be saved.") (defun winsav-current-default-dir () "Current winsav configuration directory." (or winsav-dirname "~/")) ;;(find-file (winsav-full-file-name)) (defun winsav-default-file-name () "Default winsav save file name. The file name consist of `winsav-base-file-name' with a system identifier added. This will be '-nw' for a terminal and '-' + the value of `window-system' otherwise." (let ((sys-id (if (not window-system) "nw" (format "%s" window-system)))) (concat winsav-base-file-name "-" sys-id))) (defun winsav-full-file-name (&optional dirname) "Return the full name of the winsav session file in DIRNAME. DIRNAME omitted or nil means use `~'. The file name part is given by `winsav-default-file-name'." ;; Fix-me: Different frames in different files? Can multi-tty be handled?? (expand-file-name (winsav-default-file-name) (or dirname (winsav-current-default-dir)))) (defun winsav-serialize (obj) "Return a string with the printed representation of OBJ. This should be possible to eval and get a similar object like OBJ again." ;;(message "winsav-serialize a") (prin1-to-string obj) ;;(message "winsav-serialize b") ) (defcustom winsav-before-save-configuration-hook nil "Hook called before saving frames. Hook for writing elisp code at the beginning of a winsav configuration file. When this hook is called the current buffer and point is where the code should be written. This is a normal hook. For more information see `winsav-save-configuration'." :type 'hook :group 'winsav) (defcustom winsav-after-save-configuration-hook nil "Hook called after saving frames. Hook for writing elisp code at the end of a winsav configuration file. When this hook is called the current buffer and point is where the code should be written. This is a normal hook. For more information see `winsav-save-configuration'." :type 'hook :group 'winsav) (defcustom winsav-after-save-frame-hook nil "Hook called when saving a frame after saving frame data. Hook for writing elisp code in a winsav configuration file after each frame creation. When this hook is called code for restoring a frame has been written and code that sets `winsav-last-loaded-frame' to point to it. Point is in the configuration file buffer right after this. This is a normal hook. For more information see `winsav-save-configuration'." :type 'hook :group 'winsav) (defvar winsav-loaded-frames nil) (defvar winsav-last-loaded-frame nil) (defun winsav-restore-frame (frame-params window-tree-params use-minibuffer-frame window-state window-visible) "Restore a frame with specified values. If this is a minibuffer only frame then just apply the frame parameters FRAME-PARAMS. Otherwise create a new frame using FRAME-PARAMS and set up windows and buffers according to WINDOW-TREE-PARAMS. Also, if USE-MINIBUFFER-FRAME let the new frame have this minibuffer frame. WINDOW-STATE is 1 for minimized, 2 for normal and 3 for maximized." (let* ((default-minibuffer-frame use-minibuffer-frame) (frame-name (cdr (assoc 'name frame-params))) (minibuffer-val (cdr (assoc 'minibuffer frame-params))) (minibuffer-only (eq 'only minibuffer-val)) (mini-frames (delq nil (mapcar (lambda (frm) (when (eq 'only (frame-parameter frm 'minibuffer)) frm)) (frame-list)))) (frame-with-that-name (when (and frame-name minibuffer-only) (catch 'frame (dolist (frame (frame-list)) (when (string= frame-name (frame-parameter frame 'name)) (throw 'frame frame)))))) ;; If this is a minibuffer only frame then if it is already ;; there under a correct name then do not create it because ;; there might be variables pointing to it; just set the ;; parameters. Perhaps even better: if it is not already ;; there give an error - because it might be impossible to ;; set things up correctly then. (frame-with-that-name-has-mini (when frame-with-that-name (eq 'only (frame-parameter frame-with-that-name 'minibuffer)))) (this-mini-frame (when minibuffer-only (or frame-with-that-name (and (= 1 (length mini-frames)) (car mini-frames))))) (create-new (if minibuffer-only (if this-mini-frame ;frame-with-that-name-has-mini nil (error "Winsav: Can't find minibuffer only frame with name %s" frame-name)) t)) (this-frame (if create-new (make-frame frame-params) this-mini-frame)) (win (frame-first-window this-frame))) ;;(message "create-new=%s, frame-with-that-name=%s" create-new frame-with-that-name) ;; (when was-max ;; (winsav-set-maximized-size this-frame) ;; ;; Wait for maximize to occur so horizontal scrolling gets ok. ;; (sit-for 1.5) ;; ) (case window-state (1 (winsav-set-minimized-state this-frame)) (3 (winsav-set-maximized-state this-frame))) (unless window-visible (make-frame-invisible this-frame)) (if create-new (winsav-put-window-tree window-tree-params win) (modify-frame-parameters this-frame frame-params)) (setq winsav-last-loaded-frame this-frame) (setq winsav-loaded-frames (cons this-frame winsav-loaded-frames)) )) (defcustom winsav-frame-parameters-to-save '( ;;explicit-name ;;name ;;parent-id ;;title alpha auto-lower auto-raise background-color background-mode border-color border-width buffer-predicate cursor-color cursor-type font font-backend foreground-color fullscreen icon-name icon-type icon-left icon-top internal-border-width left-fringe line-spacing menu-bar-lines modeline mouse-color right-fringe screen-gamma scroll-bar-width tool-bar-lines top left width height tty-color-mode ;; ?? unsplittable user-position user-size vertical-scroll-bars visibility ) "Parameters saved for frames by `winsav-save-configuration'. Parameters are those returned by `frame-parameters'." :type '(repeat (symbol :tag "Frame parameter")) :group 'winsav) (defun frame-visible-really-p (frame) "Return t if FRAME is visible. This tries to be more corrent on w32 than `frame-visible-p'." (cond ((fboundp 'w32-frame-placement) (< 0 (nth 4 (w32-frame-placement frame)))) (t (frame-visible-p frame)))) (defun frame-maximized-p (frame) "Return t if it is known that frame is maximized." (cond ((fboundp 'w32-frame-placement) (= 3 (abs (nth 4 (w32-frame-placement frame))))) (t nil))) (defun frame-minimized-p (frame) "Return t if it is known that frame is minimized." (cond ((fboundp 'w32-frame-placement) (= 3 (abs (nth 4 (w32-frame-placement frame))))) (t nil))) ;;(winsav-set-restore-size nil) ;; (defun winsav-set-restore-size (frame) ;; (when (fboundp 'w32-send-sys-command) ;; (let ((cur-frm (selected-frame))) ;; (select-frame-set-input-focus frame) ;; (w32-send-sys-command #xf120) ;; ;; Note: sit-for must be used, not sleep-for. Using the latter ;; ;; prevents the fetching of the new size (for some reason I do not ;; ;; understand). ;; (sit-for 1.5) ;; (select-frame-set-input-focus cur-frm)) ;; t)) (defun winsav-set-maximized-state (frame) (when (fboundp 'w32-send-sys-command) (select-frame-set-input-focus frame) (w32-send-sys-command #xf030) (sit-for 1.0) t)) (defun winsav-set-minimized-state (frame) (when (fboundp 'w32-send-sys-command) (select-frame-set-input-focus frame) (w32-send-sys-command #xf020) (sit-for 1.0) t)) (defun winsav-save-frame (frame mb-frm-nr buffer) "Write into buffer BUFFER elisp code to recreate frame FRAME. If MB-FRM-NR is a number then it is the order number of the frame whose minibuffer should be used." (message "winsav-save-frame buffer=%s" buffer) (message "winsav-save-frame buffer 2=%s" (current-buffer)) (let* ((start nil) (end nil) (obj (winsav-get-window-tree frame)) (dummy (message "winsav-save-frame buffer 3=%s" (current-buffer))) (frm-size-now (cons (frame-pixel-height frame) (frame-pixel-width frame))) (dummy (message "winsav-save-frame buffer 4=%s" (current-buffer))) (placement (when (fboundp 'w32-frame-placement) (w32-frame-placement frame))) ;; (was-max (and frm-size-rst ;; (not (equal frm-size-now frm-size-rst)))) (window-state (abs (nth 4 placement))) ;; (frm-size-rst (when (winsav-set-restore-size frame) ;; (cons (frame-pixel-height frame) ;; (frame-pixel-width frame)))) ;;(frm-size-rst (when was-max)) ;;(frm-size-rst (when (= 3 (abs (nth 4 placement))))) (dummy (message "winsav-save-frame buffer 5=%s" (current-buffer))) (frm-par (frame-parameters frame)) (dummy (message "winsav-save-frame buffer 6=%s" (current-buffer))) ) (message "winsav-save-frame a1 cb=%s" (current-buffer)) (with-current-buffer buffer ;;(y-or-n-p (format "was-max=%s" was-max)) (message "winsav-save-frame a2 cb=%s" (current-buffer)) (setq frm-par (delq nil (mapcar (lambda (elt) (cond ((memq (car elt) winsav-frame-parameters-to-save) elt) ((eq (car elt) 'minibuffer) (let ((val (cdr elt))) (if (not (windowp val)) elt (if (eq (window-frame val) frame) nil (cons 'minibuffer nil))))))) frm-par))) (message "winsav-save-frame b cb=%s" (current-buffer)) (insert "(winsav-restore-frame\n'" ;;make-frame-params (winsav-serialize frm-par)) (message "winsav-save-frame b.0.1") ;;window-tree-params (setq start (point)) (insert "'" (winsav-serialize obj) "\n") (message "winsav-save-frame b.0.2") (setq end (copy-marker (point) t)) (message "winsav-save-frame b.0.3") (message "winsav-save-frame b.1") ;; (replace-regexp (rx "#"))) ;; (1+ ">")) ;; 1+ for indirect buffers ... ;; "buffer" ;; nil start end) (goto-char start) (while (re-search-forward (rx "#"))) (1+ ">")) ;; 1+ for indirect buffers ... end t) (replace-match "buffer" nil t)) (message "winsav-save-frame b.2") ;; (replace-regexp (rx "#"))) ;; (1+ ">")) ;; "nil" ;; nil start end) (goto-char start) (while (re-search-forward (rx "#"))) (1+ ">")) ;; 1+ for indirect buffers ... end t) (replace-match "nil" nil t)) (message "winsav-save-frame c") (goto-char end) ;;use-minibuffer-frame (insert (if mb-frm-nr (format "(nth %s (reverse winsav-loaded-frames))" mb-frm-nr) "nil") (format " %s" window-state) (if (frame-visible-really-p frame) " t " " nil ") ")\n\n") (insert " ;; ---- before after-save-frame-hook ----\n") ;; (dolist (fun winsav-after-save-frame-hook) ;; (funcall fun frame (current-buffer))) (run-hooks winsav-after-save-frame-hook) (message "winsav-save-frame d") (insert " ;; ---- after after-save-frame-hook ----\n") ;;(insert " )\n\n\n") ))) (defvar winsav-file-version "1" "Version number of winsav file format. Written into the winsav file and used at winsav read to provide backward compatibility.") ;; fix-me: This should be in desktop.el ;; Fix-me: incomplete, not ready. (defun winsav-restore-indirect-file-buffer (file name) "Make indirect buffer from file buffer visiting file FILE. Give it the name NAME." (let* ((fbuf (find-file-noselect file))) (when fbuf (make-indirect-buffer fbuf name)))) (defun winsav-save-indirect-buffers (to-buffer) "Save information about indirect buffers. Only file visiting buffers currently. Clone the base buffers." (with-current-buffer to-buffer (dolist (buf (buffer-list)) (when (buffer-base-buffer buf) (let* ((base-buf (buffer-base-buffer buf)) (file (buffer-file-name base-buf))) (when file (insert "(winsav-restore-indirect-file-buffer \"" file "\" \"" (buffer-name buf) "\")\n"))))))) ;; Fix-me: test ;; (defun winsav-restore-minibuffer (frame-num frm-num win-num) ;; (let* ((frame (nth (1- frame-num) winsav-loaded-frames)) ;; (mini-frm (nth (1- frm-num) winsav-loaded-frames)) ;; (mini-win (nth (1- win-num) (reverse (window-list mini-frm)))) ;; ) ;; (with-selected-frame frame ;; (set-minibuffer-window mini-win)))) (defvar winsav-minibuffer-alist nil) (defun winsav-save-minibuffers (sorted-frames to-buffer) "Save information about minibuffer frames. SORTED-FRAMES should be a list of all frames sorted using `winsav-frame-sort-predicate'." (with-current-buffer to-buffer (setq winsav-minibuffer-alist nil) (dolist (frame sorted-frames) (let* ((num-frames (length sorted-frames)) (mini-win (minibuffer-window frame)) (mini-frm (window-frame mini-win)) (win-num (length (memq mini-win (window-list mini-frm t (frame-first-window mini-frm))))) (frm-num (- num-frames (length (memq mini-frm sorted-frames)))) (frame-num (- num-frames (length (memq frame sorted-frames))))) (unless (and (eq mini-frm frame) (= win-num 1)) ;; Not the normal minibuffer window ;;(insert (format ";;(winsav-restore-minibuffer %s %s %s)\n" ;;(insert (format "'(%s %s)\n" frame-num frm-num) (setq winsav-minibuffer-alist (cons (list frame-num frm-num) winsav-minibuffer-alist)) ))) (insert "(setq winsav-minibuffer-alist '" (winsav-serialize winsav-minibuffer-alist) ")\n"))) (defun winsav-restore-dedicated-window (frame-num win-num dedicate-flag) "Set dedicated window flag. On frame number FRAME-NUM in `winsav-loaded-frames' set the dedicated flag on window number WIN-NUM to DEDICATE-FLAG." (let* ((frame (nth (1- frame-num) winsav-loaded-frames)) (win (nth (1- win-num) (reverse (window-list frame t (frame-first-window frame)))))) (set-window-dedicated-p win dedicate-flag))) (defun winsav-save-dedicated-windows (sorted-frames) "Save information about dedicated windows on frames in SORTED-FRAMES. Write this to current buffer." (dolist (frame sorted-frames) (dolist (win (window-list frame)) (when (window-dedicated-p win) (let ((frame-num (length (memq frame sorted-frames))) (win-num (length (memq win (window-list frame t (frame-first-window frame))))) (flag (window-dedicated-p win))) (insert (format "(winsav-restore-dedicated-window %s %s %S)\n" frame-num win-num flag)) ))))) (defun winsav-restore-ecb (frame-num layout-ecb) "Restore ECB. On frame number FRAME-NUM-ECB in `winsav-loaded-frames' restore ECB layout LAYOUT-ECB." (when (boundp 'ecb-minor-mode) (let* ((frame (nth (1- frame-num) winsav-loaded-frames))) (select-frame frame) (unless (string= layout-ecb ecb-layout-name) (setq ecb-layout-name layout-ecb)) (ecb-minor-mode 1)))) (defun winsav-save-ecb (frame-ecb layout-ecb sorted-frames) "Save information about ECB layout on frames in SORTED-FRAMES. Write this in current buffer." (dolist (frame sorted-frames) (when (eq frame frame-ecb) (let ((frame-num (length (memq frame sorted-frames)))) (insert (format "(winsav-restore-ecb %s %S)\n" frame-num layout-ecb)))))) ;; (make-frame '((minibuffer))) ;; (sort (frame-list) 'winsav-frame-sort-predicate) (defun winsav-frame-sort-predicate (a b) "Compare frame A and B for sorting. Sort in the order frames can be created. - Frames without minibuffers will come later since the need to refer to the minibuffer frame when they are created. - Invisible frames comes last since there must be at least one visible frame from the beginning." (let* ((a-mbw (minibuffer-window a)) (a-mbw-frm (window-frame a-mbw)) (b-mbw (minibuffer-window b)) (b-mbw-frm (window-frame b-mbw)) (a-visible (frame-visible-really-p a)) (b-visible (frame-visible-really-p b)) ) ;;(message "a-mbw-frm=%s, b=%s" a-mbw-frm b) ;;(message "b-mbw-frm=%s, a=%s" a-mbw-frm b) (when (or (not b-visible) (eq a-mbw-frm b) (not (eq b-mbw-frm b))) ;;(message "a > b") t ))) (defun winsav-can-read-config (config-version) "Return t we can read config file version CONFIG-VERSION." (when (<= config-version 1) t)) (defvar winsav-file-modtime nil) ;; Like desktop-save, fix-me (defun winsav-save-configuration (&optional dirname release) "Write elisp code to recreate all frames. Write into the file name computed by `winsav-full-file-name' given the argument DIRNAME. The information that is saved for each frame is its size and position, the window configuration including buffers and the parameters in `winsav-frame-parameters-to-save'. If you want save more information for frames you can do that in the hook `winsav-after-save-frame-hook'. See also the hook variables `winsav-before-save-configuration-hook' and `winsav-after-save-configuration-hook'. Fix-me: RELEASE is not implemented." (winsav-save-config-to-file (winsav-full-file-name dirname))) (defun winsav-save-config-to-file (conf-file) "Write elisp code to recreate all frames to CONF-FILE." (let (start end (sorted-frames (sort (frame-list) 'winsav-frame-sort-predicate)) (frm-nr 0) frame-ecb layout-ecb) ;; Recreating invisible frames hits Emacs bug 3859 (setq sorted-frames (delq nil (mapcar (lambda (f) (when (frame-parameter f 'visibility) f)) sorted-frames))) (when (and (boundp 'ecb-minor-mode) ecb-minor-mode) (when (frame-live-p ecb-frame) (setq layout-ecb ecb-layout-name) (setq frame-ecb ecb-frame)) (ecb-minor-mode -1) (sit-for 0) ;; Fix-me: is this needed? ) (message "winsav-save-config:here a") (with-temp-buffer (let ((this-buffer (current-buffer))) (message "winsav-save-config:here b") ;;(erase-buffer) (insert ";; -*- mode: emacs-lisp; coding: utf-8; -*-\n" ";; --------------------------------------------------------------------------\n" ";; Winsav File for Emacs\n" ";; --------------------------------------------------------------------------\n" ";; Created " (current-time-string) "\n" ";; Winsav file format version " winsav-file-version "\n" ";; Emacs version " emacs-version "\n\n" "(if (not (winsav-can-read-config " winsav-file-version "))\n\n" " (message \"Winsav: Can't read config file with version " winsav-file-version "\")\n") (message "winsav-save-config:here c") (insert ";; ---- indirect buffers ------------------------\n") (winsav-save-indirect-buffers this-buffer) (message "winsav-save-config:here c.1") ;;(insert ";; ---- special minibuffers ------------------------\n") (winsav-save-minibuffers sorted-frames this-buffer) (message "winsav-save-config:here c.2") (insert "(setq winsav-loaded-frames nil)\n") (insert ";; ---- before winsav-before-save-configuration-hook ------------------------\n") (run-hooks 'winsav-before-save-configuration-hook) (message "winsav-save-config:here c.2a cb=%s" (current-buffer)) (insert ";; ---- after winsav-before-save-configuration-hook ------------------------\n\n") (dolist (frm sorted-frames) (let ((mb-frm-nr (cadr (assoc frm-nr winsav-minibuffer-alist))) ;;(mb-frm (when mb-frm-nr (nth mb-frm-nr sorted-frames))) ) (message "winsav-save-config:here c.2b.1 tb=%s cb=%s frm=%s" this-buffer (current-buffer) frm) (winsav-save-frame frm mb-frm-nr this-buffer) (message "winsav-save-config:here c.2b.2") (setq frm-nr (1+ frm-nr)))) (message "winsav-save-config:here c.2c cb=%s" (current-buffer)) (insert ";; ---- dedicated windows ------------------------\n") (winsav-save-dedicated-windows sorted-frames) (message "winsav-save-config:here c.3") (insert ";; ---- ECB --------------------------------------\n") (winsav-save-ecb frame-ecb layout-ecb sorted-frames) (message "winsav-save-config:here c.4") (insert "\n\n;; ---- before winsav-after-save-configuration-hook ------------------------\n") (run-hooks 'winsav-after-save-configuration-hook) (message "winsav-save-config:here c.5") (insert "\n\n;; ---- before winsav-after-save-configuration-hook ------------------------\n") (run-hooks 'winsav-after-save-configuration-hook) (message "winsav-save-config:here c.6") (insert ";; ---- after winsav-after-save-configuration-hook ------------------------\n") (insert "\n)\n") (message "winsav-save-config:here d") ;; For pp-buffer: (let (emacs-lisp-mode-hook after-change-major-mode-hook change-major-mode-hook) (font-lock-mode -1) (emacs-lisp-mode)) (message "winsav-save-config:here e") (pp-buffer) (message "winsav-save-config:here f") (indent-region (point-min) (point-max)) (message "winsav-save-config:here g") ;;(save-buffer 0) ;; No backups ;;(kill-buffer) ;;(with-current-buffer (find-file-noselect file) (let ((coding-system-for-write 'utf-8)) (write-region (point-min) (point-max) conf-file nil 'nomessage)) (setq winsav-file-modtime (nth 5 (file-attributes conf-file))) (setq winsav-dirname (file-name-as-directory (file-name-directory conf-file))) (message "winsav-save-config:here h") )))) (defvar winsav-current-config-name nil) ;;(winsav-restore-configuration) ;;(winsav-full-file-name "~") ;; (defun winsav-restore-winsav-configuration () ;; ) (defcustom winsav-after-restore-hook nil "Normal hook run after a successful `winsav-restore-configuration'." :type 'hook :group 'winsav) ;; Like desktop-read, fix-me (defun winsav-restore-configuration (&optional dirname) "Restore frames from default file in directory DIRNAME. The default file is given by `winsav-default-file-name'. The file was probably written by `winsav-save-configuration'. Delete the frames that were used before." ;;(message "winsav-restore-configuration %s" dirname) (winsav-restore-config-from-file (winsav-full-file-name dirname))) (defun winsav-restore-config-from-file (conf-file) "Restore frames from configuration file CONF-FILE. The file was probably written by `winsav-save-configuration'. Delete the frames that were used before." (let ((old-frames (sort (frame-list) 'winsav-frame-sort-predicate)) (num-old-deleted 0) ;; Avoid winsav saving during restore. (winsav-save nil)) ;;(message "winsav:conf-file=%s" conf-file) (if (or (not conf-file) (not (file-exists-p conf-file))) (progn (message (propertize "Winsav: No default configuration file found" 'face 'secondary-selection)) t) ;; Ok (setq debug-on-error t) ;; fix-me (if (file-exists-p conf-file) (progn (load conf-file nil nil t) (setq winsav-file-modtime (nth 5 (file-attributes conf-file))) (setq winsav-dirname (file-name-as-directory (file-name-directory conf-file))) (when (< 0 (length winsav-loaded-frames)) (dolist (old (reverse old-frames)) (unless (eq 'only (frame-parameter old 'minibuffer)) (setq num-old-deleted (1+ num-old-deleted)) (delete-frame old))) ) (message "winsav-after-restore-hook =%S" winsav-after-restore-hook) (run-hooks 'winsav-after-restore-hook) (message "Winsav: %s frame(s) restored" (length winsav-loaded-frames)) t) ;; No winsav file found ;;(winsav-clear) (message "No winsav file: %s" conf-file) nil)))) ;; (defcustom winsav-add-to-desktop nil ;; "Set this to let desktop save and restore also winsav configurations." ;; :type 'boolean ;; :set (lambda (sym val) ;; (set-default sym val) ;; (if value ;; (progn ;; (add-hook 'desktop-after-read-hook 'winsav-restore-configuration) ;; (add-hook 'desktop-save-hook 'winsav-save-configuration)) ;; (remove-hook 'desktop-after-read-hook 'winsav-restore-configuration) ;; (remove-hook 'desktop-save-hook 'winsav-save-configuration)) ) ;; :group 'winsav) (defun winsav-restore-configuration-protected (&optional dirname) "Like `winsav-restore-configuration' but protect for errors. DIRNAME has the same meaning." (condition-case err (winsav-restore-configuration dirname) (error (message "winsav-restore-configuration: %s" err)))) (defun winsav-relative-~-or-full (dirname) (let* ((rel-dir (file-relative-name dirname (file-name-directory (winsav-full-file-name "~")))) (confname (if (string= ".." (substring rel-dir 0 2)) winsav-dirname (if (string= rel-dir "./") "(default)" (concat "~/" rel-dir))))) confname)) (defun winsav-tell-configuration () "Tell which winsav configuration that is used." (interactive) (save-match-data ;; runs in timer (let ((confname (if (not winsav-dirname) "(none)" (winsav-relative-~-or-full winsav-dirname)))) (if t ;;(called-interactively-p) (message (propertize (format "Current winsav config is '%s'" confname) 'face 'secondary-selection)) (save-window-excursion (delete-other-windows) (set-window-buffer (selected-window) (get-buffer-create " *winsav*")) (with-current-buffer (window-buffer) (momentary-string-display (propertize (format "\n\n\n Current winsav config is '%s'\n\n\n\n" confname) 'face 'secondary-selection) (window-start) (kill-buffer)))))))) (defun winsav-tell-configuration-request () "Start an idle timer to call `winsav-tell-configuration'." (run-with-idle-timer 1 nil 'winsav-tell-configuration)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Startup and shut down ;; Run after desktop at startup so that desktop has loaded files and ;; buffers. (defun winsav-after-init () "Restore frames and windows. Run this once after Emacs startup, after desktop in the `after-init-hook'." ;; Turn off with --no-deskttop: (unless desktop-save-mode (winsav-save-mode -1)) (when winsav-save-mode ;;(run-with-idle-timer 0.1 nil 'winsav-restore-configuration-protected) ;;(message "winsav-after-init") ;;(winsav-restore-configuration-protected) ;; In case of error make sure winsav-save-mode is turned off (setq inhibit-startup-screen t) (winsav-save-mode -1) (winsav-restore-configuration) (winsav-save-mode 1) )) (add-hook 'after-init-hook 'winsav-after-init t) (add-hook 'kill-emacs-hook 'winsav-kill) ;;(remove-hook 'kill-emacs-hook 'winsav-kill) (defun winsav-kill () "Save winsav frame configuration. Run this before Emacs exits." ;; (when winsav-save-mode ;; (let ((conf-dir (when winsav-current-config-name ;; (winsav-full-config-dir-name winsav-current-config-name)))) ;; (winsav-save-configuration conf-dir)))) (when (and winsav-save-mode (let ((exists (file-exists-p (winsav-full-file-name)))) (or (eq winsav-save t) (and exists (memq winsav-save '(ask-if-new if-exists))) (and (or (memq winsav-save '(ask ask-if-new)) (and exists (eq winsav-save 'ask-if-exists))) (y-or-n-p "Save winsav? "))))) (unless winsav-dirname ;; Fix-me: Since this can be a new user of winsav I think the ;; best thing to do here is to encourage the user to save in the ;; default directory since otherwise the winsav file will not be ;; loaded at startup. Desktop does not currently do that however ;; (report that!). (when (y-or-n-p "Winsav was not loaded from file. Save it to file? ") (let* ((full-file (winsav-full-file-name)) (default-directory (directory-file-name (file-name-directory full-file)))) (setq winsav-dirname (file-name-as-directory (expand-file-name (read-directory-name "Directory for winsav file: " nil nil t))))))) (when winsav-dirname (condition-case err ;;(winsav-save winsav-dirname t) (winsav-save-configuration winsav-dirname) (file-error (unless (yes-or-no-p (format "Error while saving winsav config: %s Save anyway? " (error-message-string err))) (signal (car err) (cdr err))))))) ;; If we own it, we don't anymore. ;;(when (eq (emacs-pid) (winsav-owner)) (winsav-release-lock)) ) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Switching configurations (defun winsav-restore-full-config (dirname) "Restore the winsav configuration in directory DIRNAME. If NAME is nil then restore the startup configuration." ;;(desktop-change-dir dirname) (when (and winsav-handle-also-desktop desktop-save-mode) (when (eq (emacs-pid) (desktop-owner)) (desktop-release-lock)) (desktop-clear) (desktop-read dirname)) (winsav-restore-configuration dirname) ;;(setq winsav-current-config-name name) (winsav-tell-configuration-request)) (defun winsav-full-config-dir-name (name) "Return full directory path where configuration NAME is stored." (let* ((base-dir (concat (winsav-full-file-name) ".d")) (conf-dir (expand-file-name name base-dir))) (setq conf-dir (file-name-as-directory conf-dir)) ;;(message "conf-dir=%s" conf-dir) conf-dir)) ;;;###autoload (defun winsav-save-full-config (dirname) "Saved current winsav configuration in directory DIRNAME. Then change to this configuration. See also `winsav-switch-config'." (unless (file-name-absolute-p dirname) (error "Directory ame must be absolute: %s" dirname)) (let* ((conf-dir (or dirname "~")) (old-conf-dir winsav-dirname)) (make-directory conf-dir t) (winsav-save-configuration conf-dir) (when (and winsav-handle-also-desktop desktop-save-mode) (desktop-release-lock) (desktop-save conf-dir)) ;;(unless (string= winsav-current-config-name name) (unless (string= old-conf-dir conf-dir) ;;(setq winsav-current-config-name name) (winsav-tell-configuration-request)))) ;; Fix-me: remove named configurations, use just dir as desktop (defun winsav-switch-to-default-config () "Change to default winsav configuration. See also `winsav-switch-config'." (interactive) (winsav-switch-config "~")) ;;;###autoload (defun winsav-switch-config (dirname) "Change to winsav configuration in directory DIRNAME. If DIRNAME is the current winsav configuration directory then offer to save it or restore it from saved values. Otherwise, before switching offer to save the current winsav configuration. Then finally switch to the new winsav configuration, creating it if it does not exist. If option `desktop-save-mode' is on then buffers and files are also restored and saved the same way. See also option `winsav-save-mode' and command `winsav-tell-configuration'." (interactive (list (let ((default-directory (or winsav-dirname default-directory)) (base-dir (concat (winsav-full-file-name) ".d")) new-dir) (make-directory base-dir t) (setq new-dir (read-directory-name "Winsav: Switch config directory: ")) (when (string= "" new-dir) (setq new-dir nil)) (or new-dir "~")))) (setq dirname (file-name-as-directory (expand-file-name dirname))) (catch 'stop (let ((conf-file (expand-file-name winsav-base-file-name dirname)) config-exists) (if (file-exists-p conf-file) (setq config-exists t) (unless (y-or-n-p (format "%s was not found. Create it? " conf-file)) (throw 'stop nil))) (if (string= winsav-dirname dirname) (if (y-or-n-p "You are already using this configuration, restore it from saved values? ") (winsav-restore-full-config winsav-dirname) (when (y-or-n-p "You are already using this winsav configuration, save it? ") (winsav-save-full-config winsav-dirname))) (when (y-or-n-p (format "Save current config, %s,\n first before switching to %s? " (if (and winsav-dirname (not (string= winsav-dirname (file-name-directory (winsav-full-file-name "~"))))) winsav-dirname "the startup config") dirname)) (winsav-save-full-config winsav-dirname)) (if config-exists (winsav-restore-full-config dirname) (winsav-save-full-config dirname)))))) ;;; Old things ;; (defun winsav-log-buffer () ;; (get-buffer-create "winsav log buffer")) ;; (defun winsav-log (mark obj) ;; (with-current-buffer (winsav-log-buffer) ;; (insert "=== " mark "===\n" (pp-to-string obj)))) ;; (global-set-key [f2] 'winsav-test-get) ;; (global-set-key [f3] 'winsav-test-put) ;; (defvar winsav-saved-window-tree nil) ;; (defun winsav-test-get() ;; (interactive) ;; (setq winsav-saved-window-tree (winsav-get-window-tree))) ;; (defun winsav-test-put() ;; (interactive) ;; (let ((ret (winsav-put-window-tree winsav-saved-window-tree ;; (selected-window)))) ;; ;;(message "ret=%s" ret) ;; )) ;; (defun winsav-serialize-to-file (obj file) ;; (with-current-buffer (find-file-noselect file) ;; ;;(erase-buffer) ;; (save-restriction ;; (widen) ;; (goto-char (point-max)) ;; (insert (winsav-serialize obj) ;; "\n")) ;; ;;(basic-save-buffer) ;; )) ;;(global-set-key [f11] 'winsav-rotate) ;; (defun winsav-de-serialize-window-tree-from-file (file) ;; (with-current-buffer (find-file-noselect file) ;; (save-restriction ;; (widen) ;; (let ((start (point)) ;; (end nil)) ;; (forward-list) ;; (setq end (point)) ;; ;;(goto-char (point-min)) ;; (winsav-de-serialize-window-tree (buffer-substring-no-properties start end)))))) ;; (defun winsav-restore-from-file (file) ;; (winsav-put-window-tree ;; (winsav-de-serialize-window-tree-from-file file) ;; (selected-window))) ;; (defun winsav-de-serialize-window-tree (str) ;; (save-match-data ;; (let ((read-str ;; (replace-regexp-in-string (rx "#"))) ;; ">") ;; "buffer" ;; str)) ;; obj-last ;; obj ;; last) ;; (setq read-str ;; (replace-regexp-in-string (rx "#"))) ;; ">") ;; "nil" ;; read-str)) ;; (setq obj-last (read-from-string read-str)) ;; (setq obj (car obj-last)) ;; (setq last (cdr obj-last)) ;; ;; Fix me, maby check there are only spaces left (or trim them above...) ;; obj))) (provide 'winsav) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; winsav.el ends here