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