(cvs-mode-map): Map follow-link to a function which
[bpt/emacs.git] / lisp / wid-edit.el
CommitLineData
bfa6c260 1;;; wid-edit.el --- Functions for creating and using widgets -*-byte-compile-dynamic: t;-*-
d543e20b 2;;
125f1820 3;; Copyright (C) 1996,97,1999,2000,01,02,2003, 2004 Free Software Foundation, Inc.
d543e20b
PA
4;;
5;; Author: Per Abrahamsen <abraham@dina.kvl.dk>
a89a9d34 6;; Maintainer: FSF
d543e20b 7;; Keywords: extensions
d543e20b 8
ef3f635f
RS
9;; This file is part of GNU Emacs.
10
11;; GNU Emacs is free software; you can redistribute it and/or modify
12;; it under the terms of the GNU General Public License as published by
13;; the Free Software Foundation; either version 2, or (at your option)
14;; any later version.
15
16;; GNU Emacs is distributed in the hope that it will be useful,
17;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19;; GNU General Public License for more details.
20
21;; You should have received a copy of the GNU General Public License
22;; along with GNU Emacs; see the file COPYING. If not, write to the
23;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
24;; Boston, MA 02111-1307, USA.
25
7fdbdbea
DL
26;;; Wishlist items (from widget.texi):
27
28;; * The `menu-choice' tag should be prettier, something like the
29;; abbreviated menus in Open Look.
30
31;; * Finish `:tab-order'.
32
33;; * Make indentation work with glyphs and proportional fonts.
34
35;; * Add commands to show overview of object and class hierarchies to
36;; the browser.
37
38;; * Find a way to disable mouse highlight for inactive widgets.
39
40;; * Find a way to make glyphs look inactive.
41
42;; * Add `key-binding' widget.
43
44;; * Add `widget' widget for editing widget specifications.
45
46;; * Find clean way to implement variable length list. See
47;; `TeX-printer-list' for an explanation.
48
49;; * `C-h' in `widget-prompt-value' should give type specific help.
50
51;; * A mailto widget. [This should work OK as a url-link if with
52;; browse-url-browser-function' set up appropriately.]
53
d543e20b
PA
54;;; Commentary:
55;;
56;; See `widget.el'.
57
58;;; Code:
59
d543e20b 60;;; Compatibility.
bfa6c260 61
4084d128
RS
62(defun widget-event-point (event)
63 "Character position of the end of event if that exists, or nil."
17030183 64 (posn-point (event-end event)))
4084d128 65
bfa6c260
DL
66(defun widget-button-release-event-p (event)
67 "Non-nil if EVENT is a mouse-button-release event object."
68 (and (eventp event)
69 (memq (event-basic-type event) '(mouse-1 mouse-2 mouse-3))
70 (or (memq 'click (event-modifiers event))
71 (memq 'drag (event-modifiers event)))))
d543e20b
PA
72
73;;; Customization.
74
75(defgroup widgets nil
76 "Customization support for the Widget Library."
77 :link '(custom-manual "(widget)Top")
62f44662 78 :link '(emacs-library-link :tag "Lisp File" "widget.el")
d543e20b
PA
79 :prefix "widget-"
80 :group 'extensions
d543e20b
PA
81 :group 'hypermedia)
82
8697863a
PA
83(defgroup widget-documentation nil
84 "Options controling the display of documentation strings."
85 :group 'widgets)
86
6aaedd12
PA
87(defgroup widget-faces nil
88 "Faces used by the widget library."
89 :group 'widgets
90 :group 'faces)
91
0b296dac 92(defvar widget-documentation-face 'widget-documentation-face
a89a9d34 93 "Face used for documentation strings in widgets.
0b296dac
RS
94This exists as a variable so it can be set locally in certain buffers.")
95
8697863a
PA
96(defface widget-documentation-face '((((class color)
97 (background dark))
98 (:foreground "lime green"))
99 (((class color)
100 (background light))
101 (:foreground "dark green"))
102 (t nil))
103 "Face used for documentation text."
104 :group 'widget-documentation
105 :group 'widget-faces)
106
2f477381 107(defvar widget-button-face 'widget-button-face
a89a9d34 108 "Face used for buttons in widgets.
2f477381
RS
109This exists as a variable so it can be set locally in certain buffers.")
110
e31c1fd5 111(defface widget-button-face '((t (:weight bold)))
d543e20b 112 "Face used for widget buttons."
6aaedd12 113 :group 'widget-faces)
d543e20b
PA
114
115(defcustom widget-mouse-face 'highlight
116 "Face used for widget buttons when the mouse is above them."
117 :type 'face
6aaedd12 118 :group 'widget-faces)
d543e20b 119
2670cf80
EZ
120;; TTY gets special definitions here and in the next defface, because
121;; the gray colors defined for other displays cause black text on a black
122;; background, at least on light-background TTYs.
123(defface widget-field-face '((((type tty))
7e784293
MB
124 :background "yellow3"
125 :foreground "black")
2670cf80 126 (((class grayscale color)
d543e20b 127 (background light))
7e784293 128 :background "gray85")
d543e20b
PA
129 (((class grayscale color)
130 (background dark))
7e784293 131 :background "dim gray")
bfa6c260 132 (t
7e784293 133 :slant italic))
d543e20b 134 "Face used for editable fields."
6aaedd12 135 :group 'widget-faces)
d543e20b 136
2670cf80 137(defface widget-single-line-field-face '((((type tty))
7e784293
MB
138 :background "green3"
139 :foreground "black")
2670cf80 140 (((class grayscale color)
c953515e 141 (background light))
7e784293 142 :background "gray85")
c953515e
PA
143 (((class grayscale color)
144 (background dark))
7e784293 145 :background "dim gray")
bfa6c260 146 (t
7e784293 147 :slant italic))
c953515e
PA
148 "Face used for editable fields spanning only a single line."
149 :group 'widget-faces)
150
33eae9c0
RS
151;;; This causes display-table to be loaded, and not usefully.
152;;;(defvar widget-single-line-display-table
153;;; (let ((table (make-display-table)))
154;;; (aset table 9 "^I")
155;;; (aset table 10 "^J")
156;;; table)
157;;; "Display table used for single-line editable fields.")
158
159;;;(when (fboundp 'set-face-display-table)
160;;; (set-face-display-table 'widget-single-line-field-face
161;;; widget-single-line-display-table))
c953515e 162
d543e20b
PA
163;;; Utility functions.
164;;
165;; These are not really widget specific.
166
d543e20b 167(defun widget-princ-to-string (object)
bfa6c260
DL
168 "Return string representation of OBJECT, any Lisp object.
169No quoting characters are used; no delimiters are printed around
170the contents of strings."
171 (with-output-to-string
172 (princ object)))
d543e20b
PA
173
174(defun widget-clear-undo ()
175 "Clear all undo information."
176 (buffer-disable-undo (current-buffer))
177 (buffer-enable-undo))
178
a3c88c59
PA
179(defcustom widget-menu-max-size 40
180 "Largest number of items allowed in a popup-menu.
181Larger menus are read through the minibuffer."
182 :group 'widgets
183 :type 'integer)
184
703c3a11
KH
185(defcustom widget-menu-max-shortcuts 40
186 "Largest number of items for which it works to choose one with a character.
187For a larger number of items, the minibuffer is used."
188 :group 'widgets
189 :type 'integer)
190
191(defcustom widget-menu-minibuffer-flag nil
0b296dac
RS
192 "*Control how to ask for a choice from the keyboard.
193Non-nil means use the minibuffer;
194nil means read a single character."
195 :group 'widgets
196 :type 'boolean)
197
d543e20b
PA
198(defun widget-choose (title items &optional event)
199 "Choose an item from a list.
200
201First argument TITLE is the name of the list.
77339a6e 202Second argument ITEMS is a list whose members are either
6d528fc5
PA
203 (NAME . VALUE), to indicate selectable items, or just strings to
204 indicate unselectable items.
d543e20b
PA
205Optional third argument EVENT is an input event.
206
207The user is asked to choose between each NAME from the items alist,
208and the VALUE of the chosen element will be returned. If EVENT is a
209mouse event, and the number of elements in items is less than
210`widget-menu-max-size', a popup menu will be used, otherwise the
211minibuffer."
212 (cond ((and (< (length items) widget-menu-max-size)
fb55ff10 213 event (display-popup-menus-p))
7fdbdbea 214 ;; Mouse click.
d543e20b
PA
215 (x-popup-menu event
216 (list title (cons "" items))))
703c3a11
KH
217 ((or widget-menu-minibuffer-flag
218 (> (length items) widget-menu-max-shortcuts))
0b296dac 219 ;; Read the choice of name from the minibuffer.
e5dfabb4 220 (setq items (widget-remove-if 'stringp items))
d543e20b
PA
221 (let ((val (completing-read (concat title ": ") items nil t)))
222 (if (stringp val)
223 (let ((try (try-completion val items)))
224 (when (stringp try)
225 (setq val try))
bfa6c260 226 (cdr (assoc val items))))))
0b296dac
RS
227 (t
228 ;; Construct a menu of the choices
229 ;; and then use it for prompting for a single character.
7fdbdbea
DL
230 (let* ((overriding-terminal-local-map (make-sparse-keymap))
231 (next-digit ?0)
232 map choice some-choice-enabled value)
0b296dac
RS
233 ;; Define SPC as a prefix char to get to this menu.
234 (define-key overriding-terminal-local-map " "
235 (setq map (make-sparse-keymap title)))
7c0a9c8f 236 (with-current-buffer (get-buffer-create " widget-choose")
d4b8422f
RS
237 (erase-buffer)
238 (insert "Available choices:\n\n")
239 (while items
240 (setq choice (car items) items (cdr items))
241 (if (consp choice)
242 (let* ((name (car choice))
243 (function (cdr choice)))
244 (insert (format "%c = %s\n" next-digit name))
cfc198e5
RS
245 (define-key map (vector next-digit) function)
246 (setq some-choice-enabled t)))
d4b8422f
RS
247 ;; Allocate digits to disabled alternatives
248 ;; so that the digit of a given alternative never varies.
249 (setq next-digit (1+ next-digit)))
250 (insert "\nC-g = Quit"))
cfc198e5
RS
251 (or some-choice-enabled
252 (error "None of the choices is currently meaningful"))
d4b8422f 253 (define-key map [?\C-g] 'keyboard-quit)
0b296dac 254 (define-key map [t] 'keyboard-quit)
4d52438e
KH
255 (define-key map [?\M-\C-v] 'scroll-other-window)
256 (define-key map [?\M--] 'negative-argument)
0b296dac 257 (setcdr map (nreverse (cdr map)))
0b296dac
RS
258 ;; Read a char with the menu, and return the result
259 ;; that corresponds to it.
d4b8422f 260 (save-window-excursion
4d52438e 261 (let ((buf (get-buffer " widget-choose")))
d970106b 262 (fit-window-to-buffer (display-buffer buf))
4d52438e
KH
263 (let ((cursor-in-echo-area t)
264 keys
265 (char 0)
266 (arg 1))
267 (while (not (or (and (>= char ?0) (< char next-digit))
268 (eq value 'keyboard-quit)))
269 ;; Unread a SPC to lead to our new menu.
b5cb36ac 270 (setq unread-command-events (cons ?\ unread-command-events))
4d52438e 271 (setq keys (read-key-sequence title))
bfa6c260
DL
272 (setq value
273 (lookup-key overriding-terminal-local-map keys t)
4d52438e
KH
274 char (string-to-char (substring keys 1)))
275 (cond ((eq value 'scroll-other-window)
bfa6c260
DL
276 (let ((minibuffer-scroll-window
277 (get-buffer-window buf)))
4d52438e 278 (if (> 0 arg)
bfa6c260
DL
279 (scroll-other-window-down
280 (window-height minibuffer-scroll-window))
4d52438e
KH
281 (scroll-other-window))
282 (setq arg 1)))
283 ((eq value 'negative-argument)
284 (setq arg -1))
285 (t
286 (setq arg 1)))))))
0b296dac
RS
287 (when (eq value 'keyboard-quit)
288 (error "Canceled"))
289 value))))
d543e20b 290
e5dfabb4
RS
291(defun widget-remove-if (predictate list)
292 (let (result (tail list))
293 (while tail
294 (or (funcall predictate (car tail))
295 (setq result (cons (car tail) result)))
296 (setq tail (cdr tail)))
297 (nreverse result)))
298
d543e20b 299;;; Widget text specifications.
77339a6e 300;;
bfa6c260 301;; These functions are for specifying text properties.
d543e20b 302
7c0a9c8f
SM
303;; We can set it to nil now that get_local_map uses get_pos_property.
304(defconst widget-field-add-space nil
8697863a 305 "Non-nil means add extra space at the end of editable text fields.
8697863a 306If you don't add the space, it will become impossible to edit a zero
bfa6c260 307size field.")
8697863a 308
bfa6c260 309(defvar widget-field-use-before-change t
da5ec617 310 "Non-nil means use `before-change-functions' to track editable fields.
bfa6c260 311This enables the use of undo, but doesn't work on Emacs 19.34 and earlier.
da5ec617 312Using before hooks also means that the :notify function can't know the
bfa6c260 313new value.")
da5ec617 314
d543e20b 315(defun widget-specify-field (widget from to)
0a3a0b56 316 "Specify editable button for WIDGET between FROM and TO."
0ce5b5d5
PA
317 ;; Terminating space is not part of the field, but necessary in
318 ;; order for local-map to work. Remove next sexp if local-map works
319 ;; at the end of the overlay.
320 (save-excursion
321 (goto-char to)
62f44662
PA
322 (cond ((null (widget-get widget :size))
323 (forward-char 1))
324 (widget-field-add-space
325 (insert-and-inherit " ")))
0ce5b5d5 326 (setq to (point)))
a850ac03
MB
327 (let ((keymap (widget-get widget :keymap))
328 (face (or (widget-get widget :value-face) 'widget-field-face))
329 (help-echo (widget-get widget :help-echo))
330 (rear-sticky
331 (or (not widget-field-add-space) (widget-get widget :size))))
233d5cde 332 (if (functionp help-echo)
77339a6e 333 (setq help-echo 'widget-mouse-help))
a850ac03
MB
334 (when (= (char-before to) ?\n)
335 ;; When the last character in the field is a newline, we want to
336 ;; give it a `field' char-property of `boundary', which helps the
337 ;; C-n/C-p act more naturally when entering/leaving the field. We
338 ;; do this by making a small secondary overlay to contain just that
339 ;; one character.
340 (let ((overlay (make-overlay (1- to) to nil t nil)))
341 (overlay-put overlay 'field 'boundary)
263a40a6
RS
342 ;; We need the real field for tabbing.
343 (overlay-put overlay 'real-field widget)
5701edda
DL
344 ;; Use `local-map' here, not `keymap', so that normal editing
345 ;; works in the field when, say, Custom uses `suppress-keymap'.
346 (overlay-put overlay 'local-map keymap)
a850ac03
MB
347 (overlay-put overlay 'face face)
348 (overlay-put overlay 'help-echo help-echo))
349 (setq to (1- to))
350 (setq rear-sticky t))
351 (let ((overlay (make-overlay from to nil nil rear-sticky)))
352 (widget-put widget :field-overlay overlay)
353 ;;(overlay-put overlay 'detachable nil)
354 (overlay-put overlay 'field widget)
5701edda 355 (overlay-put overlay 'local-map keymap)
a850ac03
MB
356 (overlay-put overlay 'face face)
357 (overlay-put overlay 'help-echo help-echo)))
e9367b9c
RS
358 (widget-specify-secret widget))
359
360(defun widget-specify-secret (field)
361 "Replace text in FIELD with value of `:secret', if non-nil."
362 (let ((secret (widget-get field :secret))
363 (size (widget-get field :size)))
364 (when secret
365 (let ((begin (widget-field-start field))
366 (end (widget-field-end field)))
bfa6c260 367 (when size
e9367b9c
RS
368 (while (and (> end begin)
369 (eq (char-after (1- end)) ?\ ))
370 (setq end (1- end))))
371 (while (< begin end)
372 (let ((old (char-after begin)))
373 (unless (eq old secret)
374 (subst-char-in-region begin (1+ begin) old secret)
375 (put-text-property begin (1+ begin) 'secret old))
376 (setq begin (1+ begin))))))))
d543e20b 377
d543e20b 378(defun widget-specify-button (widget from to)
0a3a0b56 379 "Specify button for WIDGET between FROM and TO."
233d5cde
DL
380 (let ((overlay (make-overlay from to nil t nil))
381 (help-echo (widget-get widget :help-echo)))
0a3a0b56 382 (widget-put widget :button-overlay overlay)
233d5cde
DL
383 (if (functionp help-echo)
384 (setq help-echo 'widget-mouse-help))
0a3a0b56 385 (overlay-put overlay 'button widget)
7fdbdbea 386 (overlay-put overlay 'keymap (widget-get widget :keymap))
f24485f1 387 (overlay-put overlay 'evaporate t)
bfa6c260
DL
388 ;; We want to avoid the face with image buttons.
389 (unless (widget-get widget :suppress-face)
0e726aa5
KS
390 (overlay-put overlay 'face (widget-apply widget :button-face-get)))
391 (overlay-put overlay 'pointer 'hand)
233d5cde
DL
392 (overlay-put overlay 'help-echo help-echo)))
393
394(defun widget-mouse-help (window overlay point)
395 "Help-echo callback for widgets whose :help-echo is a function."
396 (with-current-buffer (overlay-buffer overlay)
397 (let* ((widget (widget-at (overlay-start overlay)))
398 (help-echo (if widget (widget-get widget :help-echo))))
399 (if (functionp help-echo)
400 (funcall help-echo widget)
401 help-echo))))
d543e20b 402
d543e20b 403(defun widget-specify-sample (widget from to)
bfa6c260 404 "Specify sample for WIDGET between FROM and TO."
7fdbdbea
DL
405 (let ((overlay (make-overlay from to nil t nil)))
406 (overlay-put overlay 'face (widget-apply widget :sample-face-get))
f24485f1 407 (overlay-put overlay 'evaporate t)
0f648ca2
PA
408 (widget-put widget :sample-overlay overlay)))
409
d543e20b 410(defun widget-specify-doc (widget from to)
bfa6c260 411 "Specify documentation for WIDGET between FROM and TO."
4ee1cf9f
PA
412 (let ((overlay (make-overlay from to nil t nil)))
413 (overlay-put overlay 'widget-doc widget)
414 (overlay-put overlay 'face widget-documentation-face)
f24485f1 415 (overlay-put overlay 'evaporate t)
4ee1cf9f 416 (widget-put widget :doc-overlay overlay)))
d543e20b
PA
417
418(defmacro widget-specify-insert (&rest form)
bfa6c260
DL
419 "Execute FORM without inheriting any text properties."
420 `(save-restriction
421 (let ((inhibit-read-only t)
407e43be
SM
422 (inhibit-modification-hooks t))
423 (narrow-to-region (point) (point))
424 (prog1 (progn ,@form)
425 (goto-char (point-max))))))
d543e20b
PA
426
427(defface widget-inactive-face '((((class grayscale color)
428 (background dark))
429 (:foreground "light gray"))
430 (((class grayscale color)
431 (background light))
83427907 432 (:foreground "dim gray"))
bfa6c260 433 (t
e31c1fd5 434 (:slant italic)))
d543e20b 435 "Face used for inactive widgets."
6aaedd12 436 :group 'widget-faces)
d543e20b
PA
437
438(defun widget-specify-inactive (widget from to)
439 "Make WIDGET inactive for user modifications."
440 (unless (widget-get widget :inactive)
441 (let ((overlay (make-overlay from to nil t nil)))
442 (overlay-put overlay 'face 'widget-inactive-face)
6aaedd12
PA
443 ;; This is disabled, as it makes the mouse cursor change shape.
444 ;; (overlay-put overlay 'mouse-face 'widget-inactive-face)
6d528fc5
PA
445 (overlay-put overlay 'evaporate t)
446 (overlay-put overlay 'priority 100)
a89a9d34 447 (overlay-put overlay 'modification-hooks '(widget-overlay-inactive))
d543e20b
PA
448 (widget-put widget :inactive overlay))))
449
450(defun widget-overlay-inactive (&rest junk)
451 "Ignoring the arguments, signal an error."
452 (unless inhibit-read-only
a89a9d34 453 (error "The widget here is not active")))
d543e20b
PA
454
455
456(defun widget-specify-active (widget)
457 "Make WIDGET active for user modifications."
458 (let ((inactive (widget-get widget :inactive)))
459 (when inactive
460 (delete-overlay inactive)
461 (widget-put widget :inactive nil))))
462
463;;; Widget Properties.
464
465(defsubst widget-type (widget)
466 "Return the type of WIDGET, a symbol."
467 (car widget))
468
0e520006
PA
469;;;###autoload
470(defun widgetp (widget)
471 "Return non-nil iff WIDGET is a widget."
472 (if (symbolp widget)
473 (get widget 'widget-type)
474 (and (consp widget)
bbc562cc
PA
475 (symbolp (car widget))
476 (get (car widget) 'widget-type))))
0e520006 477
944c91b6
PA
478(defun widget-get-indirect (widget property)
479 "In WIDGET, get the value of PROPERTY.
bfa6c260 480If the value is a symbol, return its binding.
944c91b6
PA
481Otherwise, just return the value."
482 (let ((value (widget-get widget property)))
483 (if (symbolp value)
484 (symbol-value value)
485 value)))
486
d543e20b
PA
487(defun widget-member (widget property)
488 "Non-nil iff there is a definition in WIDGET for PROPERTY."
ff83e968 489 (cond ((plist-member (cdr widget) property)
d543e20b
PA
490 t)
491 ((car widget)
492 (widget-member (get (car widget) 'widget-type) property))
493 (t nil)))
494
d543e20b
PA
495(defun widget-value (widget)
496 "Extract the current value of WIDGET."
497 (widget-apply widget
498 :value-to-external (widget-apply widget :value-get)))
499
500(defun widget-value-set (widget value)
501 "Set the current value of WIDGET to VALUE."
502 (widget-apply widget
503 :value-set (widget-apply widget
504 :value-to-internal value)))
505
783824f5 506(defun widget-default-get (widget)
4c2f559e 507 "Extract the default external value of WIDGET."
77339a6e 508 (widget-apply widget :value-to-external
4c2f559e
PA
509 (or (widget-get widget :value)
510 (widget-apply widget :default-get))))
783824f5 511
d543e20b 512(defun widget-match-inline (widget vals)
a89a9d34 513 "In WIDGET, match the start of VALS."
d543e20b
PA
514 (cond ((widget-get widget :inline)
515 (widget-apply widget :match-inline vals))
b2aeee30 516 ((and (listp vals)
d543e20b
PA
517 (widget-apply widget :match (car vals)))
518 (cons (list (car vals)) (cdr vals)))
519 (t nil)))
520
521(defun widget-apply-action (widget &optional event)
522 "Apply :action in WIDGET in response to EVENT."
8697863a
PA
523 (if (widget-apply widget :active)
524 (widget-apply widget :action event)
525 (error "Attempt to perform action on inactive widget")))
6d528fc5 526
a3c88c59
PA
527;;; Helper functions.
528;;
529;; These are widget specific.
530
531;;;###autoload
532(defun widget-prompt-value (widget prompt &optional value unbound)
533 "Prompt for a value matching WIDGET, using PROMPT.
534The current value is assumed to be VALUE, unless UNBOUND is non-nil."
535 (unless (listp widget)
536 (setq widget (list widget)))
537 (setq prompt (format "[%s] %s" (widget-type widget) prompt))
538 (setq widget (widget-convert widget))
539 (let ((answer (widget-apply widget :prompt-value prompt value unbound)))
540 (unless (widget-apply widget :match answer)
bfa6c260 541 (error "Value does not match %S type" (car widget)))
a3c88c59
PA
542 answer))
543
544(defun widget-get-sibling (widget)
545 "Get the item WIDGET is assumed to toggle.
546This is only meaningful for radio buttons or checkboxes in a list."
7fdbdbea 547 (let* ((children (widget-get (widget-get widget :parent) :children))
a3c88c59
PA
548 child)
549 (catch 'child
550 (while children
551 (setq child (car children)
552 children (cdr children))
553 (when (eq (widget-get child :button) widget)
554 (throw 'child child)))
555 nil)))
556
6aaedd12
PA
557(defun widget-map-buttons (function &optional buffer maparg)
558 "Map FUNCTION over the buttons in BUFFER.
559FUNCTION is called with the arguments WIDGET and MAPARG.
560
561If FUNCTION returns non-nil, the walk is cancelled.
562
563The arguments MAPARG, and BUFFER default to nil and (current-buffer),
564respectively."
565 (let ((cur (point-min))
566 (widget nil)
6aaedd12 567 (overlays (if buffer
7c0a9c8f 568 (with-current-buffer buffer (overlay-lists))
6aaedd12
PA
569 (overlay-lists))))
570 (setq overlays (append (car overlays) (cdr overlays)))
571 (while (setq cur (pop overlays))
572 (setq widget (overlay-get cur 'button))
573 (if (and widget (funcall function widget maparg))
574 (setq overlays nil)))))
575
bfa6c260 576;;; Images.
d543e20b 577
bfa6c260
DL
578(defcustom widget-image-directory (file-name-as-directory
579 (expand-file-name "custom" data-directory))
580 "Where widget button images are located.
d543e20b 581If this variable is nil, widget will try to locate the directory
25ac13b5 582automatically."
d543e20b
PA
583 :group 'widgets
584 :type 'directory)
585
bfa6c260
DL
586(defcustom widget-image-enable t
587 "If non nil, use image buttons in widgets when available."
588 :version "21.1"
d543e20b
PA
589 :group 'widgets
590 :type 'boolean)
591
25ac13b5
PA
592(defcustom widget-image-conversion
593 '((xpm ".xpm") (gif ".gif") (png ".png") (jpeg ".jpg" ".jpeg")
594 (xbm ".xbm"))
595 "Conversion alist from image formats to file name suffixes."
596 :group 'widgets
597 :type '(repeat (cons :format "%v"
598 (symbol :tag "Image Format" unknown)
599 (repeat :tag "Suffixes"
600 (string :format "%v")))))
601
bfa6c260
DL
602(defun widget-image-find (image)
603 "Create a graphical button from IMAGE.
604IMAGE should either already be an image, or be a file name sans
3acab5ef 605extension (xpm, xbm, gif, jpg, or png) located in
bfa6c260
DL
606`widget-image-directory' or otherwise where `find-image' will find it."
607 (cond ((not (and image widget-image-enable (display-graphic-p)))
608 ;; We don't want or can't use images.
3acab5ef 609 nil)
bfa6c260
DL
610 ((and (consp image)
611 (eq 'image (car image)))
612 ;; Already an image spec. Use it.
3acab5ef 613 image)
25ac13b5
PA
614 ((stringp image)
615 ;; A string. Look it up in relevant directories.
bfa6c260 616 (let* ((load-path (cons widget-image-directory load-path))
bfa6c260
DL
617 specs)
618 (dolist (elt widget-image-conversion)
619 (dolist (ext (cdr elt))
620 (push (list :type (car elt) :file (concat image ext)) specs)))
621 (setq specs (nreverse specs))
622 (find-image specs)))
d543e20b 623 (t
25ac13b5 624 ;; Oh well.
3acab5ef
PA
625 nil)))
626
bfa6c260
DL
627(defvar widget-button-pressed-face 'widget-button-pressed-face
628 "Face used for pressed buttons in widgets.
629This exists as a variable so it can be set locally in certain
630buffers.")
631
632(defun widget-image-insert (widget tag image &optional down inactive)
3acab5ef 633 "In WIDGET, insert the text TAG or, if supported, IMAGE.
bfa6c260
DL
634IMAGE should either be an image or an image file name sans extension
635\(xpm, xbm, gif, jpg, or png) located in `widget-image-directory'.
636
637Optional arguments DOWN and INACTIVE are used instead of IMAGE when the
638button is pressed or inactive, respectively. These are currently ignored."
639 (if (and (display-graphic-p)
640 (setq image (widget-image-find image)))
641 (progn (widget-put widget :suppress-face t)
642 (insert-image image
643 (propertize
644 tag 'mouse-face widget-button-pressed-face)))
645 (insert tag)))
d543e20b 646
25ac13b5
PA
647;;; Buttons.
648
649(defgroup widget-button nil
650 "The look of various kinds of buttons."
651 :group 'widgets)
652
653(defcustom widget-button-prefix ""
654 "String used as prefix for buttons."
655 :type 'string
3acab5ef 656 :group 'widget-button)
25ac13b5
PA
657
658(defcustom widget-button-suffix ""
659 "String used as suffix for buttons."
660 :type 'string
3acab5ef 661 :group 'widget-button)
25ac13b5 662
d543e20b
PA
663;;; Creating Widgets.
664
665;;;###autoload
666(defun widget-create (type &rest args)
bfa6c260 667 "Create widget of TYPE.
d543e20b
PA
668The optional ARGS are additional keyword arguments."
669 (let ((widget (apply 'widget-convert type args)))
670 (widget-apply widget :create)
671 widget))
672
673(defun widget-create-child-and-convert (parent type &rest args)
674 "As part of the widget PARENT, create a child widget TYPE.
675The child is converted, using the keyword arguments ARGS."
676 (let ((widget (apply 'widget-convert type args)))
677 (widget-put widget :parent parent)
678 (unless (widget-get widget :indent)
679 (widget-put widget :indent (+ (or (widget-get parent :indent) 0)
680 (or (widget-get widget :extra-offset) 0)
681 (widget-get parent :offset))))
682 (widget-apply widget :create)
683 widget))
684
685(defun widget-create-child (parent type)
686 "Create widget of TYPE."
4c2f559e 687 (let ((widget (widget-copy type)))
d543e20b
PA
688 (widget-put widget :parent parent)
689 (unless (widget-get widget :indent)
690 (widget-put widget :indent (+ (or (widget-get parent :indent) 0)
691 (or (widget-get widget :extra-offset) 0)
692 (widget-get parent :offset))))
693 (widget-apply widget :create)
694 widget))
695
696(defun widget-create-child-value (parent type value)
697 "Create widget of TYPE with value VALUE."
4c2f559e 698 (let ((widget (widget-copy type)))
d543e20b
PA
699 (widget-put widget :value (widget-apply widget :value-to-internal value))
700 (widget-put widget :parent parent)
701 (unless (widget-get widget :indent)
702 (widget-put widget :indent (+ (or (widget-get parent :indent) 0)
703 (or (widget-get widget :extra-offset) 0)
704 (widget-get parent :offset))))
705 (widget-apply widget :create)
706 widget))
707
708;;;###autoload
709(defun widget-delete (widget)
710 "Delete WIDGET."
711 (widget-apply widget :delete))
712
4c2f559e
PA
713(defun widget-copy (widget)
714 "Make a deep copy of WIDGET."
715 (widget-apply (copy-sequence widget) :copy))
716
d543e20b 717(defun widget-convert (type &rest args)
bfa6c260 718 "Convert TYPE to a widget without inserting it in the buffer.
d543e20b
PA
719The optional ARGS are additional keyword arguments."
720 ;; Don't touch the type.
bfa6c260 721 (let* ((widget (if (symbolp type)
d543e20b 722 (list type)
ef3f635f 723 (copy-sequence type)))
d543e20b 724 (current widget)
ab6a3668 725 done
d543e20b
PA
726 (keys args))
727 ;; First set the :args keyword.
728 (while (cdr current) ;Look in the type.
ab6a3668
RS
729 (if (and (keywordp (cadr current))
730 ;; If the last element is a keyword,
731 ;; it is still the :args element,
732 ;; even though it is a keyword.
733 (cddr current))
734 (if (eq (cadr current) :args)
735 ;; If :args is explicitly specified, obey it.
736 (setq current nil)
737 ;; Some other irrelevant keyword.
738 (setq current (cdr (cdr current))))
7fdbdbea
DL
739 (setcdr current (list :args (cdr current)))
740 (setq current nil)))
ab6a3668
RS
741 (while (and args (not done)) ;Look in ARGS.
742 (cond ((eq (car args) :args)
743 ;; Handle explicit specification of :args.
744 (setq args (cadr args)
745 done t))
746 ((keywordp (car args))
747 (setq args (cddr args)))
748 (t (setq done t))))
749 (when done
750 (widget-put widget :args args))
d543e20b
PA
751 ;; Then Convert the widget.
752 (setq type widget)
753 (while type
754 (let ((convert-widget (plist-get (cdr type) :convert-widget)))
755 (if convert-widget
756 (setq widget (funcall convert-widget widget))))
757 (setq type (get (car type) 'widget-type)))
758 ;; Finally set the keyword args.
bfa6c260 759 (while keys
d543e20b 760 (let ((next (nth 0 keys)))
bfa6c260
DL
761 (if (keywordp next)
762 (progn
d543e20b
PA
763 (widget-put widget next (nth 1 keys))
764 (setq keys (nthcdr 2 keys)))
765 (setq keys nil))))
766 ;; Convert the :value to internal format.
767 (if (widget-member widget :value)
7fdbdbea
DL
768 (widget-put widget
769 :value (widget-apply widget
770 :value-to-internal
771 (widget-get widget :value))))
d543e20b
PA
772 ;; Return the newly create widget.
773 widget))
774
0e520006 775;;;###autoload
d543e20b 776(defun widget-insert (&rest args)
7fdbdbea 777 "Call `insert' with ARGS even if surrounding text is read only."
d543e20b 778 (let ((inhibit-read-only t)
7fdbdbea 779 (inhibit-modification-hooks t))
4ee1cf9f 780 (apply 'insert args)))
d543e20b 781
8697863a
PA
782(defun widget-convert-text (type from to
783 &optional button-from button-to
784 &rest args)
6aaedd12 785 "Return a widget of type TYPE with endpoint FROM TO.
8697863a 786Optional ARGS are extra keyword arguments for TYPE.
6aaedd12
PA
787and TO will be used as the widgets end points. If optional arguments
788BUTTON-FROM and BUTTON-TO are given, these will be used as the widgets
8697863a
PA
789button end points.
790Optional ARGS are extra keyword arguments for TYPE."
791 (let ((widget (apply 'widget-convert type :delete 'widget-leave-text args))
6aaedd12 792 (from (copy-marker from))
2ff864e0 793 (to (copy-marker to)))
6aaedd12
PA
794 (set-marker-insertion-type from t)
795 (set-marker-insertion-type to nil)
796 (widget-put widget :from from)
797 (widget-put widget :to to)
798 (when button-from
799 (widget-specify-button widget button-from button-to))
800 widget))
801
8697863a 802(defun widget-convert-button (type from to &rest args)
6aaedd12 803 "Return a widget of type TYPE with endpoint FROM TO.
8697863a 804Optional ARGS are extra keyword arguments for TYPE.
6aaedd12
PA
805No text will be inserted to the buffer, instead the text between FROM
806and TO will be used as the widgets end points, as well as the widgets
807button end points."
8697863a
PA
808 (apply 'widget-convert-text type from to from to args))
809
810(defun widget-leave-text (widget)
811 "Remove markers and overlays from WIDGET and its children."
7fdbdbea 812 (let ((button (widget-get widget :button-overlay))
0f648ca2 813 (sample (widget-get widget :sample-overlay))
4ee1cf9f 814 (doc (widget-get widget :doc-overlay))
7fdbdbea
DL
815 (field (widget-get widget :field-overlay)))
816 (set-marker (widget-get widget :from) nil)
817 (set-marker (widget-get widget :to) nil)
208920be
PA
818 (when button
819 (delete-overlay button))
0f648ca2
PA
820 (when sample
821 (delete-overlay sample))
4ee1cf9f
PA
822 (when doc
823 (delete-overlay doc))
208920be
PA
824 (when field
825 (delete-overlay field))
7fdbdbea 826 (mapc 'widget-leave-text (widget-get widget :children))))
6aaedd12 827
d543e20b
PA
828;;; Keymap and Commands.
829
0e520006 830;;;###autoload
bfa6c260
DL
831(defvar widget-keymap
832 (let ((map (make-sparse-keymap)))
833 (define-key map "\t" 'widget-forward)
834 (define-key map [(shift tab)] 'widget-backward)
835 (define-key map [backtab] 'widget-backward)
836 (define-key map [down-mouse-2] 'widget-button-click)
837 (define-key map "\C-m" 'widget-button-press)
838 map)
d543e20b
PA
839 "Keymap containing useful binding for buffers containing widgets.
840Recommended as a parent keymap for modes using widgets.")
841
d543e20b 842(defvar widget-global-map global-map
f4b020f6 843 "Keymap used for events a widget does not handle itself.")
d543e20b
PA
844(make-variable-buffer-local 'widget-global-map)
845
bfa6c260
DL
846(defvar widget-field-keymap
847 (let ((map (copy-keymap widget-keymap)))
bfa6c260
DL
848 (define-key map "\C-k" 'widget-kill-line)
849 (define-key map "\M-\t" 'widget-complete)
850 (define-key map "\C-m" 'widget-field-activate)
8b9a0f45 851 ;; Since the widget code uses a `field' property to identify fields,
0697c662 852 ;; ordinary beginning-of-line does the right thing.
8b9a0f45 853 ;; (define-key map "\C-a" 'widget-beginning-of-line)
0697c662 854 (define-key map "\C-e" 'widget-end-of-line)
bfa6c260 855 map)
d543e20b
PA
856 "Keymap used inside an editable field.")
857
bfa6c260
DL
858(defvar widget-text-keymap
859 (let ((map (copy-keymap widget-keymap)))
8b9a0f45 860 ;; Since the widget code uses a `field' property to identify fields,
0697c662 861 ;; ordinary beginning-of-line does the right thing.
8b9a0f45 862 ;; (define-key map "\C-a" 'widget-beginning-of-line)
0697c662 863 (define-key map "\C-e" 'widget-end-of-line)
bfa6c260 864 map)
d543e20b
PA
865 "Keymap used inside a text field.")
866
d543e20b 867(defun widget-field-activate (pos &optional event)
e2896b22 868 "Invoke the editable field at point."
d543e20b 869 (interactive "@d")
a850ac03 870 (let ((field (widget-field-at pos)))
d543e20b
PA
871 (if field
872 (widget-apply-action field event)
873 (call-interactively
874 (lookup-key widget-global-map (this-command-keys))))))
875
bfa6c260 876(defface widget-button-pressed-face
a3c88c59
PA
877 '((((class color))
878 (:foreground "red"))
879 (t
e31c1fd5 880 (:weight bold :underline t)))
a3c88c59 881 "Face used for pressed buttons."
6aaedd12 882 :group 'widget-faces)
a3c88c59 883
d543e20b 884(defun widget-button-click (event)
bfa6c260 885 "Invoke the button that the mouse is pointing at."
bc3420db 886 (interactive "e")
bfa6c260 887 (if (widget-event-point event)
eaaf76b6 888 (let* ((pos (widget-event-point event))
bc3420db 889 (start (event-start event))
77339a6e 890 (button (get-char-property
bc3420db
RS
891 pos 'button (and (windowp (posn-window start))
892 (window-buffer (posn-window start))))))
eaaf76b6
GM
893 (if button
894 ;; Mouse click on a widget button. Do the following
895 ;; in a save-excursion so that the click on the button
896 ;; doesn't change point.
136b27c5 897 (save-selected-window
bc3420db 898 (select-window (posn-window (event-start event)))
5710730c 899 (save-excursion
bc3420db 900 (goto-char (posn-point (event-start event)))
5710730c
GM
901 (let* ((overlay (widget-get button :button-overlay))
902 (face (overlay-get overlay 'face))
903 (mouse-face (overlay-get overlay 'mouse-face)))
904 (unwind-protect
eaaf76b6
GM
905 ;; Read events, including mouse-movement events
906 ;; until we receive a release event. Highlight/
907 ;; unhighlight the button the mouse was initially
908 ;; on when we move over it.
5710730c
GM
909 (let ((track-mouse t))
910 (save-excursion
911 (when face ; avoid changing around image
912 (overlay-put overlay
913 'face widget-button-pressed-face)
914 (overlay-put overlay
915 'mouse-face widget-button-pressed-face))
916 (unless (widget-apply button :mouse-down-action event)
917 (while (not (widget-button-release-event-p event))
918 (setq event (read-event)
919 pos (widget-event-point event))
920 (if (and pos
921 (eq (get-char-property pos 'button)
922 button))
923 (when face
924 (overlay-put overlay
925 'face
926 widget-button-pressed-face)
927 (overlay-put overlay
928 'mouse-face
929 widget-button-pressed-face))
930 (overlay-put overlay 'face face)
931 (overlay-put overlay 'mouse-face mouse-face))))
eaaf76b6
GM
932
933 ;; When mouse is released over the button, run
934 ;; its action function.
5710730c
GM
935 (when (and pos
936 (eq (get-char-property pos 'button) button))
937 (widget-apply-action button event))))
938 (overlay-put overlay 'face face)
939 (overlay-put overlay 'mouse-face mouse-face))))
940
bc3420db
RS
941 (unless (pos-visible-in-window-p (widget-event-point event))
942 (mouse-set-point event)
943 (beginning-of-line)
944 (recenter))
945 )
eaaf76b6
GM
946
947 (let ((up t) command)
948 ;; Mouse click not on a widget button. Find the global
949 ;; command to run, and check whether it is bound to an
950 ;; up event.
951 (mouse-set-point event)
bfa6c260
DL
952 (if (memq (event-basic-type event) '(mouse-1 down-mouse-1))
953 (cond ((setq command ;down event
954 (lookup-key widget-global-map [down-mouse-1]))
955 (setq up nil))
956 ((setq command ;up event
957 (lookup-key widget-global-map [mouse-1]))))
958 (cond ((setq command ;down event
959 (lookup-key widget-global-map [down-mouse-2]))
960 (setq up nil))
961 ((setq command ;up event
962 (lookup-key widget-global-map [mouse-2])))))
963 (when up
964 ;; Don't execute up events twice.
965 (while (not (widget-button-release-event-p event))
966 (setq event (read-event))))
967 (when command
968 (call-interactively command)))))
bfa6c260 969 (message "You clicked somewhere weird.")))
a3c88c59 970
d543e20b 971(defun widget-button-press (pos &optional event)
25ac13b5 972 "Invoke button at POS."
d543e20b 973 (interactive "@d")
0a3a0b56 974 (let ((button (get-char-property pos 'button)))
d543e20b
PA
975 (if button
976 (widget-apply-action button event)
977 (let ((command (lookup-key widget-global-map (this-command-keys))))
978 (when (commandp command)
979 (call-interactively command))))))
980
8697863a
PA
981(defun widget-tabable-at (&optional pos)
982 "Return the tabable widget at POS, or nil.
983POS defaults to the value of (point)."
a850ac03 984 (let ((widget (widget-at pos)))
8697863a
PA
985 (if widget
986 (let ((order (widget-get widget :tab-order)))
987 (if order
988 (if (>= order 0)
bfa6c260
DL
989 widget)
990 widget)))))
8697863a 991
35194e3f 992(defvar widget-use-overlay-change t
4ee1cf9f 993 "If non-nil, use overlay change functions to tab around in the buffer.
35194e3f 994This is much faster, but doesn't work reliably on Emacs 19.34.")
4ee1cf9f 995
d543e20b
PA
996(defun widget-move (arg)
997 "Move point to the ARG next field or button.
998ARG may be negative to move backward."
0a3a0b56 999 (or (bobp) (> arg 0) (backward-char))
ea13a2b4 1000 (let ((wrapped 0)
0a3a0b56 1001 (number arg)
407e43be 1002 (old (widget-tabable-at)))
0a3a0b56
PA
1003 ;; Forward.
1004 (while (> arg 0)
4ee1cf9f 1005 (cond ((eobp)
ea13a2b4
AS
1006 (goto-char (point-min))
1007 (setq wrapped (1+ wrapped)))
4ee1cf9f
PA
1008 (widget-use-overlay-change
1009 (goto-char (next-overlay-change (point))))
1010 (t
1011 (forward-char 1)))
ea13a2b4 1012 (and (= wrapped 2)
0a3a0b56
PA
1013 (eq arg number)
1014 (error "No buttons or fields found"))
8697863a 1015 (let ((new (widget-tabable-at)))
0a3a0b56
PA
1016 (when new
1017 (unless (eq new old)
8697863a 1018 (setq arg (1- arg))
0a3a0b56
PA
1019 (setq old new)))))
1020 ;; Backward.
1021 (while (< arg 0)
4ee1cf9f 1022 (cond ((bobp)
ea13a2b4
AS
1023 (goto-char (point-max))
1024 (setq wrapped (1+ wrapped)))
4ee1cf9f
PA
1025 (widget-use-overlay-change
1026 (goto-char (previous-overlay-change (point))))
1027 (t
1028 (backward-char 1)))
ea13a2b4 1029 (and (= wrapped 2)
0a3a0b56
PA
1030 (eq arg number)
1031 (error "No buttons or fields found"))
8697863a 1032 (let ((new (widget-tabable-at)))
0a3a0b56
PA
1033 (when new
1034 (unless (eq new old)
8697863a
PA
1035 (setq arg (1+ arg))))))
1036 (let ((new (widget-tabable-at)))
1037 (while (eq (widget-tabable-at) new)
1038 (backward-char)))
0ce5b5d5
PA
1039 (forward-char))
1040 (widget-echo-help (point))
1041 (run-hooks 'widget-move-hook))
d543e20b
PA
1042
1043(defun widget-forward (arg)
1044 "Move point to the next field or button.
1045With optional ARG, move across that many fields."
1046 (interactive "p")
1047 (run-hooks 'widget-forward-hook)
1048 (widget-move arg))
1049
1050(defun widget-backward (arg)
1051 "Move point to the previous field or button.
1052With optional ARG, move across that many fields."
1053 (interactive "p")
1054 (run-hooks 'widget-backward-hook)
1055 (widget-move (- arg)))
1056
8b9a0f45 1057;; Since the widget code uses a `field' property to identify fields,
0697c662 1058;; ordinary beginning-of-line does the right thing.
8b9a0f45 1059(defalias 'widget-beginning-of-line 'beginning-of-line)
0697c662
MB
1060
1061(defun widget-end-of-line ()
1062 "Go to end of field or end of line, whichever is first.
1063Trailing spaces at the end of padded fields are not considered part of
1064the field."
1065 (interactive)
1066 ;; Ordinary end-of-line does the right thing, because we're inside
1067 ;; text with a `field' property.
1068 (end-of-line)
1069 (unless (eolp)
1070 ;; ... except that we want to ignore trailing spaces in fields that
1071 ;; aren't terminated by a newline, because they are used as padding,
1072 ;; and ignored when extracting the entered value of the field.
1073 (skip-chars-backward " " (field-beginning (1- (point))))))
d543e20b
PA
1074
1075(defun widget-kill-line ()
1076 "Kill to end of field or end of line, whichever is first."
1077 (interactive)
0ce5b5d5 1078 (let* ((field (widget-field-find (point)))
0ce5b5d5 1079 (end (and field (widget-field-end field))))
7fdbdbea 1080 (if (and field (> (line-beginning-position 2) end))
0ce5b5d5 1081 (kill-region (point) end)
d543e20b
PA
1082 (call-interactively 'kill-line))))
1083
0ce5b5d5
PA
1084(defcustom widget-complete-field (lookup-key global-map "\M-\t")
1085 "Default function to call for completion inside fields."
1086 :options '(ispell-complete-word complete-tag lisp-complete-symbol)
1087 :type 'function
1088 :group 'widgets)
1089
301f9235
EZ
1090(defun widget-narrow-to-field ()
1091 "Narrow to field"
1092 (interactive)
1093 (let ((field (widget-field-find (point))))
1094 (if field
1095 (narrow-to-region (line-beginning-position) (line-end-position)))))
1096
0ce5b5d5
PA
1097(defun widget-complete ()
1098 "Complete content of editable field from point.
1099When not inside a field, move to the previous button or field."
1100 (interactive)
1101 (let ((field (widget-field-find (point))))
1102 (if field
301f9235
EZ
1103 (save-restriction
1104 (widget-narrow-to-field)
1105 (widget-apply field :complete))
1106 (error "Not in an editable field"))))
0ce5b5d5 1107
d543e20b
PA
1108;;; Setting up the buffer.
1109
7c0a9c8f
SM
1110(defvar widget-field-new nil
1111 "List of all newly created editable fields in the buffer.")
d543e20b
PA
1112(make-variable-buffer-local 'widget-field-new)
1113
7c0a9c8f
SM
1114(defvar widget-field-list nil
1115 "List of all editable fields in the buffer.")
d543e20b
PA
1116(make-variable-buffer-local 'widget-field-list)
1117
a850ac03
MB
1118(defun widget-at (&optional pos)
1119 "The button or field at POS (default, point)."
1120 (or (get-char-property (or pos (point)) 'button)
1121 (widget-field-at pos)))
1122
0e520006 1123;;;###autoload
d543e20b
PA
1124(defun widget-setup ()
1125 "Setup current buffer so editing string widgets works."
1126 (let ((inhibit-read-only t)
7fdbdbea 1127 (inhibit-modification-hooks t)
d543e20b
PA
1128 field)
1129 (while widget-field-new
1130 (setq field (car widget-field-new)
1131 widget-field-new (cdr widget-field-new)
1132 widget-field-list (cons field widget-field-list))
0a3a0b56
PA
1133 (let ((from (car (widget-get field :field-overlay)))
1134 (to (cdr (widget-get field :field-overlay))))
bfa6c260 1135 (widget-specify-field field
6aaedd12 1136 (marker-position from) (marker-position to))
0a3a0b56
PA
1137 (set-marker from nil)
1138 (set-marker to nil))))
d543e20b 1139 (widget-clear-undo)
4ee1cf9f 1140 (widget-add-change))
d543e20b
PA
1141
1142(defvar widget-field-last nil)
1143;; Last field containing point.
1144(make-variable-buffer-local 'widget-field-last)
1145
1146(defvar widget-field-was nil)
1147;; The widget data before the change.
1148(make-variable-buffer-local 'widget-field-was)
1149
a850ac03
MB
1150(defun widget-field-at (pos)
1151 "Return the widget field at POS, or nil if none."
1152 (let ((field (get-char-property (or pos (point)) 'field)))
1153 (if (eq field 'boundary)
263a40a6 1154 (get-char-property (or pos (point)) 'real-field)
a850ac03
MB
1155 field)))
1156
0a3a0b56 1157(defun widget-field-buffer (widget)
80a7a1bf 1158 "Return the buffer of WIDGET's editing field."
6aaedd12 1159 (let ((overlay (widget-get widget :field-overlay)))
ec725166
MB
1160 (cond ((overlayp overlay)
1161 (overlay-buffer overlay))
1162 ((consp overlay)
1163 (marker-buffer (car overlay))))))
0a3a0b56
PA
1164
1165(defun widget-field-start (widget)
1166 "Return the start of WIDGET's editing field."
6aaedd12 1167 (let ((overlay (widget-get widget :field-overlay)))
ec725166
MB
1168 (if (overlayp overlay)
1169 (overlay-start overlay)
1170 (car overlay))))
0a3a0b56
PA
1171
1172(defun widget-field-end (widget)
1173 "Return the end of WIDGET's editing field."
6aaedd12 1174 (let ((overlay (widget-get widget :field-overlay)))
a850ac03
MB
1175 ;; Don't subtract one if local-map works at the end of the overlay,
1176 ;; or if a special `boundary' field has been added after the widget
1177 ;; field.
ec725166
MB
1178 (if (overlayp overlay)
1179 (if (and (not (eq (get-char-property (overlay-end overlay)
1180 'field
1181 (widget-field-buffer widget))
1182 'boundary))
1183 (or widget-field-add-space
1184 (null (widget-get widget :size))))
1185 (1- (overlay-end overlay))
1186 (overlay-end overlay))
1187 (cdr overlay))))
0a3a0b56 1188
d543e20b 1189(defun widget-field-find (pos)
0a3a0b56
PA
1190 "Return the field at POS.
1191Unlike (get-char-property POS 'field) this, works with empty fields too."
d543e20b
PA
1192 (let ((fields widget-field-list)
1193 field found)
1194 (while fields
1195 (setq field (car fields)
1196 fields (cdr fields))
7fdbdbea
DL
1197 (when (and (<= (widget-field-start field) pos)
1198 (<= pos (widget-field-end field)))
1199 (when found
1200 (error "Overlapping fields"))
1201 (setq found field)))
d543e20b
PA
1202 found))
1203
4ee1cf9f 1204(defun widget-before-change (from to)
944c91b6
PA
1205 ;; This is how, for example, a variable changes its state to `modified'.
1206 ;; when it is being edited.
540a8bd2
RS
1207 (unless inhibit-read-only
1208 (let ((from-field (widget-field-find from))
1209 (to-field (widget-field-find to)))
1210 (cond ((not (eq from-field to-field))
1211 (add-hook 'post-command-hook 'widget-add-change nil t)
808bcfd2
KH
1212 (signal 'text-read-only
1213 '("Change should be restricted to a single field")))
540a8bd2
RS
1214 ((null from-field)
1215 (add-hook 'post-command-hook 'widget-add-change nil t)
808bcfd2
KH
1216 (signal 'text-read-only
1217 '("Attempt to change text outside editable field")))
540a8bd2 1218 (widget-field-use-before-change
7fdbdbea 1219 (widget-apply from-field :notify from-field))))))
4ee1cf9f
PA
1220
1221(defun widget-add-change ()
4ee1cf9f 1222 (remove-hook 'post-command-hook 'widget-add-change t)
4ee1cf9f 1223 (add-hook 'before-change-functions 'widget-before-change nil t)
4ee1cf9f 1224 (add-hook 'after-change-functions 'widget-after-change nil t))
c6753d66 1225
d543e20b 1226(defun widget-after-change (from to old)
bfa6c260 1227 "Adjust field size and text properties."
7fdbdbea
DL
1228 (let ((field (widget-field-find from))
1229 (other (widget-field-find to)))
1230 (when field
1231 (unless (eq field other)
1232 (error "Change in different fields"))
1233 (let ((size (widget-get field :size)))
1234 (when size
1235 (let ((begin (widget-field-start field))
1236 (end (widget-field-end field)))
1237 (cond ((< (- end begin) size)
1238 ;; Field too small.
1239 (save-excursion
1240 (goto-char end)
1241 (insert-char ?\ (- (+ begin size) end))))
1242 ((> (- end begin) size)
1243 ;; Field too large and
1244 (if (or (< (point) (+ begin size))
1245 (> (point) end))
1246 ;; Point is outside extra space.
1247 (setq begin (+ begin size))
1248 ;; Point is within the extra space.
1249 (setq begin (point)))
1250 (save-excursion
1251 (goto-char end)
1252 (while (and (eq (preceding-char) ?\ )
1253 (> (point) begin))
1254 (delete-backward-char 1)))))))
1255 (widget-specify-secret field))
1256 (widget-apply field :notify field))))
d543e20b
PA
1257
1258;;; Widget Functions
1259;;
bfa6c260 1260;; These functions are used in the definition of multiple widgets.
d543e20b 1261
a3c88c59
PA
1262(defun widget-parent-action (widget &optional event)
1263 "Tell :parent of WIDGET to handle the :action.
1264Optional EVENT is the event that triggered the action."
1265 (widget-apply (widget-get widget :parent) :action event))
1266
d543e20b
PA
1267(defun widget-children-value-delete (widget)
1268 "Delete all :children and :buttons in WIDGET."
bfa6c260 1269 (mapc 'widget-delete (widget-get widget :children))
d543e20b 1270 (widget-put widget :children nil)
bfa6c260 1271 (mapc 'widget-delete (widget-get widget :buttons))
d543e20b
PA
1272 (widget-put widget :buttons nil))
1273
a3c88c59
PA
1274(defun widget-children-validate (widget)
1275 "All the :children must be valid."
1276 (let ((children (widget-get widget :children))
1277 child found)
1278 (while (and children (not found))
1279 (setq child (car children)
1280 children (cdr children)
1281 found (widget-apply child :validate)))
1282 found))
1283
cfa921fd
PA
1284(defun widget-child-value-get (widget)
1285 "Get the value of the first member of :children in WIDGET."
1286 (widget-value (car (widget-get widget :children))))
1287
1288(defun widget-child-value-inline (widget)
1289 "Get the inline value of the first member of :children in WIDGET."
1290 (widget-apply (car (widget-get widget :children)) :value-inline))
1291
1292(defun widget-child-validate (widget)
1293 "The result of validating the first member of :children in WIDGET."
1294 (widget-apply (car (widget-get widget :children)) :validate))
1295
1296(defun widget-type-value-create (widget)
1297 "Convert and instantiate the value of the :type attribute of WIDGET.
1298Store the newly created widget in the :children attribute.
1299
1300The value of the :type attribute should be an unconverted widget type."
1301 (let ((value (widget-get widget :value))
1302 (type (widget-get widget :type)))
0e726aa5
KS
1303 (widget-put widget :children
1304 (list (widget-create-child-value widget
cfa921fd
PA
1305 (widget-convert type)
1306 value)))))
1307
1308(defun widget-type-default-get (widget)
1309 "Get default value from the :type attribute of WIDGET.
1310
1311The value of the :type attribute should be an unconverted widget type."
1312 (widget-default-get (widget-convert (widget-get widget :type))))
1313
1314(defun widget-type-match (widget value)
1315 "Non-nil if the :type value of WIDGET matches VALUE.
1316
1317The value of the :type attribute should be an unconverted widget type."
1318 (widget-apply (widget-convert (widget-get widget :type)) :match value))
1319
4c2f559e
PA
1320(defun widget-types-copy (widget)
1321 "Copy :args as widget types in WIDGET."
1322 (widget-put widget :args (mapcar 'widget-copy (widget-get widget :args)))
1323 widget)
1324
aeba6f9a
DL
1325;; Made defsubst to speed up face editor creation.
1326(defsubst widget-types-convert-widget (widget)
d543e20b
PA
1327 "Convert :args as widget types in WIDGET."
1328 (widget-put widget :args (mapcar 'widget-convert (widget-get widget :args)))
1329 widget)
1330
a3c88c59
PA
1331(defun widget-value-convert-widget (widget)
1332 "Initialize :value from :args in WIDGET."
1333 (let ((args (widget-get widget :args)))
bfa6c260 1334 (when args
a3c88c59
PA
1335 (widget-put widget :value (car args))
1336 ;; Don't convert :value here, as this is done in `widget-convert'.
1337 ;; (widget-put widget :value (widget-apply widget
1338 ;; :value-to-internal (car args)))
1339 (widget-put widget :args nil)))
1340 widget)
1341
1342(defun widget-value-value-get (widget)
1343 "Return the :value property of WIDGET."
1344 (widget-get widget :value))
1345
d543e20b
PA
1346;;; The `default' Widget.
1347
1348(define-widget 'default nil
1349 "Basic widget other widgets are derived from."
1350 :value-to-internal (lambda (widget value) value)
1351 :value-to-external (lambda (widget value) value)
25ac13b5
PA
1352 :button-prefix 'widget-button-prefix
1353 :button-suffix 'widget-button-suffix
bfa6c260 1354 :complete 'widget-default-complete
d543e20b
PA
1355 :create 'widget-default-create
1356 :indent nil
1357 :offset 0
1358 :format-handler 'widget-default-format-handler
77339a6e
JB
1359 :button-face-get 'widget-default-button-face-get
1360 :sample-face-get 'widget-default-sample-face-get
d543e20b 1361 :delete 'widget-default-delete
4c2f559e 1362 :copy 'identity
d543e20b
PA
1363 :value-set 'widget-default-value-set
1364 :value-inline 'widget-default-value-inline
125f1820 1365 :value-delete 'ignore
783824f5 1366 :default-get 'widget-default-default-get
d543e20b 1367 :menu-tag-get 'widget-default-menu-tag-get
99f01612 1368 :validate #'ignore
d543e20b
PA
1369 :active 'widget-default-active
1370 :activate 'widget-specify-active
1371 :deactivate 'widget-default-deactivate
99f01612 1372 :mouse-down-action #'ignore
d543e20b 1373 :action 'widget-default-action
6d528fc5
PA
1374 :notify 'widget-default-notify
1375 :prompt-value 'widget-default-prompt-value)
d543e20b 1376
0ce5b5d5
PA
1377(defun widget-default-complete (widget)
1378 "Call the value of the :complete-function property of WIDGET.
1379If that does not exists, call the value of `widget-complete-field'."
7fdbdbea
DL
1380 (call-interactively (or (widget-get widget :complete-function)
1381 widget-complete-field)))
0ce5b5d5 1382
d543e20b
PA
1383(defun widget-default-create (widget)
1384 "Create WIDGET at point in the current buffer."
1385 (widget-specify-insert
1386 (let ((from (point))
d543e20b
PA
1387 button-begin button-end
1388 sample-begin sample-end
1389 doc-begin doc-end
1390 value-pos)
1391 (insert (widget-get widget :format))
1392 (goto-char from)
1393 ;; Parse escapes in format.
1394 (while (re-search-forward "%\\(.\\)" nil t)
7fdbdbea
DL
1395 (let ((escape (char-after (match-beginning 1))))
1396 (delete-backward-char 2)
d543e20b 1397 (cond ((eq escape ?%)
bfa6c260 1398 (insert ?%))
d543e20b 1399 ((eq escape ?\[)
25ac13b5 1400 (setq button-begin (point))
944c91b6 1401 (insert (widget-get-indirect widget :button-prefix)))
d543e20b 1402 ((eq escape ?\])
944c91b6 1403 (insert (widget-get-indirect widget :button-suffix))
d543e20b
PA
1404 (setq button-end (point)))
1405 ((eq escape ?\{)
1406 (setq sample-begin (point)))
1407 ((eq escape ?\})
1408 (setq sample-end (point)))
1409 ((eq escape ?n)
1410 (when (widget-get widget :indent)
bfa6c260 1411 (insert ?\n)
d543e20b
PA
1412 (insert-char ? (widget-get widget :indent))))
1413 ((eq escape ?t)
bfa6c260 1414 (let ((image (widget-get widget :tag-glyph))
25ac13b5 1415 (tag (widget-get widget :tag)))
bfa6c260
DL
1416 (cond (image
1417 (widget-image-insert widget (or tag "image") image))
25ac13b5
PA
1418 (tag
1419 (insert tag))
1420 (t
bfa6c260
DL
1421 (princ (widget-get widget :value)
1422 (current-buffer))))))
d543e20b 1423 ((eq escape ?d)
25ac13b5
PA
1424 (let ((doc (widget-get widget :doc)))
1425 (when doc
1426 (setq doc-begin (point))
1427 (insert doc)
1428 (while (eq (preceding-char) ?\n)
1429 (delete-backward-char 1))
bfa6c260 1430 (insert ?\n)
25ac13b5 1431 (setq doc-end (point)))))
d543e20b
PA
1432 ((eq escape ?v)
1433 (if (and button-begin (not button-end))
1434 (widget-apply widget :value-create)
1435 (setq value-pos (point))))
bfa6c260 1436 (t
d543e20b
PA
1437 (widget-apply widget :format-handler escape)))))
1438 ;; Specify button, sample, and doc, and insert value.
1439 (and button-begin button-end
1440 (widget-specify-button widget button-begin button-end))
1441 (and sample-begin sample-end
1442 (widget-specify-sample widget sample-begin sample-end))
1443 (and doc-begin doc-end
1444 (widget-specify-doc widget doc-begin doc-end))
1445 (when value-pos
1446 (goto-char value-pos)
1447 (widget-apply widget :value-create)))
7fdbdbea
DL
1448 (let ((from (point-min-marker))
1449 (to (point-max-marker)))
d543e20b
PA
1450 (set-marker-insertion-type from t)
1451 (set-marker-insertion-type to nil)
1452 (widget-put widget :from from)
6d528fc5
PA
1453 (widget-put widget :to to)))
1454 (widget-clear-undo))
d543e20b
PA
1455
1456(defun widget-default-format-handler (widget escape)
1457 ;; We recognize the %h escape by default.
6aaedd12 1458 (let* ((buttons (widget-get widget :buttons)))
d543e20b 1459 (cond ((eq escape ?h)
6aaedd12
PA
1460 (let* ((doc-property (widget-get widget :documentation-property))
1461 (doc-try (cond ((widget-get widget :doc))
820d4181
DL
1462 ((functionp doc-property)
1463 (funcall doc-property
1464 (widget-get widget :value)))
6aaedd12 1465 ((symbolp doc-property)
bfa6c260 1466 (documentation-property
6aaedd12 1467 (widget-get widget :value)
820d4181 1468 doc-property))))
6aaedd12
PA
1469 (doc-text (and (stringp doc-try)
1470 (> (length doc-try) 1)
8697863a
PA
1471 doc-try))
1472 (doc-indent (widget-get widget :documentation-indent)))
6aaedd12
PA
1473 (when doc-text
1474 (and (eq (preceding-char) ?\n)
1475 (widget-get widget :indent)
1476 (insert-char ? (widget-get widget :indent)))
1477 ;; The `*' in the beginning is redundant.
1478 (when (eq (aref doc-text 0) ?*)
1479 (setq doc-text (substring doc-text 1)))
1480 ;; Get rid of trailing newlines.
1481 (when (string-match "\n+\\'" doc-text)
1482 (setq doc-text (substring doc-text 0 (match-beginning 0))))
1483 (push (widget-create-child-and-convert
1484 widget 'documentation-string
8697863a
PA
1485 :indent (cond ((numberp doc-indent )
1486 doc-indent)
1487 ((null doc-indent)
1488 nil)
1489 (t 0))
6aaedd12
PA
1490 doc-text)
1491 buttons))))
bfa6c260 1492 (t
d543e20b
PA
1493 (error "Unknown escape `%c'" escape)))
1494 (widget-put widget :buttons buttons)))
1495
1496(defun widget-default-button-face-get (widget)
1497 ;; Use :button-face or widget-button-face
0b296dac
RS
1498 (or (widget-get widget :button-face)
1499 (let ((parent (widget-get widget :parent)))
1500 (if parent
1501 (widget-apply parent :button-face-get)
2f477381 1502 widget-button-face))))
d543e20b
PA
1503
1504(defun widget-default-sample-face-get (widget)
1505 ;; Use :sample-face.
1506 (widget-get widget :sample-face))
1507
1508(defun widget-default-delete (widget)
bfa6c260 1509 "Remove widget from the buffer."
d543e20b
PA
1510 (let ((from (widget-get widget :from))
1511 (to (widget-get widget :to))
9097aeb7
PA
1512 (inactive-overlay (widget-get widget :inactive))
1513 (button-overlay (widget-get widget :button-overlay))
0f648ca2 1514 (sample-overlay (widget-get widget :sample-overlay))
4ee1cf9f 1515 (doc-overlay (widget-get widget :doc-overlay))
7fdbdbea 1516 (inhibit-modification-hooks t)
0a3a0b56 1517 (inhibit-read-only t))
d543e20b 1518 (widget-apply widget :value-delete)
7d9d1ab6 1519 (widget-children-value-delete widget)
9097aeb7
PA
1520 (when inactive-overlay
1521 (delete-overlay inactive-overlay))
1522 (when button-overlay
1523 (delete-overlay button-overlay))
0f648ca2
PA
1524 (when sample-overlay
1525 (delete-overlay sample-overlay))
4ee1cf9f
PA
1526 (when doc-overlay
1527 (delete-overlay doc-overlay))
d543e20b
PA
1528 (when (< from to)
1529 ;; Kludge: this doesn't need to be true for empty formats.
1530 (delete-region from to))
1531 (set-marker from nil)
6d528fc5
PA
1532 (set-marker to nil))
1533 (widget-clear-undo))
d543e20b
PA
1534
1535(defun widget-default-value-set (widget value)
bfa6c260 1536 "Recreate widget with new value."
d0acc4ea
RS
1537 (let* ((old-pos (point))
1538 (from (copy-marker (widget-get widget :from)))
1539 (to (copy-marker (widget-get widget :to)))
1540 (offset (if (and (<= from old-pos) (<= old-pos to))
1541 (if (>= old-pos (1- to))
1542 (- old-pos to 1)
1543 (- old-pos from)))))
1544 ;;??? Bug: this ought to insert the new value before deleting the old one,
bfa6c260 1545 ;; so that markers on either side of the value automatically
d0acc4ea
RS
1546 ;; stay on the same side. -- rms.
1547 (save-excursion
1548 (goto-char (widget-get widget :from))
1549 (widget-apply widget :delete)
1550 (widget-put widget :value value)
1551 (widget-apply widget :create))
1552 (if offset
1553 (if (< offset 0)
1554 (goto-char (+ (widget-get widget :to) offset 1))
1555 (goto-char (min (+ from offset) (1- (widget-get widget :to))))))))
d543e20b
PA
1556
1557(defun widget-default-value-inline (widget)
bfa6c260 1558 "Wrap value in a list unless it is inline."
d543e20b
PA
1559 (if (widget-get widget :inline)
1560 (widget-value widget)
1561 (list (widget-value widget))))
1562
783824f5 1563(defun widget-default-default-get (widget)
bfa6c260 1564 "Get `:value'."
783824f5
RS
1565 (widget-get widget :value))
1566
d543e20b 1567(defun widget-default-menu-tag-get (widget)
bfa6c260 1568 "Use tag or value for menus."
d543e20b
PA
1569 (or (widget-get widget :menu-tag)
1570 (widget-get widget :tag)
1571 (widget-princ-to-string (widget-get widget :value))))
1572
1573(defun widget-default-active (widget)
1574 "Return t iff this widget active (user modifiable)."
0640c647
GM
1575 (or (widget-get widget :always-active)
1576 (and (not (widget-get widget :inactive))
1577 (let ((parent (widget-get widget :parent)))
77339a6e 1578 (or (null parent)
0640c647 1579 (widget-apply parent :active))))))
d543e20b
PA
1580
1581(defun widget-default-deactivate (widget)
1582 "Make WIDGET inactive for user modifications."
1583 (widget-specify-inactive widget
1584 (widget-get widget :from)
1585 (widget-get widget :to)))
1586
1587(defun widget-default-action (widget &optional event)
bfa6c260 1588 "Notify the parent when a widget changes."
d543e20b
PA
1589 (let ((parent (widget-get widget :parent)))
1590 (when parent
1591 (widget-apply parent :notify widget event))))
1592
1593(defun widget-default-notify (widget child &optional event)
bfa6c260 1594 "Pass notification to parent."
d543e20b
PA
1595 (widget-default-action widget event))
1596
6d528fc5 1597(defun widget-default-prompt-value (widget prompt value unbound)
bfa6c260
DL
1598 "Read an arbitrary value. Stolen from `set-variable'."
1599;; (let ((initial (if unbound
7fdbdbea 1600;; nil
bfa6c260
DL
1601;; It would be nice if we could do a `(cons val 1)' here.
1602;; (prin1-to-string (custom-quote value))))))
7fdbdbea 1603 (eval-minibuffer prompt))
6d528fc5 1604
d543e20b
PA
1605;;; The `item' Widget.
1606
1607(define-widget 'item 'default
1608 "Constant items for inclusion in other widgets."
a3c88c59 1609 :convert-widget 'widget-value-convert-widget
d543e20b
PA
1610 :value-create 'widget-item-value-create
1611 :value-delete 'ignore
a3c88c59 1612 :value-get 'widget-value-value-get
d543e20b
PA
1613 :match 'widget-item-match
1614 :match-inline 'widget-item-match-inline
1615 :action 'widget-item-action
1616 :format "%t\n")
1617
d543e20b 1618(defun widget-item-value-create (widget)
bfa6c260
DL
1619 "Insert the printed representation of the value."
1620 (princ (widget-get widget :value) (current-buffer)))
d543e20b
PA
1621
1622(defun widget-item-match (widget value)
1623 ;; Match if the value is the same.
1624 (equal (widget-get widget :value) value))
1625
1626(defun widget-item-match-inline (widget values)
1627 ;; Match if the value is the same.
1628 (let ((value (widget-get widget :value)))
1629 (and (listp value)
1630 (<= (length value) (length values))
e5dfabb4 1631 (let ((head (widget-sublist values 0 (length value))))
d543e20b 1632 (and (equal head value)
e5dfabb4
RS
1633 (cons head (widget-sublist values (length value))))))))
1634
1635(defun widget-sublist (list start &optional end)
1636 "Return the sublist of LIST from START to END.
1637If END is omitted, it defaults to the length of LIST."
0a3a0b56
PA
1638 (if (> start 0) (setq list (nthcdr start list)))
1639 (if end
bfa6c260 1640 (unless (<= end start)
0a3a0b56
PA
1641 (setq list (copy-sequence list))
1642 (setcdr (nthcdr (- end start 1) list) nil)
1643 list)
1644 (copy-sequence list)))
d543e20b
PA
1645
1646(defun widget-item-action (widget &optional event)
1647 ;; Just notify itself.
1648 (widget-apply widget :notify widget event))
1649
d543e20b
PA
1650;;; The `push-button' Widget.
1651
7fdbdbea
DL
1652;; (defcustom widget-push-button-gui t
1653;; "If non nil, use GUI push buttons when available."
1654;; :group 'widgets
1655;; :type 'boolean)
d543e20b
PA
1656
1657;; Cache already created GUI objects.
7fdbdbea 1658;; (defvar widget-push-button-cache nil)
d543e20b 1659
25ac13b5
PA
1660(defcustom widget-push-button-prefix "["
1661 "String used as prefix for buttons."
1662 :type 'string
1663 :group 'widget-button)
1664
1665(defcustom widget-push-button-suffix "]"
1666 "String used as suffix for buttons."
1667 :type 'string
1668 :group 'widget-button)
1669
d543e20b
PA
1670(define-widget 'push-button 'item
1671 "A pushable button."
25ac13b5
PA
1672 :button-prefix ""
1673 :button-suffix ""
d543e20b
PA
1674 :value-create 'widget-push-button-value-create
1675 :format "%[%v%]")
1676
1677(defun widget-push-button-value-create (widget)
bfa6c260 1678 "Insert text representing the `on' and `off' states."
d543e20b
PA
1679 (let* ((tag (or (widget-get widget :tag)
1680 (widget-get widget :value)))
da5ec617 1681 (tag-glyph (widget-get widget :tag-glyph))
25ac13b5 1682 (text (concat widget-push-button-prefix
7fdbdbea
DL
1683 tag widget-push-button-suffix)))
1684 (if tag-glyph
1685 (widget-image-insert widget text tag-glyph)
1686 (insert text))))
d543e20b 1687
7fdbdbea
DL
1688;; (defun widget-gui-action (widget)
1689;; "Apply :action for WIDGET."
1690;; (widget-apply-action widget (this-command-keys)))
d543e20b
PA
1691
1692;;; The `link' Widget.
1693
25ac13b5
PA
1694(defcustom widget-link-prefix "["
1695 "String used as prefix for links."
1696 :type 'string
1697 :group 'widget-button)
1698
1699(defcustom widget-link-suffix "]"
1700 "String used as suffix for links."
1701 :type 'string
1702 :group 'widget-button)
1703
d543e20b
PA
1704(define-widget 'link 'item
1705 "An embedded link."
25ac13b5
PA
1706 :button-prefix 'widget-link-prefix
1707 :button-suffix 'widget-link-suffix
d543e20b 1708 :help-echo "Follow the link."
25ac13b5 1709 :format "%[%t%]")
d543e20b
PA
1710
1711;;; The `info-link' Widget.
1712
1713(define-widget 'info-link 'link
1714 "A link to an info file."
1715 :action 'widget-info-link-action)
1716
1717(defun widget-info-link-action (widget &optional event)
1718 "Open the info node specified by WIDGET."
7c0a9c8f 1719 (info (widget-value widget)))
d543e20b
PA
1720
1721;;; The `url-link' Widget.
1722
1723(define-widget 'url-link 'link
1724 "A link to an www page."
1725 :action 'widget-url-link-action)
1726
1727(defun widget-url-link-action (widget &optional event)
1728 "Open the url specified by WIDGET."
af0f19d7 1729 (browse-url (widget-value widget)))
d543e20b 1730
a59b7025
KH
1731;;; The `function-link' Widget.
1732
1733(define-widget 'function-link 'link
1734 "A link to an Emacs function."
1735 :action 'widget-function-link-action)
1736
1737(defun widget-function-link-action (widget &optional event)
1738 "Show the function specified by WIDGET."
1739 (describe-function (widget-value widget)))
1740
1741;;; The `variable-link' Widget.
1742
1743(define-widget 'variable-link 'link
1744 "A link to an Emacs variable."
1745 :action 'widget-variable-link-action)
1746
1747(defun widget-variable-link-action (widget &optional event)
1748 "Show the variable specified by WIDGET."
1749 (describe-variable (widget-value widget)))
1750
62f44662
PA
1751;;; The `file-link' Widget.
1752
1753(define-widget 'file-link 'link
1754 "A link to a file."
1755 :action 'widget-file-link-action)
1756
1757(defun widget-file-link-action (widget &optional event)
1758 "Find the file specified by WIDGET."
1759 (find-file (widget-value widget)))
1760
1761;;; The `emacs-library-link' Widget.
1762
1763(define-widget 'emacs-library-link 'link
1764 "A link to an Emacs Lisp library file."
1765 :action 'widget-emacs-library-link-action)
1766
1767(defun widget-emacs-library-link-action (widget &optional event)
1768 "Find the Emacs Library file specified by WIDGET."
1769 (find-file (locate-library (widget-value widget))))
1770
4ee1cf9f 1771;;; The `emacs-commentary-link' Widget.
77339a6e 1772
4ee1cf9f
PA
1773(define-widget 'emacs-commentary-link 'link
1774 "A link to Commentary in an Emacs Lisp library file."
1775 :action 'widget-emacs-commentary-link-action)
77339a6e 1776
4ee1cf9f
PA
1777(defun widget-emacs-commentary-link-action (widget &optional event)
1778 "Find the Commentary section of the Emacs file specified by WIDGET."
1779 (finder-commentary (widget-value widget)))
1780
d543e20b
PA
1781;;; The `editable-field' Widget.
1782
1783(define-widget 'editable-field 'default
1784 "An editable text field."
a3c88c59 1785 :convert-widget 'widget-value-convert-widget
d543e20b
PA
1786 :keymap widget-field-keymap
1787 :format "%v"
7fdbdbea 1788 :help-echo "M-TAB: complete field; RET: enter value"
d543e20b 1789 :value ""
a3c88c59
PA
1790 :prompt-internal 'widget-field-prompt-internal
1791 :prompt-history 'widget-field-history
1792 :prompt-value 'widget-field-prompt-value
d543e20b
PA
1793 :action 'widget-field-action
1794 :validate 'widget-field-validate
1795 :valid-regexp ""
820d4181 1796 :error "Field's value doesn't match allowed forms"
d543e20b
PA
1797 :value-create 'widget-field-value-create
1798 :value-delete 'widget-field-value-delete
1799 :value-get 'widget-field-value-get
1800 :match 'widget-field-match)
1801
a3c88c59
PA
1802(defvar widget-field-history nil
1803 "History of field minibuffer edits.")
1804
1805(defun widget-field-prompt-internal (widget prompt initial history)
bfa6c260
DL
1806 "Read string for WIDGET promptinhg with PROMPT.
1807INITIAL is the initial input and HISTORY is a symbol containing
1808the earlier input."
a3c88c59
PA
1809 (read-string prompt initial history))
1810
1811(defun widget-field-prompt-value (widget prompt value unbound)
bfa6c260 1812 "Prompt for a string."
7fdbdbea
DL
1813 (widget-apply widget
1814 :value-to-external
1815 (widget-apply widget
1816 :prompt-internal prompt
1817 (unless unbound
1818 (cons (widget-apply widget
1819 :value-to-internal value)
1820 0))
1821 (widget-get widget :prompt-history))))
d543e20b 1822
0b296dac 1823(defvar widget-edit-functions nil)
211c9fe9 1824
d543e20b 1825(defun widget-field-action (widget &optional event)
bfa6c260 1826 "Move to next field."
f1231b8e 1827 (widget-forward 1)
0b296dac 1828 (run-hook-with-args 'widget-edit-functions widget))
d543e20b
PA
1829
1830(defun widget-field-validate (widget)
bfa6c260 1831 "Valid if the content matches `:valid-regexp'."
7fdbdbea
DL
1832 (unless (string-match (widget-get widget :valid-regexp)
1833 (widget-apply widget :value-get))
1834 widget))
d543e20b
PA
1835
1836(defun widget-field-value-create (widget)
bfa6c260 1837 "Create an editable text field."
d543e20b
PA
1838 (let ((size (widget-get widget :size))
1839 (value (widget-get widget :value))
0a3a0b56 1840 (from (point))
c953515e
PA
1841 ;; This is changed to a real overlay in `widget-setup'. We
1842 ;; need the end points to behave differently until
bfa6c260 1843 ;; `widget-setup' is called.
0a3a0b56
PA
1844 (overlay (cons (make-marker) (make-marker))))
1845 (widget-put widget :field-overlay overlay)
d543e20b
PA
1846 (insert value)
1847 (and size
1848 (< (length value) size)
1849 (insert-char ?\ (- size (length value))))
1850 (unless (memq widget widget-field-list)
1851 (setq widget-field-new (cons widget widget-field-new)))
0a3a0b56
PA
1852 (move-marker (cdr overlay) (point))
1853 (set-marker-insertion-type (cdr overlay) nil)
1854 (when (null size)
1855 (insert ?\n))
1856 (move-marker (car overlay) from)
1857 (set-marker-insertion-type (car overlay) t)))
d543e20b
PA
1858
1859(defun widget-field-value-delete (widget)
bfa6c260 1860 "Remove the widget from the list of active editing fields."
d543e20b 1861 (setq widget-field-list (delq widget widget-field-list))
ec725166 1862 (setq widget-field-new (delq widget widget-field-new))
d543e20b 1863 ;; These are nil if the :format string doesn't contain `%v'.
0a3a0b56 1864 (let ((overlay (widget-get widget :field-overlay)))
d8f02b91 1865 (when (overlayp overlay)
0a3a0b56 1866 (delete-overlay overlay))))
d543e20b
PA
1867
1868(defun widget-field-value-get (widget)
bfa6c260 1869 "Return current text in editing field."
0a3a0b56
PA
1870 (let ((from (widget-field-start widget))
1871 (to (widget-field-end widget))
1872 (buffer (widget-field-buffer widget))
d543e20b
PA
1873 (size (widget-get widget :size))
1874 (secret (widget-get widget :secret))
1875 (old (current-buffer)))
1876 (if (and from to)
bfa6c260 1877 (progn
0a3a0b56 1878 (set-buffer buffer)
d543e20b
PA
1879 (while (and size
1880 (not (zerop size))
1881 (> to from)
1882 (eq (char-after (1- to)) ?\ ))
1883 (setq to (1- to)))
1884 (let ((result (buffer-substring-no-properties from to)))
1885 (when secret
1886 (let ((index 0))
1887 (while (< (+ from index) to)
1888 (aset result index
0a3a0b56 1889 (get-char-property (+ from index) 'secret))
d543e20b
PA
1890 (setq index (1+ index)))))
1891 (set-buffer old)
1892 result))
1893 (widget-get widget :value))))
1894
1895(defun widget-field-match (widget value)
1896 ;; Match any string.
1897 (stringp value))
1898
1899;;; The `text' Widget.
1900
1901(define-widget 'text 'editable-field
7fce8d93
SM
1902 "A multiline text area."
1903 :keymap widget-text-keymap)
d543e20b
PA
1904
1905;;; The `menu-choice' Widget.
1906
1907(define-widget 'menu-choice 'default
1908 "A menu of options."
1909 :convert-widget 'widget-types-convert-widget
4c2f559e 1910 :copy 'widget-types-copy
d543e20b
PA
1911 :format "%[%t%]: %v"
1912 :case-fold t
1913 :tag "choice"
1914 :void '(item :format "invalid (%t)\n")
1915 :value-create 'widget-choice-value-create
cfa921fd
PA
1916 :value-get 'widget-child-value-get
1917 :value-inline 'widget-child-value-inline
783824f5 1918 :default-get 'widget-choice-default-get
a3c88c59 1919 :mouse-down-action 'widget-choice-mouse-down-action
d543e20b
PA
1920 :action 'widget-choice-action
1921 :error "Make a choice"
1922 :validate 'widget-choice-validate
1923 :match 'widget-choice-match
1924 :match-inline 'widget-choice-match-inline)
1925
1926(defun widget-choice-value-create (widget)
bfa6c260 1927 "Insert the first choice that matches the value."
d543e20b
PA
1928 (let ((value (widget-get widget :value))
1929 (args (widget-get widget :args))
4084d128 1930 (explicit (widget-get widget :explicit-choice))
d543e20b 1931 current)
7fdbdbea 1932 (if (and explicit (equal value (widget-get widget :explicit-choice-value)))
4084d128
RS
1933 (progn
1934 ;; If the user specified the choice for this value,
1935 ;; respect that choice as long as the value is the same.
1936 (widget-put widget :children (list (widget-create-child-value
1937 widget explicit value)))
1938 (widget-put widget :choice explicit))
1939 (while args
1940 (setq current (car args)
1941 args (cdr args))
1942 (when (widget-apply current :match value)
1943 (widget-put widget :children (list (widget-create-child-value
1944 widget current value)))
1945 (widget-put widget :choice current)
1946 (setq args nil
1947 current nil)))
1948 (when current
1949 (let ((void (widget-get widget :void)))
1950 (widget-put widget :children (list (widget-create-child-and-convert
1951 widget void :value value)))
1952 (widget-put widget :choice void))))))
d543e20b 1953
783824f5
RS
1954(defun widget-choice-default-get (widget)
1955 ;; Get default for the first choice.
1956 (widget-default-get (car (widget-get widget :args))))
1957
a3c88c59
PA
1958(defcustom widget-choice-toggle nil
1959 "If non-nil, a binary choice will just toggle between the values.
1960Otherwise, the user will explicitly have to choose between the values
25ac13b5 1961when he invoked the menu."
a3c88c59
PA
1962 :type 'boolean
1963 :group 'widgets)
1964
1965(defun widget-choice-mouse-down-action (widget &optional event)
1966 ;; Return non-nil if we need a menu.
1967 (let ((args (widget-get widget :args))
1968 (old (widget-get widget :choice)))
e2c00a47 1969 (cond ((not (display-popup-menus-p))
a3c88c59
PA
1970 ;; No place to pop up a menu.
1971 nil)
a3c88c59
PA
1972 ((< (length args) 2)
1973 ;; Empty or singleton list, just return the value.
1974 nil)
1975 ((> (length args) widget-menu-max-size)
1976 ;; Too long, prompt.
1977 nil)
1978 ((> (length args) 2)
1979 ;; Reasonable sized list, use menu.
1980 t)
1981 ((and widget-choice-toggle (memq old args))
1982 ;; We toggle.
1983 nil)
1984 (t
1985 ;; Ask which of the two.
1986 t))))
1987
d543e20b
PA
1988(defun widget-choice-action (widget &optional event)
1989 ;; Make a choice.
1990 (let ((args (widget-get widget :args))
1991 (old (widget-get widget :choice))
1992 (tag (widget-apply widget :menu-tag-get))
1993 (completion-ignore-case (widget-get widget :case-fold))
4084d128 1994 this-explicit
d543e20b
PA
1995 current choices)
1996 ;; Remember old value.
1997 (if (and old (not (widget-apply widget :validate)))
1998 (let* ((external (widget-value widget))
1999 (internal (widget-apply old :value-to-internal external)))
2000 (widget-put old :value internal)))
2001 ;; Find new choice.
2002 (setq current
2003 (cond ((= (length args) 0)
2004 nil)
2005 ((= (length args) 1)
2006 (nth 0 args))
a3c88c59
PA
2007 ((and widget-choice-toggle
2008 (= (length args) 2)
d543e20b
PA
2009 (memq old args))
2010 (if (eq old (nth 0 args))
2011 (nth 1 args)
2012 (nth 0 args)))
2013 (t
2014 (while args
2015 (setq current (car args)
2016 args (cdr args))
2017 (setq choices
2018 (cons (cons (widget-apply current :menu-tag-get)
2019 current)
2020 choices)))
4084d128 2021 (setq this-explicit t)
d543e20b 2022 (widget-choose tag (reverse choices) event))))
d0acc4ea 2023 (when current
4084d128
RS
2024 ;; If this was an explicit user choice,
2025 ;; record the choice, and the record the value it was made for.
2026 ;; widget-choice-value-create will respect this choice,
2027 ;; as long as the value is the same.
2028 (when this-explicit
2029 (widget-put widget :explicit-choice current)
2030 (widget-put widget :explicit-choice-value (widget-get widget :value)))
4c2f559e 2031 (widget-value-set widget (widget-default-get current))
d0acc4ea
RS
2032 (widget-setup)
2033 (widget-apply widget :notify widget event)))
d4b8422f 2034 (run-hook-with-args 'widget-edit-functions widget))
d543e20b
PA
2035
2036(defun widget-choice-validate (widget)
2037 ;; Valid if we have made a valid choice.
7fdbdbea
DL
2038 (if (eq (widget-get widget :void) (widget-get widget :choice))
2039 widget
2040 (widget-apply (car (widget-get widget :children)) :validate)))
d543e20b
PA
2041
2042(defun widget-choice-match (widget value)
2043 ;; Matches if one of the choices matches.
2044 (let ((args (widget-get widget :args))
2045 current found)
2046 (while (and args (not found))
2047 (setq current (car args)
2048 args (cdr args)
2049 found (widget-apply current :match value)))
2050 found))
2051
2052(defun widget-choice-match-inline (widget values)
2053 ;; Matches if one of the choices matches.
2054 (let ((args (widget-get widget :args))
2055 current found)
2056 (while (and args (null found))
2057 (setq current (car args)
2058 args (cdr args)
2059 found (widget-match-inline current values)))
2060 found))
2061
2062;;; The `toggle' Widget.
2063
2064(define-widget 'toggle 'item
2065 "Toggle between two states."
2066 :format "%[%v%]\n"
2067 :value-create 'widget-toggle-value-create
2068 :action 'widget-toggle-action
2069 :match (lambda (widget value) t)
2070 :on "on"
2071 :off "off")
2072
2073(defun widget-toggle-value-create (widget)
bfa6c260 2074 "Insert text representing the `on' and `off' states."
d543e20b 2075 (if (widget-value widget)
3058e436 2076 (let ((image (widget-get widget :on-glyph)))
805e9a05 2077 (and (display-graphic-p)
3058e436
MB
2078 (listp image)
2079 (not (eq (car image) 'image))
2080 (widget-put widget :on-glyph (setq image (eval image))))
805e9a05
RS
2081 (widget-image-insert widget
2082 (widget-get widget :on)
3058e436
MB
2083 image))
2084 (let ((image (widget-get widget :off-glyph)))
2085 (and (display-graphic-p)
2086 (listp image)
2087 (not (eq (car image) 'image))
2088 (widget-put widget :off-glyph (setq image (eval image))))
2089 (widget-image-insert widget (widget-get widget :off) image))))
d543e20b
PA
2090
2091(defun widget-toggle-action (widget &optional event)
2092 ;; Toggle value.
d0acc4ea
RS
2093 (widget-value-set widget (not (widget-value widget)))
2094 (widget-apply widget :notify widget event)
d4b8422f 2095 (run-hook-with-args 'widget-edit-functions widget))
6d528fc5 2096
d543e20b
PA
2097;;; The `checkbox' Widget.
2098
2099(define-widget 'checkbox 'toggle
2100 "A checkbox toggle."
25ac13b5
PA
2101 :button-suffix ""
2102 :button-prefix ""
d543e20b
PA
2103 :format "%[%v%]"
2104 :on "[X]"
35a7ac84
DL
2105 ;; We could probably do the same job as the images using single
2106 ;; space characters in a boxed face with a stretch specification to
2107 ;; make them square.
8cf30128
KS
2108 :on-glyph '(create-image "\300\300\141\143\067\076\034\030"
2109 'xbm t :width 8 :height 8
805e9a05 2110 :background "grey75" ; like default mode line
b6715b9f 2111 :foreground "black"
8cf30128 2112 :relief -2
1ed74431 2113 :ascent 'center)
805e9a05 2114 :off "[ ]"
8cf30128
KS
2115 :off-glyph '(create-image (make-string 8 0)
2116 'xbm t :width 8 :height 8
805e9a05
RS
2117 :background "grey75"
2118 :foreground "black"
8cf30128 2119 :relief -2
805e9a05 2120 :ascent 'center)
99f01612 2121 :help-echo "Toggle this item."
d543e20b
PA
2122 :action 'widget-checkbox-action)
2123
2124(defun widget-checkbox-action (widget &optional event)
2125 "Toggle checkbox, notify parent, and set active state of sibling."
2126 (widget-toggle-action widget event)
2127 (let ((sibling (widget-get-sibling widget)))
2128 (when sibling
2129 (if (widget-value widget)
2130 (widget-apply sibling :activate)
2131 (widget-apply sibling :deactivate)))))
2132
2133;;; The `checklist' Widget.
2134
2135(define-widget 'checklist 'default
2136 "A multiple choice widget."
2137 :convert-widget 'widget-types-convert-widget
4c2f559e 2138 :copy 'widget-types-copy
d543e20b
PA
2139 :format "%v"
2140 :offset 4
2141 :entry-format "%b %v"
d543e20b
PA
2142 :greedy nil
2143 :value-create 'widget-checklist-value-create
d543e20b
PA
2144 :value-get 'widget-checklist-value-get
2145 :validate 'widget-checklist-validate
2146 :match 'widget-checklist-match
2147 :match-inline 'widget-checklist-match-inline)
2148
2149(defun widget-checklist-value-create (widget)
2150 ;; Insert all values
2151 (let ((alist (widget-checklist-match-find widget (widget-get widget :value)))
2152 (args (widget-get widget :args)))
bfa6c260 2153 (while args
d543e20b
PA
2154 (widget-checklist-add-item widget (car args) (assq (car args) alist))
2155 (setq args (cdr args)))
2156 (widget-put widget :children (nreverse (widget-get widget :children)))))
2157
2158(defun widget-checklist-add-item (widget type chosen)
bfa6c260
DL
2159 "Create checklist item in WIDGET of type TYPE.
2160If the item is checked, CHOSEN is a cons whose cdr is the value."
d543e20b
PA
2161 (and (eq (preceding-char) ?\n)
2162 (widget-get widget :indent)
2163 (insert-char ? (widget-get widget :indent)))
bfa6c260 2164 (widget-specify-insert
d543e20b
PA
2165 (let* ((children (widget-get widget :children))
2166 (buttons (widget-get widget :buttons))
2167 (button-args (or (widget-get type :sibling-args)
2168 (widget-get widget :button-args)))
2169 (from (point))
2170 child button)
2171 (insert (widget-get widget :entry-format))
2172 (goto-char from)
2173 ;; Parse % escapes in format.
2174 (while (re-search-forward "%\\([bv%]\\)" nil t)
7fdbdbea
DL
2175 (let ((escape (char-after (match-beginning 1))))
2176 (delete-backward-char 2)
d543e20b 2177 (cond ((eq escape ?%)
bfa6c260 2178 (insert ?%))
d543e20b
PA
2179 ((eq escape ?b)
2180 (setq button (apply 'widget-create-child-and-convert
2181 widget 'checkbox
2182 :value (not (null chosen))
2183 button-args)))
2184 ((eq escape ?v)
2185 (setq child
2186 (cond ((not chosen)
2187 (let ((child (widget-create-child widget type)))
2188 (widget-apply child :deactivate)
2189 child))
2190 ((widget-get type :inline)
2191 (widget-create-child-value
2192 widget type (cdr chosen)))
2193 (t
2194 (widget-create-child-value
2195 widget type (car (cdr chosen)))))))
bfa6c260 2196 (t
d543e20b
PA
2197 (error "Unknown escape `%c'" escape)))))
2198 ;; Update properties.
2199 (and button child (widget-put child :button button))
2200 (and button (widget-put widget :buttons (cons button buttons)))
2201 (and child (widget-put widget :children (cons child children))))))
2202
2203(defun widget-checklist-match (widget values)
2204 ;; All values must match a type in the checklist.
2205 (and (listp values)
2206 (null (cdr (widget-checklist-match-inline widget values)))))
2207
2208(defun widget-checklist-match-inline (widget values)
2209 ;; Find the values which match a type in the checklist.
2210 (let ((greedy (widget-get widget :greedy))
ef3f635f 2211 (args (copy-sequence (widget-get widget :args)))
d543e20b
PA
2212 found rest)
2213 (while values
2214 (let ((answer (widget-checklist-match-up args values)))
bfa6c260 2215 (cond (answer
d543e20b
PA
2216 (let ((vals (widget-match-inline answer values)))
2217 (setq found (append found (car vals))
2218 values (cdr vals)
2219 args (delq answer args))))
2220 (greedy
2221 (setq rest (append rest (list (car values)))
2222 values (cdr values)))
bfa6c260 2223 (t
d543e20b
PA
2224 (setq rest (append rest values)
2225 values nil)))))
2226 (cons found rest)))
2227
2228(defun widget-checklist-match-find (widget vals)
bfa6c260
DL
2229 "Find the vals which match a type in the checklist.
2230Return an alist of (TYPE MATCH)."
d543e20b 2231 (let ((greedy (widget-get widget :greedy))
ef3f635f 2232 (args (copy-sequence (widget-get widget :args)))
d543e20b
PA
2233 found)
2234 (while vals
2235 (let ((answer (widget-checklist-match-up args vals)))
bfa6c260 2236 (cond (answer
d543e20b
PA
2237 (let ((match (widget-match-inline answer vals)))
2238 (setq found (cons (cons answer (car match)) found)
2239 vals (cdr match)
2240 args (delq answer args))))
2241 (greedy
2242 (setq vals (cdr vals)))
bfa6c260 2243 (t
d543e20b
PA
2244 (setq vals nil)))))
2245 found))
2246
2247(defun widget-checklist-match-up (args vals)
bfa6c260 2248 "Return the first type from ARGS that matches VALS."
d543e20b
PA
2249 (let (current found)
2250 (while (and args (null found))
2251 (setq current (car args)
2252 args (cdr args)
2253 found (widget-match-inline current vals)))
2254 (if found
bfa6c260 2255 current)))
d543e20b
PA
2256
2257(defun widget-checklist-value-get (widget)
2258 ;; The values of all selected items.
2259 (let ((children (widget-get widget :children))
2260 child result)
bfa6c260 2261 (while children
d543e20b
PA
2262 (setq child (car children)
2263 children (cdr children))
2264 (if (widget-value (widget-get child :button))
2265 (setq result (append result (widget-apply child :value-inline)))))
2266 result))
2267
2268(defun widget-checklist-validate (widget)
2269 ;; Ticked chilren must be valid.
2270 (let ((children (widget-get widget :children))
2271 child button found)
2272 (while (and children (not found))
2273 (setq child (car children)
2274 children (cdr children)
2275 button (widget-get child :button)
2276 found (and (widget-value button)
2277 (widget-apply child :validate))))
2278 found))
2279
2280;;; The `option' Widget
2281
2282(define-widget 'option 'checklist
2283 "An widget with an optional item."
2284 :inline t)
2285
2286;;; The `choice-item' Widget.
2287
2288(define-widget 'choice-item 'item
2289 "Button items that delegate action events to their parents."
a3c88c59 2290 :action 'widget-parent-action
d543e20b
PA
2291 :format "%[%t%] \n")
2292
d543e20b
PA
2293;;; The `radio-button' Widget.
2294
2295(define-widget 'radio-button 'toggle
2296 "A radio button for use in the `radio' widget."
2297 :notify 'widget-radio-button-notify
2298 :format "%[%v%]"
25ac13b5
PA
2299 :button-suffix ""
2300 :button-prefix ""
d543e20b
PA
2301 :on "(*)"
2302 :on-glyph "radio1"
2303 :off "( )"
2304 :off-glyph "radio0")
2305
2306(defun widget-radio-button-notify (widget child &optional event)
2307 ;; Tell daddy.
15aa7790 2308 (widget-apply (widget-get widget :parent) :action widget event))
d543e20b
PA
2309
2310;;; The `radio-button-choice' Widget.
2311
2312(define-widget 'radio-button-choice 'default
2313 "Select one of multiple options."
2314 :convert-widget 'widget-types-convert-widget
4c2f559e 2315 :copy 'widget-types-copy
d543e20b
PA
2316 :offset 4
2317 :format "%v"
2318 :entry-format "%b %v"
d543e20b 2319 :value-create 'widget-radio-value-create
d543e20b
PA
2320 :value-get 'widget-radio-value-get
2321 :value-inline 'widget-radio-value-inline
2322 :value-set 'widget-radio-value-set
2323 :error "You must push one of the buttons"
2324 :validate 'widget-radio-validate
2325 :match 'widget-choice-match
2326 :match-inline 'widget-choice-match-inline
2327 :action 'widget-radio-action)
2328
2329(defun widget-radio-value-create (widget)
2330 ;; Insert all values
2331 (let ((args (widget-get widget :args))
2332 arg)
bfa6c260 2333 (while args
d543e20b
PA
2334 (setq arg (car args)
2335 args (cdr args))
2336 (widget-radio-add-item widget arg))))
2337
2338(defun widget-radio-add-item (widget type)
2339 "Add to radio widget WIDGET a new radio button item of type TYPE."
2340 ;; (setq type (widget-convert type))
2341 (and (eq (preceding-char) ?\n)
2342 (widget-get widget :indent)
2343 (insert-char ? (widget-get widget :indent)))
bfa6c260 2344 (widget-specify-insert
d543e20b
PA
2345 (let* ((value (widget-get widget :value))
2346 (children (widget-get widget :children))
2347 (buttons (widget-get widget :buttons))
2348 (button-args (or (widget-get type :sibling-args)
2349 (widget-get widget :button-args)))
2350 (from (point))
2351 (chosen (and (null (widget-get widget :choice))
2352 (widget-apply type :match value)))
2353 child button)
2354 (insert (widget-get widget :entry-format))
2355 (goto-char from)
2356 ;; Parse % escapes in format.
2357 (while (re-search-forward "%\\([bv%]\\)" nil t)
7fdbdbea
DL
2358 (let ((escape (char-after (match-beginning 1))))
2359 (delete-backward-char 2)
d543e20b 2360 (cond ((eq escape ?%)
bfa6c260 2361 (insert ?%))
d543e20b
PA
2362 ((eq escape ?b)
2363 (setq button (apply 'widget-create-child-and-convert
bfa6c260 2364 widget 'radio-button
d543e20b
PA
2365 :value (not (null chosen))
2366 button-args)))
2367 ((eq escape ?v)
2368 (setq child (if chosen
2369 (widget-create-child-value
2370 widget type value)
2371 (widget-create-child widget type)))
bfa6c260 2372 (unless chosen
d543e20b 2373 (widget-apply child :deactivate)))
bfa6c260 2374 (t
d543e20b
PA
2375 (error "Unknown escape `%c'" escape)))))
2376 ;; Update properties.
2377 (when chosen
2378 (widget-put widget :choice type))
bfa6c260 2379 (when button
d543e20b
PA
2380 (widget-put child :button button)
2381 (widget-put widget :buttons (nconc buttons (list button))))
2382 (when child
2383 (widget-put widget :children (nconc children (list child))))
2384 child)))
2385
2386(defun widget-radio-value-get (widget)
2387 ;; Get value of the child widget.
2388 (let ((chosen (widget-radio-chosen widget)))
2389 (and chosen (widget-value chosen))))
2390
2391(defun widget-radio-chosen (widget)
2392 "Return the widget representing the chosen radio button."
2393 (let ((children (widget-get widget :children))
2394 current found)
2395 (while children
2396 (setq current (car children)
2397 children (cdr children))
7fdbdbea
DL
2398 (when (widget-apply (widget-get current :button) :value-get)
2399 (setq found current
2400 children nil)))
d543e20b
PA
2401 found))
2402
2403(defun widget-radio-value-inline (widget)
2404 ;; Get value of the child widget.
2405 (let ((children (widget-get widget :children))
2406 current found)
2407 (while children
2408 (setq current (car children)
2409 children (cdr children))
7fdbdbea
DL
2410 (when (widget-apply (widget-get current :button) :value-get)
2411 (setq found (widget-apply current :value-inline)
2412 children nil)))
d543e20b
PA
2413 found))
2414
2415(defun widget-radio-value-set (widget value)
2416 ;; We can't just delete and recreate a radio widget, since children
2417 ;; can be added after the original creation and won't be recreated
2418 ;; by `:create'.
2419 (let ((children (widget-get widget :children))
2420 current found)
2421 (while children
2422 (setq current (car children)
2423 children (cdr children))
2424 (let* ((button (widget-get current :button))
2425 (match (and (not found)
2426 (widget-apply current :match value))))
2427 (widget-value-set button match)
bfa6c260
DL
2428 (if match
2429 (progn
d543e20b
PA
2430 (widget-value-set current value)
2431 (widget-apply current :activate))
2432 (widget-apply current :deactivate))
2433 (setq found (or found match))))))
2434
2435(defun widget-radio-validate (widget)
2436 ;; Valid if we have made a valid choice.
2437 (let ((children (widget-get widget :children))
2438 current found button)
2439 (while (and children (not found))
2440 (setq current (car children)
2441 children (cdr children)
2442 button (widget-get current :button)
2443 found (widget-apply button :value-get)))
2444 (if found
2445 (widget-apply current :validate)
2446 widget)))
2447
2448(defun widget-radio-action (widget child event)
2449 ;; Check if a radio button was pressed.
2450 (let ((children (widget-get widget :children))
2451 (buttons (widget-get widget :buttons))
2452 current)
2453 (when (memq child buttons)
2454 (while children
2455 (setq current (car children)
2456 children (cdr children))
2457 (let* ((button (widget-get current :button)))
2458 (cond ((eq child button)
2459 (widget-value-set button t)
2460 (widget-apply current :activate))
2461 ((widget-value button)
2462 (widget-value-set button nil)
2463 (widget-apply current :deactivate)))))))
2464 ;; Pass notification to parent.
2465 (widget-apply widget :notify child event))
2466
2467;;; The `insert-button' Widget.
2468
2469(define-widget 'insert-button 'push-button
2ff864e0
DL
2470 "An insert button for the `editable-list' widget."
2471 :tag "INS"
2472 :help-echo "Insert a new item into the list at this position."
d543e20b
PA
2473 :action 'widget-insert-button-action)
2474
2475(defun widget-insert-button-action (widget &optional event)
2476 ;; Ask the parent to insert a new item.
bfa6c260 2477 (widget-apply (widget-get widget :parent)
d543e20b
PA
2478 :insert-before (widget-get widget :widget)))
2479
2ff864e0
DL
2480;;; The `delete-button' Widget.
2481
2482(define-widget 'delete-button 'push-button
2483 "A delete button for the `editable-list' widget."
2484 :tag "DEL"
2485 :help-echo "Delete this item from the list."
2486 :action 'widget-delete-button-action)
2487
2488(defun widget-delete-button-action (widget &optional event)
2489 ;; Ask the parent to insert a new item.
2490 (widget-apply (widget-get widget :parent)
2491 :delete-at (widget-get widget :widget)))
2492
d543e20b
PA
2493;;; The `editable-list' Widget.
2494
7fdbdbea
DL
2495;; (defcustom widget-editable-list-gui nil
2496;; "If non nil, use GUI push-buttons in editable list when available."
2497;; :type 'boolean
2498;; :group 'widgets)
d543e20b
PA
2499
2500(define-widget 'editable-list 'default
2501 "A variable list of widgets of the same type."
2502 :convert-widget 'widget-types-convert-widget
4c2f559e 2503 :copy 'widget-types-copy
d543e20b
PA
2504 :offset 12
2505 :format "%v%i\n"
2506 :format-handler 'widget-editable-list-format-handler
2ff864e0 2507 :entry-format "%i %d %v"
d543e20b 2508 :value-create 'widget-editable-list-value-create
d543e20b 2509 :value-get 'widget-editable-list-value-get
a3c88c59 2510 :validate 'widget-children-validate
d543e20b
PA
2511 :match 'widget-editable-list-match
2512 :match-inline 'widget-editable-list-match-inline
2513 :insert-before 'widget-editable-list-insert-before
2514 :delete-at 'widget-editable-list-delete-at)
2515
2516(defun widget-editable-list-format-handler (widget escape)
2517 ;; We recognize the insert button.
407e43be 2518 ;; (let ((widget-push-button-gui widget-editable-list-gui))
d543e20b
PA
2519 (cond ((eq escape ?i)
2520 (and (widget-get widget :indent)
7fdbdbea 2521 (insert-char ?\ (widget-get widget :indent)))
bfa6c260 2522 (apply 'widget-create-child-and-convert
d543e20b
PA
2523 widget 'insert-button
2524 (widget-get widget :append-button-args)))
bfa6c260 2525 (t
7fdbdbea 2526 (widget-default-format-handler widget escape)))
407e43be 2527 ;; )
7fdbdbea 2528 )
d543e20b
PA
2529
2530(defun widget-editable-list-value-create (widget)
2531 ;; Insert all values
2532 (let* ((value (widget-get widget :value))
2533 (type (nth 0 (widget-get widget :args)))
d543e20b
PA
2534 children)
2535 (widget-put widget :value-pos (copy-marker (point)))
2536 (set-marker-insertion-type (widget-get widget :value-pos) t)
2537 (while value
2538 (let ((answer (widget-match-inline type value)))
2539 (if answer
2540 (setq children (cons (widget-editable-list-entry-create
2541 widget
7fdbdbea 2542 (if (widget-get type :inline)
d543e20b
PA
2543 (car answer)
2544 (car (car answer)))
2545 t)
2546 children)
2547 value (cdr answer))
2548 (setq value nil))))
2549 (widget-put widget :children (nreverse children))))
2550
2551(defun widget-editable-list-value-get (widget)
2552 ;; Get value of the child widget.
2553 (apply 'append (mapcar (lambda (child) (widget-apply child :value-inline))
2554 (widget-get widget :children))))
2555
d543e20b
PA
2556(defun widget-editable-list-match (widget value)
2557 ;; Value must be a list and all the members must match the type.
2558 (and (listp value)
2559 (null (cdr (widget-editable-list-match-inline widget value)))))
2560
2561(defun widget-editable-list-match-inline (widget value)
2562 (let ((type (nth 0 (widget-get widget :args)))
2563 (ok t)
2564 found)
2565 (while (and value ok)
2566 (let ((answer (widget-match-inline type value)))
bfa6c260 2567 (if answer
d543e20b
PA
2568 (setq found (append found (car answer))
2569 value (cdr answer))
2570 (setq ok nil))))
2571 (cons found value)))
2572
2573(defun widget-editable-list-insert-before (widget before)
2574 ;; Insert a new child in the list of children.
2575 (save-excursion
2576 (let ((children (widget-get widget :children))
2577 (inhibit-read-only t)
c6753d66 2578 before-change-functions
d543e20b 2579 after-change-functions)
bfa6c260 2580 (cond (before
d543e20b
PA
2581 (goto-char (widget-get before :entry-from)))
2582 (t
2583 (goto-char (widget-get widget :value-pos))))
bfa6c260 2584 (let ((child (widget-editable-list-entry-create
d543e20b
PA
2585 widget nil nil)))
2586 (when (< (widget-get child :entry-from) (widget-get widget :from))
2587 (set-marker (widget-get widget :from)
2588 (widget-get child :entry-from)))
d543e20b
PA
2589 (if (eq (car children) before)
2590 (widget-put widget :children (cons child children))
2591 (while (not (eq (car (cdr children)) before))
2592 (setq children (cdr children)))
2593 (setcdr children (cons child (cdr children)))))))
2594 (widget-setup)
0a3a0b56 2595 (widget-apply widget :notify widget))
d543e20b
PA
2596
2597(defun widget-editable-list-delete-at (widget child)
2598 ;; Delete child from list of children.
2599 (save-excursion
ef3f635f 2600 (let ((buttons (copy-sequence (widget-get widget :buttons)))
d543e20b
PA
2601 button
2602 (inhibit-read-only t)
c6753d66 2603 before-change-functions
d543e20b
PA
2604 after-change-functions)
2605 (while buttons
2606 (setq button (car buttons)
2607 buttons (cdr buttons))
2608 (when (eq (widget-get button :widget) child)
2609 (widget-put widget
2610 :buttons (delq button (widget-get widget :buttons)))
2611 (widget-delete button))))
2612 (let ((entry-from (widget-get child :entry-from))
2613 (entry-to (widget-get child :entry-to))
2614 (inhibit-read-only t)
c6753d66 2615 before-change-functions
d543e20b
PA
2616 after-change-functions)
2617 (widget-delete child)
2618 (delete-region entry-from entry-to)
2619 (set-marker entry-from nil)
2620 (set-marker entry-to nil))
2621 (widget-put widget :children (delq child (widget-get widget :children))))
2622 (widget-setup)
2623 (widget-apply widget :notify widget))
2624
2625(defun widget-editable-list-entry-create (widget value conv)
2626 ;; Create a new entry to the list.
2627 (let ((type (nth 0 (widget-get widget :args)))
407e43be 2628 ;; (widget-push-button-gui widget-editable-list-gui)
2ff864e0 2629 child delete insert)
bfa6c260 2630 (widget-specify-insert
d543e20b
PA
2631 (save-excursion
2632 (and (widget-get widget :indent)
7fdbdbea 2633 (insert-char ?\ (widget-get widget :indent)))
d543e20b
PA
2634 (insert (widget-get widget :entry-format)))
2635 ;; Parse % escapes in format.
2636 (while (re-search-forward "%\\(.\\)" nil t)
7fdbdbea
DL
2637 (let ((escape (char-after (match-beginning 1))))
2638 (delete-backward-char 2)
d543e20b 2639 (cond ((eq escape ?%)
bfa6c260 2640 (insert ?%))
2ff864e0
DL
2641 ((eq escape ?i)
2642 (setq insert (apply 'widget-create-child-and-convert
2643 widget 'insert-button
2644 (widget-get widget :insert-button-args))))
2645 ((eq escape ?d)
2646 (setq delete (apply 'widget-create-child-and-convert
2647 widget 'delete-button
2648 (widget-get widget :delete-button-args))))
d543e20b
PA
2649 ((eq escape ?v)
2650 (if conv
bfa6c260 2651 (setq child (widget-create-child-value
d543e20b 2652 widget type value))
bfa6c260 2653 (setq child (widget-create-child-value
4c2f559e 2654 widget type (widget-default-get type)))))
bfa6c260 2655 (t
d543e20b 2656 (error "Unknown escape `%c'" escape)))))
407e43be
SM
2657 (let ((buttons (widget-get widget :buttons)))
2658 (if insert (push insert buttons))
2659 (if delete (push delete buttons))
2660 (widget-put widget :buttons buttons))
7fdbdbea
DL
2661 (let ((entry-from (point-min-marker))
2662 (entry-to (point-max-marker)))
d543e20b
PA
2663 (set-marker-insertion-type entry-from t)
2664 (set-marker-insertion-type entry-to nil)
2665 (widget-put child :entry-from entry-from)
2666 (widget-put child :entry-to entry-to)))
407e43be
SM
2667 (if insert (widget-put insert :widget child))
2668 (if delete (widget-put delete :widget child))
d543e20b
PA
2669 child))
2670
2671;;; The `group' Widget.
2672
2673(define-widget 'group 'default
a89a9d34 2674 "A widget which groups other widgets inside."
d543e20b 2675 :convert-widget 'widget-types-convert-widget
4c2f559e 2676 :copy 'widget-types-copy
d543e20b
PA
2677 :format "%v"
2678 :value-create 'widget-group-value-create
d543e20b 2679 :value-get 'widget-editable-list-value-get
783824f5 2680 :default-get 'widget-group-default-get
a3c88c59 2681 :validate 'widget-children-validate
d543e20b
PA
2682 :match 'widget-group-match
2683 :match-inline 'widget-group-match-inline)
2684
2685(defun widget-group-value-create (widget)
2686 ;; Create each component.
2687 (let ((args (widget-get widget :args))
2688 (value (widget-get widget :value))
2689 arg answer children)
2690 (while args
2691 (setq arg (car args)
2692 args (cdr args)
2693 answer (widget-match-inline arg value)
2694 value (cdr answer))
2695 (and (eq (preceding-char) ?\n)
2696 (widget-get widget :indent)
7fdbdbea 2697 (insert-char ?\ (widget-get widget :indent)))
3acab5ef
PA
2698 (push (cond ((null answer)
2699 (widget-create-child widget arg))
2700 ((widget-get arg :inline)
7fdbdbea 2701 (widget-create-child-value widget arg (car answer)))
3acab5ef 2702 (t
7fdbdbea 2703 (widget-create-child-value widget arg (car (car answer)))))
3acab5ef 2704 children))
d543e20b
PA
2705 (widget-put widget :children (nreverse children))))
2706
783824f5
RS
2707(defun widget-group-default-get (widget)
2708 ;; Get the default of the components.
2709 (mapcar 'widget-default-get (widget-get widget :args)))
2710
d543e20b
PA
2711(defun widget-group-match (widget values)
2712 ;; Match if the components match.
2713 (and (listp values)
2714 (let ((match (widget-group-match-inline widget values)))
2715 (and match (null (cdr match))))))
2716
2717(defun widget-group-match-inline (widget vals)
2718 ;; Match if the components match.
2719 (let ((args (widget-get widget :args))
2720 argument answer found)
2721 (while args
2722 (setq argument (car args)
2723 args (cdr args)
2724 answer (widget-match-inline argument vals))
bfa6c260 2725 (if answer
d543e20b
PA
2726 (setq vals (cdr answer)
2727 found (append found (car answer)))
2728 (setq vals nil
2729 args nil)))
2730 (if answer
bfa6c260 2731 (cons found vals))))
d543e20b 2732
3acab5ef 2733;;; The `visibility' Widget.
d543e20b 2734
3acab5ef
PA
2735(define-widget 'visibility 'item
2736 "An indicator and manipulator for hidden items."
2737 :format "%[%v%]"
2738 :button-prefix ""
2739 :button-suffix ""
c6753d66
RS
2740 :on "Hide"
2741 :off "Show"
3acab5ef
PA
2742 :value-create 'widget-visibility-value-create
2743 :action 'widget-toggle-action
2744 :match (lambda (widget value) t))
d543e20b 2745
3acab5ef
PA
2746(defun widget-visibility-value-create (widget)
2747 ;; Insert text representing the `on' and `off' states.
2748 (let ((on (widget-get widget :on))
2749 (off (widget-get widget :off)))
2750 (if on
2751 (setq on (concat widget-push-button-prefix
2752 on
2753 widget-push-button-suffix))
2754 (setq on ""))
2755 (if off
2756 (setq off (concat widget-push-button-prefix
c6753d66
RS
2757 off
2758 widget-push-button-suffix))
3acab5ef
PA
2759 (setq off ""))
2760 (if (widget-value widget)
bfa6c260
DL
2761 (widget-image-insert widget on "down" "down-pushed")
2762 (widget-image-insert widget off "right" "right-pushed"))))
c6753d66 2763
8697863a
PA
2764;;; The `documentation-link' Widget.
2765;;
2766;; This is a helper widget for `documentation-string'.
3acab5ef 2767
8697863a
PA
2768(define-widget 'documentation-link 'link
2769 "Link type used in documentation strings."
2770 :tab-order -1
bfa6c260 2771 :help-echo "Describe this symbol"
8697863a
PA
2772 :action 'widget-documentation-link-action)
2773
8697863a 2774(defun widget-documentation-link-action (widget &optional event)
f9923499 2775 "Display documentation for WIDGET's value. Ignore optional argument EVENT."
9dccd7ef
RS
2776 (let* ((string (widget-get widget :value))
2777 (symbol (intern string)))
2778 (if (and (fboundp symbol) (boundp symbol))
f9923499 2779 ;; If there are two doc strings, give the user a way to pick one.
9dccd7ef
RS
2780 (apropos (concat "\\`" (regexp-quote string) "\\'"))
2781 (if (fboundp symbol)
2782 (describe-function symbol)
2783 (describe-variable symbol)))))
8697863a
PA
2784
2785(defcustom widget-documentation-links t
2786 "Add hyperlinks to documentation strings when non-nil."
2787 :type 'boolean
2788 :group 'widget-documentation)
2789
2790(defcustom widget-documentation-link-regexp "`\\([^\n`' ]+\\)'"
2791 "Regexp for matching potential links in documentation strings.
2792The first group should be the link itself."
2793 :type 'regexp
2794 :group 'widget-documentation)
2795
2796(defcustom widget-documentation-link-p 'intern-soft
2797 "Predicate used to test if a string is useful as a link.
2798The value should be a function. The function will be called one
2799argument, a string, and should return non-nil if there should be a
2800link for that string."
2801 :type 'function
2802 :options '(widget-documentation-link-p)
2803 :group 'widget-documentation)
2804
2805(defcustom widget-documentation-link-type 'documentation-link
2806 "Widget type used for links in documentation strings."
2807 :type 'symbol
2808 :group 'widget-documentation)
2809
2810(defun widget-documentation-link-add (widget from to)
2811 (widget-specify-doc widget from to)
2812 (when widget-documentation-links
2813 (let ((regexp widget-documentation-link-regexp)
a89a9d34
DL
2814 (buttons (widget-get widget :buttons))
2815 (widget-mouse-face (default-value 'widget-mouse-face))
2816 (widget-button-face widget-documentation-face)
2817 (widget-button-pressed-face widget-documentation-face))
8697863a
PA
2818 (save-excursion
2819 (goto-char from)
2820 (while (re-search-forward regexp to t)
2821 (let ((name (match-string 1))
a1a4fa22
PA
2822 (begin (match-beginning 1))
2823 (end (match-end 1)))
7fdbdbea
DL
2824 (when (funcall widget-documentation-link-p name)
2825 (push (widget-convert-button widget-documentation-link-type
2826 begin end :value name)
8697863a
PA
2827 buttons)))))
2828 (widget-put widget :buttons buttons)))
2829 (let ((indent (widget-get widget :indent)))
2830 (when (and indent (not (zerop indent)))
bfa6c260 2831 (save-excursion
8697863a
PA
2832 (save-restriction
2833 (narrow-to-region from to)
2834 (goto-char (point-min))
2835 (while (search-forward "\n" nil t)
2836 (insert-char ?\ indent)))))))
2837
2838;;; The `documentation-string' Widget.
0ce5b5d5 2839
3acab5ef
PA
2840(define-widget 'documentation-string 'item
2841 "A documentation string."
2842 :format "%v"
2843 :action 'widget-documentation-string-action
3acab5ef
PA
2844 :value-create 'widget-documentation-string-value-create)
2845
2846(defun widget-documentation-string-value-create (widget)
2847 ;; Insert documentation string.
2848 (let ((doc (widget-value widget))
8697863a 2849 (indent (widget-get widget :indent))
6aaedd12
PA
2850 (shown (widget-get (widget-get widget :parent) :documentation-shown))
2851 (start (point)))
3acab5ef
PA
2852 (if (string-match "\n" doc)
2853 (let ((before (substring doc 0 (match-beginning 0)))
2854 (after (substring doc (match-beginning 0)))
7fdbdbea 2855 button)
bfa6c260 2856 (insert before ?\ )
8697863a 2857 (widget-documentation-link-add widget start (point))
7fdbdbea
DL
2858 (setq button
2859 (widget-create-child-and-convert
3acab5ef 2860 widget 'visibility
8697863a 2861 :help-echo "Show or hide rest of the documentation."
dd98f00a 2862 :on "Hide Rest"
c6753d66 2863 :off "More"
0640c647 2864 :always-active t
3acab5ef 2865 :action 'widget-parent-action
7fdbdbea 2866 shown))
3acab5ef 2867 (when shown
0ce5b5d5 2868 (setq start (point))
8697863a
PA
2869 (when (and indent (not (zerop indent)))
2870 (insert-char ?\ indent))
0ce5b5d5 2871 (insert after)
8697863a 2872 (widget-documentation-link-add widget start (point)))
7fdbdbea 2873 (widget-put widget :buttons (list button)))
6aaedd12 2874 (insert doc)
8697863a 2875 (widget-documentation-link-add widget start (point))))
bfa6c260 2876 (insert ?\n))
3acab5ef
PA
2877
2878(defun widget-documentation-string-action (widget &rest ignore)
2879 ;; Toggle documentation.
2880 (let ((parent (widget-get widget :parent)))
bfa6c260 2881 (widget-put parent :documentation-shown
3acab5ef
PA
2882 (not (widget-get parent :documentation-shown))))
2883 ;; Redraw.
d543e20b 2884 (widget-value-set widget (widget-value widget)))
fc56773e 2885\f
d543e20b
PA
2886;;; The Sexp Widgets.
2887
2888(define-widget 'const 'item
2889 "An immutable sexp."
6d528fc5 2890 :prompt-value 'widget-const-prompt-value
d543e20b
PA
2891 :format "%t\n%d")
2892
6d528fc5
PA
2893(defun widget-const-prompt-value (widget prompt value unbound)
2894 ;; Return the value of the const.
2895 (widget-value widget))
2896
2897(define-widget 'function-item 'const
d543e20b
PA
2898 "An immutable function name."
2899 :format "%v\n%h"
2900 :documentation-property (lambda (symbol)
2901 (condition-case nil
2902 (documentation symbol t)
2903 (error nil))))
2904
6d528fc5 2905(define-widget 'variable-item 'const
d543e20b
PA
2906 "An immutable variable name."
2907 :format "%v\n%h"
2908 :documentation-property 'variable-documentation)
2909
cc0a25e1
RS
2910(define-widget 'other 'sexp
2911 "Matches any value, but doesn't let the user edit the value.
2912This is useful as last item in a `choice' widget.
2913You should use this widget type with a default value,
b720878d 2914as in (other DEFAULT) or (other :tag \"NAME\" DEFAULT).
cc0a25e1
RS
2915If the user selects this alternative, that specifies DEFAULT
2916as the value."
2917 :tag "Other"
2918 :format "%t%n"
2919 :value 'other)
2920
6d528fc5
PA
2921(defvar widget-string-prompt-value-history nil
2922 "History of input to `widget-string-prompt-value'.")
2923
a3c88c59
PA
2924(define-widget 'string 'editable-field
2925 "A string"
2926 :tag "String"
2927 :format "%{%t%}: %v"
0ce5b5d5 2928 :complete-function 'ispell-complete-word
a3c88c59 2929 :prompt-history 'widget-string-prompt-value-history)
6d528fc5 2930
d543e20b
PA
2931(define-widget 'regexp 'string
2932 "A regular expression."
6d528fc5
PA
2933 :match 'widget-regexp-match
2934 :validate 'widget-regexp-validate
4ee1cf9f
PA
2935 ;; Doesn't work well with terminating newline.
2936 ;; :value-face 'widget-single-line-field-face
d543e20b
PA
2937 :tag "Regexp")
2938
6d528fc5
PA
2939(defun widget-regexp-match (widget value)
2940 ;; Match valid regexps.
2941 (and (stringp value)
a3c88c59 2942 (condition-case nil
6d528fc5
PA
2943 (prog1 t
2944 (string-match value ""))
2945 (error nil))))
2946
2947(defun widget-regexp-validate (widget)
2948 "Check that the value of WIDGET is a valid regexp."
7fdbdbea
DL
2949 (condition-case data
2950 (prog1 nil
2951 (string-match (widget-value widget) ""))
2952 (error (widget-put widget :error (error-message-string data))
2953 widget)))
6d528fc5 2954
d543e20b 2955(define-widget 'file 'string
bfa6c260 2956 "A file widget.
25ac13b5 2957It will read a file name from the minibuffer when invoked."
f1231b8e 2958 :complete-function 'widget-file-complete
6d528fc5 2959 :prompt-value 'widget-file-prompt-value
a3c88c59 2960 :format "%{%t%}: %v"
4ee1cf9f
PA
2961 ;; Doesn't work well with terminating newline.
2962 ;; :value-face 'widget-single-line-field-face
f1231b8e
RS
2963 :tag "File")
2964
2965(defun widget-file-complete ()
2966 "Perform completion on file name preceding point."
2967 (interactive)
2968 (let* ((end (point))
2969 (beg (save-excursion
2970 (skip-chars-backward "^ ")
2971 (point)))
2972 (pattern (buffer-substring beg end))
2973 (name-part (file-name-nondirectory pattern))
2974 (directory (file-name-directory pattern))
2975 (completion (file-name-completion name-part directory)))
2976 (cond ((eq completion t))
2977 ((null completion)
2978 (message "Can't find completion for \"%s\"" pattern)
2979 (ding))
2980 ((not (string= name-part completion))
2981 (delete-region beg end)
2982 (insert (expand-file-name completion directory)))
2983 (t
2984 (message "Making completion list...")
7fdbdbea
DL
2985 (with-output-to-temp-buffer "*Completions*"
2986 (display-completion-list
2987 (sort (file-name-all-completions name-part directory)
2988 'string<)))
f1231b8e 2989 (message "Making completion list...%s" "done")))))
d543e20b 2990
6d528fc5
PA
2991(defun widget-file-prompt-value (widget prompt value unbound)
2992 ;; Read file from minibuffer.
2993 (abbreviate-file-name
2994 (if unbound
2995 (read-file-name prompt)
a3c88c59 2996 (let ((prompt2 (format "%s (default %s) " prompt value))
6d528fc5
PA
2997 (dir (file-name-directory value))
2998 (file (file-name-nondirectory value))
2999 (must-match (widget-get widget :must-match)))
3000 (read-file-name prompt2 dir nil must-match file)))))
3001
f1231b8e
RS
3002;;;(defun widget-file-action (widget &optional event)
3003;;; ;; Read a file name from the minibuffer.
3004;;; (let* ((value (widget-value widget))
3005;;; (dir (file-name-directory value))
3006;;; (file (file-name-nondirectory value))
3007;;; (menu-tag (widget-apply widget :menu-tag-get))
3008;;; (must-match (widget-get widget :must-match))
3009;;; (answer (read-file-name (concat menu-tag ": (default `" value "') ")
3010;;; dir nil must-match file)))
3011;;; (widget-value-set widget (abbreviate-file-name answer))
3012;;; (widget-setup)
3013;;; (widget-apply widget :notify widget event)))
d543e20b 3014
bd1f16ce 3015;; Fixme: use file-name-as-directory.
d543e20b 3016(define-widget 'directory 'file
bfa6c260 3017 "A directory widget.
25ac13b5 3018It will read a directory name from the minibuffer when invoked."
d543e20b
PA
3019 :tag "Directory")
3020
a3c88c59
PA
3021(defvar widget-symbol-prompt-value-history nil
3022 "History of input to `widget-symbol-prompt-value'.")
3023
3024(define-widget 'symbol 'editable-field
4084d128 3025 "A Lisp symbol."
d543e20b
PA
3026 :value nil
3027 :tag "Symbol"
a3c88c59 3028 :format "%{%t%}: %v"
d543e20b 3029 :match (lambda (widget value) (symbolp value))
f1231b8e 3030 :complete-function 'lisp-complete-symbol
a3c88c59
PA
3031 :prompt-internal 'widget-symbol-prompt-internal
3032 :prompt-match 'symbolp
3033 :prompt-history 'widget-symbol-prompt-value-history
d543e20b
PA
3034 :value-to-internal (lambda (widget value)
3035 (if (symbolp value)
3036 (symbol-name value)
3037 value))
3038 :value-to-external (lambda (widget value)
3039 (if (stringp value)
3040 (intern value)
3041 value)))
3042
a3c88c59
PA
3043(defun widget-symbol-prompt-internal (widget prompt initial history)
3044 ;; Read file from minibuffer.
bfa6c260 3045 (let ((answer (completing-read prompt obarray
a3c88c59
PA
3046 (widget-get widget :prompt-match)
3047 nil initial history)))
3048 (if (and (stringp answer)
3049 (not (zerop (length answer))))
3050 answer
3051 (error "No value"))))
3052
3053(defvar widget-function-prompt-value-history nil
3054 "History of input to `widget-function-prompt-value'.")
3055
d543e20b 3056(define-widget 'function 'sexp
4084d128 3057 "A Lisp function."
7fdbdbea
DL
3058 :complete-function (lambda ()
3059 (interactive)
3060 (lisp-complete-symbol 'fboundp))
a3c88c59
PA
3061 :prompt-value 'widget-field-prompt-value
3062 :prompt-internal 'widget-symbol-prompt-internal
3063 :prompt-match 'fboundp
3064 :prompt-history 'widget-function-prompt-value-history
3065 :action 'widget-field-action
bd1f16ce 3066 :match-alternatives '(functionp)
7fdbdbea
DL
3067 :validate (lambda (widget)
3068 (unless (functionp (widget-value widget))
3069 (widget-put widget :error (format "Invalid function: %S"
3070 (widget-value widget)))
3071 widget))
3072 :value 'ignore
d543e20b
PA
3073 :tag "Function")
3074
a3c88c59
PA
3075(defvar widget-variable-prompt-value-history nil
3076 "History of input to `widget-variable-prompt-value'.")
3077
d543e20b 3078(define-widget 'variable 'symbol
be96282a 3079 "A Lisp variable."
a3c88c59
PA
3080 :prompt-match 'boundp
3081 :prompt-history 'widget-variable-prompt-value-history
7fdbdbea
DL
3082 :complete-function (lambda ()
3083 (interactive)
3084 (lisp-complete-symbol 'boundp))
d543e20b
PA
3085 :tag "Variable")
3086
fc56773e
RS
3087(defvar widget-coding-system-prompt-value-history nil
3088 "History of input to `widget-coding-system-prompt-value'.")
77339a6e 3089
fc56773e
RS
3090(define-widget 'coding-system 'symbol
3091 "A MULE coding-system."
3092 :format "%{%t%}: %v"
3093 :tag "Coding system"
7fdbdbea 3094 :base-only nil
fc56773e
RS
3095 :prompt-history 'widget-coding-system-prompt-value-history
3096 :prompt-value 'widget-coding-system-prompt-value
7fdbdbea
DL
3097 :action 'widget-coding-system-action
3098 :complete-function (lambda ()
3099 (interactive)
3100 (lisp-complete-symbol 'coding-system-p))
3101 :validate (lambda (widget)
3102 (unless (coding-system-p (widget-value widget))
3103 (widget-put widget :error (format "Invalid coding system: %S"
3104 (widget-value widget)))
3105 widget))
3106 :value 'undecided
3107 :prompt-match 'coding-system-p)
3108
fc56773e 3109(defun widget-coding-system-prompt-value (widget prompt value unbound)
7fdbdbea
DL
3110 "Read coding-system from minibuffer."
3111 (if (widget-get widget :base-only)
3112 (intern
3113 (completing-read (format "%s (default %s) " prompt value)
3114 (mapcar #'list (coding-system-list t)) nil nil nil
3115 coding-system-history))
3116 (read-coding-system (format "%s (default %s) " prompt value) value)))
fc56773e
RS
3117
3118(defun widget-coding-system-action (widget &optional event)
fc56773e
RS
3119 (let ((answer
3120 (widget-coding-system-prompt-value
3121 widget
3122 (widget-apply widget :menu-tag-get)
3123 (widget-value widget)
3124 t)))
3125 (widget-value-set widget answer)
3126 (widget-apply widget :notify widget event)
3127 (widget-setup)))
fc56773e 3128\f
a3c88c59 3129(define-widget 'sexp 'editable-field
be96282a 3130 "An arbitrary Lisp expression."
d543e20b 3131 :tag "Lisp expression"
a3c88c59 3132 :format "%{%t%}: %v"
d543e20b
PA
3133 :value nil
3134 :validate 'widget-sexp-validate
3135 :match (lambda (widget value) t)
3136 :value-to-internal 'widget-sexp-value-to-internal
6d528fc5 3137 :value-to-external (lambda (widget value) (read value))
a3c88c59 3138 :prompt-history 'widget-sexp-prompt-value-history
6d528fc5 3139 :prompt-value 'widget-sexp-prompt-value)
d543e20b
PA
3140
3141(defun widget-sexp-value-to-internal (widget value)
3142 ;; Use pp for printer representation.
6d1ab9d4
RS
3143 (let ((pp (if (symbolp value)
3144 (prin1-to-string value)
3145 (pp-to-string value))))
d543e20b
PA
3146 (while (string-match "\n\\'" pp)
3147 (setq pp (substring pp 0 -1)))
3148 (if (or (string-match "\n\\'" pp)
3149 (> (length pp) 40))
3150 (concat "\n" pp)
3151 pp)))
3152
3153(defun widget-sexp-validate (widget)
3154 ;; Valid if we can read the string and there is no junk left after it.
99f01612
DL
3155 (with-temp-buffer
3156 (insert (widget-apply widget :value-get))
3157 (goto-char (point-min))
1d869634
DL
3158 (let (err)
3159 (condition-case data
3160 (progn
3161 ;; Avoid a confusing end-of-file error.
3162 (skip-syntax-forward "\\s-")
3163 (if (eobp)
3164 (setq err "Empty sexp -- use `nil'?")
7fdbdbea 3165 (unless (widget-apply widget :match (read (current-buffer)))
1d869634 3166 (setq err (widget-get widget :type-error))))
3bc603c4
LH
3167 ;; Allow whitespace after expression.
3168 (skip-syntax-forward "\\s-")
1d869634
DL
3169 (if (and (not (eobp))
3170 (not err))
3171 (setq err (format "Junk at end of expression: %s"
3172 (buffer-substring (point)
3173 (point-max))))))
3174 (end-of-file ; Avoid confusing error message.
3175 (setq err "Unbalanced sexp"))
3176 (error (setq err (error-message-string data))))
3177 (if (not err)
3178 nil
3179 (widget-put widget :error err)
3180 widget))))
d543e20b 3181
6d528fc5
PA
3182(defvar widget-sexp-prompt-value-history nil
3183 "History of input to `widget-sexp-prompt-value'.")
3184
3185(defun widget-sexp-prompt-value (widget prompt value unbound)
3186 ;; Read an arbitrary sexp.
3187 (let ((found (read-string prompt
a3c88c59
PA
3188 (if unbound nil (cons (prin1-to-string value) 0))
3189 (widget-get widget :prompt-history))))
bfa6c260
DL
3190 (let ((answer (read-from-string found)))
3191 (unless (= (cdr answer) (length found))
3192 (error "Junk at end of expression: %s"
3193 (substring found (cdr answer))))
3194 (car answer))))
a3c88c59 3195
0b296dac
RS
3196(define-widget 'restricted-sexp 'sexp
3197 "A Lisp expression restricted to values that match.
3198To use this type, you must define :match or :match-alternatives."
3199 :type-error "The specified value is not valid"
3200 :match 'widget-restricted-sexp-match
3201 :value-to-internal (lambda (widget value)
3202 (if (widget-apply widget :match value)
3203 (prin1-to-string value)
3204 value)))
3205
3206(defun widget-restricted-sexp-match (widget value)
3207 (let ((alternatives (widget-get widget :match-alternatives))
3208 matched)
3209 (while (and alternatives (not matched))
3210 (if (cond ((functionp (car alternatives))
3211 (funcall (car alternatives) value))
3212 ((and (consp (car alternatives))
3213 (eq (car (car alternatives)) 'quote))
3214 (eq value (nth 1 (car alternatives)))))
3215 (setq matched t))
3216 (setq alternatives (cdr alternatives)))
3217 matched))
fc56773e 3218\f
0b296dac 3219(define-widget 'integer 'restricted-sexp
d543e20b
PA
3220 "An integer."
3221 :tag "Integer"
3222 :value 0
3223 :type-error "This field should contain an integer"
0b296dac
RS
3224 :match-alternatives '(integerp))
3225
3226(define-widget 'number 'restricted-sexp
d9bfd9dc 3227 "A number (floating point or integer)."
0b296dac
RS
3228 :tag "Number"
3229 :value 0.0
d9bfd9dc 3230 :type-error "This field should contain a number (floating point or integer)"
0b296dac 3231 :match-alternatives '(numberp))
d543e20b 3232
d9bfd9dc
MR
3233(define-widget 'float 'restricted-sexp
3234 "A floating point number."
3235 :tag "Floating point number"
3236 :value 0.0
3237 :type-error "This field should contain a floating point number"
3238 :match-alternatives '(floatp))
3239
a3c88c59 3240(define-widget 'character 'editable-field
0b296dac 3241 "A character."
d543e20b
PA
3242 :tag "Character"
3243 :value 0
bfa6c260 3244 :size 1
d543e20b 3245 :format "%{%t%}: %v\n"
6d528fc5
PA
3246 :valid-regexp "\\`.\\'"
3247 :error "This field should contain a single character"
d543e20b 3248 :value-to-internal (lambda (widget value)
bfa6c260 3249 (if (stringp value)
a3c88c59
PA
3250 value
3251 (char-to-string value)))
d543e20b
PA
3252 :value-to-external (lambda (widget value)
3253 (if (stringp value)
3254 (aref value 0)
3255 value))
a3c88c59 3256 :match (lambda (widget value)
99f01612 3257 (char-valid-p value)))
d543e20b 3258
d543e20b 3259(define-widget 'list 'group
be96282a 3260 "A Lisp list."
d543e20b
PA
3261 :tag "List"
3262 :format "%{%t%}:\n%v")
3263
3264(define-widget 'vector 'group
be96282a 3265 "A Lisp vector."
d543e20b
PA
3266 :tag "Vector"
3267 :format "%{%t%}:\n%v"
3268 :match 'widget-vector-match
3269 :value-to-internal (lambda (widget value) (append value nil))
3270 :value-to-external (lambda (widget value) (apply 'vector value)))
3271
bfa6c260 3272(defun widget-vector-match (widget value)
d543e20b
PA
3273 (and (vectorp value)
3274 (widget-group-match widget
bd042c03 3275 (widget-apply widget :value-to-internal value))))
d543e20b
PA
3276
3277(define-widget 'cons 'group
3278 "A cons-cell."
3279 :tag "Cons-cell"
3280 :format "%{%t%}:\n%v"
3281 :match 'widget-cons-match
3282 :value-to-internal (lambda (widget value)
3283 (list (car value) (cdr value)))
3284 :value-to-external (lambda (widget value)
407e43be 3285 (apply 'cons value)))
d543e20b 3286
bfa6c260 3287(defun widget-cons-match (widget value)
d543e20b
PA
3288 (and (consp value)
3289 (widget-group-match widget
3290 (widget-apply widget :value-to-internal value))))
fc56773e 3291\f
cfa921fd
PA
3292;;; The `lazy' Widget.
3293;;
3294;; Recursive datatypes.
3295
3296(define-widget 'lazy 'default
3297 "Base widget for recursive datastructures.
3298
3299The `lazy' widget will, when instantiated, contain a single inferior
3300widget, of the widget type specified by the :type parameter. The
3301value of the `lazy' widget is the same as the value of the inferior
3302widget. When deriving a new widget from the 'lazy' widget, the :type
3303parameter is allowed to refer to the widget currently being defined,
3304thus allowing recursive datastructures to be described.
3305
3306The :type parameter takes the same arguments as the defcustom
3307parameter with the same name.
3308
3309Most composite widgets, i.e. widgets containing other widgets, does
3310not allow recursion. That is, when you define a new widget type, none
3311of the inferior widgets may be of the same type you are currently
3312defining.
3313
3314In Lisp, however, it is custom to define datastructures in terms of
3315themselves. A list, for example, is defined as either nil, or a cons
3316cell whose cdr itself is a list. The obvious way to translate this
3317into a widget type would be
3318
3319 (define-widget 'my-list 'choice
3320 \"A list of sexps.\"
3321 :tag \"Sexp list\"
3322 :args '((const nil) (cons :value (nil) sexp my-list)))
3323
3324Here we attempt to define my-list as a choice of either the constant
3325nil, or a cons-cell containing a sexp and my-lisp. This will not work
3326because the `choice' widget does not allow recursion.
3327
0e726aa5
KS
3328Using the `lazy' widget you can overcome this problem, as in this
3329example:
cfa921fd
PA
3330
3331 (define-widget 'sexp-list 'lazy
3332 \"A list of sexps.\"
3333 :tag \"Sexp list\"
3334 :type '(choice (const nil) (cons :value (nil) sexp sexp-list)))"
3335 :format "%{%t%}: %v"
3336 ;; We don't convert :type because we want to allow recursive
3337 ;; datastructures. This is slow, so we should not create speed
0e726aa5 3338 ;; critical widgets by deriving from this.
cfa921fd
PA
3339 :convert-widget 'widget-value-convert-widget
3340 :value-create 'widget-type-value-create
3341 :value-get 'widget-child-value-get
3342 :value-inline 'widget-child-value-inline
3343 :default-get 'widget-type-default-get
3344 :match 'widget-type-match
3345 :validate 'widget-child-validate)
3346
3347\f
fc56773e
RS
3348;;; The `plist' Widget.
3349;;
3350;; Property lists.
3351
3352(define-widget 'plist 'list
3353 "A property list."
3354 :key-type '(symbol :tag "Key")
3355 :value-type '(sexp :tag "Value")
3356 :convert-widget 'widget-plist-convert-widget
3357 :tag "Plist")
3358
3359(defvar widget-plist-value-type) ;Dynamic variable
3360
3361(defun widget-plist-convert-widget (widget)
3362 ;; Handle `:options'.
3363 (let* ((options (widget-get widget :options))
4681ca3a 3364 (widget-plist-value-type (widget-get widget :value-type))
bfa6c260 3365 (other `(editable-list :inline t
fc56773e 3366 (group :inline t
7fdbdbea 3367 ,(widget-get widget :key-type)
4681ca3a 3368 ,widget-plist-value-type)))
fc56773e
RS
3369 (args (if options
3370 (list `(checklist :inline t
3371 :greedy t
3372 ,@(mapcar 'widget-plist-convert-option
3373 options))
3374 other)
3375 (list other))))
3376 (widget-put widget :args args)
3377 widget))
d543e20b 3378
fc56773e
RS
3379(defun widget-plist-convert-option (option)
3380 ;; Convert a single plist option.
3381 (let (key-type value-type)
3382 (if (listp option)
3383 (let ((key (nth 0 option)))
3384 (setq value-type (nth 1 option))
3385 (if (listp key)
31d5543d 3386 (setq key-type key)
fc56773e
RS
3387 (setq key-type `(const ,key))))
3388 (setq key-type `(const ,option)
3389 value-type widget-plist-value-type))
3390 `(group :format "Key: %v" :inline t ,key-type ,value-type)))
3391
3392
3393;;; The `alist' Widget.
3394;;
3395;; Association lists.
3396
3397(define-widget 'alist 'list
3398 "An association list."
a7013a02 3399 :key-type '(sexp :tag "Key")
fc56773e
RS
3400 :value-type '(sexp :tag "Value")
3401 :convert-widget 'widget-alist-convert-widget
3402 :tag "Alist")
3403
3404(defvar widget-alist-value-type) ;Dynamic variable
3405
3406(defun widget-alist-convert-widget (widget)
3407 ;; Handle `:options'.
3408 (let* ((options (widget-get widget :options))
4681ca3a 3409 (widget-alist-value-type (widget-get widget :value-type))
bfa6c260 3410 (other `(editable-list :inline t
fc56773e 3411 (cons :format "%v"
7fdbdbea 3412 ,(widget-get widget :key-type)
4681ca3a 3413 ,widget-alist-value-type)))
fc56773e
RS
3414 (args (if options
3415 (list `(checklist :inline t
3416 :greedy t
3417 ,@(mapcar 'widget-alist-convert-option
3418 options))
3419 other)
3420 (list other))))
3421 (widget-put widget :args args)
3422 widget))
3423
3424(defun widget-alist-convert-option (option)
3425 ;; Convert a single alist option.
3426 (let (key-type value-type)
3427 (if (listp option)
3428 (let ((key (nth 0 option)))
3429 (setq value-type (nth 1 option))
3430 (if (listp key)
31d5543d 3431 (setq key-type key)
fc56773e
RS
3432 (setq key-type `(const ,key))))
3433 (setq key-type `(const ,option)
3434 value-type widget-alist-value-type))
3435 `(cons :format "Key: %v" ,key-type ,value-type)))
3436\f
d543e20b
PA
3437(define-widget 'choice 'menu-choice
3438 "A union of several sexp types."
3439 :tag "Choice"
c6753d66 3440 :format "%{%t%}: %[Value Menu%] %v"
8697863a
PA
3441 :button-prefix 'widget-push-button-prefix
3442 :button-suffix 'widget-push-button-suffix
a3c88c59
PA
3443 :prompt-value 'widget-choice-prompt-value)
3444
3445(defun widget-choice-prompt-value (widget prompt value unbound)
bfa6c260 3446 "Make a choice."
a3c88c59
PA
3447 (let ((args (widget-get widget :args))
3448 (completion-ignore-case (widget-get widget :case-fold))
3449 current choices old)
7fdbdbea 3450 ;; Find the first arg that matches VALUE.
a3c88c59
PA
3451 (let ((look args))
3452 (while look
3453 (if (widget-apply (car look) :match value)
3454 (setq old (car look)
3455 look nil)
3456 (setq look (cdr look)))))
3457 ;; Find new choice.
3458 (setq current
3459 (cond ((= (length args) 0)
3460 nil)
3461 ((= (length args) 1)
3462 (nth 0 args))
3463 ((and (= (length args) 2)
3464 (memq old args))
3465 (if (eq old (nth 0 args))
3466 (nth 1 args)
3467 (nth 0 args)))
3468 (t
3469 (while args
3470 (setq current (car args)
3471 args (cdr args))
3472 (setq choices
3473 (cons (cons (widget-apply current :menu-tag-get)
3474 current)
3475 choices)))
3476 (let ((val (completing-read prompt choices nil t)))
3477 (if (stringp val)
3478 (let ((try (try-completion val choices)))
3479 (when (stringp try)
3480 (setq val try))
3481 (cdr (assoc val choices)))
3482 nil)))))
3483 (if current
3484 (widget-prompt-value current prompt nil t)
3485 value)))
fc56773e 3486\f
d543e20b
PA
3487(define-widget 'radio 'radio-button-choice
3488 "A union of several sexp types."
3489 :tag "Choice"
a3c88c59
PA
3490 :format "%{%t%}:\n%v"
3491 :prompt-value 'widget-choice-prompt-value)
d543e20b
PA
3492
3493(define-widget 'repeat 'editable-list
3494 "A variable length homogeneous list."
3495 :tag "Repeat"
3496 :format "%{%t%}:\n%v%i\n")
3497
3498(define-widget 'set 'checklist
3499 "A list of members from a fixed set."
3500 :tag "Set"
3501 :format "%{%t%}:\n%v")
3502
3503(define-widget 'boolean 'toggle
3504 "To be nil or non-nil, that is the question."
3505 :tag "Boolean"
6d528fc5 3506 :prompt-value 'widget-boolean-prompt-value
8697863a
PA
3507 :button-prefix 'widget-push-button-prefix
3508 :button-suffix 'widget-push-button-suffix
c6753d66
RS
3509 :format "%{%t%}: %[Toggle%] %v\n"
3510 :on "on (non-nil)"
3511 :off "off (nil)")
d543e20b 3512
6d528fc5
PA
3513(defun widget-boolean-prompt-value (widget prompt value unbound)
3514 ;; Toggle a boolean.
a3c88c59 3515 (y-or-n-p prompt))
fc56773e 3516\f
d543e20b
PA
3517;;; The `color' Widget.
3518
77339a6e 3519;; Fixme: match
bfa6c260 3520(define-widget 'color 'editable-field
0f648ca2
PA
3521 "Choose a color name (with sample)."
3522 :format "%t: %v (%{sample%})\n"
3523 :size 10
3524 :tag "Color"
3525 :value "black"
3526 :complete 'widget-color-complete
3527 :sample-face-get 'widget-color-sample-face-get
3528 :notify 'widget-color-notify
3529 :action 'widget-color-action)
3530
3531(defun widget-color-complete (widget)
3532 "Complete the color in WIDGET."
99f01612 3533 (require 'facemenu) ; for facemenu-color-alist
0f648ca2
PA
3534 (let* ((prefix (buffer-substring-no-properties (widget-field-start widget)
3535 (point)))
451a66e3 3536 (list (or facemenu-color-alist (defined-colors)))
0f648ca2
PA
3537 (completion (try-completion prefix list)))
3538 (cond ((eq completion t)
3539 (message "Exact match."))
3540 ((null completion)
3541 (error "Can't find completion for \"%s\"" prefix))
3542 ((not (string-equal prefix completion))
3543 (insert-and-inherit (substring completion (length prefix))))
3544 (t
3545 (message "Making completion list...")
7fdbdbea
DL
3546 (with-output-to-temp-buffer "*Completions*"
3547 (display-completion-list (all-completions prefix list nil)))
0f648ca2 3548 (message "Making completion list...done")))))
d543e20b 3549
0f648ca2 3550(defun widget-color-sample-face-get (widget)
4ee1cf9f
PA
3551 (let* ((value (condition-case nil
3552 (widget-value widget)
76834555
GM
3553 (error (widget-get widget :value)))))
3554 (if (color-defined-p value)
546cf5b0 3555 (list (cons 'foreground-color value))
76834555 3556 'default)))
d543e20b 3557
d543e20b 3558(defun widget-color-action (widget &optional event)
bd1f16ce 3559 "Prompt for a color."
d543e20b
PA
3560 (let* ((tag (widget-apply widget :menu-tag-get))
3561 (prompt (concat tag ": "))
4ee1cf9f
PA
3562 (value (widget-value widget))
3563 (start (widget-field-start widget))
99f01612 3564 (answer (facemenu-read-color prompt)))
d543e20b
PA
3565 (unless (zerop (length answer))
3566 (widget-value-set widget answer)
0a3a0b56
PA
3567 (widget-setup)
3568 (widget-apply widget :notify widget event))))
d543e20b 3569
0f648ca2
PA
3570(defun widget-color-notify (widget child &optional event)
3571 "Update the sample, and notofy the parent."
bfa6c260 3572 (overlay-put (widget-get widget :sample-overlay)
0f648ca2
PA
3573 'face (widget-apply widget :sample-face-get))
3574 (widget-default-notify widget child event))
fc56773e 3575\f
d543e20b
PA
3576;;; The Help Echo
3577
d543e20b 3578(defun widget-echo-help (pos)
233d5cde 3579 "Display help-echo text for widget at POS."
d543e20b
PA
3580 (let* ((widget (widget-at pos))
3581 (help-echo (and widget (widget-get widget :help-echo))))
233d5cde
DL
3582 (if (functionp help-echo)
3583 (setq help-echo (funcall help-echo widget)))
3b26f44c 3584 (if help-echo (message "%s" (eval help-echo)))))
d543e20b
PA
3585
3586;;; The End:
3587
3588(provide 'wid-edit)
3589
ab5796a9 3590;;; arch-tag: a076e75e-18a1-4b46-8be5-3f317bcbc707
aeba6f9a 3591;;; wid-edit.el ends here