(sh-font-lock-keywords): Don't crash for an unrecognized shell.
[bpt/emacs.git] / lisp / winner.el
CommitLineData
2a92dc25 1;;; winner.el --- Restore old window configurations
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
2a92dc25
RS
8;; Time-stamp: <1998-03-05 19:01:37 ivarr>
9;; Keywords: windows
a14fb2b1 10
21f3d1d3
RS
11;; This file is part of GNU Emacs.
12
13;; GNU Emacs is free software; you can redistribute it and/or modify
a14fb2b1
RS
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)
16;; any later version.
17
21f3d1d3 18;; GNU Emacs is distributed in the hope that it will be useful,
a14fb2b1
RS
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.
22
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.
27
28;;; Commentary:
1ffece3a 29
2a92dc25
RS
30;; Winner mode is a global minor mode that records the changes in the
31;; window configuration (i.e. how the frames are partitioned into
32;; windows). This way the changes can be "undone" using the function
33;; `winner-undo'. By default this one is bound to the key sequence
34;; ctrl-x left. If you change your mind (while undoing), you can
35;; press ctrl-x right (calling `winner-redo'). Even though it uses
36;; some features of Emacs20.3, winner.el should also work with
37;; Emacs19.34 and XEmacs20, provided that the installed version of
38;; custom is not obsolete.
39
40\f;;; Code:
a14fb2b1 41
01b9f355 42(eval-when-compile (require 'cl))
1ffece3a 43(require 'ring)
a14fb2b1 44
2a92dc25
RS
45(when (fboundp 'defgroup)
46 (defgroup winner nil ; Customization by Dave Love
47 "Restoring window configurations."
48 :group 'windows))
49
50(unless (fboundp 'defcustom)
51 (defmacro defcustom (symbol &optional initvalue docs &rest rest)
52 (list 'defvar symbol initvalue docs)))
53
4f850b26 54
0471bec2 55;;;###autoload
4f850b26
DL
56(defcustom winner-mode nil
57 "Toggle winner-mode.
58You must modify via \\[customize] for this variable to have an effect."
2a92dc25
RS
59 :set #'(lambda (symbol value)
60 (winner-mode (or value 0)))
4f850b26 61 :initialize 'custom-initialize-default
2a92dc25
RS
62 :type 'boolean
63 :group 'winner
4f850b26
DL
64 :require 'winner)
65
66(defcustom winner-dont-bind-my-keys nil
67 "If non-nil: Do not use `winner-mode-map' in Winner mode."
2a92dc25 68 :type 'boolean
4f850b26 69 :group 'winner)
a14fb2b1 70
2a92dc25
RS
71(defcustom winner-ring-size 200
72 "Maximum number of stored window configurations per frame."
73 :type 'integer
4f850b26 74 :group 'winner)
a14fb2b1 75
a14fb2b1 76
a14fb2b1 77
2a92dc25
RS
78
79\f;;;; Internal variables and subroutines
80
81
82;; This variable contains the window cofiguration rings.
83;; The key in this alist is the frame.
1ffece3a 84(defvar winner-ring-alist nil)
a14fb2b1 85
2a92dc25 86;; Find the right ring. If it does not exist, create one.
1ffece3a
RS
87(defsubst winner-ring (frame)
88 (or (cdr (assq frame winner-ring-alist))
89 (progn
2a92dc25
RS
90 (let ((ring (make-ring winner-ring-size)))
91 (ring-insert ring (winner-configuration frame))
92 (push (cons frame ring) winner-ring-alist)
93 ring))))
94
95(defvar winner-last-saviour nil)
96
97;; Save the current window configuration, if it has changed and return
98;; frame, else return nil. If the last change was due to the same
99;; command, save only the latest configuration.
100(defun winner-insert-if-new (frame)
101 (let ((conf (winner-configuration))
102 (ring (winner-ring frame)))
103 (cond
104 ((winner-equal conf (ring-ref ring 0)) nil)
105 (t (when (and (eq this-command (car winner-last-saviour))
106 (memq frame (cdr winner-last-saviour)))
107 (ring-remove ring 0))
108 (ring-insert ring conf)
109 frame))))
a14fb2b1 110
2a92dc25 111(defvar winner-modified-list nil) ; Which frames have changed?
a14fb2b1 112
2a92dc25 113;; This function is called when the window configuration changes.
1ffece3a 114(defun winner-change-fun ()
2a92dc25
RS
115 (unless (memq (selected-frame) winner-modified-list)
116 (push (selected-frame) winner-modified-list)))
a14fb2b1 117
2a92dc25 118;; For Emacs20
1ffece3a 119(defun winner-save-new-configurations ()
2a92dc25
RS
120 (setq winner-last-saviour
121 (cons this-command
122 (mapcar 'winner-insert-if-new winner-modified-list)))
123 (setq winner-modified-list nil))
124
125;; For compatibility with other emacsen.
126(defun winner-save-unconditionally ()
127 (setq winner-last-saviour
128 (cons this-command
129 (list (winner-insert-if-new (selected-frame))))))
130
131;; Arrgh. This is storing the same information twice.
132(defun winner-configuration (&optional frame)
133 (if frame (letf (((selected-frame) frame)) (winner-configuration))
134 (cons (current-window-configuration)
135 (loop for w being the windows
136 collect (window-buffer w)))))
137
138\f
139;; The same as `set-window-configuration',
140;; but doesn't touch the minibuffer.
141(defun winner-set-conf (winconf)
142 (let ((min-sel (window-minibuffer-p (selected-window)))
143 (minibuf (window-buffer (minibuffer-window)))
144 (minipoint (letf ((selected-window) (minibuffer-window))
145 (point)))
146 win)
147 (set-window-configuration winconf)
148 (setq win (selected-window))
149 (select-window (minibuffer-window))
150 (set-window-buffer (minibuffer-window) minibuf)
151 (goto-char minipoint)
152 (cond
153 (min-sel)
154 ((window-minibuffer-p win)
155 (other-window 1))
156 (t (select-window win)))))
157
158(defun winner-win-data () ; Information about the windows
159 (loop for win being the windows
160 unless (window-minibuffer-p win)
161 collect (list (window-buffer win)
162 (window-width win)
163 (window-height win))))
164
165;; Make sure point doesn't end up in the minibuffer and
166;; delete windows displaying dead buffers. Return nil
167;; if and only if all the windows should have been deleted.
1ffece3a 168(defun winner-set (conf)
2a92dc25
RS
169 (let ((origpoints
170 (save-excursion
171 (loop for buf in (cdr conf)
172 collect (if (buffer-name buf)
173 (progn (set-buffer buf) (point))
174 nil)))))
175 (winner-set-conf (car conf))
176 (let* ((win (selected-window))
177 (xwins (loop for window being the windows
178 for pos in origpoints
179 unless (window-minibuffer-p window)
180 if pos do (progn (select-window window)
181 (goto-char pos))
182 else collect window)))
183 (select-window win)
184 ;; Return t if possible configuration
185 (cond
186 ((null xwins) t)
187 ((progn (mapcar 'delete-window (cdr xwins))
188 (one-window-p t))
189 nil) ; No existing buffers
190 (t (delete-window (car xwins)))))))
191
192
193
194
195\f;;;; Winner mode (a minor mode)
a14fb2b1 196
4f850b26
DL
197(defcustom winner-mode-hook nil
198 "Functions to run whenever Winner mode is turned on."
199 :type 'hook
78900bc3 200 :group 'winner)
a14fb2b1 201
78900bc3 202(defcustom winner-mode-leave-hook nil
4f850b26
DL
203 "Functions to run whenever Winner mode is turned off."
204 :type 'hook
78900bc3 205 :group 'winner)
1ffece3a 206
a14fb2b1 207(defvar winner-mode-map nil "Keymap for Winner mode.")
21f3d1d3 208
2a92dc25
RS
209;; Is `window-configuration-change-hook' working?
210(defun winner-hook-installed-p ()
211 (save-window-excursion
212 (let ((winner-var nil)
213 (window-configuration-change-hook
214 '((lambda () (setq winner-var t)))))
215 (split-window)
216 winner-var)))
217
4f850b26 218;;;###autoload
a14fb2b1
RS
219(defun winner-mode (&optional arg)
220 "Toggle Winner mode.
221With arg, turn Winner mode on if and only if arg is positive."
222 (interactive "P")
223 (let ((on-p (if arg (> (prefix-numeric-value arg) 0)
224 (not winner-mode))))
225 (cond
1ffece3a
RS
226 ;; Turn mode on
227 (on-p
228 (setq winner-mode t)
2a92dc25
RS
229 (cond
230 ((winner-hook-installed-p)
231 (add-hook 'window-configuration-change-hook 'winner-change-fun)
232 (add-hook 'post-command-hook 'winner-save-new-configurations))
233 (t (add-hook 'post-command-hook 'winner-save-unconditionally)))
1ffece3a
RS
234 (setq winner-modified-list (frame-list))
235 (winner-save-new-configurations)
236 (run-hooks 'winner-mode-hook))
237 ;; Turn mode off
238 (winner-mode
239 (setq winner-mode nil)
2a92dc25
RS
240 (remove-hook 'window-configuration-change-hook 'winner-change-fun)
241 (remove-hook 'post-command-hook 'winner-save-new-configurations)
242 (remove-hook 'post-command-hook 'winner-save-unconditionally)
1ffece3a 243 (run-hooks 'winner-mode-leave-hook)))
a14fb2b1
RS
244 (force-mode-line-update)))
245
2a92dc25 246\f;; Inspired by undo (simple.el)
1ffece3a
RS
247(defun winner-undo (arg)
248 "Switch back to an earlier window configuration saved by Winner mode.
4f850b26
DL
249In other words, \"undo\" changes in window configuration.
250With prefix arg, undo that many levels."
1ffece3a
RS
251 (interactive "p")
252 (cond
253 ((not winner-mode) (error "Winner mode is turned off"))
2a92dc25
RS
254 ;; ((eq (selected-window) (minibuffer-window))
255 ;; (error "No winner undo from minibuffer."))
1ffece3a 256 (t (setq this-command t)
2a92dc25 257 (unless (eq last-command 'winner-undo)
1ffece3a 258 (setq winner-pending-undo-ring (winner-ring (selected-frame)))
2a92dc25
RS
259 (setq winner-undo-counter 0)
260 (setq winner-undone-data (list (winner-win-data))))
261 (incf winner-undo-counter arg)
262 (winner-undo-this)
263 (unless (window-minibuffer-p (selected-window))
264 (message "Winner undo (%d)" winner-undo-counter))
1ffece3a
RS
265 (setq this-command 'winner-undo))))
266
2a92dc25
RS
267(defvar winner-pending-undo-ring nil) ; The ring currently used by
268 ; undo.
269(defvar winner-undo-counter nil)
270(defvar winner-undone-data nil) ; There confs have been passed.
271
272(defun winner-undo-this () ; The heart of winner undo.
273 (if (>= winner-undo-counter (ring-length winner-pending-undo-ring))
274 (error "No further window configuration undo information")
275 (unless (and
276 ;; Possible configuration
277 (winner-set
278 (ring-ref winner-pending-undo-ring
279 winner-undo-counter))
280 ;; New configuration
281 (let ((data (winner-win-data)))
282 (if (member data winner-undone-data) nil
283 (push data winner-undone-data))))
284 (ring-remove winner-pending-undo-ring winner-undo-counter)
285 (winner-undo-this))))
286
287(defun winner-redo () ; If you change your mind.
1ffece3a
RS
288 "Restore a more recent window configuration saved by Winner mode."
289 (interactive)
290 (cond
291 ((eq last-command 'winner-undo)
292 (ring-remove winner-pending-undo-ring 0)
293 (winner-set
294 (ring-remove winner-pending-undo-ring 0))
295 (or (eq (selected-window) (minibuffer-window))
2a92dc25 296 (message "Winner undid undo")))
1ffece3a
RS
297 (t (error "Previous command was not a winner-undo"))))
298
2a92dc25
RS
299\f;;;; To be evaluated when the package is loaded:
300
301(if (fboundp 'compare-window-configurations)
302 (defalias 'winner-equal 'compare-window-configurations)
303 (defalias 'winner-equal 'equal))
a14fb2b1
RS
304
305(unless winner-mode-map
306 (setq winner-mode-map (make-sparse-keymap))
2a92dc25
RS
307 (define-key winner-mode-map [(control x) left] 'winner-undo)
308 (define-key winner-mode-map [(control x) right] 'winner-redo))
a14fb2b1
RS
309
310(unless (or (assq 'winner-mode minor-mode-map-alist)
311 winner-dont-bind-my-keys)
312 (push (cons 'winner-mode winner-mode-map)
313 minor-mode-map-alist))
314
315(unless (assq 'winner-mode minor-mode-alist)
316 (push '(winner-mode " Win") minor-mode-alist))
317
318(provide 'winner)
319
21f3d1d3 320;;; winner.el ends here