From 94d2fc1815a919734353c942f224db1de4b4fcb8 Mon Sep 17 00:00:00 2001 From: Tom Willemsen Date: Mon, 7 Mar 2011 09:04:49 +0100 Subject: Django, org * Added nxhtml, mostly for django support. * Changed some org settings. --- emacs.d/nxhtml/util/winsav.el | 1585 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1585 insertions(+) create mode 100644 emacs.d/nxhtml/util/winsav.el (limited to 'emacs.d/nxhtml/util/winsav.el') diff --git a/emacs.d/nxhtml/util/winsav.el b/emacs.d/nxhtml/util/winsav.el new file mode 100644 index 0000000..771f6ce --- /dev/null +++ b/emacs.d/nxhtml/util/winsav.el @@ -0,0 +1,1585 @@ +;;; 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 -- cgit v1.2.3-54-g00ecf