Commit | Line | Data |
---|---|---|
1ffece3a | 1 | ;;; winner.el --- Restore window configuration (or switch buffer) |
a14fb2b1 | 2 | |
4f850b26 | 3 | ;; Copyright (C) 1997, 1998 Free Software Foundation. Inc. |
a14fb2b1 RS |
4 | |
5 | ;; Author: Ivar Rummelhoff <ivarr@ifi.uio.no> | |
6 | ;; Maintainer: Ivar Rummelhoff <ivarr@ifi.uio.no> | |
7 | ;; Created: 27 Feb 1997 | |
1ffece3a | 8 | ;; Keywords: extensions, windows |
a14fb2b1 | 9 | |
21f3d1d3 RS |
10 | ;; This file is part of GNU Emacs. |
11 | ||
12 | ;; GNU Emacs is free software; you can redistribute it and/or modify | |
a14fb2b1 RS |
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) | |
15 | ;; any later version. | |
16 | ||
21f3d1d3 | 17 | ;; GNU Emacs is distributed in the hope that it will be useful, |
a14fb2b1 RS |
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. | |
21 | ||
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. | |
26 | ||
27 | ;;; Commentary: | |
1ffece3a RS |
28 | |
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. | |
a14fb2b1 | 38 | ;; |
1ffece3a RS |
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. | |
a14fb2b1 RS |
45 | |
46 | ;;; Code: | |
47 | ||
4f850b26 | 48 | (require 'cl) |
1ffece3a | 49 | (require 'ring) |
a14fb2b1 | 50 | |
4f850b26 DL |
51 | (defgroup winner nil |
52 | "Restoring window configurations." | |
53 | :group 'windows) | |
54 | ||
55 | (defcustom winner-mode nil | |
56 | "Toggle winner-mode. | |
57 | You must modify via \\[customize] for this variable to have an effect." | |
58 | :set (lambda (symbol value) | |
59 | (winner-mode (or value 0))) | |
60 | :initialize 'custom-initialize-default | |
61 | :type 'boolean | |
62 | :group 'winner | |
63 | :require 'winner) | |
64 | ||
65 | (defcustom winner-dont-bind-my-keys nil | |
66 | "If non-nil: Do not use `winner-mode-map' in Winner mode." | |
67 | :type 'boolean | |
68 | :group 'winner) | |
a14fb2b1 | 69 | |
1ffece3a RS |
70 | (defvar winner-ring-size 100 |
71 | "Maximum number of stored window configurations per frame.") | |
a14fb2b1 | 72 | |
4f850b26 | 73 | (defcustom winner-skip-buffers |
a14fb2b1 RS |
74 | '("*Messages*", |
75 | "*Compile-Log*", | |
76 | ".newsrc-dribble", | |
77 | "*Completions*", | |
78 | "*Buffer list*") | |
4f850b26 DL |
79 | "Exclude these buffer names from any \(Winner switch\) list of buffers." |
80 | :type '(repeat string) | |
81 | :group 'winner) | |
a14fb2b1 | 82 | |
4f850b26 | 83 | (defcustom winner-skip-regexps '("^ ") |
79fd9b2f RS |
84 | "Winner excludes buffers with names matching any of these regexps. |
85 | They are not included in any Winner mode list of buffers. | |
a14fb2b1 RS |
86 | |
87 | By default `winner-skip-regexps' is set to \(\"^ \"\), | |
4f850b26 DL |
88 | which excludes \"invisible buffers\"." |
89 | :type '(repeat regexp) | |
90 | :group 'winner) | |
a14fb2b1 | 91 | |
1ffece3a | 92 | (defvar winner-ring-alist nil) |
a14fb2b1 | 93 | |
1ffece3a RS |
94 | (defsubst winner-ring (frame) |
95 | (or (cdr (assq frame winner-ring-alist)) | |
96 | (progn | |
97 | (push (cons frame (make-ring winner-ring-size)) | |
98 | winner-ring-alist) | |
99 | (cdar winner-ring-alist)))) | |
a14fb2b1 | 100 | |
1ffece3a | 101 | (defvar winner-modified-list nil) |
a14fb2b1 | 102 | |
1ffece3a | 103 | (defun winner-change-fun () |
d2f27357 KH |
104 | (or (memq (selected-frame) winner-modified-list) |
105 | (push (selected-frame) winner-modified-list))) | |
a14fb2b1 | 106 | |
1ffece3a RS |
107 | (defun winner-save-new-configurations () |
108 | (while winner-modified-list | |
109 | (ring-insert | |
110 | (winner-ring (car winner-modified-list)) | |
111 | (current-window-configuration (pop winner-modified-list))))) | |
a14fb2b1 | 112 | |
1ffece3a RS |
113 | (defun winner-set (conf) |
114 | (set-window-configuration conf) | |
115 | (if (eq (selected-window) (minibuffer-window)) | |
116 | (other-window 1))) | |
a14fb2b1 | 117 | |
a14fb2b1 | 118 | |
1ffece3a | 119 | ;;; Winner mode (a minor mode) |
a14fb2b1 | 120 | |
4f850b26 DL |
121 | (defcustom winner-mode-hook nil |
122 | "Functions to run whenever Winner mode is turned on." | |
123 | :type 'hook | |
124 | :group winner) | |
a14fb2b1 | 125 | |
1ffece3a | 126 | (defvar winner-mode-leave-hook nil |
4f850b26 DL |
127 | "Functions to run whenever Winner mode is turned off." |
128 | :type 'hook | |
129 | :group winner) | |
1ffece3a | 130 | |
a14fb2b1 | 131 | (defvar winner-mode-map nil "Keymap for Winner mode.") |
21f3d1d3 | 132 | |
4f850b26 | 133 | ;;;###autoload |
a14fb2b1 RS |
134 | (defun winner-mode (&optional arg) |
135 | "Toggle Winner mode. | |
136 | With arg, turn Winner mode on if and only if arg is positive." | |
137 | (interactive "P") | |
138 | (let ((on-p (if arg (> (prefix-numeric-value arg) 0) | |
139 | (not winner-mode)))) | |
140 | (cond | |
1ffece3a RS |
141 | ;; Turn mode on |
142 | (on-p | |
143 | (setq winner-mode t) | |
144 | (add-hook 'window-configuration-change-hook 'winner-change-fun) | |
145 | (add-hook 'post-command-hook 'winner-save-new-configurations) | |
146 | (setq winner-modified-list (frame-list)) | |
147 | (winner-save-new-configurations) | |
148 | (run-hooks 'winner-mode-hook)) | |
149 | ;; Turn mode off | |
150 | (winner-mode | |
151 | (setq winner-mode nil) | |
152 | (run-hooks 'winner-mode-leave-hook))) | |
a14fb2b1 RS |
153 | (force-mode-line-update))) |
154 | ||
1ffece3a | 155 | ;; Inspired by undo (simple.el) |
ac1f790a KH |
156 | |
157 | (defvar winner-pending-undo-ring nil) | |
158 | ||
159 | (defvar winner-undo-counter nil) | |
160 | ||
1ffece3a RS |
161 | (defun winner-undo (arg) |
162 | "Switch back to an earlier window configuration saved by Winner mode. | |
4f850b26 DL |
163 | In other words, \"undo\" changes in window configuration. |
164 | With prefix arg, undo that many levels." | |
1ffece3a RS |
165 | (interactive "p") |
166 | (cond | |
167 | ((not winner-mode) (error "Winner mode is turned off")) | |
168 | ((eq (selected-window) (minibuffer-window)) | |
169 | (error "No winner undo from minibuffer.")) | |
170 | (t (setq this-command t) | |
171 | (if (eq last-command 'winner-undo) | |
172 | ;; This was no new window configuration after all. | |
173 | (ring-remove winner-pending-undo-ring 0) | |
174 | (setq winner-pending-undo-ring (winner-ring (selected-frame))) | |
175 | (setq winner-undo-counter 0)) | |
176 | (winner-undo-more (or arg 1)) | |
177 | (message "Winner undo (%d)!" winner-undo-counter) | |
178 | (setq this-command 'winner-undo)))) | |
179 | ||
1ffece3a RS |
180 | (defun winner-undo-more (count) |
181 | "Undo N window configuration changes beyond what was already undone. | |
182 | Call `winner-undo-start' to get ready to undo recent changes, | |
183 | then call `winner-undo-more' one or more times to undo them." | |
184 | (let ((len (ring-length winner-pending-undo-ring))) | |
185 | (incf winner-undo-counter count) | |
186 | (if (>= winner-undo-counter len) | |
187 | (error "No further window configuration undo information") | |
188 | (winner-set | |
189 | (ring-ref winner-pending-undo-ring | |
190 | winner-undo-counter))))) | |
191 | ||
192 | (defun winner-redo () | |
193 | "Restore a more recent window configuration saved by Winner mode." | |
194 | (interactive) | |
195 | (cond | |
196 | ((eq last-command 'winner-undo) | |
197 | (ring-remove winner-pending-undo-ring 0) | |
198 | (winner-set | |
199 | (ring-remove winner-pending-undo-ring 0)) | |
200 | (or (eq (selected-window) (minibuffer-window)) | |
201 | (message "Winner undid undo!"))) | |
202 | (t (error "Previous command was not a winner-undo")))) | |
203 | ||
204 | ;;; Winner switch | |
205 | ||
206 | (defun winner-switch-buffer-list () | |
a14fb2b1 RS |
207 | (loop for buf in (buffer-list) |
208 | for name = (buffer-name buf) | |
209 | unless (or (eq (current-buffer) buf) | |
210 | (member name winner-skip-buffers) | |
211 | (loop for regexp in winner-skip-regexps | |
212 | if (string-match regexp name) return t | |
213 | finally return nil)) | |
214 | collect name)) | |
1ffece3a RS |
215 | |
216 | (defvar winner-switch-list nil) | |
217 | ||
218 | (defun winner-switch (count) | |
219 | "Step through your buffers without disturbing `winner-mode'. | |
220 | `winner-switch' does not consider buffers mentioned in the list | |
221 | `winner-skip-buffers' or matched by `winner-skip-regexps'." | |
222 | (interactive "p") | |
223 | (decf count) | |
224 | (setq this-command t) | |
225 | (cond | |
226 | ((eq last-command 'winner-switch) | |
227 | (if winner-mode (ring-remove (winner-ring (selected-frame)) 0)) | |
228 | (bury-buffer (current-buffer)) | |
229 | (mapcar 'bury-buffer winner-switch-list)) | |
230 | (t (setq winner-switch-list (winner-switch-buffer-list)))) | |
231 | (setq winner-switch-list (nthcdr count winner-switch-list)) | |
232 | (or winner-switch-list | |
233 | (setq winner-switch-list (winner-switch-buffer-list)) | |
234 | (error "No more buffers")) | |
235 | (switch-to-buffer (pop winner-switch-list)) | |
236 | (message (concat "Winner: [%s] " | |
237 | (mapconcat 'identity winner-switch-list " ")) | |
238 | (buffer-name)) | |
239 | (setq this-command 'winner-switch)) | |
a14fb2b1 | 240 | |
a14fb2b1 RS |
241 | ;;;; To be evaluated when the package is loaded: |
242 | ||
243 | (unless winner-mode-map | |
244 | (setq winner-mode-map (make-sparse-keymap)) | |
1ffece3a RS |
245 | (define-key winner-mode-map [?\C-x left] 'winner-undo) |
246 | (define-key winner-mode-map [?\C-x right] 'winner-redo)) | |
a14fb2b1 RS |
247 | |
248 | (unless (or (assq 'winner-mode minor-mode-map-alist) | |
249 | winner-dont-bind-my-keys) | |
250 | (push (cons 'winner-mode winner-mode-map) | |
251 | minor-mode-map-alist)) | |
252 | ||
253 | (unless (assq 'winner-mode minor-mode-alist) | |
254 | (push '(winner-mode " Win") minor-mode-alist)) | |
255 | ||
256 | (provide 'winner) | |
257 | ||
21f3d1d3 | 258 | ;;; winner.el ends here |