1 ;;; winner.el --- Restore window configuration (or switch buffer)
3 ;; Copyright (C) 1997 Free Software Foundation. Inc.
5 ;; Author: Ivar Rummelhoff <ivarr@ifi.uio.no>
6 ;; Maintainer: Ivar Rummelhoff <ivarr@ifi.uio.no>
7 ;; Created: 27 Feb 1997
8 ;; Keywords: extensions, windows
10 ;; This file is part of GNU Emacs.
12 ;; GNU Emacs is free software; you can redistribute it and/or modify
13 ;; it under the terms of the GNU General Public License as published by
14 ;; the Free Software Foundation; either version 2, or (at your option)
17 ;; GNU Emacs is distributed in the hope that it will be useful,
18 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 ;; GNU General Public License for more details.
22 ;; You should have received a copy of the GNU General Public License
23 ;; along with GNU Emacs; see the file COPYING. If not, write to the
24 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
25 ;; Boston, MA 02111-1307, USA.
29 ;; Winner mode is a global minor mode that when turned on records
30 ;; changes in window configuration. This way the changes can be
31 ;; "undone" using the function `winner-undo'. By default this one is
32 ;; bound to the key sequence ctrl-x left. If you change your mind
33 ;; (while undoing), you can press ctrl-x right (calling
34 ;; `winner-redo'). Unlike the normal undo, you may have to skip
35 ;; through several identical window configurations in order to find
36 ;; the one you want. This is a bug due to some techical limitations
37 ;; in Emacs and can maybe be fixed in the future.
39 ;; In addition to this I have added `winner-switch' which is a program
40 ;; that switches to other buffers without disturbing Winner mode. If
41 ;; you bind this command to a key sequence, you may step through all
42 ;; your buffers (except the ones mentioned in `winner-skip-buffers' or
43 ;; matched by `winner-skip-regexps'). With a numeric prefix argument
44 ;; skip several buffers at a time.
48 (eval-when-compile (require 'cl
))
51 (defvar winner-dont-bind-my-keys nil
52 "If non-nil: Do not use `winner-mode-map' in Winner mode.")
54 (defvar winner-ring-size
100
55 "Maximum number of stored window configurations per frame.")
57 (defvar winner-skip-buffers
63 "Exclude these buffer names from any \(Winner switch\) list of buffers.")
65 (defvar winner-skip-regexps
'("^ ")
66 "Winner excludes buffers with names matching any of these regexps.
67 They are not included in any Winner mode list of buffers.
69 By default `winner-skip-regexps' is set to \(\"^ \"\),
70 which excludes \"invisible buffers\".")
72 (defvar winner-ring-alist nil
)
74 (defsubst winner-ring
(frame)
75 (or (cdr (assq frame winner-ring-alist
))
77 (push (cons frame
(make-ring winner-ring-size
))
79 (cdar winner-ring-alist
))))
81 (defvar winner-modified-list nil
)
83 (defun winner-change-fun ()
84 (or (memq (selected-frame) winner-modified-list
)
85 (push (selected-frame) winner-modified-list
)))
87 (defun winner-save-new-configurations ()
88 (while winner-modified-list
90 (winner-ring (car winner-modified-list
))
91 (current-window-configuration (pop winner-modified-list
)))))
93 (defun winner-set (conf)
94 (set-window-configuration conf
)
95 (if (eq (selected-window) (minibuffer-window))
99 ;;; Winner mode (a minor mode)
101 (defvar winner-mode-hook nil
102 "Functions to run whenever Winner mode is turned on.")
104 (defvar winner-mode-leave-hook nil
105 "Functions to run whenever Winner mode is turned off.")
107 (defvar winner-mode nil
) ; mode variable
108 (defvar winner-mode-map nil
"Keymap for Winner mode.")
110 (defun winner-mode (&optional arg
)
112 With arg, turn Winner mode on if and only if arg is positive."
114 (let ((on-p (if arg
(> (prefix-numeric-value arg
) 0)
120 (add-hook 'window-configuration-change-hook
'winner-change-fun
)
121 (add-hook 'post-command-hook
'winner-save-new-configurations
)
122 (setq winner-modified-list
(frame-list))
123 (winner-save-new-configurations)
124 (run-hooks 'winner-mode-hook
))
127 (setq winner-mode nil
)
128 (run-hooks 'winner-mode-leave-hook
)))
129 (force-mode-line-update)))
131 ;; Inspired by undo (simple.el)
133 (defvar winner-pending-undo-ring nil
)
135 (defvar winner-undo-counter nil
)
137 (defun winner-undo (arg)
138 "Switch back to an earlier window configuration saved by Winner mode.
139 In other words, \"undo\" changes in window configuration."
142 ((not winner-mode
) (error "Winner mode is turned off"))
143 ((eq (selected-window) (minibuffer-window))
144 (error "No winner undo from minibuffer."))
145 (t (setq this-command t
)
146 (if (eq last-command
'winner-undo
)
147 ;; This was no new window configuration after all.
148 (ring-remove winner-pending-undo-ring
0)
149 (setq winner-pending-undo-ring
(winner-ring (selected-frame)))
150 (setq winner-undo-counter
0))
151 (winner-undo-more (or arg
1))
152 (message "Winner undo (%d)!" winner-undo-counter
)
153 (setq this-command
'winner-undo
))))
155 (defun winner-undo-more (count)
156 "Undo N window configuration changes beyond what was already undone.
157 Call `winner-undo-start' to get ready to undo recent changes,
158 then call `winner-undo-more' one or more times to undo them."
159 (let ((len (ring-length winner-pending-undo-ring
)))
160 (incf winner-undo-counter count
)
161 (if (>= winner-undo-counter len
)
162 (error "No further window configuration undo information")
164 (ring-ref winner-pending-undo-ring
165 winner-undo-counter
)))))
167 (defun winner-redo ()
168 "Restore a more recent window configuration saved by Winner mode."
171 ((eq last-command
'winner-undo
)
172 (ring-remove winner-pending-undo-ring
0)
174 (ring-remove winner-pending-undo-ring
0))
175 (or (eq (selected-window) (minibuffer-window))
176 (message "Winner undid undo!")))
177 (t (error "Previous command was not a winner-undo"))))
181 (defun winner-switch-buffer-list ()
182 (loop for buf in
(buffer-list)
183 for name
= (buffer-name buf
)
184 unless
(or (eq (current-buffer) buf
)
185 (member name winner-skip-buffers
)
186 (loop for regexp in winner-skip-regexps
187 if
(string-match regexp name
) return t
191 (defvar winner-switch-list nil
)
193 (defun winner-switch (count)
194 "Step through your buffers without disturbing `winner-mode'.
195 `winner-switch' does not consider buffers mentioned in the list
196 `winner-skip-buffers' or matched by `winner-skip-regexps'."
199 (setq this-command t
)
201 ((eq last-command
'winner-switch
)
202 (if winner-mode
(ring-remove (winner-ring (selected-frame)) 0))
203 (bury-buffer (current-buffer))
204 (mapcar 'bury-buffer winner-switch-list
))
205 (t (setq winner-switch-list
(winner-switch-buffer-list))))
206 (setq winner-switch-list
(nthcdr count winner-switch-list
))
207 (or winner-switch-list
208 (setq winner-switch-list
(winner-switch-buffer-list))
209 (error "No more buffers"))
210 (switch-to-buffer (pop winner-switch-list
))
211 (message (concat "Winner: [%s] "
212 (mapconcat 'identity winner-switch-list
" "))
214 (setq this-command
'winner-switch
))
216 ;;;; To be evaluated when the package is loaded:
218 (unless winner-mode-map
219 (setq winner-mode-map
(make-sparse-keymap))
220 (define-key winner-mode-map
[?\C-x left
] 'winner-undo
)
221 (define-key winner-mode-map
[?\C-x right
] 'winner-redo
))
223 (unless (or (assq 'winner-mode minor-mode-map-alist
)
224 winner-dont-bind-my-keys
)
225 (push (cons 'winner-mode winner-mode-map
)
226 minor-mode-map-alist
))
228 (unless (assq 'winner-mode minor-mode-alist
)
229 (push '(winner-mode " Win") minor-mode-alist
))
233 ;;; winner.el ends here