1 ;;; winner.el --- Restore window configuration or change 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.el provides a minor mode (`winner-mode') that does
30 ;; essentially two things:
32 ;; 1) It keeps track of changing window configurations, so that
33 ;; when you wish to go back to a previous view, all you have
34 ;; to do is to press C-left a couple of times.
36 ;; 2) It lets you switch to other buffers by pressing C-right.
38 ;; To use Winner mode, put this line in your .emacs file:
40 ;; (add-hook 'after-init-hook (lambda () (winner-mode 1)))
44 ;; 1. You may of course decide to use other bindings than those
45 ;; mentioned above. Just set these variables in your .emacs:
47 ;; `winner-prev-event'
48 ;; `winner-next-event'
50 ;; 2. When you have found the view of your choice
51 ;; (using your favourite keys), you may press ctrl-space
52 ;; (`winner-max-event') to `delete-other-windows'.
54 ;; 3. Winner now keeps one configuration stack for each frame.
58 ;; Yours sincerely, Ivar Rummelhoff
60 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
66 ;;;; Variables you may want to change
68 (defvar winner-prev-event
'C-left
69 "Winner mode binds this event to the command `winner-previous'.")
71 (defvar winner-next-event
'C-right
72 "Winner mode binds this event to the command `winner-next'.")
74 (defvar winner-max-event
67108896 ; CTRL-space
75 "Event for deleting other windows
76 after having selected a view with Winner.
78 The normal functions of this event will also be performed.
79 In the default case (CTRL-SPACE) the mark will be set.")
81 (defvar winner-skip-buffers
87 "Exclude these buffer names
88 from any \(Winner mode\) list of buffers.")
90 (defvar winner-skip-regexps
'("^ ")
91 "Exclude buffers with names matching any of these regexps.
92 ..from any \(Winner mode\) list of buffers.
94 By default `winner-skip-regexps' is set to \(\"^ \"\),
95 which excludes \"invisible buffers\".")
98 (defvar winner-limit
50
99 "Winner will save no more than 2 * `winner-limit' window configurations.
100 \(.. and no less than `winner-limit'.\)")
102 (defvar winner-mode-hook nil
103 "Functions to run whenever Winner mode is turned on.")
105 (defvar winner-mode-leave-hook nil
106 "Functions to run whenever Winner mode is turned off.")
108 (defvar winner-dont-bind-my-keys nil
109 "If non-nil: Do not use `winner-mode-map' in Winner mode.")
115 (eval-when-compile (require 'cl
))
118 (defvar winner-mode nil
) ; For the modeline.
119 (defvar winner-mode-map nil
"Keymap for Winner mode.")
122 (defun winner-mode (&optional arg
)
124 With arg, turn Winner mode on if and only if arg is positive."
126 (let ((on-p (if arg
(> (prefix-numeric-value arg
) 0)
129 (on-p (let ((winner-frames-changed (frame-list)))
130 (winner-do-save)) ; Save current configurations
131 (add-hook 'window-configuration-change-hook
'winner-save-configuration
)
133 (run-hooks 'winner-mode-hook
))
134 (t (remove-hook 'window-configuration-change-hook
'winner-save-configuration
)
136 (setq winner-mode nil
)
137 (run-hooks 'winner-mode-leave-hook
))))
138 (force-mode-line-update)))
141 ;; List of frames which have changed
142 (defvar winner-frames-changed nil
)
144 ;; Time to save the window configuration.
145 (defun winner-save-configuration ()
146 (push (selected-frame) winner-frames-changed
)
147 (add-hook 'post-command-hook
'winner-do-save
))
150 (defun winner-do-save ()
151 (let ((current (selected-frame)))
153 (do ((frames winner-frames-changed
(cdr frames
)))
155 (unless (memq (car frames
) (cdr frames
))
156 ;; Process each frame once.
157 (select-frame (car frames
))
158 (winner-push (current-window-configuration) (car frames
))))
159 (setq winner-frames-changed nil
)
160 (select-frame current
)
161 (remove-hook 'post-command-hook
'winner-do-save
))))
167 ;;;; Configuration stacks (one for each frame)
170 (defvar winner-stacks nil
) ; ------ " ------
173 ;; A stack of window configurations with some additional information.
174 (defstruct (winner-stack
175 (:constructor winner-stack-new
179 data place
(count 1))
182 ;; Return the stack of this frame
183 (defun winner-stack (frame)
184 (let ((stack (cdr (assq frame winner-stacks
))))
185 (if stack
(winner-stack-data stack
)
186 ;; Else make new stack
187 (letf (((selected-frame) frame
))
188 (let ((config (current-window-configuration)))
189 (push (cons frame
(winner-stack-new config
))
196 ;; Push this window configuration on the right stack,
197 ;; but make sure the stack doesn't get too large etc...
198 (defun winner-push (config frame
)
199 (let ((this (cdr (assq frame winner-stacks
))))
200 (if (not this
) (push (cons frame
(winner-stack-new config
))
202 (push config
(winner-stack-data this
))
203 (when (> (incf (winner-stack-count this
)) winner-limit
)
204 ;; No more than 2*winner-limit configs
205 (setcdr (winner-stack-place this
) nil
)
206 (setf (winner-stack-place this
)
207 (winner-stack-data this
))
208 (setf (winner-stack-count this
) 1)))))
217 ;;;; Selecting a window configuration
220 ;; Return list of names of other buffers, excluding the current buffer
221 ;; and buffers specified by the user.
222 (defun winner-other-buffers ()
223 (loop for buf in
(buffer-list)
224 for name
= (buffer-name buf
)
225 unless
(or (eq (current-buffer) buf
)
226 (member name winner-skip-buffers
)
227 (loop for regexp in winner-skip-regexps
228 if
(string-match regexp name
) return t
234 (defun winner-select (&optional arg
)
236 "Change to previous or new window configuration.
237 With arg start at position 1 if arg is positive, and
238 at -1 if arg is negative; else start at position 0.
239 \(For Winner to record changes in window configurations,
240 Winner mode must be turned on.\)"
246 ((> (prefix-numeric-value arg
) 0) winner-next-event
)
247 ((< (prefix-numeric-value arg
) 0) winner-prev-event
)
249 (if arg
(push arg unread-command-events
))
251 (let ((stack (winner-stack (selected-frame)))
253 (buffers (winner-other-buffers))
255 (config (current-window-configuration))
257 ;; `stack' and `store' are stacks of window configuration while
258 ;; `buffers' and `passed' are stacks of buffer names.
263 (setq event
(read-event))
266 ((eq event winner-prev-event
)
267 (cond (passed (push (pop passed
) buffers
)(decf pos
))
268 ((cdr stack
)(push (pop stack
) store
) (decf pos
))
269 (t (setq stack
(append (nreverse store
) stack
))
273 ((eq event winner-next-event
)
274 (cond (store (push (pop store
) stack
) (incf pos
))
275 (buffers (push (pop buffers
) passed
) (incf pos
))
276 (t (setq buffers
(nreverse passed
))
280 ((eq event winner-max-event
)
281 ;; Delete other windows and leave.
282 (delete-other-windows)
283 ;; Let this change be saved.
285 ;; Perform other actions of this event.
286 (push event unread-command-events
)
288 (t (push event unread-command-events
) (return)))
292 (passed (set-window-buffer (selected-window) (car passed
))
293 (message (concat "Winner\(%d\): [%s] "
294 (mapconcat 'identity buffers
" "))
297 (t (set-window-configuration (car stack
))
298 (if (window-minibuffer-p (selected-window))
300 (message "Winner\(%d\)" pos
))))
302 (quit (set-window-configuration config
)
305 ;; Do not record these changes.
306 (remove-hook 'post-command-hook
'winner-do-save
)
307 ;; Else update the buffer list and make sure that the displayed
308 ;; buffer is the same as the current buffer.
309 (switch-to-buffer (window-buffer)))))
315 (defun winner-previous ()
316 "Change to previous window configuration."
320 (defun winner-next ()
321 "Change to new window configuration."
328 ;;;; To be evaluated when the package is loaded:
330 (unless winner-mode-map
331 (setq winner-mode-map
(make-sparse-keymap))
332 (define-key winner-mode-map
(vector winner-prev-event
) 'winner-previous
)
333 (define-key winner-mode-map
(vector winner-next-event
) 'winner-next
))
335 (unless (or (assq 'winner-mode minor-mode-map-alist
)
336 winner-dont-bind-my-keys
)
337 (push (cons 'winner-mode winner-mode-map
)
338 minor-mode-map-alist
))
340 (unless (assq 'winner-mode minor-mode-alist
)
341 (push '(winner-mode " Win") minor-mode-alist
))
345 ;;; winner.el ends here