1 ;;; winner.el --- Restore window configuration or change buffer
3 ;; (C) 1997 Ivar Rummelhoff
5 ;; Author: Ivar Rummelhoff <ivarr@ifi.uio.no>
6 ;; Maintainer: Ivar Rummelhoff <ivarr@ifi.uio.no>
7 ;; Created: 27 Feb 1997
9 ;; RCS: $Id: winner.el,v 1.13 1997/04/01 11:11:12 ivarr Exp ivarr $
10 ;; Keywords: extensions,windows
11 ;; Location: http://www.ifi.uio.no/~ivarr/share/elisp/
13 ;; This program is free software; you can redistribute it and/or modify
14 ;; it under the terms of the GNU General Public License as published by
15 ;; the Free Software Foundation; either version 2, or (at your option)
18 ;; This program is distributed in the hope that it will be useful,
19 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
20 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 ;; GNU General Public License for more details.
23 ;; You should have received a copy of the GNU General Public License
24 ;; along with GNU Emacs; see the file COPYING. If not, write to the
25 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
26 ;; Boston, MA 02111-1307, USA.
30 ;; Winner.el provides a minor mode (`winner-mode') that does
31 ;; essentially two things:
33 ;; 1) It keeps track of changing window configurations, so that
34 ;; when you wish to go back to a previous view, all you have
35 ;; to do is to press C-left a couple of times.
37 ;; 2) It lets you switch to other buffers by pressing C-right.
42 ;; 1. Put this file in a directory on your (emacs) load-path
43 ;; 2. Byte-compile the file (eg. with M-x byte-compile-file)
44 ;; 3. Put these two lines in your .emacs - file:
46 ;; (autoload 'winner-mode "winner" "Toggle Winner mode." t)
47 ;; (add-hook 'after-init-hook (lambda () (winner-mode 1)))
49 ;; 4. Restart emacs for changes to take effect.
51 ;; (This version of) Winner will only run properly
52 ;; on Emacs-19.35 or newer.
57 ;; 1. You may of course decide to use other bindings than those
58 ;; mentioned above. Just set these variables in your .emacs:
60 ;; `winner-prev-event'
61 ;; `winner-next-event'
63 ;; 2. When you have found the view of your choice
64 ;; (using your favourite keys), you may press ctrl-space
65 ;; (`winner-max-event') to `delete-other-windows'.
67 ;; 3. Winner now keeps one configuration stack for each frame.
71 ;; Yours sincerely, Ivar Rummelhoff
73 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
79 ;;;; Variables you may want to change
81 (defvar winner-prev-event
'C-left
82 "Winner mode binds this event to the command `winner-previous'.")
84 (defvar winner-next-event
'C-right
85 "Winner mode binds this event to the command `winner-next'.")
87 (defvar winner-max-event
67108896 ; CTRL-space
88 "Event for deleting other windows
89 after having selected a view with Winner.
91 The normal functions of this event will also be performed.
92 In the default case (CTRL-SPACE) the mark will be set.")
94 (defvar winner-skip-buffers
100 "Exclude these buffer names
101 from any \(Winner mode\) list of buffers.")
103 (defvar winner-skip-regexps
'("^ ")
104 "Exclude buffers with names matching any of these regexps.
105 ..from any \(Winner mode\) list of buffers.
107 By default `winner-skip-regexps' is set to \(\"^ \"\),
108 which excludes \"invisible buffers\".")
111 (defvar winner-limit
50
112 "Winner will save no more than 2 * `winner-limit' window configurations.
113 \(.. and no less than `winner-limit'.\)")
115 (defvar winner-mode-hook nil
116 "Functions to run whenever Winner mode is turned on.")
118 (defvar winner-mode-leave-hook nil
119 "Functions to run whenever Winner mode is turned off.")
121 (defvar winner-dont-bind-my-keys nil
122 "If non-nil: Do not use `winner-mode-map' in Winner mode.")
128 (eval-when-compile (require 'cl
))
131 (defvar winner-mode nil
) ; For the modeline.
132 (defvar winner-mode-map nil
"Keymap for Winner mode.")
133 (defun winner-mode (&optional arg
)
135 With arg, turn Winner mode on if and only if arg is positive."
137 (let ((on-p (if arg
(> (prefix-numeric-value arg
) 0)
140 (on-p (let ((winner-frames-changed (frame-list)))
141 (winner-do-save)) ; Save current configurations
142 (add-hook 'window-configuration-change-hook
'winner-save-configuration
)
144 (run-hooks 'winner-mode-hook
))
145 (t (remove-hook 'window-configuration-change-hook
'winner-save-configuration
)
147 (setq winner-mode nil
)
148 (run-hooks 'winner-mode-leave-hook
))))
149 (force-mode-line-update)))
152 ;; List of frames which have changed
153 (defvar winner-frames-changed nil
)
155 ;; Time to save the window configuration.
156 (defun winner-save-configuration ()
157 (push (selected-frame) winner-frames-changed
)
158 (add-hook 'post-command-hook
'winner-do-save
))
161 (defun winner-do-save ()
162 (let ((current (selected-frame)))
164 (do ((frames winner-frames-changed
(cdr frames
)))
166 (unless (memq (car frames
) (cdr frames
))
167 ;; Process each frame once.
168 (select-frame (car frames
))
169 (winner-push (current-window-configuration) (car frames
))))
170 (setq winner-frames-changed nil
)
171 (select-frame current
)
172 (remove-hook 'post-command-hook
'winner-do-save
))))
178 ;;;; Configuration stacks (one for each frame)
181 (defvar winner-stacks nil
) ; ------ " ------
184 ;; A stack of window configurations with some additional information.
185 (defstruct (winner-stack
186 (:constructor winner-stack-new
190 data place
(count 1))
193 ;; Return the stack of this frame
194 (defun winner-stack (frame)
195 (let ((stack (cdr (assq frame winner-stacks
))))
196 (if stack
(winner-stack-data stack
)
197 ;; Else make new stack
198 (letf (((selected-frame) frame
))
199 (let ((config (current-window-configuration)))
200 (push (cons frame
(winner-stack-new config
))
207 ;; Push this window configuration on the right stack,
208 ;; but make sure the stack doesn't get too large etc...
209 (defun winner-push (config frame
)
210 (let ((this (cdr (assq frame winner-stacks
))))
211 (if (not this
) (push (cons frame
(winner-stack-new config
))
213 (push config
(winner-stack-data this
))
214 (when (> (incf (winner-stack-count this
)) winner-limit
)
215 ;; No more than 2*winner-limit configs
216 (setcdr (winner-stack-place this
) nil
)
217 (setf (winner-stack-place this
)
218 (winner-stack-data this
))
219 (setf (winner-stack-count this
) 1)))))
228 ;;;; Selecting a window configuration
231 ;; Return list of names of other buffers, excluding the current buffer
232 ;; and buffers specified by the user.
233 (defun winner-other-buffers ()
234 (loop for buf in
(buffer-list)
235 for name
= (buffer-name buf
)
236 unless
(or (eq (current-buffer) buf
)
237 (member name winner-skip-buffers
)
238 (loop for regexp in winner-skip-regexps
239 if
(string-match regexp name
) return t
245 (defun winner-select (&optional arg
)
247 "Change to previous or new window configuration.
248 With arg start at position 1 if arg is positive, and
249 at -1 if arg is negative; else start at position 0.
250 \(For Winner to record changes in window configurations,
251 Winner mode must be turned on.\)"
257 ((> (prefix-numeric-value arg
) 0) winner-next-event
)
258 ((< (prefix-numeric-value arg
) 0) winner-prev-event
)
260 (if arg
(push arg unread-command-events
))
262 (let ((stack (winner-stack (selected-frame)))
264 (buffers (winner-other-buffers))
266 (config (current-window-configuration))
268 ;; `stack' and `store' are stacks of window configuration while
269 ;; `buffers' and `passed' are stacks of buffer names.
274 (setq event
(read-event))
277 ((eq event winner-prev-event
)
278 (cond (passed (push (pop passed
) buffers
)(decf pos
))
279 ((cdr stack
)(push (pop stack
) store
) (decf pos
))
280 (t (setq stack
(append (nreverse store
) stack
))
284 ((eq event winner-next-event
)
285 (cond (store (push (pop store
) stack
) (incf pos
))
286 (buffers (push (pop buffers
) passed
) (incf pos
))
287 (t (setq buffers
(nreverse passed
))
291 ((eq event winner-max-event
)
292 ;; Delete other windows and leave.
293 (delete-other-windows)
294 ;; Let this change be saved.
296 ;; Perform other actions of this event.
297 (push event unread-command-events
)
299 (t (push event unread-command-events
) (return)))
303 (passed (set-window-buffer (selected-window) (car passed
))
304 (message (concat "Winner\(%d\): [%s] "
305 (mapconcat 'identity buffers
" "))
308 (t (set-window-configuration (car stack
))
309 (if (window-minibuffer-p (selected-window))
311 (message "Winner\(%d\)" pos
))))
313 (quit (set-window-configuration config
)
316 ;; Do not record these changes.
317 (remove-hook 'post-command-hook
'winner-do-save
)
318 ;; Else update the buffer list and make sure that the displayed
319 ;; buffer is the same as the current buffer.
320 (switch-to-buffer (window-buffer)))))
326 (defun winner-previous ()
327 "Change to previous window configuration."
331 (defun winner-next ()
332 "Change to new window configuration."
339 ;;;; To be evaluated when the package is loaded:
341 (unless winner-mode-map
342 (setq winner-mode-map
(make-sparse-keymap))
343 (define-key winner-mode-map
(vector winner-prev-event
) 'winner-previous
)
344 (define-key winner-mode-map
(vector winner-next-event
) 'winner-next
))
346 (unless (or (assq 'winner-mode minor-mode-map-alist
)
347 winner-dont-bind-my-keys
)
348 (push (cons 'winner-mode winner-mode-map
)
349 minor-mode-map-alist
))
351 (unless (assq 'winner-mode minor-mode-alist
)
352 (push '(winner-mode " Win") minor-mode-alist
))
356 ;;; Winner.el ends here