initial commit
[emacs-init.git] / auto-install / doremi.el
1 ;;; doremi.el --- Do Re Mi: Incremental change using arrow keys or mouse wheel.
2 ;;
3 ;; Filename: doremi.el
4 ;; Description: Incremental change using arrow keys or mouse wheel.
5 ;; Author: Drew Adams
6 ;; Maintainer: Drew Adams
7 ;; Copyright (C) 2004-2011, Drew Adams, all rights reserved.
8 ;; Created: Thu Sep 02 08:21:37 2004
9 ;; Version: 21.1
10 ;; Last-Updated: Wed Sep  7 15:53:07 2011 (-0700)
11 ;;           By: dradams
12 ;;     Update #: 1600
13 ;; URL: http://www.emacswiki.org/cgi-bin/wiki/doremi.el
14 ;; Keywords: keys, cycle, repeat, higher-order
15 ;; Compatibility: GNU Emacs: 20.x, 21.x, 22.x, 23.x
16 ;;
17 ;; Features that might be required by this library:
18 ;;
19 ;;   `mwheel', `ring', `ring+'.
20 ;;
21 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
22 ;;
23 ;;; Commentary:
24 ;;
25 ;;    Do Re Mi: Incremental change using arrow keys or mouse wheel.
26 ;;
27 ;; When you invoke Do Re Mi commands, you can then press and hold an
28 ;; up/down arrow key, or turn the mouse wheel, to run up and down the
29 ;; scale: do, re, mi,...
30 ;;
31 ;; Use the up/down arrow keys or the mouse wheel to:
32 ;;
33 ;;  - Change nearly any parameter incrementally (dynamically).
34 ;;
35 ;;  - Repeat an action.
36 ;;
37 ;;  - Cycle through a set of values, without changing anything (for
38 ;;    example, to choose an item).  In this use, think of choosing
39 ;;    from a menu.  This is similar to using a minibuffer history.
40 ;;    The input choices can take the form of any Emacs-Lisp sequence
41 ;;    (list, array, string, vector) - this sequence is converted to a
42 ;;    circular structure (ring).
43 ;;
44 ;;  - Do just about anything: call a different function for each
45 ;;    arrow.
46 ;;
47 ;; This works with numerical parameters that can be incremented and
48 ;; decremented, and it works with parameters that can take on one of a
49 ;; number of values.  In fact, it is even more general than that: you
50 ;; can use it to associate nearly any function or pair of functions
51 ;; with the arrow keys and the mouse wheel.
52 ;;
53 ;; By default, the up and down arrow keys are used, but any other keys
54 ;; may be used instead.  Mouse wheel movements are recognized for
55 ;; Emacs 20 and Emacs 21 (using library `mwheel.el').  `mouse-2'
56 ;; presses are ignored, so that they won't interfere with rotating the
57 ;; wheel.
58 ;;
59 ;; See the doc string for function `doremi' for more information.
60 ;;
61 ;; Code defining a few example commands is included here (but
62 ;; commented out), so you can see how to use this.  For more examples
63 ;; of using function `doremi', see files `doremi-frm.el' and
64 ;; `doremi-cmd.el'.
65 ;;
66 ;; This library uses library `ring+.el', which provides extensions to
67 ;; the standard library `ring.el' to let you manipulate circular
68 ;; structures.
69 ;;
70 ;;
71 ;;  Non-interactive functions defined here:
72 ;;
73 ;;    `doremi', `doremi-intersection', `doremi-limit',
74 ;;    `doremi-set-new-value', `doremi-wrap'.
75 ;;
76 ;;  User options (variables) defined here:
77 ;;
78 ;;    `doremi-boost-down-keys', `doremi-boost-scale-factor',
79 ;;    `doremi-boost-up-keys', `doremi-down-keys', `doremi-up-keys'.
80 ;;
81 ;;  Add this to your initialization file (~/.emacs or ~/_emacs):
82 ;;
83 ;;    (require 'doremi)
84 ;;
85 ;;  See also these related libraries that make use of `doremi':
86 ;;
87 ;;    `doremi-frm.el' - Incrementally adjust frame properties.
88 ;;    `doremi-cmd.el' - Other Do Re Mi commands.
89 ;;    `doremi-mac.el' - Macro to define Do Re Mi commands and
90 ;;                      automatically add them to Do Re Mi menu.
91 ;;
92 ;;  This has been tested on GNU Emacs 20, 21, and 22 on MS Windows.
93 ;;
94 ;;
95 ;; TO DO?:
96 ;;
97 ;;   - Replace `boost-*' keys by test for modifiers (as for wheel).
98 ;;   - Combine with customize.  That is, have customize buffers use
99 ;;     Do Re Mi commands to defined numeric or enumeration values.
100 ;;   - Provide buttons (menu items) in menus that act like up & down
101 ;;     arrows.
102 ;;
103 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
104 ;;
105 ;;; Change log:
106 ;;
107 ;; 2011/09/07 dadams
108 ;;     doremi: Use mouse-wheel-(up|down)-event everywhere.  Thx to Michael Heerdegen.
109 ;; 2011/01/04 dadams
110 ;;     Removed autoload cookies from non-interactive functions.
111 ;;     Added autoload cookies for defgroup, defcustom.
112 ;; 2009/11/14 dadams
113 ;;     doremi-wrap: Wrap value around, instead of just moving to the other limit.
114 ;; 2009/11/07 dadams
115 ;;     doremi: Increment can now be a list of numbers.
116 ;;             Use >= 0, not natnump, since not necessarily an integer.
117 ;; 2009/06/26 dadams
118 ;;     Added: doremi-intersection.
119 ;;     Renamed options doremi-...-key to doremi-...-keys, and made them lists.
120 ;;     Added top-level warning when load the library if obsolete vars are boundp.
121 ;;     doremi, doremi-set-new-value: Use these new list vars.
122 ;;     doremi: Handle Emacs 23 mouse-wheel modifiers using doremi-intersection (ugly hack).
123 ;; 2009/06/19 dadams
124 ;;     doremi-*-key: fixed :type, clarified doc string about value.
125 ;; 2009/06/18 dadams
126 ;;     doremi: Use single-key-description in messages.
127 ;; 2007/12/31 dadams
128 ;;     doremi: Use doremi-set-new-value instead of wrapping input functions.
129 ;;             Commented out setting redisplay-dont-pause to t.
130 ;;     Added: doremi-set-new-value.
131 ;;     oremi-boost-scale-factor: Clarified doc string.
132 ;;      property -> parameter in all doc strings (RMS).
133 ;; 2007/10/21 dadams
134 ;;     doremi: Don't let switch-frame events quit the loop.
135 ;;     Added: doremi-limit, doremi-wrap.
136 ;; 2006/01/07 dadams
137 ;;     Added :link.
138 ;; 2005/07/25 dadams
139 ;;     Added :prefix to defgroup.
140 ;; 2005/01/16 dadams
141 ;;     doremi: Bind redisplay-dont-pause to `t' for Emacs 21.
142 ;;             Use error-message-string to format error string.
143 ;; 2005/01/01 dadams
144 ;;     defvar -> defcustom.  Added (defgroup doremi).
145 ;; 2004/11/28 dadams
146 ;;     doremi: Allowed for GROWTH-FN to be a function, not just a flag.
147 ;;             Added initial value to initial prompt.
148 ;;             Corrected addition of last event to unread-command-events.
149 ;;             Improved error messages.
150 ;; 2004/09/26 dadams
151 ;;     Renamed do-re-mi* to doremi*.
152 ;;     Prefixed everything here with doremi-.
153 ;;     Changed convert-sequence-to-ring to ring-convert-sequence-to-ring.
154 ;; 2004/09/24 dadams
155 ;;     Added use of mouse wheel.  Changed key sequences to events.
156 ;;     Change prompt to add Meta info only for non-enumeration.
157 ;;     Suppress keystroke echoing.
158 ;; 2004/09/19 dadams
159 ;;     Moved doremi-buffers to doremi-cmd.el.
160 ;;     Commented-out commands test-*.
161 ;; 2004/09/11 dadams
162 ;;     Moved to doremi-frm.el: adjust-*, cycle-frame-configs,
163 ;;           grow-font, move-frame-*, and apply of push-frame-config.
164 ;; 2004/09/07 dadams
165 ;;     Added: cycle-frame-configs.
166 ;;     Apply push-frame-config to frame commands here.
167 ;; 2004/09/06 dadams
168 ;;     Added boost-*.  Added error treatment to move-frame-*.
169 ;;
170 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
171 ;;
172 ;; This program is free software; you can redistribute it and/or modify
173 ;; it under the terms of the GNU General Public License as published by
174 ;; the Free Software Foundation; either version 2, or (at your option)
175 ;; any later version.
176
177 ;; This program is distributed in the hope that it will be useful,
178 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
179 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
180 ;; GNU General Public License for more details.
181
182 ;; You should have received a copy of the GNU General Public License
183 ;; along with this program; see the file COPYING.  If not, write to
184 ;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth
185 ;; Floor, Boston, MA 02110-1301, USA.
186 ;;
187 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
188 ;;
189 ;;; Code:
190
191 (require 'ring+) ;; ring-convert-sequence-to-ring, ring-insert+extend,
192                  ;; ring-member, ring-next, ring-previous
193 (require 'mwheel nil t) ; (no error if not found): mwheel-event-button
194
195 (and (< emacs-major-version 20) (eval-when-compile (require 'cl))) ;; when, unless
196
197 ;; In Emacs 20, because `mwheel.el' is not loaded, byte-compiling
198 ;; would give the following error messages, which can be ignored:
199 ;;
200 ;; While compiling doremi:
201 ;;   ** reference to free variable mouse-wheel-down-event
202 ;;   ** reference to free variable mouse-wheel-up-event
203 ;; While compiling the end of the data:
204 ;;   ** the function mwheel-event-button is not known to be defined.
205 ;;
206 ;; This eliminates (only) the first of these two byte-compiler messages:
207 (defvar mouse-wheel-down-event)
208 (defvar mouse-wheel-up-event)
209
210 ;;;;;;;;;;;;;;;;;;;;;;;;;;
211  
212 ;;; User Options (Variables)
213
214 ;;;###autoload
215 (defgroup doremi nil
216   "Do Re Mi: Incremental change using arrow keys or mouse wheel.
217 Define commands to perform repetitive or incremental operations."
218   :prefix "doremi-" :group 'convenience
219   :link `(url-link :tag "Send Bug Report"
220           ,(concat "mailto:" "drew.adams" "@" "oracle" ".com?subject=\
221 doremi.el bug: \
222 &body=Describe bug here, starting with `emacs -q'.  \
223 Don't forget to mention your Emacs and library versions."))
224   :link '(url-link :tag "Other Libraries by Drew"
225           "http://www.emacswiki.org/cgi-bin/wiki/DrewsElispLibraries")
226   :link '(url-link :tag "Download"
227           "http://www.emacswiki.org/cgi-bin/wiki/doremi.el")
228   :link '(url-link :tag "Description"
229           "http://www.emacswiki.org/cgi-bin/wiki/Doremi")
230   :link '(emacs-commentary-link :tag "Commentary" "doremi"))
231
232 ;;;###autoload
233 (defcustom doremi-up-keys '(up)
234   "*Keys (events) associated with one direction of adjusting by `doremi'.
235 The other direction is associated with `doremi-down-keys'.
236
237 The value must be a list of keyboard events: characters or symbols.
238 For example, a list element might be `?\C-p' or `prior'."
239   :type '(repeat (restricted-sexp :match-alternatives (integerp symbolp))) :group 'doremi)
240
241 ;;;###autoload
242 (defcustom doremi-down-keys '(down)
243   "*Keys (events) associated with one direction of adjusting by `doremi'.
244 The other direction is associated with `doremi-up-keys'.
245
246 The value must be a list of keyboard events: characters or symbols.
247 For example, a list element might be `?\C-n' or `next'."
248   :type '(repeat (restricted-sexp :match-alternatives (integerp symbolp))) :group 'doremi)
249
250 ;;;###autoload
251 (defcustom doremi-boost-up-keys '(M-up)
252   "*Like `doremi-up-keys', but increments by `doremi-boost-scale-factor'.
253
254 The value must be a list of keyboard events: characters or symbols.
255 For example, a list element might be `?\M-p' or `S-prior'."
256   :type '(repeat (restricted-sexp :match-alternatives (integerp symbolp))) :group 'doremi)
257
258 ;;;###autoload
259 (defcustom doremi-boost-down-keys '(M-down)
260   "*Like `doremi-down-keys', but increments by `doremi-boost-scale-factor'.
261
262 The value must be a list of keyboard events: characters or symbols.
263 For example, a list element might be `?\M-n' or `S-next'."
264   :type '(repeat (restricted-sexp :match-alternatives (integerp symbolp))) :group 'doremi)
265
266 ;;;###autoload
267 (defcustom doremi-boost-scale-factor 10
268   "*Factor to boost incremental change of numerical properties.
269 Using `doremi-boost-up-keys' or `doremi-boost-down-keys', instead of
270 `doremi-up-keys' or `doremi-down-keys' means that the increment is
271 this many times larger.  Using a modifier key with the mouse wheel has
272 the same effect as using `doremi-boost-up-keys' or
273 `doremi-boost-down-keys'."
274   :type 'integer :group 'doremi)
275
276 ;; Originally, the key-variable options were for a single key, not a list of keys.
277 ;; Top-level warning when load the library.
278 (when (or (boundp 'doremi-up-key)   (boundp 'doremi-boost-up-key) 
279           (boundp 'doremi-down-key) (boundp 'doremi-boost-down-key))
280   (message "WARNING: Single-key options `doremi-...-key' are OBSOLETE. Use `doremi-...-keys'."))
281  
282 ;;; Non-Interactive Functions
283
284 (defun doremi (setter-fn init-val incr &optional growth-fn enum allow-new-p)
285   "Use arrow keys and/or mouse wheel to adjust some parameter.
286
287 Variables `doremi-up-keys' and `doremi-down-keys' are variables that
288 you can assign to any key sequences.  You can use these keys or the
289 mouse wheel to dynamically adjust any parameter.  The keys can be held
290 down for continual adjustment.
291
292 Example parameters include background color and font size, but a
293 parameter can be anything that is adjustable in any of these ways:
294  * A numerical parameter that can be incremented or decremented, such
295    as frame height.
296  * A parameter that can take on one of several values (an enumerated
297    choice), such as a frame background color.
298  * A parameter that has an associated function to change its value
299    incrementally.
300
301 SETTER-FN is a function that adjusts the parameter.  Two forms:
302  1) It takes a value as argument and sets the parameter to this value.
303  2) It is a \"growth\" function, which takes an increment as argument
304     and incrementally adjusts the value of the parameter.
305
306  Note that \"growth\" function really means, here, that the function
307  takes an increment as argument and does the incrementation (or
308  whatever) itself.  It is contrasted with an absolute SETTER-FN that
309  just uses a value that is incremented by `doremi'.  The difference is
310  which function does the incrementing, SETTER-FN or `doremi'.
311
312  In case #1, the new parameter value _must_ be returned by SETTER-FN.
313  In case #2, the new parameter value should be returned by SETTER-FN,
314              so that it can be echoed to the user.
315
316 INIT-VAL is the initial value for adjustment.  In the case of an
317  incremental growth function (case #2), this is ignored.
318
319 INCR is an adjustment increment.
320  For an absolute SETTER-FN (#1), this is applied to INIT-VAL before
321      calling the function.  If ENUM is non-nil, then INCR is ignored.
322  For an incremental growth function, this is passed to the function.
323
324 INCR can be a number or a list of numbers.  When it is a list of
325 numbers, each is incremented or decremented (and possibly boosted by
326 `doremi-boost-scale-factor' - see below).
327
328 If GROWTH-FN is non-nil, then SETTER-FN is an incremental growth
329   function (case #2), and it is called with INCR as its only argument.
330 If GROWTH-FN is a function, then it is used as an alternative growth
331   function.  In this case, SETTER-FN is called for `doremi-up-keys'
332   and GROWTH-FN is called for `doremi-down-keys' (mouse wheel is
333   similar).
334
335 ENUM is a choice-enumeration sequence (list, array, string...).
336   If ENUM is non-nil, then it is converted to a ring (circular
337   structure), and `doremi-up-keys' and `doremi-down-keys' set the
338   parameter to `ring-next' and `ring-previous' values, respectively.
339
340 If ENUM is non-nil, then ALLOW-NEW-P defines what happens if INIT-VAL
341 is not a member of ENUM.  If ALLOW-NEW-P is nil, then an error is
342 raised.  If non-nil, then INIT-VAL is added (to the ring created from
343 ENUM).  If the symbol `extend', then if the ring is full it is
344 extended to include INIT-VAL; other non-nil values cause the oldest
345 item in a full ring to be dropped.
346
347 For numerical parameters (not enumerated choices), there are actually
348 two levels of incrementation.  For faster incrementation, you can use
349 `doremi-boost-up-keys' and `doremi-boost-down-keys', or you can use
350 any keyboard modifier(s) (Shift, Meta, Control...) with the mouse
351 wheel.  Incrementation is then `doremi-boost-scale-factor' times
352 faster.
353
354 For examples of using `doremi', see the source code of libraries
355 `doremi.el', `doremi-frm.el', and `doremi-cmd.el'."
356   (setq incr  (or incr 1))
357   (let ((new-incr  incr))
358     ;; $$$$ (redisplay-dont-pause t))       ; To give continual feedback.
359     ;; Convert sequence of values (list, array, vector, string) to a ring.
360     (when (and enum (sequencep enum)) (setq enum  (ring-convert-sequence-to-ring enum)))
361
362     ;; Loop.  Prompt, read event, and act on arrow-key or mouse-wheel event.
363     (let ((prompt           (format "Use %s, %s, or mouse wheel to adjust value"
364                                     (single-key-description (car doremi-up-keys))
365                                     (single-key-description (car doremi-down-keys))))
366           (keys             (append doremi-up-keys doremi-down-keys
367                                     doremi-boost-up-keys doremi-boost-down-keys))
368           (echo-keystrokes  0)          ; Suppress keystroke echoing.
369           (wheel-down       (if (boundp 'mouse-wheel-up-event)
370                                 mouse-wheel-up-event
371                               'wheel-down)) ; Emacs 20.
372           (wheel-up         (if (boundp 'mouse-wheel-down-event)
373                                 mouse-wheel-down-event
374                               'wheel-up)) ; Emacs 20.
375           evnt save-prompt)
376       (unless enum (setq prompt  (concat prompt " (modifier key: faster)")))
377       (setq prompt       (format (concat prompt ".  Value now: %s") init-val)
378             save-prompt  prompt)
379       (while (progn (setq evnt    (read-event prompt)
380                           prompt  nil)
381                     (or (member evnt keys)
382                         (and (consp evnt)
383                              (member (event-basic-type (car evnt))
384                                      `(switch-frame mouse-wheel mouse-2
385                                        ,wheel-up ,wheel-down)))))
386         ;; Set up the proper increment value.
387         (cond ((member evnt doremi-up-keys) (setq new-incr  incr)) ; +
388               ((member evnt doremi-down-keys) ; -
389                (setq new-incr  (if (atom incr) (- incr) (mapcar #'- incr))))
390               ((member evnt doremi-boost-up-keys) ; ++
391                (setq new-incr
392                      (if (atom incr)
393                          (* doremi-boost-scale-factor incr)
394                        (mapcar #'(lambda (in) (* doremi-boost-scale-factor in)) incr))))
395               ((member evnt doremi-boost-down-keys) ; --
396                (setq new-incr
397                      (if (atom incr)
398                          (* doremi-boost-scale-factor (- incr))
399                        (mapcar #'(lambda (in) (* doremi-boost-scale-factor (- in))) incr))))
400
401               ;; Emacs 20 mouse wheel.
402               ((and (consp evnt) (equal 'mouse-wheel (event-basic-type (car evnt))))
403                (setq new-incr  (if (< 0 (nth 2 evnt))
404                                    incr
405                                  (if (atom incr) (- incr) (mapcar #'- incr))))
406                (when (event-modifiers evnt) ; Boost it
407                  (setq new-incr
408                        (if (atom new-incr)
409                            (* doremi-boost-scale-factor new-incr)
410                          (mapcar #'(lambda (in) (* doremi-boost-scale-factor in)) new-incr)))))
411
412               ;; Emacs 21+ mouse wheel: `mwheel.el'
413               ;; Free vars here: `mouse-wheel-down-event', `mouse-wheel-up-event'.
414               ;; Those vars and function `mwheel-event-button' are defined in `mwheel.el'.
415               ((and (consp evnt) (member (event-basic-type (car evnt))
416                                          `(,wheel-up ,wheel-down)))
417                (let ((button  (mwheel-event-button evnt)))
418                  (cond ((eq button mouse-wheel-down-event) (setq new-incr  incr))
419                        ((eq button mouse-wheel-up-event)
420                         (setq new-incr  (if (atom incr) (- incr) (mapcar #'- incr))))
421                        (t (error "`doremi', bad mwheel-scroll binding - report bug to %s%s%s%s"
422                                  "drew.adams" "@" "oracle" ".com"))))
423                (when (if (> emacs-major-version 22) ; Boost it
424                          (doremi-intersection (event-modifiers evnt)
425                                               '(shift control meta alt hyper super))
426                        (event-modifiers evnt))
427                  (setq new-incr
428                        (if (atom new-incr)
429                            (* doremi-boost-scale-factor new-incr)
430                          (mapcar #'(lambda (in) (* doremi-boost-scale-factor in)) new-incr)))))
431               (t (error "`doremi', unexpected event: `%S' - report bug to %s%s%s%s"
432                         evnt "drew.adams" "@" "oracle" ".com")))
433         (if (and (consp evnt) (memq (event-basic-type (car evnt)) '(mouse-2 switch-frame)))
434             (message save-prompt)       ; Just skip mouse-2 event (ignore while using wheel).
435
436           ;; Adjust setting and update INIT-VAL.  Four cases are treated separately:
437           ;; 1) ENUM non-nil: use `ring-next' and `ring-previous'.
438           ;; 2) SETTER-FN and GROWTH-FN are both "growth" functions: call one of them.
439           ;; 3) SETTER-FN is a "growth" function: call it on the INCR arg.
440           ;; 4) otherwise (absolute fn): increment INIT-VAL, then call SETTER-FN on it.
441           (condition-case failure
442               (setq init-val
443                     (cond (;; 1) Ring of values (enumeration list).  Use `ring-next''...
444                            (ring-p enum)
445                            ;; If INIT-VAL is not already in the ring, add it.
446                            ;; Extend the ring size if ALLOW-NEW-P is `extend'.
447                            (when (and allow-new-p (not (ring-member enum init-val)))
448                              (ring-insert+extend enum init-val
449                                                  (eq 'extend allow-new-p)))
450                            (when (< (ring-length enum) 2)
451                              (error "`doremi' - Need at least two alternatives: %s" enum))
452                            (let* ((vec     (cdr (cdr enum)))
453                                   (veclen  (length vec)))
454                              (if (and (numberp new-incr) (>= new-incr 0))
455                                  (doremi-set-new-value setter-fn (ring-next enum init-val))
456                                (doremi-set-new-value setter-fn (ring-previous enum init-val)))))
457
458                           ;; 2) Two incremental growth functions.  Call one on (new) INCR only.
459                           ((functionp growth-fn)
460                            (if (atom new-incr)
461                                (if (and (numberp new-incr) (>= new-incr 0))
462                                    (doremi-set-new-value setter-fn new-incr)
463                                  (doremi-set-new-value growth-fn (- new-incr)))
464                              (if (and (numberp (car new-incr)) (>= (car new-incr) 0))
465                                  (doremi-set-new-value setter-fn new-incr)
466                                (doremi-set-new-value growth-fn (mapcar #'- new-incr)))))
467
468                           ;; 3) Single incremental growth function.  Call it on (new) INCR only.
469                           (growth-fn (doremi-set-new-value setter-fn new-incr))
470
471                           ;; 4) Otherwise.  Increment value.  Call setter function on new value.
472                           ((and (numberp new-incr) (numberp init-val))
473                            (doremi-set-new-value setter-fn (+ init-val new-incr)))
474                           (t (error "`doremi' - Bad argument.  INIT-VAL: %s, INCR: %s"
475                                     init-val new-incr))))
476             (error (error "%s" (error-message-string failure))))))
477       (message nil)
478       (setq unread-command-events  (cons evnt unread-command-events)))))
479
480 (defun doremi-intersection (list1 list2)
481   "Set intersection of lists LIST1 and LIST2.
482 This is a non-destructive operation: it copies the data if necessary."
483   (and list1 list2 (if (equal list1 list2)
484                        list1
485                      (let ((result  ()))
486                        (unless (>= (length list1) (length list2))
487                          (setq list1  (prog1 list2 (setq list2  list1)))) ; Swap them.
488                        (while list2
489                          (when (member (car list2) list1)
490                            (setq result  (cons (car list2) result)))
491                          (setq list2  (cdr list2)))
492                        result))))
493
494 (defun doremi-set-new-value (setter-fn newval)
495   "Apply SETTER-FN to NEWVAL, and return NEWVAL.  Display progress message."
496   (prog1 (setq newval  (funcall setter-fn newval))
497     (message "Use %s, %s, or mouse wheel again.  New value: %s"
498              (single-key-description (car doremi-up-keys))
499              (single-key-description (car doremi-down-keys))
500              newval)))
501
502 (defun doremi-limit (value min max)
503   "Limit VALUE to MIN or MAX limit if either is overshot.
504 MIN or MAX = nil means no such limit.
505 Return the new, possibly limited value."
506   (cond ((and max (> value max)) max)
507         ((and min (< value min)) min)
508         (t value)))
509
510 ;; $$$$$
511 ;; (defun doremi-wrap (value min max)
512 ;;   "Wrap VALUE around if it overshoots MIN or MAX."
513 ;;   (cond ((> value max) min)
514 ;;         ((< value min) max)
515 ;;         (t value)))
516
517 (defun doremi-wrap (value min max)
518   "Wrap VALUE around if it overshoots MIN or MAX.
519 Return the new, wrapped value.
520 MAX must be greater than min."
521   (let ((new  value)
522         (del  (- max min)))
523     (while (> new max) (setq new  (- new del)))
524     (while (< new min) (setq new  (+ new del)))
525     new))
526   
527  
528 ;;; Example Commands.  Uncomment these and try them to get the idea.
529 ;;
530 ;; See also the commands in `doremi-cmd.el' and `doremi-frm.el' for
531 ;; more examples.
532
533
534 ;; Uses an enumeration list, (buffer-list).
535 ;; (defun doremi-buffers+ ()
536 ;;   "Successively cycle among existing buffers."
537 ;;   (interactive)
538 ;;   (doremi (lambda (newval) (switch-to-buffer newval 'norecord) newval)
539 ;;            (current-buffer)
540 ;;            nil                         ; ignored
541 ;;            nil                         ; ignored
542 ;;            (buffer-list)))
543
544 ;; Test command that uses an enumeration list.
545 ;; This command changes nothing.  It just echoes successive values.
546 ;; (defun test-list+ ()
547 ;;   (interactive)
548 ;;   (doremi (lambda (newval) newval) 'c 1 nil '(a b c d e f g)))
549
550 ;; Test command that uses an enumeration list.
551 ;; In this test, the init-val is not a member of the enumeration list.
552 ;; An error is raised.
553 ;; This command changes nothing.  It just echoes successive values.
554 ;; (defun test-list-prohibit-nonmember+ ()
555 ;;   (interactive)
556 ;;   (doremi (lambda (newval) newval) 'c 1 nil '(a b)))
557
558 ;; Test command that uses an enumeration list.
559 ;; In this test, the init-val is not a member of the enumeration list.
560 ;; Because of non-nil 6th arg ALLOW-NEW-P, the initial value 'c is added
561 ;; to the enumeration.
562 ;; This command changes nothing.  It just echoes successive values.
563 ;; (defun test-list-allow-nonmember+ ()
564 ;;   (interactive)
565 ;;   (doremi (lambda (newval) newval) 'c 1 nil '(a b) t))
566
567 ;; Test command that uses an enumeration list.
568 ;; In this test, the init-val is not a member of the enumeration list.
569 ;; Because 6th arg ALLOW-NEW-P is 'extend, the enumeration is enlarged
570 ;; to include the initial value 'c.
571 ;; This command changes nothing.  It just echoes successive values.
572 ;; (defun test-list-allow-nonmember+extend+ ()
573 ;;   (interactive)
574 ;;   (doremi (lambda (newval) newval) 'c 1 nil '(a b) 'extend))
575
576 ;;;;;;;;;;;;;;;;;;;;;;;;;
577
578 (provide 'doremi)
579
580 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
581 ;;; doremi.el ends here