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