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 (pushnew (selected-frame) winner-modified-list
))
86 (defun winner-save-new-configurations ()
87 (while winner-modified-list
89 (winner-ring (car winner-modified-list
))
90 (current-window-configuration (pop winner-modified-list
)))))
92 (defun winner-set (conf)
93 (set-window-configuration conf
)
94 (if (eq (selected-window) (minibuffer-window))
98 ;;; Winner mode (a minor mode)
100 (defvar winner-mode-hook nil
101 "Functions to run whenever Winner mode is turned on.")
103 (defvar winner-mode-leave-hook nil
104 "Functions to run whenever Winner mode is turned off.")
106 (defvar winner-mode nil
) ; mode variable
107 (defvar winner-mode-map nil
"Keymap for Winner mode.")
109 (defun winner-mode (&optional arg
)
111 With arg, turn Winner mode on if and only if arg is positive."
113 (let ((on-p (if arg
(> (prefix-numeric-value arg
) 0)
119 (add-hook 'window-configuration-change-hook
'winner-change-fun
)
120 (add-hook 'post-command-hook
'winner-save-new-configurations
)
121 (setq winner-modified-list
(frame-list))
122 (winner-save-new-configurations)
123 (run-hooks 'winner-mode-hook
))
126 (setq winner-mode nil
)
127 (run-hooks 'winner-mode-leave-hook
)))
128 (force-mode-line-update)))
130 ;; Inspired by undo (simple.el)
131 (defun winner-undo (arg)
132 "Switch back to an earlier window configuration saved by Winner mode.
133 In other words, \"undo\" changes in window configuration."
136 ((not winner-mode
) (error "Winner mode is turned off"))
137 ((eq (selected-window) (minibuffer-window))
138 (error "No winner undo from minibuffer."))
139 (t (setq this-command t
)
140 (if (eq last-command
'winner-undo
)
141 ;; This was no new window configuration after all.
142 (ring-remove winner-pending-undo-ring
0)
143 (setq winner-pending-undo-ring
(winner-ring (selected-frame)))
144 (setq winner-undo-counter
0))
145 (winner-undo-more (or arg
1))
146 (message "Winner undo (%d)!" winner-undo-counter
)
147 (setq this-command
'winner-undo
))))
149 (defvar winner-pending-undo-ring nil
)
151 (defvar winner-undo-counter nil
)
153 (defun winner-undo-more (count)
154 "Undo N window configuration changes beyond what was already undone.
155 Call `winner-undo-start' to get ready to undo recent changes,
156 then call `winner-undo-more' one or more times to undo them."
157 (let ((len (ring-length winner-pending-undo-ring
)))
158 (incf winner-undo-counter count
)
159 (if (>= winner-undo-counter len
)
160 (error "No further window configuration undo information")
162 (ring-ref winner-pending-undo-ring
163 winner-undo-counter
)))))
165 (defun winner-redo ()
166 "Restore a more recent window configuration saved by Winner mode."
169 ((eq last-command
'winner-undo
)
170 (ring-remove winner-pending-undo-ring
0)
172 (ring-remove winner-pending-undo-ring
0))
173 (or (eq (selected-window) (minibuffer-window))
174 (message "Winner undid undo!")))
175 (t (error "Previous command was not a winner-undo"))))
179 (defun winner-switch-buffer-list ()
180 (loop for buf in
(buffer-list)
181 for name
= (buffer-name buf
)
182 unless
(or (eq (current-buffer) buf
)
183 (member name winner-skip-buffers
)
184 (loop for regexp in winner-skip-regexps
185 if
(string-match regexp name
) return t
189 (defvar winner-switch-list nil
)
191 (defun winner-switch (count)
192 "Step through your buffers without disturbing `winner-mode'.
193 `winner-switch' does not consider buffers mentioned in the list
194 `winner-skip-buffers' or matched by `winner-skip-regexps'."
197 (setq this-command t
)
199 ((eq last-command
'winner-switch
)
200 (if winner-mode
(ring-remove (winner-ring (selected-frame)) 0))
201 (bury-buffer (current-buffer))
202 (mapcar 'bury-buffer winner-switch-list
))
203 (t (setq winner-switch-list
(winner-switch-buffer-list))))
204 (setq winner-switch-list
(nthcdr count winner-switch-list
))
205 (or winner-switch-list
206 (setq winner-switch-list
(winner-switch-buffer-list))
207 (error "No more buffers"))
208 (switch-to-buffer (pop winner-switch-list
))
209 (message (concat "Winner: [%s] "
210 (mapconcat 'identity winner-switch-list
" "))
212 (setq this-command
'winner-switch
))
214 ;;;; To be evaluated when the package is loaded:
216 (unless winner-mode-map
217 (setq winner-mode-map
(make-sparse-keymap))
218 (define-key winner-mode-map
[?\C-x left
] 'winner-undo
)
219 (define-key winner-mode-map
[?\C-x right
] 'winner-redo
))
221 (unless (or (assq 'winner-mode minor-mode-map-alist
)
222 winner-dont-bind-my-keys
)
223 (push (cons 'winner-mode winner-mode-map
)
224 minor-mode-map-alist
))
226 (unless (assq 'winner-mode minor-mode-alist
)
227 (push '(winner-mode " Win") minor-mode-alist
))
231 ;;; winner.el ends here