layout-restore.el

Download

;;; layout-restore.el
;; --- keep window configuration as layout and restore it simply.

;; Copyleft (C) Vektor

;; Emacs Lisp Archive Entry
;; Filename:      layout-restore.el
;; Version:       0.4
;; Keywords:      convenience window-configuration layout
;; Author:        Vektor (also Veldrin@SMTH)
;; Maintainer:    Vektor
;; Description:   keep window configuration as layout and restore it.
;; Compatibility: Emacs21.3 Emacs-CVS-21.3.50
;; URL:           http://www.emacswiki.org/cgi-bin/wiki/LayoutRestore

(defconst layout-restore-version "0.4"
  "LayoutRestore version number. The latest version is available from
http://www.emacswiki.org/cgi-bin/wiki/LayoutRestore")

;; NOTE: Read the commentary below for how to use this package.

;; 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 GNU Emacs; see the file COPYING.  If not, write to the
;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
;; Boston, MA 02111-1307, USA.

;;; Commentary:

;;
;;
;; -------------
;; Background:
;; -------------
;;
;; Sometimes I use multi windows to do my job, and when i
;; switch to other buffer and go back, the original layout is
;; gone.  I have to set it up again.
;; I was very annoyed about this so I tried other packages to
;; help me out.  I found `WinnerMode', which always did
;; something I didn't want, and `TaskMode', which seems too
;; powerful to be used simply.  So I wrote this by myself.
;; Actually this package is my first emacs extention package
;; and I don't really know if I have made it well.  But I think
;; this package is already quite useable.
;;
;;
;;
;; -------------
;; Commands:
;; -------------
;;
;; `layout-save-current' save the current window-configuration
;; as layout so that when next time you switch back to this
;; buffer, the layout will be brought back automatically.  You
;; can also manually use `layout-restore' to restore the layout.
;; When you feel a layout is no more needed, switch to the
;; buffer where saved it and use `layout-delete-current' to
;; delete this layout.  These codes are simple and well
;; documented, you can easily hack it by yourself.
;;
;;
;;
;; -------------
;; Set it up:
;; -------------
;;
;; To start using this package, add following lines to your emacs
;; startup file.
;; ---------------------------------------------------------------
;; (require 'layout-restore)
;; ;; save layout key
;; (global-set-key [?\C-c ?l] 'layout-save-current)
;; ;; load layout key
;; (global-set-key [?\C-c ?\C-l ?\C-l] 'layout-restore)
;; ;; cancel(delete) layout key
;; (global-set-key [?\C-c ?\C-l ?\C-c] 'layout-delete-current)
;; ---------------------------------------------------------------
;; Change the keybindings to whatever you like.
;;
;;
;;
;; -----------------
;; Detailed example:
;; -----------------
;;
;; Let's suppose you are working in buffer A and the emacs now
;; looks like:
;;
;; +-------+
;; |   A_  |
;; +---+---+  (layout 1)
;; | B | C |
;; +---+---+
;;
;; Now for some reason, you want to switch to other buffer and
;; do some other thing, these make your emacs looks like:
;;
;; +---+---+
;; |   |   |
;; | D_| E |  (layout 2)
;; |   |   |
;; +---+---+
;;
;; Now you are working in buffer D and want to switch back to
;; buffer A.  Usually you can do this simply by `C-x b RET'.
;; But what about buffer B and buffer C?  They won't be back
;; automatically when you switch to buffer A.  So your emacs
;; looks like below:
;;
;; +---+---+
;; |   |   |
;; | A_| E |
;; |   |   |
;; +---+---+
;;
;; Well, I am sure this is NOT what you want.
;;
;; By using this package, when you are in buffer A of layout 1,
;; press `C-c l' to remember this layout. Then switch to buffer
;; D to your work.  Now when you switch back to buffer A, the
;; buffer B and C will be brought back automatically, and be
;; placed exactly as where they were.
;;
;; If you want, you can alsa remember layout 2 when you are in
;; buffer D.  simply press `C-c l' to remember it, then you can
;; switch between layout 1 and layout 2 easily when you switch
;; between buffer A and D.
;;
;; To unmemorise a layout, simply press `C-c C-l C-c' in the
;; buffer where you press `C-c l' before.
;;
;;
;;
;; -----------
;; Contact me:
;; -----------
;;
;; Any question, or advice, please mail to
;;
;; XYZvektorXYZ@XYZyeahXYZ.net
;;
;; remove all XYZ from above address to get the real one.
;;

;;; Code:

 


(require 'advice)


(defvar layout-configuration-alist nil
  "This list contains  window configuration to restore for certain
buffer correlated layout. Each element of this list is a list itself,
it consists of 'active buffer of this layout', 'window-configuration
of this layout', '(buffer . buffer-name) cons of this layout'.")

(defvar layout-accept-buffer-by-name t
  "This variable decide whether we'll accept a different buffer which have
the same name in case we could find the original buffer. Useful when we want
to keep a layout after close one of its buffer and reopen it.")

(defvar layout-verbose t
  "Print verbose message.")

(defvar layout-restore-old-window-point nil
  "Restore the window point at the old place where layout recorded it.")

(defvar layout-restore-after-switchbuffer t
  "If we should restore layout after `switch-buffer'.")

(defvar layout-restore-after-killbuffer t
  "If we should restore layout after `kill-buffer'.")

(defvar layout-restore-after-otherwindow nil
  "If we should restore layout after `other-window', which normally invoked
by C-x o.")


(defun layout-save-current ()
  "Save the current layout, add a list of current layout to
layout-configuration-alist."
  (interactive)
  (let ((curbuf (current-buffer))
        (curwincfg (current-window-configuration))
        layoutcfg)
    (setq layoutcfg (list curbuf curwincfg))
    (dolist (window (window-list nil 0))
      ;; (window-list) maybe contain a minibuffer, append (nil 0) to avoid
      (setq layoutcfg
            (append layoutcfg
                    (list (cons (window-buffer window)
                                (buffer-name (window-buffer window)))))))
    (dolist (locfg layout-configuration-alist)
      (if (eq curbuf (car locfg))
          (setq layout-configuration-alist
                (delq locfg layout-configuration-alist))))
    (setq layout-configuration-alist
          (cons layoutcfg layout-configuration-alist)))
  (if layout-verbose (message "Current layout saved.")))
  

(defun layout-restore (&optional BUFFER)
  "Restore the layout related to the buffer BUFFER, if there is such a layout
saved in `layout-configuration-alist', and update the layout if necessary."
  (interactive)
  (if (not BUFFER) (setq BUFFER (current-buffer)))
  (let (wincfg
        buflist
        (new-point (not layout-restore-old-window-point))
        new-point-list
        buffer-changed-p
        bufname-changed-p
        new-buffer-cons-list
        (restorep t))
    (dolist (locfg layout-configuration-alist)
      (when (eq BUFFER (car locfg))
        (setq wincfg (cadr locfg))
        (setq buflist (cddr locfg))))
    (when wincfg
      (dolist (bufcons buflist)
        (if (buffer-live-p (car bufcons))
            (if (not (string= (buffer-name (car bufcons))
                              (cdr bufcons)))
                
                (setq bufname-changed-p t
                      new-buffer-cons-list
                      (append new-buffer-cons-list
                              (list (cons (car bufcons)
                                          (buffer-name (car bufcons)))))
                      new-point-list (append new-point-list
                                             (list (save-excursion
                                                     (set-buffer (car bufcons))
                                                     (point)))))
              (setq new-buffer-cons-list (append new-buffer-cons-list (list bufcons))
                    new-point-list (append new-point-list
                                           (list (save-excursion
                                                   (set-buffer (car bufcons))
                                                   (point))))))
          (if (not layout-accept-buffer-by-name)
              (setq buffer-changed-p t
                    restorep nil)
            ;; accept reopened buffer by name, if any
            (progn
              (setq buffer-changed-p t)
              (let ((rebuf (get-buffer (cdr bufcons))
                           ))
                (if (not rebuf)
                    (setq restorep nil)
                  (setq new-buffer-cons-list
                        (append new-buffer-cons-list
                                (list (cons rebuf (cdr bufcons))))
                        new-point-list
                        (append new-point-list
                                (list (save-excursion
                                        (set-buffer rebuf)
                                        (point)))))))))))
      (when restorep
        (set-window-configuration wincfg)
        (dolist (window (window-list nil 0))
          (set-window-buffer window (caar new-buffer-cons-list))
          (setq new-buffer-cons-list (cdr new-buffer-cons-list))
          (when new-point
            (set-window-point window (car new-point-list))
            (setq new-point-list (cdr new-point-list))))
        (if (or bufname-changed-p buffer-changed-p)
            (layout-save-current))
        (if layout-verbose (message "Previous saved layout restored.")))
      )))

(defun layout-delete-current (&optional BUFFER)
  "Delete the layout information from `layout-configuration-alist'
if there is an element list related to BUFFER."
  (interactive)
  (if (not BUFFER) (setq BUFFER (current-buffer)))
  (dolist (locfg layout-configuration-alist)
    (when (eq BUFFER (car locfg))
      (setq layout-configuration-alist
            (delq locfg layout-configuration-alist))
      (if layout-verbose (message "Layout about this buffer deleted.")))
    ))

(defun layout-unique-point-in-same-buffer-windows (&optional BUFFER)
  "Make identical opint in all windows of a same buffer."
  (if (not BUFFER) (setq BUFFER (current-buffer)))
  (dolist (locfg layout-configuration-alist)
    (when (eq BUFFER (car locfg))
      (save-excursion
        (set-buffer BUFFER)
        (let ((wlist (get-buffer-window-list BUFFER 0)))
          (dolist (window wlist)
            (set-window-point window (point))))))))
        

(defadvice switch-to-buffer (around layout-restore-after-switch-buffer (BUFFER))
  "Unique window point before `switch-to-buffer', and restore possible layout
after `switch-to-buffer'."
  (layout-unique-point-in-same-buffer-windows)
  ad-do-it
  (if layout-restore-after-switchbuffer 
      (layout-restore)))

(defadvice kill-buffer (after layout-restore-after-kill-buffer (BUFFER))
  "Restore possible layout after `kill-buffer' funcall."
  (if layout-restore-after-killbuffer
      (layout-restore)))

(defadvice other-window (after layout-restore-after-other-window (ARG))
  "Restore possible layout after `other-window' funcall."
  (if layout-restore-after-otherwindow
      (layout-restore)))

(ad-activate 'switch-to-buffer)
(ad-activate 'kill-buffer)
(ad-activate 'other-window)


(provide 'layout-restore)

;;; layout-restore.el ends here.