comparison lisp/custom/wid-edit.el @ 28:1917ad0d78d7 r19-15b97

Import from CVS: tag r19-15b97
author cvs
date Mon, 13 Aug 2007 08:51:55 +0200
parents
children ec9a17fef872
comparison
equal deleted inserted replaced
27:0a3286277d9b 28:1917ad0d78d7
1 ;;; wid-edit.el --- Functions for creating and using widgets.
2 ;;
3 ;; Copyright (C) 1996, 1997 Free Software Foundation, Inc.
4 ;;
5 ;; Author: Per Abrahamsen <abraham@dina.kvl.dk>
6 ;; Keywords: extensions
7 ;; Version: 1.50
8 ;; X-URL: http://www.dina.kvl.dk/~abraham/custom/
9
10 ;;; Commentary:
11 ;;
12 ;; See `widget.el'.
13
14 ;;; Code:
15
16 (require 'widget)
17 (require 'cl)
18 (autoload 'pp-to-string "pp")
19 (autoload 'Info-goto-node "info")
20
21 (if (string-match "XEmacs" emacs-version)
22 ;; XEmacs spell `intangible' as `atomic'.
23 (defun widget-make-intangible (from to side)
24 "Make text between FROM and TO atomic with regard to movement.
25 Third argument should be `start-open' if it should be sticky to the rear,
26 and `end-open' if it should sticky to the front."
27 (require 'atomic-extents)
28 (let ((ext (make-extent from to)))
29 ;; XEmacs doesn't understant different kinds of read-only, so
30 ;; we have to use extents instead.
31 (put-text-property from to 'read-only nil)
32 (set-extent-property ext 'read-only t)
33 (set-extent-property ext 'start-open nil)
34 (set-extent-property ext 'end-open nil)
35 (set-extent-property ext side t)
36 (set-extent-property ext 'atomic t)))
37 (defun widget-make-intangible (from to size)
38 "Make text between FROM and TO intangible."
39 (put-text-property from to 'intangible 'front)))
40
41 ;; The following should go away when bundled with Emacs.
42 (eval-and-compile
43 (condition-case ()
44 (require 'custom)
45 (error nil))
46
47 (unless (and (featurep 'custom) (fboundp 'custom-declare-variable))
48 ;; We have the old custom-library, hack around it!
49 (defmacro defgroup (&rest args) nil)
50 (defmacro defcustom (var value doc &rest args)
51 `(defvar ,var ,value ,doc))
52 (defmacro defface (&rest args) nil)
53 (define-widget-keywords :prefix :tag :load :link :options :type :group)
54 (when (fboundp 'copy-face)
55 (copy-face 'default 'widget-documentation-face)
56 (copy-face 'bold 'widget-button-face)
57 (copy-face 'italic 'widget-field-face))))
58
59 ;;; Compatibility.
60
61 (unless (fboundp 'event-point)
62 ;; XEmacs function missing in Emacs.
63 (defun event-point (event)
64 "Return the character position of the given mouse-motion, button-press,
65 or button-release event. If the event did not occur over a window, or did
66 not occur over text, then this returns nil. Otherwise, it returns an index
67 into the buffer visible in the event's window."
68 (posn-point (event-start event))))
69
70 (unless (fboundp 'error-message-string)
71 ;; Emacs function missing in XEmacs.
72 (defun error-message-string (obj)
73 "Convert an error value to an error message."
74 (let ((buf (get-buffer-create " *error-message*")))
75 (erase-buffer buf)
76 (display-error obj buf)
77 (buffer-string buf))))
78
79 ;;; Customization.
80
81 (defgroup widgets nil
82 "Customization support for the Widget Library."
83 :link '(custom-manual "(widget)Top")
84 :link '(url-link :tag "Development Page"
85 "http://www.dina.kvl.dk/~abraham/custom/")
86 :prefix "widget-"
87 :group 'extensions
88 :group 'faces
89 :group 'hypermedia)
90
91 (defface widget-documentation-face '((((class color)
92 (background dark))
93 (:foreground "lime green"))
94 (((class color)
95 (background light))
96 (:foreground "dark green"))
97 (t nil))
98 "Face used for documentation text."
99 :group 'widgets)
100
101 (defface widget-button-face '((t (:bold t)))
102 "Face used for widget buttons."
103 :group 'widgets)
104
105 (defcustom widget-mouse-face 'highlight
106 "Face used for widget buttons when the mouse is above them."
107 :type 'face
108 :group 'widgets)
109
110 (defface widget-field-face '((((class grayscale color)
111 (background light))
112 (:background "light gray"))
113 (((class grayscale color)
114 (background dark))
115 (:background "dark gray"))
116 (t
117 (:italic t)))
118 "Face used for editable fields."
119 :group 'widgets)
120
121 (defcustom widget-menu-max-size 40
122 "Largest number of items allowed in a popup-menu.
123 Larger menus are read through the minibuffer."
124 :group 'widgets
125 :type 'integer)
126
127 ;;; Utility functions.
128 ;;
129 ;; These are not really widget specific.
130
131 (defsubst widget-plist-member (plist prop)
132 ;; Return non-nil if PLIST has the property PROP.
133 ;; PLIST is a property list, which is a list of the form
134 ;; (PROP1 VALUE1 PROP2 VALUE2 ...). PROP is a symbol.
135 ;; Unlike `plist-get', this allows you to distinguish between a missing
136 ;; property and a property with the value nil.
137 ;; The value is actually the tail of PLIST whose car is PROP.
138 (while (and plist (not (eq (car plist) prop)))
139 (setq plist (cdr (cdr plist))))
140 plist)
141
142 (defun widget-princ-to-string (object)
143 ;; Return string representation of OBJECT, any Lisp object.
144 ;; No quoting characters are used; no delimiters are printed around
145 ;; the contents of strings.
146 (save-excursion
147 (set-buffer (get-buffer-create " *widget-tmp*"))
148 (erase-buffer)
149 (let ((standard-output (current-buffer)))
150 (princ object))
151 (buffer-string)))
152
153 (defun widget-clear-undo ()
154 "Clear all undo information."
155 (buffer-disable-undo (current-buffer))
156 (buffer-enable-undo))
157
158 (defun widget-choose (title items &optional event)
159 "Choose an item from a list.
160
161 First argument TITLE is the name of the list.
162 Second argument ITEMS is an alist (NAME . VALUE).
163 Optional third argument EVENT is an input event.
164
165 The user is asked to choose between each NAME from the items alist,
166 and the VALUE of the chosen element will be returned. If EVENT is a
167 mouse event, and the number of elements in items is less than
168 `widget-menu-max-size', a popup menu will be used, otherwise the
169 minibuffer."
170 (cond ((and (< (length items) widget-menu-max-size)
171 event (fboundp 'x-popup-menu) window-system)
172 ;; We are in Emacs-19, pressed by the mouse
173 (x-popup-menu event
174 (list title (cons "" items))))
175 ((and (< (length items) widget-menu-max-size)
176 event (fboundp 'popup-menu) window-system)
177 ;; We are in XEmacs, pressed by the mouse
178 (let ((val (get-popup-menu-response
179 (cons title
180 (mapcar
181 (function
182 (lambda (x)
183 (vector (car x) (list (car x)) t)))
184 items)))))
185 (setq val (and val
186 (listp (event-object val))
187 (stringp (car-safe (event-object val)))
188 (car (event-object val))))
189 (cdr (assoc val items))))
190 (t
191 (cdr (assoc (completing-read (concat title ": ")
192 items nil t)
193 items)))))
194
195 (defun widget-get-sibling (widget)
196 "Get the item WIDGET is assumed to toggle.
197 This is only meaningful for radio buttons or checkboxes in a list."
198 (let* ((parent (widget-get widget :parent))
199 (children (widget-get parent :children))
200 child)
201 (catch 'child
202 (while children
203 (setq child (car children)
204 children (cdr children))
205 (when (eq (widget-get child :button) widget)
206 (throw 'child child)))
207 nil)))
208
209 ;;; Widget text specifications.
210 ;;
211 ;; These functions are for specifying text properties.
212
213 (defun widget-specify-none (from to)
214 ;; Clear all text properties between FROM and TO.
215 (set-text-properties from to nil))
216
217 (defun widget-specify-text (from to)
218 ;; Default properties.
219 (add-text-properties from to (list 'read-only t
220 'front-sticky t
221 'start-open t
222 'end-open t
223 'rear-nonsticky nil)))
224
225 (defun widget-specify-field (widget from to)
226 ;; Specify editable button for WIDGET between FROM and TO.
227 (widget-specify-field-update widget from to)
228
229 ;; Make it possible to edit the front end of the field.
230 (add-text-properties (1- from) from (list 'rear-nonsticky t
231 'end-open t
232 'invisible t))
233 (when (or (string-match "\\(.\\|\n\\)%v" (widget-get widget :format))
234 (widget-get widget :hide-front-space))
235 ;; WARNING: This is going to lose horrible if the character just
236 ;; before the field can be modified (e.g. if it belongs to a
237 ;; choice widget). We try to compensate by checking the format
238 ;; string, and hope the user hasn't changed the :create method.
239 (widget-make-intangible (- from 2) from 'end-open))
240
241 ;; Make it possible to edit back end of the field.
242 (add-text-properties to (1+ to) (list 'front-sticky nil
243 'read-only t
244 'start-open t))
245
246 (cond ((widget-get widget :size)
247 (put-text-property to (1+ to) 'invisible t)
248 (when (or (string-match "%v\\(.\\|\n\\)" (widget-get widget :format))
249 (widget-get widget :hide-rear-space))
250 ;; WARNING: This is going to lose horrible if the character just
251 ;; after the field can be modified (e.g. if it belongs to a
252 ;; choice widget). We try to compensate by checking the format
253 ;; string, and hope the user hasn't changed the :create method.
254 (widget-make-intangible to (+ to 2) 'start-open)))
255 ((string-match "XEmacs" emacs-version)
256 ;; XEmacs does not allow you to insert before a read-only
257 ;; character, even if it is start.open.
258 ;; XEmacs does allow you to delete an read-only extent, so
259 ;; making the terminating newline read only doesn't help.
260 ;; I tried putting an invisible intangible read-only space
261 ;; before the newline, which gave really weird effects.
262 ;; So for now, we just have trust the user not to delete the
263 ;; newline.
264 (put-text-property to (1+ to) 'read-only nil))))
265
266 (defun widget-specify-field-update (widget from to)
267 ;; Specify editable button for WIDGET between FROM and TO.
268 (let ((map (widget-get widget :keymap))
269 (secret (widget-get widget :secret))
270 (secret-to to)
271 (size (widget-get widget :size))
272 (face (or (widget-get widget :value-face)
273 'widget-field-face)))
274
275 (when secret
276 (while (and size
277 (not (zerop size))
278 (> secret-to from)
279 (eq (char-after (1- secret-to)) ?\ ))
280 (setq secret-to (1- secret-to)))
281
282 (save-excursion
283 (goto-char from)
284 (while (< (point) secret-to)
285 (let ((old (get-text-property (point) 'secret)))
286 (when old
287 (subst-char-in-region (point) (1+ (point)) secret old)))
288 (forward-char))))
289
290 (set-text-properties from to (list 'field widget
291 'read-only nil
292 'keymap map
293 'local-map map
294 'face face))
295
296 (when secret
297 (save-excursion
298 (goto-char from)
299 (while (< (point) secret-to)
300 (let ((old (following-char)))
301 (subst-char-in-region (point) (1+ (point)) old secret)
302 (put-text-property (point) (1+ (point)) 'secret old))
303 (forward-char))))
304
305 (unless (widget-get widget :size)
306 (add-text-properties to (1+ to) (list 'field widget
307 'face face)))
308 (add-text-properties to (1+ to) (list 'local-map map
309 'keymap map))))
310
311 (defun widget-specify-button (widget from to)
312 ;; Specify button for WIDGET between FROM and TO.
313 (let ((face (widget-apply widget :button-face-get)))
314 (add-text-properties from to (list 'button widget
315 'mouse-face widget-mouse-face
316 'start-open t
317 'end-open t
318 'face face))))
319
320 (defun widget-specify-sample (widget from to)
321 ;; Specify sample for WIDGET between FROM and TO.
322 (let ((face (widget-apply widget :sample-face-get)))
323 (when face
324 (add-text-properties from to (list 'start-open t
325 'end-open t
326 'face face)))))
327
328 (defun widget-specify-doc (widget from to)
329 ;; Specify documentation for WIDGET between FROM and TO.
330 (add-text-properties from to (list 'widget-doc widget
331 'face 'widget-documentation-face)))
332
333 (defmacro widget-specify-insert (&rest form)
334 ;; Execute FORM without inheriting any text properties.
335 `(save-restriction
336 (let ((inhibit-read-only t)
337 result
338 after-change-functions)
339 (insert "<>")
340 (narrow-to-region (- (point) 2) (point))
341 (widget-specify-none (point-min) (point-max))
342 (goto-char (1+ (point-min)))
343 (setq result (progn ,@form))
344 (delete-region (point-min) (1+ (point-min)))
345 (delete-region (1- (point-max)) (point-max))
346 (goto-char (point-max))
347 result)))
348
349 ;;; Widget Properties.
350
351 (defsubst widget-type (widget)
352 "Return the type of WIDGET, a symbol."
353 (car widget))
354
355 (defun widget-put (widget property value)
356 "In WIDGET set PROPERTY to VALUE.
357 The value can later be retrived with `widget-get'."
358 (setcdr widget (plist-put (cdr widget) property value)))
359
360 (defun widget-get (widget property)
361 "In WIDGET, get the value of PROPERTY.
362 The value could either be specified when the widget was created, or
363 later with `widget-put'."
364 (let ((missing t)
365 value tmp)
366 (while missing
367 (cond ((setq tmp (widget-plist-member (cdr widget) property))
368 (setq value (car (cdr tmp))
369 missing nil))
370 ((setq tmp (car widget))
371 (setq widget (get tmp 'widget-type)))
372 (t
373 (setq missing nil))))
374 value))
375
376 (defun widget-member (widget property)
377 "Non-nil iff there is a definition in WIDGET for PROPERTY."
378 (cond ((widget-plist-member (cdr widget) property)
379 t)
380 ((car widget)
381 (widget-member (get (car widget) 'widget-type) property))
382 (t nil)))
383
384 (defun widget-apply (widget property &rest args)
385 "Apply the value of WIDGET's PROPERTY to the widget itself.
386 ARGS are passed as extra argments to the function."
387 (apply (widget-get widget property) widget args))
388
389 (defun widget-value (widget)
390 "Extract the current value of WIDGET."
391 (widget-apply widget
392 :value-to-external (widget-apply widget :value-get)))
393
394 (defun widget-value-set (widget value)
395 "Set the current value of WIDGET to VALUE."
396 (widget-apply widget
397 :value-set (widget-apply widget
398 :value-to-internal value)))
399
400 (defun widget-match-inline (widget vals)
401 ;; In WIDGET, match the start of VALS.
402 (cond ((widget-get widget :inline)
403 (widget-apply widget :match-inline vals))
404 ((and vals
405 (widget-apply widget :match (car vals)))
406 (cons (list (car vals)) (cdr vals)))
407 (t nil)))
408
409 ;;; Glyphs.
410
411 (defcustom widget-glyph-directory (concat data-directory "custom/")
412 "Where widget glyphs are located.
413 If this variable is nil, widget will try to locate the directory
414 automatically. This does not work yet."
415 :group 'widgets
416 :type 'directory)
417
418 (defcustom widget-glyph-enable t
419 "If non nil, use glyphs in images when available."
420 :group 'widgets
421 :type 'boolean)
422
423 (defun widget-glyph-insert (widget tag image)
424 "In WIDGET, insert the text TAG or, if supported, IMAGE.
425 IMAGE should be a name sans extension of an xpm or xbm file located in
426 `widget-glyph-directory'"
427 (if (and (string-match "XEmacs" emacs-version)
428 widget-glyph-enable
429 (fboundp 'make-glyph)
430 image)
431 (let ((file (concat widget-glyph-directory
432 (if (string-match "/\\'" widget-glyph-directory)
433 ""
434 "/")
435 image
436 (if (featurep 'xpm) ".xpm" ".xbm"))))
437 (if (file-readable-p file)
438 (widget-glyph-insert-glyph widget tag (make-glyph file))
439 ;; File not readable, give up.
440 (insert tag)))
441 ;; We don't want or can't use glyphs.
442 (insert tag)))
443
444 (defun widget-glyph-insert-glyph (widget tag glyph)
445 "In WIDGET, with alternative text TAG, insert GLYPH."
446 (set-glyph-image glyph (cons 'tty tag))
447 (set-glyph-property glyph 'widget widget)
448 (insert "*")
449 (add-text-properties (1- (point)) (point)
450 (list 'invisible t
451 'end-glyph glyph)))
452
453 ;;; Creating Widgets.
454
455 ;;;###autoload
456 (defun widget-create (type &rest args)
457 "Create widget of TYPE.
458 The optional ARGS are additional keyword arguments."
459 (let ((widget (apply 'widget-convert type args)))
460 (widget-apply widget :create)
461 widget))
462
463 (defun widget-create-child-and-convert (parent type &rest args)
464 "As part of the widget PARENT, create a child widget TYPE.
465 The child is converted, using the keyword arguments ARGS."
466 (let ((widget (apply 'widget-convert type args)))
467 (widget-put widget :parent parent)
468 (unless (widget-get widget :indent)
469 (widget-put widget :indent (+ (or (widget-get parent :indent) 0)
470 (or (widget-get widget :extra-offset) 0)
471 (widget-get parent :offset))))
472 (widget-apply widget :create)
473 widget))
474
475 (defun widget-create-child (parent type)
476 "Create widget of TYPE."
477 (let ((widget (copy-list type)))
478 (widget-put widget :parent parent)
479 (unless (widget-get widget :indent)
480 (widget-put widget :indent (+ (or (widget-get parent :indent) 0)
481 (or (widget-get widget :extra-offset) 0)
482 (widget-get parent :offset))))
483 (widget-apply widget :create)
484 widget))
485
486 (defun widget-create-child-value (parent type value)
487 "Create widget of TYPE with value VALUE."
488 (let ((widget (copy-list type)))
489 (widget-put widget :value (widget-apply widget :value-to-internal value))
490 (widget-put widget :parent parent)
491 (unless (widget-get widget :indent)
492 (widget-put widget :indent (+ (or (widget-get parent :indent) 0)
493 (or (widget-get widget :extra-offset) 0)
494 (widget-get parent :offset))))
495 (widget-apply widget :create)
496 widget))
497
498 ;;;###autoload
499 (defun widget-delete (widget)
500 "Delete WIDGET."
501 (widget-apply widget :delete))
502
503 (defun widget-convert (type &rest args)
504 "Convert TYPE to a widget without inserting it in the buffer.
505 The optional ARGS are additional keyword arguments."
506 ;; Don't touch the type.
507 (let* ((widget (if (symbolp type)
508 (list type)
509 (copy-list type)))
510 (current widget)
511 (keys args))
512 ;; First set the :args keyword.
513 (while (cdr current) ;Look in the type.
514 (let ((next (car (cdr current))))
515 (if (and (symbolp next) (eq (aref (symbol-name next) 0) ?:))
516 (setq current (cdr (cdr current)))
517 (setcdr current (list :args (cdr current)))
518 (setq current nil))))
519 (while args ;Look in the args.
520 (let ((next (nth 0 args)))
521 (if (and (symbolp next) (eq (aref (symbol-name next) 0) ?:))
522 (setq args (nthcdr 2 args))
523 (widget-put widget :args args)
524 (setq args nil))))
525 ;; Then Convert the widget.
526 (setq type widget)
527 (while type
528 (let ((convert-widget (plist-get (cdr type) :convert-widget)))
529 (if convert-widget
530 (setq widget (funcall convert-widget widget))))
531 (setq type (get (car type) 'widget-type)))
532 ;; Finally set the keyword args.
533 (while keys
534 (let ((next (nth 0 keys)))
535 (if (and (symbolp next) (eq (aref (symbol-name next) 0) ?:))
536 (progn
537 (widget-put widget next (nth 1 keys))
538 (setq keys (nthcdr 2 keys)))
539 (setq keys nil))))
540 ;; Convert the :value to internal format.
541 (if (widget-member widget :value)
542 (let ((value (widget-get widget :value)))
543 (widget-put widget
544 :value (widget-apply widget :value-to-internal value))))
545 ;; Return the newly create widget.
546 widget))
547
548 (defun widget-insert (&rest args)
549 "Call `insert' with ARGS and make the text read only."
550 (let ((inhibit-read-only t)
551 after-change-functions
552 (from (point)))
553 (apply 'insert args)
554 (widget-specify-text from (point))))
555
556 ;;; Keymap and Comands.
557
558 (defvar widget-keymap nil
559 "Keymap containing useful binding for buffers containing widgets.
560 Recommended as a parent keymap for modes using widgets.")
561
562 (unless widget-keymap
563 (setq widget-keymap (make-sparse-keymap))
564 (define-key widget-keymap "\C-k" 'widget-kill-line)
565 (define-key widget-keymap "\t" 'widget-forward)
566 (define-key widget-keymap "\M-\t" 'widget-backward)
567 (define-key widget-keymap [(shift tab)] 'widget-backward)
568 (define-key widget-keymap [backtab] 'widget-backward)
569 (if (string-match "XEmacs" (emacs-version))
570 (progn
571 (define-key widget-keymap [button2] 'widget-button-click)
572 (define-key widget-keymap [button1] 'widget-button1-click))
573 (define-key widget-keymap [mouse-2] 'ignore)
574 (define-key widget-keymap [down-mouse-2] 'widget-button-click))
575 (define-key widget-keymap "\C-m" 'widget-button-press))
576
577 (defvar widget-global-map global-map
578 "Keymap used for events the widget does not handle themselves.")
579 (make-variable-buffer-local 'widget-global-map)
580
581 (defvar widget-field-keymap nil
582 "Keymap used inside an editable field.")
583
584 (unless widget-field-keymap
585 (setq widget-field-keymap (copy-keymap widget-keymap))
586 (unless (string-match "XEmacs" (emacs-version))
587 (define-key widget-field-keymap [menu-bar] 'nil))
588 (define-key widget-field-keymap "\C-m" 'widget-field-activate)
589 (define-key widget-field-keymap "\C-a" 'widget-beginning-of-line)
590 (define-key widget-field-keymap "\C-e" 'widget-end-of-line)
591 (set-keymap-parent widget-field-keymap global-map))
592
593 (defvar widget-text-keymap nil
594 "Keymap used inside a text field.")
595
596 (unless widget-text-keymap
597 (setq widget-text-keymap (copy-keymap widget-keymap))
598 (unless (string-match "XEmacs" (emacs-version))
599 (define-key widget-text-keymap [menu-bar] 'nil))
600 (define-key widget-text-keymap "\C-a" 'widget-beginning-of-line)
601 (define-key widget-text-keymap "\C-e" 'widget-end-of-line)
602 (set-keymap-parent widget-text-keymap global-map))
603
604 (defun widget-field-activate (pos &optional event)
605 "Activate the ediable field at point."
606 (interactive "@d")
607 (let ((field (get-text-property pos 'field)))
608 (if field
609 (widget-apply field :action event)
610 (call-interactively
611 (lookup-key widget-global-map (this-command-keys))))))
612
613 (defun widget-button-click (event)
614 "Activate button below mouse pointer."
615 (interactive "@e")
616 (cond ((and (fboundp 'event-glyph)
617 (event-glyph event))
618 (let ((widget (glyph-property (event-glyph event) 'widget)))
619 (if widget
620 (widget-apply widget :action event)
621 (message "You clicked on a glyph."))))
622 ((event-point event)
623 (let ((button (get-text-property (event-point event) 'button)))
624 (if button
625 (widget-apply button :action event)
626 (call-interactively
627 (or (lookup-key widget-global-map [ button2 ])
628 (lookup-key widget-global-map [ down-mouse-2 ])
629 (lookup-key widget-global-map [ mouse-2]))))))
630 (t
631 (message "You clicked somewhere weird."))))
632
633 (defun widget-button1-click (event)
634 "Activate glyph below mouse pointer."
635 (interactive "@e")
636 (if (and (fboundp 'event-glyph)
637 (event-glyph event))
638 (let ((widget (glyph-property (event-glyph event) 'widget)))
639 (if widget
640 (widget-apply widget :action event)
641 (message "You clicked on a glyph.")))
642 (call-interactively (lookup-key widget-global-map (this-command-keys)))))
643
644 (defun widget-button-press (pos &optional event)
645 "Activate button at POS."
646 (interactive "@d")
647 (let ((button (get-text-property pos 'button)))
648 (if button
649 (widget-apply button :action event)
650 (let ((command (lookup-key widget-global-map (this-command-keys))))
651 (when (commandp command)
652 (call-interactively command))))))
653
654 (defun widget-move (arg)
655 "Move point to the ARG next field or button.
656 ARG may be negative to move backward."
657 (while (> arg 0)
658 (setq arg (1- arg))
659 (let ((next (cond ((get-text-property (point) 'button)
660 (next-single-property-change (point) 'button))
661 ((get-text-property (point) 'field)
662 (next-single-property-change (point) 'field))
663 (t
664 (point)))))
665 (if (null next) ; Widget extends to end. of buffer
666 (setq next (point-min)))
667 (let ((button (next-single-property-change next 'button))
668 (field (next-single-property-change next 'field)))
669 (cond ((or (get-text-property next 'button)
670 (get-text-property next 'field))
671 (goto-char next))
672 ((and button field)
673 (goto-char (min button field)))
674 (button (goto-char button))
675 (field (goto-char field))
676 (t
677 (let ((button (next-single-property-change (point-min) 'button))
678 (field (next-single-property-change (point-min) 'field)))
679 (cond ((and button field) (goto-char (min button field)))
680 (button (goto-char button))
681 (field (goto-char field))
682 (t
683 (error "No buttons or fields found")))))))))
684 (while (< arg 0)
685 (if (= (point-min) (point))
686 (forward-char 1))
687 (setq arg (1+ arg))
688 (let ((previous (cond ((get-text-property (1- (point)) 'button)
689 (previous-single-property-change (point) 'button))
690 ((get-text-property (1- (point)) 'field)
691 (previous-single-property-change (point) 'field))
692 (t
693 (point)))))
694 (if (null previous) ; Widget extends to beg. of buffer
695 (setq previous (point-max)))
696 (let ((button (previous-single-property-change previous 'button))
697 (field (previous-single-property-change previous 'field)))
698 (cond ((and button field)
699 (goto-char (max button field)))
700 (button (goto-char button))
701 (field (goto-char field))
702 (t
703 (let ((button (previous-single-property-change
704 (point-max) 'button))
705 (field (previous-single-property-change
706 (point-max) 'field)))
707 (cond ((and button field) (goto-char (max button field)))
708 (button (goto-char button))
709 (field (goto-char field))
710 (t
711 (error "No buttons or fields found"))))))))
712 (let ((button (previous-single-property-change (point) 'button))
713 (field (previous-single-property-change (point) 'field)))
714 (cond ((and button field)
715 (goto-char (max button field)))
716 (button (goto-char button))
717 (field (goto-char field)))))
718 (widget-echo-help (point))
719 (run-hooks 'widget-move-hook))
720
721 (defun widget-forward (arg)
722 "Move point to the next field or button.
723 With optional ARG, move across that many fields."
724 (interactive "p")
725 (run-hooks 'widget-forward-hook)
726 (widget-move arg))
727
728 (defun widget-backward (arg)
729 "Move point to the previous field or button.
730 With optional ARG, move across that many fields."
731 (interactive "p")
732 (run-hooks 'widget-backward-hook)
733 (widget-move (- arg)))
734
735 (defun widget-beginning-of-line ()
736 "Go to beginning of field or beginning of line, whichever is first."
737 (interactive)
738 (let ((bol (save-excursion (beginning-of-line) (point)))
739 (prev (previous-single-property-change (point) 'field)))
740 (goto-char (max bol (or prev bol)))))
741
742 (defun widget-end-of-line ()
743 "Go to end of field or end of line, whichever is first."
744 (interactive)
745 (let ((bol (save-excursion (end-of-line) (point)))
746 (prev (next-single-property-change (point) 'field)))
747 (goto-char (min bol (or prev bol)))))
748
749 (defun widget-kill-line ()
750 "Kill to end of field or end of line, whichever is first."
751 (interactive)
752 (let ((field (get-text-property (point) 'field))
753 (newline (save-excursion (search-forward "\n")))
754 (next (next-single-property-change (point) 'field)))
755 (if (and field (> newline next))
756 (kill-region (point) next)
757 (call-interactively 'kill-line))))
758
759 ;;; Setting up the buffer.
760
761 (defvar widget-field-new nil)
762 ;; List of all newly created editable fields in the buffer.
763 (make-variable-buffer-local 'widget-field-new)
764
765 (defvar widget-field-list nil)
766 ;; List of all editable fields in the buffer.
767 (make-variable-buffer-local 'widget-field-list)
768
769 (defun widget-setup ()
770 "Setup current buffer so editing string widgets works."
771 (let ((inhibit-read-only t)
772 (after-change-functions nil)
773 field)
774 (while widget-field-new
775 (setq field (car widget-field-new)
776 widget-field-new (cdr widget-field-new)
777 widget-field-list (cons field widget-field-list))
778 (let ((from (widget-get field :value-from))
779 (to (widget-get field :value-to)))
780 (widget-specify-field field from to)
781 (move-marker from (1- from))
782 (move-marker to (1+ to)))))
783 (widget-clear-undo)
784 ;; We need to maintain text properties and size of the editing fields.
785 (make-local-variable 'after-change-functions)
786 (if widget-field-list
787 (setq after-change-functions '(widget-after-change))
788 (setq after-change-functions nil)))
789
790 (defvar widget-field-last nil)
791 ;; Last field containing point.
792 (make-variable-buffer-local 'widget-field-last)
793
794 (defvar widget-field-was nil)
795 ;; The widget data before the change.
796 (make-variable-buffer-local 'widget-field-was)
797
798 (defun widget-field-find (pos)
799 ;; Find widget whose editing field is located at POS.
800 ;; Return nil if POS is not inside and editing field.
801 ;;
802 ;; This is only used in `widget-field-modified', since ordinarily
803 ;; you would just test the field property.
804 (let ((fields widget-field-list)
805 field found)
806 (while fields
807 (setq field (car fields)
808 fields (cdr fields))
809 (let ((from (widget-get field :value-from))
810 (to (widget-get field :value-to)))
811 (if (and from to (< from pos) (> to pos))
812 (setq fields nil
813 found field))))
814 found))
815
816 (defun widget-after-change (from to old)
817 ;; Adjust field size and text properties.
818 (condition-case nil
819 (let ((field (widget-field-find from))
820 (inhibit-read-only t))
821 (cond ((null field))
822 ((not (eq field (widget-field-find to)))
823 (debug)
824 (message "Error: `widget-after-change' called on two fields"))
825 (t
826 (let ((size (widget-get field :size)))
827 (if size
828 (let ((begin (1+ (widget-get field :value-from)))
829 (end (1- (widget-get field :value-to))))
830 (widget-specify-field-update field begin end)
831 (cond ((< (- end begin) size)
832 ;; Field too small.
833 (save-excursion
834 (goto-char end)
835 (insert-char ?\ (- (+ begin size) end))
836 (widget-specify-field-update field
837 begin
838 (+ begin size))))
839 ((> (- end begin) size)
840 ;; Field too large and
841 (if (or (< (point) (+ begin size))
842 (> (point) end))
843 ;; Point is outside extra space.
844 (setq begin (+ begin size))
845 ;; Point is within the extra space.
846 (setq begin (point)))
847 (save-excursion
848 (goto-char end)
849 (while (and (eq (preceding-char) ?\ )
850 (> (point) begin))
851 (delete-backward-char 1))))))
852 (widget-specify-field-update field from to)))
853 (widget-apply field :notify field))))
854 (error (debug))))
855
856 ;;; Widget Functions
857 ;;
858 ;; These functions are used in the definition of multiple widgets.
859
860 (defun widget-children-value-delete (widget)
861 "Delete all :children and :buttons in WIDGET."
862 (mapcar 'widget-delete (widget-get widget :children))
863 (widget-put widget :children nil)
864 (mapcar 'widget-delete (widget-get widget :buttons))
865 (widget-put widget :buttons nil))
866
867 (defun widget-types-convert-widget (widget)
868 "Convert :args as widget types in WIDGET."
869 (widget-put widget :args (mapcar 'widget-convert (widget-get widget :args)))
870 widget)
871
872 ;;; The `default' Widget.
873
874 (define-widget 'default nil
875 "Basic widget other widgets are derived from."
876 :value-to-internal (lambda (widget value) value)
877 :value-to-external (lambda (widget value) value)
878 :create 'widget-default-create
879 :indent nil
880 :offset 0
881 :format-handler 'widget-default-format-handler
882 :button-face-get 'widget-default-button-face-get
883 :sample-face-get 'widget-default-sample-face-get
884 :delete 'widget-default-delete
885 :value-set 'widget-default-value-set
886 :value-inline 'widget-default-value-inline
887 :menu-tag-get 'widget-default-menu-tag-get
888 :validate (lambda (widget) nil)
889 :action 'widget-default-action
890 :notify 'widget-default-notify)
891
892 (defun widget-default-create (widget)
893 "Create WIDGET at point in the current buffer."
894 (widget-specify-insert
895 (let ((from (point))
896 (tag (widget-get widget :tag))
897 (glyph (widget-get widget :tag-glyph))
898 (doc (widget-get widget :doc))
899 button-begin button-end
900 sample-begin sample-end
901 doc-begin doc-end
902 value-pos)
903 (insert (widget-get widget :format))
904 (goto-char from)
905 ;; Parse escapes in format.
906 (while (re-search-forward "%\\(.\\)" nil t)
907 (let ((escape (aref (match-string 1) 0)))
908 (replace-match "" t t)
909 (cond ((eq escape ?%)
910 (insert "%"))
911 ((eq escape ?\[)
912 (setq button-begin (point)))
913 ((eq escape ?\])
914 (setq button-end (point)))
915 ((eq escape ?\{)
916 (setq sample-begin (point)))
917 ((eq escape ?\})
918 (setq sample-end (point)))
919 ((eq escape ?n)
920 (when (widget-get widget :indent)
921 (insert "\n")
922 (insert-char ? (widget-get widget :indent))))
923 ((eq escape ?t)
924 (cond (glyph
925 (widget-glyph-insert widget (or tag "image") glyph))
926 (tag
927 (insert tag))
928 (t
929 (let ((standard-output (current-buffer)))
930 (princ (widget-get widget :value))))))
931 ((eq escape ?d)
932 (when doc
933 (setq doc-begin (point))
934 (insert doc)
935 (while (eq (preceding-char) ?\n)
936 (delete-backward-char 1))
937 (insert "\n")
938 (setq doc-end (point))))
939 ((eq escape ?v)
940 (if (and button-begin (not button-end))
941 (widget-apply widget :value-create)
942 (setq value-pos (point))))
943 (t
944 (widget-apply widget :format-handler escape)))))
945 ;; Specify button, sample, and doc, and insert value.
946 (and button-begin button-end
947 (widget-specify-button widget button-begin button-end))
948 (and sample-begin sample-end
949 (widget-specify-sample widget sample-begin sample-end))
950 (and doc-begin doc-end
951 (widget-specify-doc widget doc-begin doc-end))
952 (when value-pos
953 (goto-char value-pos)
954 (widget-apply widget :value-create)))
955 (let ((from (copy-marker (point-min)))
956 (to (copy-marker (point-max))))
957 (widget-specify-text from to)
958 (set-marker-insertion-type from t)
959 (set-marker-insertion-type to nil)
960 (widget-put widget :from from)
961 (widget-put widget :to to))))
962
963 (defun widget-default-format-handler (widget escape)
964 ;; We recognize the %h escape by default.
965 (let* ((buttons (widget-get widget :buttons))
966 (doc-property (widget-get widget :documentation-property))
967 (doc-try (cond ((widget-get widget :doc))
968 ((symbolp doc-property)
969 (documentation-property (widget-get widget :value)
970 doc-property))
971 (t
972 (funcall doc-property (widget-get widget :value)))))
973 (doc-text (and (stringp doc-try)
974 (> (length doc-try) 1)
975 doc-try)))
976 (cond ((eq escape ?h)
977 (when doc-text
978 (and (eq (preceding-char) ?\n)
979 (widget-get widget :indent)
980 (insert-char ? (widget-get widget :indent)))
981 ;; The `*' in the beginning is redundant.
982 (when (eq (aref doc-text 0) ?*)
983 (setq doc-text (substring doc-text 1)))
984 ;; Get rid of trailing newlines.
985 (when (string-match "\n+\\'" doc-text)
986 (setq doc-text (substring doc-text 0 (match-beginning 0))))
987 (push (if (string-match "\n." doc-text)
988 ;; Allow multiline doc to be hiden.
989 (widget-create-child-and-convert
990 widget 'widget-help
991 :doc (progn
992 (string-match "\\`.*" doc-text)
993 (match-string 0 doc-text))
994 :widget-doc doc-text
995 "?")
996 ;; A single line is just inserted.
997 (widget-create-child-and-convert
998 widget 'item :format "%d" :doc doc-text nil))
999 buttons)))
1000 (t
1001 (error "Unknown escape `%c'" escape)))
1002 (widget-put widget :buttons buttons)))
1003
1004 (defun widget-default-button-face-get (widget)
1005 ;; Use :button-face or widget-button-face
1006 (or (widget-get widget :button-face) 'widget-button-face))
1007
1008 (defun widget-default-sample-face-get (widget)
1009 ;; Use :sample-face.
1010 (widget-get widget :sample-face))
1011
1012 (defun widget-default-delete (widget)
1013 ;; Remove widget from the buffer.
1014 (let ((from (widget-get widget :from))
1015 (to (widget-get widget :to))
1016 (inhibit-read-only t)
1017 after-change-functions)
1018 (widget-apply widget :value-delete)
1019 (delete-region from to)
1020 (set-marker from nil)
1021 (set-marker to nil)))
1022
1023 (defun widget-default-value-set (widget value)
1024 ;; Recreate widget with new value.
1025 (save-excursion
1026 (goto-char (widget-get widget :from))
1027 (widget-apply widget :delete)
1028 (widget-put widget :value value)
1029 (widget-apply widget :create)))
1030
1031 (defun widget-default-value-inline (widget)
1032 ;; Wrap value in a list unless it is inline.
1033 (if (widget-get widget :inline)
1034 (widget-value widget)
1035 (list (widget-value widget))))
1036
1037 (defun widget-default-menu-tag-get (widget)
1038 ;; Use tag or value for menus.
1039 (or (widget-get widget :menu-tag)
1040 (widget-get widget :tag)
1041 (widget-princ-to-string (widget-get widget :value))))
1042
1043 (defun widget-default-action (widget &optional event)
1044 ;; Notify the parent when a widget change
1045 (let ((parent (widget-get widget :parent)))
1046 (when parent
1047 (widget-apply parent :notify widget event))))
1048
1049 (defun widget-default-notify (widget child &optional event)
1050 ;; Pass notification to parent.
1051 (widget-default-action widget event))
1052
1053 ;;; The `item' Widget.
1054
1055 (define-widget 'item 'default
1056 "Constant items for inclusion in other widgets."
1057 :convert-widget 'widget-item-convert-widget
1058 :value-create 'widget-item-value-create
1059 :value-delete 'ignore
1060 :value-get 'widget-item-value-get
1061 :match 'widget-item-match
1062 :match-inline 'widget-item-match-inline
1063 :action 'widget-item-action
1064 :format "%t\n")
1065
1066 (defun widget-item-convert-widget (widget)
1067 ;; Initialize :value from :args in WIDGET.
1068 (let ((args (widget-get widget :args)))
1069 (when args
1070 (widget-put widget :value (widget-apply widget
1071 :value-to-internal (car args)))
1072 (widget-put widget :args nil)))
1073 widget)
1074
1075 (defun widget-item-value-create (widget)
1076 ;; Insert the printed representation of the value.
1077 (let ((standard-output (current-buffer)))
1078 (princ (widget-get widget :value))))
1079
1080 (defun widget-item-match (widget value)
1081 ;; Match if the value is the same.
1082 (equal (widget-get widget :value) value))
1083
1084 (defun widget-item-match-inline (widget values)
1085 ;; Match if the value is the same.
1086 (let ((value (widget-get widget :value)))
1087 (and (listp value)
1088 (<= (length value) (length values))
1089 (let ((head (subseq values 0 (length value))))
1090 (and (equal head value)
1091 (cons head (subseq values (length value))))))))
1092
1093 (defun widget-item-action (widget &optional event)
1094 ;; Just notify itself.
1095 (widget-apply widget :notify widget event))
1096
1097 (defun widget-item-value-get (widget)
1098 ;; Items are simple.
1099 (widget-get widget :value))
1100
1101 ;;; The `push-button' Widget.
1102
1103 (defcustom widget-push-button-gui t
1104 "If non nil, use GUI push buttons when available."
1105 :group 'widgets
1106 :type 'boolean)
1107
1108 ;; Cache already created GUI objects.
1109 (defvar widget-push-button-cache nil)
1110
1111 (define-widget 'push-button 'item
1112 "A pushable button."
1113 :value-create 'widget-push-button-value-create
1114 :format "%[%v%]")
1115
1116 (defun widget-push-button-value-create (widget)
1117 ;; Insert text representing the `on' and `off' states.
1118 (let* ((tag (or (widget-get widget :tag)
1119 (widget-get widget :value)))
1120 (text (concat "[" tag "]"))
1121 (gui (cdr (assoc tag widget-push-button-cache))))
1122 (if (and (fboundp 'make-gui-button)
1123 (fboundp 'make-glyph)
1124 widget-push-button-gui
1125 (fboundp 'device-on-window-system-p)
1126 (device-on-window-system-p)
1127 (string-match "XEmacs" emacs-version))
1128 (progn
1129 (unless gui
1130 (setq gui (make-gui-button tag 'widget-gui-action widget))
1131 (push (cons tag gui) widget-push-button-cache))
1132 (widget-glyph-insert-glyph widget text
1133 (make-glyph (car (aref gui 1)))))
1134 (insert text))))
1135
1136 (defun widget-gui-action (widget)
1137 "Apply :action for WIDGET."
1138 (widget-apply widget :action (this-command-keys)))
1139
1140 ;;; The `link' Widget.
1141
1142 (define-widget 'link 'item
1143 "An embedded link."
1144 :help-echo "Push me to follow the link."
1145 :format "%[_%t_%]")
1146
1147 ;;; The `info-link' Widget.
1148
1149 (define-widget 'info-link 'link
1150 "A link to an info file."
1151 :action 'widget-info-link-action)
1152
1153 (defun widget-info-link-action (widget &optional event)
1154 "Open the info node specified by WIDGET."
1155 (Info-goto-node (widget-value widget)))
1156
1157 ;;; The `url-link' Widget.
1158
1159 (define-widget 'url-link 'link
1160 "A link to an www page."
1161 :action 'widget-url-link-action)
1162
1163 (defun widget-url-link-action (widget &optional event)
1164 "Open the url specified by WIDGET."
1165 (require 'browse-url)
1166 (funcall browse-url-browser-function (widget-value widget)))
1167
1168 ;;; The `editable-field' Widget.
1169
1170 (define-widget 'editable-field 'default
1171 "An editable text field."
1172 :convert-widget 'widget-item-convert-widget
1173 :keymap widget-field-keymap
1174 :format "%v"
1175 :value ""
1176 :action 'widget-field-action
1177 :validate 'widget-field-validate
1178 :valid-regexp ""
1179 :error "No match"
1180 :value-create 'widget-field-value-create
1181 :value-delete 'widget-field-value-delete
1182 :value-get 'widget-field-value-get
1183 :match 'widget-field-match)
1184
1185 ;; History of field minibuffer edits.
1186 (defvar widget-field-history nil)
1187
1188 (defun widget-field-action (widget &optional event)
1189 ;; Edit the value in the minibuffer.
1190 (let ((tag (widget-apply widget :menu-tag-get))
1191 (invalid (widget-apply widget :validate)))
1192 (when invalid
1193 (error (widget-get invalid :error)))
1194 (widget-value-set widget
1195 (widget-apply widget
1196 :value-to-external
1197 (read-string (concat tag ": ")
1198 (widget-apply
1199 widget
1200 :value-to-internal
1201 (widget-value widget))
1202 'widget-field-history)))
1203 (widget-apply widget :notify widget event)
1204 (widget-setup)))
1205
1206 (defun widget-field-validate (widget)
1207 ;; Valid if the content matches `:valid-regexp'.
1208 (save-excursion
1209 (let ((value (widget-apply widget :value-get))
1210 (regexp (widget-get widget :valid-regexp)))
1211 (if (string-match regexp value)
1212 nil
1213 widget))))
1214
1215 (defun widget-field-value-create (widget)
1216 ;; Create an editable text field.
1217 (insert " ")
1218 (let ((size (widget-get widget :size))
1219 (value (widget-get widget :value))
1220 (from (point)))
1221 (insert value)
1222 (and size
1223 (< (length value) size)
1224 (insert-char ?\ (- size (length value))))
1225 (unless (memq widget widget-field-list)
1226 (setq widget-field-new (cons widget widget-field-new)))
1227 (widget-put widget :value-to (copy-marker (point)))
1228 (set-marker-insertion-type (widget-get widget :value-to) nil)
1229 (if (null size)
1230 (insert ?\n)
1231 (insert ?\ ))
1232 (widget-put widget :value-from (copy-marker from))
1233 (set-marker-insertion-type (widget-get widget :value-from) t)))
1234
1235 (defun widget-field-value-delete (widget)
1236 ;; Remove the widget from the list of active editing fields.
1237 (setq widget-field-list (delq widget widget-field-list))
1238 ;; These are nil if the :format string doesn't contain `%v'.
1239 (when (widget-get widget :value-from)
1240 (set-marker (widget-get widget :value-from) nil))
1241 (when (widget-get widget :value-from)
1242 (set-marker (widget-get widget :value-to) nil)))
1243
1244 (defun widget-field-value-get (widget)
1245 ;; Return current text in editing field.
1246 (let ((from (widget-get widget :value-from))
1247 (to (widget-get widget :value-to))
1248 (size (widget-get widget :size))
1249 (secret (widget-get widget :secret))
1250 (old (current-buffer)))
1251 (if (and from to)
1252 (progn
1253 (set-buffer (marker-buffer from))
1254 (setq from (1+ from)
1255 to (1- to))
1256 (while (and size
1257 (not (zerop size))
1258 (> to from)
1259 (eq (char-after (1- to)) ?\ ))
1260 (setq to (1- to)))
1261 (let ((result (buffer-substring-no-properties from to)))
1262 (when secret
1263 (let ((index 0))
1264 (while (< (+ from index) to)
1265 (aset result index
1266 (get-text-property (+ from index) 'secret))
1267 (setq index (1+ index)))))
1268 (set-buffer old)
1269 result))
1270 (widget-get widget :value))))
1271
1272 (defun widget-field-match (widget value)
1273 ;; Match any string.
1274 (stringp value))
1275
1276 ;;; The `text' Widget.
1277
1278 (define-widget 'text 'editable-field
1279 :keymap widget-text-keymap
1280 "A multiline text area.")
1281
1282 ;;; The `menu-choice' Widget.
1283
1284 (define-widget 'menu-choice 'default
1285 "A menu of options."
1286 :convert-widget 'widget-types-convert-widget
1287 :format "%[%t%]: %v"
1288 :case-fold t
1289 :tag "choice"
1290 :void '(item :format "invalid (%t)\n")
1291 :value-create 'widget-choice-value-create
1292 :value-delete 'widget-children-value-delete
1293 :value-get 'widget-choice-value-get
1294 :value-inline 'widget-choice-value-inline
1295 :action 'widget-choice-action
1296 :error "Make a choice"
1297 :validate 'widget-choice-validate
1298 :match 'widget-choice-match
1299 :match-inline 'widget-choice-match-inline)
1300
1301 (defun widget-choice-value-create (widget)
1302 ;; Insert the first choice that matches the value.
1303 (let ((value (widget-get widget :value))
1304 (args (widget-get widget :args))
1305 current)
1306 (while args
1307 (setq current (car args)
1308 args (cdr args))
1309 (when (widget-apply current :match value)
1310 (widget-put widget :children (list (widget-create-child-value
1311 widget current value)))
1312 (widget-put widget :choice current)
1313 (setq args nil
1314 current nil)))
1315 (when current
1316 (let ((void (widget-get widget :void)))
1317 (widget-put widget :children (list (widget-create-child-and-convert
1318 widget void :value value)))
1319 (widget-put widget :choice void)))))
1320
1321 (defun widget-choice-value-get (widget)
1322 ;; Get value of the child widget.
1323 (widget-value (car (widget-get widget :children))))
1324
1325 (defun widget-choice-value-inline (widget)
1326 ;; Get value of the child widget.
1327 (widget-apply (car (widget-get widget :children)) :value-inline))
1328
1329 (defun widget-choice-action (widget &optional event)
1330 ;; Make a choice.
1331 (let ((args (widget-get widget :args))
1332 (old (widget-get widget :choice))
1333 (tag (widget-apply widget :menu-tag-get))
1334 (completion-ignore-case (widget-get widget :case-fold))
1335 current choices)
1336 ;; Remember old value.
1337 (if (and old (not (widget-apply widget :validate)))
1338 (let* ((external (widget-value widget))
1339 (internal (widget-apply old :value-to-internal external)))
1340 (widget-put old :value internal)))
1341 ;; Find new choice.
1342 (setq current
1343 (cond ((= (length args) 0)
1344 nil)
1345 ((= (length args) 1)
1346 (nth 0 args))
1347 ((and (= (length args) 2)
1348 (memq old args))
1349 (if (eq old (nth 0 args))
1350 (nth 1 args)
1351 (nth 0 args)))
1352 (t
1353 (while args
1354 (setq current (car args)
1355 args (cdr args))
1356 (setq choices
1357 (cons (cons (widget-apply current :menu-tag-get)
1358 current)
1359 choices)))
1360 (widget-choose tag (reverse choices) event))))
1361 (when current
1362 (widget-value-set widget
1363 (widget-apply current :value-to-external
1364 (widget-get current :value)))
1365 (widget-apply widget :notify widget event)
1366 (widget-setup)))
1367 ;; Notify parent.
1368 (widget-apply widget :notify widget event)
1369 (widget-clear-undo))
1370
1371 (defun widget-choice-validate (widget)
1372 ;; Valid if we have made a valid choice.
1373 (let ((void (widget-get widget :void))
1374 (choice (widget-get widget :choice))
1375 (child (car (widget-get widget :children))))
1376 (if (eq void choice)
1377 widget
1378 (widget-apply child :validate))))
1379
1380 (defun widget-choice-match (widget value)
1381 ;; Matches if one of the choices matches.
1382 (let ((args (widget-get widget :args))
1383 current found)
1384 (while (and args (not found))
1385 (setq current (car args)
1386 args (cdr args)
1387 found (widget-apply current :match value)))
1388 found))
1389
1390 (defun widget-choice-match-inline (widget values)
1391 ;; Matches if one of the choices matches.
1392 (let ((args (widget-get widget :args))
1393 current found)
1394 (while (and args (null found))
1395 (setq current (car args)
1396 args (cdr args)
1397 found (widget-match-inline current values)))
1398 found))
1399
1400 ;;; The `toggle' Widget.
1401
1402 (define-widget 'toggle 'item
1403 "Toggle between two states."
1404 :format "%[%v%]\n"
1405 :value-create 'widget-toggle-value-create
1406 :action 'widget-toggle-action
1407 :match (lambda (widget value) t)
1408 :on "on"
1409 :off "off")
1410
1411 (defun widget-toggle-value-create (widget)
1412 ;; Insert text representing the `on' and `off' states.
1413 (if (widget-value widget)
1414 (widget-glyph-insert widget
1415 (widget-get widget :on)
1416 (widget-get widget :on-glyph))
1417 (widget-glyph-insert widget
1418 (widget-get widget :off)
1419 (widget-get widget :off-glyph))))
1420
1421 (defun widget-toggle-action (widget &optional event)
1422 ;; Toggle value.
1423 (widget-value-set widget (not (widget-value widget)))
1424 (widget-apply widget :notify widget event))
1425
1426 ;;; The `checkbox' Widget.
1427
1428 (define-widget 'checkbox 'toggle
1429 "A checkbox toggle."
1430 :format "%[%v%]"
1431 :on "[X]"
1432 :on-glyph "check1"
1433 :off "[ ]"
1434 :off-glyph "check0")
1435
1436 ;;; The `checklist' Widget.
1437
1438 (define-widget 'checklist 'default
1439 "A multiple choice widget."
1440 :convert-widget 'widget-types-convert-widget
1441 :format "%v"
1442 :offset 4
1443 :entry-format "%b %v"
1444 :menu-tag "checklist"
1445 :greedy nil
1446 :value-create 'widget-checklist-value-create
1447 :value-delete 'widget-children-value-delete
1448 :value-get 'widget-checklist-value-get
1449 :validate 'widget-checklist-validate
1450 :match 'widget-checklist-match
1451 :match-inline 'widget-checklist-match-inline)
1452
1453 (defun widget-checklist-value-create (widget)
1454 ;; Insert all values
1455 (let ((alist (widget-checklist-match-find widget (widget-get widget :value)))
1456 (args (widget-get widget :args)))
1457 (while args
1458 (widget-checklist-add-item widget (car args) (assq (car args) alist))
1459 (setq args (cdr args)))
1460 (widget-put widget :children (nreverse (widget-get widget :children)))))
1461
1462 (defun widget-checklist-add-item (widget type chosen)
1463 ;; Create checklist item in WIDGET of type TYPE.
1464 ;; If the item is checked, CHOSEN is a cons whose cdr is the value.
1465 (and (eq (preceding-char) ?\n)
1466 (widget-get widget :indent)
1467 (insert-char ? (widget-get widget :indent)))
1468 (widget-specify-insert
1469 (let* ((children (widget-get widget :children))
1470 (buttons (widget-get widget :buttons))
1471 (from (point))
1472 child button)
1473 (insert (widget-get widget :entry-format))
1474 (goto-char from)
1475 ;; Parse % escapes in format.
1476 (while (re-search-forward "%\\([bv%]\\)" nil t)
1477 (let ((escape (aref (match-string 1) 0)))
1478 (replace-match "" t t)
1479 (cond ((eq escape ?%)
1480 (insert "%"))
1481 ((eq escape ?b)
1482 (setq button (widget-create-child-and-convert
1483 widget 'checkbox :value (not (null chosen)))))
1484 ((eq escape ?v)
1485 (setq child
1486 (cond ((not chosen)
1487 (widget-create-child widget type))
1488 ((widget-get type :inline)
1489 (widget-create-child-value
1490 widget type (cdr chosen)))
1491 (t
1492 (widget-create-child-value
1493 widget type (car (cdr chosen)))))))
1494 (t
1495 (error "Unknown escape `%c'" escape)))))
1496 ;; Update properties.
1497 (and button child (widget-put child :button button))
1498 (and button (widget-put widget :buttons (cons button buttons)))
1499 (and child (widget-put widget :children (cons child children))))))
1500
1501 (defun widget-checklist-match (widget values)
1502 ;; All values must match a type in the checklist.
1503 (and (listp values)
1504 (null (cdr (widget-checklist-match-inline widget values)))))
1505
1506 (defun widget-checklist-match-inline (widget values)
1507 ;; Find the values which match a type in the checklist.
1508 (let ((greedy (widget-get widget :greedy))
1509 (args (copy-list (widget-get widget :args)))
1510 found rest)
1511 (while values
1512 (let ((answer (widget-checklist-match-up args values)))
1513 (cond (answer
1514 (let ((vals (widget-match-inline answer values)))
1515 (setq found (append found (car vals))
1516 values (cdr vals)
1517 args (delq answer args))))
1518 (greedy
1519 (setq rest (append rest (list (car values)))
1520 values (cdr values)))
1521 (t
1522 (setq rest (append rest values)
1523 values nil)))))
1524 (cons found rest)))
1525
1526 (defun widget-checklist-match-find (widget vals)
1527 ;; Find the vals which match a type in the checklist.
1528 ;; Return an alist of (TYPE MATCH).
1529 (let ((greedy (widget-get widget :greedy))
1530 (args (copy-list (widget-get widget :args)))
1531 found)
1532 (while vals
1533 (let ((answer (widget-checklist-match-up args vals)))
1534 (cond (answer
1535 (let ((match (widget-match-inline answer vals)))
1536 (setq found (cons (cons answer (car match)) found)
1537 vals (cdr match)
1538 args (delq answer args))))
1539 (greedy
1540 (setq vals (cdr vals)))
1541 (t
1542 (setq vals nil)))))
1543 found))
1544
1545 (defun widget-checklist-match-up (args vals)
1546 ;; Rerturn the first type from ARGS that matches VALS.
1547 (let (current found)
1548 (while (and args (null found))
1549 (setq current (car args)
1550 args (cdr args)
1551 found (widget-match-inline current vals)))
1552 (if found
1553 current
1554 nil)))
1555
1556 (defun widget-checklist-value-get (widget)
1557 ;; The values of all selected items.
1558 (let ((children (widget-get widget :children))
1559 child result)
1560 (while children
1561 (setq child (car children)
1562 children (cdr children))
1563 (if (widget-value (widget-get child :button))
1564 (setq result (append result (widget-apply child :value-inline)))))
1565 result))
1566
1567 (defun widget-checklist-validate (widget)
1568 ;; Ticked chilren must be valid.
1569 (let ((children (widget-get widget :children))
1570 child button found)
1571 (while (and children (not found))
1572 (setq child (car children)
1573 children (cdr children)
1574 button (widget-get child :button)
1575 found (and (widget-value button)
1576 (widget-apply child :validate))))
1577 found))
1578
1579 ;;; The `option' Widget
1580
1581 (define-widget 'option 'checklist
1582 "An widget with an optional item."
1583 :inline t)
1584
1585 ;;; The `choice-item' Widget.
1586
1587 (define-widget 'choice-item 'item
1588 "Button items that delegate action events to their parents."
1589 :action 'widget-choice-item-action
1590 :format "%[%t%] \n")
1591
1592 (defun widget-choice-item-action (widget &optional event)
1593 ;; Tell parent what happened.
1594 (widget-apply (widget-get widget :parent) :action event))
1595
1596 ;;; The `radio-button' Widget.
1597
1598 (define-widget 'radio-button 'toggle
1599 "A radio button for use in the `radio' widget."
1600 :notify 'widget-radio-button-notify
1601 :format "%[%v%]"
1602 :on "(*)"
1603 :on-glyph "radio1"
1604 :off "( )"
1605 :off-glyph "radio0")
1606
1607 (defun widget-radio-button-notify (widget child &optional event)
1608 ;; Tell daddy.
1609 (widget-apply (widget-get widget :parent) :action widget event))
1610
1611 ;;; The `radio-button-choice' Widget.
1612
1613 (define-widget 'radio-button-choice 'default
1614 "Select one of multiple options."
1615 :convert-widget 'widget-types-convert-widget
1616 :offset 4
1617 :format "%v"
1618 :entry-format "%b %v"
1619 :menu-tag "radio"
1620 :value-create 'widget-radio-value-create
1621 :value-delete 'widget-children-value-delete
1622 :value-get 'widget-radio-value-get
1623 :value-inline 'widget-radio-value-inline
1624 :value-set 'widget-radio-value-set
1625 :error "You must push one of the buttons"
1626 :validate 'widget-radio-validate
1627 :match 'widget-choice-match
1628 :match-inline 'widget-choice-match-inline
1629 :action 'widget-radio-action)
1630
1631 (defun widget-radio-value-create (widget)
1632 ;; Insert all values
1633 (let ((args (widget-get widget :args))
1634 arg)
1635 (while args
1636 (setq arg (car args)
1637 args (cdr args))
1638 (widget-radio-add-item widget arg))))
1639
1640 (defun widget-radio-add-item (widget type)
1641 "Add to radio widget WIDGET a new radio button item of type TYPE."
1642 ;; (setq type (widget-convert type))
1643 (and (eq (preceding-char) ?\n)
1644 (widget-get widget :indent)
1645 (insert-char ? (widget-get widget :indent)))
1646 (widget-specify-insert
1647 (let* ((value (widget-get widget :value))
1648 (children (widget-get widget :children))
1649 (buttons (widget-get widget :buttons))
1650 (from (point))
1651 (chosen (and (null (widget-get widget :choice))
1652 (widget-apply type :match value)))
1653 child button)
1654 (insert (widget-get widget :entry-format))
1655 (goto-char from)
1656 ;; Parse % escapes in format.
1657 (while (re-search-forward "%\\([bv%]\\)" nil t)
1658 (let ((escape (aref (match-string 1) 0)))
1659 (replace-match "" t t)
1660 (cond ((eq escape ?%)
1661 (insert "%"))
1662 ((eq escape ?b)
1663 (setq button (widget-create-child-and-convert
1664 widget 'radio-button
1665 :value (not (null chosen)))))
1666 ((eq escape ?v)
1667 (setq child (if chosen
1668 (widget-create-child-value
1669 widget type value)
1670 (widget-create-child widget type))))
1671 (t
1672 (error "Unknown escape `%c'" escape)))))
1673 ;; Update properties.
1674 (when chosen
1675 (widget-put widget :choice type))
1676 (when button
1677 (widget-put child :button button)
1678 (widget-put widget :buttons (nconc buttons (list button))))
1679 (when child
1680 (widget-put widget :children (nconc children (list child))))
1681 child)))
1682
1683 (defun widget-radio-value-get (widget)
1684 ;; Get value of the child widget.
1685 (let ((chosen (widget-radio-chosen widget)))
1686 (and chosen (widget-value chosen))))
1687
1688 (defun widget-radio-chosen (widget)
1689 "Return the widget representing the chosen radio button."
1690 (let ((children (widget-get widget :children))
1691 current found)
1692 (while children
1693 (setq current (car children)
1694 children (cdr children))
1695 (let* ((button (widget-get current :button))
1696 (value (widget-apply button :value-get)))
1697 (when value
1698 (setq found current
1699 children nil))))
1700 found))
1701
1702 (defun widget-radio-value-inline (widget)
1703 ;; Get value of the child widget.
1704 (let ((children (widget-get widget :children))
1705 current found)
1706 (while children
1707 (setq current (car children)
1708 children (cdr children))
1709 (let* ((button (widget-get current :button))
1710 (value (widget-apply button :value-get)))
1711 (when value
1712 (setq found (widget-apply current :value-inline)
1713 children nil))))
1714 found))
1715
1716 (defun widget-radio-value-set (widget value)
1717 ;; We can't just delete and recreate a radio widget, since children
1718 ;; can be added after the original creation and won't be recreated
1719 ;; by `:create'.
1720 (let ((children (widget-get widget :children))
1721 current found)
1722 (while children
1723 (setq current (car children)
1724 children (cdr children))
1725 (let* ((button (widget-get current :button))
1726 (match (and (not found)
1727 (widget-apply current :match value))))
1728 (widget-value-set button match)
1729 (if match
1730 (widget-value-set current value))
1731 (setq found (or found match))))))
1732
1733 (defun widget-radio-validate (widget)
1734 ;; Valid if we have made a valid choice.
1735 (let ((children (widget-get widget :children))
1736 current found button)
1737 (while (and children (not found))
1738 (setq current (car children)
1739 children (cdr children)
1740 button (widget-get current :button)
1741 found (widget-apply button :value-get)))
1742 (if found
1743 (widget-apply current :validate)
1744 widget)))
1745
1746 (defun widget-radio-action (widget child event)
1747 ;; Check if a radio button was pressed.
1748 (let ((children (widget-get widget :children))
1749 (buttons (widget-get widget :buttons))
1750 current)
1751 (when (memq child buttons)
1752 (while children
1753 (setq current (car children)
1754 children (cdr children))
1755 (let* ((button (widget-get current :button)))
1756 (cond ((eq child button)
1757 (widget-value-set button t))
1758 ((widget-value button)
1759 (widget-value-set button nil)))))))
1760 ;; Pass notification to parent.
1761 (widget-apply widget :notify child event))
1762
1763 ;;; The `insert-button' Widget.
1764
1765 (define-widget 'insert-button 'push-button
1766 "An insert button for the `editable-list' widget."
1767 :tag "INS"
1768 :action 'widget-insert-button-action)
1769
1770 (defun widget-insert-button-action (widget &optional event)
1771 ;; Ask the parent to insert a new item.
1772 (widget-apply (widget-get widget :parent)
1773 :insert-before (widget-get widget :widget)))
1774
1775 ;;; The `delete-button' Widget.
1776
1777 (define-widget 'delete-button 'push-button
1778 "A delete button for the `editable-list' widget."
1779 :tag "DEL"
1780 :action 'widget-delete-button-action)
1781
1782 (defun widget-delete-button-action (widget &optional event)
1783 ;; Ask the parent to insert a new item.
1784 (widget-apply (widget-get widget :parent)
1785 :delete-at (widget-get widget :widget)))
1786
1787 ;;; The `editable-list' Widget.
1788
1789 (defcustom widget-editable-list-gui nil
1790 "If non nil, use GUI push-buttons in editable list when available."
1791 :type 'boolean
1792 :group 'widgets)
1793
1794 (define-widget 'editable-list 'default
1795 "A variable list of widgets of the same type."
1796 :convert-widget 'widget-types-convert-widget
1797 :offset 12
1798 :format "%v%i\n"
1799 :format-handler 'widget-editable-list-format-handler
1800 :entry-format "%i %d %v"
1801 :menu-tag "editable-list"
1802 :value-create 'widget-editable-list-value-create
1803 :value-delete 'widget-children-value-delete
1804 :value-get 'widget-editable-list-value-get
1805 :validate 'widget-editable-list-validate
1806 :match 'widget-editable-list-match
1807 :match-inline 'widget-editable-list-match-inline
1808 :insert-before 'widget-editable-list-insert-before
1809 :delete-at 'widget-editable-list-delete-at)
1810
1811 (defun widget-editable-list-format-handler (widget escape)
1812 ;; We recognize the insert button.
1813 (let ((widget-push-button-gui widget-editable-list-gui))
1814 (cond ((eq escape ?i)
1815 (and (widget-get widget :indent)
1816 (insert-char ? (widget-get widget :indent)))
1817 (widget-create-child-and-convert widget 'insert-button))
1818 (t
1819 (widget-default-format-handler widget escape)))))
1820
1821 (defun widget-editable-list-value-create (widget)
1822 ;; Insert all values
1823 (let* ((value (widget-get widget :value))
1824 (type (nth 0 (widget-get widget :args)))
1825 (inlinep (widget-get type :inline))
1826 children)
1827 (widget-put widget :value-pos (copy-marker (point)))
1828 (set-marker-insertion-type (widget-get widget :value-pos) t)
1829 (while value
1830 (let ((answer (widget-match-inline type value)))
1831 (if answer
1832 (setq children (cons (widget-editable-list-entry-create
1833 widget
1834 (if inlinep
1835 (car answer)
1836 (car (car answer)))
1837 t)
1838 children)
1839 value (cdr answer))
1840 (setq value nil))))
1841 (widget-put widget :children (nreverse children))))
1842
1843 (defun widget-editable-list-value-get (widget)
1844 ;; Get value of the child widget.
1845 (apply 'append (mapcar (lambda (child) (widget-apply child :value-inline))
1846 (widget-get widget :children))))
1847
1848 (defun widget-editable-list-validate (widget)
1849 ;; All the chilren must be valid.
1850 (let ((children (widget-get widget :children))
1851 child found)
1852 (while (and children (not found))
1853 (setq child (car children)
1854 children (cdr children)
1855 found (widget-apply child :validate)))
1856 found))
1857
1858 (defun widget-editable-list-match (widget value)
1859 ;; Value must be a list and all the members must match the type.
1860 (and (listp value)
1861 (null (cdr (widget-editable-list-match-inline widget value)))))
1862
1863 (defun widget-editable-list-match-inline (widget value)
1864 (let ((type (nth 0 (widget-get widget :args)))
1865 (ok t)
1866 found)
1867 (while (and value ok)
1868 (let ((answer (widget-match-inline type value)))
1869 (if answer
1870 (setq found (append found (car answer))
1871 value (cdr answer))
1872 (setq ok nil))))
1873 (cons found value)))
1874
1875 (defun widget-editable-list-insert-before (widget before)
1876 ;; Insert a new child in the list of children.
1877 (save-excursion
1878 (let ((children (widget-get widget :children))
1879 (inhibit-read-only t)
1880 after-change-functions)
1881 (cond (before
1882 (goto-char (widget-get before :entry-from)))
1883 (t
1884 (goto-char (widget-get widget :value-pos))))
1885 (let ((child (widget-editable-list-entry-create
1886 widget nil nil)))
1887 (when (< (widget-get child :entry-from) (widget-get widget :from))
1888 (set-marker (widget-get widget :from)
1889 (widget-get child :entry-from)))
1890 (widget-specify-text (widget-get child :entry-from)
1891 (widget-get child :entry-to))
1892 (if (eq (car children) before)
1893 (widget-put widget :children (cons child children))
1894 (while (not (eq (car (cdr children)) before))
1895 (setq children (cdr children)))
1896 (setcdr children (cons child (cdr children)))))))
1897 (widget-setup)
1898 (widget-apply widget :notify widget))
1899
1900 (defun widget-editable-list-delete-at (widget child)
1901 ;; Delete child from list of children.
1902 (save-excursion
1903 (let ((buttons (copy-list (widget-get widget :buttons)))
1904 button
1905 (inhibit-read-only t)
1906 after-change-functions)
1907 (while buttons
1908 (setq button (car buttons)
1909 buttons (cdr buttons))
1910 (when (eq (widget-get button :widget) child)
1911 (widget-put widget
1912 :buttons (delq button (widget-get widget :buttons)))
1913 (widget-delete button))))
1914 (let ((entry-from (widget-get child :entry-from))
1915 (entry-to (widget-get child :entry-to))
1916 (inhibit-read-only t)
1917 after-change-functions)
1918 (widget-delete child)
1919 (delete-region entry-from entry-to)
1920 (set-marker entry-from nil)
1921 (set-marker entry-to nil))
1922 (widget-put widget :children (delq child (widget-get widget :children))))
1923 (widget-setup)
1924 (widget-apply widget :notify widget))
1925
1926 (defun widget-editable-list-entry-create (widget value conv)
1927 ;; Create a new entry to the list.
1928 (let ((type (nth 0 (widget-get widget :args)))
1929 (widget-push-button-gui widget-editable-list-gui)
1930 child delete insert)
1931 (widget-specify-insert
1932 (save-excursion
1933 (and (widget-get widget :indent)
1934 (insert-char ? (widget-get widget :indent)))
1935 (insert (widget-get widget :entry-format)))
1936 ;; Parse % escapes in format.
1937 (while (re-search-forward "%\\(.\\)" nil t)
1938 (let ((escape (aref (match-string 1) 0)))
1939 (replace-match "" t t)
1940 (cond ((eq escape ?%)
1941 (insert "%"))
1942 ((eq escape ?i)
1943 (setq insert (widget-create-child-and-convert
1944 widget 'insert-button)))
1945 ((eq escape ?d)
1946 (setq delete (widget-create-child-and-convert
1947 widget 'delete-button)))
1948 ((eq escape ?v)
1949 (if conv
1950 (setq child (widget-create-child-value
1951 widget type value))
1952 (setq child (widget-create-child widget type))))
1953 (t
1954 (error "Unknown escape `%c'" escape)))))
1955 (widget-put widget
1956 :buttons (cons delete
1957 (cons insert
1958 (widget-get widget :buttons))))
1959 (let ((entry-from (copy-marker (point-min)))
1960 (entry-to (copy-marker (point-max))))
1961 (widget-specify-text entry-from entry-to)
1962 (set-marker-insertion-type entry-from t)
1963 (set-marker-insertion-type entry-to nil)
1964 (widget-put child :entry-from entry-from)
1965 (widget-put child :entry-to entry-to)))
1966 (widget-put insert :widget child)
1967 (widget-put delete :widget child)
1968 child))
1969
1970 ;;; The `group' Widget.
1971
1972 (define-widget 'group 'default
1973 "A widget which group other widgets inside."
1974 :convert-widget 'widget-types-convert-widget
1975 :format "%v"
1976 :value-create 'widget-group-value-create
1977 :value-delete 'widget-children-value-delete
1978 :value-get 'widget-editable-list-value-get
1979 :validate 'widget-editable-list-validate
1980 :match 'widget-group-match
1981 :match-inline 'widget-group-match-inline)
1982
1983 (defun widget-group-value-create (widget)
1984 ;; Create each component.
1985 (let ((args (widget-get widget :args))
1986 (value (widget-get widget :value))
1987 arg answer children)
1988 (while args
1989 (setq arg (car args)
1990 args (cdr args)
1991 answer (widget-match-inline arg value)
1992 value (cdr answer))
1993 (and (eq (preceding-char) ?\n)
1994 (widget-get widget :indent)
1995 (insert-char ? (widget-get widget :indent)))
1996 (push (cond ((null answer)
1997 (widget-create-child widget arg))
1998 ((widget-get arg :inline)
1999 (widget-create-child-value widget arg (car answer)))
2000 (t
2001 (widget-create-child-value widget arg (car (car answer)))))
2002 children))
2003 (widget-put widget :children (nreverse children))))
2004
2005 (defun widget-group-match (widget values)
2006 ;; Match if the components match.
2007 (and (listp values)
2008 (let ((match (widget-group-match-inline widget values)))
2009 (and match (null (cdr match))))))
2010
2011 (defun widget-group-match-inline (widget vals)
2012 ;; Match if the components match.
2013 (let ((args (widget-get widget :args))
2014 argument answer found)
2015 (while args
2016 (setq argument (car args)
2017 args (cdr args)
2018 answer (widget-match-inline argument vals))
2019 (if answer
2020 (setq vals (cdr answer)
2021 found (append found (car answer)))
2022 (setq vals nil
2023 args nil)))
2024 (if answer
2025 (cons found vals)
2026 nil)))
2027
2028 ;;; The `widget-help' Widget.
2029
2030 (define-widget 'widget-help 'push-button
2031 "The widget documentation button."
2032 :format "%[[%t]%] %d"
2033 :help-echo "Push me to toggle the documentation."
2034 :action 'widget-help-action)
2035
2036 (defun widget-help-action (widget &optional event)
2037 "Toggle documentation for WIDGET."
2038 (let ((old (widget-get widget :doc))
2039 (new (widget-get widget :widget-doc)))
2040 (widget-put widget :doc new)
2041 (widget-put widget :widget-doc old))
2042 (widget-value-set widget (widget-value widget)))
2043
2044 ;;; The Sexp Widgets.
2045
2046 (define-widget 'const 'item
2047 "An immutable sexp."
2048 :format "%t\n%d")
2049
2050 (define-widget 'function-item 'item
2051 "An immutable function name."
2052 :format "%v\n%h"
2053 :documentation-property (lambda (symbol)
2054 (condition-case nil
2055 (documentation symbol t)
2056 (error nil))))
2057
2058 (define-widget 'variable-item 'item
2059 "An immutable variable name."
2060 :format "%v\n%h"
2061 :documentation-property 'variable-documentation)
2062
2063 (define-widget 'string 'editable-field
2064 "A string"
2065 :tag "String"
2066 :format "%[%t%]: %v")
2067
2068 (define-widget 'regexp 'string
2069 "A regular expression."
2070 ;; Should do validation.
2071 :tag "Regexp")
2072
2073 (define-widget 'file 'string
2074 "A file widget.
2075 It will read a file name from the minibuffer when activated."
2076 :format "%[%t%]: %v"
2077 :tag "File"
2078 :action 'widget-file-action)
2079
2080 (defun widget-file-action (widget &optional event)
2081 ;; Read a file name from the minibuffer.
2082 (let* ((value (widget-value widget))
2083 (dir (file-name-directory value))
2084 (file (file-name-nondirectory value))
2085 (menu-tag (widget-apply widget :menu-tag-get))
2086 (must-match (widget-get widget :must-match))
2087 (answer (read-file-name (concat menu-tag ": (defalt `" value "') ")
2088 dir nil must-match file)))
2089 (widget-value-set widget (abbreviate-file-name answer))
2090 (widget-apply widget :notify widget event)
2091 (widget-setup)))
2092
2093 (define-widget 'directory 'file
2094 "A directory widget.
2095 It will read a directory name from the minibuffer when activated."
2096 :tag "Directory")
2097
2098 (define-widget 'symbol 'string
2099 "A lisp symbol."
2100 :value nil
2101 :tag "Symbol"
2102 :match (lambda (widget value) (symbolp value))
2103 :value-to-internal (lambda (widget value)
2104 (if (symbolp value)
2105 (symbol-name value)
2106 value))
2107 :value-to-external (lambda (widget value)
2108 (if (stringp value)
2109 (intern value)
2110 value)))
2111
2112 (define-widget 'function 'sexp
2113 ;; Should complete on functions.
2114 "A lisp function."
2115 :tag "Function")
2116
2117 (define-widget 'variable 'symbol
2118 ;; Should complete on variables.
2119 "A lisp variable."
2120 :tag "Variable")
2121
2122 (define-widget 'sexp 'string
2123 "An arbitrary lisp expression."
2124 :tag "Lisp expression"
2125 :value nil
2126 :validate 'widget-sexp-validate
2127 :match (lambda (widget value) t)
2128 :value-to-internal 'widget-sexp-value-to-internal
2129 :value-to-external (lambda (widget value) (read value)))
2130
2131 (defun widget-sexp-value-to-internal (widget value)
2132 ;; Use pp for printer representation.
2133 (let ((pp (pp-to-string value)))
2134 (while (string-match "\n\\'" pp)
2135 (setq pp (substring pp 0 -1)))
2136 (if (or (string-match "\n\\'" pp)
2137 (> (length pp) 40))
2138 (concat "\n" pp)
2139 pp)))
2140
2141 (defun widget-sexp-validate (widget)
2142 ;; Valid if we can read the string and there is no junk left after it.
2143 (save-excursion
2144 (let ((buffer (set-buffer (get-buffer-create " *Widget Scratch*"))))
2145 (erase-buffer)
2146 (insert (widget-apply widget :value-get))
2147 (goto-char (point-min))
2148 (condition-case data
2149 (let ((value (read buffer)))
2150 (if (eobp)
2151 (if (widget-apply widget :match value)
2152 nil
2153 (widget-put widget :error (widget-get widget :type-error))
2154 widget)
2155 (widget-put widget
2156 :error (format "Junk at end of expression: %s"
2157 (buffer-substring (point)
2158 (point-max))))
2159 widget))
2160 (error (widget-put widget :error (error-message-string data))
2161 widget)))))
2162
2163 (define-widget 'integer 'sexp
2164 "An integer."
2165 :tag "Integer"
2166 :value 0
2167 :type-error "This field should contain an integer"
2168 :value-to-internal (lambda (widget value)
2169 (if (integerp value)
2170 (prin1-to-string value)
2171 value))
2172 :match (lambda (widget value) (integerp value)))
2173
2174 (define-widget 'character 'string
2175 "An character."
2176 :tag "Character"
2177 :value 0
2178 :size 1
2179 :format "%{%t%}: %v\n"
2180 :type-error "This field should contain a character"
2181 :value-to-internal (lambda (widget value)
2182 (if (integerp value)
2183 (char-to-string value)
2184 value))
2185 :value-to-external (lambda (widget value)
2186 (if (stringp value)
2187 (aref value 0)
2188 value))
2189 :match (lambda (widget value) (integerp value)))
2190
2191 (define-widget 'number 'sexp
2192 "A floating point number."
2193 :tag "Number"
2194 :value 0.0
2195 :type-error "This field should contain a number"
2196 :value-to-internal (lambda (widget value)
2197 (if (numberp value)
2198 (prin1-to-string value)
2199 value))
2200 :match (lambda (widget value) (numberp value)))
2201
2202 (define-widget 'list 'group
2203 "A lisp list."
2204 :tag "List"
2205 :format "%{%t%}:\n%v")
2206
2207 (define-widget 'vector 'group
2208 "A lisp vector."
2209 :tag "Vector"
2210 :format "%{%t%}:\n%v"
2211 :match 'widget-vector-match
2212 :value-to-internal (lambda (widget value) (append value nil))
2213 :value-to-external (lambda (widget value) (apply 'vector value)))
2214
2215 (defun widget-vector-match (widget value)
2216 (and (vectorp value)
2217 (widget-group-match widget
2218 (widget-apply :value-to-internal widget value))))
2219
2220 (define-widget 'cons 'group
2221 "A cons-cell."
2222 :tag "Cons-cell"
2223 :format "%{%t%}:\n%v"
2224 :match 'widget-cons-match
2225 :value-to-internal (lambda (widget value)
2226 (list (car value) (cdr value)))
2227 :value-to-external (lambda (widget value)
2228 (cons (nth 0 value) (nth 1 value))))
2229
2230 (defun widget-cons-match (widget value)
2231 (and (consp value)
2232 (widget-group-match widget
2233 (widget-apply widget :value-to-internal value))))
2234
2235 (define-widget 'choice 'menu-choice
2236 "A union of several sexp types."
2237 :tag "Choice"
2238 :format "%[%t%]: %v")
2239
2240 (define-widget 'radio 'radio-button-choice
2241 "A union of several sexp types."
2242 :tag "Choice"
2243 :format "%{%t%}:\n%v")
2244
2245 (define-widget 'repeat 'editable-list
2246 "A variable length homogeneous list."
2247 :tag "Repeat"
2248 :format "%{%t%}:\n%v%i\n")
2249
2250 (define-widget 'set 'checklist
2251 "A list of members from a fixed set."
2252 :tag "Set"
2253 :format "%{%t%}:\n%v")
2254
2255 (define-widget 'boolean 'toggle
2256 "To be nil or non-nil, that is the question."
2257 :tag "Boolean"
2258 :format "%{%t%}: %[%v%]\n")
2259
2260 ;;; The `color' Widget.
2261
2262 (define-widget 'color-item 'choice-item
2263 "A color name (with sample)."
2264 :format "%v (%[sample%])\n"
2265 :button-face-get 'widget-color-item-button-face-get)
2266
2267 (defun widget-color-item-button-face-get (widget)
2268 ;; We create a face from the value.
2269 (require 'facemenu)
2270 (condition-case nil
2271 (facemenu-get-face (intern (concat "fg:" (widget-value widget))))
2272 (error 'default)))
2273
2274 (define-widget 'color 'push-button
2275 "Choose a color name (with sample)."
2276 :format "%[%t%]: %v"
2277 :tag "Color"
2278 :value "default"
2279 :value-create 'widget-color-value-create
2280 :value-delete 'widget-children-value-delete
2281 :value-get 'widget-color-value-get
2282 :value-set 'widget-color-value-set
2283 :action 'widget-color-action
2284 :match 'widget-field-match
2285 :tag "Color")
2286
2287 (defvar widget-color-choice-list nil)
2288 ;; Variable holding the possible colors.
2289
2290 (defun widget-color-choice-list ()
2291 (unless widget-color-choice-list
2292 (setq widget-color-choice-list
2293 (mapcar '(lambda (color) (list color))
2294 (x-defined-colors))))
2295 widget-color-choice-list)
2296
2297 (defun widget-color-value-create (widget)
2298 (let ((child (widget-create-child-and-convert
2299 widget 'color-item (widget-get widget :value))))
2300 (widget-put widget :children (list child))))
2301
2302 (defun widget-color-value-get (widget)
2303 ;; Pass command to first child.
2304 (widget-apply (car (widget-get widget :children)) :value-get))
2305
2306 (defun widget-color-value-set (widget value)
2307 ;; Pass command to first child.
2308 (widget-apply (car (widget-get widget :children)) :value-set value))
2309
2310 (defvar widget-color-history nil
2311 "History of entered colors")
2312
2313 (defun widget-color-action (widget &optional event)
2314 ;; Prompt for a color.
2315 (let* ((tag (widget-apply widget :menu-tag-get))
2316 (prompt (concat tag ": "))
2317 (answer (cond ((string-match "XEmacs" emacs-version)
2318 (read-color prompt))
2319 ((fboundp 'x-defined-colors)
2320 (completing-read (concat tag ": ")
2321 (widget-color-choice-list)
2322 nil nil nil 'widget-color-history))
2323 (t
2324 (read-string prompt (widget-value widget))))))
2325 (unless (zerop (length answer))
2326 (widget-value-set widget answer)
2327 (widget-apply widget :notify widget event)
2328 (widget-setup))))
2329
2330 ;;; The Help Echo
2331
2332 (defun widget-echo-help-mouse ()
2333 "Display the help message for the widget under the mouse.
2334 Enable with (run-with-idle-timer 1 t 'widget-echo-help-mouse)"
2335 (let* ((pos (mouse-position))
2336 (frame (car pos))
2337 (x (car (cdr pos)))
2338 (y (cdr (cdr pos)))
2339 (win (window-at x y frame))
2340 (where (coordinates-in-window-p (cons x y) win)))
2341 (when (consp where)
2342 (save-window-excursion
2343 (progn ; save-excursion
2344 (select-window win)
2345 (let* ((result (compute-motion (window-start win)
2346 '(0 . 0)
2347 (window-end win)
2348 where
2349 (window-width win)
2350 (cons (window-hscroll) 0)
2351 win)))
2352 (when (and (eq (nth 1 result) x)
2353 (eq (nth 2 result) y))
2354 (widget-echo-help (nth 0 result))))))))
2355 (unless track-mouse
2356 (setq track-mouse t)
2357 (add-hook 'post-command-hook 'widget-stop-mouse-tracking)))
2358
2359 (defun widget-stop-mouse-tracking (&rest args)
2360 "Stop the mouse tracking done while idle."
2361 (remove-hook 'post-command-hook 'widget-stop-mouse-tracking)
2362 (setq track-mouse nil))
2363
2364 (defun widget-at (pos)
2365 "The button or field at POS."
2366 (or (get-text-property pos 'button)
2367 (get-text-property pos 'field)))
2368
2369 (defun widget-echo-help (pos)
2370 "Display the help echo for widget at POS."
2371 (let* ((widget (widget-at pos))
2372 (help-echo (and widget (widget-get widget :help-echo))))
2373 (cond ((stringp help-echo)
2374 (message "%s" help-echo))
2375 ((and (symbolp help-echo) (fboundp help-echo)
2376 (stringp (setq help-echo (funcall help-echo widget))))
2377 (message "%s" help-echo)))))
2378
2379 ;;; The End:
2380
2381 (provide 'wid-edit)
2382
2383 ;; wid-edit.el ends here